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);
771 if (thinktime > oldltime && thinktime <= ent->v->ltime)
773 ent->v->nextthink = 0;
774 pr_global_struct->time = sv.time;
775 pr_global_struct->self = EDICT_TO_PROG(ent);
776 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
777 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
784 ===============================================================================
788 ===============================================================================
795 This is a big hack to try and fix the rare case of getting stuck in the world
799 void SV_CheckStuck (edict_t *ent)
804 if (!SV_TestEntityPosition(ent))
806 VectorCopy (ent->v->origin, ent->v->oldorigin);
810 VectorCopy (ent->v->origin, org);
811 VectorCopy (ent->v->oldorigin, ent->v->origin);
812 if (!SV_TestEntityPosition(ent))
814 Con_DPrint("Unstuck.\n");
815 SV_LinkEdict (ent, true);
819 for (z=0 ; z< 18 ; z++)
820 for (i=-1 ; i <= 1 ; i++)
821 for (j=-1 ; j <= 1 ; j++)
823 ent->v->origin[0] = org[0] + i;
824 ent->v->origin[1] = org[1] + j;
825 ent->v->origin[2] = org[2] + z;
826 if (!SV_TestEntityPosition(ent))
828 Con_DPrint("Unstuck.\n");
829 SV_LinkEdict (ent, true);
834 VectorCopy (org, ent->v->origin);
835 Con_DPrint("player is stuck.\n");
844 qboolean SV_CheckWater (edict_t *ent)
849 point[0] = ent->v->origin[0];
850 point[1] = ent->v->origin[1];
851 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
853 ent->v->waterlevel = 0;
854 ent->v->watertype = CONTENTS_EMPTY;
855 cont = SV_PointQ1Contents(point);
856 if (cont <= CONTENTS_WATER)
858 ent->v->watertype = cont;
859 ent->v->waterlevel = 1;
860 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
861 cont = SV_PointQ1Contents(point);
862 if (cont <= CONTENTS_WATER)
864 ent->v->waterlevel = 2;
865 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
866 cont = SV_PointQ1Contents(point);
867 if (cont <= CONTENTS_WATER)
868 ent->v->waterlevel = 3;
872 return ent->v->waterlevel > 1;
881 void SV_WallFriction (edict_t *ent, float *stepnormal)
884 vec3_t forward, into, side;
886 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
887 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
889 // cut the tangential velocity
890 i = DotProduct (stepnormal, ent->v->velocity);
891 VectorScale (stepnormal, i, into);
892 VectorSubtract (ent->v->velocity, into, side);
893 ent->v->velocity[0] = side[0] * (1 + d);
894 ent->v->velocity[1] = side[1] * (1 + d);
899 =====================
902 Player has come to a dead stop, possibly due to the problem with limited
903 float precision at some angle joins in the BSP hull.
905 Try fixing by pushing one pixel in each direction.
907 This is a hack, but in the interest of good gameplay...
908 ======================
910 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
915 VectorCopy (ent->v->origin, oldorg);
918 for (i=0 ; i<8 ; i++)
920 // try pushing a little in an axial direction
923 case 0: dir[0] = 2; dir[1] = 0; break;
924 case 1: dir[0] = 0; dir[1] = 2; break;
925 case 2: dir[0] = -2; dir[1] = 0; break;
926 case 3: dir[0] = 0; dir[1] = -2; break;
927 case 4: dir[0] = 2; dir[1] = 2; break;
928 case 5: dir[0] = -2; dir[1] = 2; break;
929 case 6: dir[0] = 2; dir[1] = -2; break;
930 case 7: dir[0] = -2; dir[1] = -2; break;
933 SV_PushEntity (ent, dir);
935 // retry the original move
936 ent->v->velocity[0] = oldvel[0];
937 ent->v->velocity[1] = oldvel[1];
938 ent->v->velocity[2] = 0;
939 clip = SV_FlyMove (ent, 0.1, NULL);
941 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
942 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
944 Con_DPrint("TryUnstick - success.\n");
948 // go back to the original pos and try again
949 VectorCopy (oldorg, ent->v->origin);
953 VectorClear (ent->v->velocity);
954 Con_DPrint("TryUnstick - failure.\n");
959 =====================
963 ======================
965 void SV_WalkMove (edict_t *ent)
967 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
968 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
971 SV_CheckVelocity(ent);
973 // do a regular slide move unless it looks like you ran into a step
974 oldonground = (int)ent->v->flags & FL_ONGROUND;
975 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
977 VectorCopy (ent->v->origin, start_origin);
978 VectorCopy (ent->v->velocity, start_velocity);
980 clip = SV_FlyMove (ent, sv.frametime, NULL);
982 SV_SetOnGround (ent);
983 SV_CheckVelocity(ent);
985 VectorCopy(ent->v->origin, originalmove_origin);
986 VectorCopy(ent->v->velocity, originalmove_velocity);
987 originalmove_clip = clip;
988 originalmove_flags = (int)ent->v->flags;
989 originalmove_groundentity = ent->v->groundentity;
991 if ((int)ent->v->flags & FL_WATERJUMP)
994 if (sv_nostep.integer)
997 // if move didn't block on a step, return
1000 // if move was not trying to move into the step, return
1001 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1004 if (ent->v->movetype != MOVETYPE_FLY)
1006 // return if gibbed by a trigger
1007 if (ent->v->movetype != MOVETYPE_WALK)
1010 // only step up while jumping if that is enabled
1011 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1012 if (!oldonground && ent->v->waterlevel == 0)
1016 // try moving up and forward to go up a step
1017 // back to start pos
1018 VectorCopy (start_origin, ent->v->origin);
1019 VectorCopy (start_velocity, ent->v->velocity);
1022 VectorClear (upmove);
1023 upmove[2] = sv_stepheight.value;
1024 // FIXME: don't link?
1025 SV_PushEntity(ent, upmove);
1028 ent->v->velocity[2] = 0;
1029 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1030 ent->v->velocity[2] += start_velocity[2];
1032 SV_CheckVelocity(ent);
1034 // check for stuckness, possibly due to the limited precision of floats
1035 // in the clipping hulls
1037 && fabs(originalmove_origin[1] - ent->v->origin[1]) < 0.03125
1038 && fabs(originalmove_origin[0] - ent->v->origin[0]) < 0.03125)
1040 //Con_Printf("wall\n");
1041 // stepping up didn't make any progress, revert to original move
1042 VectorCopy(originalmove_origin, ent->v->origin);
1043 VectorCopy(originalmove_velocity, ent->v->velocity);
1044 //clip = originalmove_clip;
1045 ent->v->flags = originalmove_flags;
1046 ent->v->groundentity = originalmove_groundentity;
1047 // now try to unstick if needed
1048 //clip = SV_TryUnstick (ent, oldvel);
1052 //Con_Printf("step - ");
1054 // extra friction based on view angle
1055 if (clip & 2 && sv_wallfriction.integer)
1056 SV_WallFriction (ent, stepnormal);
1058 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1059 else if (!(sv_gameplayfix_stepdown.integer && ent->v->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->v->flags & FL_ONGROUND)))
1063 VectorClear (downmove);
1064 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1065 // FIXME: don't link?
1066 downtrace = SV_PushEntity (ent, downmove);
1068 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1070 // LordHavoc: disabled this check so you can walk on monsters/players
1071 //if (ent->v->solid == SOLID_BSP)
1073 //Con_Printf("onground\n");
1074 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1075 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1080 //Con_Printf("slope\n");
1081 // if the push down didn't end up on good ground, use the move without
1082 // the step up. This happens near wall / slope combinations, and can
1083 // cause the player to hop up higher on a slope too steep to climb
1084 VectorCopy(originalmove_origin, ent->v->origin);
1085 VectorCopy(originalmove_velocity, ent->v->velocity);
1086 //clip = originalmove_clip;
1087 ent->v->flags = originalmove_flags;
1088 ent->v->groundentity = originalmove_groundentity;
1091 SV_SetOnGround (ent);
1092 SV_CheckVelocity(ent);
1095 //============================================================================
1101 Entities that are "stuck" to another entity
1104 void SV_Physics_Follow (edict_t *ent)
1106 vec3_t vf, vr, vu, angles, v;
1110 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1111 e = PROG_TO_EDICT(ent->v->aiment);
1112 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])
1114 // quick case for no rotation
1115 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1119 angles[0] = -ent->v->punchangle[0];
1120 angles[1] = ent->v->punchangle[1];
1121 angles[2] = ent->v->punchangle[2];
1122 AngleVectors (angles, vf, vr, vu);
1123 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1124 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1125 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1126 angles[0] = -e->v->angles[0];
1127 angles[1] = e->v->angles[1];
1128 angles[2] = e->v->angles[2];
1129 AngleVectors (angles, vf, vr, vu);
1130 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1131 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1132 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1134 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1135 SV_LinkEdict (ent, true);
1139 ==============================================================================
1143 ==============================================================================
1148 SV_CheckWaterTransition
1152 void SV_CheckWaterTransition (edict_t *ent)
1155 cont = SV_PointQ1Contents(ent->v->origin);
1156 if (!ent->v->watertype)
1158 // just spawned here
1159 ent->v->watertype = cont;
1160 ent->v->waterlevel = 1;
1164 // check if the entity crossed into or out of water
1165 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1166 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1168 if (cont <= CONTENTS_WATER)
1170 ent->v->watertype = cont;
1171 ent->v->waterlevel = 1;
1175 ent->v->watertype = CONTENTS_EMPTY;
1176 ent->v->waterlevel = 0;
1184 Toss, bounce, and fly movement. When onground, do nothing.
1187 void SV_Physics_Toss (edict_t *ent)
1191 edict_t *groundentity;
1193 // don't stick to ground if onground and moving upward
1194 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1195 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1197 // if onground, return without moving
1198 if ((int)ent->v->flags & FL_ONGROUND)
1200 if (!sv_gameplayfix_noairborncorpse.integer)
1202 if (ent->v->groundentity == 0)
1204 // if ent was supported by a brush model on previous frame,
1205 // and groundentity is now freed, set groundentity to 0 (floating)
1206 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1207 if (groundentity->v->solid == SOLID_BSP)
1209 ent->e->suspendedinairflag = true;
1212 else if (ent->e->suspendedinairflag && groundentity->e->free)
1214 // leave it suspended in the air
1215 ent->v->groundentity = 0;
1216 ent->e->suspendedinairflag = false;
1220 ent->e->suspendedinairflag = false;
1222 SV_CheckVelocity (ent);
1225 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1226 SV_AddGravity (ent);
1229 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1232 VectorScale (ent->v->velocity, sv.frametime, move);
1233 trace = SV_PushEntity (ent, move);
1237 if (trace.fraction < 1)
1239 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1241 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1242 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1244 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1247 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1248 // LordHavoc: fixed grenades not bouncing when fired down a slope
1249 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1251 d = DotProduct(trace.plane.normal, ent->v->velocity);
1252 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1254 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1255 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1256 VectorClear (ent->v->velocity);
1257 VectorClear (ent->v->avelocity);
1260 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1264 if (trace.plane.normal[2] > 0.7 && ent->v->velocity[2] < 60)
1266 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1267 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1268 VectorClear (ent->v->velocity);
1269 VectorClear (ent->v->avelocity);
1272 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1277 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1278 if (trace.plane.normal[2] > 0.7)
1280 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1281 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1282 VectorClear (ent->v->velocity);
1283 VectorClear (ent->v->avelocity);
1286 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1290 // check for in water
1291 SV_CheckWaterTransition (ent);
1295 ===============================================================================
1299 ===============================================================================
1306 Monsters freefall when they don't have a ground entity, otherwise
1307 all movement is done with discrete steps.
1309 This is also used for objects that have become still on the ground, but
1310 will fall if the floor is pulled out from under them.
1313 void SV_Physics_Step (edict_t *ent)
1315 // don't stick to ground if onground and moving upward
1316 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1317 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1319 // freefall if not onground/fly/swim
1320 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1322 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1325 SV_CheckVelocity(ent);
1326 SV_FlyMove(ent, sv.frametime, NULL);
1327 SV_LinkEdict(ent, true);
1330 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1331 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1334 SV_CheckWaterTransition(ent);
1337 //============================================================================
1345 void SV_Physics (void)
1347 int i, end, retouch;
1352 // let the progs know that a new frame has started
1353 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1354 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1355 pr_global_struct->time = sv.time;
1356 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1358 retouch = pr_global_struct->force_retouch > 0;
1359 end = i = sv_freezenonclients.integer ? svs.maxclients + 1 : sv.num_edicts;
1360 for (i = 0, ent = sv.edicts;i < end;i++, ent = NEXT_EDICT(ent))
1362 if (ent->e->free || ent->v->movetype == MOVETYPE_NONE)
1365 // LordHavoc: merged client and normal entity physics
1366 VectorCopy(ent->v->origin, oldorigin);
1368 switch ((int) ent->v->movetype)
1371 case MOVETYPE_FAKEPUSH:
1372 SV_Physics_Pusher (ent);
1376 case MOVETYPE_FOLLOW:
1377 SV_Physics_Follow (ent);
1379 case MOVETYPE_NOCLIP:
1381 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1382 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1385 SV_Physics_Step (ent);
1388 if (!SV_CheckWater(ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1394 case MOVETYPE_BOUNCE:
1395 case MOVETYPE_BOUNCEMISSILE:
1396 case MOVETYPE_FLYMISSILE:
1397 SV_Physics_Toss(ent);
1400 if (i >= 1 && i <= svs.maxclients && svs.clients[i-1].spawned)
1406 SV_Physics_Toss(ent);
1409 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1413 if (!VectorCompare(ent->v->origin, oldorigin) || retouch)
1414 SV_LinkEdict(ent, true);
1417 for (i = 1, ent = NEXT_EDICT(sv.edicts);i <= svs.maxclients;i++, ent = NEXT_EDICT(ent))
1419 if (ent->e->free || !svs.clients[i-1].spawned)
1422 // call standard client pre-think
1423 SV_CheckVelocity (ent);
1424 pr_global_struct->time = sv.time;
1425 pr_global_struct->self = EDICT_TO_PROG(ent);
1426 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1427 SV_CheckVelocity (ent);
1430 nexttime = sv.time + sv.frametime;
1431 end = i = sv_freezenonclients.integer ? svs.maxclients + 1 : sv.num_edicts;
1432 for (i = 0, ent = sv.edicts;i < end;i++, ent = NEXT_EDICT(ent))
1437 // LordHavoc: merged client and normal entity physics
1438 switch ((int) ent->v->movetype)
1441 case MOVETYPE_FAKEPUSH:
1442 if (ent->v->nextthink && ent->v->ltime > ent->v->nextthink)
1444 ent->v->nextthink = 0;
1445 pr_global_struct->time = sv.time;
1446 pr_global_struct->self = EDICT_TO_PROG(ent);
1447 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1448 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
1452 case MOVETYPE_FOLLOW:
1453 case MOVETYPE_NOCLIP:
1457 case MOVETYPE_BOUNCE:
1458 case MOVETYPE_BOUNCEMISSILE:
1460 case MOVETYPE_FLYMISSILE:
1461 // LordHavoc: manually inlined SV_RunThink here
1462 if (ent->v->nextthink && ent->v->nextthink <= nexttime)
1466 Runs thinking code if time. There is some play in the exact time the think
1467 function will be called, because it is called before any movement is done
1468 in a frame. Not used for pushmove objects, because they must be exact.
1469 Returns false if the entity removed itself.
1471 float thinktime = ent->v->nextthink;
1472 if (thinktime && thinktime < sv.time + sv.frametime)
1474 ent->v->nextthink = 0;
1475 // don't let things stay in the past.
1476 // it is possible to start that way by a trigger with a local time.
1477 pr_global_struct->time = max(thinktime, sv.time);
1478 pr_global_struct->self = EDICT_TO_PROG(ent);
1479 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1480 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
1485 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1490 for (i = 1, ent = NEXT_EDICT(sv.edicts);i <= svs.maxclients;i++, ent = NEXT_EDICT(ent))
1492 if (ent->e->free || !svs.clients[i-1].spawned)
1495 // call standard player post-think
1496 SV_LinkEdict (ent, true);
1497 SV_CheckVelocity (ent);
1498 pr_global_struct->time = sv.time;
1499 pr_global_struct->self = EDICT_TO_PROG(ent);
1500 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1503 if (pr_global_struct->force_retouch > 0)
1504 pr_global_struct->force_retouch = max(0, pr_global_struct->force_retouch - 1);
1506 // LordHavoc: endframe support
1509 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1510 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1511 pr_global_struct->time = sv.time;
1512 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "QC function EndFrame is missing");
1515 if (!sv_freezenonclients.integer)
1516 sv.time += sv.frametime;
1520 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1523 float gravity, savesolid;
1525 edict_t tempent, *tent;
1530 // copy the vars over
1531 memcpy(&vars, tossent->v, sizeof(entvars_t));
1532 // set up the temp entity to point to the copied vars
1536 savesolid = tossent->v->solid;
1537 tossent->v->solid = SOLID_NOT;
1539 // this has to fetch the field from the original edict, since our copy is truncated
1540 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1541 if (val != NULL && val->_float != 0)
1542 gravity = val->_float;
1545 gravity *= sv_gravity.value * 0.05;
1547 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1549 SV_CheckVelocity (tent);
1550 tent->v->velocity[2] -= gravity;
1551 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1552 VectorScale (tent->v->velocity, 0.05, move);
1553 VectorAdd (tent->v->origin, move, end);
1554 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1555 VectorCopy (trace.endpos, tent->v->origin);
1557 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1560 tossent->v->solid = savesolid;
1561 trace.fraction = 0; // not relevant