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]);
250 #ifdef COLLISIONPARANOID
254 VectorCopy(trace.endpos, temp);
255 endstuck = SV_Move(temp, ent->v->mins, ent->v->maxs, temp, MOVE_WORLDONLY, ent).startsolid;
256 Con_Printf("%s{%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "\002" : "", bumpcount, ent->v->origin[0], ent->v->origin[1], ent->v->origin[2], end[0] - ent->v->origin[0], end[1] - ent->v->origin[1], end[2] - ent->v->origin[2], trace.fraction, trace.endpos[0] - ent->v->origin[0], trace.endpos[1] - ent->v->origin[1], trace.endpos[2] - ent->v->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
257 //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]);
259 Cbuf_AddText("disconnect\n");
264 if (trace.startsolid)
266 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
267 // entity is trapped in another solid
268 VectorClear(ent->v->velocity);
273 if (trace.fraction >= 0.001)
275 // actually covered some distance
276 VectorCopy (trace.endpos, ent->v->origin);
279 // break if it moved the entire distance
280 if (trace.fraction == 1)
284 Host_Error ("SV_FlyMove: !trace.ent");
286 if ((int) ent->v->flags & FL_ONGROUND)
288 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
292 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
299 if (trace.plane.normal[2] > 0.7)
303 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
304 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
306 if (!trace.plane.normal[2])
310 // save the trace for player extrafriction
312 VectorCopy(trace.plane.normal, stepnormal);
315 // run the impact function
318 SV_Impact (ent, trace.ent);
320 // break if removed by the impact function
325 time *= 1 - trace.fraction;
327 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
334 int i, j, blocked, impact, numplanes, bumpcount, numbumps;
336 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
342 VectorCopy (ent->v->velocity, original_velocity);
343 VectorCopy (ent->v->velocity, primal_velocity);
348 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
350 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
353 for (i=0 ; i<3 ; i++)
354 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
356 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
359 if (trace.startsolid)
361 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
362 // entity is trapped in another solid
363 VectorClear(ent->v->velocity);
368 if (trace.fraction > 0)
370 // actually covered some distance
371 VectorCopy (trace.endpos, ent->v->origin);
372 VectorCopy (ent->v->velocity, original_velocity);
376 // break if it moved the entire distance
377 if (trace.fraction == 1)
381 Host_Error ("SV_FlyMove: !trace.ent");
383 if ((int) ent->v->flags & FL_ONGROUND)
385 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
389 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
396 if (trace.plane.normal[2] > 0.7)
400 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
401 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
403 if (!trace.plane.normal[2])
407 // save the trace for player extrafriction
409 VectorCopy(trace.plane.normal, stepnormal);
412 // run the impact function
415 SV_Impact (ent, trace.ent);
417 // break if removed by the impact function
423 time_left -= time_left * trace.fraction;
425 // clipped to another plane
426 if (numplanes >= MAX_CLIP_PLANES)
428 // this shouldn't really happen
429 VectorClear(ent->v->velocity);
433 VectorCopy (trace.plane.normal, planes[numplanes]);
436 // modify original_velocity so it parallels all of the clip planes
437 for (i=0 ; i<numplanes ; i++)
439 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
440 for (j=0 ; j<numplanes ; j++)
444 if (DotProduct (new_velocity, planes[j]) < 0)
453 // go along this plane
454 VectorCopy (new_velocity, ent->v->velocity);
458 // go along the crease
461 VectorClear(ent->v->velocity);
464 CrossProduct (planes[0], planes[1], dir);
465 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
466 VectorNormalize(dir);
467 d = DotProduct (dir, ent->v->velocity);
468 VectorScale (dir, d, ent->v->velocity);
471 // if original velocity is against the original velocity,
472 // stop dead to avoid tiny occilations in sloping corners
473 if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
475 VectorClear(ent->v->velocity);
491 void SV_AddGravity (edict_t *ent)
496 val = GETEDICTFIELDVALUE(ent, eval_gravity);
497 if (val!=0 && val->_float)
498 ent_gravity = val->_float;
501 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
506 ===============================================================================
510 ===============================================================================
517 Does not change the entities velocity at all
520 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
525 VectorAdd (ent->v->origin, push, end);
527 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
528 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
529 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
530 // only clip against bmodels
531 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
533 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
535 VectorCopy (trace.endpos, ent->v->origin);
536 // FIXME: turn players specially
537 ent->v->angles[1] += trace.fraction * pushangles[1];
538 SV_LinkEdict (ent, true);
540 if (trace.fraction < 1 && trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
541 SV_Impact (ent, trace.ent);
552 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
553 void SV_PushMove (edict_t *pusher, float movetime)
557 float savesolid, movetime2, pushltime;
558 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
560 model_t *pushermodel;
563 switch ((int) pusher->v->solid)
565 // LordHavoc: valid pusher types
569 case SOLID_CORPSE: // LordHavoc: this would be weird...
571 // LordHavoc: no collisions
574 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
575 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
576 pusher->v->ltime += movetime;
577 SV_LinkEdict (pusher, false);
580 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
582 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])
584 pusher->v->ltime += movetime;
587 index = (int) pusher->v->modelindex;
588 if (index < 1 || index >= MAX_MODELS)
589 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
590 pushermodel = sv.models[index];
592 movetime2 = movetime;
593 VectorScale(pusher->v->velocity, movetime2, move1);
594 VectorScale(pusher->v->avelocity, movetime2, moveangle);
595 if (moveangle[0] || moveangle[2])
597 for (i = 0;i < 3;i++)
601 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
602 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
606 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
607 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
611 else if (moveangle[1])
613 for (i = 0;i < 3;i++)
617 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
618 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
622 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
623 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
629 for (i = 0;i < 3;i++)
633 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
634 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
638 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
639 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
644 VectorNegate (moveangle, a);
645 AngleVectorsFLU (a, forward, left, up);
647 VectorCopy (pusher->v->origin, pushorig);
648 VectorCopy (pusher->v->angles, pushang);
649 pushltime = pusher->v->ltime;
651 // move the pusher to it's final position
653 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
654 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
655 pusher->v->ltime += movetime;
656 SV_LinkEdict (pusher, false);
658 savesolid = pusher->v->solid;
660 // see if any solid entities are inside the final position
662 check = NEXT_EDICT(sv.edicts);
663 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
667 if (check->v->movetype == MOVETYPE_PUSH
668 || check->v->movetype == MOVETYPE_NONE
669 || check->v->movetype == MOVETYPE_FOLLOW
670 || check->v->movetype == MOVETYPE_NOCLIP)
673 // if the entity is standing on the pusher, it will definitely be moved
674 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
676 if (check->v->absmin[0] >= maxs[0]
677 || check->v->absmax[0] <= mins[0]
678 || check->v->absmin[1] >= maxs[1]
679 || check->v->absmax[1] <= mins[1]
680 || check->v->absmin[2] >= maxs[2]
681 || check->v->absmax[2] <= mins[2])
684 trace = SV_ClipMoveToEntity (pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin);
685 if (!trace.startsolid)
689 if (forward[0] < 0.999f) // quick way to check if any rotation is used
691 VectorSubtract (check->v->origin, pusher->v->origin, org);
692 org2[0] = DotProduct (org, forward);
693 org2[1] = DotProduct (org, left);
694 org2[2] = DotProduct (org, up);
695 VectorSubtract (org2, org, move);
696 VectorAdd (move, move1, move);
699 VectorCopy (move1, move);
701 // remove the onground flag for non-players
702 if (check->v->movetype != MOVETYPE_WALK)
703 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
705 VectorCopy (check->v->origin, check->e->moved_from);
706 VectorCopy (check->v->angles, check->e->moved_fromangles);
707 sv.moved_edicts[num_moved++] = check;
709 // try moving the contacted entity
710 pusher->v->solid = SOLID_NOT;
711 trace = SV_PushEntity (check, move, moveangle);
712 pusher->v->solid = savesolid; // was SOLID_BSP
714 // if it is still inside the pusher, block
715 if (SV_TestEntityPosition (check))
717 // try moving the contacted entity a tiny bit further to account for precision errors
718 pusher->v->solid = SOLID_NOT;
719 VectorScale(move, 0.1, move);
720 trace = SV_PushEntity (check, move, vec3_origin);
721 pusher->v->solid = savesolid;
722 if (SV_TestEntityPosition (check))
724 // still inside pusher, so it's really blocked
727 if (check->v->mins[0] == check->v->maxs[0])
729 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
732 check->v->mins[0] = check->v->mins[1] = 0;
733 VectorCopy (check->v->mins, check->v->maxs);
737 VectorCopy (pushorig, pusher->v->origin);
738 VectorCopy (pushang, pusher->v->angles);
739 pusher->v->ltime = pushltime;
740 SV_LinkEdict (pusher, false);
742 // move back any entities we already moved
743 for (i = 0;i < num_moved;i++)
745 ed = sv.moved_edicts[i];
746 VectorCopy (ed->e->moved_from, ed->v->origin);
747 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
748 SV_LinkEdict (ed, false);
751 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
752 if (pusher->v->blocked)
754 pr_global_struct->self = EDICT_TO_PROG(pusher);
755 pr_global_struct->other = EDICT_TO_PROG(check);
756 PR_ExecuteProgram (pusher->v->blocked, "");
770 void SV_Physics_Pusher (edict_t *ent)
772 float thinktime, oldltime, movetime;
774 oldltime = ent->v->ltime;
776 thinktime = ent->v->nextthink;
777 if (thinktime < ent->v->ltime + sv.frametime)
779 movetime = thinktime - ent->v->ltime;
784 movetime = sv.frametime;
787 // advances ent->v->ltime if not blocked
788 SV_PushMove (ent, movetime);
790 if (thinktime > oldltime && thinktime <= ent->v->ltime)
792 ent->v->nextthink = 0;
793 pr_global_struct->time = sv.time;
794 pr_global_struct->self = EDICT_TO_PROG(ent);
795 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
796 PR_ExecuteProgram (ent->v->think, "NULL think function");
802 ===============================================================================
806 ===============================================================================
813 This is a big hack to try and fix the rare case of getting stuck in the world
817 void SV_CheckStuck (edict_t *ent)
822 if (!SV_TestEntityPosition(ent))
824 VectorCopy (ent->v->origin, ent->v->oldorigin);
828 VectorCopy (ent->v->origin, org);
829 VectorCopy (ent->v->oldorigin, ent->v->origin);
830 if (!SV_TestEntityPosition(ent))
832 Con_DPrintf ("Unstuck.\n");
833 SV_LinkEdict (ent, true);
837 for (z=0 ; z< 18 ; z++)
838 for (i=-1 ; i <= 1 ; i++)
839 for (j=-1 ; j <= 1 ; j++)
841 ent->v->origin[0] = org[0] + i;
842 ent->v->origin[1] = org[1] + j;
843 ent->v->origin[2] = org[2] + z;
844 if (!SV_TestEntityPosition(ent))
846 Con_DPrintf ("Unstuck.\n");
847 SV_LinkEdict (ent, true);
852 VectorCopy (org, ent->v->origin);
853 Con_DPrintf ("player is stuck.\n");
862 qboolean SV_CheckWater (edict_t *ent)
867 point[0] = ent->v->origin[0];
868 point[1] = ent->v->origin[1];
869 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
871 ent->v->waterlevel = 0;
872 ent->v->watertype = CONTENTS_EMPTY;
873 cont = SV_PointQ1Contents(point);
874 if (cont <= CONTENTS_WATER)
876 ent->v->watertype = cont;
877 ent->v->waterlevel = 1;
878 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
879 cont = SV_PointQ1Contents(point);
880 if (cont <= CONTENTS_WATER)
882 ent->v->waterlevel = 2;
883 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
884 cont = SV_PointQ1Contents(point);
885 if (cont <= CONTENTS_WATER)
886 ent->v->waterlevel = 3;
890 return ent->v->waterlevel > 1;
899 void SV_WallFriction (edict_t *ent, float *stepnormal)
902 vec3_t forward, into, side;
904 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
905 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
907 // cut the tangential velocity
908 i = DotProduct (stepnormal, ent->v->velocity);
909 VectorScale (stepnormal, i, into);
910 VectorSubtract (ent->v->velocity, into, side);
911 ent->v->velocity[0] = side[0] * (1 + d);
912 ent->v->velocity[1] = side[1] * (1 + d);
917 =====================
920 Player has come to a dead stop, possibly due to the problem with limited
921 float precision at some angle joins in the BSP hull.
923 Try fixing by pushing one pixel in each direction.
925 This is a hack, but in the interest of good gameplay...
926 ======================
928 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
933 VectorCopy (ent->v->origin, oldorg);
936 for (i=0 ; i<8 ; i++)
938 // try pushing a little in an axial direction
941 case 0: dir[0] = 2; dir[1] = 0; break;
942 case 1: dir[0] = 0; dir[1] = 2; break;
943 case 2: dir[0] = -2; dir[1] = 0; break;
944 case 3: dir[0] = 0; dir[1] = -2; break;
945 case 4: dir[0] = 2; dir[1] = 2; break;
946 case 5: dir[0] = -2; dir[1] = 2; break;
947 case 6: dir[0] = 2; dir[1] = -2; break;
948 case 7: dir[0] = -2; dir[1] = -2; break;
951 SV_PushEntity (ent, dir, vec3_origin);
953 // retry the original move
954 ent->v->velocity[0] = oldvel[0];
955 ent->v->velocity[1] = oldvel[1];
956 ent->v->velocity[2] = 0;
957 clip = SV_FlyMove (ent, 0.1, NULL);
959 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
960 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
963 // go back to the original pos and try again
964 VectorCopy (oldorg, ent->v->origin);
968 VectorClear (ent->v->velocity);
973 =====================
977 ======================
979 void SV_WalkMove (edict_t *ent)
981 int clip, oldonground;
982 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
985 SV_CheckVelocity(ent);
987 // do a regular slide move unless it looks like you ran into a step
988 oldonground = (int)ent->v->flags & FL_ONGROUND;
989 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
991 VectorCopy (ent->v->origin, oldorg);
992 VectorCopy (ent->v->velocity, oldvel);
994 clip = SV_FlyMove (ent, sv.frametime, NULL);
996 SV_CheckVelocity(ent);
998 // if move didn't block on a step, return
1002 if (ent->v->movetype != MOVETYPE_FLY)
1004 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
1005 // don't stair up while jumping
1008 if (ent->v->movetype != MOVETYPE_WALK)
1009 // gibbed by a trigger
1013 SV_CheckVelocity(ent);
1015 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1018 VectorCopy (ent->v->origin, nosteporg);
1019 VectorCopy (ent->v->velocity, nostepvel);
1021 // try moving up and forward to go up a step
1022 // back to start pos
1023 VectorCopy (oldorg, ent->v->origin);
1025 VectorClear (upmove);
1026 VectorClear (downmove);
1027 upmove[2] = sv_stepheight.value;
1028 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1031 // FIXME: don't link?
1032 SV_PushEntity (ent, upmove, vec3_origin);
1035 ent->v->velocity[0] = oldvel[0];
1036 ent->v->velocity[1] = oldvel[1];
1037 ent->v->velocity[2] = 0;
1038 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1039 ent->v->velocity[2] += oldvel[2];
1041 // check for stuckness, possibly due to the limited precision of floats
1042 // in the clipping hulls
1044 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1045 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1046 // stepping up didn't make any progress
1047 clip = SV_TryUnstick (ent, oldvel);
1049 // extra friction based on view angle
1050 if (clip & 2 && sv_wallfriction.integer)
1051 SV_WallFriction (ent, stepnormal);
1054 // FIXME: don't link?
1055 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1057 if (downtrace.plane.normal[2] > 0.7)
1059 // LordHavoc: disabled this so you can walk on monsters/players
1060 //if (ent->v->solid == SOLID_BSP)
1062 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1063 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1068 // if the push down didn't end up on good ground, use the move without
1069 // the step up. This happens near wall / slope combinations, and can
1070 // cause the player to hop up higher on a slope too steep to climb
1071 VectorCopy (nosteporg, ent->v->origin);
1072 VectorCopy (nostepvel, ent->v->velocity);
1075 SV_CheckVelocity(ent);
1078 //============================================================================
1084 Entities that are "stuck" to another entity
1087 void SV_Physics_Follow (edict_t *ent)
1089 vec3_t vf, vr, vu, angles, v;
1093 if (!SV_RunThink (ent))
1096 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1097 e = PROG_TO_EDICT(ent->v->aiment);
1098 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])
1100 // quick case for no rotation
1101 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1105 angles[0] = -ent->v->punchangle[0];
1106 angles[1] = ent->v->punchangle[1];
1107 angles[2] = ent->v->punchangle[2];
1108 AngleVectors (angles, vf, vr, vu);
1109 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1110 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1111 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1112 angles[0] = -e->v->angles[0];
1113 angles[1] = e->v->angles[1];
1114 angles[2] = e->v->angles[2];
1115 AngleVectors (angles, vf, vr, vu);
1116 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1117 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1118 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1120 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1121 SV_LinkEdict (ent, true);
1125 ==============================================================================
1129 ==============================================================================
1134 SV_CheckWaterTransition
1138 void SV_CheckWaterTransition (edict_t *ent)
1141 cont = SV_PointQ1Contents(ent->v->origin);
1142 if (!ent->v->watertype)
1144 // just spawned here
1145 ent->v->watertype = cont;
1146 ent->v->waterlevel = 1;
1150 // check if the entity crossed into or out of water
1151 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1152 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1154 if (cont <= CONTENTS_WATER)
1156 ent->v->watertype = cont;
1157 ent->v->waterlevel = 1;
1161 ent->v->watertype = CONTENTS_EMPTY;
1162 ent->v->waterlevel = 0;
1170 Toss, bounce, and fly movement. When onground, do nothing.
1173 void SV_Physics_Toss (edict_t *ent)
1177 edict_t *groundentity;
1180 if (!SV_RunThink (ent))
1183 // if onground, return without moving
1184 if ((int)ent->v->flags & FL_ONGROUND)
1186 if (ent->v->groundentity == 0)
1188 // if ent was supported by a brush model on previous frame,
1189 // and groundentity is now freed, set groundentity to 0 (floating)
1190 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1191 if (groundentity->v->solid == SOLID_BSP)
1193 ent->e->suspendedinairflag = true;
1196 else if (ent->e->suspendedinairflag && groundentity->e->free)
1198 // leave it suspended in the air
1199 ent->v->groundentity = 0;
1200 ent->e->suspendedinairflag = false;
1204 ent->e->suspendedinairflag = false;
1206 SV_CheckVelocity (ent);
1209 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1210 SV_AddGravity (ent);
1213 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1216 VectorScale (ent->v->velocity, sv.frametime, move);
1217 trace = SV_PushEntity (ent, move, vec3_origin);
1221 if (trace.fraction < 1)
1223 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1225 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1226 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1228 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1230 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1231 // LordHavoc: fixed grenades not bouncing when fired down a slope
1232 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1234 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1235 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1236 VectorClear (ent->v->velocity);
1237 VectorClear (ent->v->avelocity);
1240 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1244 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1245 if (trace.plane.normal[2] > 0.7)
1247 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1248 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1249 VectorClear (ent->v->velocity);
1250 VectorClear (ent->v->avelocity);
1253 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1257 // check for in water
1258 SV_CheckWaterTransition (ent);
1262 ===============================================================================
1266 ===============================================================================
1273 Monsters freefall when they don't have a ground entity, otherwise
1274 all movement is done with discrete steps.
1276 This is also used for objects that have become still on the ground, but
1277 will fall if the floor is pulled out from under them.
1280 void SV_Physics_Step (edict_t *ent)
1282 // freefall if not onground/fly/swim
1283 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1285 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1288 SV_CheckVelocity(ent);
1289 SV_FlyMove(ent, sv.frametime, NULL);
1290 SV_LinkEdict(ent, false);
1293 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1294 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1300 SV_CheckWaterTransition(ent);
1303 //============================================================================
1311 void SV_Physics (void)
1316 // let the progs know that a new frame has started
1317 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1318 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1319 pr_global_struct->time = sv.time;
1320 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1323 // treat each object in turn
1326 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1331 if (pr_global_struct->force_retouch)
1332 SV_LinkEdict (ent, true); // force retouch even for stationary
1334 if (i > 0 && i <= svs.maxclients)
1336 if (!svs.clients[i-1].spawned)
1339 // call standard client pre-think
1340 SV_CheckVelocity (ent);
1341 pr_global_struct->time = sv.time;
1342 pr_global_struct->self = EDICT_TO_PROG(ent);
1343 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1344 SV_CheckVelocity (ent);
1347 // LordHavoc: merged client and normal entity physics
1348 switch ((int) ent->v->movetype)
1351 SV_Physics_Pusher (ent);
1354 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1355 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1358 case MOVETYPE_FOLLOW:
1359 SV_Physics_Follow (ent);
1361 case MOVETYPE_NOCLIP:
1362 if (SV_RunThink(ent))
1365 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1366 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1368 // relink normal entities here, players always get relinked so don't relink twice
1369 if (!(i > 0 && i <= svs.maxclients))
1370 SV_LinkEdict(ent, false);
1373 SV_Physics_Step (ent);
1376 if (SV_RunThink (ent))
1378 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1379 SV_AddGravity (ent);
1380 SV_CheckStuck (ent);
1382 SV_LinkEdict (ent, true);
1386 case MOVETYPE_BOUNCE:
1387 case MOVETYPE_BOUNCEMISSILE:
1388 case MOVETYPE_FLYMISSILE:
1389 SV_Physics_Toss (ent);
1392 if (i > 0 && i <= svs.maxclients)
1394 if (SV_RunThink (ent))
1396 SV_CheckWater (ent);
1401 SV_Physics_Toss (ent);
1404 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1408 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1410 SV_CheckVelocity (ent);
1412 // call standard player post-think
1413 SV_LinkEdict (ent, true);
1415 SV_CheckVelocity (ent);
1417 pr_global_struct->time = sv.time;
1418 pr_global_struct->self = EDICT_TO_PROG(ent);
1419 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1423 if (pr_global_struct->force_retouch)
1424 pr_global_struct->force_retouch--;
1426 // LordHavoc: endframe support
1429 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1430 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1431 pr_global_struct->time = sv.time;
1432 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1435 sv.time += sv.frametime;
1439 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1442 float gravity, savesolid;
1444 edict_t tempent, *tent;
1449 // copy the vars over
1450 memcpy(&vars, tossent->v, sizeof(entvars_t));
1451 // set up the temp entity to point to the copied vars
1455 savesolid = tossent->v->solid;
1456 tossent->v->solid = SOLID_NOT;
1458 // this has to fetch the field from the original edict, since our copy is truncated
1459 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1460 if (val != NULL && val->_float != 0)
1461 gravity = val->_float;
1464 gravity *= sv_gravity.value * 0.05;
1466 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1468 SV_CheckVelocity (tent);
1469 tent->v->velocity[2] -= gravity;
1470 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1471 VectorScale (tent->v->velocity, 0.05, move);
1472 VectorAdd (tent->v->origin, move, end);
1473 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1474 VectorCopy (trace.endpos, tent->v->origin);
1476 if (trace.fraction < 1 && trace.ent)
1477 if (trace.ent != ignore)
1480 tossent->v->solid = savesolid;
1481 trace.fraction = 0; // not relevant