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;
562 switch ((int) pusher->v->solid)
564 // LordHavoc: valid pusher types
568 case SOLID_CORPSE: // LordHavoc: this would be weird...
570 // LordHavoc: no collisions
573 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
574 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
575 pusher->v->ltime += movetime;
576 SV_LinkEdict (pusher, false);
579 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
581 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])
583 pusher->v->ltime += movetime;
586 index = (int) pusher->v->modelindex;
587 if (index < 1 || index >= MAX_MODELS)
588 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
589 pushermodel = sv.models[index];
591 movetime2 = movetime;
592 VectorScale(pusher->v->velocity, movetime2, move1);
593 VectorScale(pusher->v->avelocity, movetime2, moveangle);
594 if (moveangle[0] || moveangle[2])
596 for (i = 0;i < 3;i++)
600 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
601 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
605 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
606 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
610 else if (moveangle[1])
612 for (i = 0;i < 3;i++)
616 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
617 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
621 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
622 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
628 for (i = 0;i < 3;i++)
632 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
633 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
637 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
638 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
643 VectorNegate (moveangle, a);
644 AngleVectorsFLU (a, forward, left, up);
646 VectorCopy (pusher->v->origin, pushorig);
647 VectorCopy (pusher->v->angles, pushang);
648 pushltime = pusher->v->ltime;
650 // move the pusher to it's final position
652 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
653 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
654 pusher->v->ltime += movetime;
655 SV_LinkEdict (pusher, false);
657 savesolid = pusher->v->solid;
659 // see if any solid entities are inside the final position
661 check = NEXT_EDICT(sv.edicts);
662 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
666 if (check->v->movetype == MOVETYPE_PUSH
667 || check->v->movetype == MOVETYPE_NONE
668 || check->v->movetype == MOVETYPE_FOLLOW
669 || check->v->movetype == MOVETYPE_NOCLIP)
672 // if the entity is standing on the pusher, it will definitely be moved
673 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
675 if (check->v->absmin[0] >= maxs[0]
676 || check->v->absmax[0] <= mins[0]
677 || check->v->absmin[1] >= maxs[1]
678 || check->v->absmax[1] <= mins[1]
679 || check->v->absmin[2] >= maxs[2]
680 || check->v->absmax[2] <= mins[2])
683 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
687 if (forward[0] != 1) // quick way to check if any rotation is used
689 VectorSubtract (check->v->origin, pusher->v->origin, org);
690 org2[0] = DotProduct (org, forward);
691 org2[1] = DotProduct (org, left);
692 org2[2] = DotProduct (org, up);
693 VectorSubtract (org2, org, move);
694 VectorAdd (move, move1, move);
697 VectorCopy (move1, move);
699 // remove the onground flag for non-players
700 if (check->v->movetype != MOVETYPE_WALK)
701 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
703 VectorCopy (check->v->origin, check->e->moved_from);
704 VectorCopy (check->v->angles, check->e->moved_fromangles);
705 sv.moved_edicts[num_moved++] = check;
707 // try moving the contacted entity
708 pusher->v->solid = SOLID_NOT;
709 SV_PushEntity (check, move, moveangle);
710 pusher->v->solid = savesolid; // was SOLID_BSP
712 // if it is still inside the pusher, block
713 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
715 // try moving the contacted entity a tiny bit further to account for precision errors
716 pusher->v->solid = SOLID_NOT;
717 VectorScale(move, 0.1, move);
718 SV_PushEntity (check, move, vec3_origin);
719 pusher->v->solid = savesolid;
720 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
722 // still inside pusher, so it's really blocked
725 if (check->v->mins[0] == check->v->maxs[0])
727 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
730 check->v->mins[0] = check->v->mins[1] = 0;
731 VectorCopy (check->v->mins, check->v->maxs);
735 VectorCopy (pushorig, pusher->v->origin);
736 VectorCopy (pushang, pusher->v->angles);
737 pusher->v->ltime = pushltime;
738 SV_LinkEdict (pusher, false);
740 // move back any entities we already moved
741 for (i = 0;i < num_moved;i++)
743 ed = sv.moved_edicts[i];
744 VectorCopy (ed->e->moved_from, ed->v->origin);
745 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
746 SV_LinkEdict (ed, false);
749 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
750 if (pusher->v->blocked)
752 pr_global_struct->self = EDICT_TO_PROG(pusher);
753 pr_global_struct->other = EDICT_TO_PROG(check);
754 PR_ExecuteProgram (pusher->v->blocked, "");
768 void SV_Physics_Pusher (edict_t *ent)
770 float thinktime, oldltime, movetime;
772 oldltime = ent->v->ltime;
774 thinktime = ent->v->nextthink;
775 if (thinktime < ent->v->ltime + sv.frametime)
777 movetime = thinktime - ent->v->ltime;
782 movetime = sv.frametime;
785 // advances ent->v->ltime if not blocked
786 SV_PushMove (ent, movetime);
788 if (thinktime > oldltime && thinktime <= ent->v->ltime)
790 ent->v->nextthink = 0;
791 pr_global_struct->time = sv.time;
792 pr_global_struct->self = EDICT_TO_PROG(ent);
793 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
794 PR_ExecuteProgram (ent->v->think, "NULL think function");
800 ===============================================================================
804 ===============================================================================
811 This is a big hack to try and fix the rare case of getting stuck in the world
815 void SV_CheckStuck (edict_t *ent)
820 if (!SV_TestEntityPosition(ent))
822 VectorCopy (ent->v->origin, ent->v->oldorigin);
826 VectorCopy (ent->v->origin, org);
827 VectorCopy (ent->v->oldorigin, ent->v->origin);
828 if (!SV_TestEntityPosition(ent))
830 Con_DPrintf ("Unstuck.\n");
831 SV_LinkEdict (ent, true);
835 for (z=0 ; z< 18 ; z++)
836 for (i=-1 ; i <= 1 ; i++)
837 for (j=-1 ; j <= 1 ; j++)
839 ent->v->origin[0] = org[0] + i;
840 ent->v->origin[1] = org[1] + j;
841 ent->v->origin[2] = org[2] + z;
842 if (!SV_TestEntityPosition(ent))
844 Con_DPrintf ("Unstuck.\n");
845 SV_LinkEdict (ent, true);
850 VectorCopy (org, ent->v->origin);
851 Con_DPrintf ("player is stuck.\n");
860 qboolean SV_CheckWater (edict_t *ent)
865 point[0] = ent->v->origin[0];
866 point[1] = ent->v->origin[1];
867 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
869 ent->v->waterlevel = 0;
870 ent->v->watertype = CONTENTS_EMPTY;
871 cont = SV_PointQ1Contents(point);
872 if (cont <= CONTENTS_WATER)
874 ent->v->watertype = cont;
875 ent->v->waterlevel = 1;
876 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
877 cont = SV_PointQ1Contents(point);
878 if (cont <= CONTENTS_WATER)
880 ent->v->waterlevel = 2;
881 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
882 cont = SV_PointQ1Contents(point);
883 if (cont <= CONTENTS_WATER)
884 ent->v->waterlevel = 3;
888 return ent->v->waterlevel > 1;
897 void SV_WallFriction (edict_t *ent, float *stepnormal)
900 vec3_t forward, into, side;
902 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
903 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
905 // cut the tangential velocity
906 i = DotProduct (stepnormal, ent->v->velocity);
907 VectorScale (stepnormal, i, into);
908 VectorSubtract (ent->v->velocity, into, side);
909 ent->v->velocity[0] = side[0] * (1 + d);
910 ent->v->velocity[1] = side[1] * (1 + d);
915 =====================
918 Player has come to a dead stop, possibly due to the problem with limited
919 float precision at some angle joins in the BSP hull.
921 Try fixing by pushing one pixel in each direction.
923 This is a hack, but in the interest of good gameplay...
924 ======================
926 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
931 VectorCopy (ent->v->origin, oldorg);
934 for (i=0 ; i<8 ; i++)
936 // try pushing a little in an axial direction
939 case 0: dir[0] = 2; dir[1] = 0; break;
940 case 1: dir[0] = 0; dir[1] = 2; break;
941 case 2: dir[0] = -2; dir[1] = 0; break;
942 case 3: dir[0] = 0; dir[1] = -2; break;
943 case 4: dir[0] = 2; dir[1] = 2; break;
944 case 5: dir[0] = -2; dir[1] = 2; break;
945 case 6: dir[0] = 2; dir[1] = -2; break;
946 case 7: dir[0] = -2; dir[1] = -2; break;
949 SV_PushEntity (ent, dir, vec3_origin);
951 // retry the original move
952 ent->v->velocity[0] = oldvel[0];
953 ent->v->velocity[1] = oldvel[1];
954 ent->v->velocity[2] = 0;
955 clip = SV_FlyMove (ent, 0.1, NULL);
957 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
958 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
961 // go back to the original pos and try again
962 VectorCopy (oldorg, ent->v->origin);
966 VectorClear (ent->v->velocity);
971 =====================
975 ======================
977 void SV_WalkMove (edict_t *ent)
979 int clip, oldonground;
980 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
983 SV_CheckVelocity(ent);
985 // do a regular slide move unless it looks like you ran into a step
986 oldonground = (int)ent->v->flags & FL_ONGROUND;
987 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
989 VectorCopy (ent->v->origin, oldorg);
990 VectorCopy (ent->v->velocity, oldvel);
992 clip = SV_FlyMove (ent, sv.frametime, NULL);
994 SV_CheckVelocity(ent);
996 // if move didn't block on a step, return
1000 if (ent->v->movetype != MOVETYPE_FLY)
1002 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
1003 // don't stair up while jumping
1006 if (ent->v->movetype != MOVETYPE_WALK)
1007 // gibbed by a trigger
1011 SV_CheckVelocity(ent);
1013 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1016 VectorCopy (ent->v->origin, nosteporg);
1017 VectorCopy (ent->v->velocity, nostepvel);
1019 // try moving up and forward to go up a step
1020 // back to start pos
1021 VectorCopy (oldorg, ent->v->origin);
1023 VectorClear (upmove);
1024 VectorClear (downmove);
1025 upmove[2] = sv_stepheight.value;
1026 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1029 // FIXME: don't link?
1030 SV_PushEntity (ent, upmove, vec3_origin);
1033 ent->v->velocity[0] = oldvel[0];
1034 ent->v->velocity[1] = oldvel[1];
1035 ent->v->velocity[2] = 0;
1036 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1037 ent->v->velocity[2] += oldvel[2];
1039 // check for stuckness, possibly due to the limited precision of floats
1040 // in the clipping hulls
1042 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1043 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1044 // stepping up didn't make any progress
1045 clip = SV_TryUnstick (ent, oldvel);
1047 // extra friction based on view angle
1048 if (clip & 2 && sv_wallfriction.integer)
1049 SV_WallFriction (ent, stepnormal);
1052 // FIXME: don't link?
1053 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1055 if (downtrace.plane.normal[2] > 0.7)
1057 // LordHavoc: disabled this so you can walk on monsters/players
1058 //if (ent->v->solid == SOLID_BSP)
1060 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1061 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1066 // if the push down didn't end up on good ground, use the move without
1067 // the step up. This happens near wall / slope combinations, and can
1068 // cause the player to hop up higher on a slope too steep to climb
1069 VectorCopy (nosteporg, ent->v->origin);
1070 VectorCopy (nostepvel, ent->v->velocity);
1073 SV_CheckVelocity(ent);
1076 //============================================================================
1082 Entities that are "stuck" to another entity
1085 void SV_Physics_Follow (edict_t *ent)
1087 vec3_t vf, vr, vu, angles, v;
1091 if (!SV_RunThink (ent))
1094 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1095 e = PROG_TO_EDICT(ent->v->aiment);
1096 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])
1098 // quick case for no rotation
1099 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1103 angles[0] = -ent->v->punchangle[0];
1104 angles[1] = ent->v->punchangle[1];
1105 angles[2] = ent->v->punchangle[2];
1106 AngleVectors (angles, vf, vr, vu);
1107 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1108 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1109 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1110 angles[0] = -e->v->angles[0];
1111 angles[1] = e->v->angles[1];
1112 angles[2] = e->v->angles[2];
1113 AngleVectors (angles, vf, vr, vu);
1114 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1115 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1116 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1118 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1119 SV_LinkEdict (ent, true);
1123 ==============================================================================
1127 ==============================================================================
1132 SV_CheckWaterTransition
1136 void SV_CheckWaterTransition (edict_t *ent)
1139 cont = SV_PointQ1Contents(ent->v->origin);
1140 if (!ent->v->watertype)
1142 // just spawned here
1143 ent->v->watertype = cont;
1144 ent->v->waterlevel = 1;
1148 // check if the entity crossed into or out of water
1149 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1150 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1152 if (cont <= CONTENTS_WATER)
1154 ent->v->watertype = cont;
1155 ent->v->waterlevel = 1;
1159 ent->v->watertype = CONTENTS_EMPTY;
1160 ent->v->waterlevel = 0;
1168 Toss, bounce, and fly movement. When onground, do nothing.
1171 void SV_Physics_Toss (edict_t *ent)
1175 edict_t *groundentity;
1178 if (!SV_RunThink (ent))
1181 // if onground, return without moving
1182 if ((int)ent->v->flags & FL_ONGROUND)
1184 if (ent->v->groundentity == 0)
1186 // if ent was supported by a brush model on previous frame,
1187 // and groundentity is now freed, set groundentity to 0 (floating)
1188 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1189 if (groundentity->v->solid == SOLID_BSP)
1191 ent->e->suspendedinairflag = true;
1194 else if (ent->e->suspendedinairflag && groundentity->e->free)
1196 // leave it suspended in the air
1197 ent->v->groundentity = 0;
1198 ent->e->suspendedinairflag = false;
1202 ent->e->suspendedinairflag = false;
1204 SV_CheckVelocity (ent);
1207 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1208 SV_AddGravity (ent);
1211 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1214 VectorScale (ent->v->velocity, sv.frametime, move);
1215 trace = SV_PushEntity (ent, move, vec3_origin);
1219 if (trace.fraction < 1)
1221 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1223 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1224 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1226 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1228 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1229 // LordHavoc: fixed grenades not bouncing when fired down a slope
1230 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1232 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1233 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1234 VectorClear (ent->v->velocity);
1235 VectorClear (ent->v->avelocity);
1238 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1242 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1243 if (trace.plane.normal[2] > 0.7)
1245 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1246 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1247 VectorClear (ent->v->velocity);
1248 VectorClear (ent->v->avelocity);
1251 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1255 // check for in water
1256 SV_CheckWaterTransition (ent);
1260 ===============================================================================
1264 ===============================================================================
1271 Monsters freefall when they don't have a ground entity, otherwise
1272 all movement is done with discrete steps.
1274 This is also used for objects that have become still on the ground, but
1275 will fall if the floor is pulled out from under them.
1278 void SV_Physics_Step (edict_t *ent)
1280 // freefall if not onground/fly/swim
1281 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1283 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1286 SV_CheckVelocity(ent);
1287 SV_FlyMove(ent, sv.frametime, NULL);
1288 SV_LinkEdict(ent, false);
1291 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1292 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1298 SV_CheckWaterTransition(ent);
1301 //============================================================================
1309 void SV_Physics (void)
1314 // let the progs know that a new frame has started
1315 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1316 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1317 pr_global_struct->time = sv.time;
1318 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1321 // treat each object in turn
1324 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1329 if (pr_global_struct->force_retouch)
1330 SV_LinkEdict (ent, true); // force retouch even for stationary
1332 if (i > 0 && i <= svs.maxclients)
1334 if (!svs.clients[i-1].spawned)
1337 // call standard client pre-think
1338 SV_CheckVelocity (ent);
1339 pr_global_struct->time = sv.time;
1340 pr_global_struct->self = EDICT_TO_PROG(ent);
1341 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1342 SV_CheckVelocity (ent);
1345 // LordHavoc: merged client and normal entity physics
1346 switch ((int) ent->v->movetype)
1349 SV_Physics_Pusher (ent);
1352 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1353 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1356 case MOVETYPE_FOLLOW:
1357 SV_Physics_Follow (ent);
1359 case MOVETYPE_NOCLIP:
1360 if (SV_RunThink(ent))
1363 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1364 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1366 // relink normal entities here, players always get relinked so don't relink twice
1367 if (!(i > 0 && i <= svs.maxclients))
1368 SV_LinkEdict(ent, false);
1371 SV_Physics_Step (ent);
1374 if (SV_RunThink (ent))
1376 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1377 SV_AddGravity (ent);
1378 SV_CheckStuck (ent);
1380 SV_LinkEdict (ent, true);
1384 case MOVETYPE_BOUNCE:
1385 case MOVETYPE_BOUNCEMISSILE:
1386 case MOVETYPE_FLYMISSILE:
1387 SV_Physics_Toss (ent);
1390 if (i > 0 && i <= svs.maxclients)
1392 if (SV_RunThink (ent))
1394 SV_CheckWater (ent);
1399 SV_Physics_Toss (ent);
1402 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1406 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1408 SV_CheckVelocity (ent);
1410 // call standard player post-think
1411 SV_LinkEdict (ent, true);
1413 SV_CheckVelocity (ent);
1415 pr_global_struct->time = sv.time;
1416 pr_global_struct->self = EDICT_TO_PROG(ent);
1417 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1421 if (pr_global_struct->force_retouch)
1422 pr_global_struct->force_retouch--;
1424 // LordHavoc: endframe support
1427 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1428 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1429 pr_global_struct->time = sv.time;
1430 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1433 sv.time += sv.frametime;
1437 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1440 float gravity, savesolid;
1442 edict_t tempent, *tent;
1447 // copy the vars over
1448 memcpy(&vars, tossent->v, sizeof(entvars_t));
1449 // set up the temp entity to point to the copied vars
1453 savesolid = tossent->v->solid;
1454 tossent->v->solid = SOLID_NOT;
1456 // this has to fetch the field from the original edict, since our copy is truncated
1457 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1458 if (val != NULL && val->_float != 0)
1459 gravity = val->_float;
1462 gravity *= sv_gravity.value * 0.05;
1464 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1466 SV_CheckVelocity (tent);
1467 tent->v->velocity[2] -= gravity;
1468 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1469 VectorScale (tent->v->velocity, 0.05, move);
1470 VectorAdd (tent->v->origin, move, end);
1471 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1472 VectorCopy (trace.endpos, tent->v->origin);
1474 if (trace.fraction < 1 && trace.ent)
1475 if (trace.ent != ignore)
1478 tossent->v->solid = savesolid;
1479 trace.fraction = 0; // not relevant