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", "how fast you slow down"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
54 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
55 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
57 #define MOVE_EPSILON 0.01
59 void SV_Physics_Toss (prvm_edict_t *ent);
61 void SV_Phys_Init (void)
63 Cvar_RegisterVariable(&sv_stepheight);
64 Cvar_RegisterVariable(&sv_jumpstep);
65 Cvar_RegisterVariable(&sv_wallfriction);
66 Cvar_RegisterVariable(&sv_newflymove);
67 Cvar_RegisterVariable(&sv_freezenonclients);
69 Cvar_RegisterVariable(&sv_playerphysicsqc);
71 Cvar_RegisterVariable(&sv_sound_watersplash);
72 Cvar_RegisterVariable(&sv_sound_land);
80 void SV_CheckAllEnts (void)
85 // see if any solid entities are inside the final position
86 check = PRVM_NEXT_EDICT(prog->edicts);
87 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
89 if (check->priv.server->free)
91 if (check->fields.server->movetype == MOVETYPE_PUSH
92 || check->fields.server->movetype == MOVETYPE_NONE
93 || check->fields.server->movetype == MOVETYPE_FOLLOW
94 || check->fields.server->movetype == MOVETYPE_NOCLIP)
97 if (SV_TestEntityPosition (check))
98 Con_Print("entity in invalid position\n");
107 void SV_CheckVelocity (prvm_edict_t *ent)
115 for (i=0 ; i<3 ; i++)
117 if (IS_NAN(ent->fields.server->velocity[i]))
119 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
120 ent->fields.server->velocity[i] = 0;
122 if (IS_NAN(ent->fields.server->origin[i]))
124 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
125 ent->fields.server->origin[i] = 0;
129 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
130 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
131 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
133 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
134 ent->fields.server->velocity[0] *= wishspeed;
135 ent->fields.server->velocity[1] *= wishspeed;
136 ent->fields.server->velocity[2] *= wishspeed;
144 Runs thinking code if time. There is some play in the exact time the think
145 function will be called, because it is called before any movement is done
146 in a frame. Not used for pushmove objects, because they must be exact.
147 Returns false if the entity removed itself.
150 qboolean SV_RunThink (prvm_edict_t *ent)
154 thinktime = ent->fields.server->nextthink;
155 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
158 // don't let things stay in the past.
159 // it is possible to start that way by a trigger with a local time.
160 if (thinktime < sv.time)
163 ent->fields.server->nextthink = 0;
164 prog->globals.server->time = thinktime;
165 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
166 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
167 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
168 return !ent->priv.server->free;
175 Two entities have touched, so run their touch functions
178 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
180 int old_self, old_other;
182 old_self = prog->globals.server->self;
183 old_other = prog->globals.server->other;
185 prog->globals.server->time = sv.time;
186 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
188 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
189 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
190 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
193 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
195 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
196 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
197 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
200 prog->globals.server->self = old_self;
201 prog->globals.server->other = old_other;
209 Slide off of the impacting object
210 returns the blocked flags (1 = floor, 2 = step / wall)
213 #define STOP_EPSILON 0.1
214 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
219 backoff = -DotProduct (in, normal) * overbounce;
220 VectorMA(in, backoff, normal, out);
222 for (i = 0;i < 3;i++)
223 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
232 The basic solid body movement clip that slides along multiple planes
233 Returns the clipflags if the velocity was modified (hit something solid)
237 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
240 // LordHavoc: increased from 5 to 32
241 #define MAX_CLIP_PLANES 32
242 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
244 int blocked, bumpcount;
245 int i, j, impact, numplanes;
247 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
250 VectorCopy(ent->fields.server->velocity, original_velocity);
251 VectorCopy(ent->fields.server->velocity, primal_velocity);
254 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
256 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
259 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
260 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
262 //if (trace.fraction < 0.002)
267 VectorCopy(ent->fields.server->origin, start);
268 start[2] += 3;//0.03125;
269 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
270 end[2] += 3;//0.03125;
271 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
272 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
274 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
280 for (i = 0;i < numplanes;i++)
282 VectorCopy(ent->fields.server->origin, start);
283 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
284 VectorMA(start, 3, planes[i], start);
285 VectorMA(end, 3, planes[i], end);
286 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
287 if (trace.fraction < testtrace.fraction)
290 VectorCopy(start, ent->fields.server->origin);
295 // VectorAdd(ent->fields.server->origin, planes[j], start);
301 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
302 if (trace.fraction < 1)
303 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
308 if (trace.startsolid)
310 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
311 // entity is trapped in another solid
312 VectorClear(ent->fields.server->velocity);
317 // break if it moved the entire distance
318 if (trace.fraction == 1)
320 VectorCopy(trace.endpos, ent->fields.server->origin);
326 Con_Printf ("SV_FlyMove: !trace.ent");
327 trace.ent = prog->edicts;
330 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
334 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
338 if (trace.plane.normal[2])
340 if (trace.plane.normal[2] > 0.7)
344 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
345 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
352 // save the trace for player extrafriction
354 VectorCopy(trace.plane.normal, stepnormal);
357 if (trace.fraction >= 0.001)
359 // actually covered some distance
360 VectorCopy(trace.endpos, ent->fields.server->origin);
361 VectorCopy(ent->fields.server->velocity, original_velocity);
365 // run the impact function
368 SV_Impact(ent, (prvm_edict_t *)trace.ent);
370 // break if removed by the impact function
371 if (ent->priv.server->free)
375 time_left *= 1 - trace.fraction;
377 // clipped to another plane
378 if (numplanes >= MAX_CLIP_PLANES)
380 // this shouldn't really happen
381 VectorClear(ent->fields.server->velocity);
387 for (i = 0;i < numplanes;i++)
388 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
392 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
397 VectorCopy(trace.plane.normal, planes[numplanes]);
400 if (sv_newflymove.integer)
401 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
404 // modify original_velocity so it parallels all of the clip planes
405 for (i = 0;i < numplanes;i++)
407 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
408 for (j = 0;j < numplanes;j++)
413 if (DotProduct(new_velocity, planes[j]) < 0)
423 // go along this plane
424 VectorCopy(new_velocity, ent->fields.server->velocity);
428 // go along the crease
431 VectorClear(ent->fields.server->velocity);
435 CrossProduct(planes[0], planes[1], dir);
436 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
437 VectorNormalize(dir);
438 d = DotProduct(dir, ent->fields.server->velocity);
439 VectorScale(dir, d, ent->fields.server->velocity);
443 // if current velocity is against the original velocity,
444 // stop dead to avoid tiny occilations in sloping corners
445 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
447 VectorClear(ent->fields.server->velocity);
452 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
455 if ((blocked & 1) == 0 && bumpcount > 1)
457 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
458 // flag ONGROUND if there's ground under it
459 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
471 void SV_AddGravity (prvm_edict_t *ent)
476 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
477 if (val!=0 && val->_float)
478 ent_gravity = val->_float;
481 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
486 ===============================================================================
490 ===============================================================================
497 Does not change the entities velocity at all
500 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
506 VectorAdd (ent->fields.server->origin, push, end);
508 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
510 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
511 type = MOVE_NOMONSTERS; // only clip against bmodels
515 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
517 VectorCopy (trace.endpos, ent->fields.server->origin);
518 SV_LinkEdict (ent, true);
520 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
521 SV_Impact (ent, (prvm_edict_t *)trace.ent);
532 void SV_PushMove (prvm_edict_t *pusher, float movetime)
535 float savesolid, movetime2, pushltime;
536 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
538 int numcheckentities;
539 static prvm_edict_t *checkentities[MAX_EDICTS];
540 model_t *pushermodel;
543 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
545 pusher->fields.server->ltime += movetime;
549 switch ((int) pusher->fields.server->solid)
551 // LordHavoc: valid pusher types
555 case SOLID_CORPSE: // LordHavoc: this would be weird...
557 // LordHavoc: no collisions
560 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
561 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
562 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
563 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
564 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
565 pusher->fields.server->ltime += movetime;
566 SV_LinkEdict (pusher, false);
569 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
572 index = (int) pusher->fields.server->modelindex;
573 if (index < 1 || index >= MAX_MODELS)
575 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
578 pushermodel = sv.models[index];
580 movetime2 = movetime;
581 VectorScale(pusher->fields.server->velocity, movetime2, move1);
582 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
583 if (moveangle[0] || moveangle[2])
585 for (i = 0;i < 3;i++)
589 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
590 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
594 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
595 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
599 else if (moveangle[1])
601 for (i = 0;i < 3;i++)
605 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
606 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
610 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
611 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
617 for (i = 0;i < 3;i++)
621 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
622 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
626 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
627 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
632 VectorNegate (moveangle, a);
633 AngleVectorsFLU (a, forward, left, up);
635 VectorCopy (pusher->fields.server->origin, pushorig);
636 VectorCopy (pusher->fields.server->angles, pushang);
637 pushltime = pusher->fields.server->ltime;
639 // move the pusher to its final position
641 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
642 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
643 pusher->fields.server->ltime += movetime;
644 SV_LinkEdict (pusher, false);
646 savesolid = pusher->fields.server->solid;
648 // see if any solid entities are inside the final position
651 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
652 for (e = 0;e < numcheckentities;e++)
654 prvm_edict_t *check = checkentities[e];
655 if (check->fields.server->movetype == MOVETYPE_NONE
656 || check->fields.server->movetype == MOVETYPE_PUSH
657 || check->fields.server->movetype == MOVETYPE_FOLLOW
658 || check->fields.server->movetype == MOVETYPE_NOCLIP
659 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
662 // if the entity is standing on the pusher, it will definitely be moved
663 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
665 // if the entity is not inside the pusher's final position, leave it alone
666 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
668 // remove the onground flag for non-players
669 if (check->fields.server->movetype != MOVETYPE_WALK)
670 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
674 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
677 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
678 org2[0] = DotProduct (org, forward);
679 org2[1] = DotProduct (org, left);
680 org2[2] = DotProduct (org, up);
681 VectorSubtract (org2, org, move);
682 VectorAdd (move, move1, move);
685 VectorCopy (move1, move);
687 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
688 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
689 sv.moved_edicts[num_moved++] = check;
691 // try moving the contacted entity
692 pusher->fields.server->solid = SOLID_NOT;
693 trace = SV_PushEntity (check, move);
694 // FIXME: turn players specially
695 check->fields.server->angles[1] += trace.fraction * moveangle[1];
696 pusher->fields.server->solid = savesolid; // was SOLID_BSP
698 // if it is still inside the pusher, block
699 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
701 // try moving the contacted entity a tiny bit further to account for precision errors
703 pusher->fields.server->solid = SOLID_NOT;
704 VectorScale(move, 1.1, move2);
705 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
706 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
707 SV_PushEntity (check, move2);
708 pusher->fields.server->solid = savesolid;
709 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
711 // try moving the contacted entity a tiny bit less to account for precision errors
712 pusher->fields.server->solid = SOLID_NOT;
713 VectorScale(move, 0.9, move2);
714 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
715 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
716 SV_PushEntity (check, move2);
717 pusher->fields.server->solid = savesolid;
718 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
720 // still inside pusher, so it's really blocked
723 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
725 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
728 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
729 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
733 VectorCopy (pushorig, pusher->fields.server->origin);
734 VectorCopy (pushang, pusher->fields.server->angles);
735 pusher->fields.server->ltime = pushltime;
736 SV_LinkEdict (pusher, false);
738 // move back any entities we already moved
739 for (i = 0;i < num_moved;i++)
741 prvm_edict_t *ed = sv.moved_edicts[i];
742 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
743 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
744 SV_LinkEdict (ed, false);
747 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
748 if (pusher->fields.server->blocked)
750 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
751 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
752 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
759 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
760 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
761 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
770 void SV_Physics_Pusher (prvm_edict_t *ent)
772 float thinktime, oldltime, movetime;
774 oldltime = ent->fields.server->ltime;
776 thinktime = ent->fields.server->nextthink;
777 if (thinktime < ent->fields.server->ltime + sv.frametime)
779 movetime = thinktime - ent->fields.server->ltime;
784 movetime = sv.frametime;
787 // advances ent->fields.server->ltime if not blocked
788 SV_PushMove (ent, movetime);
790 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
792 ent->fields.server->nextthink = 0;
793 prog->globals.server->time = sv.time;
794 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
795 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
796 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
802 ===============================================================================
806 ===============================================================================
813 This is a big hack to try and fix the rare case of getting stuck in the world
817 void SV_CheckStuck (prvm_edict_t *ent)
822 if (!SV_TestEntityPosition(ent))
824 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
828 VectorCopy (ent->fields.server->origin, org);
829 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
830 if (!SV_TestEntityPosition(ent))
832 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
833 SV_LinkEdict (ent, true);
837 for (z=0 ; z< 18 ; z++)
838 for (i=-1 ; i <= 1 ; i++)
839 for (j=-1 ; j <= 1 ; j++)
841 ent->fields.server->origin[0] = org[0] + i;
842 ent->fields.server->origin[1] = org[1] + j;
843 ent->fields.server->origin[2] = org[2] + z;
844 if (!SV_TestEntityPosition(ent))
846 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
847 SV_LinkEdict (ent, true);
852 VectorCopy (org, ent->fields.server->origin);
853 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
856 void SV_UnstickEntity (prvm_edict_t *ent)
861 VectorCopy (ent->fields.server->origin, org);
863 for (z=0 ; z< 18 ; z += 6)
864 for (i=-1 ; i <= 1 ; i++)
865 for (j=-1 ; j <= 1 ; j++)
867 ent->fields.server->origin[0] = org[0] + i;
868 ent->fields.server->origin[1] = org[1] + j;
869 ent->fields.server->origin[2] = org[2] + z;
870 if (!SV_TestEntityPosition(ent))
872 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
873 SV_LinkEdict (ent, true);
878 VectorCopy (org, ent->fields.server->origin);
879 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
888 qboolean SV_CheckWater (prvm_edict_t *ent)
893 point[0] = ent->fields.server->origin[0];
894 point[1] = ent->fields.server->origin[1];
895 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
897 ent->fields.server->waterlevel = 0;
898 ent->fields.server->watertype = CONTENTS_EMPTY;
899 cont = SV_PointSuperContents(point);
900 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
902 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
903 ent->fields.server->waterlevel = 1;
904 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
905 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
907 ent->fields.server->waterlevel = 2;
908 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
909 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
910 ent->fields.server->waterlevel = 3;
914 return ent->fields.server->waterlevel > 1;
923 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
926 vec3_t forward, into, side;
928 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
929 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
931 // cut the tangential velocity
932 i = DotProduct (stepnormal, ent->fields.server->velocity);
933 VectorScale (stepnormal, i, into);
934 VectorSubtract (ent->fields.server->velocity, into, side);
935 ent->fields.server->velocity[0] = side[0] * (1 + d);
936 ent->fields.server->velocity[1] = side[1] * (1 + d);
941 =====================
944 Player has come to a dead stop, possibly due to the problem with limited
945 float precision at some angle joins in the BSP hull.
947 Try fixing by pushing one pixel in each direction.
949 This is a hack, but in the interest of good gameplay...
950 ======================
952 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
957 VectorCopy (ent->fields.server->origin, oldorg);
960 for (i=0 ; i<8 ; i++)
962 // try pushing a little in an axial direction
965 case 0: dir[0] = 2; dir[1] = 0; break;
966 case 1: dir[0] = 0; dir[1] = 2; break;
967 case 2: dir[0] = -2; dir[1] = 0; break;
968 case 3: dir[0] = 0; dir[1] = -2; break;
969 case 4: dir[0] = 2; dir[1] = 2; break;
970 case 5: dir[0] = -2; dir[1] = 2; break;
971 case 6: dir[0] = 2; dir[1] = -2; break;
972 case 7: dir[0] = -2; dir[1] = -2; break;
975 SV_PushEntity (ent, dir);
977 // retry the original move
978 ent->fields.server->velocity[0] = oldvel[0];
979 ent->fields.server->velocity[1] = oldvel[1];
980 ent->fields.server->velocity[2] = 0;
981 clip = SV_FlyMove (ent, 0.1, NULL);
983 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
984 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
986 Con_DPrint("TryUnstick - success.\n");
990 // go back to the original pos and try again
991 VectorCopy (oldorg, ent->fields.server->origin);
995 VectorClear (ent->fields.server->velocity);
996 Con_DPrint("TryUnstick - failure.\n");
1001 =====================
1004 Only used by players
1005 ======================
1007 void SV_WalkMove (prvm_edict_t *ent)
1009 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1010 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1013 SV_CheckVelocity(ent);
1015 // do a regular slide move unless it looks like you ran into a step
1016 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1017 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1019 VectorCopy (ent->fields.server->origin, start_origin);
1020 VectorCopy (ent->fields.server->velocity, start_velocity);
1022 clip = SV_FlyMove (ent, sv.frametime, NULL);
1024 SV_CheckVelocity(ent);
1026 VectorCopy(ent->fields.server->origin, originalmove_origin);
1027 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1028 originalmove_clip = clip;
1029 originalmove_flags = (int)ent->fields.server->flags;
1030 originalmove_groundentity = ent->fields.server->groundentity;
1032 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1035 if (sv_nostep.integer)
1038 // if move didn't block on a step, return
1041 // if move was not trying to move into the step, return
1042 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1045 if (ent->fields.server->movetype != MOVETYPE_FLY)
1047 // return if gibbed by a trigger
1048 if (ent->fields.server->movetype != MOVETYPE_WALK)
1051 // only step up while jumping if that is enabled
1052 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1053 if (!oldonground && ent->fields.server->waterlevel == 0)
1057 // try moving up and forward to go up a step
1058 // back to start pos
1059 VectorCopy (start_origin, ent->fields.server->origin);
1060 VectorCopy (start_velocity, ent->fields.server->velocity);
1063 VectorClear (upmove);
1064 upmove[2] = sv_stepheight.value;
1065 // FIXME: don't link?
1066 SV_PushEntity(ent, upmove);
1069 ent->fields.server->velocity[2] = 0;
1070 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1071 ent->fields.server->velocity[2] += start_velocity[2];
1073 SV_CheckVelocity(ent);
1075 // check for stuckness, possibly due to the limited precision of floats
1076 // in the clipping hulls
1078 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1079 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1081 //Con_Printf("wall\n");
1082 // stepping up didn't make any progress, revert to original move
1083 VectorCopy(originalmove_origin, ent->fields.server->origin);
1084 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1085 //clip = originalmove_clip;
1086 ent->fields.server->flags = originalmove_flags;
1087 ent->fields.server->groundentity = originalmove_groundentity;
1088 // now try to unstick if needed
1089 //clip = SV_TryUnstick (ent, oldvel);
1093 //Con_Printf("step - ");
1095 // extra friction based on view angle
1096 if (clip & 2 && sv_wallfriction.integer)
1097 SV_WallFriction (ent, stepnormal);
1099 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1100 else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1104 VectorClear (downmove);
1105 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1106 // FIXME: don't link?
1107 downtrace = SV_PushEntity (ent, downmove);
1109 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1111 // LordHavoc: disabled this check so you can walk on monsters/players
1112 //if (ent->fields.server->solid == SOLID_BSP)
1114 //Con_Printf("onground\n");
1115 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1116 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1121 //Con_Printf("slope\n");
1122 // if the push down didn't end up on good ground, use the move without
1123 // the step up. This happens near wall / slope combinations, and can
1124 // cause the player to hop up higher on a slope too steep to climb
1125 VectorCopy(originalmove_origin, ent->fields.server->origin);
1126 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1127 //clip = originalmove_clip;
1128 ent->fields.server->flags = originalmove_flags;
1129 ent->fields.server->groundentity = originalmove_groundentity;
1132 SV_CheckVelocity(ent);
1135 //============================================================================
1141 Entities that are "stuck" to another entity
1144 void SV_Physics_Follow (prvm_edict_t *ent)
1146 vec3_t vf, vr, vu, angles, v;
1150 if (!SV_RunThink (ent))
1153 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1154 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1155 if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1157 // quick case for no rotation
1158 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1162 angles[0] = -ent->fields.server->punchangle[0];
1163 angles[1] = ent->fields.server->punchangle[1];
1164 angles[2] = ent->fields.server->punchangle[2];
1165 AngleVectors (angles, vf, vr, vu);
1166 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1167 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1168 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1169 angles[0] = -e->fields.server->angles[0];
1170 angles[1] = e->fields.server->angles[1];
1171 angles[2] = e->fields.server->angles[2];
1172 AngleVectors (angles, vf, vr, vu);
1173 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1174 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1175 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1177 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1178 SV_LinkEdict (ent, true);
1182 ==============================================================================
1186 ==============================================================================
1191 SV_CheckWaterTransition
1195 void SV_CheckWaterTransition (prvm_edict_t *ent)
1198 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1199 if (!ent->fields.server->watertype)
1201 // just spawned here
1202 ent->fields.server->watertype = cont;
1203 ent->fields.server->waterlevel = 1;
1207 // check if the entity crossed into or out of water
1208 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1209 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1211 if (cont <= CONTENTS_WATER)
1213 ent->fields.server->watertype = cont;
1214 ent->fields.server->waterlevel = 1;
1218 ent->fields.server->watertype = CONTENTS_EMPTY;
1219 ent->fields.server->waterlevel = 0;
1227 Toss, bounce, and fly movement. When onground, do nothing.
1230 void SV_Physics_Toss (prvm_edict_t *ent)
1235 // if onground, return without moving
1236 if ((int)ent->fields.server->flags & FL_ONGROUND)
1238 // don't stick to ground if onground and moving upward
1239 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1240 ent->fields.server->flags -= FL_ONGROUND;
1243 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1244 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1246 // if ent was supported by a brush model on previous frame,
1247 // and groundentity is now freed, set groundentity to 0 (floating)
1248 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1250 // leave it suspended in the air
1251 ent->fields.server->groundentity = 0;
1256 ent->priv.server->suspendedinairflag = false;
1258 SV_CheckVelocity (ent);
1261 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1262 SV_AddGravity (ent);
1265 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1268 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1269 trace = SV_PushEntity (ent, move);
1270 if (ent->priv.server->free)
1272 if (trace.startsolid)
1274 // try to unstick the entity
1275 SV_UnstickEntity(ent);
1276 trace = SV_PushEntity (ent, move);
1277 if (ent->priv.server->free)
1281 if (trace.fraction < 1)
1283 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1285 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1286 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1288 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1291 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1292 // LordHavoc: fixed grenades not bouncing when fired down a slope
1293 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1295 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1296 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1298 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1299 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1300 VectorClear (ent->fields.server->velocity);
1301 VectorClear (ent->fields.server->avelocity);
1304 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1308 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1310 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1311 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1312 VectorClear (ent->fields.server->velocity);
1313 VectorClear (ent->fields.server->avelocity);
1316 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1321 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1322 if (trace.plane.normal[2] > 0.7)
1324 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1325 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1326 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1327 ent->priv.server->suspendedinairflag = true;
1328 VectorClear (ent->fields.server->velocity);
1329 VectorClear (ent->fields.server->avelocity);
1332 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1336 // check for in water
1337 SV_CheckWaterTransition (ent);
1341 ===============================================================================
1345 ===============================================================================
1352 Monsters freefall when they don't have a ground entity, otherwise
1353 all movement is done with discrete steps.
1355 This is also used for objects that have become still on the ground, but
1356 will fall if the floor is pulled out from under them.
1359 void SV_Physics_Step (prvm_edict_t *ent)
1361 int flags = (int)ent->fields.server->flags;
1362 // don't fall at all if fly/swim
1363 if (!(flags & (FL_FLY | FL_SWIM)))
1365 if (flags & FL_ONGROUND)
1367 // freefall if onground and moving upward
1368 // freefall if not standing on a world surface (it may be a lift)
1369 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1370 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1372 ent->fields.server->flags -= FL_ONGROUND;
1374 SV_CheckVelocity(ent);
1375 SV_FlyMove(ent, sv.frametime, NULL);
1376 SV_LinkEdict(ent, true);
1381 // freefall if not onground
1382 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1385 SV_CheckVelocity(ent);
1386 SV_FlyMove(ent, sv.frametime, NULL);
1387 SV_LinkEdict(ent, true);
1390 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1391 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1398 SV_CheckWaterTransition(ent);
1401 //============================================================================
1403 static void SV_Physics_Entity (prvm_edict_t *ent)
1405 // don't run a move on newly spawned projectiles as it messes up movement
1406 // interpolation and rocket trails
1407 qboolean runmove = ent->priv.server->move;
1408 ent->priv.server->move = true;
1409 switch ((int) ent->fields.server->movetype)
1412 case MOVETYPE_FAKEPUSH:
1413 SV_Physics_Pusher (ent);
1416 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1417 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1420 case MOVETYPE_FOLLOW:
1421 SV_Physics_Follow (ent);
1423 case MOVETYPE_NOCLIP:
1424 if (SV_RunThink(ent))
1427 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1428 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1430 SV_LinkEdict(ent, false);
1433 SV_Physics_Step (ent);
1436 if (SV_RunThink (ent))
1438 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1439 SV_AddGravity (ent);
1440 SV_CheckStuck (ent);
1442 SV_LinkEdict (ent, true);
1446 case MOVETYPE_BOUNCE:
1447 case MOVETYPE_BOUNCEMISSILE:
1448 case MOVETYPE_FLYMISSILE:
1451 if (SV_RunThink (ent) && runmove)
1452 SV_Physics_Toss (ent);
1455 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1460 void SV_ApplyClientMove (void);
1461 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1463 SV_ApplyClientMove();
1464 // make sure the velocity is sane (not a NaN)
1465 SV_CheckVelocity(ent);
1466 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1467 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1469 prog->globals.server->time = sv.time;
1470 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1471 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1475 // make sure the velocity is sane (not a NaN)
1476 SV_CheckVelocity(ent);
1477 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1478 // player_run/player_stand1 does not horribly malfunction if the
1479 // velocity becomes a number that is both == 0 and != 0
1480 // (sounds to me like NaN but to be absolutely safe...)
1481 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1482 VectorClear(ent->fields.server->velocity);
1483 // call standard client pre-think
1484 prog->globals.server->time = sv.time;
1485 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1486 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1487 SV_CheckVelocity (ent);
1489 switch ((int) ent->fields.server->movetype)
1492 case MOVETYPE_FAKEPUSH:
1493 SV_Physics_Pusher (ent);
1496 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1497 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1500 case MOVETYPE_FOLLOW:
1501 SV_Physics_Follow (ent);
1503 case MOVETYPE_NOCLIP:
1504 if (SV_RunThink(ent))
1507 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1508 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1512 SV_Physics_Step (ent);
1515 if (SV_RunThink (ent))
1517 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1518 SV_AddGravity (ent);
1519 SV_CheckStuck (ent);
1524 case MOVETYPE_BOUNCE:
1525 case MOVETYPE_BOUNCEMISSILE:
1526 case MOVETYPE_FLYMISSILE:
1528 if (SV_RunThink (ent))
1529 SV_Physics_Toss (ent);
1532 if (SV_RunThink (ent))
1534 SV_CheckWater (ent);
1539 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1543 SV_CheckVelocity (ent);
1545 // call standard player post-think
1546 SV_LinkEdict (ent, true);
1548 SV_CheckVelocity (ent);
1550 prog->globals.server->time = sv.time;
1551 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1552 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1561 void SV_Physics (void)
1566 // let the progs know that a new frame has started
1567 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1568 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1569 prog->globals.server->time = sv.time;
1570 prog->globals.server->frametime = sv.frametime;
1571 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1574 // treat each object in turn
1577 // if force_retouch, relink all the entities
1578 if (prog->globals.server->force_retouch > 0)
1579 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1580 if (!ent->priv.server->free)
1581 SV_LinkEdict (ent, true); // force retouch even for stationary
1583 // run physics on the client entities
1584 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1586 if (!ent->priv.server->free)
1588 // don't do physics on disconnected clients, FrikBot relies on this
1589 if (!host_client->spawned)
1590 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1591 // don't run physics here if running asynchronously
1592 else if (!host_client->movesequence)
1593 SV_Physics_ClientEntity(ent);
1597 // run physics on all the non-client entities
1598 if (!sv_freezenonclients.integer)
1599 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1600 if (!ent->priv.server->free)
1601 SV_Physics_Entity(ent);
1603 if (prog->globals.server->force_retouch > 0)
1604 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1606 // LordHavoc: endframe support
1609 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1610 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1611 prog->globals.server->time = sv.time;
1612 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1615 // decrement prog->num_edicts if the highest number entities died
1616 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1618 if (!sv_freezenonclients.integer)
1619 sv.time += sv.frametime;
1623 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1626 float gravity, savesolid;
1628 prvm_edict_t tempent, *tent;
1633 // copy the vars over
1634 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1635 // set up the temp entity to point to the copied vars
1637 tent->fields.server = &vars;
1639 savesolid = tossent->fields.server->solid;
1640 tossent->fields.server->solid = SOLID_NOT;
1642 // this has to fetch the field from the original edict, since our copy is truncated
1643 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1644 if (val != NULL && val->_float != 0)
1645 gravity = val->_float;
1648 gravity *= sv_gravity.value * 0.05;
1650 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1652 SV_CheckVelocity (tent);
1653 tent->fields.server->velocity[2] -= gravity;
1654 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1655 VectorScale (tent->fields.server->velocity, 0.05, move);
1656 VectorAdd (tent->fields.server->origin, move, end);
1657 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1658 VectorCopy (trace.endpos, tent->fields.server->origin);
1660 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1663 tossent->fields.server->solid = savesolid;
1664 trace.fraction = 0; // not relevant