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 #if COLLISIONPARANOID >= 1
254 VectorCopy(trace.endpos, temp);
255 endstuck = SV_Move(temp, ent->v->mins, ent->v->maxs, temp, MOVE_WORLDONLY, ent).startsolid;
256 #if COLLISIONPARANOID < 2
257 if (trace.startsolid || endstuck)
260 Con_Printf("%s{e%i:%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "\002" : "", ent - sv.edicts, 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" : "");
261 //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]);
263 Cbuf_AddText("disconnect\n");
269 if (trace.startsolid)
271 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
272 // entity is trapped in another solid
273 VectorClear(ent->v->velocity);
278 if (trace.fraction >= 0.001)
280 // actually covered some distance
281 VectorCopy (trace.endpos, ent->v->origin);
284 // break if it moved the entire distance
285 if (trace.fraction == 1)
289 Host_Error ("SV_FlyMove: !trace.ent");
291 if ((int) ent->v->flags & FL_ONGROUND)
293 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
297 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
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 if (!trace.plane.normal[2])
315 // save the trace for player extrafriction
317 VectorCopy(trace.plane.normal, stepnormal);
320 // run the impact function
323 SV_Impact (ent, trace.ent);
325 // break if removed by the impact function
330 time *= 1 - trace.fraction;
332 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
339 int i, j, blocked, impact, numplanes, bumpcount, numbumps;
341 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
347 VectorCopy (ent->v->velocity, original_velocity);
348 VectorCopy (ent->v->velocity, primal_velocity);
353 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
355 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
358 for (i=0 ; i<3 ; i++)
359 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
361 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
364 if (trace.startsolid)
366 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
367 // entity is trapped in another solid
368 VectorClear(ent->v->velocity);
373 if (trace.fraction > 0)
375 // actually covered some distance
376 VectorCopy (trace.endpos, ent->v->origin);
377 VectorCopy (ent->v->velocity, original_velocity);
381 // break if it moved the entire distance
382 if (trace.fraction == 1)
386 Host_Error ("SV_FlyMove: !trace.ent");
388 if ((int) ent->v->flags & FL_ONGROUND)
390 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
394 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
401 if (trace.plane.normal[2] > 0.7)
405 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
406 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
408 if (!trace.plane.normal[2])
412 // save the trace for player extrafriction
414 VectorCopy(trace.plane.normal, stepnormal);
417 // run the impact function
420 SV_Impact (ent, trace.ent);
422 // break if removed by the impact function
428 time_left -= time_left * trace.fraction;
430 // clipped to another plane
431 if (numplanes >= MAX_CLIP_PLANES)
433 // this shouldn't really happen
434 VectorClear(ent->v->velocity);
438 VectorCopy (trace.plane.normal, planes[numplanes]);
441 // modify original_velocity so it parallels all of the clip planes
442 for (i=0 ; i<numplanes ; i++)
444 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
445 for (j=0 ; j<numplanes ; j++)
449 if (DotProduct (new_velocity, planes[j]) < 0)
458 // go along this plane
459 VectorCopy (new_velocity, ent->v->velocity);
463 // go along the crease
466 VectorClear(ent->v->velocity);
469 CrossProduct (planes[0], planes[1], dir);
470 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
471 VectorNormalize(dir);
472 d = DotProduct (dir, ent->v->velocity);
473 VectorScale (dir, d, ent->v->velocity);
476 // if original velocity is against the original velocity,
477 // stop dead to avoid tiny occilations in sloping corners
478 if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
480 VectorClear(ent->v->velocity);
496 void SV_AddGravity (edict_t *ent)
501 val = GETEDICTFIELDVALUE(ent, eval_gravity);
502 if (val!=0 && val->_float)
503 ent_gravity = val->_float;
506 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
511 ===============================================================================
515 ===============================================================================
522 Does not change the entities velocity at all
525 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
530 VectorAdd (ent->v->origin, push, end);
532 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
533 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
534 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
535 // only clip against bmodels
536 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
538 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
539 #if COLLISIONPARANOID >= 1
543 VectorCopy(trace.endpos, temp);
544 endstuck = SV_Move(temp, ent->v->mins, ent->v->maxs, temp, MOVE_WORLDONLY, ent).startsolid;
545 #if COLLISIONPARANOID < 2
546 if (trace.startsolid || endstuck)
549 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "\002" : "", ent - sv.edicts, 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" : "");
550 //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]);
552 Cbuf_AddText("disconnect\n");
557 VectorCopy (trace.endpos, ent->v->origin);
558 // FIXME: turn players specially
559 ent->v->angles[1] += trace.fraction * pushangles[1];
560 SV_LinkEdict (ent, true);
562 if (trace.fraction < 1 && trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
563 SV_Impact (ent, trace.ent);
574 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
575 void SV_PushMove (edict_t *pusher, float movetime)
579 float savesolid, movetime2, pushltime;
580 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
582 model_t *pushermodel;
584 switch ((int) pusher->v->solid)
586 // LordHavoc: valid pusher types
590 case SOLID_CORPSE: // LordHavoc: this would be weird...
592 // LordHavoc: no collisions
595 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
596 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
597 pusher->v->ltime += movetime;
598 SV_LinkEdict (pusher, false);
601 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
603 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])
605 pusher->v->ltime += movetime;
608 index = (int) pusher->v->modelindex;
609 if (index < 1 || index >= MAX_MODELS)
610 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
611 pushermodel = sv.models[index];
613 movetime2 = movetime;
614 VectorScale(pusher->v->velocity, movetime2, move1);
615 VectorScale(pusher->v->avelocity, movetime2, moveangle);
616 if (moveangle[0] || moveangle[2])
618 for (i = 0;i < 3;i++)
622 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
623 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
627 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
628 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
632 else if (moveangle[1])
634 for (i = 0;i < 3;i++)
638 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
639 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
643 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
644 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
650 for (i = 0;i < 3;i++)
654 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
655 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
659 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
660 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
665 VectorNegate (moveangle, a);
666 AngleVectorsFLU (a, forward, left, up);
668 VectorCopy (pusher->v->origin, pushorig);
669 VectorCopy (pusher->v->angles, pushang);
670 pushltime = pusher->v->ltime;
672 // move the pusher to it's final position
674 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
675 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
676 pusher->v->ltime += movetime;
677 SV_LinkEdict (pusher, false);
679 savesolid = pusher->v->solid;
681 // see if any solid entities are inside the final position
683 check = NEXT_EDICT(sv.edicts);
684 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
688 if (check->v->movetype == MOVETYPE_PUSH
689 || check->v->movetype == MOVETYPE_NONE
690 || check->v->movetype == MOVETYPE_FOLLOW
691 || check->v->movetype == MOVETYPE_NOCLIP)
694 // if the entity is standing on the pusher, it will definitely be moved
695 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
697 if (check->v->absmin[0] >= maxs[0]
698 || check->v->absmax[0] <= mins[0]
699 || check->v->absmin[1] >= maxs[1]
700 || check->v->absmax[1] <= mins[1]
701 || check->v->absmin[2] >= maxs[2]
702 || check->v->absmax[2] <= mins[2])
705 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
709 if (forward[0] != 1) // quick way to check if any rotation is used
711 VectorSubtract (check->v->origin, pusher->v->origin, org);
712 org2[0] = DotProduct (org, forward);
713 org2[1] = DotProduct (org, left);
714 org2[2] = DotProduct (org, up);
715 VectorSubtract (org2, org, move);
716 VectorAdd (move, move1, move);
719 VectorCopy (move1, move);
721 // remove the onground flag for non-players
722 if (check->v->movetype != MOVETYPE_WALK)
723 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
725 VectorCopy (check->v->origin, check->e->moved_from);
726 VectorCopy (check->v->angles, check->e->moved_fromangles);
727 sv.moved_edicts[num_moved++] = check;
729 // try moving the contacted entity
730 pusher->v->solid = SOLID_NOT;
731 SV_PushEntity (check, move, moveangle);
732 pusher->v->solid = savesolid; // was SOLID_BSP
734 // if it is still inside the pusher, block
735 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
737 // try moving the contacted entity a tiny bit further to account for precision errors
738 pusher->v->solid = SOLID_NOT;
739 VectorScale(move, 0.1, move);
740 SV_PushEntity (check, move, vec3_origin);
741 pusher->v->solid = savesolid;
742 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
744 // still inside pusher, so it's really blocked
747 if (check->v->mins[0] == check->v->maxs[0])
749 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
752 check->v->mins[0] = check->v->mins[1] = 0;
753 VectorCopy (check->v->mins, check->v->maxs);
757 VectorCopy (pushorig, pusher->v->origin);
758 VectorCopy (pushang, pusher->v->angles);
759 pusher->v->ltime = pushltime;
760 SV_LinkEdict (pusher, false);
762 // move back any entities we already moved
763 for (i = 0;i < num_moved;i++)
765 ed = sv.moved_edicts[i];
766 VectorCopy (ed->e->moved_from, ed->v->origin);
767 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
768 SV_LinkEdict (ed, false);
771 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
772 if (pusher->v->blocked)
774 pr_global_struct->self = EDICT_TO_PROG(pusher);
775 pr_global_struct->other = EDICT_TO_PROG(check);
776 PR_ExecuteProgram (pusher->v->blocked, "");
790 void SV_Physics_Pusher (edict_t *ent)
792 float thinktime, oldltime, movetime;
794 oldltime = ent->v->ltime;
796 thinktime = ent->v->nextthink;
797 if (thinktime < ent->v->ltime + sv.frametime)
799 movetime = thinktime - ent->v->ltime;
804 movetime = sv.frametime;
807 // advances ent->v->ltime if not blocked
808 SV_PushMove (ent, movetime);
810 if (thinktime > oldltime && thinktime <= ent->v->ltime)
812 ent->v->nextthink = 0;
813 pr_global_struct->time = sv.time;
814 pr_global_struct->self = EDICT_TO_PROG(ent);
815 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
816 PR_ExecuteProgram (ent->v->think, "NULL think function");
822 ===============================================================================
826 ===============================================================================
833 This is a big hack to try and fix the rare case of getting stuck in the world
837 void SV_CheckStuck (edict_t *ent)
842 if (!SV_TestEntityPosition(ent))
844 VectorCopy (ent->v->origin, ent->v->oldorigin);
848 VectorCopy (ent->v->origin, org);
849 VectorCopy (ent->v->oldorigin, ent->v->origin);
850 if (!SV_TestEntityPosition(ent))
852 Con_DPrintf ("Unstuck.\n");
853 SV_LinkEdict (ent, true);
857 for (z=0 ; z< 18 ; z++)
858 for (i=-1 ; i <= 1 ; i++)
859 for (j=-1 ; j <= 1 ; j++)
861 ent->v->origin[0] = org[0] + i;
862 ent->v->origin[1] = org[1] + j;
863 ent->v->origin[2] = org[2] + z;
864 if (!SV_TestEntityPosition(ent))
866 Con_DPrintf ("Unstuck.\n");
867 SV_LinkEdict (ent, true);
872 VectorCopy (org, ent->v->origin);
873 Con_DPrintf ("player is stuck.\n");
882 qboolean SV_CheckWater (edict_t *ent)
887 point[0] = ent->v->origin[0];
888 point[1] = ent->v->origin[1];
889 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
891 ent->v->waterlevel = 0;
892 ent->v->watertype = CONTENTS_EMPTY;
893 cont = SV_PointQ1Contents(point);
894 if (cont <= CONTENTS_WATER)
896 ent->v->watertype = cont;
897 ent->v->waterlevel = 1;
898 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
899 cont = SV_PointQ1Contents(point);
900 if (cont <= CONTENTS_WATER)
902 ent->v->waterlevel = 2;
903 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
904 cont = SV_PointQ1Contents(point);
905 if (cont <= CONTENTS_WATER)
906 ent->v->waterlevel = 3;
910 return ent->v->waterlevel > 1;
919 void SV_WallFriction (edict_t *ent, float *stepnormal)
922 vec3_t forward, into, side;
924 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
925 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
927 // cut the tangential velocity
928 i = DotProduct (stepnormal, ent->v->velocity);
929 VectorScale (stepnormal, i, into);
930 VectorSubtract (ent->v->velocity, into, side);
931 ent->v->velocity[0] = side[0] * (1 + d);
932 ent->v->velocity[1] = side[1] * (1 + d);
937 =====================
940 Player has come to a dead stop, possibly due to the problem with limited
941 float precision at some angle joins in the BSP hull.
943 Try fixing by pushing one pixel in each direction.
945 This is a hack, but in the interest of good gameplay...
946 ======================
948 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
953 VectorCopy (ent->v->origin, oldorg);
956 for (i=0 ; i<8 ; i++)
958 // try pushing a little in an axial direction
961 case 0: dir[0] = 2; dir[1] = 0; break;
962 case 1: dir[0] = 0; dir[1] = 2; break;
963 case 2: dir[0] = -2; dir[1] = 0; break;
964 case 3: dir[0] = 0; dir[1] = -2; break;
965 case 4: dir[0] = 2; dir[1] = 2; break;
966 case 5: dir[0] = -2; dir[1] = 2; break;
967 case 6: dir[0] = 2; dir[1] = -2; break;
968 case 7: dir[0] = -2; dir[1] = -2; break;
971 SV_PushEntity (ent, dir, vec3_origin);
973 // retry the original move
974 ent->v->velocity[0] = oldvel[0];
975 ent->v->velocity[1] = oldvel[1];
976 ent->v->velocity[2] = 0;
977 clip = SV_FlyMove (ent, 0.1, NULL);
979 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
980 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
982 Con_DPrintf("TryUnstick - success.\n");
986 // go back to the original pos and try again
987 VectorCopy (oldorg, ent->v->origin);
991 VectorClear (ent->v->velocity);
992 Con_Printf("TryUnstick - failure.\n");
997 =====================
1000 Only used by players
1001 ======================
1003 void SV_WalkMove (edict_t *ent)
1005 int clip, oldonground;
1006 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
1009 SV_CheckVelocity(ent);
1011 // do a regular slide move unless it looks like you ran into a step
1012 oldonground = (int)ent->v->flags & FL_ONGROUND;
1013 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1015 VectorCopy (ent->v->origin, oldorg);
1016 VectorCopy (ent->v->velocity, oldvel);
1018 clip = SV_FlyMove (ent, sv.frametime, NULL);
1020 SV_CheckVelocity(ent);
1022 // if move didn't block on a step, return
1026 if (ent->v->movetype != MOVETYPE_FLY)
1028 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
1029 // don't stair up while jumping
1032 if (ent->v->movetype != MOVETYPE_WALK)
1033 // gibbed by a trigger
1037 SV_CheckVelocity(ent);
1039 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1042 VectorCopy (ent->v->origin, nosteporg);
1043 VectorCopy (ent->v->velocity, nostepvel);
1045 // try moving up and forward to go up a step
1046 // back to start pos
1047 VectorCopy (oldorg, ent->v->origin);
1049 VectorClear (upmove);
1050 VectorClear (downmove);
1051 upmove[2] = sv_stepheight.value;
1052 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1055 // FIXME: don't link?
1056 SV_PushEntity (ent, upmove, vec3_origin);
1059 ent->v->velocity[0] = oldvel[0];
1060 ent->v->velocity[1] = oldvel[1];
1061 ent->v->velocity[2] = 0;
1062 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1063 ent->v->velocity[2] += oldvel[2];
1065 // check for stuckness, possibly due to the limited precision of floats
1066 // in the clipping hulls
1068 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1069 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1070 // stepping up didn't make any progress
1071 clip = SV_TryUnstick (ent, oldvel);
1073 // extra friction based on view angle
1074 if (clip & 2 && sv_wallfriction.integer)
1075 SV_WallFriction (ent, stepnormal);
1078 // FIXME: don't link?
1079 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1081 if (downtrace.plane.normal[2] > 0.7)
1083 // LordHavoc: disabled this so you can walk on monsters/players
1084 //if (ent->v->solid == SOLID_BSP)
1086 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1087 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1092 // if the push down didn't end up on good ground, use the move without
1093 // the step up. This happens near wall / slope combinations, and can
1094 // cause the player to hop up higher on a slope too steep to climb
1095 VectorCopy (nosteporg, ent->v->origin);
1096 VectorCopy (nostepvel, ent->v->velocity);
1099 SV_CheckVelocity(ent);
1102 //============================================================================
1108 Entities that are "stuck" to another entity
1111 void SV_Physics_Follow (edict_t *ent)
1113 vec3_t vf, vr, vu, angles, v;
1117 if (!SV_RunThink (ent))
1120 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1121 e = PROG_TO_EDICT(ent->v->aiment);
1122 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])
1124 // quick case for no rotation
1125 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1129 angles[0] = -ent->v->punchangle[0];
1130 angles[1] = ent->v->punchangle[1];
1131 angles[2] = ent->v->punchangle[2];
1132 AngleVectors (angles, vf, vr, vu);
1133 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1134 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1135 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1136 angles[0] = -e->v->angles[0];
1137 angles[1] = e->v->angles[1];
1138 angles[2] = e->v->angles[2];
1139 AngleVectors (angles, vf, vr, vu);
1140 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1141 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1142 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1144 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1145 SV_LinkEdict (ent, true);
1149 ==============================================================================
1153 ==============================================================================
1158 SV_CheckWaterTransition
1162 void SV_CheckWaterTransition (edict_t *ent)
1165 cont = SV_PointQ1Contents(ent->v->origin);
1166 if (!ent->v->watertype)
1168 // just spawned here
1169 ent->v->watertype = cont;
1170 ent->v->waterlevel = 1;
1174 // check if the entity crossed into or out of water
1175 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1176 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1178 if (cont <= CONTENTS_WATER)
1180 ent->v->watertype = cont;
1181 ent->v->waterlevel = 1;
1185 ent->v->watertype = CONTENTS_EMPTY;
1186 ent->v->waterlevel = 0;
1194 Toss, bounce, and fly movement. When onground, do nothing.
1197 void SV_Physics_Toss (edict_t *ent)
1201 edict_t *groundentity;
1204 if (!SV_RunThink (ent))
1207 // if onground, return without moving
1208 if ((int)ent->v->flags & FL_ONGROUND)
1210 if (ent->v->groundentity == 0)
1212 // if ent was supported by a brush model on previous frame,
1213 // and groundentity is now freed, set groundentity to 0 (floating)
1214 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1215 if (groundentity->v->solid == SOLID_BSP)
1217 ent->e->suspendedinairflag = true;
1220 else if (ent->e->suspendedinairflag && groundentity->e->free)
1222 // leave it suspended in the air
1223 ent->v->groundentity = 0;
1224 ent->e->suspendedinairflag = false;
1228 ent->e->suspendedinairflag = false;
1230 SV_CheckVelocity (ent);
1233 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1234 SV_AddGravity (ent);
1237 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1240 VectorScale (ent->v->velocity, sv.frametime, move);
1241 trace = SV_PushEntity (ent, move, vec3_origin);
1245 if (trace.fraction < 1)
1247 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1249 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1250 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1252 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1254 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1255 // LordHavoc: fixed grenades not bouncing when fired down a slope
1256 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1258 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1259 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1260 VectorClear (ent->v->velocity);
1261 VectorClear (ent->v->avelocity);
1264 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1268 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1269 if (trace.plane.normal[2] > 0.7)
1271 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1272 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1273 VectorClear (ent->v->velocity);
1274 VectorClear (ent->v->avelocity);
1277 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1281 // check for in water
1282 SV_CheckWaterTransition (ent);
1286 ===============================================================================
1290 ===============================================================================
1297 Monsters freefall when they don't have a ground entity, otherwise
1298 all movement is done with discrete steps.
1300 This is also used for objects that have become still on the ground, but
1301 will fall if the floor is pulled out from under them.
1304 void SV_Physics_Step (edict_t *ent)
1306 // freefall if not onground/fly/swim
1307 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1309 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1312 SV_CheckVelocity(ent);
1313 SV_FlyMove(ent, sv.frametime, NULL);
1314 SV_LinkEdict(ent, false);
1317 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1318 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1324 SV_CheckWaterTransition(ent);
1327 //============================================================================
1335 void SV_Physics (void)
1340 // let the progs know that a new frame has started
1341 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1342 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1343 pr_global_struct->time = sv.time;
1344 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1347 // treat each object in turn
1350 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1355 if (pr_global_struct->force_retouch)
1356 SV_LinkEdict (ent, true); // force retouch even for stationary
1358 if (i > 0 && i <= svs.maxclients)
1360 if (!svs.clients[i-1].spawned)
1363 // call standard client pre-think
1364 SV_CheckVelocity (ent);
1365 pr_global_struct->time = sv.time;
1366 pr_global_struct->self = EDICT_TO_PROG(ent);
1367 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1368 SV_CheckVelocity (ent);
1371 // LordHavoc: merged client and normal entity physics
1372 switch ((int) ent->v->movetype)
1375 SV_Physics_Pusher (ent);
1378 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1379 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1382 case MOVETYPE_FOLLOW:
1383 SV_Physics_Follow (ent);
1385 case MOVETYPE_NOCLIP:
1386 if (SV_RunThink(ent))
1389 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1390 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1392 // relink normal entities here, players always get relinked so don't relink twice
1393 if (!(i > 0 && i <= svs.maxclients))
1394 SV_LinkEdict(ent, false);
1397 SV_Physics_Step (ent);
1400 if (SV_RunThink (ent))
1402 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1403 SV_AddGravity (ent);
1404 SV_CheckStuck (ent);
1406 SV_LinkEdict (ent, true);
1410 case MOVETYPE_BOUNCE:
1411 case MOVETYPE_BOUNCEMISSILE:
1412 case MOVETYPE_FLYMISSILE:
1413 SV_Physics_Toss (ent);
1416 if (i > 0 && i <= svs.maxclients)
1418 if (SV_RunThink (ent))
1420 SV_CheckWater (ent);
1425 SV_Physics_Toss (ent);
1428 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1432 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1434 SV_CheckVelocity (ent);
1436 // call standard player post-think
1437 SV_LinkEdict (ent, true);
1439 SV_CheckVelocity (ent);
1441 pr_global_struct->time = sv.time;
1442 pr_global_struct->self = EDICT_TO_PROG(ent);
1443 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1447 if (pr_global_struct->force_retouch)
1448 pr_global_struct->force_retouch--;
1450 // LordHavoc: endframe support
1453 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1454 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1455 pr_global_struct->time = sv.time;
1456 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1459 sv.time += sv.frametime;
1463 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1466 float gravity, savesolid;
1468 edict_t tempent, *tent;
1473 // copy the vars over
1474 memcpy(&vars, tossent->v, sizeof(entvars_t));
1475 // set up the temp entity to point to the copied vars
1479 savesolid = tossent->v->solid;
1480 tossent->v->solid = SOLID_NOT;
1482 // this has to fetch the field from the original edict, since our copy is truncated
1483 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1484 if (val != NULL && val->_float != 0)
1485 gravity = val->_float;
1488 gravity *= sv_gravity.value * 0.05;
1490 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1492 SV_CheckVelocity (tent);
1493 tent->v->velocity[2] -= gravity;
1494 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1495 VectorScale (tent->v->velocity, 0.05, move);
1496 VectorAdd (tent->v->origin, move, end);
1497 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1498 VectorCopy (trace.endpos, tent->v->origin);
1500 if (trace.fraction < 1 && trace.ent)
1501 if (trace.ent != ignore)
1504 tossent->v->solid = savesolid;
1505 trace.fraction = 0; // not relevant