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 Runs thinking code if time. There is some play in the exact time the think
136 function will be called, because it is called before any movement is done
137 in a frame. Not used for pushmove objects, because they must be exact.
138 Returns false if the entity removed itself.
141 qboolean SV_RunThink (edict_t *ent)
145 thinktime = ent->v->nextthink;
146 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
149 // don't let things stay in the past.
150 // it is possible to start that way by a trigger with a local time.
151 if (thinktime < sv.time)
154 ent->v->nextthink = 0;
155 pr_global_struct->time = thinktime;
156 pr_global_struct->self = EDICT_TO_PROG(ent);
157 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
158 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
159 return !ent->e->free;
166 Two entities have touched, so run their touch functions
169 void SV_Impact (edict_t *e1, edict_t *e2)
171 int old_self, old_other;
173 old_self = pr_global_struct->self;
174 old_other = pr_global_struct->other;
176 pr_global_struct->time = sv.time;
177 if (e1->v->touch && e1->v->solid != SOLID_NOT)
179 pr_global_struct->self = EDICT_TO_PROG(e1);
180 pr_global_struct->other = EDICT_TO_PROG(e2);
181 PR_ExecuteProgram (e1->v->touch, "QC function self.touch is missing");
184 if (e2->v->touch && e2->v->solid != SOLID_NOT)
186 pr_global_struct->self = EDICT_TO_PROG(e2);
187 pr_global_struct->other = EDICT_TO_PROG(e1);
188 PR_ExecuteProgram (e2->v->touch, "QC function self.touch is missing");
191 pr_global_struct->self = old_self;
192 pr_global_struct->other = old_other;
200 Slide off of the impacting object
201 returns the blocked flags (1 = floor, 2 = step / wall)
204 #define STOP_EPSILON 0.1
205 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
210 backoff = -DotProduct (in, normal) * overbounce;
211 VectorMA(in, backoff, normal, out);
213 for (i = 0;i < 3;i++)
214 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
223 The basic solid body movement clip that slides along multiple planes
224 Returns the clipflags if the velocity was modified (hit something solid)
228 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
231 // LordHavoc: increased from 5 to 20
232 #define MAX_CLIP_PLANES 20
233 int SV_FlyMove (edict_t *ent, float time, float *stepnormal)
235 int blocked, bumpcount;
236 edict_t *hackongroundentity;
237 int i, j, impact, numplanes;
239 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
242 VectorCopy(ent->v->velocity, original_velocity);
243 VectorCopy(ent->v->velocity, primal_velocity);
246 hackongroundentity = NULL;
247 for (bumpcount = 0;bumpcount < 8;bumpcount++)
249 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
252 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
253 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
255 //if (trace.fraction < 0.002)
260 VectorCopy(ent->v->origin, start);
261 start[2] += 3;//0.03125;
262 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
263 end[2] += 3;//0.03125;
264 testtrace = SV_Move(start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
265 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->v->velocity) < DotProduct(testtrace.plane.normal, ent->v->velocity)))
267 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
273 for (i = 0;i < numplanes;i++)
275 VectorCopy(ent->v->origin, start);
276 VectorMA(ent->v->origin, time_left, ent->v->velocity, end);
277 VectorMA(start, 3, planes[i], start);
278 VectorMA(end, 3, planes[i], end);
279 testtrace = SV_Move(start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
280 if (trace.fraction < testtrace.fraction)
283 VectorCopy(start, ent->v->origin);
288 // VectorAdd(ent->v->origin, planes[j], start);
294 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);
295 if (trace.fraction < 1)
296 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
301 if (trace.startsolid)
303 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
304 // entity is trapped in another solid
305 VectorClear(ent->v->velocity);
310 // break if it moved the entire distance
311 if (trace.fraction == 1)
313 VectorCopy(trace.endpos, ent->v->origin);
318 Host_Error("SV_FlyMove: !trace.ent");
320 if ((int) ent->v->flags & FL_ONGROUND)
322 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
326 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
333 if (trace.plane.normal[2])
335 if (trace.plane.normal[2] > 0.7)
339 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
340 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
342 else if (trace.fraction < 0.001)
343 hackongroundentity = trace.ent;
349 // save the trace for player extrafriction
351 VectorCopy(trace.plane.normal, stepnormal);
354 if (trace.fraction >= 0.001)
356 // actually covered some distance
357 VectorCopy(trace.endpos, ent->v->origin);
358 VectorCopy(ent->v->velocity, original_velocity);
362 // run the impact function
365 SV_Impact(ent, trace.ent);
367 // break if removed by the impact function
372 time_left *= 1 - trace.fraction;
374 // clipped to another plane
375 if (numplanes >= MAX_CLIP_PLANES)
377 // this shouldn't really happen
378 VectorClear(ent->v->velocity);
384 for (i = 0;i < numplanes;i++)
385 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
389 VectorAdd(ent->v->velocity, trace.plane.normal, ent->v->velocity);
394 VectorCopy(trace.plane.normal, planes[numplanes]);
397 if (sv_newflymove.integer)
398 ClipVelocity(ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
401 // modify original_velocity so it parallels all of the clip planes
402 for (i = 0;i < numplanes;i++)
404 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
405 for (j = 0;j < numplanes;j++)
410 if (DotProduct(new_velocity, planes[j]) < 0)
420 // go along this plane
421 VectorCopy(new_velocity, ent->v->velocity);
425 // go along the crease
428 VectorClear(ent->v->velocity);
432 CrossProduct(planes[0], planes[1], dir);
433 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
434 VectorNormalize(dir);
435 d = DotProduct(dir, ent->v->velocity);
436 VectorScale(dir, d, ent->v->velocity);
440 // if original velocity is against the original velocity,
441 // stop dead to avoid tiny occilations in sloping corners
442 if (DotProduct(ent->v->velocity, primal_velocity) <= 0)
444 VectorClear(ent->v->velocity);
449 //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]);
452 // FIXME: this doesn't work well at all, find another solution
453 // if player is ontop of a non-onground floor and made no progress,
454 // set onground anyway (this tends to happen if standing in a wedge)
455 if (bumpcount == 8 && hackongroundentity)
458 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
459 ent->v->groundentity = EDICT_TO_PROG(hackongroundentity);
464 if ((blocked & 1) == 0 && bumpcount > 1)
466 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
467 // flag ONGROUND if there's ground under it
468 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
474 int SV_SetOnGround (edict_t *ent)
478 if ((int)ent->v->flags & FL_ONGROUND)
480 VectorSet(end, ent->v->origin[0], ent->v->origin[1], ent->v->origin[2] - 1);
481 trace = SV_Move(ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
482 if (trace.fraction < 1 && trace.plane.normal[2] >= 0.7)
484 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
485 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
497 void SV_AddGravity (edict_t *ent)
502 val = GETEDICTFIELDVALUE(ent, eval_gravity);
503 if (val!=0 && val->_float)
504 ent_gravity = val->_float;
507 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
512 ===============================================================================
516 ===============================================================================
523 Does not change the entities velocity at all
526 trace_t SV_PushEntity (edict_t *ent, vec3_t push)
532 VectorAdd (ent->v->origin, push, end);
534 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
536 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
537 type = MOVE_NOMONSTERS; // only clip against bmodels
541 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, type, ent);
543 VectorCopy (trace.endpos, ent->v->origin);
544 SV_LinkEdict (ent, true);
546 if (trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
547 SV_Impact (ent, trace.ent);
558 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
559 void SV_PushMove (edict_t *pusher, float movetime)
563 float savesolid, movetime2, pushltime;
564 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
566 int numcheckentities;
567 static edict_t *checkentities[MAX_EDICTS];
568 model_t *pushermodel;
571 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])
573 pusher->v->ltime += movetime;
577 switch ((int) pusher->v->solid)
579 // LordHavoc: valid pusher types
583 case SOLID_CORPSE: // LordHavoc: this would be weird...
585 // LordHavoc: no collisions
588 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
589 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
590 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
591 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
592 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
593 pusher->v->ltime += movetime;
594 SV_LinkEdict (pusher, false);
597 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
600 index = (int) pusher->v->modelindex;
601 if (index < 1 || index >= MAX_MODELS)
603 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
606 pushermodel = sv.models[index];
608 movetime2 = movetime;
609 VectorScale(pusher->v->velocity, movetime2, move1);
610 VectorScale(pusher->v->avelocity, movetime2, moveangle);
611 if (moveangle[0] || moveangle[2])
613 for (i = 0;i < 3;i++)
617 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
618 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
622 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
623 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
627 else if (moveangle[1])
629 for (i = 0;i < 3;i++)
633 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
634 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
638 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
639 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
645 for (i = 0;i < 3;i++)
649 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
650 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
654 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
655 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
660 VectorNegate (moveangle, a);
661 AngleVectorsFLU (a, forward, left, up);
663 VectorCopy (pusher->v->origin, pushorig);
664 VectorCopy (pusher->v->angles, pushang);
665 pushltime = pusher->v->ltime;
667 // move the pusher to it's final position
669 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
670 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
671 pusher->v->ltime += movetime;
672 SV_LinkEdict (pusher, false);
674 savesolid = pusher->v->solid;
676 // see if any solid entities are inside the final position
679 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
680 for (e = 1;e < numcheckentities;e++)
682 check = checkentities[e];
683 if (check->v->movetype == MOVETYPE_PUSH
684 || check->v->movetype == MOVETYPE_NONE
685 || check->v->movetype == MOVETYPE_FOLLOW
686 || check->v->movetype == MOVETYPE_NOCLIP
687 || check->v->movetype == MOVETYPE_FAKEPUSH)
690 // if the entity is standing on the pusher, it will definitely be moved
691 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
692 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
695 if (forward[0] != 1) // quick way to check if any rotation is used
697 VectorSubtract (check->v->origin, pusher->v->origin, org);
698 org2[0] = DotProduct (org, forward);
699 org2[1] = DotProduct (org, left);
700 org2[2] = DotProduct (org, up);
701 VectorSubtract (org2, org, move);
702 VectorAdd (move, move1, move);
705 VectorCopy (move1, move);
707 // remove the onground flag for non-players
708 if (check->v->movetype != MOVETYPE_WALK)
709 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
711 VectorCopy (check->v->origin, check->e->moved_from);
712 VectorCopy (check->v->angles, check->e->moved_fromangles);
713 sv.moved_edicts[num_moved++] = check;
715 // try moving the contacted entity
716 pusher->v->solid = SOLID_NOT;
717 trace = SV_PushEntity (check, move);
718 // FIXME: turn players specially
719 check->v->angles[1] += trace.fraction * moveangle[1];
720 pusher->v->solid = savesolid; // was SOLID_BSP
722 // if it is still inside the pusher, block
723 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
725 // try moving the contacted entity a tiny bit further to account for precision errors
726 pusher->v->solid = SOLID_NOT;
727 VectorScale(move, 0.1, move);
728 SV_PushEntity (check, move);
729 pusher->v->solid = savesolid;
730 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
732 // still inside pusher, so it's really blocked
735 if (check->v->mins[0] == check->v->maxs[0])
737 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
740 check->v->mins[0] = check->v->mins[1] = 0;
741 VectorCopy (check->v->mins, check->v->maxs);
745 VectorCopy (pushorig, pusher->v->origin);
746 VectorCopy (pushang, pusher->v->angles);
747 pusher->v->ltime = pushltime;
748 SV_LinkEdict (pusher, false);
750 // move back any entities we already moved
751 for (i = 0;i < num_moved;i++)
753 ed = sv.moved_edicts[i];
754 VectorCopy (ed->e->moved_from, ed->v->origin);
755 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
756 SV_LinkEdict (ed, false);
759 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
760 if (pusher->v->blocked)
762 pr_global_struct->self = EDICT_TO_PROG(pusher);
763 pr_global_struct->other = EDICT_TO_PROG(check);
764 PR_ExecuteProgram (pusher->v->blocked, "QC function self.blocked is missing");
770 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
771 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
772 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
781 void SV_Physics_Pusher (edict_t *ent)
783 float thinktime, oldltime, movetime;
785 oldltime = ent->v->ltime;
787 thinktime = ent->v->nextthink;
788 if (thinktime < ent->v->ltime + sv.frametime)
790 movetime = thinktime - ent->v->ltime;
795 movetime = sv.frametime;
798 // advances ent->v->ltime if not blocked
799 SV_PushMove (ent, movetime);
801 if (thinktime > oldltime && thinktime <= ent->v->ltime)
803 ent->v->nextthink = 0;
804 pr_global_struct->time = sv.time;
805 pr_global_struct->self = EDICT_TO_PROG(ent);
806 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
807 PR_ExecuteProgram (ent->v->think, "QC function self.think is missing");
813 ===============================================================================
817 ===============================================================================
824 This is a big hack to try and fix the rare case of getting stuck in the world
828 void SV_CheckStuck (edict_t *ent)
833 if (!SV_TestEntityPosition(ent))
835 VectorCopy (ent->v->origin, ent->v->oldorigin);
839 VectorCopy (ent->v->origin, org);
840 VectorCopy (ent->v->oldorigin, ent->v->origin);
841 if (!SV_TestEntityPosition(ent))
843 Con_DPrint("Unstuck.\n");
844 SV_LinkEdict (ent, true);
848 for (z=0 ; z< 18 ; z++)
849 for (i=-1 ; i <= 1 ; i++)
850 for (j=-1 ; j <= 1 ; j++)
852 ent->v->origin[0] = org[0] + i;
853 ent->v->origin[1] = org[1] + j;
854 ent->v->origin[2] = org[2] + z;
855 if (!SV_TestEntityPosition(ent))
857 Con_DPrint("Unstuck.\n");
858 SV_LinkEdict (ent, true);
863 VectorCopy (org, ent->v->origin);
864 Con_DPrint("player is stuck.\n");
873 qboolean SV_CheckWater (edict_t *ent)
878 point[0] = ent->v->origin[0];
879 point[1] = ent->v->origin[1];
880 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
882 ent->v->waterlevel = 0;
883 ent->v->watertype = CONTENTS_EMPTY;
884 cont = SV_PointQ1Contents(point);
885 if (cont <= CONTENTS_WATER)
887 ent->v->watertype = cont;
888 ent->v->waterlevel = 1;
889 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
890 cont = SV_PointQ1Contents(point);
891 if (cont <= CONTENTS_WATER)
893 ent->v->waterlevel = 2;
894 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
895 cont = SV_PointQ1Contents(point);
896 if (cont <= CONTENTS_WATER)
897 ent->v->waterlevel = 3;
901 return ent->v->waterlevel > 1;
910 void SV_WallFriction (edict_t *ent, float *stepnormal)
913 vec3_t forward, into, side;
915 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
916 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
918 // cut the tangential velocity
919 i = DotProduct (stepnormal, ent->v->velocity);
920 VectorScale (stepnormal, i, into);
921 VectorSubtract (ent->v->velocity, into, side);
922 ent->v->velocity[0] = side[0] * (1 + d);
923 ent->v->velocity[1] = side[1] * (1 + d);
928 =====================
931 Player has come to a dead stop, possibly due to the problem with limited
932 float precision at some angle joins in the BSP hull.
934 Try fixing by pushing one pixel in each direction.
936 This is a hack, but in the interest of good gameplay...
937 ======================
939 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
944 VectorCopy (ent->v->origin, oldorg);
947 for (i=0 ; i<8 ; i++)
949 // try pushing a little in an axial direction
952 case 0: dir[0] = 2; dir[1] = 0; break;
953 case 1: dir[0] = 0; dir[1] = 2; break;
954 case 2: dir[0] = -2; dir[1] = 0; break;
955 case 3: dir[0] = 0; dir[1] = -2; break;
956 case 4: dir[0] = 2; dir[1] = 2; break;
957 case 5: dir[0] = -2; dir[1] = 2; break;
958 case 6: dir[0] = 2; dir[1] = -2; break;
959 case 7: dir[0] = -2; dir[1] = -2; break;
962 SV_PushEntity (ent, dir);
964 // retry the original move
965 ent->v->velocity[0] = oldvel[0];
966 ent->v->velocity[1] = oldvel[1];
967 ent->v->velocity[2] = 0;
968 clip = SV_FlyMove (ent, 0.1, NULL);
970 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
971 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
973 Con_DPrint("TryUnstick - success.\n");
977 // go back to the original pos and try again
978 VectorCopy (oldorg, ent->v->origin);
982 VectorClear (ent->v->velocity);
983 Con_DPrint("TryUnstick - failure.\n");
988 =====================
992 ======================
994 void SV_WalkMove (edict_t *ent)
996 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
997 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1000 SV_CheckVelocity(ent);
1002 // do a regular slide move unless it looks like you ran into a step
1003 oldonground = (int)ent->v->flags & FL_ONGROUND;
1004 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1006 VectorCopy (ent->v->origin, start_origin);
1007 VectorCopy (ent->v->velocity, start_velocity);
1009 clip = SV_FlyMove (ent, sv.frametime, NULL);
1011 SV_SetOnGround (ent);
1012 SV_CheckVelocity(ent);
1014 VectorCopy(ent->v->origin, originalmove_origin);
1015 VectorCopy(ent->v->velocity, originalmove_velocity);
1016 originalmove_clip = clip;
1017 originalmove_flags = (int)ent->v->flags;
1018 originalmove_groundentity = ent->v->groundentity;
1020 if ((int)ent->v->flags & FL_WATERJUMP)
1023 if (sv_nostep.integer)
1026 // if move didn't block on a step, return
1029 // if move was not trying to move into the step, return
1030 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1033 if (ent->v->movetype != MOVETYPE_FLY)
1035 // return if gibbed by a trigger
1036 if (ent->v->movetype != MOVETYPE_WALK)
1039 // only step up while jumping if that is enabled
1040 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1041 if (!oldonground && ent->v->waterlevel == 0)
1045 // try moving up and forward to go up a step
1046 // back to start pos
1047 VectorCopy (start_origin, ent->v->origin);
1048 VectorCopy (start_velocity, ent->v->velocity);
1051 VectorClear (upmove);
1052 upmove[2] = sv_stepheight.value;
1053 // FIXME: don't link?
1054 SV_PushEntity(ent, upmove);
1057 ent->v->velocity[2] = 0;
1058 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1059 ent->v->velocity[2] += start_velocity[2];
1061 SV_CheckVelocity(ent);
1063 // check for stuckness, possibly due to the limited precision of floats
1064 // in the clipping hulls
1066 && fabs(originalmove_origin[1] - ent->v->origin[1]) < 0.03125
1067 && fabs(originalmove_origin[0] - ent->v->origin[0]) < 0.03125)
1069 //Con_Printf("wall\n");
1070 // stepping up didn't make any progress, revert to original move
1071 VectorCopy(originalmove_origin, ent->v->origin);
1072 VectorCopy(originalmove_velocity, ent->v->velocity);
1073 //clip = originalmove_clip;
1074 ent->v->flags = originalmove_flags;
1075 ent->v->groundentity = originalmove_groundentity;
1076 // now try to unstick if needed
1077 //clip = SV_TryUnstick (ent, oldvel);
1081 //Con_Printf("step - ");
1083 // extra friction based on view angle
1084 if (clip & 2 && sv_wallfriction.integer)
1085 SV_WallFriction (ent, stepnormal);
1087 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1088 else if (!(sv_gameplayfix_stepdown.integer && ent->v->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->v->flags & FL_ONGROUND)))
1092 VectorClear (downmove);
1093 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1094 // FIXME: don't link?
1095 downtrace = SV_PushEntity (ent, downmove);
1097 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1099 // LordHavoc: disabled this check so you can walk on monsters/players
1100 //if (ent->v->solid == SOLID_BSP)
1102 //Con_Printf("onground\n");
1103 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1104 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1109 //Con_Printf("slope\n");
1110 // if the push down didn't end up on good ground, use the move without
1111 // the step up. This happens near wall / slope combinations, and can
1112 // cause the player to hop up higher on a slope too steep to climb
1113 VectorCopy(originalmove_origin, ent->v->origin);
1114 VectorCopy(originalmove_velocity, ent->v->velocity);
1115 //clip = originalmove_clip;
1116 ent->v->flags = originalmove_flags;
1117 ent->v->groundentity = originalmove_groundentity;
1120 SV_SetOnGround (ent);
1121 SV_CheckVelocity(ent);
1124 //============================================================================
1130 Entities that are "stuck" to another entity
1133 void SV_Physics_Follow (edict_t *ent)
1135 vec3_t vf, vr, vu, angles, v;
1139 if (!SV_RunThink (ent))
1142 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1143 e = PROG_TO_EDICT(ent->v->aiment);
1144 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])
1146 // quick case for no rotation
1147 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1151 angles[0] = -ent->v->punchangle[0];
1152 angles[1] = ent->v->punchangle[1];
1153 angles[2] = ent->v->punchangle[2];
1154 AngleVectors (angles, vf, vr, vu);
1155 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1156 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1157 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1158 angles[0] = -e->v->angles[0];
1159 angles[1] = e->v->angles[1];
1160 angles[2] = e->v->angles[2];
1161 AngleVectors (angles, vf, vr, vu);
1162 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1163 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1164 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1166 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1167 SV_LinkEdict (ent, true);
1171 ==============================================================================
1175 ==============================================================================
1180 SV_CheckWaterTransition
1184 void SV_CheckWaterTransition (edict_t *ent)
1187 cont = SV_PointQ1Contents(ent->v->origin);
1188 if (!ent->v->watertype)
1190 // just spawned here
1191 ent->v->watertype = cont;
1192 ent->v->waterlevel = 1;
1196 // check if the entity crossed into or out of water
1197 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1198 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1200 if (cont <= CONTENTS_WATER)
1202 ent->v->watertype = cont;
1203 ent->v->waterlevel = 1;
1207 ent->v->watertype = CONTENTS_EMPTY;
1208 ent->v->waterlevel = 0;
1216 Toss, bounce, and fly movement. When onground, do nothing.
1219 void SV_Physics_Toss (edict_t *ent)
1223 edict_t *groundentity;
1225 // don't stick to ground if onground and moving upward
1226 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1227 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1229 // if onground, return without moving
1230 if ((int)ent->v->flags & FL_ONGROUND)
1232 if (!sv_gameplayfix_noairborncorpse.integer)
1234 if (ent->v->groundentity == 0)
1236 // if ent was supported by a brush model on previous frame,
1237 // and groundentity is now freed, set groundentity to 0 (floating)
1238 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1239 if (groundentity->v->solid == SOLID_BSP)
1241 ent->e->suspendedinairflag = true;
1244 else if (ent->e->suspendedinairflag && groundentity->e->free)
1246 // leave it suspended in the air
1247 ent->v->groundentity = 0;
1248 ent->e->suspendedinairflag = false;
1252 ent->e->suspendedinairflag = false;
1254 SV_CheckVelocity (ent);
1257 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1258 SV_AddGravity (ent);
1261 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1264 VectorScale (ent->v->velocity, sv.frametime, move);
1265 trace = SV_PushEntity (ent, move);
1269 if (trace.fraction < 1)
1271 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1273 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1274 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1276 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1279 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1280 // LordHavoc: fixed grenades not bouncing when fired down a slope
1281 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1283 d = DotProduct(trace.plane.normal, ent->v->velocity);
1284 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1286 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1287 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1288 VectorClear (ent->v->velocity);
1289 VectorClear (ent->v->avelocity);
1292 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1296 if (trace.plane.normal[2] > 0.7 && ent->v->velocity[2] < 60)
1298 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1299 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1300 VectorClear (ent->v->velocity);
1301 VectorClear (ent->v->avelocity);
1304 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1309 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1310 if (trace.plane.normal[2] > 0.7)
1312 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1313 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1314 VectorClear (ent->v->velocity);
1315 VectorClear (ent->v->avelocity);
1318 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1322 // check for in water
1323 SV_CheckWaterTransition (ent);
1327 ===============================================================================
1331 ===============================================================================
1338 Monsters freefall when they don't have a ground entity, otherwise
1339 all movement is done with discrete steps.
1341 This is also used for objects that have become still on the ground, but
1342 will fall if the floor is pulled out from under them.
1345 void SV_Physics_Step (edict_t *ent)
1347 // don't stick to ground if onground and moving upward
1348 if (ent->v->velocity[2] >= (1.0 / 32.0) && ((int)ent->v->flags & FL_ONGROUND))
1349 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1351 // freefall if not onground/fly/swim
1352 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1354 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1357 SV_CheckVelocity(ent);
1358 SV_FlyMove(ent, sv.frametime, NULL);
1359 SV_LinkEdict(ent, true);
1362 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1363 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1369 SV_CheckWaterTransition(ent);
1372 //============================================================================
1380 void SV_Physics (void)
1382 int i, newnum_edicts;
1384 qbyte runmove[MAX_EDICTS];
1386 // let the progs know that a new frame has started
1387 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1388 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1389 pr_global_struct->time = sv.time;
1390 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1393 for (i = 0, ent = sv.edicts;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
1394 if ((runmove[i] = !ent->e->free))
1395 newnum_edicts = i + 1;
1396 sv.num_edicts = max(svs.maxclients + 1, newnum_edicts);
1399 // treat each object in turn
1402 for (i = 0, ent = sv.edicts;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
1407 if (pr_global_struct->force_retouch)
1408 SV_LinkEdict (ent, true); // force retouch even for stationary
1410 if (i >= 1 && i <= svs.maxclients && svs.clients[i-1].spawned)
1413 // call standard client pre-think
1414 SV_CheckVelocity (ent);
1415 pr_global_struct->time = sv.time;
1416 pr_global_struct->self = EDICT_TO_PROG(ent);
1417 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1418 SV_CheckVelocity (ent);
1420 else if (sv_freezenonclients.integer)
1423 // LordHavoc: merged client and normal entity physics
1424 switch ((int) ent->v->movetype)
1427 case MOVETYPE_FAKEPUSH:
1428 SV_Physics_Pusher (ent);
1431 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1432 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1435 case MOVETYPE_FOLLOW:
1436 SV_Physics_Follow (ent);
1438 case MOVETYPE_NOCLIP:
1439 if (SV_RunThink(ent))
1442 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1443 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1445 // relink normal entities here, players always get relinked so don't relink twice
1446 if (!(i > 0 && i <= svs.maxclients))
1447 SV_LinkEdict(ent, false);
1450 SV_Physics_Step (ent);
1453 if (SV_RunThink (ent))
1455 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1456 SV_AddGravity (ent);
1457 SV_CheckStuck (ent);
1459 // relink normal entities here, players always get relinked so don't relink twice
1460 if (!(i > 0 && i <= svs.maxclients))
1461 SV_LinkEdict (ent, true);
1465 case MOVETYPE_BOUNCE:
1466 case MOVETYPE_BOUNCEMISSILE:
1467 case MOVETYPE_FLYMISSILE:
1469 if (SV_RunThink (ent) && runmove[i])
1470 SV_Physics_Toss (ent);
1473 if (SV_RunThink (ent) && runmove[i])
1475 if (i > 0 && i <= svs.maxclients)
1477 SV_CheckWater (ent);
1481 SV_Physics_Toss (ent);
1485 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1489 if (i >= 1 && i <= svs.maxclients && svs.clients[i-1].spawned)
1491 SV_CheckVelocity (ent);
1493 // call standard player post-think
1494 SV_LinkEdict (ent, true);
1496 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");
1504 if (pr_global_struct->force_retouch > 0)
1505 pr_global_struct->force_retouch = max(0, pr_global_struct->force_retouch - 1);
1507 // LordHavoc: endframe support
1510 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1511 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1512 pr_global_struct->time = sv.time;
1513 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "QC function EndFrame is missing");
1516 if (!sv_freezenonclients.integer)
1517 sv.time += sv.frametime;
1521 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1524 float gravity, savesolid;
1526 edict_t tempent, *tent;
1531 // copy the vars over
1532 memcpy(&vars, tossent->v, sizeof(entvars_t));
1533 // set up the temp entity to point to the copied vars
1537 savesolid = tossent->v->solid;
1538 tossent->v->solid = SOLID_NOT;
1540 // this has to fetch the field from the original edict, since our copy is truncated
1541 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1542 if (val != NULL && val->_float != 0)
1543 gravity = val->_float;
1546 gravity *= sv_gravity.value * 0.05;
1548 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1550 SV_CheckVelocity (tent);
1551 tent->v->velocity[2] -= gravity;
1552 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1553 VectorScale (tent->v->velocity, 0.05, move);
1554 VectorAdd (tent->v->origin, move, end);
1555 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1556 VectorCopy (trace.endpos, tent->v->origin);
1558 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1561 tossent->v->solid = savesolid;
1562 trace.fraction = 0; // not relevant