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]);
378 if (trace.bmodelstartsolid)
380 // LordHavoc: note: this code is what makes entities stick in place
381 // if embedded in world only (you can walk through other objects if
383 // entity is trapped in another solid
384 VectorClear(ent->fields.server->velocity);
389 // break if it moved the entire distance
390 if (trace.fraction == 1)
392 VectorCopy(trace.endpos, ent->fields.server->origin);
398 Con_Printf ("SV_FlyMove: !trace.ent");
399 trace.ent = prog->edicts;
402 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
406 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
410 if (trace.plane.normal[2])
412 if (trace.plane.normal[2] > 0.7)
416 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
417 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
424 // save the trace for player extrafriction
426 VectorCopy(trace.plane.normal, stepnormal);
429 if (trace.fraction >= 0.001)
431 // actually covered some distance
432 VectorCopy(trace.endpos, ent->fields.server->origin);
433 VectorCopy(ent->fields.server->velocity, original_velocity);
437 // run the impact function
440 SV_Impact(ent, &trace);
442 // break if removed by the impact function
443 if (ent->priv.server->free)
447 time_left *= 1 - trace.fraction;
449 // clipped to another plane
450 if (numplanes >= MAX_CLIP_PLANES)
452 // this shouldn't really happen
453 VectorClear(ent->fields.server->velocity);
459 for (i = 0;i < numplanes;i++)
460 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
464 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
469 VectorCopy(trace.plane.normal, planes[numplanes]);
472 if (sv_newflymove.integer)
473 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
476 // modify original_velocity so it parallels all of the clip planes
477 for (i = 0;i < numplanes;i++)
479 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
480 for (j = 0;j < numplanes;j++)
485 if (DotProduct(new_velocity, planes[j]) < 0)
495 // go along this plane
496 VectorCopy(new_velocity, ent->fields.server->velocity);
500 // go along the crease
503 VectorClear(ent->fields.server->velocity);
507 CrossProduct(planes[0], planes[1], dir);
508 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
509 VectorNormalize(dir);
510 d = DotProduct(dir, ent->fields.server->velocity);
511 VectorScale(dir, d, ent->fields.server->velocity);
515 // if current velocity is against the original velocity,
516 // stop dead to avoid tiny occilations in sloping corners
517 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
519 VectorClear(ent->fields.server->velocity);
524 //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]);
527 if ((blocked & 1) == 0 && bumpcount > 1)
529 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
530 // flag ONGROUND if there's ground under it
531 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
535 // LordHavoc: this came from QW and allows you to get out of water more easily
536 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
537 VectorCopy(primal_velocity, ent->fields.server->velocity);
547 void SV_AddGravity (prvm_edict_t *ent)
552 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
553 if (val!=0 && val->_float)
554 ent_gravity = val->_float;
557 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
562 ===============================================================================
566 ===============================================================================
573 Does not change the entities velocity at all
576 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonstartsolid)
582 VectorAdd (ent->fields.server->origin, push, end);
584 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
586 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
587 type = MOVE_NOMONSTERS; // only clip against bmodels
591 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
592 if (trace.startsolid && failonstartsolid)
595 VectorCopy (trace.endpos, ent->fields.server->origin);
596 SV_LinkEdict (ent, true);
598 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
599 SV_Impact (ent, &trace);
610 void SV_PushMove (prvm_edict_t *pusher, float movetime)
613 float savesolid, movetime2, pushltime;
614 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
616 int numcheckentities;
617 static prvm_edict_t *checkentities[MAX_EDICTS];
618 model_t *pushermodel;
621 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])
623 pusher->fields.server->ltime += movetime;
627 switch ((int) pusher->fields.server->solid)
629 // LordHavoc: valid pusher types
633 case SOLID_CORPSE: // LordHavoc: this would be weird...
635 // LordHavoc: no collisions
638 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
639 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
640 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
641 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
642 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
643 pusher->fields.server->ltime += movetime;
644 SV_LinkEdict (pusher, false);
647 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
650 index = (int) pusher->fields.server->modelindex;
651 if (index < 1 || index >= MAX_MODELS)
653 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
656 pushermodel = sv.models[index];
658 movetime2 = movetime;
659 VectorScale(pusher->fields.server->velocity, movetime2, move1);
660 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
661 if (moveangle[0] || moveangle[2])
663 for (i = 0;i < 3;i++)
667 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
668 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
672 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
673 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
677 else if (moveangle[1])
679 for (i = 0;i < 3;i++)
683 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
684 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
688 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
689 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
695 for (i = 0;i < 3;i++)
699 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
700 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
704 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
705 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
710 VectorNegate (moveangle, a);
711 AngleVectorsFLU (a, forward, left, up);
713 VectorCopy (pusher->fields.server->origin, pushorig);
714 VectorCopy (pusher->fields.server->angles, pushang);
715 pushltime = pusher->fields.server->ltime;
717 // move the pusher to its final position
719 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
720 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
721 pusher->fields.server->ltime += movetime;
722 SV_LinkEdict (pusher, false);
724 savesolid = pusher->fields.server->solid;
726 // see if any solid entities are inside the final position
729 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
730 for (e = 0;e < numcheckentities;e++)
732 prvm_edict_t *check = checkentities[e];
733 if (check->fields.server->movetype == MOVETYPE_NONE
734 || check->fields.server->movetype == MOVETYPE_PUSH
735 || check->fields.server->movetype == MOVETYPE_FOLLOW
736 || check->fields.server->movetype == MOVETYPE_NOCLIP
737 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
740 // if the entity is standing on the pusher, it will definitely be moved
741 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
743 // remove the onground flag for non-players
744 if (check->fields.server->movetype != MOVETYPE_WALK)
745 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
749 // if the entity is not inside the pusher's final position, leave it alone
750 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)
755 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
758 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
759 org2[0] = DotProduct (org, forward);
760 org2[1] = DotProduct (org, left);
761 org2[2] = DotProduct (org, up);
762 VectorSubtract (org2, org, move);
763 VectorAdd (move, move1, move);
766 VectorCopy (move1, move);
768 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
769 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
770 sv.moved_edicts[num_moved++] = check;
772 // try moving the contacted entity
773 pusher->fields.server->solid = SOLID_NOT;
774 trace = SV_PushEntity (check, move, true);
775 // FIXME: turn players specially
776 check->fields.server->angles[1] += trace.fraction * moveangle[1];
777 pusher->fields.server->solid = savesolid; // was SOLID_BSP
778 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
780 // if it is still inside the pusher, block
781 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)
783 // try moving the contacted entity a tiny bit further to account for precision errors
785 pusher->fields.server->solid = SOLID_NOT;
786 VectorScale(move, 1.1, move2);
787 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
788 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
789 SV_PushEntity (check, move2, true);
790 pusher->fields.server->solid = savesolid;
791 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)
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 // still inside pusher, so it's really blocked
805 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
807 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
810 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
811 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
815 VectorCopy (pushorig, pusher->fields.server->origin);
816 VectorCopy (pushang, pusher->fields.server->angles);
817 pusher->fields.server->ltime = pushltime;
818 SV_LinkEdict (pusher, false);
820 // move back any entities we already moved
821 for (i = 0;i < num_moved;i++)
823 prvm_edict_t *ed = sv.moved_edicts[i];
824 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
825 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
826 SV_LinkEdict (ed, false);
829 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
830 if (pusher->fields.server->blocked)
832 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
833 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
834 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
841 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
842 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
843 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
852 void SV_Physics_Pusher (prvm_edict_t *ent)
854 float thinktime, oldltime, movetime;
856 oldltime = ent->fields.server->ltime;
858 thinktime = ent->fields.server->nextthink;
859 if (thinktime < ent->fields.server->ltime + sv.frametime)
861 movetime = thinktime - ent->fields.server->ltime;
866 movetime = sv.frametime;
869 // advances ent->fields.server->ltime if not blocked
870 SV_PushMove (ent, movetime);
872 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
874 ent->fields.server->nextthink = 0;
875 prog->globals.server->time = sv.time;
876 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
877 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
878 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
884 ===============================================================================
888 ===============================================================================
895 This is a big hack to try and fix the rare case of getting stuck in the world
899 void SV_CheckStuck (prvm_edict_t *ent)
904 if (!SV_TestEntityPosition(ent))
906 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
910 VectorCopy (ent->fields.server->origin, org);
911 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
912 if (!SV_TestEntityPosition(ent))
914 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
915 SV_LinkEdict (ent, true);
919 for (z=-1 ; z< 18 ; z++)
920 for (i=-1 ; i <= 1 ; i++)
921 for (j=-1 ; j <= 1 ; j++)
923 ent->fields.server->origin[0] = org[0] + i;
924 ent->fields.server->origin[1] = org[1] + j;
925 ent->fields.server->origin[2] = org[2] + z;
926 if (!SV_TestEntityPosition(ent))
928 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);
929 SV_LinkEdict (ent, true);
934 VectorCopy (org, ent->fields.server->origin);
935 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
938 static void SV_UnstickEntity (prvm_edict_t *ent)
943 // if not stuck in a bmodel, just return
944 if (!SV_TestEntityPosition(ent))
947 VectorCopy (ent->fields.server->origin, org);
949 for (z=-1 ; z< 18 ; z += 6)
950 for (i=-1 ; i <= 1 ; i++)
951 for (j=-1 ; j <= 1 ; j++)
953 ent->fields.server->origin[0] = org[0] + i;
954 ent->fields.server->origin[1] = org[1] + j;
955 ent->fields.server->origin[2] = org[2] + z;
956 if (!SV_TestEntityPosition(ent))
958 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);
959 SV_LinkEdict (ent, true);
964 VectorCopy (org, ent->fields.server->origin);
965 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
974 qboolean SV_CheckWater (prvm_edict_t *ent)
979 point[0] = ent->fields.server->origin[0];
980 point[1] = ent->fields.server->origin[1];
981 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
983 ent->fields.server->waterlevel = 0;
984 ent->fields.server->watertype = CONTENTS_EMPTY;
985 cont = SV_PointSuperContents(point);
986 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
988 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
989 ent->fields.server->waterlevel = 1;
990 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
991 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
993 ent->fields.server->waterlevel = 2;
994 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
995 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
996 ent->fields.server->waterlevel = 3;
1000 return ent->fields.server->waterlevel > 1;
1009 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1012 vec3_t forward, into, side;
1014 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1015 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1017 // cut the tangential velocity
1018 i = DotProduct (stepnormal, ent->fields.server->velocity);
1019 VectorScale (stepnormal, i, into);
1020 VectorSubtract (ent->fields.server->velocity, into, side);
1021 ent->fields.server->velocity[0] = side[0] * (1 + d);
1022 ent->fields.server->velocity[1] = side[1] * (1 + d);
1027 =====================
1030 Player has come to a dead stop, possibly due to the problem with limited
1031 float precision at some angle joins in the BSP hull.
1033 Try fixing by pushing one pixel in each direction.
1035 This is a hack, but in the interest of good gameplay...
1036 ======================
1038 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1043 VectorCopy (ent->fields.server->origin, oldorg);
1046 for (i=0 ; i<8 ; i++)
1048 // try pushing a little in an axial direction
1051 case 0: dir[0] = 2; dir[1] = 0; break;
1052 case 1: dir[0] = 0; dir[1] = 2; break;
1053 case 2: dir[0] = -2; dir[1] = 0; break;
1054 case 3: dir[0] = 0; dir[1] = -2; break;
1055 case 4: dir[0] = 2; dir[1] = 2; break;
1056 case 5: dir[0] = -2; dir[1] = 2; break;
1057 case 6: dir[0] = 2; dir[1] = -2; break;
1058 case 7: dir[0] = -2; dir[1] = -2; break;
1061 SV_PushEntity (ent, dir, false);
1063 // retry the original move
1064 ent->fields.server->velocity[0] = oldvel[0];
1065 ent->fields.server->velocity[1] = oldvel[1];
1066 ent->fields.server->velocity[2] = 0;
1067 clip = SV_FlyMove (ent, 0.1, NULL);
1069 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1070 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1072 Con_DPrint("TryUnstick - success.\n");
1076 // go back to the original pos and try again
1077 VectorCopy (oldorg, ent->fields.server->origin);
1081 VectorClear (ent->fields.server->velocity);
1082 Con_DPrint("TryUnstick - failure.\n");
1087 =====================
1090 Only used by players
1091 ======================
1093 void SV_WalkMove (prvm_edict_t *ent)
1095 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1096 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1099 SV_CheckVelocity(ent);
1101 // do a regular slide move unless it looks like you ran into a step
1102 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1103 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1105 VectorCopy (ent->fields.server->origin, start_origin);
1106 VectorCopy (ent->fields.server->velocity, start_velocity);
1108 clip = SV_FlyMove (ent, sv.frametime, NULL);
1110 SV_CheckVelocity(ent);
1112 VectorCopy(ent->fields.server->origin, originalmove_origin);
1113 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1114 originalmove_clip = clip;
1115 originalmove_flags = (int)ent->fields.server->flags;
1116 originalmove_groundentity = ent->fields.server->groundentity;
1118 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1121 if (sv_nostep.integer)
1124 // if move didn't block on a step, return
1127 // if move was not trying to move into the step, return
1128 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1131 if (ent->fields.server->movetype != MOVETYPE_FLY)
1133 // return if gibbed by a trigger
1134 if (ent->fields.server->movetype != MOVETYPE_WALK)
1137 // only step up while jumping if that is enabled
1138 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1139 if (!oldonground && ent->fields.server->waterlevel == 0)
1143 // try moving up and forward to go up a step
1144 // back to start pos
1145 VectorCopy (start_origin, ent->fields.server->origin);
1146 VectorCopy (start_velocity, ent->fields.server->velocity);
1149 VectorClear (upmove);
1150 upmove[2] = sv_stepheight.value;
1151 // FIXME: don't link?
1152 SV_PushEntity(ent, upmove, false);
1155 ent->fields.server->velocity[2] = 0;
1156 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1157 ent->fields.server->velocity[2] += start_velocity[2];
1159 SV_CheckVelocity(ent);
1161 // check for stuckness, possibly due to the limited precision of floats
1162 // in the clipping hulls
1164 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1165 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1167 //Con_Printf("wall\n");
1168 // stepping up didn't make any progress, revert to original move
1169 VectorCopy(originalmove_origin, ent->fields.server->origin);
1170 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1171 //clip = originalmove_clip;
1172 ent->fields.server->flags = originalmove_flags;
1173 ent->fields.server->groundentity = originalmove_groundentity;
1174 // now try to unstick if needed
1175 //clip = SV_TryUnstick (ent, oldvel);
1179 //Con_Printf("step - ");
1181 // extra friction based on view angle
1182 if (clip & 2 && sv_wallfriction.integer)
1183 SV_WallFriction (ent, stepnormal);
1185 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1186 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)))
1190 VectorClear (downmove);
1191 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1192 // FIXME: don't link?
1193 downtrace = SV_PushEntity (ent, downmove, false);
1195 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1197 // this has been disabled so that you can't jump when you are stepping
1198 // up while already jumping (also known as the Quake2 stair jump bug)
1200 // LordHavoc: disabled this check so you can walk on monsters/players
1201 //if (ent->fields.server->solid == SOLID_BSP)
1203 //Con_Printf("onground\n");
1204 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1205 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1211 //Con_Printf("slope\n");
1212 // if the push down didn't end up on good ground, use the move without
1213 // the step up. This happens near wall / slope combinations, and can
1214 // cause the player to hop up higher on a slope too steep to climb
1215 VectorCopy(originalmove_origin, ent->fields.server->origin);
1216 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1217 //clip = originalmove_clip;
1218 ent->fields.server->flags = originalmove_flags;
1219 ent->fields.server->groundentity = originalmove_groundentity;
1222 SV_CheckVelocity(ent);
1225 //============================================================================
1231 Entities that are "stuck" to another entity
1234 void SV_Physics_Follow (prvm_edict_t *ent)
1236 vec3_t vf, vr, vu, angles, v;
1240 if (!SV_RunThink (ent))
1243 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1244 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1245 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])
1247 // quick case for no rotation
1248 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1252 angles[0] = -ent->fields.server->punchangle[0];
1253 angles[1] = ent->fields.server->punchangle[1];
1254 angles[2] = ent->fields.server->punchangle[2];
1255 AngleVectors (angles, vf, vr, vu);
1256 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];
1257 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];
1258 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];
1259 angles[0] = -e->fields.server->angles[0];
1260 angles[1] = e->fields.server->angles[1];
1261 angles[2] = e->fields.server->angles[2];
1262 AngleVectors (angles, vf, vr, vu);
1263 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1264 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1265 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1267 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1268 SV_LinkEdict (ent, true);
1272 ==============================================================================
1276 ==============================================================================
1281 SV_CheckWaterTransition
1285 void SV_CheckWaterTransition (prvm_edict_t *ent)
1288 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1289 if (!ent->fields.server->watertype)
1291 // just spawned here
1292 ent->fields.server->watertype = cont;
1293 ent->fields.server->waterlevel = 1;
1297 // check if the entity crossed into or out of water
1298 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1299 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1301 if (cont <= CONTENTS_WATER)
1303 ent->fields.server->watertype = cont;
1304 ent->fields.server->waterlevel = 1;
1308 ent->fields.server->watertype = CONTENTS_EMPTY;
1309 ent->fields.server->waterlevel = 0;
1317 Toss, bounce, and fly movement. When onground, do nothing.
1320 void SV_Physics_Toss (prvm_edict_t *ent)
1325 // if onground, return without moving
1326 if ((int)ent->fields.server->flags & FL_ONGROUND)
1328 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1330 // don't stick to ground if onground and moving upward
1331 ent->fields.server->flags -= FL_ONGROUND;
1333 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1335 // we can trust FL_ONGROUND if groundentity is world because it never moves
1338 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1340 // if ent was supported by a brush model on previous frame,
1341 // and groundentity is now freed, set groundentity to 0 (world)
1342 // which leaves it suspended in the air
1343 ent->fields.server->groundentity = 0;
1347 ent->priv.server->suspendedinairflag = false;
1349 SV_CheckVelocity (ent);
1352 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1353 SV_AddGravity (ent);
1356 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1359 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1360 trace = SV_PushEntity (ent, move, true);
1361 if (ent->priv.server->free)
1363 if (trace.bmodelstartsolid)
1365 // try to unstick the entity
1366 SV_UnstickEntity(ent);
1367 trace = SV_PushEntity (ent, move, false);
1368 if (ent->priv.server->free)
1372 if (trace.fraction < 1)
1374 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1376 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1377 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1379 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1382 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1383 // LordHavoc: fixed grenades not bouncing when fired down a slope
1384 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1386 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1387 if (trace.plane.normal[2] > 0.7 && fabs(d) < 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;
1399 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1401 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1402 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1403 VectorClear (ent->fields.server->velocity);
1404 VectorClear (ent->fields.server->avelocity);
1407 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1412 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1413 if (trace.plane.normal[2] > 0.7)
1415 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1416 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1417 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1418 ent->priv.server->suspendedinairflag = true;
1419 VectorClear (ent->fields.server->velocity);
1420 VectorClear (ent->fields.server->avelocity);
1423 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1427 // check for in water
1428 SV_CheckWaterTransition (ent);
1432 ===============================================================================
1436 ===============================================================================
1443 Monsters freefall when they don't have a ground entity, otherwise
1444 all movement is done with discrete steps.
1446 This is also used for objects that have become still on the ground, but
1447 will fall if the floor is pulled out from under them.
1450 void SV_Physics_Step (prvm_edict_t *ent)
1452 int flags = (int)ent->fields.server->flags;
1453 // don't fall at all if fly/swim
1454 if (!(flags & (FL_FLY | FL_SWIM)))
1456 if (flags & FL_ONGROUND)
1458 // freefall if onground and moving upward
1459 // freefall if not standing on a world surface (it may be a lift or trap door)
1460 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1462 ent->fields.server->flags -= FL_ONGROUND;
1464 SV_CheckVelocity(ent);
1465 SV_FlyMove(ent, sv.frametime, NULL);
1466 SV_LinkEdict(ent, true);
1471 // freefall if not onground
1472 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1475 SV_CheckVelocity(ent);
1476 SV_FlyMove(ent, sv.frametime, NULL);
1477 SV_LinkEdict(ent, true);
1480 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1481 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1488 SV_CheckWaterTransition(ent);
1491 //============================================================================
1493 static void SV_Physics_Entity (prvm_edict_t *ent)
1495 // don't run a move on newly spawned projectiles as it messes up movement
1496 // interpolation and rocket trails
1497 qboolean runmove = ent->priv.server->move;
1498 ent->priv.server->move = true;
1499 switch ((int) ent->fields.server->movetype)
1502 case MOVETYPE_FAKEPUSH:
1503 SV_Physics_Pusher (ent);
1506 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1507 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1510 case MOVETYPE_FOLLOW:
1511 SV_Physics_Follow (ent);
1513 case MOVETYPE_NOCLIP:
1514 if (SV_RunThink(ent))
1517 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1518 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1520 SV_LinkEdict(ent, false);
1523 SV_Physics_Step (ent);
1526 if (SV_RunThink (ent))
1528 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1529 SV_AddGravity (ent);
1530 SV_CheckStuck (ent);
1532 SV_LinkEdict (ent, true);
1536 case MOVETYPE_BOUNCE:
1537 case MOVETYPE_BOUNCEMISSILE:
1538 case MOVETYPE_FLYMISSILE:
1541 if (SV_RunThink (ent) && runmove)
1542 SV_Physics_Toss (ent);
1545 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1550 void SV_ApplyClientMove (void);
1551 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1553 SV_ApplyClientMove();
1554 // make sure the velocity is sane (not a NaN)
1555 SV_CheckVelocity(ent);
1556 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1557 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1559 prog->globals.server->time = sv.time;
1560 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1561 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1565 // make sure the velocity is sane (not a NaN)
1566 SV_CheckVelocity(ent);
1567 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1568 // player_run/player_stand1 does not horribly malfunction if the
1569 // velocity becomes a number that is both == 0 and != 0
1570 // (sounds to me like NaN but to be absolutely safe...)
1571 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1572 VectorClear(ent->fields.server->velocity);
1573 // call standard client pre-think
1574 prog->globals.server->time = sv.time;
1575 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1576 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1577 SV_CheckVelocity (ent);
1579 switch ((int) ent->fields.server->movetype)
1582 case MOVETYPE_FAKEPUSH:
1583 SV_Physics_Pusher (ent);
1586 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1587 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1590 case MOVETYPE_FOLLOW:
1591 SV_Physics_Follow (ent);
1593 case MOVETYPE_NOCLIP:
1594 if (SV_RunThink(ent))
1597 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1598 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1602 SV_Physics_Step (ent);
1605 if (SV_RunThink (ent))
1607 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1608 SV_AddGravity (ent);
1609 SV_CheckStuck (ent);
1614 case MOVETYPE_BOUNCE:
1615 case MOVETYPE_BOUNCEMISSILE:
1616 case MOVETYPE_FLYMISSILE:
1618 if (SV_RunThink (ent))
1619 SV_Physics_Toss (ent);
1622 if (SV_RunThink (ent))
1624 SV_CheckWater (ent);
1629 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1633 SV_CheckVelocity (ent);
1635 // call standard player post-think
1636 SV_LinkEdict (ent, true);
1638 SV_CheckVelocity (ent);
1640 prog->globals.server->time = sv.time;
1641 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1642 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1651 void SV_Physics (void)
1656 // let the progs know that a new frame has started
1657 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1658 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1659 prog->globals.server->time = sv.time;
1660 prog->globals.server->frametime = sv.frametime;
1661 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1664 // treat each object in turn
1667 // if force_retouch, relink all the entities
1668 if (prog->globals.server->force_retouch > 0)
1669 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1670 if (!ent->priv.server->free)
1671 SV_LinkEdict (ent, true); // force retouch even for stationary
1673 // run physics on the client entities
1674 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1676 if (!ent->priv.server->free)
1678 // don't do physics on disconnected clients, FrikBot relies on this
1679 if (!host_client->spawned)
1680 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1681 // don't run physics here if running asynchronously
1682 else if (host_client->clmovement_skipphysicsframes > 0)
1683 host_client->clmovement_skipphysicsframes--;
1685 SV_Physics_ClientEntity(ent);
1689 // run physics on all the non-client entities
1690 if (!sv_freezenonclients.integer)
1691 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1692 if (!ent->priv.server->free)
1693 SV_Physics_Entity(ent);
1695 if (prog->globals.server->force_retouch > 0)
1696 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1698 // LordHavoc: endframe support
1701 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1702 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1703 prog->globals.server->time = sv.time;
1704 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1707 // decrement prog->num_edicts if the highest number entities died
1708 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1710 if (!sv_freezenonclients.integer)
1711 sv.time += sv.frametime;
1715 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1720 vec3_t original_origin;
1721 vec3_t original_velocity;
1722 vec3_t original_angles;
1723 vec3_t original_avelocity;
1727 VectorCopy(tossent->fields.server->origin , original_origin );
1728 VectorCopy(tossent->fields.server->velocity , original_velocity );
1729 VectorCopy(tossent->fields.server->angles , original_angles );
1730 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1732 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1733 if (val != NULL && val->_float != 0)
1734 gravity = val->_float;
1737 gravity *= sv_gravity.value * 0.05;
1739 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1741 SV_CheckVelocity (tossent);
1742 tossent->fields.server->velocity[2] -= gravity;
1743 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1744 VectorScale (tossent->fields.server->velocity, 0.05, move);
1745 VectorAdd (tossent->fields.server->origin, move, end);
1746 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1747 VectorCopy (trace.endpos, tossent->fields.server->origin);
1749 if (trace.fraction < 1)
1753 VectorCopy(original_origin , tossent->fields.server->origin );
1754 VectorCopy(original_velocity , tossent->fields.server->velocity );
1755 VectorCopy(original_angles , tossent->fields.server->angles );
1756 VectorCopy(original_avelocity, tossent->fields.server->avelocity);