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_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
46 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
47 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
48 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
49 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
50 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
51 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
52 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
53 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
54 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
55 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
57 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)"};
58 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)"};
60 // TODO: move this extern to server.h
61 extern cvar_t sv_clmovement_waitforinput;
63 #define MOVE_EPSILON 0.01
65 void SV_Physics_Toss (prvm_edict_t *ent);
67 void SV_Phys_Init (void)
69 Cvar_RegisterVariable(&sv_stepheight);
70 Cvar_RegisterVariable(&sv_jumpstep);
71 Cvar_RegisterVariable(&sv_wallfriction);
72 Cvar_RegisterVariable(&sv_newflymove);
73 Cvar_RegisterVariable(&sv_freezenonclients);
75 Cvar_RegisterVariable(&sv_playerphysicsqc);
77 Cvar_RegisterVariable(&sv_sound_watersplash);
78 Cvar_RegisterVariable(&sv_sound_land);
85 returns true if the entity is in solid currently
88 static int SV_TestEntityPosition (prvm_edict_t *ent)
90 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
91 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
102 void SV_CheckAllEnts (void)
107 // see if any solid entities are inside the final position
108 check = PRVM_NEXT_EDICT(prog->edicts);
109 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
111 if (check->priv.server->free)
113 if (check->fields.server->movetype == MOVETYPE_PUSH
114 || check->fields.server->movetype == MOVETYPE_NONE
115 || check->fields.server->movetype == MOVETYPE_FOLLOW
116 || check->fields.server->movetype == MOVETYPE_NOCLIP)
119 if (SV_TestEntityPosition (check))
120 Con_Print("entity in invalid position\n");
129 void SV_CheckVelocity (prvm_edict_t *ent)
137 for (i=0 ; i<3 ; i++)
139 if (IS_NAN(ent->fields.server->velocity[i]))
141 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
142 ent->fields.server->velocity[i] = 0;
144 if (IS_NAN(ent->fields.server->origin[i]))
146 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
147 ent->fields.server->origin[i] = 0;
151 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
152 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
153 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
155 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
156 ent->fields.server->velocity[0] *= wishspeed;
157 ent->fields.server->velocity[1] *= wishspeed;
158 ent->fields.server->velocity[2] *= wishspeed;
166 Runs thinking code if time. There is some play in the exact time the think
167 function will be called, because it is called before any movement is done
168 in a frame. Not used for pushmove objects, because they must be exact.
169 Returns false if the entity removed itself.
172 qboolean SV_RunThink (prvm_edict_t *ent)
176 thinktime = ent->fields.server->nextthink;
177 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
180 // don't let things stay in the past.
181 // it is possible to start that way by a trigger with a local time.
182 if (thinktime < sv.time)
185 ent->fields.server->nextthink = 0;
186 prog->globals.server->time = thinktime;
187 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
188 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
189 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
190 return !ent->priv.server->free;
197 Two entities have touched, so run their touch functions
200 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
202 int old_self, old_other;
203 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
206 old_self = prog->globals.server->self;
207 old_other = prog->globals.server->other;
209 prog->globals.server->time = sv.time;
210 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
212 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
213 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
214 prog->globals.server->trace_allsolid = trace->allsolid;
215 prog->globals.server->trace_startsolid = trace->startsolid;
216 prog->globals.server->trace_fraction = trace->fraction;
217 prog->globals.server->trace_inwater = trace->inwater;
218 prog->globals.server->trace_inopen = trace->inopen;
219 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
220 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
221 prog->globals.server->trace_plane_dist = trace->plane.dist;
223 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
225 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
226 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
227 val->_float = trace->startsupercontents;
228 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
229 val->_float = trace->hitsupercontents;
230 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
231 val->_float = trace->hitq3surfaceflags;
232 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
234 if (trace->hittexture)
236 char *s = VM_GetTempString();
237 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
238 val->string = PRVM_SetEngineString(s);
243 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
246 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
248 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
249 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
250 prog->globals.server->trace_allsolid = false;
251 prog->globals.server->trace_startsolid = false;
252 prog->globals.server->trace_fraction = 1;
253 prog->globals.server->trace_inwater = false;
254 prog->globals.server->trace_inopen = true;
255 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
256 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
257 prog->globals.server->trace_plane_dist = 0;
258 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
259 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
261 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
263 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
265 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
267 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
270 prog->globals.server->self = old_self;
271 prog->globals.server->other = old_other;
279 Slide off of the impacting object
280 returns the blocked flags (1 = floor, 2 = step / wall)
283 #define STOP_EPSILON 0.1
284 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
289 backoff = -DotProduct (in, normal) * overbounce;
290 VectorMA(in, backoff, normal, out);
292 for (i = 0;i < 3;i++)
293 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
302 The basic solid body movement clip that slides along multiple planes
303 Returns the clipflags if the velocity was modified (hit something solid)
307 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
310 // LordHavoc: increased from 5 to 32
311 #define MAX_CLIP_PLANES 32
312 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
314 int blocked, bumpcount;
315 int i, j, impact, numplanes;
317 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
320 VectorCopy(ent->fields.server->velocity, original_velocity);
321 VectorCopy(ent->fields.server->velocity, primal_velocity);
324 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
326 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
329 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
330 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
332 //if (trace.fraction < 0.002)
337 VectorCopy(ent->fields.server->origin, start);
338 start[2] += 3;//0.03125;
339 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
340 end[2] += 3;//0.03125;
341 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
342 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)))
344 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
350 for (i = 0;i < numplanes;i++)
352 VectorCopy(ent->fields.server->origin, start);
353 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
354 VectorMA(start, 3, planes[i], start);
355 VectorMA(end, 3, planes[i], end);
356 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
357 if (trace.fraction < testtrace.fraction)
360 VectorCopy(start, ent->fields.server->origin);
365 // VectorAdd(ent->fields.server->origin, planes[j], start);
371 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);
372 if (trace.fraction < 1)
373 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
377 if (trace.bmodelstartsolid)
379 // LordHavoc: note: this code is what makes entities stick in place
380 // if embedded in world only (you can walk through other objects if
382 // entity is trapped in another solid
383 VectorClear(ent->fields.server->velocity);
387 // break if it moved the entire distance
388 if (trace.fraction == 1)
390 VectorCopy(trace.endpos, ent->fields.server->origin);
396 Con_Printf ("SV_FlyMove: !trace.ent");
397 trace.ent = prog->edicts;
400 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
404 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
408 if (trace.plane.normal[2])
410 if (trace.plane.normal[2] > 0.7)
414 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
415 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
422 // save the trace for player extrafriction
424 VectorCopy(trace.plane.normal, stepnormal);
427 if (trace.fraction >= 0.001)
429 // actually covered some distance
430 VectorCopy(trace.endpos, ent->fields.server->origin);
431 VectorCopy(ent->fields.server->velocity, original_velocity);
435 // run the impact function
438 SV_Impact(ent, &trace);
440 // break if removed by the impact function
441 if (ent->priv.server->free)
445 time_left *= 1 - trace.fraction;
447 // clipped to another plane
448 if (numplanes >= MAX_CLIP_PLANES)
450 // this shouldn't really happen
451 VectorClear(ent->fields.server->velocity);
457 for (i = 0;i < numplanes;i++)
458 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
462 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
467 VectorCopy(trace.plane.normal, planes[numplanes]);
470 if (sv_newflymove.integer)
471 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
474 // modify original_velocity so it parallels all of the clip planes
475 for (i = 0;i < numplanes;i++)
477 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
478 for (j = 0;j < numplanes;j++)
483 if (DotProduct(new_velocity, planes[j]) < 0)
493 // go along this plane
494 VectorCopy(new_velocity, ent->fields.server->velocity);
498 // go along the crease
501 VectorClear(ent->fields.server->velocity);
505 CrossProduct(planes[0], planes[1], dir);
506 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
507 VectorNormalize(dir);
508 d = DotProduct(dir, ent->fields.server->velocity);
509 VectorScale(dir, d, ent->fields.server->velocity);
513 // if current velocity is against the original velocity,
514 // stop dead to avoid tiny occilations in sloping corners
515 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
517 VectorClear(ent->fields.server->velocity);
522 //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]);
525 if ((blocked & 1) == 0 && bumpcount > 1)
527 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
528 // flag ONGROUND if there's ground under it
529 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
533 // LordHavoc: this came from QW and allows you to get out of water more easily
534 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
535 VectorCopy(primal_velocity, ent->fields.server->velocity);
545 void SV_AddGravity (prvm_edict_t *ent)
550 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
551 if (val!=0 && val->_float)
552 ent_gravity = val->_float;
555 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
560 ===============================================================================
564 ===============================================================================
571 Does not change the entities velocity at all
574 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonstartsolid)
580 VectorAdd (ent->fields.server->origin, push, end);
582 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
584 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
585 type = MOVE_NOMONSTERS; // only clip against bmodels
589 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
590 if (trace.startsolid && failonstartsolid)
593 VectorCopy (trace.endpos, ent->fields.server->origin);
594 SV_LinkEdict (ent, true);
596 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
597 SV_Impact (ent, &trace);
608 void SV_PushMove (prvm_edict_t *pusher, float movetime)
611 float savesolid, movetime2, pushltime;
612 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
614 int numcheckentities;
615 static prvm_edict_t *checkentities[MAX_EDICTS];
616 model_t *pushermodel;
619 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])
621 pusher->fields.server->ltime += movetime;
625 switch ((int) pusher->fields.server->solid)
627 // LordHavoc: valid pusher types
631 case SOLID_CORPSE: // LordHavoc: this would be weird...
633 // LordHavoc: no collisions
636 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
637 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
638 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
639 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
640 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
641 pusher->fields.server->ltime += movetime;
642 SV_LinkEdict (pusher, false);
645 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
648 index = (int) pusher->fields.server->modelindex;
649 if (index < 1 || index >= MAX_MODELS)
651 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
654 pushermodel = sv.models[index];
656 movetime2 = movetime;
657 VectorScale(pusher->fields.server->velocity, movetime2, move1);
658 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
659 if (moveangle[0] || moveangle[2])
661 for (i = 0;i < 3;i++)
665 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
666 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
670 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
671 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
675 else if (moveangle[1])
677 for (i = 0;i < 3;i++)
681 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
682 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
686 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
687 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
693 for (i = 0;i < 3;i++)
697 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
698 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
702 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
703 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
708 VectorNegate (moveangle, a);
709 AngleVectorsFLU (a, forward, left, up);
711 VectorCopy (pusher->fields.server->origin, pushorig);
712 VectorCopy (pusher->fields.server->angles, pushang);
713 pushltime = pusher->fields.server->ltime;
715 // move the pusher to its final position
717 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
718 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
719 pusher->fields.server->ltime += movetime;
720 SV_LinkEdict (pusher, false);
722 savesolid = pusher->fields.server->solid;
724 // see if any solid entities are inside the final position
727 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
728 for (e = 0;e < numcheckentities;e++)
730 prvm_edict_t *check = checkentities[e];
731 if (check->fields.server->movetype == MOVETYPE_NONE
732 || check->fields.server->movetype == MOVETYPE_PUSH
733 || check->fields.server->movetype == MOVETYPE_FOLLOW
734 || check->fields.server->movetype == MOVETYPE_NOCLIP
735 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
738 // if the entity is standing on the pusher, it will definitely be moved
739 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
741 // remove the onground flag for non-players
742 if (check->fields.server->movetype != MOVETYPE_WALK)
743 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
747 // if the entity is not inside the pusher's final position, leave it alone
748 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)
753 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
756 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
757 org2[0] = DotProduct (org, forward);
758 org2[1] = DotProduct (org, left);
759 org2[2] = DotProduct (org, up);
760 VectorSubtract (org2, org, move);
761 VectorAdd (move, move1, move);
764 VectorCopy (move1, move);
766 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
767 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
768 sv.moved_edicts[num_moved++] = check;
770 // try moving the contacted entity
771 pusher->fields.server->solid = SOLID_NOT;
772 trace = SV_PushEntity (check, move, true);
773 // FIXME: turn players specially
774 check->fields.server->angles[1] += trace.fraction * moveangle[1];
775 pusher->fields.server->solid = savesolid; // was SOLID_BSP
776 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
778 // if it is still inside the pusher, block
779 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)
781 // try moving the contacted entity a tiny bit further to account for precision errors
783 pusher->fields.server->solid = SOLID_NOT;
784 VectorScale(move, 1.1, move2);
785 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
786 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
787 SV_PushEntity (check, move2, true);
788 pusher->fields.server->solid = savesolid;
789 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)
791 // try moving the contacted entity a tiny bit less to account for precision errors
792 pusher->fields.server->solid = SOLID_NOT;
793 VectorScale(move, 0.9, move2);
794 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
795 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
796 SV_PushEntity (check, move2, true);
797 pusher->fields.server->solid = savesolid;
798 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)
800 // still inside pusher, so it's really blocked
803 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
805 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
808 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
809 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
813 VectorCopy (pushorig, pusher->fields.server->origin);
814 VectorCopy (pushang, pusher->fields.server->angles);
815 pusher->fields.server->ltime = pushltime;
816 SV_LinkEdict (pusher, false);
818 // move back any entities we already moved
819 for (i = 0;i < num_moved;i++)
821 prvm_edict_t *ed = sv.moved_edicts[i];
822 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
823 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
824 SV_LinkEdict (ed, false);
827 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
828 if (pusher->fields.server->blocked)
830 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
831 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
832 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
839 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
840 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
841 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
850 void SV_Physics_Pusher (prvm_edict_t *ent)
852 float thinktime, oldltime, movetime;
854 oldltime = ent->fields.server->ltime;
856 thinktime = ent->fields.server->nextthink;
857 if (thinktime < ent->fields.server->ltime + sv.frametime)
859 movetime = thinktime - ent->fields.server->ltime;
864 movetime = sv.frametime;
867 // advances ent->fields.server->ltime if not blocked
868 SV_PushMove (ent, movetime);
870 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
872 ent->fields.server->nextthink = 0;
873 prog->globals.server->time = sv.time;
874 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
875 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
876 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
882 ===============================================================================
886 ===============================================================================
893 This is a big hack to try and fix the rare case of getting stuck in the world
897 void SV_CheckStuck (prvm_edict_t *ent)
902 if (!SV_TestEntityPosition(ent))
904 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
908 VectorCopy (ent->fields.server->origin, org);
909 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
910 if (!SV_TestEntityPosition(ent))
912 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
913 SV_LinkEdict (ent, true);
917 for (z=-1 ; z< 18 ; z++)
918 for (i=-1 ; i <= 1 ; i++)
919 for (j=-1 ; j <= 1 ; j++)
921 ent->fields.server->origin[0] = org[0] + i;
922 ent->fields.server->origin[1] = org[1] + j;
923 ent->fields.server->origin[2] = org[2] + z;
924 if (!SV_TestEntityPosition(ent))
926 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
927 SV_LinkEdict (ent, true);
932 VectorCopy (org, ent->fields.server->origin);
933 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
936 static void SV_UnstickEntity (prvm_edict_t *ent)
941 // if not stuck in a bmodel, just return
942 if (!SV_TestEntityPosition(ent))
945 VectorCopy (ent->fields.server->origin, org);
947 for (z=-1 ; z< 18 ; z += 6)
948 for (i=-1 ; i <= 1 ; i++)
949 for (j=-1 ; j <= 1 ; j++)
951 ent->fields.server->origin[0] = org[0] + i;
952 ent->fields.server->origin[1] = org[1] + j;
953 ent->fields.server->origin[2] = org[2] + z;
954 if (!SV_TestEntityPosition(ent))
956 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
957 SV_LinkEdict (ent, true);
962 VectorCopy (org, ent->fields.server->origin);
963 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
972 qboolean SV_CheckWater (prvm_edict_t *ent)
977 point[0] = ent->fields.server->origin[0];
978 point[1] = ent->fields.server->origin[1];
979 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
981 ent->fields.server->waterlevel = 0;
982 ent->fields.server->watertype = CONTENTS_EMPTY;
983 cont = SV_PointSuperContents(point);
984 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
986 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
987 ent->fields.server->waterlevel = 1;
988 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
989 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
991 ent->fields.server->waterlevel = 2;
992 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
993 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
994 ent->fields.server->waterlevel = 3;
998 return ent->fields.server->waterlevel > 1;
1007 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1010 vec3_t forward, into, side;
1012 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1013 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1015 // cut the tangential velocity
1016 i = DotProduct (stepnormal, ent->fields.server->velocity);
1017 VectorScale (stepnormal, i, into);
1018 VectorSubtract (ent->fields.server->velocity, into, side);
1019 ent->fields.server->velocity[0] = side[0] * (1 + d);
1020 ent->fields.server->velocity[1] = side[1] * (1 + d);
1025 =====================
1028 Player has come to a dead stop, possibly due to the problem with limited
1029 float precision at some angle joins in the BSP hull.
1031 Try fixing by pushing one pixel in each direction.
1033 This is a hack, but in the interest of good gameplay...
1034 ======================
1036 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1041 VectorCopy (ent->fields.server->origin, oldorg);
1044 for (i=0 ; i<8 ; i++)
1046 // try pushing a little in an axial direction
1049 case 0: dir[0] = 2; dir[1] = 0; break;
1050 case 1: dir[0] = 0; dir[1] = 2; break;
1051 case 2: dir[0] = -2; dir[1] = 0; break;
1052 case 3: dir[0] = 0; dir[1] = -2; break;
1053 case 4: dir[0] = 2; dir[1] = 2; break;
1054 case 5: dir[0] = -2; dir[1] = 2; break;
1055 case 6: dir[0] = 2; dir[1] = -2; break;
1056 case 7: dir[0] = -2; dir[1] = -2; break;
1059 SV_PushEntity (ent, dir, false);
1061 // retry the original move
1062 ent->fields.server->velocity[0] = oldvel[0];
1063 ent->fields.server->velocity[1] = oldvel[1];
1064 ent->fields.server->velocity[2] = 0;
1065 clip = SV_FlyMove (ent, 0.1, NULL);
1067 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1068 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1070 Con_DPrint("TryUnstick - success.\n");
1074 // go back to the original pos and try again
1075 VectorCopy (oldorg, ent->fields.server->origin);
1079 VectorClear (ent->fields.server->velocity);
1080 Con_DPrint("TryUnstick - failure.\n");
1085 =====================
1088 Only used by players
1089 ======================
1091 void SV_WalkMove (prvm_edict_t *ent)
1093 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1094 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1097 SV_CheckVelocity(ent);
1099 // do a regular slide move unless it looks like you ran into a step
1100 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1101 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1103 VectorCopy (ent->fields.server->origin, start_origin);
1104 VectorCopy (ent->fields.server->velocity, start_velocity);
1106 clip = SV_FlyMove (ent, sv.frametime, NULL);
1108 SV_CheckVelocity(ent);
1110 VectorCopy(ent->fields.server->origin, originalmove_origin);
1111 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1112 originalmove_clip = clip;
1113 originalmove_flags = (int)ent->fields.server->flags;
1114 originalmove_groundentity = ent->fields.server->groundentity;
1116 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1119 if (sv_nostep.integer)
1122 // if move didn't block on a step, return
1125 // if move was not trying to move into the step, return
1126 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1129 if (ent->fields.server->movetype != MOVETYPE_FLY)
1131 // return if gibbed by a trigger
1132 if (ent->fields.server->movetype != MOVETYPE_WALK)
1135 // only step up while jumping if that is enabled
1136 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1137 if (!oldonground && ent->fields.server->waterlevel == 0)
1141 // try moving up and forward to go up a step
1142 // back to start pos
1143 VectorCopy (start_origin, ent->fields.server->origin);
1144 VectorCopy (start_velocity, ent->fields.server->velocity);
1147 VectorClear (upmove);
1148 upmove[2] = sv_stepheight.value;
1149 // FIXME: don't link?
1150 SV_PushEntity(ent, upmove, false);
1153 ent->fields.server->velocity[2] = 0;
1154 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1155 ent->fields.server->velocity[2] += start_velocity[2];
1157 SV_CheckVelocity(ent);
1159 // check for stuckness, possibly due to the limited precision of floats
1160 // in the clipping hulls
1162 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1163 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1165 //Con_Printf("wall\n");
1166 // stepping up didn't make any progress, revert to original move
1167 VectorCopy(originalmove_origin, ent->fields.server->origin);
1168 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1169 //clip = originalmove_clip;
1170 ent->fields.server->flags = originalmove_flags;
1171 ent->fields.server->groundentity = originalmove_groundentity;
1172 // now try to unstick if needed
1173 //clip = SV_TryUnstick (ent, oldvel);
1177 //Con_Printf("step - ");
1179 // extra friction based on view angle
1180 if (clip & 2 && sv_wallfriction.integer)
1181 SV_WallFriction (ent, stepnormal);
1183 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1184 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)))
1188 VectorClear (downmove);
1189 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1190 // FIXME: don't link?
1191 downtrace = SV_PushEntity (ent, downmove, false);
1193 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1195 // this has been disabled so that you can't jump when you are stepping
1196 // up while already jumping (also known as the Quake2 stair jump bug)
1198 // LordHavoc: disabled this check so you can walk on monsters/players
1199 //if (ent->fields.server->solid == SOLID_BSP)
1201 //Con_Printf("onground\n");
1202 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1203 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1209 //Con_Printf("slope\n");
1210 // if the push down didn't end up on good ground, use the move without
1211 // the step up. This happens near wall / slope combinations, and can
1212 // cause the player to hop up higher on a slope too steep to climb
1213 VectorCopy(originalmove_origin, ent->fields.server->origin);
1214 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1215 //clip = originalmove_clip;
1216 ent->fields.server->flags = originalmove_flags;
1217 ent->fields.server->groundentity = originalmove_groundentity;
1220 SV_CheckVelocity(ent);
1223 //============================================================================
1229 Entities that are "stuck" to another entity
1232 void SV_Physics_Follow (prvm_edict_t *ent)
1234 vec3_t vf, vr, vu, angles, v;
1238 if (!SV_RunThink (ent))
1241 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1242 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1243 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])
1245 // quick case for no rotation
1246 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1250 angles[0] = -ent->fields.server->punchangle[0];
1251 angles[1] = ent->fields.server->punchangle[1];
1252 angles[2] = ent->fields.server->punchangle[2];
1253 AngleVectors (angles, vf, vr, vu);
1254 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];
1255 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];
1256 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];
1257 angles[0] = -e->fields.server->angles[0];
1258 angles[1] = e->fields.server->angles[1];
1259 angles[2] = e->fields.server->angles[2];
1260 AngleVectors (angles, vf, vr, vu);
1261 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1262 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1263 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1265 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1266 SV_LinkEdict (ent, true);
1270 ==============================================================================
1274 ==============================================================================
1279 SV_CheckWaterTransition
1283 void SV_CheckWaterTransition (prvm_edict_t *ent)
1286 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1287 if (!ent->fields.server->watertype)
1289 // just spawned here
1290 ent->fields.server->watertype = cont;
1291 ent->fields.server->waterlevel = 1;
1295 // check if the entity crossed into or out of water
1296 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1297 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1299 if (cont <= CONTENTS_WATER)
1301 ent->fields.server->watertype = cont;
1302 ent->fields.server->waterlevel = 1;
1306 ent->fields.server->watertype = CONTENTS_EMPTY;
1307 ent->fields.server->waterlevel = 0;
1315 Toss, bounce, and fly movement. When onground, do nothing.
1318 void SV_Physics_Toss (prvm_edict_t *ent)
1323 // if onground, return without moving
1324 if ((int)ent->fields.server->flags & FL_ONGROUND)
1326 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1328 // don't stick to ground if onground and moving upward
1329 ent->fields.server->flags -= FL_ONGROUND;
1331 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1333 // we can trust FL_ONGROUND if groundentity is world because it never moves
1336 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1338 // if ent was supported by a brush model on previous frame,
1339 // and groundentity is now freed, set groundentity to 0 (world)
1340 // which leaves it suspended in the air
1341 ent->fields.server->groundentity = 0;
1345 ent->priv.server->suspendedinairflag = false;
1347 SV_CheckVelocity (ent);
1350 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1351 SV_AddGravity (ent);
1354 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1357 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1358 trace = SV_PushEntity (ent, move, true);
1359 if (ent->priv.server->free)
1361 if (trace.bmodelstartsolid)
1363 // try to unstick the entity
1364 SV_UnstickEntity(ent);
1365 trace = SV_PushEntity (ent, move, false);
1366 if (ent->priv.server->free)
1370 if (trace.fraction < 1)
1372 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1374 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1375 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1377 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1380 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1381 // LordHavoc: fixed grenades not bouncing when fired down a slope
1382 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1384 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1385 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1387 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1388 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1389 VectorClear (ent->fields.server->velocity);
1390 VectorClear (ent->fields.server->avelocity);
1393 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1397 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1399 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1400 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1401 VectorClear (ent->fields.server->velocity);
1402 VectorClear (ent->fields.server->avelocity);
1405 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1410 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1411 if (trace.plane.normal[2] > 0.7)
1413 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1414 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1415 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1416 ent->priv.server->suspendedinairflag = true;
1417 VectorClear (ent->fields.server->velocity);
1418 VectorClear (ent->fields.server->avelocity);
1421 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1425 // check for in water
1426 SV_CheckWaterTransition (ent);
1430 ===============================================================================
1434 ===============================================================================
1441 Monsters freefall when they don't have a ground entity, otherwise
1442 all movement is done with discrete steps.
1444 This is also used for objects that have become still on the ground, but
1445 will fall if the floor is pulled out from under them.
1448 void SV_Physics_Step (prvm_edict_t *ent)
1450 int flags = (int)ent->fields.server->flags;
1451 // don't fall at all if fly/swim
1452 if (!(flags & (FL_FLY | FL_SWIM)))
1454 if (flags & FL_ONGROUND)
1456 // freefall if onground and moving upward
1457 // freefall if not standing on a world surface (it may be a lift or trap door)
1458 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1460 ent->fields.server->flags -= FL_ONGROUND;
1462 SV_CheckVelocity(ent);
1463 SV_FlyMove(ent, sv.frametime, NULL);
1464 SV_LinkEdict(ent, true);
1469 // freefall if not onground
1470 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1473 SV_CheckVelocity(ent);
1474 SV_FlyMove(ent, sv.frametime, NULL);
1475 SV_LinkEdict(ent, true);
1478 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1479 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1486 SV_CheckWaterTransition(ent);
1489 //============================================================================
1491 static void SV_Physics_Entity (prvm_edict_t *ent)
1493 // don't run a move on newly spawned projectiles as it messes up movement
1494 // interpolation and rocket trails
1495 qboolean runmove = ent->priv.server->move;
1496 ent->priv.server->move = true;
1497 switch ((int) ent->fields.server->movetype)
1500 case MOVETYPE_FAKEPUSH:
1501 SV_Physics_Pusher (ent);
1504 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1505 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1508 case MOVETYPE_FOLLOW:
1509 SV_Physics_Follow (ent);
1511 case MOVETYPE_NOCLIP:
1512 if (SV_RunThink(ent))
1515 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1516 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1518 SV_LinkEdict(ent, false);
1521 SV_Physics_Step (ent);
1524 if (SV_RunThink (ent))
1526 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1527 SV_AddGravity (ent);
1528 SV_CheckStuck (ent);
1530 SV_LinkEdict (ent, true);
1534 case MOVETYPE_BOUNCE:
1535 case MOVETYPE_BOUNCEMISSILE:
1536 case MOVETYPE_FLYMISSILE:
1539 if (SV_RunThink (ent) && runmove)
1540 SV_Physics_Toss (ent);
1543 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1548 void SV_ApplyClientMove (void);
1549 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1551 SV_ApplyClientMove();
1552 // make sure the velocity is sane (not a NaN)
1553 SV_CheckVelocity(ent);
1554 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1555 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1557 prog->globals.server->time = sv.time;
1558 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1559 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1563 // make sure the velocity is sane (not a NaN)
1564 SV_CheckVelocity(ent);
1565 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1566 // player_run/player_stand1 does not horribly malfunction if the
1567 // velocity becomes a number that is both == 0 and != 0
1568 // (sounds to me like NaN but to be absolutely safe...)
1569 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1570 VectorClear(ent->fields.server->velocity);
1571 // call standard client pre-think
1572 prog->globals.server->time = sv.time;
1573 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1574 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1575 SV_CheckVelocity (ent);
1577 switch ((int) ent->fields.server->movetype)
1580 case MOVETYPE_FAKEPUSH:
1581 SV_Physics_Pusher (ent);
1584 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1585 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1588 case MOVETYPE_FOLLOW:
1589 SV_Physics_Follow (ent);
1591 case MOVETYPE_NOCLIP:
1592 if (SV_RunThink(ent))
1595 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1596 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1600 SV_Physics_Step (ent);
1603 if (SV_RunThink (ent))
1605 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1606 SV_AddGravity (ent);
1607 SV_CheckStuck (ent);
1612 case MOVETYPE_BOUNCE:
1613 case MOVETYPE_BOUNCEMISSILE:
1614 case MOVETYPE_FLYMISSILE:
1616 if (SV_RunThink (ent))
1617 SV_Physics_Toss (ent);
1620 if (SV_RunThink (ent))
1622 SV_CheckWater (ent);
1627 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1631 SV_CheckVelocity (ent);
1633 // call standard player post-think
1634 SV_LinkEdict (ent, true);
1636 SV_CheckVelocity (ent);
1638 prog->globals.server->time = sv.time;
1639 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1640 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1649 void SV_Physics (void)
1654 // let the progs know that a new frame has started
1655 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1656 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1657 prog->globals.server->time = sv.time;
1658 prog->globals.server->frametime = sv.frametime;
1659 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1662 // treat each object in turn
1665 // if force_retouch, relink all the entities
1666 if (prog->globals.server->force_retouch > 0)
1667 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1668 if (!ent->priv.server->free)
1669 SV_LinkEdict (ent, true); // force retouch even for stationary
1671 // run physics on the client entities
1672 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1674 if (!ent->priv.server->free)
1676 // don't do physics on disconnected clients, FrikBot relies on this
1677 if (!host_client->spawned)
1678 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1679 // don't run physics here if running asynchronously
1680 else if (host_client->clmovement_skipphysicsframes > 0)
1681 host_client->clmovement_skipphysicsframes--;
1683 SV_Physics_ClientEntity(ent);
1687 // run physics on all the non-client entities
1688 if (!sv_freezenonclients.integer)
1689 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1690 if (!ent->priv.server->free)
1691 SV_Physics_Entity(ent);
1693 if (prog->globals.server->force_retouch > 0)
1694 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1696 // LordHavoc: endframe support
1699 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1700 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1701 prog->globals.server->time = sv.time;
1702 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1705 // decrement prog->num_edicts if the highest number entities died
1706 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1708 if (!sv_freezenonclients.integer)
1709 sv.time += sv.frametime;
1713 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1718 vec3_t original_origin;
1719 vec3_t original_velocity;
1720 vec3_t original_angles;
1721 vec3_t original_avelocity;
1725 VectorCopy(tossent->fields.server->origin , original_origin );
1726 VectorCopy(tossent->fields.server->velocity , original_velocity );
1727 VectorCopy(tossent->fields.server->angles , original_angles );
1728 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1730 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1731 if (val != NULL && val->_float != 0)
1732 gravity = val->_float;
1735 gravity *= sv_gravity.value * 0.05;
1737 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1739 SV_CheckVelocity (tossent);
1740 tossent->fields.server->velocity[2] -= gravity;
1741 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1742 VectorScale (tossent->fields.server->velocity, 0.05, move);
1743 VectorAdd (tossent->fields.server->origin, move, end);
1744 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1745 VectorCopy (trace.endpos, tossent->fields.server->origin);
1747 if (trace.fraction < 1)
1751 VectorCopy(original_origin , tossent->fields.server->origin );
1752 VectorCopy(original_velocity , tossent->fields.server->velocity );
1753 VectorCopy(original_angles , tossent->fields.server->angles );
1754 VectorCopy(original_avelocity, tossent->fields.server->avelocity);