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 // this has been disabled so that you can't jump when you are stepping
1128 // up while already jumping (also known as the Quake2 stair jump bug)
1130 // LordHavoc: disabled this check so you can walk on monsters/players
1131 //if (ent->fields.server->solid == SOLID_BSP)
1133 //Con_Printf("onground\n");
1134 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1135 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1141 //Con_Printf("slope\n");
1142 // if the push down didn't end up on good ground, use the move without
1143 // the step up. This happens near wall / slope combinations, and can
1144 // cause the player to hop up higher on a slope too steep to climb
1145 VectorCopy(originalmove_origin, ent->fields.server->origin);
1146 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1147 //clip = originalmove_clip;
1148 ent->fields.server->flags = originalmove_flags;
1149 ent->fields.server->groundentity = originalmove_groundentity;
1152 SV_CheckVelocity(ent);
1155 //============================================================================
1161 Entities that are "stuck" to another entity
1164 void SV_Physics_Follow (prvm_edict_t *ent)
1166 vec3_t vf, vr, vu, angles, v;
1170 if (!SV_RunThink (ent))
1173 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1174 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1175 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])
1177 // quick case for no rotation
1178 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1182 angles[0] = -ent->fields.server->punchangle[0];
1183 angles[1] = ent->fields.server->punchangle[1];
1184 angles[2] = ent->fields.server->punchangle[2];
1185 AngleVectors (angles, vf, vr, vu);
1186 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];
1187 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];
1188 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];
1189 angles[0] = -e->fields.server->angles[0];
1190 angles[1] = e->fields.server->angles[1];
1191 angles[2] = e->fields.server->angles[2];
1192 AngleVectors (angles, vf, vr, vu);
1193 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1194 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1195 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1197 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1198 SV_LinkEdict (ent, true);
1202 ==============================================================================
1206 ==============================================================================
1211 SV_CheckWaterTransition
1215 void SV_CheckWaterTransition (prvm_edict_t *ent)
1218 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1219 if (!ent->fields.server->watertype)
1221 // just spawned here
1222 ent->fields.server->watertype = cont;
1223 ent->fields.server->waterlevel = 1;
1227 // check if the entity crossed into or out of water
1228 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1229 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1231 if (cont <= CONTENTS_WATER)
1233 ent->fields.server->watertype = cont;
1234 ent->fields.server->waterlevel = 1;
1238 ent->fields.server->watertype = CONTENTS_EMPTY;
1239 ent->fields.server->waterlevel = 0;
1247 Toss, bounce, and fly movement. When onground, do nothing.
1250 void SV_Physics_Toss (prvm_edict_t *ent)
1255 // if onground, return without moving
1256 if ((int)ent->fields.server->flags & FL_ONGROUND)
1258 // don't stick to ground if onground and moving upward
1259 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1260 ent->fields.server->flags -= FL_ONGROUND;
1263 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1264 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1266 // if ent was supported by a brush model on previous frame,
1267 // and groundentity is now freed, set groundentity to 0 (floating)
1268 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1270 // leave it suspended in the air
1271 ent->fields.server->groundentity = 0;
1276 ent->priv.server->suspendedinairflag = false;
1278 SV_CheckVelocity (ent);
1281 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1282 SV_AddGravity (ent);
1285 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1288 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1289 trace = SV_PushEntity (ent, move);
1290 if (ent->priv.server->free)
1292 if (trace.startsolid)
1294 // try to unstick the entity
1295 SV_UnstickEntity(ent);
1296 trace = SV_PushEntity (ent, move);
1297 if (ent->priv.server->free)
1301 if (trace.fraction < 1)
1303 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1305 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1306 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1308 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1311 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1312 // LordHavoc: fixed grenades not bouncing when fired down a slope
1313 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1315 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1316 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1318 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1319 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1320 VectorClear (ent->fields.server->velocity);
1321 VectorClear (ent->fields.server->avelocity);
1324 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1328 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1330 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1331 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1332 VectorClear (ent->fields.server->velocity);
1333 VectorClear (ent->fields.server->avelocity);
1336 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1341 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1342 if (trace.plane.normal[2] > 0.7)
1344 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1345 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1346 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1347 ent->priv.server->suspendedinairflag = true;
1348 VectorClear (ent->fields.server->velocity);
1349 VectorClear (ent->fields.server->avelocity);
1352 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1356 // check for in water
1357 SV_CheckWaterTransition (ent);
1361 ===============================================================================
1365 ===============================================================================
1372 Monsters freefall when they don't have a ground entity, otherwise
1373 all movement is done with discrete steps.
1375 This is also used for objects that have become still on the ground, but
1376 will fall if the floor is pulled out from under them.
1379 void SV_Physics_Step (prvm_edict_t *ent)
1381 int flags = (int)ent->fields.server->flags;
1382 // don't fall at all if fly/swim
1383 if (!(flags & (FL_FLY | FL_SWIM)))
1385 if (flags & FL_ONGROUND)
1387 // freefall if onground and moving upward
1388 // freefall if not standing on a world surface (it may be a lift)
1389 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1390 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1392 ent->fields.server->flags -= FL_ONGROUND;
1394 SV_CheckVelocity(ent);
1395 SV_FlyMove(ent, sv.frametime, NULL);
1396 SV_LinkEdict(ent, true);
1401 // freefall if not onground
1402 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1405 SV_CheckVelocity(ent);
1406 SV_FlyMove(ent, sv.frametime, NULL);
1407 SV_LinkEdict(ent, true);
1410 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1411 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1418 SV_CheckWaterTransition(ent);
1421 //============================================================================
1423 static void SV_Physics_Entity (prvm_edict_t *ent)
1425 // don't run a move on newly spawned projectiles as it messes up movement
1426 // interpolation and rocket trails
1427 qboolean runmove = ent->priv.server->move;
1428 ent->priv.server->move = true;
1429 switch ((int) ent->fields.server->movetype)
1432 case MOVETYPE_FAKEPUSH:
1433 SV_Physics_Pusher (ent);
1436 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1437 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1440 case MOVETYPE_FOLLOW:
1441 SV_Physics_Follow (ent);
1443 case MOVETYPE_NOCLIP:
1444 if (SV_RunThink(ent))
1447 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1448 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1450 SV_LinkEdict(ent, false);
1453 SV_Physics_Step (ent);
1456 if (SV_RunThink (ent))
1458 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1459 SV_AddGravity (ent);
1460 SV_CheckStuck (ent);
1462 SV_LinkEdict (ent, true);
1466 case MOVETYPE_BOUNCE:
1467 case MOVETYPE_BOUNCEMISSILE:
1468 case MOVETYPE_FLYMISSILE:
1471 if (SV_RunThink (ent) && runmove)
1472 SV_Physics_Toss (ent);
1475 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1480 void SV_ApplyClientMove (void);
1481 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1483 SV_ApplyClientMove();
1484 // make sure the velocity is sane (not a NaN)
1485 SV_CheckVelocity(ent);
1486 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1487 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1489 prog->globals.server->time = sv.time;
1490 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1491 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1495 // make sure the velocity is sane (not a NaN)
1496 SV_CheckVelocity(ent);
1497 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1498 // player_run/player_stand1 does not horribly malfunction if the
1499 // velocity becomes a number that is both == 0 and != 0
1500 // (sounds to me like NaN but to be absolutely safe...)
1501 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1502 VectorClear(ent->fields.server->velocity);
1503 // call standard client pre-think
1504 prog->globals.server->time = sv.time;
1505 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1506 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1507 SV_CheckVelocity (ent);
1509 switch ((int) ent->fields.server->movetype)
1512 case MOVETYPE_FAKEPUSH:
1513 SV_Physics_Pusher (ent);
1516 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1517 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1520 case MOVETYPE_FOLLOW:
1521 SV_Physics_Follow (ent);
1523 case MOVETYPE_NOCLIP:
1524 if (SV_RunThink(ent))
1527 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1528 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1532 SV_Physics_Step (ent);
1535 if (SV_RunThink (ent))
1537 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1538 SV_AddGravity (ent);
1539 SV_CheckStuck (ent);
1544 case MOVETYPE_BOUNCE:
1545 case MOVETYPE_BOUNCEMISSILE:
1546 case MOVETYPE_FLYMISSILE:
1548 if (SV_RunThink (ent))
1549 SV_Physics_Toss (ent);
1552 if (SV_RunThink (ent))
1554 SV_CheckWater (ent);
1559 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1563 SV_CheckVelocity (ent);
1565 // call standard player post-think
1566 SV_LinkEdict (ent, true);
1568 SV_CheckVelocity (ent);
1570 prog->globals.server->time = sv.time;
1571 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1572 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1581 void SV_Physics (void)
1586 // let the progs know that a new frame has started
1587 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1588 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1589 prog->globals.server->time = sv.time;
1590 prog->globals.server->frametime = sv.frametime;
1591 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1594 // treat each object in turn
1597 // if force_retouch, relink all the entities
1598 if (prog->globals.server->force_retouch > 0)
1599 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1600 if (!ent->priv.server->free)
1601 SV_LinkEdict (ent, true); // force retouch even for stationary
1603 // run physics on the client entities
1604 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1606 if (!ent->priv.server->free)
1608 // don't do physics on disconnected clients, FrikBot relies on this
1609 if (!host_client->spawned)
1610 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1611 // don't run physics here if running asynchronously
1612 else if (!host_client->movesequence)
1613 SV_Physics_ClientEntity(ent);
1617 // run physics on all the non-client entities
1618 if (!sv_freezenonclients.integer)
1619 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1620 if (!ent->priv.server->free)
1621 SV_Physics_Entity(ent);
1623 if (prog->globals.server->force_retouch > 0)
1624 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1626 // LordHavoc: endframe support
1629 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1630 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1631 prog->globals.server->time = sv.time;
1632 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1635 // decrement prog->num_edicts if the highest number entities died
1636 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1638 if (!sv_freezenonclients.integer)
1639 sv.time += sv.frametime;
1643 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1646 float gravity, savesolid;
1648 prvm_edict_t tempent, *tent;
1653 // copy the vars over
1654 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1655 // set up the temp entity to point to the copied vars
1657 tent->fields.server = &vars;
1659 savesolid = tossent->fields.server->solid;
1660 tossent->fields.server->solid = SOLID_NOT;
1662 // this has to fetch the field from the original edict, since our copy is truncated
1663 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1664 if (val != NULL && val->_float != 0)
1665 gravity = val->_float;
1668 gravity *= sv_gravity.value * 0.05;
1670 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1672 SV_CheckVelocity (tent);
1673 tent->fields.server->velocity[2] -= gravity;
1674 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1675 VectorScale (tent->fields.server->velocity, 0.05, move);
1676 VectorAdd (tent->fields.server->origin, move, end);
1677 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1678 VectorCopy (trace.endpos, tent->fields.server->origin);
1680 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1683 tossent->fields.server->solid = savesolid;
1684 trace.fraction = 0; // not relevant