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 Con_Printf("%s:%d\n", __FILE__, __LINE__);
782 // try moving the contacted entity a tiny bit further to account for precision errors
784 pusher->fields.server->solid = SOLID_NOT;
785 VectorScale(move, 1.1, move2);
786 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
787 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
788 SV_PushEntity (check, move2, true);
789 pusher->fields.server->solid = savesolid;
790 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)
792 Con_Printf("%s:%d\n", __FILE__, __LINE__);
793 // try moving the contacted entity a tiny bit less to account for precision errors
794 pusher->fields.server->solid = SOLID_NOT;
795 VectorScale(move, 0.9, move2);
796 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
797 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
798 SV_PushEntity (check, move2, true);
799 pusher->fields.server->solid = savesolid;
800 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)
802 Con_Printf("%s:%d\n", __FILE__, __LINE__);
803 // still inside pusher, so it's really blocked
806 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
808 Con_Printf("%s:%d\n", __FILE__, __LINE__);
811 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
813 Con_Printf("%s:%d\n", __FILE__, __LINE__);
815 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
816 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
820 VectorCopy (pushorig, pusher->fields.server->origin);
821 VectorCopy (pushang, pusher->fields.server->angles);
822 pusher->fields.server->ltime = pushltime;
823 SV_LinkEdict (pusher, false);
825 // move back any entities we already moved
826 for (i = 0;i < num_moved;i++)
828 prvm_edict_t *ed = sv.moved_edicts[i];
829 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
830 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
831 SV_LinkEdict (ed, false);
834 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
835 if (pusher->fields.server->blocked)
837 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
838 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
839 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
846 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
847 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
848 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
857 void SV_Physics_Pusher (prvm_edict_t *ent)
859 float thinktime, oldltime, movetime;
861 oldltime = ent->fields.server->ltime;
863 thinktime = ent->fields.server->nextthink;
864 if (thinktime < ent->fields.server->ltime + sv.frametime)
866 movetime = thinktime - ent->fields.server->ltime;
871 movetime = sv.frametime;
874 // advances ent->fields.server->ltime if not blocked
875 SV_PushMove (ent, movetime);
877 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
879 ent->fields.server->nextthink = 0;
880 prog->globals.server->time = sv.time;
881 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
882 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
883 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
889 ===============================================================================
893 ===============================================================================
900 This is a big hack to try and fix the rare case of getting stuck in the world
904 void SV_CheckStuck (prvm_edict_t *ent)
909 if (!SV_TestEntityPosition(ent))
911 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
915 VectorCopy (ent->fields.server->origin, org);
916 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
917 if (!SV_TestEntityPosition(ent))
919 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
920 SV_LinkEdict (ent, true);
924 for (z=-1 ; z< 18 ; z++)
925 for (i=-1 ; i <= 1 ; i++)
926 for (j=-1 ; j <= 1 ; j++)
928 ent->fields.server->origin[0] = org[0] + i;
929 ent->fields.server->origin[1] = org[1] + j;
930 ent->fields.server->origin[2] = org[2] + z;
931 if (!SV_TestEntityPosition(ent))
933 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);
934 SV_LinkEdict (ent, true);
939 VectorCopy (org, ent->fields.server->origin);
940 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
943 static void SV_UnstickEntity (prvm_edict_t *ent)
948 // if not stuck in a bmodel, just return
949 if (!SV_TestEntityPosition(ent))
952 VectorCopy (ent->fields.server->origin, org);
954 for (z=-1 ; z< 18 ; z += 6)
955 for (i=-1 ; i <= 1 ; i++)
956 for (j=-1 ; j <= 1 ; j++)
958 ent->fields.server->origin[0] = org[0] + i;
959 ent->fields.server->origin[1] = org[1] + j;
960 ent->fields.server->origin[2] = org[2] + z;
961 if (!SV_TestEntityPosition(ent))
963 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);
964 SV_LinkEdict (ent, true);
969 VectorCopy (org, ent->fields.server->origin);
970 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
979 qboolean SV_CheckWater (prvm_edict_t *ent)
984 point[0] = ent->fields.server->origin[0];
985 point[1] = ent->fields.server->origin[1];
986 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
988 ent->fields.server->waterlevel = 0;
989 ent->fields.server->watertype = CONTENTS_EMPTY;
990 cont = SV_PointSuperContents(point);
991 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
993 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
994 ent->fields.server->waterlevel = 1;
995 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
996 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
998 ent->fields.server->waterlevel = 2;
999 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1000 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1001 ent->fields.server->waterlevel = 3;
1005 return ent->fields.server->waterlevel > 1;
1014 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1017 vec3_t forward, into, side;
1019 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1020 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1022 // cut the tangential velocity
1023 i = DotProduct (stepnormal, ent->fields.server->velocity);
1024 VectorScale (stepnormal, i, into);
1025 VectorSubtract (ent->fields.server->velocity, into, side);
1026 ent->fields.server->velocity[0] = side[0] * (1 + d);
1027 ent->fields.server->velocity[1] = side[1] * (1 + d);
1032 =====================
1035 Player has come to a dead stop, possibly due to the problem with limited
1036 float precision at some angle joins in the BSP hull.
1038 Try fixing by pushing one pixel in each direction.
1040 This is a hack, but in the interest of good gameplay...
1041 ======================
1043 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1048 VectorCopy (ent->fields.server->origin, oldorg);
1051 for (i=0 ; i<8 ; i++)
1053 // try pushing a little in an axial direction
1056 case 0: dir[0] = 2; dir[1] = 0; break;
1057 case 1: dir[0] = 0; dir[1] = 2; break;
1058 case 2: dir[0] = -2; dir[1] = 0; break;
1059 case 3: dir[0] = 0; dir[1] = -2; break;
1060 case 4: dir[0] = 2; dir[1] = 2; break;
1061 case 5: dir[0] = -2; dir[1] = 2; break;
1062 case 6: dir[0] = 2; dir[1] = -2; break;
1063 case 7: dir[0] = -2; dir[1] = -2; break;
1066 SV_PushEntity (ent, dir, false);
1068 // retry the original move
1069 ent->fields.server->velocity[0] = oldvel[0];
1070 ent->fields.server->velocity[1] = oldvel[1];
1071 ent->fields.server->velocity[2] = 0;
1072 clip = SV_FlyMove (ent, 0.1, NULL);
1074 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1075 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1077 Con_DPrint("TryUnstick - success.\n");
1081 // go back to the original pos and try again
1082 VectorCopy (oldorg, ent->fields.server->origin);
1086 VectorClear (ent->fields.server->velocity);
1087 Con_DPrint("TryUnstick - failure.\n");
1092 =====================
1095 Only used by players
1096 ======================
1098 void SV_WalkMove (prvm_edict_t *ent)
1100 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1101 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1104 SV_CheckVelocity(ent);
1106 // do a regular slide move unless it looks like you ran into a step
1107 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1108 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1110 VectorCopy (ent->fields.server->origin, start_origin);
1111 VectorCopy (ent->fields.server->velocity, start_velocity);
1113 clip = SV_FlyMove (ent, sv.frametime, NULL);
1115 SV_CheckVelocity(ent);
1117 VectorCopy(ent->fields.server->origin, originalmove_origin);
1118 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1119 originalmove_clip = clip;
1120 originalmove_flags = (int)ent->fields.server->flags;
1121 originalmove_groundentity = ent->fields.server->groundentity;
1123 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1126 if (sv_nostep.integer)
1129 // if move didn't block on a step, return
1132 // if move was not trying to move into the step, return
1133 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1136 if (ent->fields.server->movetype != MOVETYPE_FLY)
1138 // return if gibbed by a trigger
1139 if (ent->fields.server->movetype != MOVETYPE_WALK)
1142 // only step up while jumping if that is enabled
1143 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1144 if (!oldonground && ent->fields.server->waterlevel == 0)
1148 // try moving up and forward to go up a step
1149 // back to start pos
1150 VectorCopy (start_origin, ent->fields.server->origin);
1151 VectorCopy (start_velocity, ent->fields.server->velocity);
1154 VectorClear (upmove);
1155 upmove[2] = sv_stepheight.value;
1156 // FIXME: don't link?
1157 SV_PushEntity(ent, upmove, false);
1160 ent->fields.server->velocity[2] = 0;
1161 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1162 ent->fields.server->velocity[2] += start_velocity[2];
1164 SV_CheckVelocity(ent);
1166 // check for stuckness, possibly due to the limited precision of floats
1167 // in the clipping hulls
1169 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1170 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1172 //Con_Printf("wall\n");
1173 // stepping up didn't make any progress, revert to original move
1174 VectorCopy(originalmove_origin, ent->fields.server->origin);
1175 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1176 //clip = originalmove_clip;
1177 ent->fields.server->flags = originalmove_flags;
1178 ent->fields.server->groundentity = originalmove_groundentity;
1179 // now try to unstick if needed
1180 //clip = SV_TryUnstick (ent, oldvel);
1184 //Con_Printf("step - ");
1186 // extra friction based on view angle
1187 if (clip & 2 && sv_wallfriction.integer)
1188 SV_WallFriction (ent, stepnormal);
1190 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1191 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)))
1195 VectorClear (downmove);
1196 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1197 // FIXME: don't link?
1198 downtrace = SV_PushEntity (ent, downmove, false);
1200 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1202 // this has been disabled so that you can't jump when you are stepping
1203 // up while already jumping (also known as the Quake2 stair jump bug)
1205 // LordHavoc: disabled this check so you can walk on monsters/players
1206 //if (ent->fields.server->solid == SOLID_BSP)
1208 //Con_Printf("onground\n");
1209 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1210 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1216 //Con_Printf("slope\n");
1217 // if the push down didn't end up on good ground, use the move without
1218 // the step up. This happens near wall / slope combinations, and can
1219 // cause the player to hop up higher on a slope too steep to climb
1220 VectorCopy(originalmove_origin, ent->fields.server->origin);
1221 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1222 //clip = originalmove_clip;
1223 ent->fields.server->flags = originalmove_flags;
1224 ent->fields.server->groundentity = originalmove_groundentity;
1227 SV_CheckVelocity(ent);
1230 //============================================================================
1236 Entities that are "stuck" to another entity
1239 void SV_Physics_Follow (prvm_edict_t *ent)
1241 vec3_t vf, vr, vu, angles, v;
1245 if (!SV_RunThink (ent))
1248 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1249 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1250 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])
1252 // quick case for no rotation
1253 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1257 angles[0] = -ent->fields.server->punchangle[0];
1258 angles[1] = ent->fields.server->punchangle[1];
1259 angles[2] = ent->fields.server->punchangle[2];
1260 AngleVectors (angles, vf, vr, vu);
1261 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];
1262 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];
1263 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];
1264 angles[0] = -e->fields.server->angles[0];
1265 angles[1] = e->fields.server->angles[1];
1266 angles[2] = e->fields.server->angles[2];
1267 AngleVectors (angles, vf, vr, vu);
1268 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1269 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1270 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1272 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1273 SV_LinkEdict (ent, true);
1277 ==============================================================================
1281 ==============================================================================
1286 SV_CheckWaterTransition
1290 void SV_CheckWaterTransition (prvm_edict_t *ent)
1293 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1294 if (!ent->fields.server->watertype)
1296 // just spawned here
1297 ent->fields.server->watertype = cont;
1298 ent->fields.server->waterlevel = 1;
1302 // check if the entity crossed into or out of water
1303 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1304 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1306 if (cont <= CONTENTS_WATER)
1308 ent->fields.server->watertype = cont;
1309 ent->fields.server->waterlevel = 1;
1313 ent->fields.server->watertype = CONTENTS_EMPTY;
1314 ent->fields.server->waterlevel = 0;
1322 Toss, bounce, and fly movement. When onground, do nothing.
1325 void SV_Physics_Toss (prvm_edict_t *ent)
1330 // if onground, return without moving
1331 if ((int)ent->fields.server->flags & FL_ONGROUND)
1333 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1335 // don't stick to ground if onground and moving upward
1336 ent->fields.server->flags -= FL_ONGROUND;
1338 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1340 // we can trust FL_ONGROUND if groundentity is world because it never moves
1343 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1345 // if ent was supported by a brush model on previous frame,
1346 // and groundentity is now freed, set groundentity to 0 (world)
1347 // which leaves it suspended in the air
1348 ent->fields.server->groundentity = 0;
1352 ent->priv.server->suspendedinairflag = false;
1354 SV_CheckVelocity (ent);
1357 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1358 SV_AddGravity (ent);
1361 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1364 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1365 trace = SV_PushEntity (ent, move, true);
1366 if (ent->priv.server->free)
1368 if (trace.bmodelstartsolid)
1370 // try to unstick the entity
1371 SV_UnstickEntity(ent);
1372 trace = SV_PushEntity (ent, move, false);
1373 if (ent->priv.server->free)
1377 if (trace.fraction < 1)
1379 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1381 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1382 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1384 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1387 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1388 // LordHavoc: fixed grenades not bouncing when fired down a slope
1389 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1391 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1392 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1394 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1395 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1396 VectorClear (ent->fields.server->velocity);
1397 VectorClear (ent->fields.server->avelocity);
1400 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1404 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1406 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1407 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1408 VectorClear (ent->fields.server->velocity);
1409 VectorClear (ent->fields.server->avelocity);
1412 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1417 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1418 if (trace.plane.normal[2] > 0.7)
1420 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1421 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1422 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1423 ent->priv.server->suspendedinairflag = true;
1424 VectorClear (ent->fields.server->velocity);
1425 VectorClear (ent->fields.server->avelocity);
1428 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1432 // check for in water
1433 SV_CheckWaterTransition (ent);
1437 ===============================================================================
1441 ===============================================================================
1448 Monsters freefall when they don't have a ground entity, otherwise
1449 all movement is done with discrete steps.
1451 This is also used for objects that have become still on the ground, but
1452 will fall if the floor is pulled out from under them.
1455 void SV_Physics_Step (prvm_edict_t *ent)
1457 int flags = (int)ent->fields.server->flags;
1458 // don't fall at all if fly/swim
1459 if (!(flags & (FL_FLY | FL_SWIM)))
1461 if (flags & FL_ONGROUND)
1463 // freefall if onground and moving upward
1464 // freefall if not standing on a world surface (it may be a lift or trap door)
1465 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1467 ent->fields.server->flags -= FL_ONGROUND;
1469 SV_CheckVelocity(ent);
1470 SV_FlyMove(ent, sv.frametime, NULL);
1471 SV_LinkEdict(ent, true);
1476 // freefall if not onground
1477 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1480 SV_CheckVelocity(ent);
1481 SV_FlyMove(ent, sv.frametime, NULL);
1482 SV_LinkEdict(ent, true);
1485 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1486 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1493 SV_CheckWaterTransition(ent);
1496 //============================================================================
1498 static void SV_Physics_Entity (prvm_edict_t *ent)
1500 // don't run a move on newly spawned projectiles as it messes up movement
1501 // interpolation and rocket trails
1502 qboolean runmove = ent->priv.server->move;
1503 ent->priv.server->move = true;
1504 switch ((int) ent->fields.server->movetype)
1507 case MOVETYPE_FAKEPUSH:
1508 SV_Physics_Pusher (ent);
1511 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1512 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1515 case MOVETYPE_FOLLOW:
1516 SV_Physics_Follow (ent);
1518 case MOVETYPE_NOCLIP:
1519 if (SV_RunThink(ent))
1522 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1523 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1525 SV_LinkEdict(ent, false);
1528 SV_Physics_Step (ent);
1531 if (SV_RunThink (ent))
1533 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1534 SV_AddGravity (ent);
1535 SV_CheckStuck (ent);
1537 SV_LinkEdict (ent, true);
1541 case MOVETYPE_BOUNCE:
1542 case MOVETYPE_BOUNCEMISSILE:
1543 case MOVETYPE_FLYMISSILE:
1546 if (SV_RunThink (ent) && runmove)
1547 SV_Physics_Toss (ent);
1550 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1555 void SV_ApplyClientMove (void);
1556 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1558 SV_ApplyClientMove();
1559 // make sure the velocity is sane (not a NaN)
1560 SV_CheckVelocity(ent);
1561 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1562 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1564 prog->globals.server->time = sv.time;
1565 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1566 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1570 // make sure the velocity is sane (not a NaN)
1571 SV_CheckVelocity(ent);
1572 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1573 // player_run/player_stand1 does not horribly malfunction if the
1574 // velocity becomes a number that is both == 0 and != 0
1575 // (sounds to me like NaN but to be absolutely safe...)
1576 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1577 VectorClear(ent->fields.server->velocity);
1578 // call standard client pre-think
1579 prog->globals.server->time = sv.time;
1580 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1581 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1582 SV_CheckVelocity (ent);
1584 switch ((int) ent->fields.server->movetype)
1587 case MOVETYPE_FAKEPUSH:
1588 SV_Physics_Pusher (ent);
1591 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1592 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1595 case MOVETYPE_FOLLOW:
1596 SV_Physics_Follow (ent);
1598 case MOVETYPE_NOCLIP:
1599 if (SV_RunThink(ent))
1602 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1603 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1607 SV_Physics_Step (ent);
1610 if (SV_RunThink (ent))
1612 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1613 SV_AddGravity (ent);
1614 SV_CheckStuck (ent);
1619 case MOVETYPE_BOUNCE:
1620 case MOVETYPE_BOUNCEMISSILE:
1621 case MOVETYPE_FLYMISSILE:
1623 if (SV_RunThink (ent))
1624 SV_Physics_Toss (ent);
1627 if (SV_RunThink (ent))
1629 SV_CheckWater (ent);
1634 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1638 SV_CheckVelocity (ent);
1640 // call standard player post-think
1641 SV_LinkEdict (ent, true);
1643 SV_CheckVelocity (ent);
1645 prog->globals.server->time = sv.time;
1646 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1647 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1656 void SV_Physics (void)
1661 // let the progs know that a new frame has started
1662 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1663 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1664 prog->globals.server->time = sv.time;
1665 prog->globals.server->frametime = sv.frametime;
1666 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1669 // treat each object in turn
1672 // if force_retouch, relink all the entities
1673 if (prog->globals.server->force_retouch > 0)
1674 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1675 if (!ent->priv.server->free)
1676 SV_LinkEdict (ent, true); // force retouch even for stationary
1678 // run physics on the client entities
1679 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1681 if (!ent->priv.server->free)
1683 // don't do physics on disconnected clients, FrikBot relies on this
1684 if (!host_client->spawned)
1685 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1686 // don't run physics here if running asynchronously
1687 else if (host_client->clmovement_skipphysicsframes > 0)
1688 host_client->clmovement_skipphysicsframes--;
1690 SV_Physics_ClientEntity(ent);
1694 // run physics on all the non-client entities
1695 if (!sv_freezenonclients.integer)
1696 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1697 if (!ent->priv.server->free)
1698 SV_Physics_Entity(ent);
1700 if (prog->globals.server->force_retouch > 0)
1701 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1703 // LordHavoc: endframe support
1706 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1707 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1708 prog->globals.server->time = sv.time;
1709 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1712 // decrement prog->num_edicts if the highest number entities died
1713 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1715 if (!sv_freezenonclients.integer)
1716 sv.time += sv.frametime;
1720 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1725 vec3_t original_origin;
1726 vec3_t original_velocity;
1727 vec3_t original_angles;
1728 vec3_t original_avelocity;
1732 VectorCopy(tossent->fields.server->origin , original_origin );
1733 VectorCopy(tossent->fields.server->velocity , original_velocity );
1734 VectorCopy(tossent->fields.server->angles , original_angles );
1735 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1737 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1738 if (val != NULL && val->_float != 0)
1739 gravity = val->_float;
1742 gravity *= sv_gravity.value * 0.05;
1744 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1746 SV_CheckVelocity (tossent);
1747 tossent->fields.server->velocity[2] -= gravity;
1748 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1749 VectorScale (tossent->fields.server->velocity, 0.05, move);
1750 VectorAdd (tossent->fields.server->origin, move, end);
1751 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1752 VectorCopy (trace.endpos, tossent->fields.server->origin);
1754 if (trace.fraction < 1)
1758 VectorCopy(original_origin , tossent->fields.server->origin );
1759 VectorCopy(original_velocity , tossent->fields.server->velocity );
1760 VectorCopy(original_angles , tossent->fields.server->angles );
1761 VectorCopy(original_avelocity, tossent->fields.server->avelocity);