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.
23 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
29 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.
31 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
40 solid_edge items only clip against bsp models.
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
46 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
47 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
48 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
49 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
50 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
51 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
52 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
53 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
54 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
56 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)"};
57 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)"};
59 #define MOVE_EPSILON 0.01
61 void SV_Physics_Toss (prvm_edict_t *ent);
63 void SV_Phys_Init (void)
65 Cvar_RegisterVariable(&sv_stepheight);
66 Cvar_RegisterVariable(&sv_jumpstep);
67 Cvar_RegisterVariable(&sv_wallfriction);
68 Cvar_RegisterVariable(&sv_newflymove);
69 Cvar_RegisterVariable(&sv_freezenonclients);
71 Cvar_RegisterVariable(&sv_playerphysicsqc);
73 Cvar_RegisterVariable(&sv_sound_watersplash);
74 Cvar_RegisterVariable(&sv_sound_land);
81 returns true if the entity is in solid currently
84 static int SV_TestEntityPosition (prvm_edict_t *ent, int movemode)
86 return SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, movemode, ent).startsolid;
94 void SV_CheckAllEnts (void)
99 // see if any solid entities are inside the final position
100 check = PRVM_NEXT_EDICT(prog->edicts);
101 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
103 if (check->priv.server->free)
105 if (check->fields.server->movetype == MOVETYPE_PUSH
106 || check->fields.server->movetype == MOVETYPE_NONE
107 || check->fields.server->movetype == MOVETYPE_FOLLOW
108 || check->fields.server->movetype == MOVETYPE_NOCLIP)
111 if (SV_TestEntityPosition (check, MOVE_NORMAL))
112 Con_Print("entity in invalid position\n");
121 void SV_CheckVelocity (prvm_edict_t *ent)
129 for (i=0 ; i<3 ; i++)
131 if (IS_NAN(ent->fields.server->velocity[i]))
133 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
134 ent->fields.server->velocity[i] = 0;
136 if (IS_NAN(ent->fields.server->origin[i]))
138 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
139 ent->fields.server->origin[i] = 0;
143 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
144 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
145 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
147 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
148 ent->fields.server->velocity[0] *= wishspeed;
149 ent->fields.server->velocity[1] *= wishspeed;
150 ent->fields.server->velocity[2] *= wishspeed;
158 Runs thinking code if time. There is some play in the exact time the think
159 function will be called, because it is called before any movement is done
160 in a frame. Not used for pushmove objects, because they must be exact.
161 Returns false if the entity removed itself.
164 qboolean SV_RunThink (prvm_edict_t *ent)
168 thinktime = ent->fields.server->nextthink;
169 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
172 // don't let things stay in the past.
173 // it is possible to start that way by a trigger with a local time.
174 if (thinktime < sv.time)
177 ent->fields.server->nextthink = 0;
178 prog->globals.server->time = thinktime;
179 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
180 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
181 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
182 return !ent->priv.server->free;
189 Two entities have touched, so run their touch functions
192 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
194 int old_self, old_other;
195 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
198 old_self = prog->globals.server->self;
199 old_other = prog->globals.server->other;
201 prog->globals.server->time = sv.time;
202 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
204 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
205 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
206 prog->globals.server->trace_allsolid = trace->allsolid;
207 prog->globals.server->trace_startsolid = trace->startsolid;
208 prog->globals.server->trace_fraction = trace->fraction;
209 prog->globals.server->trace_inwater = trace->inwater;
210 prog->globals.server->trace_inopen = trace->inopen;
211 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
212 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
213 prog->globals.server->trace_plane_dist = trace->plane.dist;
215 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
217 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
218 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
219 val->_float = trace->startsupercontents;
220 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
221 val->_float = trace->hitsupercontents;
222 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
223 val->_float = trace->hitq3surfaceflags;
224 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
226 if (trace->hittexture)
228 char *s = VM_GetTempString();
229 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
230 val->string = PRVM_SetEngineString(s);
235 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
238 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
240 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
241 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
242 prog->globals.server->trace_allsolid = false;
243 prog->globals.server->trace_startsolid = false;
244 prog->globals.server->trace_fraction = 1;
245 prog->globals.server->trace_inwater = false;
246 prog->globals.server->trace_inopen = true;
247 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
248 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
249 prog->globals.server->trace_plane_dist = 0;
250 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
251 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
253 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
255 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
257 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
259 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
262 prog->globals.server->self = old_self;
263 prog->globals.server->other = old_other;
271 Slide off of the impacting object
272 returns the blocked flags (1 = floor, 2 = step / wall)
275 #define STOP_EPSILON 0.1
276 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
281 backoff = -DotProduct (in, normal) * overbounce;
282 VectorMA(in, backoff, normal, out);
284 for (i = 0;i < 3;i++)
285 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
294 The basic solid body movement clip that slides along multiple planes
295 Returns the clipflags if the velocity was modified (hit something solid)
299 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
302 // LordHavoc: increased from 5 to 32
303 #define MAX_CLIP_PLANES 32
304 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
306 int blocked, bumpcount;
307 int i, j, impact, numplanes;
309 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
312 VectorCopy(ent->fields.server->velocity, original_velocity);
313 VectorCopy(ent->fields.server->velocity, primal_velocity);
316 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
318 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
321 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
322 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
324 //if (trace.fraction < 0.002)
329 VectorCopy(ent->fields.server->origin, start);
330 start[2] += 3;//0.03125;
331 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
332 end[2] += 3;//0.03125;
333 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
334 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)))
336 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
342 for (i = 0;i < numplanes;i++)
344 VectorCopy(ent->fields.server->origin, start);
345 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
346 VectorMA(start, 3, planes[i], start);
347 VectorMA(end, 3, planes[i], end);
348 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
349 if (trace.fraction < testtrace.fraction)
352 VectorCopy(start, ent->fields.server->origin);
357 // VectorAdd(ent->fields.server->origin, planes[j], start);
363 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);
364 if (trace.fraction < 1)
365 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
370 if (trace.startsolid)
372 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
373 // entity is trapped in another solid
374 VectorClear(ent->fields.server->velocity);
379 // break if it moved the entire distance
380 if (trace.fraction == 1)
382 VectorCopy(trace.endpos, ent->fields.server->origin);
388 Con_Printf ("SV_FlyMove: !trace.ent");
389 trace.ent = prog->edicts;
392 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
396 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
400 if (trace.plane.normal[2])
402 if (trace.plane.normal[2] > 0.7)
406 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
407 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
414 // save the trace for player extrafriction
416 VectorCopy(trace.plane.normal, stepnormal);
419 if (trace.fraction >= 0.001)
421 // actually covered some distance
422 VectorCopy(trace.endpos, ent->fields.server->origin);
423 VectorCopy(ent->fields.server->velocity, original_velocity);
427 // run the impact function
430 SV_Impact(ent, &trace);
432 // break if removed by the impact function
433 if (ent->priv.server->free)
437 time_left *= 1 - trace.fraction;
439 // clipped to another plane
440 if (numplanes >= MAX_CLIP_PLANES)
442 // this shouldn't really happen
443 VectorClear(ent->fields.server->velocity);
449 for (i = 0;i < numplanes;i++)
450 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
454 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
459 VectorCopy(trace.plane.normal, planes[numplanes]);
462 if (sv_newflymove.integer)
463 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
466 // modify original_velocity so it parallels all of the clip planes
467 for (i = 0;i < numplanes;i++)
469 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
470 for (j = 0;j < numplanes;j++)
475 if (DotProduct(new_velocity, planes[j]) < 0)
485 // go along this plane
486 VectorCopy(new_velocity, ent->fields.server->velocity);
490 // go along the crease
493 VectorClear(ent->fields.server->velocity);
497 CrossProduct(planes[0], planes[1], dir);
498 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
499 VectorNormalize(dir);
500 d = DotProduct(dir, ent->fields.server->velocity);
501 VectorScale(dir, d, ent->fields.server->velocity);
505 // if current velocity is against the original velocity,
506 // stop dead to avoid tiny occilations in sloping corners
507 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
509 VectorClear(ent->fields.server->velocity);
514 //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]);
517 if ((blocked & 1) == 0 && bumpcount > 1)
519 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
520 // flag ONGROUND if there's ground under it
521 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
533 void SV_AddGravity (prvm_edict_t *ent)
538 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
539 if (val!=0 && val->_float)
540 ent_gravity = val->_float;
543 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
548 ===============================================================================
552 ===============================================================================
559 Does not change the entities velocity at all
562 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
568 VectorAdd (ent->fields.server->origin, push, end);
570 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
572 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
573 type = MOVE_NOMONSTERS; // only clip against bmodels
577 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
579 VectorCopy (trace.endpos, ent->fields.server->origin);
580 SV_LinkEdict (ent, true);
582 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
583 SV_Impact (ent, &trace);
594 void SV_PushMove (prvm_edict_t *pusher, float movetime)
597 float savesolid, movetime2, pushltime;
598 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
600 int numcheckentities;
601 static prvm_edict_t *checkentities[MAX_EDICTS];
602 model_t *pushermodel;
605 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])
607 pusher->fields.server->ltime += movetime;
611 switch ((int) pusher->fields.server->solid)
613 // LordHavoc: valid pusher types
617 case SOLID_CORPSE: // LordHavoc: this would be weird...
619 // LordHavoc: no collisions
622 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
623 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
624 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
625 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
626 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
627 pusher->fields.server->ltime += movetime;
628 SV_LinkEdict (pusher, false);
631 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
634 index = (int) pusher->fields.server->modelindex;
635 if (index < 1 || index >= MAX_MODELS)
637 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
640 pushermodel = sv.models[index];
642 movetime2 = movetime;
643 VectorScale(pusher->fields.server->velocity, movetime2, move1);
644 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
645 if (moveangle[0] || moveangle[2])
647 for (i = 0;i < 3;i++)
651 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
652 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
656 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
657 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
661 else if (moveangle[1])
663 for (i = 0;i < 3;i++)
667 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
668 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
672 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
673 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
679 for (i = 0;i < 3;i++)
683 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
684 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
688 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
689 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
694 VectorNegate (moveangle, a);
695 AngleVectorsFLU (a, forward, left, up);
697 VectorCopy (pusher->fields.server->origin, pushorig);
698 VectorCopy (pusher->fields.server->angles, pushang);
699 pushltime = pusher->fields.server->ltime;
701 // move the pusher to its final position
703 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
704 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
705 pusher->fields.server->ltime += movetime;
706 SV_LinkEdict (pusher, false);
708 savesolid = pusher->fields.server->solid;
710 // see if any solid entities are inside the final position
713 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
714 for (e = 0;e < numcheckentities;e++)
716 prvm_edict_t *check = checkentities[e];
717 if (check->fields.server->movetype == MOVETYPE_NONE
718 || check->fields.server->movetype == MOVETYPE_PUSH
719 || check->fields.server->movetype == MOVETYPE_FOLLOW
720 || check->fields.server->movetype == MOVETYPE_NOCLIP
721 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
724 // if the entity is standing on the pusher, it will definitely be moved
725 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
727 // if the entity is not inside the pusher's final position, leave it alone
728 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
730 // remove the onground flag for non-players
731 if (check->fields.server->movetype != MOVETYPE_WALK)
732 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
736 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
739 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
740 org2[0] = DotProduct (org, forward);
741 org2[1] = DotProduct (org, left);
742 org2[2] = DotProduct (org, up);
743 VectorSubtract (org2, org, move);
744 VectorAdd (move, move1, move);
747 VectorCopy (move1, move);
749 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
750 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
751 sv.moved_edicts[num_moved++] = check;
753 // try moving the contacted entity
754 pusher->fields.server->solid = SOLID_NOT;
755 trace = SV_PushEntity (check, move);
756 // FIXME: turn players specially
757 check->fields.server->angles[1] += trace.fraction * moveangle[1];
758 pusher->fields.server->solid = savesolid; // was SOLID_BSP
760 // if it is still inside the pusher, block
761 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
763 // try moving the contacted entity a tiny bit further to account for precision errors
765 pusher->fields.server->solid = SOLID_NOT;
766 VectorScale(move, 1.1, move2);
767 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
768 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
769 SV_PushEntity (check, move2);
770 pusher->fields.server->solid = savesolid;
771 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
773 // try moving the contacted entity a tiny bit less to account for precision errors
774 pusher->fields.server->solid = SOLID_NOT;
775 VectorScale(move, 0.9, move2);
776 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
777 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
778 SV_PushEntity (check, move2);
779 pusher->fields.server->solid = savesolid;
780 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
782 // still inside pusher, so it's really blocked
785 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
787 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
790 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
791 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
795 VectorCopy (pushorig, pusher->fields.server->origin);
796 VectorCopy (pushang, pusher->fields.server->angles);
797 pusher->fields.server->ltime = pushltime;
798 SV_LinkEdict (pusher, false);
800 // move back any entities we already moved
801 for (i = 0;i < num_moved;i++)
803 prvm_edict_t *ed = sv.moved_edicts[i];
804 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
805 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
806 SV_LinkEdict (ed, false);
809 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
810 if (pusher->fields.server->blocked)
812 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
813 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
814 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
821 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
822 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
823 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
832 void SV_Physics_Pusher (prvm_edict_t *ent)
834 float thinktime, oldltime, movetime;
836 oldltime = ent->fields.server->ltime;
838 thinktime = ent->fields.server->nextthink;
839 if (thinktime < ent->fields.server->ltime + sv.frametime)
841 movetime = thinktime - ent->fields.server->ltime;
846 movetime = sv.frametime;
849 // advances ent->fields.server->ltime if not blocked
850 SV_PushMove (ent, movetime);
852 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
854 ent->fields.server->nextthink = 0;
855 prog->globals.server->time = sv.time;
856 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
857 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
858 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
864 ===============================================================================
868 ===============================================================================
875 This is a big hack to try and fix the rare case of getting stuck in the world
879 void SV_CheckStuck (prvm_edict_t *ent)
884 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
886 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
890 VectorCopy (ent->fields.server->origin, org);
891 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
892 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
894 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
895 SV_LinkEdict (ent, true);
899 for (z=0 ; z< 18 ; z++)
900 for (i=-1 ; i <= 1 ; i++)
901 for (j=-1 ; j <= 1 ; j++)
903 ent->fields.server->origin[0] = org[0] + i;
904 ent->fields.server->origin[1] = org[1] + j;
905 ent->fields.server->origin[2] = org[2] + z;
906 if (!SV_TestEntityPosition(ent, MOVE_NORMAL))
908 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
909 SV_LinkEdict (ent, true);
914 VectorCopy (org, ent->fields.server->origin);
915 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
918 static void SV_UnstickEntity (prvm_edict_t *ent)
923 // if not stuck in a bmodel, just return
924 if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
927 VectorCopy (ent->fields.server->origin, org);
929 for (z=0 ; z< 18 ; z += 6)
930 for (i=-1 ; i <= 1 ; i++)
931 for (j=-1 ; j <= 1 ; j++)
933 ent->fields.server->origin[0] = org[0] + i;
934 ent->fields.server->origin[1] = org[1] + j;
935 ent->fields.server->origin[2] = org[2] + z;
936 if (!SV_TestEntityPosition(ent, MOVE_NOMONSTERS))
938 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
939 SV_LinkEdict (ent, true);
944 VectorCopy (org, ent->fields.server->origin);
945 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
954 qboolean SV_CheckWater (prvm_edict_t *ent)
959 point[0] = ent->fields.server->origin[0];
960 point[1] = ent->fields.server->origin[1];
961 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
963 ent->fields.server->waterlevel = 0;
964 ent->fields.server->watertype = CONTENTS_EMPTY;
965 cont = SV_PointSuperContents(point);
966 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
968 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
969 ent->fields.server->waterlevel = 1;
970 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
971 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
973 ent->fields.server->waterlevel = 2;
974 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
975 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
976 ent->fields.server->waterlevel = 3;
980 return ent->fields.server->waterlevel > 1;
989 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
992 vec3_t forward, into, side;
994 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
995 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
997 // cut the tangential velocity
998 i = DotProduct (stepnormal, ent->fields.server->velocity);
999 VectorScale (stepnormal, i, into);
1000 VectorSubtract (ent->fields.server->velocity, into, side);
1001 ent->fields.server->velocity[0] = side[0] * (1 + d);
1002 ent->fields.server->velocity[1] = side[1] * (1 + d);
1007 =====================
1010 Player has come to a dead stop, possibly due to the problem with limited
1011 float precision at some angle joins in the BSP hull.
1013 Try fixing by pushing one pixel in each direction.
1015 This is a hack, but in the interest of good gameplay...
1016 ======================
1018 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1023 VectorCopy (ent->fields.server->origin, oldorg);
1026 for (i=0 ; i<8 ; i++)
1028 // try pushing a little in an axial direction
1031 case 0: dir[0] = 2; dir[1] = 0; break;
1032 case 1: dir[0] = 0; dir[1] = 2; break;
1033 case 2: dir[0] = -2; dir[1] = 0; break;
1034 case 3: dir[0] = 0; dir[1] = -2; break;
1035 case 4: dir[0] = 2; dir[1] = 2; break;
1036 case 5: dir[0] = -2; dir[1] = 2; break;
1037 case 6: dir[0] = 2; dir[1] = -2; break;
1038 case 7: dir[0] = -2; dir[1] = -2; break;
1041 SV_PushEntity (ent, dir);
1043 // retry the original move
1044 ent->fields.server->velocity[0] = oldvel[0];
1045 ent->fields.server->velocity[1] = oldvel[1];
1046 ent->fields.server->velocity[2] = 0;
1047 clip = SV_FlyMove (ent, 0.1, NULL);
1049 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1050 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1052 Con_DPrint("TryUnstick - success.\n");
1056 // go back to the original pos and try again
1057 VectorCopy (oldorg, ent->fields.server->origin);
1061 VectorClear (ent->fields.server->velocity);
1062 Con_DPrint("TryUnstick - failure.\n");
1067 =====================
1070 Only used by players
1071 ======================
1073 void SV_WalkMove (prvm_edict_t *ent)
1075 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1076 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1079 SV_CheckVelocity(ent);
1081 // do a regular slide move unless it looks like you ran into a step
1082 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1083 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1085 VectorCopy (ent->fields.server->origin, start_origin);
1086 VectorCopy (ent->fields.server->velocity, start_velocity);
1088 clip = SV_FlyMove (ent, sv.frametime, NULL);
1090 SV_CheckVelocity(ent);
1092 VectorCopy(ent->fields.server->origin, originalmove_origin);
1093 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1094 originalmove_clip = clip;
1095 originalmove_flags = (int)ent->fields.server->flags;
1096 originalmove_groundentity = ent->fields.server->groundentity;
1098 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1101 if (sv_nostep.integer)
1104 // if move didn't block on a step, return
1107 // if move was not trying to move into the step, return
1108 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1111 if (ent->fields.server->movetype != MOVETYPE_FLY)
1113 // return if gibbed by a trigger
1114 if (ent->fields.server->movetype != MOVETYPE_WALK)
1117 // only step up while jumping if that is enabled
1118 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1119 if (!oldonground && ent->fields.server->waterlevel == 0)
1123 // try moving up and forward to go up a step
1124 // back to start pos
1125 VectorCopy (start_origin, ent->fields.server->origin);
1126 VectorCopy (start_velocity, ent->fields.server->velocity);
1129 VectorClear (upmove);
1130 upmove[2] = sv_stepheight.value;
1131 // FIXME: don't link?
1132 SV_PushEntity(ent, upmove);
1135 ent->fields.server->velocity[2] = 0;
1136 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1137 ent->fields.server->velocity[2] += start_velocity[2];
1139 SV_CheckVelocity(ent);
1141 // check for stuckness, possibly due to the limited precision of floats
1142 // in the clipping hulls
1144 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1145 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1147 //Con_Printf("wall\n");
1148 // stepping up didn't make any progress, revert to original move
1149 VectorCopy(originalmove_origin, ent->fields.server->origin);
1150 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1151 //clip = originalmove_clip;
1152 ent->fields.server->flags = originalmove_flags;
1153 ent->fields.server->groundentity = originalmove_groundentity;
1154 // now try to unstick if needed
1155 //clip = SV_TryUnstick (ent, oldvel);
1159 //Con_Printf("step - ");
1161 // extra friction based on view angle
1162 if (clip & 2 && sv_wallfriction.integer)
1163 SV_WallFriction (ent, stepnormal);
1165 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1166 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)))
1170 VectorClear (downmove);
1171 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1172 // FIXME: don't link?
1173 downtrace = SV_PushEntity (ent, downmove);
1175 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1177 // this has been disabled so that you can't jump when you are stepping
1178 // up while already jumping (also known as the Quake2 stair jump bug)
1180 // LordHavoc: disabled this check so you can walk on monsters/players
1181 //if (ent->fields.server->solid == SOLID_BSP)
1183 //Con_Printf("onground\n");
1184 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1185 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1191 //Con_Printf("slope\n");
1192 // if the push down didn't end up on good ground, use the move without
1193 // the step up. This happens near wall / slope combinations, and can
1194 // cause the player to hop up higher on a slope too steep to climb
1195 VectorCopy(originalmove_origin, ent->fields.server->origin);
1196 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1197 //clip = originalmove_clip;
1198 ent->fields.server->flags = originalmove_flags;
1199 ent->fields.server->groundentity = originalmove_groundentity;
1202 SV_CheckVelocity(ent);
1205 //============================================================================
1211 Entities that are "stuck" to another entity
1214 void SV_Physics_Follow (prvm_edict_t *ent)
1216 vec3_t vf, vr, vu, angles, v;
1220 if (!SV_RunThink (ent))
1223 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1224 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1225 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])
1227 // quick case for no rotation
1228 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1232 angles[0] = -ent->fields.server->punchangle[0];
1233 angles[1] = ent->fields.server->punchangle[1];
1234 angles[2] = ent->fields.server->punchangle[2];
1235 AngleVectors (angles, vf, vr, vu);
1236 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];
1237 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];
1238 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];
1239 angles[0] = -e->fields.server->angles[0];
1240 angles[1] = e->fields.server->angles[1];
1241 angles[2] = e->fields.server->angles[2];
1242 AngleVectors (angles, vf, vr, vu);
1243 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1244 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1245 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1247 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1248 SV_LinkEdict (ent, true);
1252 ==============================================================================
1256 ==============================================================================
1261 SV_CheckWaterTransition
1265 void SV_CheckWaterTransition (prvm_edict_t *ent)
1268 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1269 if (!ent->fields.server->watertype)
1271 // just spawned here
1272 ent->fields.server->watertype = cont;
1273 ent->fields.server->waterlevel = 1;
1277 // check if the entity crossed into or out of water
1278 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1279 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1281 if (cont <= CONTENTS_WATER)
1283 ent->fields.server->watertype = cont;
1284 ent->fields.server->waterlevel = 1;
1288 ent->fields.server->watertype = CONTENTS_EMPTY;
1289 ent->fields.server->waterlevel = 0;
1297 Toss, bounce, and fly movement. When onground, do nothing.
1300 void SV_Physics_Toss (prvm_edict_t *ent)
1305 // if onground, return without moving
1306 if ((int)ent->fields.server->flags & FL_ONGROUND)
1308 // don't stick to ground if onground and moving upward
1309 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1310 ent->fields.server->flags -= FL_ONGROUND;
1313 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1314 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1316 // if ent was supported by a brush model on previous frame,
1317 // and groundentity is now freed, set groundentity to 0 (floating)
1318 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1320 // leave it suspended in the air
1321 ent->fields.server->groundentity = 0;
1326 ent->priv.server->suspendedinairflag = false;
1328 SV_CheckVelocity (ent);
1331 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1332 SV_AddGravity (ent);
1335 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1338 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1339 trace = SV_PushEntity (ent, move);
1340 if (ent->priv.server->free)
1342 if (trace.bmodelstartsolid)
1344 // try to unstick the entity
1345 SV_UnstickEntity(ent);
1346 trace = SV_PushEntity (ent, move);
1347 if (ent->priv.server->free)
1351 if (trace.fraction < 1)
1353 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1355 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1356 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1358 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1361 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1362 // LordHavoc: fixed grenades not bouncing when fired down a slope
1363 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1365 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1366 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1368 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1369 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1370 VectorClear (ent->fields.server->velocity);
1371 VectorClear (ent->fields.server->avelocity);
1374 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1378 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1380 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1381 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1382 VectorClear (ent->fields.server->velocity);
1383 VectorClear (ent->fields.server->avelocity);
1386 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1391 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1392 if (trace.plane.normal[2] > 0.7)
1394 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1395 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1396 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1397 ent->priv.server->suspendedinairflag = true;
1398 VectorClear (ent->fields.server->velocity);
1399 VectorClear (ent->fields.server->avelocity);
1402 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1406 // check for in water
1407 SV_CheckWaterTransition (ent);
1411 ===============================================================================
1415 ===============================================================================
1422 Monsters freefall when they don't have a ground entity, otherwise
1423 all movement is done with discrete steps.
1425 This is also used for objects that have become still on the ground, but
1426 will fall if the floor is pulled out from under them.
1429 void SV_Physics_Step (prvm_edict_t *ent)
1431 int flags = (int)ent->fields.server->flags;
1432 // don't fall at all if fly/swim
1433 if (!(flags & (FL_FLY | FL_SWIM)))
1435 if (flags & FL_ONGROUND)
1437 // freefall if onground and moving upward
1438 // freefall if not standing on a world surface (it may be a lift)
1439 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1440 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1442 ent->fields.server->flags -= FL_ONGROUND;
1444 SV_CheckVelocity(ent);
1445 SV_FlyMove(ent, sv.frametime, NULL);
1446 SV_LinkEdict(ent, true);
1451 // freefall if not onground
1452 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1455 SV_CheckVelocity(ent);
1456 SV_FlyMove(ent, sv.frametime, NULL);
1457 SV_LinkEdict(ent, true);
1460 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1461 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1468 SV_CheckWaterTransition(ent);
1471 //============================================================================
1473 static void SV_Physics_Entity (prvm_edict_t *ent)
1475 // don't run a move on newly spawned projectiles as it messes up movement
1476 // interpolation and rocket trails
1477 qboolean runmove = ent->priv.server->move;
1478 ent->priv.server->move = true;
1479 switch ((int) ent->fields.server->movetype)
1482 case MOVETYPE_FAKEPUSH:
1483 SV_Physics_Pusher (ent);
1486 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1487 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1490 case MOVETYPE_FOLLOW:
1491 SV_Physics_Follow (ent);
1493 case MOVETYPE_NOCLIP:
1494 if (SV_RunThink(ent))
1497 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1498 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1500 SV_LinkEdict(ent, false);
1503 SV_Physics_Step (ent);
1506 if (SV_RunThink (ent))
1508 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1509 SV_AddGravity (ent);
1510 SV_CheckStuck (ent);
1512 SV_LinkEdict (ent, true);
1516 case MOVETYPE_BOUNCE:
1517 case MOVETYPE_BOUNCEMISSILE:
1518 case MOVETYPE_FLYMISSILE:
1521 if (SV_RunThink (ent) && runmove)
1522 SV_Physics_Toss (ent);
1525 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1530 void SV_ApplyClientMove (void);
1531 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1533 SV_ApplyClientMove();
1534 // make sure the velocity is sane (not a NaN)
1535 SV_CheckVelocity(ent);
1536 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1537 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1539 prog->globals.server->time = sv.time;
1540 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1541 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1545 // make sure the velocity is sane (not a NaN)
1546 SV_CheckVelocity(ent);
1547 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1548 // player_run/player_stand1 does not horribly malfunction if the
1549 // velocity becomes a number that is both == 0 and != 0
1550 // (sounds to me like NaN but to be absolutely safe...)
1551 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1552 VectorClear(ent->fields.server->velocity);
1553 // call standard client pre-think
1554 prog->globals.server->time = sv.time;
1555 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1556 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1557 SV_CheckVelocity (ent);
1559 switch ((int) ent->fields.server->movetype)
1562 case MOVETYPE_FAKEPUSH:
1563 SV_Physics_Pusher (ent);
1566 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1567 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1570 case MOVETYPE_FOLLOW:
1571 SV_Physics_Follow (ent);
1573 case MOVETYPE_NOCLIP:
1574 if (SV_RunThink(ent))
1577 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1578 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1582 SV_Physics_Step (ent);
1585 if (SV_RunThink (ent))
1587 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1588 SV_AddGravity (ent);
1589 SV_CheckStuck (ent);
1594 case MOVETYPE_BOUNCE:
1595 case MOVETYPE_BOUNCEMISSILE:
1596 case MOVETYPE_FLYMISSILE:
1598 if (SV_RunThink (ent))
1599 SV_Physics_Toss (ent);
1602 if (SV_RunThink (ent))
1604 SV_CheckWater (ent);
1609 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1613 SV_CheckVelocity (ent);
1615 // call standard player post-think
1616 SV_LinkEdict (ent, true);
1618 SV_CheckVelocity (ent);
1620 prog->globals.server->time = sv.time;
1621 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1622 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1631 void SV_Physics (void)
1636 // let the progs know that a new frame has started
1637 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1638 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1639 prog->globals.server->time = sv.time;
1640 prog->globals.server->frametime = sv.frametime;
1641 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1644 // treat each object in turn
1647 // if force_retouch, relink all the entities
1648 if (prog->globals.server->force_retouch > 0)
1649 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1650 if (!ent->priv.server->free)
1651 SV_LinkEdict (ent, true); // force retouch even for stationary
1653 // run physics on the client entities
1654 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1656 if (!ent->priv.server->free)
1658 // don't do physics on disconnected clients, FrikBot relies on this
1659 if (!host_client->spawned)
1660 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1661 // don't run physics here if running asynchronously
1662 else if (!host_client->movesequence)
1663 SV_Physics_ClientEntity(ent);
1667 // run physics on all the non-client entities
1668 if (!sv_freezenonclients.integer)
1669 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1670 if (!ent->priv.server->free)
1671 SV_Physics_Entity(ent);
1673 if (prog->globals.server->force_retouch > 0)
1674 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1676 // LordHavoc: endframe support
1679 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1680 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1681 prog->globals.server->time = sv.time;
1682 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1685 // decrement prog->num_edicts if the highest number entities died
1686 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1688 if (!sv_freezenonclients.integer)
1689 sv.time += sv.frametime;
1693 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1696 float gravity, savesolid;
1698 prvm_edict_t tempent, *tent;
1703 // copy the vars over
1704 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1705 // set up the temp entity to point to the copied vars
1707 tent->fields.server = &vars;
1709 savesolid = tossent->fields.server->solid;
1710 tossent->fields.server->solid = SOLID_NOT;
1712 // this has to fetch the field from the original edict, since our copy is truncated
1713 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1714 if (val != NULL && val->_float != 0)
1715 gravity = val->_float;
1718 gravity *= sv_gravity.value * 0.05;
1720 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1722 SV_CheckVelocity (tent);
1723 tent->fields.server->velocity[2] -= gravity;
1724 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1725 VectorScale (tent->fields.server->velocity, 0.05, move);
1726 VectorAdd (tent->fields.server->origin, move, end);
1727 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1728 VectorCopy (trace.endpos, tent->fields.server->origin);
1730 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1733 tossent->fields.server->solid = savesolid;
1734 trace.fraction = 0; // not relevant