2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
29 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
31 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
40 solid_edge items only clip against bsp models.
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
46 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
47 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
48 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
49 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
50 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
51 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
52 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
53 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
54 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
55 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
57 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
58 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
60 // TODO: move this extern to server.h
61 extern cvar_t sv_clmovement_waitforinput;
63 #define MOVE_EPSILON 0.01
65 void SV_Physics_Toss (prvm_edict_t *ent);
67 void SV_Phys_Init (void)
69 Cvar_RegisterVariable(&sv_stepheight);
70 Cvar_RegisterVariable(&sv_jumpstep);
71 Cvar_RegisterVariable(&sv_wallfriction);
72 Cvar_RegisterVariable(&sv_newflymove);
73 Cvar_RegisterVariable(&sv_freezenonclients);
75 Cvar_RegisterVariable(&sv_playerphysicsqc);
77 Cvar_RegisterVariable(&sv_sound_watersplash);
78 Cvar_RegisterVariable(&sv_sound_land);
85 returns true if the entity is in solid currently
88 static int SV_TestEntityPosition (prvm_edict_t *ent)
90 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
91 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
102 void SV_CheckAllEnts (void)
107 // see if any solid entities are inside the final position
108 check = PRVM_NEXT_EDICT(prog->edicts);
109 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
111 if (check->priv.server->free)
113 if (check->fields.server->movetype == MOVETYPE_PUSH
114 || check->fields.server->movetype == MOVETYPE_NONE
115 || check->fields.server->movetype == MOVETYPE_FOLLOW
116 || check->fields.server->movetype == MOVETYPE_NOCLIP)
119 if (SV_TestEntityPosition (check))
120 Con_Print("entity in invalid position\n");
129 void SV_CheckVelocity (prvm_edict_t *ent)
137 for (i=0 ; i<3 ; i++)
139 if (IS_NAN(ent->fields.server->velocity[i]))
141 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
142 ent->fields.server->velocity[i] = 0;
144 if (IS_NAN(ent->fields.server->origin[i]))
146 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
147 ent->fields.server->origin[i] = 0;
151 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
152 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
153 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
155 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
156 ent->fields.server->velocity[0] *= wishspeed;
157 ent->fields.server->velocity[1] *= wishspeed;
158 ent->fields.server->velocity[2] *= wishspeed;
166 Runs thinking code if time. There is some play in the exact time the think
167 function will be called, because it is called before any movement is done
168 in a frame. Not used for pushmove objects, because they must be exact.
169 Returns false if the entity removed itself.
172 qboolean SV_RunThink (prvm_edict_t *ent)
176 thinktime = ent->fields.server->nextthink;
177 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
180 // don't let things stay in the past.
181 // it is possible to start that way by a trigger with a local time.
182 if (thinktime < sv.time)
185 ent->fields.server->nextthink = 0;
186 prog->globals.server->time = thinktime;
187 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
188 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
189 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
190 return !ent->priv.server->free;
197 Two entities have touched, so run their touch functions
200 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
202 int old_self, old_other;
203 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
206 old_self = prog->globals.server->self;
207 old_other = prog->globals.server->other;
209 prog->globals.server->time = sv.time;
210 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
212 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
213 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
214 prog->globals.server->trace_allsolid = trace->allsolid;
215 prog->globals.server->trace_startsolid = trace->startsolid;
216 prog->globals.server->trace_fraction = trace->fraction;
217 prog->globals.server->trace_inwater = trace->inwater;
218 prog->globals.server->trace_inopen = trace->inopen;
219 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
220 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
221 prog->globals.server->trace_plane_dist = trace->plane.dist;
223 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
225 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
226 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
227 val->_float = trace->startsupercontents;
228 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
229 val->_float = trace->hitsupercontents;
230 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
231 val->_float = trace->hitq3surfaceflags;
232 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
234 if (trace->hittexture)
236 char *s = VM_GetTempString();
237 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
238 val->string = PRVM_SetEngineString(s);
243 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
246 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
248 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
249 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
250 prog->globals.server->trace_allsolid = false;
251 prog->globals.server->trace_startsolid = false;
252 prog->globals.server->trace_fraction = 1;
253 prog->globals.server->trace_inwater = false;
254 prog->globals.server->trace_inopen = true;
255 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
256 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
257 prog->globals.server->trace_plane_dist = 0;
258 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
259 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
261 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
263 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
265 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
267 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
270 prog->globals.server->self = old_self;
271 prog->globals.server->other = old_other;
279 Slide off of the impacting object
280 returns the blocked flags (1 = floor, 2 = step / wall)
283 #define STOP_EPSILON 0.1
284 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
289 backoff = -DotProduct (in, normal) * overbounce;
290 VectorMA(in, backoff, normal, out);
292 for (i = 0;i < 3;i++)
293 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
302 The basic solid body movement clip that slides along multiple planes
303 Returns the clipflags if the velocity was modified (hit something solid)
307 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
310 // LordHavoc: increased from 5 to 32
311 #define MAX_CLIP_PLANES 32
312 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
314 int blocked, bumpcount;
315 int i, j, impact, numplanes;
317 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
320 VectorCopy(ent->fields.server->velocity, original_velocity);
321 VectorCopy(ent->fields.server->velocity, primal_velocity);
324 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
326 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
329 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
330 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
332 //if (trace.fraction < 0.002)
337 VectorCopy(ent->fields.server->origin, start);
338 start[2] += 3;//0.03125;
339 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
340 end[2] += 3;//0.03125;
341 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
342 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
344 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
350 for (i = 0;i < numplanes;i++)
352 VectorCopy(ent->fields.server->origin, start);
353 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
354 VectorMA(start, 3, planes[i], start);
355 VectorMA(end, 3, planes[i], end);
356 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
357 if (trace.fraction < testtrace.fraction)
360 VectorCopy(start, ent->fields.server->origin);
365 // VectorAdd(ent->fields.server->origin, planes[j], start);
371 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
372 if (trace.fraction < 1)
373 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
377 if (trace.bmodelstartsolid)
379 // LordHavoc: note: this code is what makes entities stick in place
380 // if embedded in world only (you can walk through other objects if
382 // entity is trapped in another solid
383 VectorClear(ent->fields.server->velocity);
387 // break if it moved the entire distance
388 if (trace.fraction == 1)
390 VectorCopy(trace.endpos, ent->fields.server->origin);
396 Con_Printf ("SV_FlyMove: !trace.ent");
397 trace.ent = prog->edicts;
400 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
404 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
408 if (trace.plane.normal[2])
410 if (trace.plane.normal[2] > 0.7)
414 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
415 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
422 // save the trace for player extrafriction
424 VectorCopy(trace.plane.normal, stepnormal);
427 if (trace.fraction >= 0.001)
429 // actually covered some distance
430 VectorCopy(trace.endpos, ent->fields.server->origin);
431 VectorCopy(ent->fields.server->velocity, original_velocity);
435 // run the impact function
438 SV_Impact(ent, &trace);
440 // break if removed by the impact function
441 if (ent->priv.server->free)
445 time_left *= 1 - trace.fraction;
447 // clipped to another plane
448 if (numplanes >= MAX_CLIP_PLANES)
450 // this shouldn't really happen
451 VectorClear(ent->fields.server->velocity);
457 for (i = 0;i < numplanes;i++)
458 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
462 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
467 VectorCopy(trace.plane.normal, planes[numplanes]);
470 if (sv_newflymove.integer)
471 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
474 // modify original_velocity so it parallels all of the clip planes
475 for (i = 0;i < numplanes;i++)
477 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
478 for (j = 0;j < numplanes;j++)
483 if (DotProduct(new_velocity, planes[j]) < 0)
493 // go along this plane
494 VectorCopy(new_velocity, ent->fields.server->velocity);
498 // go along the crease
501 VectorClear(ent->fields.server->velocity);
505 CrossProduct(planes[0], planes[1], dir);
506 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
507 VectorNormalize(dir);
508 d = DotProduct(dir, ent->fields.server->velocity);
509 VectorScale(dir, d, ent->fields.server->velocity);
513 // if current velocity is against the original velocity,
514 // stop dead to avoid tiny occilations in sloping corners
515 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
517 VectorClear(ent->fields.server->velocity);
522 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
525 if ((blocked & 1) == 0 && bumpcount > 1)
527 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
528 // flag ONGROUND if there's ground under it
529 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
533 // LordHavoc: this came from QW and allows you to get out of water more easily
534 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
535 VectorCopy(primal_velocity, ent->fields.server->velocity);
545 void SV_AddGravity (prvm_edict_t *ent)
550 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
551 if (val!=0 && val->_float)
552 ent_gravity = val->_float;
555 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
560 ===============================================================================
564 ===============================================================================
571 Does not change the entities velocity at all
574 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
580 VectorAdd (ent->fields.server->origin, push, end);
582 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
584 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
585 type = MOVE_NOMONSTERS; // only clip against bmodels
589 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
591 VectorCopy (trace.endpos, ent->fields.server->origin);
592 SV_LinkEdict (ent, true);
594 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
595 SV_Impact (ent, &trace);
606 void SV_PushMove (prvm_edict_t *pusher, float movetime)
609 float savesolid, movetime2, pushltime;
610 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
612 int numcheckentities;
613 static prvm_edict_t *checkentities[MAX_EDICTS];
614 model_t *pushermodel;
617 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])
619 pusher->fields.server->ltime += movetime;
623 switch ((int) pusher->fields.server->solid)
625 // LordHavoc: valid pusher types
629 case SOLID_CORPSE: // LordHavoc: this would be weird...
631 // LordHavoc: no collisions
634 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
635 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
636 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
637 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
638 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
639 pusher->fields.server->ltime += movetime;
640 SV_LinkEdict (pusher, false);
643 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
646 index = (int) pusher->fields.server->modelindex;
647 if (index < 1 || index >= MAX_MODELS)
649 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
652 pushermodel = sv.models[index];
654 movetime2 = movetime;
655 VectorScale(pusher->fields.server->velocity, movetime2, move1);
656 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
657 if (moveangle[0] || moveangle[2])
659 for (i = 0;i < 3;i++)
663 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
664 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
668 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
669 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
673 else if (moveangle[1])
675 for (i = 0;i < 3;i++)
679 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
680 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
684 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
685 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
691 for (i = 0;i < 3;i++)
695 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
696 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
700 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
701 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
706 VectorNegate (moveangle, a);
707 AngleVectorsFLU (a, forward, left, up);
709 VectorCopy (pusher->fields.server->origin, pushorig);
710 VectorCopy (pusher->fields.server->angles, pushang);
711 pushltime = pusher->fields.server->ltime;
713 // move the pusher to its final position
715 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
716 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
717 pusher->fields.server->ltime += movetime;
718 SV_LinkEdict (pusher, false);
720 savesolid = pusher->fields.server->solid;
722 // see if any solid entities are inside the final position
725 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
726 for (e = 0;e < numcheckentities;e++)
728 prvm_edict_t *check = checkentities[e];
729 if (check->fields.server->movetype == MOVETYPE_NONE
730 || check->fields.server->movetype == MOVETYPE_PUSH
731 || check->fields.server->movetype == MOVETYPE_FOLLOW
732 || check->fields.server->movetype == MOVETYPE_NOCLIP
733 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
736 // if the entity is standing on the pusher, it will definitely be moved
737 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
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 the entity is not inside the pusher's final position, leave it alone
746 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)
751 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
754 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
755 org2[0] = DotProduct (org, forward);
756 org2[1] = DotProduct (org, left);
757 org2[2] = DotProduct (org, up);
758 VectorSubtract (org2, org, move);
759 VectorAdd (move, move1, move);
762 VectorCopy (move1, move);
764 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
765 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
766 sv.moved_edicts[num_moved++] = check;
768 // try moving the contacted entity
769 pusher->fields.server->solid = SOLID_NOT;
770 trace = SV_PushEntity (check, move);
771 // FIXME: turn players specially
772 check->fields.server->angles[1] += trace.fraction * moveangle[1];
773 pusher->fields.server->solid = savesolid; // was SOLID_BSP
775 // if it is still inside the pusher, block
776 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)
778 // try moving the contacted entity a tiny bit further to account for precision errors
780 pusher->fields.server->solid = SOLID_NOT;
781 VectorScale(move, 1.1, move2);
782 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
783 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
784 SV_PushEntity (check, move2);
785 pusher->fields.server->solid = savesolid;
786 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)
788 // try moving the contacted entity a tiny bit less to account for precision errors
789 pusher->fields.server->solid = SOLID_NOT;
790 VectorScale(move, 0.9, move2);
791 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
792 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
793 SV_PushEntity (check, move2);
794 pusher->fields.server->solid = savesolid;
795 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)
797 // still inside pusher, so it's really blocked
800 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
802 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
805 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
806 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
810 VectorCopy (pushorig, pusher->fields.server->origin);
811 VectorCopy (pushang, pusher->fields.server->angles);
812 pusher->fields.server->ltime = pushltime;
813 SV_LinkEdict (pusher, false);
815 // move back any entities we already moved
816 for (i = 0;i < num_moved;i++)
818 prvm_edict_t *ed = sv.moved_edicts[i];
819 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
820 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
821 SV_LinkEdict (ed, false);
824 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
825 if (pusher->fields.server->blocked)
827 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
828 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
829 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
836 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
837 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
838 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
847 void SV_Physics_Pusher (prvm_edict_t *ent)
849 float thinktime, oldltime, movetime;
851 oldltime = ent->fields.server->ltime;
853 thinktime = ent->fields.server->nextthink;
854 if (thinktime < ent->fields.server->ltime + sv.frametime)
856 movetime = thinktime - ent->fields.server->ltime;
861 movetime = sv.frametime;
864 // advances ent->fields.server->ltime if not blocked
865 SV_PushMove (ent, movetime);
867 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
869 ent->fields.server->nextthink = 0;
870 prog->globals.server->time = sv.time;
871 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
872 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
873 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
879 ===============================================================================
883 ===============================================================================
890 This is a big hack to try and fix the rare case of getting stuck in the world
894 void SV_CheckStuck (prvm_edict_t *ent)
899 if (!SV_TestEntityPosition(ent))
901 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
905 VectorCopy (ent->fields.server->origin, org);
906 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
907 if (!SV_TestEntityPosition(ent))
909 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
910 SV_LinkEdict (ent, true);
914 for (z=-1 ; z< 18 ; z++)
915 for (i=-1 ; i <= 1 ; i++)
916 for (j=-1 ; j <= 1 ; j++)
918 ent->fields.server->origin[0] = org[0] + i;
919 ent->fields.server->origin[1] = org[1] + j;
920 ent->fields.server->origin[2] = org[2] + z;
921 if (!SV_TestEntityPosition(ent))
923 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);
924 SV_LinkEdict (ent, true);
929 VectorCopy (org, ent->fields.server->origin);
930 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
933 static void SV_UnstickEntity (prvm_edict_t *ent)
938 // if not stuck in a bmodel, just return
939 if (!SV_TestEntityPosition(ent))
942 VectorCopy (ent->fields.server->origin, org);
944 for (z=-1 ; z< 18 ; z += 6)
945 for (i=-1 ; i <= 1 ; i++)
946 for (j=-1 ; j <= 1 ; j++)
948 ent->fields.server->origin[0] = org[0] + i;
949 ent->fields.server->origin[1] = org[1] + j;
950 ent->fields.server->origin[2] = org[2] + z;
951 if (!SV_TestEntityPosition(ent))
953 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);
954 SV_LinkEdict (ent, true);
959 VectorCopy (org, ent->fields.server->origin);
960 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
969 qboolean SV_CheckWater (prvm_edict_t *ent)
974 point[0] = ent->fields.server->origin[0];
975 point[1] = ent->fields.server->origin[1];
976 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
978 ent->fields.server->waterlevel = 0;
979 ent->fields.server->watertype = CONTENTS_EMPTY;
980 cont = SV_PointSuperContents(point);
981 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
983 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
984 ent->fields.server->waterlevel = 1;
985 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
986 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
988 ent->fields.server->waterlevel = 2;
989 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
990 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
991 ent->fields.server->waterlevel = 3;
995 return ent->fields.server->waterlevel > 1;
1004 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1007 vec3_t forward, into, side;
1009 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1010 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1012 // cut the tangential velocity
1013 i = DotProduct (stepnormal, ent->fields.server->velocity);
1014 VectorScale (stepnormal, i, into);
1015 VectorSubtract (ent->fields.server->velocity, into, side);
1016 ent->fields.server->velocity[0] = side[0] * (1 + d);
1017 ent->fields.server->velocity[1] = side[1] * (1 + d);
1022 =====================
1025 Player has come to a dead stop, possibly due to the problem with limited
1026 float precision at some angle joins in the BSP hull.
1028 Try fixing by pushing one pixel in each direction.
1030 This is a hack, but in the interest of good gameplay...
1031 ======================
1033 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1038 VectorCopy (ent->fields.server->origin, oldorg);
1041 for (i=0 ; i<8 ; i++)
1043 // try pushing a little in an axial direction
1046 case 0: dir[0] = 2; dir[1] = 0; break;
1047 case 1: dir[0] = 0; dir[1] = 2; break;
1048 case 2: dir[0] = -2; dir[1] = 0; break;
1049 case 3: dir[0] = 0; dir[1] = -2; break;
1050 case 4: dir[0] = 2; dir[1] = 2; break;
1051 case 5: dir[0] = -2; dir[1] = 2; break;
1052 case 6: dir[0] = 2; dir[1] = -2; break;
1053 case 7: dir[0] = -2; dir[1] = -2; break;
1056 SV_PushEntity (ent, dir);
1058 // retry the original move
1059 ent->fields.server->velocity[0] = oldvel[0];
1060 ent->fields.server->velocity[1] = oldvel[1];
1061 ent->fields.server->velocity[2] = 0;
1062 clip = SV_FlyMove (ent, 0.1, NULL);
1064 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1065 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1067 Con_DPrint("TryUnstick - success.\n");
1071 // go back to the original pos and try again
1072 VectorCopy (oldorg, ent->fields.server->origin);
1076 VectorClear (ent->fields.server->velocity);
1077 Con_DPrint("TryUnstick - failure.\n");
1082 =====================
1085 Only used by players
1086 ======================
1088 void SV_WalkMove (prvm_edict_t *ent)
1090 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1091 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1094 SV_CheckVelocity(ent);
1096 // do a regular slide move unless it looks like you ran into a step
1097 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1098 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1100 VectorCopy (ent->fields.server->origin, start_origin);
1101 VectorCopy (ent->fields.server->velocity, start_velocity);
1103 clip = SV_FlyMove (ent, sv.frametime, NULL);
1105 SV_CheckVelocity(ent);
1107 VectorCopy(ent->fields.server->origin, originalmove_origin);
1108 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1109 originalmove_clip = clip;
1110 originalmove_flags = (int)ent->fields.server->flags;
1111 originalmove_groundentity = ent->fields.server->groundentity;
1113 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1116 if (sv_nostep.integer)
1119 // if move didn't block on a step, return
1122 // if move was not trying to move into the step, return
1123 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1126 if (ent->fields.server->movetype != MOVETYPE_FLY)
1128 // return if gibbed by a trigger
1129 if (ent->fields.server->movetype != MOVETYPE_WALK)
1132 // only step up while jumping if that is enabled
1133 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1134 if (!oldonground && ent->fields.server->waterlevel == 0)
1138 // try moving up and forward to go up a step
1139 // back to start pos
1140 VectorCopy (start_origin, ent->fields.server->origin);
1141 VectorCopy (start_velocity, ent->fields.server->velocity);
1144 VectorClear (upmove);
1145 upmove[2] = sv_stepheight.value;
1146 // FIXME: don't link?
1147 SV_PushEntity(ent, upmove);
1150 ent->fields.server->velocity[2] = 0;
1151 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1152 ent->fields.server->velocity[2] += start_velocity[2];
1154 SV_CheckVelocity(ent);
1156 // check for stuckness, possibly due to the limited precision of floats
1157 // in the clipping hulls
1159 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1160 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1162 //Con_Printf("wall\n");
1163 // stepping up didn't make any progress, revert to original move
1164 VectorCopy(originalmove_origin, ent->fields.server->origin);
1165 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1166 //clip = originalmove_clip;
1167 ent->fields.server->flags = originalmove_flags;
1168 ent->fields.server->groundentity = originalmove_groundentity;
1169 // now try to unstick if needed
1170 //clip = SV_TryUnstick (ent, oldvel);
1174 //Con_Printf("step - ");
1176 // extra friction based on view angle
1177 if (clip & 2 && sv_wallfriction.integer)
1178 SV_WallFriction (ent, stepnormal);
1180 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1181 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)))
1185 VectorClear (downmove);
1186 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1187 // FIXME: don't link?
1188 downtrace = SV_PushEntity (ent, downmove);
1190 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1192 // this has been disabled so that you can't jump when you are stepping
1193 // up while already jumping (also known as the Quake2 stair jump bug)
1195 // LordHavoc: disabled this check so you can walk on monsters/players
1196 //if (ent->fields.server->solid == SOLID_BSP)
1198 //Con_Printf("onground\n");
1199 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1200 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1206 //Con_Printf("slope\n");
1207 // if the push down didn't end up on good ground, use the move without
1208 // the step up. This happens near wall / slope combinations, and can
1209 // cause the player to hop up higher on a slope too steep to climb
1210 VectorCopy(originalmove_origin, ent->fields.server->origin);
1211 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1212 //clip = originalmove_clip;
1213 ent->fields.server->flags = originalmove_flags;
1214 ent->fields.server->groundentity = originalmove_groundentity;
1217 SV_CheckVelocity(ent);
1220 //============================================================================
1226 Entities that are "stuck" to another entity
1229 void SV_Physics_Follow (prvm_edict_t *ent)
1231 vec3_t vf, vr, vu, angles, v;
1235 if (!SV_RunThink (ent))
1238 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1239 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1240 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])
1242 // quick case for no rotation
1243 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1247 angles[0] = -ent->fields.server->punchangle[0];
1248 angles[1] = ent->fields.server->punchangle[1];
1249 angles[2] = ent->fields.server->punchangle[2];
1250 AngleVectors (angles, vf, vr, vu);
1251 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];
1252 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];
1253 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];
1254 angles[0] = -e->fields.server->angles[0];
1255 angles[1] = e->fields.server->angles[1];
1256 angles[2] = e->fields.server->angles[2];
1257 AngleVectors (angles, vf, vr, vu);
1258 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1259 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1260 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1262 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1263 SV_LinkEdict (ent, true);
1267 ==============================================================================
1271 ==============================================================================
1276 SV_CheckWaterTransition
1280 void SV_CheckWaterTransition (prvm_edict_t *ent)
1283 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1284 if (!ent->fields.server->watertype)
1286 // just spawned here
1287 ent->fields.server->watertype = cont;
1288 ent->fields.server->waterlevel = 1;
1292 // check if the entity crossed into or out of water
1293 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1294 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1296 if (cont <= CONTENTS_WATER)
1298 ent->fields.server->watertype = cont;
1299 ent->fields.server->waterlevel = 1;
1303 ent->fields.server->watertype = CONTENTS_EMPTY;
1304 ent->fields.server->waterlevel = 0;
1312 Toss, bounce, and fly movement. When onground, do nothing.
1315 void SV_Physics_Toss (prvm_edict_t *ent)
1320 // if onground, return without moving
1321 if ((int)ent->fields.server->flags & FL_ONGROUND)
1323 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1325 // don't stick to ground if onground and moving upward
1326 ent->fields.server->flags -= FL_ONGROUND;
1328 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1330 // we can trust FL_ONGROUND if groundentity is world because it never moves
1333 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1335 // if ent was supported by a brush model on previous frame,
1336 // and groundentity is now freed, set groundentity to 0 (world)
1337 // which leaves it suspended in the air
1338 ent->fields.server->groundentity = 0;
1342 ent->priv.server->suspendedinairflag = false;
1344 SV_CheckVelocity (ent);
1347 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1348 SV_AddGravity (ent);
1351 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1354 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1355 trace = SV_PushEntity (ent, move);
1356 if (ent->priv.server->free)
1358 if (trace.bmodelstartsolid)
1360 // try to unstick the entity
1361 SV_UnstickEntity(ent);
1362 trace = SV_PushEntity (ent, move);
1363 if (ent->priv.server->free)
1367 if (trace.fraction < 1)
1369 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1371 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1372 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1374 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1377 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1378 // LordHavoc: fixed grenades not bouncing when fired down a slope
1379 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1381 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1382 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1384 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1385 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1386 VectorClear (ent->fields.server->velocity);
1387 VectorClear (ent->fields.server->avelocity);
1390 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1394 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1396 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1397 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1398 VectorClear (ent->fields.server->velocity);
1399 VectorClear (ent->fields.server->avelocity);
1402 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1407 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1408 if (trace.plane.normal[2] > 0.7)
1410 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1411 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1412 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1413 ent->priv.server->suspendedinairflag = true;
1414 VectorClear (ent->fields.server->velocity);
1415 VectorClear (ent->fields.server->avelocity);
1418 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1422 // check for in water
1423 SV_CheckWaterTransition (ent);
1427 ===============================================================================
1431 ===============================================================================
1438 Monsters freefall when they don't have a ground entity, otherwise
1439 all movement is done with discrete steps.
1441 This is also used for objects that have become still on the ground, but
1442 will fall if the floor is pulled out from under them.
1445 void SV_Physics_Step (prvm_edict_t *ent)
1447 int flags = (int)ent->fields.server->flags;
1448 // don't fall at all if fly/swim
1449 if (!(flags & (FL_FLY | FL_SWIM)))
1451 if (flags & FL_ONGROUND)
1453 // freefall if onground and moving upward
1454 // freefall if not standing on a world surface (it may be a lift or trap door)
1455 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1457 ent->fields.server->flags -= FL_ONGROUND;
1459 SV_CheckVelocity(ent);
1460 SV_FlyMove(ent, sv.frametime, NULL);
1461 SV_LinkEdict(ent, true);
1466 // freefall if not onground
1467 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1470 SV_CheckVelocity(ent);
1471 SV_FlyMove(ent, sv.frametime, NULL);
1472 SV_LinkEdict(ent, true);
1475 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1476 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1483 SV_CheckWaterTransition(ent);
1486 //============================================================================
1488 static void SV_Physics_Entity (prvm_edict_t *ent)
1490 // don't run a move on newly spawned projectiles as it messes up movement
1491 // interpolation and rocket trails
1492 qboolean runmove = ent->priv.server->move;
1493 ent->priv.server->move = true;
1494 switch ((int) ent->fields.server->movetype)
1497 case MOVETYPE_FAKEPUSH:
1498 SV_Physics_Pusher (ent);
1501 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1502 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1505 case MOVETYPE_FOLLOW:
1506 SV_Physics_Follow (ent);
1508 case MOVETYPE_NOCLIP:
1509 if (SV_RunThink(ent))
1512 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1513 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1515 SV_LinkEdict(ent, false);
1518 SV_Physics_Step (ent);
1521 if (SV_RunThink (ent))
1523 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1524 SV_AddGravity (ent);
1525 SV_CheckStuck (ent);
1527 SV_LinkEdict (ent, true);
1531 case MOVETYPE_BOUNCE:
1532 case MOVETYPE_BOUNCEMISSILE:
1533 case MOVETYPE_FLYMISSILE:
1536 if (SV_RunThink (ent) && runmove)
1537 SV_Physics_Toss (ent);
1540 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1545 void SV_ApplyClientMove (void);
1546 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1548 SV_ApplyClientMove();
1549 // make sure the velocity is sane (not a NaN)
1550 SV_CheckVelocity(ent);
1551 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1552 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1554 prog->globals.server->time = sv.time;
1555 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1556 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1560 // make sure the velocity is sane (not a NaN)
1561 SV_CheckVelocity(ent);
1562 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1563 // player_run/player_stand1 does not horribly malfunction if the
1564 // velocity becomes a number that is both == 0 and != 0
1565 // (sounds to me like NaN but to be absolutely safe...)
1566 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1567 VectorClear(ent->fields.server->velocity);
1568 // call standard client pre-think
1569 prog->globals.server->time = sv.time;
1570 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1571 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1572 SV_CheckVelocity (ent);
1574 switch ((int) ent->fields.server->movetype)
1577 case MOVETYPE_FAKEPUSH:
1578 SV_Physics_Pusher (ent);
1581 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1582 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1585 case MOVETYPE_FOLLOW:
1586 SV_Physics_Follow (ent);
1588 case MOVETYPE_NOCLIP:
1589 if (SV_RunThink(ent))
1592 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1593 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1597 SV_Physics_Step (ent);
1600 if (SV_RunThink (ent))
1602 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1603 SV_AddGravity (ent);
1604 SV_CheckStuck (ent);
1609 case MOVETYPE_BOUNCE:
1610 case MOVETYPE_BOUNCEMISSILE:
1611 case MOVETYPE_FLYMISSILE:
1613 if (SV_RunThink (ent))
1614 SV_Physics_Toss (ent);
1617 if (SV_RunThink (ent))
1619 SV_CheckWater (ent);
1624 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1628 SV_CheckVelocity (ent);
1630 // call standard player post-think
1631 SV_LinkEdict (ent, true);
1633 SV_CheckVelocity (ent);
1635 prog->globals.server->time = sv.time;
1636 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1637 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1646 void SV_Physics (void)
1651 // let the progs know that a new frame has started
1652 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1653 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1654 prog->globals.server->time = sv.time;
1655 prog->globals.server->frametime = sv.frametime;
1656 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1659 // treat each object in turn
1662 // if force_retouch, relink all the entities
1663 if (prog->globals.server->force_retouch > 0)
1664 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1665 if (!ent->priv.server->free)
1666 SV_LinkEdict (ent, true); // force retouch even for stationary
1668 // run physics on the client entities
1669 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1671 if (!ent->priv.server->free)
1673 // don't do physics on disconnected clients, FrikBot relies on this
1674 if (!host_client->spawned)
1675 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1676 // don't run physics here if running asynchronously
1677 else if (host_client->clmovement_skipphysicsframes > 0)
1678 host_client->clmovement_skipphysicsframes--;
1680 SV_Physics_ClientEntity(ent);
1684 // run physics on all the non-client entities
1685 if (!sv_freezenonclients.integer)
1686 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1687 if (!ent->priv.server->free)
1688 SV_Physics_Entity(ent);
1690 if (prog->globals.server->force_retouch > 0)
1691 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1693 // LordHavoc: endframe support
1696 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1697 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1698 prog->globals.server->time = sv.time;
1699 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1702 // decrement prog->num_edicts if the highest number entities died
1703 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1705 if (!sv_freezenonclients.integer)
1706 sv.time += sv.frametime;
1710 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1715 vec3_t original_origin;
1716 vec3_t original_velocity;
1717 vec3_t original_angles;
1718 vec3_t original_avelocity;
1722 VectorCopy(tossent->fields.server->origin , original_origin );
1723 VectorCopy(tossent->fields.server->velocity , original_velocity );
1724 VectorCopy(tossent->fields.server->angles , original_angles );
1725 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1727 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1728 if (val != NULL && val->_float != 0)
1729 gravity = val->_float;
1732 gravity *= sv_gravity.value * 0.05;
1734 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1736 SV_CheckVelocity (tossent);
1737 tossent->fields.server->velocity[2] -= gravity;
1738 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1739 VectorScale (tossent->fields.server->velocity, 0.05, move);
1740 VectorAdd (tossent->fields.server->origin, move, end);
1741 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1742 VectorCopy (trace.endpos, tossent->fields.server->origin);
1744 if (trace.fraction < 1)
1748 VectorCopy(original_origin , tossent->fields.server->origin );
1749 VectorCopy(original_velocity , tossent->fields.server->velocity );
1750 VectorCopy(original_angles , tossent->fields.server->angles );
1751 VectorCopy(original_avelocity, tossent->fields.server->avelocity);