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 #define MOVE_EPSILON 0.01
62 void SV_Physics_Toss (prvm_edict_t *ent);
64 void SV_Phys_Init (void)
66 Cvar_RegisterVariable(&sv_stepheight);
67 Cvar_RegisterVariable(&sv_jumpstep);
68 Cvar_RegisterVariable(&sv_wallfriction);
69 Cvar_RegisterVariable(&sv_newflymove);
70 Cvar_RegisterVariable(&sv_freezenonclients);
72 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 Cvar_RegisterVariable(&sv_sound_watersplash);
75 Cvar_RegisterVariable(&sv_sound_land);
82 returns true if the entity is in solid currently
85 static int SV_TestEntityPosition (prvm_edict_t *ent)
87 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
88 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
99 void SV_CheckAllEnts (void)
104 // see if any solid entities are inside the final position
105 check = PRVM_NEXT_EDICT(prog->edicts);
106 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
108 if (check->priv.server->free)
110 if (check->fields.server->movetype == MOVETYPE_PUSH
111 || check->fields.server->movetype == MOVETYPE_NONE
112 || check->fields.server->movetype == MOVETYPE_FOLLOW
113 || check->fields.server->movetype == MOVETYPE_NOCLIP)
116 if (SV_TestEntityPosition (check))
117 Con_Print("entity in invalid position\n");
126 void SV_CheckVelocity (prvm_edict_t *ent)
134 for (i=0 ; i<3 ; i++)
136 if (IS_NAN(ent->fields.server->velocity[i]))
138 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
139 ent->fields.server->velocity[i] = 0;
141 if (IS_NAN(ent->fields.server->origin[i]))
143 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
144 ent->fields.server->origin[i] = 0;
148 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
149 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
150 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
152 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
153 ent->fields.server->velocity[0] *= wishspeed;
154 ent->fields.server->velocity[1] *= wishspeed;
155 ent->fields.server->velocity[2] *= wishspeed;
163 Runs thinking code if time. There is some play in the exact time the think
164 function will be called, because it is called before any movement is done
165 in a frame. Not used for pushmove objects, because they must be exact.
166 Returns false if the entity removed itself.
169 qboolean SV_RunThink (prvm_edict_t *ent)
173 thinktime = ent->fields.server->nextthink;
174 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
177 // don't let things stay in the past.
178 // it is possible to start that way by a trigger with a local time.
179 if (thinktime < sv.time)
182 ent->fields.server->nextthink = 0;
183 prog->globals.server->time = thinktime;
184 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
185 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
186 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
187 return !ent->priv.server->free;
194 Two entities have touched, so run their touch functions
197 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
199 int old_self, old_other;
200 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
203 old_self = prog->globals.server->self;
204 old_other = prog->globals.server->other;
206 prog->globals.server->time = sv.time;
207 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
209 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
210 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
211 prog->globals.server->trace_allsolid = trace->allsolid;
212 prog->globals.server->trace_startsolid = trace->startsolid;
213 prog->globals.server->trace_fraction = trace->fraction;
214 prog->globals.server->trace_inwater = trace->inwater;
215 prog->globals.server->trace_inopen = trace->inopen;
216 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
217 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
218 prog->globals.server->trace_plane_dist = trace->plane.dist;
220 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
222 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
223 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
224 val->_float = trace->startsupercontents;
225 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
226 val->_float = trace->hitsupercontents;
227 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
228 val->_float = trace->hitq3surfaceflags;
229 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
231 if (trace->hittexture)
233 char *s = VM_GetTempString();
234 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
235 val->string = PRVM_SetEngineString(s);
240 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
243 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
245 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
246 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
247 prog->globals.server->trace_allsolid = false;
248 prog->globals.server->trace_startsolid = false;
249 prog->globals.server->trace_fraction = 1;
250 prog->globals.server->trace_inwater = false;
251 prog->globals.server->trace_inopen = true;
252 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
253 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
254 prog->globals.server->trace_plane_dist = 0;
255 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
256 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
258 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
260 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
262 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
264 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
267 prog->globals.server->self = old_self;
268 prog->globals.server->other = old_other;
276 Slide off of the impacting object
277 returns the blocked flags (1 = floor, 2 = step / wall)
280 #define STOP_EPSILON 0.1
281 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
286 backoff = -DotProduct (in, normal) * overbounce;
287 VectorMA(in, backoff, normal, out);
289 for (i = 0;i < 3;i++)
290 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
299 The basic solid body movement clip that slides along multiple planes
300 Returns the clipflags if the velocity was modified (hit something solid)
304 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
307 // LordHavoc: increased from 5 to 32
308 #define MAX_CLIP_PLANES 32
309 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
311 int blocked, bumpcount;
312 int i, j, impact, numplanes;
314 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
317 VectorCopy(ent->fields.server->velocity, original_velocity);
318 VectorCopy(ent->fields.server->velocity, primal_velocity);
321 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
323 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
326 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
327 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
329 //if (trace.fraction < 0.002)
334 VectorCopy(ent->fields.server->origin, start);
335 start[2] += 3;//0.03125;
336 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
337 end[2] += 3;//0.03125;
338 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
339 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)))
341 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
347 for (i = 0;i < numplanes;i++)
349 VectorCopy(ent->fields.server->origin, start);
350 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
351 VectorMA(start, 3, planes[i], start);
352 VectorMA(end, 3, planes[i], end);
353 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
354 if (trace.fraction < testtrace.fraction)
357 VectorCopy(start, ent->fields.server->origin);
362 // VectorAdd(ent->fields.server->origin, planes[j], start);
368 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);
369 if (trace.fraction < 1)
370 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
374 if (trace.bmodelstartsolid)
376 // LordHavoc: note: this code is what makes entities stick in place
377 // if embedded in world only (you can walk through other objects if
379 // entity is trapped in another solid
380 VectorClear(ent->fields.server->velocity);
384 // break if it moved the entire distance
385 if (trace.fraction == 1)
387 VectorCopy(trace.endpos, ent->fields.server->origin);
393 Con_Printf ("SV_FlyMove: !trace.ent");
394 trace.ent = prog->edicts;
397 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
401 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
405 if (trace.plane.normal[2])
407 if (trace.plane.normal[2] > 0.7)
411 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
412 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
419 // save the trace for player extrafriction
421 VectorCopy(trace.plane.normal, stepnormal);
424 if (trace.fraction >= 0.001)
426 // actually covered some distance
427 VectorCopy(trace.endpos, ent->fields.server->origin);
428 VectorCopy(ent->fields.server->velocity, original_velocity);
432 // run the impact function
435 SV_Impact(ent, &trace);
437 // break if removed by the impact function
438 if (ent->priv.server->free)
442 time_left *= 1 - trace.fraction;
444 // clipped to another plane
445 if (numplanes >= MAX_CLIP_PLANES)
447 // this shouldn't really happen
448 VectorClear(ent->fields.server->velocity);
454 for (i = 0;i < numplanes;i++)
455 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
459 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
464 VectorCopy(trace.plane.normal, planes[numplanes]);
467 if (sv_newflymove.integer)
468 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
471 // modify original_velocity so it parallels all of the clip planes
472 for (i = 0;i < numplanes;i++)
474 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
475 for (j = 0;j < numplanes;j++)
480 if (DotProduct(new_velocity, planes[j]) < 0)
490 // go along this plane
491 VectorCopy(new_velocity, ent->fields.server->velocity);
495 // go along the crease
498 VectorClear(ent->fields.server->velocity);
502 CrossProduct(planes[0], planes[1], dir);
503 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
504 VectorNormalize(dir);
505 d = DotProduct(dir, ent->fields.server->velocity);
506 VectorScale(dir, d, ent->fields.server->velocity);
510 // if current velocity is against the original velocity,
511 // stop dead to avoid tiny occilations in sloping corners
512 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
514 VectorClear(ent->fields.server->velocity);
519 //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]);
522 if ((blocked & 1) == 0 && bumpcount > 1)
524 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
525 // flag ONGROUND if there's ground under it
526 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
530 // LordHavoc: this came from QW and allows you to get out of water more easily
531 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
532 VectorCopy(primal_velocity, ent->fields.server->velocity);
542 void SV_AddGravity (prvm_edict_t *ent)
547 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
548 if (val!=0 && val->_float)
549 ent_gravity = val->_float;
552 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
557 ===============================================================================
561 ===============================================================================
568 Does not change the entities velocity at all
571 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
577 VectorAdd (ent->fields.server->origin, push, end);
579 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
581 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
582 type = MOVE_NOMONSTERS; // only clip against bmodels
586 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
588 VectorCopy (trace.endpos, ent->fields.server->origin);
589 SV_LinkEdict (ent, true);
591 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
592 SV_Impact (ent, &trace);
603 void SV_PushMove (prvm_edict_t *pusher, float movetime)
606 float savesolid, movetime2, pushltime;
607 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
609 int numcheckentities;
610 static prvm_edict_t *checkentities[MAX_EDICTS];
611 model_t *pushermodel;
614 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])
616 pusher->fields.server->ltime += movetime;
620 switch ((int) pusher->fields.server->solid)
622 // LordHavoc: valid pusher types
626 case SOLID_CORPSE: // LordHavoc: this would be weird...
628 // LordHavoc: no collisions
631 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
632 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
633 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
634 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
635 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
636 pusher->fields.server->ltime += movetime;
637 SV_LinkEdict (pusher, false);
640 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
643 index = (int) pusher->fields.server->modelindex;
644 if (index < 1 || index >= MAX_MODELS)
646 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
649 pushermodel = sv.models[index];
651 movetime2 = movetime;
652 VectorScale(pusher->fields.server->velocity, movetime2, move1);
653 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
654 if (moveangle[0] || moveangle[2])
656 for (i = 0;i < 3;i++)
660 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
661 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
665 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
666 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
670 else if (moveangle[1])
672 for (i = 0;i < 3;i++)
676 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
677 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
681 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
682 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
688 for (i = 0;i < 3;i++)
692 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
693 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
697 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
698 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
703 VectorNegate (moveangle, a);
704 AngleVectorsFLU (a, forward, left, up);
706 VectorCopy (pusher->fields.server->origin, pushorig);
707 VectorCopy (pusher->fields.server->angles, pushang);
708 pushltime = pusher->fields.server->ltime;
710 // move the pusher to its final position
712 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
713 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
714 pusher->fields.server->ltime += movetime;
715 SV_LinkEdict (pusher, false);
717 savesolid = pusher->fields.server->solid;
719 // see if any solid entities are inside the final position
722 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
723 for (e = 0;e < numcheckentities;e++)
725 prvm_edict_t *check = checkentities[e];
726 if (check->fields.server->movetype == MOVETYPE_NONE
727 || check->fields.server->movetype == MOVETYPE_PUSH
728 || check->fields.server->movetype == MOVETYPE_FOLLOW
729 || check->fields.server->movetype == MOVETYPE_NOCLIP
730 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
733 // if the entity is standing on the pusher, it will definitely be moved
734 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
736 // if the entity is not inside the pusher's final position, leave it alone
737 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)
739 // remove the onground flag for non-players
740 if (check->fields.server->movetype != MOVETYPE_WALK)
741 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
745 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
748 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
749 org2[0] = DotProduct (org, forward);
750 org2[1] = DotProduct (org, left);
751 org2[2] = DotProduct (org, up);
752 VectorSubtract (org2, org, move);
753 VectorAdd (move, move1, move);
756 VectorCopy (move1, move);
758 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
759 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
760 sv.moved_edicts[num_moved++] = check;
762 // try moving the contacted entity
763 pusher->fields.server->solid = SOLID_NOT;
764 trace = SV_PushEntity (check, move);
765 // FIXME: turn players specially
766 check->fields.server->angles[1] += trace.fraction * moveangle[1];
767 pusher->fields.server->solid = savesolid; // was SOLID_BSP
769 // if it is still inside the pusher, block
770 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)
772 // try moving the contacted entity a tiny bit further to account for precision errors
774 pusher->fields.server->solid = SOLID_NOT;
775 VectorScale(move, 1.1, move2);
776 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
777 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
778 SV_PushEntity (check, move2);
779 pusher->fields.server->solid = savesolid;
780 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
782 // try moving the contacted entity a tiny bit less to account for precision errors
783 pusher->fields.server->solid = SOLID_NOT;
784 VectorScale(move, 0.9, 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);
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 // still inside pusher, so it's really blocked
794 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
796 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
799 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
800 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
804 VectorCopy (pushorig, pusher->fields.server->origin);
805 VectorCopy (pushang, pusher->fields.server->angles);
806 pusher->fields.server->ltime = pushltime;
807 SV_LinkEdict (pusher, false);
809 // move back any entities we already moved
810 for (i = 0;i < num_moved;i++)
812 prvm_edict_t *ed = sv.moved_edicts[i];
813 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
814 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
815 SV_LinkEdict (ed, false);
818 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
819 if (pusher->fields.server->blocked)
821 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
822 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
823 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
830 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
831 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
832 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
841 void SV_Physics_Pusher (prvm_edict_t *ent)
843 float thinktime, oldltime, movetime;
845 oldltime = ent->fields.server->ltime;
847 thinktime = ent->fields.server->nextthink;
848 if (thinktime < ent->fields.server->ltime + sv.frametime)
850 movetime = thinktime - ent->fields.server->ltime;
855 movetime = sv.frametime;
858 // advances ent->fields.server->ltime if not blocked
859 SV_PushMove (ent, movetime);
861 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
863 ent->fields.server->nextthink = 0;
864 prog->globals.server->time = sv.time;
865 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
866 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
867 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
873 ===============================================================================
877 ===============================================================================
884 This is a big hack to try and fix the rare case of getting stuck in the world
888 void SV_CheckStuck (prvm_edict_t *ent)
893 if (!SV_TestEntityPosition(ent))
895 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
899 VectorCopy (ent->fields.server->origin, org);
900 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
901 if (!SV_TestEntityPosition(ent))
903 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
904 SV_LinkEdict (ent, true);
908 for (z=-1 ; z< 18 ; z++)
909 for (i=-1 ; i <= 1 ; i++)
910 for (j=-1 ; j <= 1 ; j++)
912 ent->fields.server->origin[0] = org[0] + i;
913 ent->fields.server->origin[1] = org[1] + j;
914 ent->fields.server->origin[2] = org[2] + z;
915 if (!SV_TestEntityPosition(ent))
917 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);
918 SV_LinkEdict (ent, true);
923 VectorCopy (org, ent->fields.server->origin);
924 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
927 static void SV_UnstickEntity (prvm_edict_t *ent)
932 // if not stuck in a bmodel, just return
933 if (!SV_TestEntityPosition(ent))
936 VectorCopy (ent->fields.server->origin, org);
938 for (z=-1 ; z< 18 ; z += 6)
939 for (i=-1 ; i <= 1 ; i++)
940 for (j=-1 ; j <= 1 ; j++)
942 ent->fields.server->origin[0] = org[0] + i;
943 ent->fields.server->origin[1] = org[1] + j;
944 ent->fields.server->origin[2] = org[2] + z;
945 if (!SV_TestEntityPosition(ent))
947 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);
948 SV_LinkEdict (ent, true);
953 VectorCopy (org, ent->fields.server->origin);
954 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
963 qboolean SV_CheckWater (prvm_edict_t *ent)
968 point[0] = ent->fields.server->origin[0];
969 point[1] = ent->fields.server->origin[1];
970 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
972 ent->fields.server->waterlevel = 0;
973 ent->fields.server->watertype = CONTENTS_EMPTY;
974 cont = SV_PointSuperContents(point);
975 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
977 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
978 ent->fields.server->waterlevel = 1;
979 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
980 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
982 ent->fields.server->waterlevel = 2;
983 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
984 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
985 ent->fields.server->waterlevel = 3;
989 return ent->fields.server->waterlevel > 1;
998 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1001 vec3_t forward, into, side;
1003 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1004 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1006 // cut the tangential velocity
1007 i = DotProduct (stepnormal, ent->fields.server->velocity);
1008 VectorScale (stepnormal, i, into);
1009 VectorSubtract (ent->fields.server->velocity, into, side);
1010 ent->fields.server->velocity[0] = side[0] * (1 + d);
1011 ent->fields.server->velocity[1] = side[1] * (1 + d);
1016 =====================
1019 Player has come to a dead stop, possibly due to the problem with limited
1020 float precision at some angle joins in the BSP hull.
1022 Try fixing by pushing one pixel in each direction.
1024 This is a hack, but in the interest of good gameplay...
1025 ======================
1027 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1032 VectorCopy (ent->fields.server->origin, oldorg);
1035 for (i=0 ; i<8 ; i++)
1037 // try pushing a little in an axial direction
1040 case 0: dir[0] = 2; dir[1] = 0; break;
1041 case 1: dir[0] = 0; dir[1] = 2; break;
1042 case 2: dir[0] = -2; dir[1] = 0; break;
1043 case 3: dir[0] = 0; dir[1] = -2; break;
1044 case 4: dir[0] = 2; dir[1] = 2; break;
1045 case 5: dir[0] = -2; dir[1] = 2; break;
1046 case 6: dir[0] = 2; dir[1] = -2; break;
1047 case 7: dir[0] = -2; dir[1] = -2; break;
1050 SV_PushEntity (ent, dir);
1052 // retry the original move
1053 ent->fields.server->velocity[0] = oldvel[0];
1054 ent->fields.server->velocity[1] = oldvel[1];
1055 ent->fields.server->velocity[2] = 0;
1056 clip = SV_FlyMove (ent, 0.1, NULL);
1058 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1059 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1061 Con_DPrint("TryUnstick - success.\n");
1065 // go back to the original pos and try again
1066 VectorCopy (oldorg, ent->fields.server->origin);
1070 VectorClear (ent->fields.server->velocity);
1071 Con_DPrint("TryUnstick - failure.\n");
1076 =====================
1079 Only used by players
1080 ======================
1082 void SV_WalkMove (prvm_edict_t *ent)
1084 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1085 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1088 SV_CheckVelocity(ent);
1090 // do a regular slide move unless it looks like you ran into a step
1091 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1092 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1094 VectorCopy (ent->fields.server->origin, start_origin);
1095 VectorCopy (ent->fields.server->velocity, start_velocity);
1097 clip = SV_FlyMove (ent, sv.frametime, NULL);
1099 SV_CheckVelocity(ent);
1101 VectorCopy(ent->fields.server->origin, originalmove_origin);
1102 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1103 originalmove_clip = clip;
1104 originalmove_flags = (int)ent->fields.server->flags;
1105 originalmove_groundentity = ent->fields.server->groundentity;
1107 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1110 if (sv_nostep.integer)
1113 // if move didn't block on a step, return
1116 // if move was not trying to move into the step, return
1117 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1120 if (ent->fields.server->movetype != MOVETYPE_FLY)
1122 // return if gibbed by a trigger
1123 if (ent->fields.server->movetype != MOVETYPE_WALK)
1126 // only step up while jumping if that is enabled
1127 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1128 if (!oldonground && ent->fields.server->waterlevel == 0)
1132 // try moving up and forward to go up a step
1133 // back to start pos
1134 VectorCopy (start_origin, ent->fields.server->origin);
1135 VectorCopy (start_velocity, ent->fields.server->velocity);
1138 VectorClear (upmove);
1139 upmove[2] = sv_stepheight.value;
1140 // FIXME: don't link?
1141 SV_PushEntity(ent, upmove);
1144 ent->fields.server->velocity[2] = 0;
1145 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1146 ent->fields.server->velocity[2] += start_velocity[2];
1148 SV_CheckVelocity(ent);
1150 // check for stuckness, possibly due to the limited precision of floats
1151 // in the clipping hulls
1153 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1154 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1156 //Con_Printf("wall\n");
1157 // stepping up didn't make any progress, revert to original move
1158 VectorCopy(originalmove_origin, ent->fields.server->origin);
1159 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1160 //clip = originalmove_clip;
1161 ent->fields.server->flags = originalmove_flags;
1162 ent->fields.server->groundentity = originalmove_groundentity;
1163 // now try to unstick if needed
1164 //clip = SV_TryUnstick (ent, oldvel);
1168 //Con_Printf("step - ");
1170 // extra friction based on view angle
1171 if (clip & 2 && sv_wallfriction.integer)
1172 SV_WallFriction (ent, stepnormal);
1174 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1175 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)))
1179 VectorClear (downmove);
1180 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1181 // FIXME: don't link?
1182 downtrace = SV_PushEntity (ent, downmove);
1184 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1186 // this has been disabled so that you can't jump when you are stepping
1187 // up while already jumping (also known as the Quake2 stair jump bug)
1189 // LordHavoc: disabled this check so you can walk on monsters/players
1190 //if (ent->fields.server->solid == SOLID_BSP)
1192 //Con_Printf("onground\n");
1193 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1194 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1200 //Con_Printf("slope\n");
1201 // if the push down didn't end up on good ground, use the move without
1202 // the step up. This happens near wall / slope combinations, and can
1203 // cause the player to hop up higher on a slope too steep to climb
1204 VectorCopy(originalmove_origin, ent->fields.server->origin);
1205 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1206 //clip = originalmove_clip;
1207 ent->fields.server->flags = originalmove_flags;
1208 ent->fields.server->groundentity = originalmove_groundentity;
1211 SV_CheckVelocity(ent);
1214 //============================================================================
1220 Entities that are "stuck" to another entity
1223 void SV_Physics_Follow (prvm_edict_t *ent)
1225 vec3_t vf, vr, vu, angles, v;
1229 if (!SV_RunThink (ent))
1232 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1233 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1234 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])
1236 // quick case for no rotation
1237 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1241 angles[0] = -ent->fields.server->punchangle[0];
1242 angles[1] = ent->fields.server->punchangle[1];
1243 angles[2] = ent->fields.server->punchangle[2];
1244 AngleVectors (angles, vf, vr, vu);
1245 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];
1246 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];
1247 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];
1248 angles[0] = -e->fields.server->angles[0];
1249 angles[1] = e->fields.server->angles[1];
1250 angles[2] = e->fields.server->angles[2];
1251 AngleVectors (angles, vf, vr, vu);
1252 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1253 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1254 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1256 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1257 SV_LinkEdict (ent, true);
1261 ==============================================================================
1265 ==============================================================================
1270 SV_CheckWaterTransition
1274 void SV_CheckWaterTransition (prvm_edict_t *ent)
1277 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1278 if (!ent->fields.server->watertype)
1280 // just spawned here
1281 ent->fields.server->watertype = cont;
1282 ent->fields.server->waterlevel = 1;
1286 // check if the entity crossed into or out of water
1287 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1288 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1290 if (cont <= CONTENTS_WATER)
1292 ent->fields.server->watertype = cont;
1293 ent->fields.server->waterlevel = 1;
1297 ent->fields.server->watertype = CONTENTS_EMPTY;
1298 ent->fields.server->waterlevel = 0;
1306 Toss, bounce, and fly movement. When onground, do nothing.
1309 void SV_Physics_Toss (prvm_edict_t *ent)
1314 // if onground, return without moving
1315 if ((int)ent->fields.server->flags & FL_ONGROUND)
1317 // don't stick to ground if onground and moving upward
1318 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1319 ent->fields.server->flags -= FL_ONGROUND;
1322 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1323 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1325 // if ent was supported by a brush model on previous frame,
1326 // and groundentity is now freed, set groundentity to 0 (floating)
1327 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1329 // leave it suspended in the air
1330 ent->fields.server->groundentity = 0;
1335 ent->priv.server->suspendedinairflag = false;
1337 SV_CheckVelocity (ent);
1340 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1341 SV_AddGravity (ent);
1344 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1347 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1348 trace = SV_PushEntity (ent, move);
1349 if (ent->priv.server->free)
1351 if (trace.bmodelstartsolid)
1353 // try to unstick the entity
1354 SV_UnstickEntity(ent);
1355 trace = SV_PushEntity (ent, move);
1356 if (ent->priv.server->free)
1360 if (trace.fraction < 1)
1362 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1364 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1365 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1367 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1370 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1371 // LordHavoc: fixed grenades not bouncing when fired down a slope
1372 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1374 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1375 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1377 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1378 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1379 VectorClear (ent->fields.server->velocity);
1380 VectorClear (ent->fields.server->avelocity);
1383 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1387 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1389 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1390 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1391 VectorClear (ent->fields.server->velocity);
1392 VectorClear (ent->fields.server->avelocity);
1395 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1400 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1401 if (trace.plane.normal[2] > 0.7)
1403 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1404 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1405 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1406 ent->priv.server->suspendedinairflag = true;
1407 VectorClear (ent->fields.server->velocity);
1408 VectorClear (ent->fields.server->avelocity);
1411 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1415 // check for in water
1416 SV_CheckWaterTransition (ent);
1420 ===============================================================================
1424 ===============================================================================
1431 Monsters freefall when they don't have a ground entity, otherwise
1432 all movement is done with discrete steps.
1434 This is also used for objects that have become still on the ground, but
1435 will fall if the floor is pulled out from under them.
1438 void SV_Physics_Step (prvm_edict_t *ent)
1440 int flags = (int)ent->fields.server->flags;
1441 // don't fall at all if fly/swim
1442 if (!(flags & (FL_FLY | FL_SWIM)))
1444 if (flags & FL_ONGROUND)
1446 // freefall if onground and moving upward
1447 // freefall if not standing on a world surface (it may be a lift)
1448 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1449 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1451 ent->fields.server->flags -= FL_ONGROUND;
1453 SV_CheckVelocity(ent);
1454 SV_FlyMove(ent, sv.frametime, NULL);
1455 SV_LinkEdict(ent, true);
1460 // freefall if not onground
1461 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1464 SV_CheckVelocity(ent);
1465 SV_FlyMove(ent, sv.frametime, NULL);
1466 SV_LinkEdict(ent, true);
1469 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1470 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1477 SV_CheckWaterTransition(ent);
1480 //============================================================================
1482 static void SV_Physics_Entity (prvm_edict_t *ent)
1484 // don't run a move on newly spawned projectiles as it messes up movement
1485 // interpolation and rocket trails
1486 qboolean runmove = ent->priv.server->move;
1487 ent->priv.server->move = true;
1488 switch ((int) ent->fields.server->movetype)
1491 case MOVETYPE_FAKEPUSH:
1492 SV_Physics_Pusher (ent);
1495 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1496 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1499 case MOVETYPE_FOLLOW:
1500 SV_Physics_Follow (ent);
1502 case MOVETYPE_NOCLIP:
1503 if (SV_RunThink(ent))
1506 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1507 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1509 SV_LinkEdict(ent, false);
1512 SV_Physics_Step (ent);
1515 if (SV_RunThink (ent))
1517 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1518 SV_AddGravity (ent);
1519 SV_CheckStuck (ent);
1521 SV_LinkEdict (ent, true);
1525 case MOVETYPE_BOUNCE:
1526 case MOVETYPE_BOUNCEMISSILE:
1527 case MOVETYPE_FLYMISSILE:
1530 if (SV_RunThink (ent) && runmove)
1531 SV_Physics_Toss (ent);
1534 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1539 void SV_ApplyClientMove (void);
1540 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1542 SV_ApplyClientMove();
1543 // make sure the velocity is sane (not a NaN)
1544 SV_CheckVelocity(ent);
1545 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1546 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1548 prog->globals.server->time = sv.time;
1549 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1550 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1554 // make sure the velocity is sane (not a NaN)
1555 SV_CheckVelocity(ent);
1556 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1557 // player_run/player_stand1 does not horribly malfunction if the
1558 // velocity becomes a number that is both == 0 and != 0
1559 // (sounds to me like NaN but to be absolutely safe...)
1560 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1561 VectorClear(ent->fields.server->velocity);
1562 // call standard client pre-think
1563 prog->globals.server->time = sv.time;
1564 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1565 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1566 SV_CheckVelocity (ent);
1568 switch ((int) ent->fields.server->movetype)
1571 case MOVETYPE_FAKEPUSH:
1572 SV_Physics_Pusher (ent);
1575 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1576 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1579 case MOVETYPE_FOLLOW:
1580 SV_Physics_Follow (ent);
1582 case MOVETYPE_NOCLIP:
1583 if (SV_RunThink(ent))
1586 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1587 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1591 SV_Physics_Step (ent);
1594 if (SV_RunThink (ent))
1596 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1597 SV_AddGravity (ent);
1598 SV_CheckStuck (ent);
1603 case MOVETYPE_BOUNCE:
1604 case MOVETYPE_BOUNCEMISSILE:
1605 case MOVETYPE_FLYMISSILE:
1607 if (SV_RunThink (ent))
1608 SV_Physics_Toss (ent);
1611 if (SV_RunThink (ent))
1613 SV_CheckWater (ent);
1618 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1622 SV_CheckVelocity (ent);
1624 // call standard player post-think
1625 SV_LinkEdict (ent, true);
1627 SV_CheckVelocity (ent);
1629 prog->globals.server->time = sv.time;
1630 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1631 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1640 void SV_Physics (void)
1645 // let the progs know that a new frame has started
1646 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1647 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1648 prog->globals.server->time = sv.time;
1649 prog->globals.server->frametime = sv.frametime;
1650 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1653 // treat each object in turn
1656 // if force_retouch, relink all the entities
1657 if (prog->globals.server->force_retouch > 0)
1658 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1659 if (!ent->priv.server->free)
1660 SV_LinkEdict (ent, true); // force retouch even for stationary
1662 // run physics on the client entities
1663 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1665 if (!ent->priv.server->free)
1667 // don't do physics on disconnected clients, FrikBot relies on this
1668 if (!host_client->spawned)
1669 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1670 // don't run physics here if running asynchronously
1671 else if (!host_client->movesequence)
1672 SV_Physics_ClientEntity(ent);
1676 // run physics on all the non-client entities
1677 if (!sv_freezenonclients.integer)
1678 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1679 if (!ent->priv.server->free)
1680 SV_Physics_Entity(ent);
1682 if (prog->globals.server->force_retouch > 0)
1683 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1685 // LordHavoc: endframe support
1688 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1689 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1690 prog->globals.server->time = sv.time;
1691 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1694 // decrement prog->num_edicts if the highest number entities died
1695 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1697 if (!sv_freezenonclients.integer)
1698 sv.time += sv.frametime;
1702 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1707 vec3_t original_origin;
1708 vec3_t original_velocity;
1709 vec3_t original_angles;
1710 vec3_t original_avelocity;
1714 VectorCopy(tossent->fields.server->origin , original_origin );
1715 VectorCopy(tossent->fields.server->velocity , original_velocity );
1716 VectorCopy(tossent->fields.server->angles , original_angles );
1717 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1719 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1720 if (val != NULL && val->_float != 0)
1721 gravity = val->_float;
1724 gravity *= sv_gravity.value * 0.05;
1726 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1728 SV_CheckVelocity (tossent);
1729 tossent->fields.server->velocity[2] -= gravity;
1730 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1731 VectorScale (tossent->fields.server->velocity, 0.05, move);
1732 VectorAdd (tossent->fields.server->origin, move, end);
1733 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1734 VectorCopy (trace.endpos, tossent->fields.server->origin);
1736 if (trace.fraction < 1)
1740 VectorCopy(original_origin , tossent->fields.server->origin );
1741 VectorCopy(original_velocity , tossent->fields.server->velocity );
1742 VectorCopy(original_angles , tossent->fields.server->angles );
1743 VectorCopy(original_avelocity, tossent->fields.server->avelocity);