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);
79 returns true if the entity is in solid currently
82 static int SV_TestEntityPosition (prvm_edict_t *ent, int movemode)
84 return SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, movemode, ent).startsolid;
92 void SV_CheckAllEnts (void)
97 // see if any solid entities are inside the final position
98 check = PRVM_NEXT_EDICT(prog->edicts);
99 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
101 if (check->priv.server->free)
103 if (check->fields.server->movetype == MOVETYPE_PUSH
104 || check->fields.server->movetype == MOVETYPE_NONE
105 || check->fields.server->movetype == MOVETYPE_FOLLOW
106 || check->fields.server->movetype == MOVETYPE_NOCLIP)
109 if (SV_TestEntityPosition (check, MOVE_NORMAL))
110 Con_Print("entity in invalid position\n");
119 void SV_CheckVelocity (prvm_edict_t *ent)
127 for (i=0 ; i<3 ; i++)
129 if (IS_NAN(ent->fields.server->velocity[i]))
131 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
132 ent->fields.server->velocity[i] = 0;
134 if (IS_NAN(ent->fields.server->origin[i]))
136 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
137 ent->fields.server->origin[i] = 0;
141 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
142 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
143 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
145 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
146 ent->fields.server->velocity[0] *= wishspeed;
147 ent->fields.server->velocity[1] *= wishspeed;
148 ent->fields.server->velocity[2] *= wishspeed;
156 Runs thinking code if time. There is some play in the exact time the think
157 function will be called, because it is called before any movement is done
158 in a frame. Not used for pushmove objects, because they must be exact.
159 Returns false if the entity removed itself.
162 qboolean SV_RunThink (prvm_edict_t *ent)
166 thinktime = ent->fields.server->nextthink;
167 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
170 // don't let things stay in the past.
171 // it is possible to start that way by a trigger with a local time.
172 if (thinktime < sv.time)
175 ent->fields.server->nextthink = 0;
176 prog->globals.server->time = thinktime;
177 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
178 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
179 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
180 return !ent->priv.server->free;
187 Two entities have touched, so run their touch functions
190 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
192 int old_self, old_other;
194 old_self = prog->globals.server->self;
195 old_other = prog->globals.server->other;
197 prog->globals.server->time = sv.time;
198 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
200 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
201 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
202 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
205 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
207 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
208 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
209 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
212 prog->globals.server->self = old_self;
213 prog->globals.server->other = old_other;
221 Slide off of the impacting object
222 returns the blocked flags (1 = floor, 2 = step / wall)
225 #define STOP_EPSILON 0.1
226 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
231 backoff = -DotProduct (in, normal) * overbounce;
232 VectorMA(in, backoff, normal, out);
234 for (i = 0;i < 3;i++)
235 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
244 The basic solid body movement clip that slides along multiple planes
245 Returns the clipflags if the velocity was modified (hit something solid)
249 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
252 // LordHavoc: increased from 5 to 32
253 #define MAX_CLIP_PLANES 32
254 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
256 int blocked, bumpcount;
257 int i, j, impact, numplanes;
259 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
262 VectorCopy(ent->fields.server->velocity, original_velocity);
263 VectorCopy(ent->fields.server->velocity, primal_velocity);
266 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
268 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
271 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
272 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
274 //if (trace.fraction < 0.002)
279 VectorCopy(ent->fields.server->origin, start);
280 start[2] += 3;//0.03125;
281 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
282 end[2] += 3;//0.03125;
283 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
284 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)))
286 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
292 for (i = 0;i < numplanes;i++)
294 VectorCopy(ent->fields.server->origin, start);
295 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
296 VectorMA(start, 3, planes[i], start);
297 VectorMA(end, 3, planes[i], end);
298 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
299 if (trace.fraction < testtrace.fraction)
302 VectorCopy(start, ent->fields.server->origin);
307 // VectorAdd(ent->fields.server->origin, planes[j], start);
313 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);
314 if (trace.fraction < 1)
315 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
320 if (trace.startsolid)
322 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
323 // entity is trapped in another solid
324 VectorClear(ent->fields.server->velocity);
329 // break if it moved the entire distance
330 if (trace.fraction == 1)
332 VectorCopy(trace.endpos, ent->fields.server->origin);
338 Con_Printf ("SV_FlyMove: !trace.ent");
339 trace.ent = prog->edicts;
342 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
346 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
350 if (trace.plane.normal[2])
352 if (trace.plane.normal[2] > 0.7)
356 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
357 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
364 // save the trace for player extrafriction
366 VectorCopy(trace.plane.normal, stepnormal);
369 if (trace.fraction >= 0.001)
371 // actually covered some distance
372 VectorCopy(trace.endpos, ent->fields.server->origin);
373 VectorCopy(ent->fields.server->velocity, original_velocity);
377 // run the impact function
380 SV_Impact(ent, (prvm_edict_t *)trace.ent);
382 // break if removed by the impact function
383 if (ent->priv.server->free)
387 time_left *= 1 - trace.fraction;
389 // clipped to another plane
390 if (numplanes >= MAX_CLIP_PLANES)
392 // this shouldn't really happen
393 VectorClear(ent->fields.server->velocity);
399 for (i = 0;i < numplanes;i++)
400 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
404 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
409 VectorCopy(trace.plane.normal, planes[numplanes]);
412 if (sv_newflymove.integer)
413 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
416 // modify original_velocity so it parallels all of the clip planes
417 for (i = 0;i < numplanes;i++)
419 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
420 for (j = 0;j < numplanes;j++)
425 if (DotProduct(new_velocity, planes[j]) < 0)
435 // go along this plane
436 VectorCopy(new_velocity, ent->fields.server->velocity);
440 // go along the crease
443 VectorClear(ent->fields.server->velocity);
447 CrossProduct(planes[0], planes[1], dir);
448 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
449 VectorNormalize(dir);
450 d = DotProduct(dir, ent->fields.server->velocity);
451 VectorScale(dir, d, ent->fields.server->velocity);
455 // if current velocity is against the original velocity,
456 // stop dead to avoid tiny occilations in sloping corners
457 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
459 VectorClear(ent->fields.server->velocity);
464 //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]);
467 if ((blocked & 1) == 0 && bumpcount > 1)
469 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
470 // flag ONGROUND if there's ground under it
471 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
483 void SV_AddGravity (prvm_edict_t *ent)
488 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
489 if (val!=0 && val->_float)
490 ent_gravity = val->_float;
493 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
498 ===============================================================================
502 ===============================================================================
509 Does not change the entities velocity at all
512 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
518 VectorAdd (ent->fields.server->origin, push, end);
520 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
522 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
523 type = MOVE_NOMONSTERS; // only clip against bmodels
527 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
529 VectorCopy (trace.endpos, ent->fields.server->origin);
530 SV_LinkEdict (ent, true);
532 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
533 SV_Impact (ent, (prvm_edict_t *)trace.ent);
544 void SV_PushMove (prvm_edict_t *pusher, float movetime)
547 float savesolid, movetime2, pushltime;
548 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
550 int numcheckentities;
551 static prvm_edict_t *checkentities[MAX_EDICTS];
552 model_t *pushermodel;
555 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])
557 pusher->fields.server->ltime += movetime;
561 switch ((int) pusher->fields.server->solid)
563 // LordHavoc: valid pusher types
567 case SOLID_CORPSE: // LordHavoc: this would be weird...
569 // LordHavoc: no collisions
572 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
573 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
574 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
575 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
576 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
577 pusher->fields.server->ltime += movetime;
578 SV_LinkEdict (pusher, false);
581 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
584 index = (int) pusher->fields.server->modelindex;
585 if (index < 1 || index >= MAX_MODELS)
587 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
590 pushermodel = sv.models[index];
592 movetime2 = movetime;
593 VectorScale(pusher->fields.server->velocity, movetime2, move1);
594 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
595 if (moveangle[0] || moveangle[2])
597 for (i = 0;i < 3;i++)
601 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
602 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
606 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
607 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
611 else if (moveangle[1])
613 for (i = 0;i < 3;i++)
617 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
618 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
622 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
623 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
629 for (i = 0;i < 3;i++)
633 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
634 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
638 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
639 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
644 VectorNegate (moveangle, a);
645 AngleVectorsFLU (a, forward, left, up);
647 VectorCopy (pusher->fields.server->origin, pushorig);
648 VectorCopy (pusher->fields.server->angles, pushang);
649 pushltime = pusher->fields.server->ltime;
651 // move the pusher to its final position
653 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
654 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
655 pusher->fields.server->ltime += movetime;
656 SV_LinkEdict (pusher, false);
658 savesolid = pusher->fields.server->solid;
660 // see if any solid entities are inside the final position
663 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
664 for (e = 0;e < numcheckentities;e++)
666 prvm_edict_t *check = checkentities[e];
667 if (check->fields.server->movetype == MOVETYPE_NONE
668 || check->fields.server->movetype == MOVETYPE_PUSH
669 || check->fields.server->movetype == MOVETYPE_FOLLOW
670 || check->fields.server->movetype == MOVETYPE_NOCLIP
671 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
674 // if the entity is standing on the pusher, it will definitely be moved
675 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
677 // if the entity is not inside the pusher's final position, leave it alone
678 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
680 // remove the onground flag for non-players
681 if (check->fields.server->movetype != MOVETYPE_WALK)
682 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
686 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
689 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
690 org2[0] = DotProduct (org, forward);
691 org2[1] = DotProduct (org, left);
692 org2[2] = DotProduct (org, up);
693 VectorSubtract (org2, org, move);
694 VectorAdd (move, move1, move);
697 VectorCopy (move1, move);
699 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
700 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
701 sv.moved_edicts[num_moved++] = check;
703 // try moving the contacted entity
704 pusher->fields.server->solid = SOLID_NOT;
705 trace = SV_PushEntity (check, move);
706 // FIXME: turn players specially
707 check->fields.server->angles[1] += trace.fraction * moveangle[1];
708 pusher->fields.server->solid = savesolid; // was SOLID_BSP
710 // if it is still inside the pusher, block
711 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
713 // try moving the contacted entity a tiny bit further to account for precision errors
715 pusher->fields.server->solid = SOLID_NOT;
716 VectorScale(move, 1.1, move2);
717 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
718 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
719 SV_PushEntity (check, move2);
720 pusher->fields.server->solid = savesolid;
721 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
723 // try moving the contacted entity a tiny bit less to account for precision errors
724 pusher->fields.server->solid = SOLID_NOT;
725 VectorScale(move, 0.9, move2);
726 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
727 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
728 SV_PushEntity (check, move2);
729 pusher->fields.server->solid = savesolid;
730 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
732 // still inside pusher, so it's really blocked
735 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
737 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
740 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
741 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
745 VectorCopy (pushorig, pusher->fields.server->origin);
746 VectorCopy (pushang, pusher->fields.server->angles);
747 pusher->fields.server->ltime = pushltime;
748 SV_LinkEdict (pusher, false);
750 // move back any entities we already moved
751 for (i = 0;i < num_moved;i++)
753 prvm_edict_t *ed = sv.moved_edicts[i];
754 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
755 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->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->fields.server->blocked)
762 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
763 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
764 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
771 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
772 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
773 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
782 void SV_Physics_Pusher (prvm_edict_t *ent)
784 float thinktime, oldltime, movetime;
786 oldltime = ent->fields.server->ltime;
788 thinktime = ent->fields.server->nextthink;
789 if (thinktime < ent->fields.server->ltime + sv.frametime)
791 movetime = thinktime - ent->fields.server->ltime;
796 movetime = sv.frametime;
799 // advances ent->fields.server->ltime if not blocked
800 SV_PushMove (ent, movetime);
802 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
804 ent->fields.server->nextthink = 0;
805 prog->globals.server->time = sv.time;
806 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
807 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
808 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
814 ===============================================================================
818 ===============================================================================
825 This is a big hack to try and fix the rare case of getting stuck in the world
829 void SV_CheckStuck (prvm_edict_t *ent)
834 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
836 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
840 VectorCopy (ent->fields.server->origin, org);
841 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
842 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
844 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
845 SV_LinkEdict (ent, true);
849 for (z=0 ; z< 18 ; z++)
850 for (i=-1 ; i <= 1 ; i++)
851 for (j=-1 ; j <= 1 ; j++)
853 ent->fields.server->origin[0] = org[0] + i;
854 ent->fields.server->origin[1] = org[1] + j;
855 ent->fields.server->origin[2] = org[2] + z;
856 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
858 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
859 SV_LinkEdict (ent, true);
864 VectorCopy (org, ent->fields.server->origin);
865 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
868 static void SV_UnstickEntity (prvm_edict_t *ent)
873 // if not stuck in a bmodel, just return
874 if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
877 VectorCopy (ent->fields.server->origin, org);
879 for (z=0 ; z< 18 ; z += 6)
880 for (i=-1 ; i <= 1 ; i++)
881 for (j=-1 ; j <= 1 ; j++)
883 ent->fields.server->origin[0] = org[0] + i;
884 ent->fields.server->origin[1] = org[1] + j;
885 ent->fields.server->origin[2] = org[2] + z;
886 if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
888 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
889 SV_LinkEdict (ent, true);
894 VectorCopy (org, ent->fields.server->origin);
895 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
904 qboolean SV_CheckWater (prvm_edict_t *ent)
909 point[0] = ent->fields.server->origin[0];
910 point[1] = ent->fields.server->origin[1];
911 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
913 ent->fields.server->waterlevel = 0;
914 ent->fields.server->watertype = CONTENTS_EMPTY;
915 cont = SV_PointSuperContents(point);
916 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
918 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
919 ent->fields.server->waterlevel = 1;
920 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
921 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
923 ent->fields.server->waterlevel = 2;
924 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
925 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
926 ent->fields.server->waterlevel = 3;
930 return ent->fields.server->waterlevel > 1;
939 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
942 vec3_t forward, into, side;
944 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
945 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
947 // cut the tangential velocity
948 i = DotProduct (stepnormal, ent->fields.server->velocity);
949 VectorScale (stepnormal, i, into);
950 VectorSubtract (ent->fields.server->velocity, into, side);
951 ent->fields.server->velocity[0] = side[0] * (1 + d);
952 ent->fields.server->velocity[1] = side[1] * (1 + d);
957 =====================
960 Player has come to a dead stop, possibly due to the problem with limited
961 float precision at some angle joins in the BSP hull.
963 Try fixing by pushing one pixel in each direction.
965 This is a hack, but in the interest of good gameplay...
966 ======================
968 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
973 VectorCopy (ent->fields.server->origin, oldorg);
976 for (i=0 ; i<8 ; i++)
978 // try pushing a little in an axial direction
981 case 0: dir[0] = 2; dir[1] = 0; break;
982 case 1: dir[0] = 0; dir[1] = 2; break;
983 case 2: dir[0] = -2; dir[1] = 0; break;
984 case 3: dir[0] = 0; dir[1] = -2; break;
985 case 4: dir[0] = 2; dir[1] = 2; break;
986 case 5: dir[0] = -2; dir[1] = 2; break;
987 case 6: dir[0] = 2; dir[1] = -2; break;
988 case 7: dir[0] = -2; dir[1] = -2; break;
991 SV_PushEntity (ent, dir);
993 // retry the original move
994 ent->fields.server->velocity[0] = oldvel[0];
995 ent->fields.server->velocity[1] = oldvel[1];
996 ent->fields.server->velocity[2] = 0;
997 clip = SV_FlyMove (ent, 0.1, NULL);
999 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1000 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1002 Con_DPrint("TryUnstick - success.\n");
1006 // go back to the original pos and try again
1007 VectorCopy (oldorg, ent->fields.server->origin);
1011 VectorClear (ent->fields.server->velocity);
1012 Con_DPrint("TryUnstick - failure.\n");
1017 =====================
1020 Only used by players
1021 ======================
1023 void SV_WalkMove (prvm_edict_t *ent)
1025 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1026 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1029 SV_CheckVelocity(ent);
1031 // do a regular slide move unless it looks like you ran into a step
1032 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1033 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1035 VectorCopy (ent->fields.server->origin, start_origin);
1036 VectorCopy (ent->fields.server->velocity, start_velocity);
1038 clip = SV_FlyMove (ent, sv.frametime, NULL);
1040 SV_CheckVelocity(ent);
1042 VectorCopy(ent->fields.server->origin, originalmove_origin);
1043 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1044 originalmove_clip = clip;
1045 originalmove_flags = (int)ent->fields.server->flags;
1046 originalmove_groundentity = ent->fields.server->groundentity;
1048 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1051 if (sv_nostep.integer)
1054 // if move didn't block on a step, return
1057 // if move was not trying to move into the step, return
1058 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1061 if (ent->fields.server->movetype != MOVETYPE_FLY)
1063 // return if gibbed by a trigger
1064 if (ent->fields.server->movetype != MOVETYPE_WALK)
1067 // only step up while jumping if that is enabled
1068 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1069 if (!oldonground && ent->fields.server->waterlevel == 0)
1073 // try moving up and forward to go up a step
1074 // back to start pos
1075 VectorCopy (start_origin, ent->fields.server->origin);
1076 VectorCopy (start_velocity, ent->fields.server->velocity);
1079 VectorClear (upmove);
1080 upmove[2] = sv_stepheight.value;
1081 // FIXME: don't link?
1082 SV_PushEntity(ent, upmove);
1085 ent->fields.server->velocity[2] = 0;
1086 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1087 ent->fields.server->velocity[2] += start_velocity[2];
1089 SV_CheckVelocity(ent);
1091 // check for stuckness, possibly due to the limited precision of floats
1092 // in the clipping hulls
1094 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1095 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1097 //Con_Printf("wall\n");
1098 // stepping up didn't make any progress, revert to original move
1099 VectorCopy(originalmove_origin, ent->fields.server->origin);
1100 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1101 //clip = originalmove_clip;
1102 ent->fields.server->flags = originalmove_flags;
1103 ent->fields.server->groundentity = originalmove_groundentity;
1104 // now try to unstick if needed
1105 //clip = SV_TryUnstick (ent, oldvel);
1109 //Con_Printf("step - ");
1111 // extra friction based on view angle
1112 if (clip & 2 && sv_wallfriction.integer)
1113 SV_WallFriction (ent, stepnormal);
1115 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1116 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)))
1120 VectorClear (downmove);
1121 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1122 // FIXME: don't link?
1123 downtrace = SV_PushEntity (ent, downmove);
1125 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1127 // LordHavoc: disabled this check so you can walk on monsters/players
1128 //if (ent->fields.server->solid == SOLID_BSP)
1130 //Con_Printf("onground\n");
1131 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1132 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1137 //Con_Printf("slope\n");
1138 // if the push down didn't end up on good ground, use the move without
1139 // the step up. This happens near wall / slope combinations, and can
1140 // cause the player to hop up higher on a slope too steep to climb
1141 VectorCopy(originalmove_origin, ent->fields.server->origin);
1142 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1143 //clip = originalmove_clip;
1144 ent->fields.server->flags = originalmove_flags;
1145 ent->fields.server->groundentity = originalmove_groundentity;
1148 SV_CheckVelocity(ent);
1151 //============================================================================
1157 Entities that are "stuck" to another entity
1160 void SV_Physics_Follow (prvm_edict_t *ent)
1162 vec3_t vf, vr, vu, angles, v;
1166 if (!SV_RunThink (ent))
1169 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1170 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1171 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])
1173 // quick case for no rotation
1174 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1178 angles[0] = -ent->fields.server->punchangle[0];
1179 angles[1] = ent->fields.server->punchangle[1];
1180 angles[2] = ent->fields.server->punchangle[2];
1181 AngleVectors (angles, vf, vr, vu);
1182 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];
1183 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];
1184 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];
1185 angles[0] = -e->fields.server->angles[0];
1186 angles[1] = e->fields.server->angles[1];
1187 angles[2] = e->fields.server->angles[2];
1188 AngleVectors (angles, vf, vr, vu);
1189 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1190 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1191 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1193 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1194 SV_LinkEdict (ent, true);
1198 ==============================================================================
1202 ==============================================================================
1207 SV_CheckWaterTransition
1211 void SV_CheckWaterTransition (prvm_edict_t *ent)
1214 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1215 if (!ent->fields.server->watertype)
1217 // just spawned here
1218 ent->fields.server->watertype = cont;
1219 ent->fields.server->waterlevel = 1;
1223 // check if the entity crossed into or out of water
1224 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1225 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1227 if (cont <= CONTENTS_WATER)
1229 ent->fields.server->watertype = cont;
1230 ent->fields.server->waterlevel = 1;
1234 ent->fields.server->watertype = CONTENTS_EMPTY;
1235 ent->fields.server->waterlevel = 0;
1243 Toss, bounce, and fly movement. When onground, do nothing.
1246 void SV_Physics_Toss (prvm_edict_t *ent)
1251 // if onground, return without moving
1252 if ((int)ent->fields.server->flags & FL_ONGROUND)
1254 // don't stick to ground if onground and moving upward
1255 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1256 ent->fields.server->flags -= FL_ONGROUND;
1259 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1260 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1262 // if ent was supported by a brush model on previous frame,
1263 // and groundentity is now freed, set groundentity to 0 (floating)
1264 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1266 // leave it suspended in the air
1267 ent->fields.server->groundentity = 0;
1272 ent->priv.server->suspendedinairflag = false;
1274 SV_CheckVelocity (ent);
1277 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1278 SV_AddGravity (ent);
1281 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1284 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1285 trace = SV_PushEntity (ent, move);
1286 if (ent->priv.server->free)
1288 if (trace.startsolid)
1290 // try to unstick the entity
1291 SV_UnstickEntity(ent);
1292 trace = SV_PushEntity (ent, move);
1293 if (ent->priv.server->free)
1297 if (trace.fraction < 1)
1299 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1301 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1302 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1304 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1307 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1308 // LordHavoc: fixed grenades not bouncing when fired down a slope
1309 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1311 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1312 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1314 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1315 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1316 VectorClear (ent->fields.server->velocity);
1317 VectorClear (ent->fields.server->avelocity);
1320 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1324 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1326 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1327 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1328 VectorClear (ent->fields.server->velocity);
1329 VectorClear (ent->fields.server->avelocity);
1332 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1337 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1338 if (trace.plane.normal[2] > 0.7)
1340 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1341 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1342 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1343 ent->priv.server->suspendedinairflag = true;
1344 VectorClear (ent->fields.server->velocity);
1345 VectorClear (ent->fields.server->avelocity);
1348 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1352 // check for in water
1353 SV_CheckWaterTransition (ent);
1357 ===============================================================================
1361 ===============================================================================
1368 Monsters freefall when they don't have a ground entity, otherwise
1369 all movement is done with discrete steps.
1371 This is also used for objects that have become still on the ground, but
1372 will fall if the floor is pulled out from under them.
1375 void SV_Physics_Step (prvm_edict_t *ent)
1377 int flags = (int)ent->fields.server->flags;
1378 // don't fall at all if fly/swim
1379 if (!(flags & (FL_FLY | FL_SWIM)))
1381 if (flags & FL_ONGROUND)
1383 // freefall if onground and moving upward
1384 // freefall if not standing on a world surface (it may be a lift)
1385 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1386 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1388 ent->fields.server->flags -= FL_ONGROUND;
1390 SV_CheckVelocity(ent);
1391 SV_FlyMove(ent, sv.frametime, NULL);
1392 SV_LinkEdict(ent, true);
1397 // freefall if not onground
1398 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1401 SV_CheckVelocity(ent);
1402 SV_FlyMove(ent, sv.frametime, NULL);
1403 SV_LinkEdict(ent, true);
1406 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1407 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1414 SV_CheckWaterTransition(ent);
1417 //============================================================================
1419 static void SV_Physics_Entity (prvm_edict_t *ent)
1421 // don't run a move on newly spawned projectiles as it messes up movement
1422 // interpolation and rocket trails
1423 qboolean runmove = ent->priv.server->move;
1424 ent->priv.server->move = true;
1425 switch ((int) ent->fields.server->movetype)
1428 case MOVETYPE_FAKEPUSH:
1429 SV_Physics_Pusher (ent);
1432 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1433 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1436 case MOVETYPE_FOLLOW:
1437 SV_Physics_Follow (ent);
1439 case MOVETYPE_NOCLIP:
1440 if (SV_RunThink(ent))
1443 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1444 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1446 SV_LinkEdict(ent, false);
1449 SV_Physics_Step (ent);
1452 if (SV_RunThink (ent))
1454 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1455 SV_AddGravity (ent);
1456 SV_CheckStuck (ent);
1458 SV_LinkEdict (ent, true);
1462 case MOVETYPE_BOUNCE:
1463 case MOVETYPE_BOUNCEMISSILE:
1464 case MOVETYPE_FLYMISSILE:
1467 if (SV_RunThink (ent) && runmove)
1468 SV_Physics_Toss (ent);
1471 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1476 void SV_ApplyClientMove (void);
1477 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1479 SV_ApplyClientMove();
1480 // make sure the velocity is sane (not a NaN)
1481 SV_CheckVelocity(ent);
1482 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1483 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1485 prog->globals.server->time = sv.time;
1486 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1487 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1491 // make sure the velocity is sane (not a NaN)
1492 SV_CheckVelocity(ent);
1493 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1494 // player_run/player_stand1 does not horribly malfunction if the
1495 // velocity becomes a number that is both == 0 and != 0
1496 // (sounds to me like NaN but to be absolutely safe...)
1497 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1498 VectorClear(ent->fields.server->velocity);
1499 // call standard client pre-think
1500 prog->globals.server->time = sv.time;
1501 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1502 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1503 SV_CheckVelocity (ent);
1505 switch ((int) ent->fields.server->movetype)
1508 case MOVETYPE_FAKEPUSH:
1509 SV_Physics_Pusher (ent);
1512 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1513 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1516 case MOVETYPE_FOLLOW:
1517 SV_Physics_Follow (ent);
1519 case MOVETYPE_NOCLIP:
1520 if (SV_RunThink(ent))
1523 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1524 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1528 SV_Physics_Step (ent);
1531 if (SV_RunThink (ent))
1533 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1534 SV_AddGravity (ent);
1535 SV_CheckStuck (ent);
1540 case MOVETYPE_BOUNCE:
1541 case MOVETYPE_BOUNCEMISSILE:
1542 case MOVETYPE_FLYMISSILE:
1544 if (SV_RunThink (ent))
1545 SV_Physics_Toss (ent);
1548 if (SV_RunThink (ent))
1550 SV_CheckWater (ent);
1555 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1559 SV_CheckVelocity (ent);
1561 // call standard player post-think
1562 SV_LinkEdict (ent, true);
1564 SV_CheckVelocity (ent);
1566 prog->globals.server->time = sv.time;
1567 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1568 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1577 void SV_Physics (void)
1582 // let the progs know that a new frame has started
1583 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1584 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1585 prog->globals.server->time = sv.time;
1586 prog->globals.server->frametime = sv.frametime;
1587 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1590 // treat each object in turn
1593 // if force_retouch, relink all the entities
1594 if (prog->globals.server->force_retouch > 0)
1595 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1596 if (!ent->priv.server->free)
1597 SV_LinkEdict (ent, true); // force retouch even for stationary
1599 // run physics on the client entities
1600 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1602 if (!ent->priv.server->free)
1604 // don't do physics on disconnected clients, FrikBot relies on this
1605 if (!host_client->spawned)
1606 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1607 // don't run physics here if running asynchronously
1608 else if (!host_client->movesequence)
1609 SV_Physics_ClientEntity(ent);
1613 // run physics on all the non-client entities
1614 if (!sv_freezenonclients.integer)
1615 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1616 if (!ent->priv.server->free)
1617 SV_Physics_Entity(ent);
1619 if (prog->globals.server->force_retouch > 0)
1620 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1622 // LordHavoc: endframe support
1625 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1626 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1627 prog->globals.server->time = sv.time;
1628 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1631 // decrement prog->num_edicts if the highest number entities died
1632 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1634 if (!sv_freezenonclients.integer)
1635 sv.time += sv.frametime;
1639 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1642 float gravity, savesolid;
1644 prvm_edict_t tempent, *tent;
1649 // copy the vars over
1650 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1651 // set up the temp entity to point to the copied vars
1653 tent->fields.server = &vars;
1655 savesolid = tossent->fields.server->solid;
1656 tossent->fields.server->solid = SOLID_NOT;
1658 // this has to fetch the field from the original edict, since our copy is truncated
1659 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1660 if (val != NULL && val->_float != 0)
1661 gravity = val->_float;
1664 gravity *= sv_gravity.value * 0.05;
1666 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1668 SV_CheckVelocity (tent);
1669 tent->fields.server->velocity[2] -= gravity;
1670 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1671 VectorScale (tent->fields.server->velocity, 0.05, move);
1672 VectorAdd (tent->fields.server->origin, move, end);
1673 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1674 VectorCopy (trace.endpos, tent->fields.server->origin);
1676 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1679 tossent->fields.server->solid = savesolid;
1680 trace.fraction = 0; // not relevant