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"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
53 #define MOVE_EPSILON 0.01
55 void SV_Physics_Toss (edict_t *ent);
57 void SV_Phys_Init (void)
59 Cvar_RegisterVariable(&sv_stepheight);
60 Cvar_RegisterVariable(&sv_jumpstep);
61 Cvar_RegisterVariable(&sv_wallfriction);
62 Cvar_RegisterVariable(&sv_newflymove);
63 Cvar_RegisterVariable(&sv_freezenonclients);
71 void SV_CheckAllEnts (void)
76 // see if any solid entities are inside the final position
77 check = NEXT_EDICT(sv.edicts);
78 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
82 if (check->v->movetype == MOVETYPE_PUSH
83 || check->v->movetype == MOVETYPE_NONE
84 || check->v->movetype == MOVETYPE_FOLLOW
85 || check->v->movetype == MOVETYPE_NOCLIP)
88 if (SV_TestEntityPosition (check))
89 Con_Print("entity in invalid position\n");
98 void SV_CheckVelocity (edict_t *ent)
106 for (i=0 ; i<3 ; i++)
108 if (IS_NAN(ent->v->velocity[i]))
110 Con_Printf("Got a NaN velocity on %s\n", PR_GetString(ent->v->classname));
111 ent->v->velocity[i] = 0;
113 if (IS_NAN(ent->v->origin[i]))
115 Con_Printf("Got a NaN origin on %s\n", PR_GetString(ent->v->classname));
116 ent->v->origin[i] = 0;
120 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
121 wishspeed = DotProduct(ent->v->velocity, ent->v->velocity);
122 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
124 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
125 ent->v->velocity[0] *= wishspeed;
126 ent->v->velocity[1] *= wishspeed;
127 ent->v->velocity[2] *= wishspeed;
135 Two entities have touched, so run their touch functions
138 void SV_Impact (edict_t *e1, edict_t *e2)
140 int old_self, old_other;
142 old_self = pr_global_struct->self;
143 old_other = pr_global_struct->other;
145 pr_global_struct->time = sv.time;
146 if (e1->v->touch && e1->v->solid != SOLID_NOT)
148 pr_global_struct->self = EDICT_TO_PROG(e1);
149 pr_global_struct->other = EDICT_TO_PROG(e2);
150 PR_ExecuteProgram (e1->v->touch, "QC function self.touch is missing");
153 if (e2->v->touch && e2->v->solid != SOLID_NOT)
155 pr_global_struct->self = EDICT_TO_PROG(e2);
156 pr_global_struct->other = EDICT_TO_PROG(e1);
157 PR_ExecuteProgram (e2->v->touch, "QC function self.touch is missing");
160 pr_global_struct->self = old_self;
161 pr_global_struct->other = old_other;
169 Slide off of the impacting object
170 returns the blocked flags (1 = floor, 2 = step / wall)
173 #define STOP_EPSILON 0.1
174 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
179 backoff = -DotProduct (in, normal) * overbounce;
180 VectorMA(in, backoff, normal, out);
182 for (i = 0;i < 3;i++)
183 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
192 The basic solid body movement clip that slides along multiple planes
193 Returns the clipflags if the velocity was modified (hit something solid)
197 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
200 // LordHavoc: increased from 5 to 20
201 #define MAX_CLIP_PLANES 20
202 int SV_FlyMove (edict_t *ent, float time, float *stepnormal)
204 int blocked, bumpcount;
205 edict_t *hackongroundentity;
206 int i, j, impact, numplanes;
208 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
211 VectorCopy(ent->v->velocity, original_velocity);
212 VectorCopy(ent->v->velocity, primal_velocity);
215 hackongroundentity = NULL;
216 for (bumpcount = 0;bumpcount < 8;bumpcount++)
218 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
221 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
222 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
224 //if (trace.fraction < 0.002)
229 VectorCopy(ent->v->origin, start);
230 start[2] += 3;//0.03125;
231 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
232 end[2] += 3;//0.03125;
233 testtrace = SV_Move(start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
234 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->v->velocity) < DotProduct(testtrace.plane.normal, ent->v->velocity)))
236 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
242 for (i = 0;i < numplanes;i++)
244 VectorCopy(ent->v->origin, start);
245 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
246 VectorMA(start, 3, planes[i], start);
247 VectorMA(end, 3, planes[i], end);
248 testtrace = SV_Move(start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
249 if (trace.fraction < testtrace.fraction)
252 VectorCopy(start, ent->v->origin);
257 // VectorAdd(ent->v->origin, planes[j], start);
263 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - sv.edicts, bumpcount, ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2], trace.fraction);
264 if (trace.fraction < 1)
265 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
270 if (trace.startsolid)
272 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
273 // entity is trapped in another solid
274 VectorClear(ent->v->velocity);
279 // break if it moved the entire distance
280 if (trace.fraction == 1)
282 VectorCopy(trace.endpos, ent->v->origin);
287 Host_Error("SV_FlyMove: !trace.ent");
289 if ((int) ent->v->flags & FL_ONGROUND)
291 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
295 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
302 if (trace.plane.normal[2])
304 if (trace.plane.normal[2] > 0.7)
308 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
309 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
311 else if (trace.fraction < 0.001)
312 hackongroundentity = trace.ent;
318 // save the trace for player extrafriction
320 VectorCopy(trace.plane.normal, stepnormal);
323 if (trace.fraction >= 0.001)
325 // actually covered some distance
326 VectorCopy(trace.endpos, ent->v->origin);
327 VectorCopy(ent->v->velocity, original_velocity);
331 // run the impact function
334 SV_Impact(ent, trace.ent);
336 // break if removed by the impact function
341 time_left *= 1 - trace.fraction;
343 // clipped to another plane
344 if (numplanes >= MAX_CLIP_PLANES)
346 // this shouldn't really happen
347 VectorClear(ent->v->velocity);
353 for (i = 0;i < numplanes;i++)
354 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
358 VectorAdd(ent->v->velocity, trace.plane.normal, ent->v->velocity);
363 VectorCopy(trace.plane.normal, planes[numplanes]);
366 if (sv_newflymove.integer)
367 ClipVelocity(ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
370 // modify original_velocity so it parallels all of the clip planes
371 for (i = 0;i < numplanes;i++)
373 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
374 for (j = 0;j < numplanes;j++)
379 if (DotProduct(new_velocity, planes[j]) < 0)
389 // go along this plane
390 VectorCopy(new_velocity, ent->v->velocity);
394 // go along the crease
397 VectorClear(ent->v->velocity);
401 CrossProduct(planes[0], planes[1], dir);
402 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
403 VectorNormalize(dir);
404 d = DotProduct(dir, ent->v->velocity);
405 VectorScale(dir, d, ent->v->velocity);
409 // if original velocity is against the original velocity,
410 // stop dead to avoid tiny occilations in sloping corners
411 if (DotProduct(ent->v->velocity, primal_velocity) <= 0)
413 VectorClear(ent->v->velocity);
418 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - sv.edicts, blocked, ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);
421 // FIXME: this doesn't work well at all, find another solution
422 // if player is ontop of a non-onground floor and made no progress,
423 // set onground anyway (this tends to happen if standing in a wedge)
424 if (bumpcount == 8 && hackongroundentity)
427 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
428 ent->v->groundentity = EDICT_TO_PROG(hackongroundentity);
433 if ((blocked & 1) == 0 && bumpcount > 1)
435 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
436 // flag ONGROUND if there's ground under it
437 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
443 int SV_SetOnGround (edict_t *ent)
447 if ((int)ent->v->flags & FL_ONGROUND)
449 VectorSet(end, ent->v->origin[0], ent->v->origin[1], ent->v->origin[2] - 1);
450 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
451 if (trace.fraction < 1 && trace.plane.normal[2] >= 0.7)
453 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
454 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
466 void SV_AddGravity (edict_t *ent)
471 val = GETEDICTFIELDVALUE(ent, eval_gravity);
472 if (val!=0 && val->_float)
473 ent_gravity = val->_float;
476 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
481 ===============================================================================
485 ===============================================================================
492 Does not change the entities velocity at all
495 trace_t SV_PushEntity (edict_t *ent, vec3_t push)
501 VectorAdd (ent->v->origin, push, end);
503 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
505 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
506 type = MOVE_NOMONSTERS; // only clip against bmodels
510 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, type, ent);
512 VectorCopy (trace.endpos, ent->v->origin);
513 SV_LinkEdict (ent, true);
515 if (trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
516 SV_Impact (ent, trace.ent);
527 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
528 void SV_PushMove (edict_t *pusher, float movetime)
532 float savesolid, movetime2, pushltime;
533 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
535 int numcheckentities;
536 static edict_t *checkentities[MAX_EDICTS];
537 model_t *pushermodel;
540 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])
542 pusher->v->ltime += movetime;
546 switch ((int) pusher->v->solid)
548 // LordHavoc: valid pusher types
552 case SOLID_CORPSE: // LordHavoc: this would be weird...
554 // LordHavoc: no collisions
557 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
558 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
559 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
560 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
561 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
562 pusher->v->ltime += movetime;
563 SV_LinkEdict (pusher, false);
566 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
569 index = (int) pusher->v->modelindex;
570 if (index < 1 || index >= MAX_MODELS)
572 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
575 pushermodel = sv.models[index];
577 movetime2 = movetime;
578 VectorScale(pusher->v->velocity, movetime2, move1);
579 VectorScale(pusher->v->avelocity, movetime2, moveangle);
580 if (moveangle[0] || moveangle[2])
582 for (i = 0;i < 3;i++)
586 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
587 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
591 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
592 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
596 else if (moveangle[1])
598 for (i = 0;i < 3;i++)
602 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
603 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
607 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
608 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
614 for (i = 0;i < 3;i++)
618 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
619 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
623 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
624 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
629 VectorNegate (moveangle, a);
630 AngleVectorsFLU (a, forward, left, up);
632 VectorCopy (pusher->v->origin, pushorig);
633 VectorCopy (pusher->v->angles, pushang);
634 pushltime = pusher->v->ltime;
636 // move the pusher to it's final position
638 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
639 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
640 pusher->v->ltime += movetime;
641 SV_LinkEdict (pusher, false);
643 savesolid = pusher->v->solid;
645 // see if any solid entities are inside the final position
648 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
649 for (e = 1;e < numcheckentities;e++)
651 check = checkentities[e];
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
656 || check->v->movetype == MOVETYPE_FAKEPUSH)
659 // if the entity is standing on the pusher, it will definitely be moved
660 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
661 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
664 if (forward[0] != 1) // quick way to check if any rotation is used
666 VectorSubtract (check->v->origin, pusher->v->origin, org);
667 org2[0] = DotProduct (org, forward);
668 org2[1] = DotProduct (org, left);
669 org2[2] = DotProduct (org, up);
670 VectorSubtract (org2, org, move);
671 VectorAdd (move, move1, move);
674 VectorCopy (move1, move);
676 // remove the onground flag for non-players
677 if (check->v->movetype != MOVETYPE_WALK)
678 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
680 VectorCopy (check->v->origin, check->e->moved_from);
681 VectorCopy (check->v->angles, check->e->moved_fromangles);
682 sv.moved_edicts[num_moved++] = check;
684 // try moving the contacted entity
685 pusher->v->solid = SOLID_NOT;
686 trace = SV_PushEntity (check, move);
687 // FIXME: turn players specially
688 check->v->angles[1] += trace.fraction * moveangle[1];
689 pusher->v->solid = savesolid; // was SOLID_BSP
691 // if it is still inside the pusher, block
692 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
694 // try moving the contacted entity a tiny bit further to account for precision errors
695 pusher->v->solid = SOLID_NOT;
696 VectorScale(move, 0.1, move);
697 SV_PushEntity (check, move);
698 pusher->v->solid = savesolid;
699 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
701 // still inside pusher, so it's really blocked
704 if (check->v->mins[0] == check->v->maxs[0])
706 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
709 check->v->mins[0] = check->v->mins[1] = 0;
710 VectorCopy (check->v->mins, check->v->maxs);
714 VectorCopy (pushorig, pusher->v->origin);
715 VectorCopy (pushang, pusher->v->angles);
716 pusher->v->ltime = pushltime;
717 SV_LinkEdict (pusher, false);
719 // move back any entities we already moved
720 for (i = 0;i < num_moved;i++)
722 ed = sv.moved_edicts[i];
723 VectorCopy (ed->e->moved_from, ed->v->origin);
724 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
725 SV_LinkEdict (ed, false);
728 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
729 if (pusher->v->blocked)
731 pr_global_struct->self = EDICT_TO_PROG(pusher);
732 pr_global_struct->other = EDICT_TO_PROG(check);
733 PR_ExecuteProgram (pusher->v->blocked, "QC function self.blocked is missing");
739 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
740 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
741 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
750 void SV_Physics_Pusher (edict_t *ent)
752 float thinktime, oldltime, movetime;
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;
767 // advances ent->v->ltime if not blocked
768 SV_PushMove (ent, movetime);
770 if (thinktime > oldltime && thinktime <= ent->v->ltime)
772 ent->v->nextthink = 0;
773 pr_global_struct->time = sv.time;
774 pr_global_struct->self = EDICT_TO_PROG(ent);
775 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
776 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
782 ===============================================================================
786 ===============================================================================
793 This is a big hack to try and fix the rare case of getting stuck in the world
797 void SV_CheckStuck (edict_t *ent)
802 if (!SV_TestEntityPosition(ent))
804 VectorCopy (ent->v->origin, ent->v->oldorigin);
808 VectorCopy (ent->v->origin, org);
809 VectorCopy (ent->v->oldorigin, ent->v->origin);
810 if (!SV_TestEntityPosition(ent))
812 Con_DPrint("Unstuck.\n");
813 SV_LinkEdict (ent, true);
817 for (z=0 ; z< 18 ; z++)
818 for (i=-1 ; i <= 1 ; i++)
819 for (j=-1 ; j <= 1 ; j++)
821 ent->v->origin[0] = org[0] + i;
822 ent->v->origin[1] = org[1] + j;
823 ent->v->origin[2] = org[2] + z;
824 if (!SV_TestEntityPosition(ent))
826 Con_DPrint("Unstuck.\n");
827 SV_LinkEdict (ent, true);
832 VectorCopy (org, ent->v->origin);
833 Con_DPrint("player is stuck.\n");
842 qboolean SV_CheckWater (edict_t *ent)
847 point[0] = ent->v->origin[0];
848 point[1] = ent->v->origin[1];
849 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
851 ent->v->waterlevel = 0;
852 ent->v->watertype = CONTENTS_EMPTY;
853 cont = SV_PointQ1Contents(point);
854 if (cont <= CONTENTS_WATER)
856 ent->v->watertype = cont;
857 ent->v->waterlevel = 1;
858 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
859 cont = SV_PointQ1Contents(point);
860 if (cont <= CONTENTS_WATER)
862 ent->v->waterlevel = 2;
863 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
864 cont = SV_PointQ1Contents(point);
865 if (cont <= CONTENTS_WATER)
866 ent->v->waterlevel = 3;
870 return ent->v->waterlevel > 1;
879 void SV_WallFriction (edict_t *ent, float *stepnormal)
882 vec3_t forward, into, side;
884 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
885 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
887 // cut the tangential velocity
888 i = DotProduct (stepnormal, ent->v->velocity);
889 VectorScale (stepnormal, i, into);
890 VectorSubtract (ent->v->velocity, into, side);
891 ent->v->velocity[0] = side[0] * (1 + d);
892 ent->v->velocity[1] = side[1] * (1 + d);
897 =====================
900 Player has come to a dead stop, possibly due to the problem with limited
901 float precision at some angle joins in the BSP hull.
903 Try fixing by pushing one pixel in each direction.
905 This is a hack, but in the interest of good gameplay...
906 ======================
908 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
913 VectorCopy (ent->v->origin, oldorg);
916 for (i=0 ; i<8 ; i++)
918 // try pushing a little in an axial direction
921 case 0: dir[0] = 2; dir[1] = 0; break;
922 case 1: dir[0] = 0; dir[1] = 2; break;
923 case 2: dir[0] = -2; dir[1] = 0; break;
924 case 3: dir[0] = 0; dir[1] = -2; break;
925 case 4: dir[0] = 2; dir[1] = 2; break;
926 case 5: dir[0] = -2; dir[1] = 2; break;
927 case 6: dir[0] = 2; dir[1] = -2; break;
928 case 7: dir[0] = -2; dir[1] = -2; break;
931 SV_PushEntity (ent, dir);
933 // retry the original move
934 ent->v->velocity[0] = oldvel[0];
935 ent->v->velocity[1] = oldvel[1];
936 ent->v->velocity[2] = 0;
937 clip = SV_FlyMove (ent, 0.1, NULL);
939 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
940 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
942 Con_DPrint("TryUnstick - success.\n");
946 // go back to the original pos and try again
947 VectorCopy (oldorg, ent->v->origin);
951 VectorClear (ent->v->velocity);
952 Con_DPrint("TryUnstick - failure.\n");
957 =====================
961 ======================
963 void SV_WalkMove (edict_t *ent)
965 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
966 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
969 SV_CheckVelocity(ent);
971 // do a regular slide move unless it looks like you ran into a step
972 oldonground = (int)ent->v->flags & FL_ONGROUND;
973 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
975 VectorCopy (ent->v->origin, start_origin);
976 VectorCopy (ent->v->velocity, start_velocity);
978 clip = SV_FlyMove (ent, sv.frametime, NULL);
980 SV_SetOnGround (ent);
981 SV_CheckVelocity(ent);
983 VectorCopy(ent->v->origin, originalmove_origin);
984 VectorCopy(ent->v->velocity, originalmove_velocity);
985 originalmove_clip = clip;
986 originalmove_flags = (int)ent->v->flags;
987 originalmove_groundentity = ent->v->groundentity;
989 if ((int)ent->v->flags & FL_WATERJUMP)
992 if (sv_nostep.integer)
995 // if move didn't block on a step, return
998 // if move was not trying to move into the step, return
999 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1002 if (ent->v->movetype != MOVETYPE_FLY)
1004 // return if gibbed by a trigger
1005 if (ent->v->movetype != MOVETYPE_WALK)
1008 // only step up while jumping if that is enabled
1009 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1010 if (!oldonground && ent->v->waterlevel == 0)
1014 // try moving up and forward to go up a step
1015 // back to start pos
1016 VectorCopy (start_origin, ent->v->origin);
1017 VectorCopy (start_velocity, ent->v->velocity);
1020 VectorClear (upmove);
1021 upmove[2] = sv_stepheight.value;
1022 // FIXME: don't link?
1023 SV_PushEntity(ent, upmove);
1026 ent->v->velocity[2] = 0;
1027 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1028 ent->v->velocity[2] += start_velocity[2];
1030 SV_CheckVelocity(ent);
1032 // check for stuckness, possibly due to the limited precision of floats
1033 // in the clipping hulls
1035 && fabs(originalmove_origin[1] - ent->v->origin[1]) < 0.03125
1036 && fabs(originalmove_origin[0] - ent->v->origin[0]) < 0.03125)
1038 //Con_Printf("wall\n");
1039 // stepping up didn't make any progress, revert to original move
1040 VectorCopy(originalmove_origin, ent->v->origin);
1041 VectorCopy(originalmove_velocity, ent->v->velocity);
1042 //clip = originalmove_clip;
1043 ent->v->flags = originalmove_flags;
1044 ent->v->groundentity = originalmove_groundentity;
1045 // now try to unstick if needed
1046 //clip = SV_TryUnstick (ent, oldvel);
1050 //Con_Printf("step - ");
1052 // extra friction based on view angle
1053 if (clip & 2 && sv_wallfriction.integer)
1054 SV_WallFriction (ent, stepnormal);
1056 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1057 else if (!(sv_gameplayfix_stepdown.integer && ent->v->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->v->flags & FL_ONGROUND)))
1061 VectorClear (downmove);
1062 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1063 // FIXME: don't link?
1064 downtrace = SV_PushEntity (ent, downmove);
1066 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1068 // LordHavoc: disabled this check so you can walk on monsters/players
1069 //if (ent->v->solid == SOLID_BSP)
1071 //Con_Printf("onground\n");
1072 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1073 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1078 //Con_Printf("slope\n");
1079 // if the push down didn't end up on good ground, use the move without
1080 // the step up. This happens near wall / slope combinations, and can
1081 // cause the player to hop up higher on a slope too steep to climb
1082 VectorCopy(originalmove_origin, ent->v->origin);
1083 VectorCopy(originalmove_velocity, ent->v->velocity);
1084 //clip = originalmove_clip;
1085 ent->v->flags = originalmove_flags;
1086 ent->v->groundentity = originalmove_groundentity;
1089 SV_SetOnGround (ent);
1090 SV_CheckVelocity(ent);
1093 //============================================================================
1099 Entities that are "stuck" to another entity
1102 void SV_Physics_Follow (edict_t *ent)
1104 vec3_t vf, vr, vu, angles, v;
1108 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1109 e = PROG_TO_EDICT(ent->v->aiment);
1110 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])
1112 // quick case for no rotation
1113 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1117 angles[0] = -ent->v->punchangle[0];
1118 angles[1] = ent->v->punchangle[1];
1119 angles[2] = ent->v->punchangle[2];
1120 AngleVectors (angles, vf, vr, vu);
1121 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1122 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1123 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1124 angles[0] = -e->v->angles[0];
1125 angles[1] = e->v->angles[1];
1126 angles[2] = e->v->angles[2];
1127 AngleVectors (angles, vf, vr, vu);
1128 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1129 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1130 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1132 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1133 SV_LinkEdict (ent, true);
1137 ==============================================================================
1141 ==============================================================================
1146 SV_CheckWaterTransition
1150 void SV_CheckWaterTransition (edict_t *ent)
1153 cont = SV_PointQ1Contents(ent->v->origin);
1154 if (!ent->v->watertype)
1156 // just spawned here
1157 ent->v->watertype = cont;
1158 ent->v->waterlevel = 1;
1162 // check if the entity crossed into or out of water
1163 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1164 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1166 if (cont <= CONTENTS_WATER)
1168 ent->v->watertype = cont;
1169 ent->v->waterlevel = 1;
1173 ent->v->watertype = CONTENTS_EMPTY;
1174 ent->v->waterlevel = 0;
1182 Toss, bounce, and fly movement. When onground, do nothing.
1185 void SV_Physics_Toss (edict_t *ent)
1189 edict_t *groundentity;
1191 // don't stick to ground if onground and moving upward
1192 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1193 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1195 // if onground, return without moving
1196 if ((int)ent->v->flags & FL_ONGROUND)
1198 if (!sv_gameplayfix_noairborncorpse.integer)
1200 if (ent->v->groundentity == 0)
1202 // if ent was supported by a brush model on previous frame,
1203 // and groundentity is now freed, set groundentity to 0 (floating)
1204 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1205 if (groundentity->v->solid == SOLID_BSP)
1207 ent->e->suspendedinairflag = true;
1210 else if (ent->e->suspendedinairflag && groundentity->e->free)
1212 // leave it suspended in the air
1213 ent->v->groundentity = 0;
1214 ent->e->suspendedinairflag = false;
1218 ent->e->suspendedinairflag = false;
1220 SV_CheckVelocity (ent);
1223 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1224 SV_AddGravity (ent);
1227 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1230 VectorScale (ent->v->velocity, sv.frametime, move);
1231 trace = SV_PushEntity (ent, move);
1235 if (trace.fraction < 1)
1237 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1239 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1240 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1242 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1245 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1246 // LordHavoc: fixed grenades not bouncing when fired down a slope
1247 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1249 d = DotProduct(trace.plane.normal, ent->v->velocity);
1250 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1252 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1253 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1254 VectorClear (ent->v->velocity);
1255 VectorClear (ent->v->avelocity);
1258 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1262 if (trace.plane.normal[2] > 0.7 && ent->v->velocity[2] < 60)
1264 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1265 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1266 VectorClear (ent->v->velocity);
1267 VectorClear (ent->v->avelocity);
1270 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1275 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1276 if (trace.plane.normal[2] > 0.7)
1278 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1279 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1280 VectorClear (ent->v->velocity);
1281 VectorClear (ent->v->avelocity);
1284 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1288 // check for in water
1289 SV_CheckWaterTransition (ent);
1293 ===============================================================================
1297 ===============================================================================
1304 Monsters freefall when they don't have a ground entity, otherwise
1305 all movement is done with discrete steps.
1307 This is also used for objects that have become still on the ground, but
1308 will fall if the floor is pulled out from under them.
1311 void SV_Physics_Step (edict_t *ent)
1313 // don't stick to ground if onground and moving upward
1314 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1315 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1317 // freefall if not onground/fly/swim
1318 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1320 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1323 SV_CheckVelocity(ent);
1324 SV_FlyMove(ent, sv.frametime, NULL);
1325 SV_LinkEdict(ent, true);
1328 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1329 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1332 SV_CheckWaterTransition(ent);
1335 //============================================================================
1343 void SV_Physics (void)
1345 int i, end, retouch;
1350 // let the progs know that a new frame has started
1351 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1352 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1353 pr_global_struct->time = sv.time;
1354 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1356 retouch = pr_global_struct->force_retouch > 0;
1357 end = i = sv_freezenonclients.integer ? svs.maxclients + 1 : sv.num_edicts;
1358 for (i = 0, ent = sv.edicts;i < end;i++, ent = NEXT_EDICT(ent))
1360 if (ent->e->free || ent->v->movetype == MOVETYPE_NONE)
1363 // LordHavoc: merged client and normal entity physics
1364 VectorCopy(ent->v->origin, oldorigin);
1366 switch ((int) ent->v->movetype)
1369 case MOVETYPE_FAKEPUSH:
1370 SV_Physics_Pusher (ent);
1374 case MOVETYPE_FOLLOW:
1375 SV_Physics_Follow (ent);
1377 case MOVETYPE_NOCLIP:
1379 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1380 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1383 SV_Physics_Step (ent);
1386 if (!SV_CheckWater(ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1392 case MOVETYPE_BOUNCE:
1393 case MOVETYPE_BOUNCEMISSILE:
1394 case MOVETYPE_FLYMISSILE:
1395 SV_Physics_Toss(ent);
1398 if (i >= 1 && i <= svs.maxclients && svs.clients[i-1].spawned)
1404 SV_Physics_Toss(ent);
1407 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1411 if (!VectorCompare(ent->v->origin, oldorigin) || retouch)
1412 SV_LinkEdict(ent, true);
1415 for (i = 1, ent = NEXT_EDICT(sv.edicts);i <= svs.maxclients;i++, ent = NEXT_EDICT(ent))
1417 if (ent->e->free || !svs.clients[i-1].spawned)
1420 // call standard client pre-think
1421 SV_CheckVelocity (ent);
1422 pr_global_struct->time = sv.time;
1423 pr_global_struct->self = EDICT_TO_PROG(ent);
1424 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1425 SV_CheckVelocity (ent);
1428 nexttime = sv.time + sv.frametime;
1429 end = i = sv_freezenonclients.integer ? svs.maxclients + 1 : sv.num_edicts;
1430 for (i = 0, ent = sv.edicts;i < end;i++, ent = NEXT_EDICT(ent))
1435 // LordHavoc: merged client and normal entity physics
1436 switch ((int) ent->v->movetype)
1439 case MOVETYPE_FAKEPUSH:
1440 // push thinks are called from SV_Physics_Pusher
1443 case MOVETYPE_FOLLOW:
1444 case MOVETYPE_NOCLIP:
1448 case MOVETYPE_BOUNCE:
1449 case MOVETYPE_BOUNCEMISSILE:
1451 case MOVETYPE_FLYMISSILE:
1452 // LordHavoc: manually inlined SV_RunThink here
1453 if (ent->v->nextthink && ent->v->nextthink <= nexttime)
1457 Runs thinking code if time. There is some play in the exact time the think
1458 function will be called, because it is called before any movement is done
1459 in a frame. Not used for pushmove objects, because they must be exact.
1460 Returns false if the entity removed itself.
1462 float thinktime = ent->v->nextthink;
1463 if (thinktime && thinktime < sv.time + sv.frametime)
1465 ent->v->nextthink = 0;
1466 // don't let things stay in the past.
1467 // it is possible to start that way by a trigger with a local time.
1468 pr_global_struct->time = max(thinktime, sv.time);
1469 pr_global_struct->self = EDICT_TO_PROG(ent);
1470 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1471 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
1476 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1481 for (i = 1, ent = NEXT_EDICT(sv.edicts);i <= svs.maxclients;i++, ent = NEXT_EDICT(ent))
1483 if (ent->e->free || !svs.clients[i-1].spawned)
1486 // call standard player post-think
1487 SV_LinkEdict (ent, true);
1488 SV_CheckVelocity (ent);
1489 pr_global_struct->time = sv.time;
1490 pr_global_struct->self = EDICT_TO_PROG(ent);
1491 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1494 if (pr_global_struct->force_retouch > 0)
1495 pr_global_struct->force_retouch = max(0, pr_global_struct->force_retouch - 1);
1497 // LordHavoc: endframe support
1500 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1501 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1502 pr_global_struct->time = sv.time;
1503 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "QC function EndFrame is missing");
1506 if (!sv_freezenonclients.integer)
1507 sv.time += sv.frametime;
1511 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1514 float gravity, savesolid;
1516 edict_t tempent, *tent;
1521 // copy the vars over
1522 memcpy(&vars, tossent->v, sizeof(entvars_t));
1523 // set up the temp entity to point to the copied vars
1527 savesolid = tossent->v->solid;
1528 tossent->v->solid = SOLID_NOT;
1530 // this has to fetch the field from the original edict, since our copy is truncated
1531 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1532 if (val != NULL && val->_float != 0)
1533 gravity = val->_float;
1536 gravity *= sv_gravity.value * 0.05;
1538 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1540 SV_CheckVelocity (tent);
1541 tent->v->velocity[2] -= gravity;
1542 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1543 VectorScale (tent->v->velocity, 0.05, move);
1544 VectorAdd (tent->v->origin, move, end);
1545 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1546 VectorCopy (trace.endpos, tent->v->origin);
1548 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1551 tossent->v->solid = savesolid;
1552 trace.fraction = 0; // not relevant