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 // if the entity is not inside the pusher's final position, leave it alone
740 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)
742 // remove the onground flag for non-players
743 if (check->fields.server->movetype != MOVETYPE_WALK)
744 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
748 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
751 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
752 org2[0] = DotProduct (org, forward);
753 org2[1] = DotProduct (org, left);
754 org2[2] = DotProduct (org, up);
755 VectorSubtract (org2, org, move);
756 VectorAdd (move, move1, move);
759 VectorCopy (move1, move);
761 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
762 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
763 sv.moved_edicts[num_moved++] = check;
765 // try moving the contacted entity
766 pusher->fields.server->solid = SOLID_NOT;
767 trace = SV_PushEntity (check, move);
768 // FIXME: turn players specially
769 check->fields.server->angles[1] += trace.fraction * moveangle[1];
770 pusher->fields.server->solid = savesolid; // was SOLID_BSP
772 // if it is still inside the pusher, block
773 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)
775 // try moving the contacted entity a tiny bit further to account for precision errors
777 pusher->fields.server->solid = SOLID_NOT;
778 VectorScale(move, 1.1, move2);
779 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
780 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
781 SV_PushEntity (check, move2);
782 pusher->fields.server->solid = savesolid;
783 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)
785 // try moving the contacted entity a tiny bit less to account for precision errors
786 pusher->fields.server->solid = SOLID_NOT;
787 VectorScale(move, 0.9, move2);
788 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
789 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
790 SV_PushEntity (check, move2);
791 pusher->fields.server->solid = savesolid;
792 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)
794 // still inside pusher, so it's really blocked
797 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
799 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
802 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
803 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
807 VectorCopy (pushorig, pusher->fields.server->origin);
808 VectorCopy (pushang, pusher->fields.server->angles);
809 pusher->fields.server->ltime = pushltime;
810 SV_LinkEdict (pusher, false);
812 // move back any entities we already moved
813 for (i = 0;i < num_moved;i++)
815 prvm_edict_t *ed = sv.moved_edicts[i];
816 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
817 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
818 SV_LinkEdict (ed, false);
821 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
822 if (pusher->fields.server->blocked)
824 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
825 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
826 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
833 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
834 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
835 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
844 void SV_Physics_Pusher (prvm_edict_t *ent)
846 float thinktime, oldltime, movetime;
848 oldltime = ent->fields.server->ltime;
850 thinktime = ent->fields.server->nextthink;
851 if (thinktime < ent->fields.server->ltime + sv.frametime)
853 movetime = thinktime - ent->fields.server->ltime;
858 movetime = sv.frametime;
861 // advances ent->fields.server->ltime if not blocked
862 SV_PushMove (ent, movetime);
864 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
866 ent->fields.server->nextthink = 0;
867 prog->globals.server->time = sv.time;
868 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
869 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
870 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
876 ===============================================================================
880 ===============================================================================
887 This is a big hack to try and fix the rare case of getting stuck in the world
891 void SV_CheckStuck (prvm_edict_t *ent)
896 if (!SV_TestEntityPosition(ent))
898 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
902 VectorCopy (ent->fields.server->origin, org);
903 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
904 if (!SV_TestEntityPosition(ent))
906 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
907 SV_LinkEdict (ent, true);
911 for (z=-1 ; z< 18 ; z++)
912 for (i=-1 ; i <= 1 ; i++)
913 for (j=-1 ; j <= 1 ; j++)
915 ent->fields.server->origin[0] = org[0] + i;
916 ent->fields.server->origin[1] = org[1] + j;
917 ent->fields.server->origin[2] = org[2] + z;
918 if (!SV_TestEntityPosition(ent))
920 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);
921 SV_LinkEdict (ent, true);
926 VectorCopy (org, ent->fields.server->origin);
927 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
930 static void SV_UnstickEntity (prvm_edict_t *ent)
935 // if not stuck in a bmodel, just return
936 if (!SV_TestEntityPosition(ent))
939 VectorCopy (ent->fields.server->origin, org);
941 for (z=-1 ; z< 18 ; z += 6)
942 for (i=-1 ; i <= 1 ; i++)
943 for (j=-1 ; j <= 1 ; j++)
945 ent->fields.server->origin[0] = org[0] + i;
946 ent->fields.server->origin[1] = org[1] + j;
947 ent->fields.server->origin[2] = org[2] + z;
948 if (!SV_TestEntityPosition(ent))
950 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);
951 SV_LinkEdict (ent, true);
956 VectorCopy (org, ent->fields.server->origin);
957 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
966 qboolean SV_CheckWater (prvm_edict_t *ent)
971 point[0] = ent->fields.server->origin[0];
972 point[1] = ent->fields.server->origin[1];
973 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
975 ent->fields.server->waterlevel = 0;
976 ent->fields.server->watertype = CONTENTS_EMPTY;
977 cont = SV_PointSuperContents(point);
978 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
980 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
981 ent->fields.server->waterlevel = 1;
982 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
983 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
985 ent->fields.server->waterlevel = 2;
986 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
987 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
988 ent->fields.server->waterlevel = 3;
992 return ent->fields.server->waterlevel > 1;
1001 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1004 vec3_t forward, into, side;
1006 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1007 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1009 // cut the tangential velocity
1010 i = DotProduct (stepnormal, ent->fields.server->velocity);
1011 VectorScale (stepnormal, i, into);
1012 VectorSubtract (ent->fields.server->velocity, into, side);
1013 ent->fields.server->velocity[0] = side[0] * (1 + d);
1014 ent->fields.server->velocity[1] = side[1] * (1 + d);
1019 =====================
1022 Player has come to a dead stop, possibly due to the problem with limited
1023 float precision at some angle joins in the BSP hull.
1025 Try fixing by pushing one pixel in each direction.
1027 This is a hack, but in the interest of good gameplay...
1028 ======================
1030 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1035 VectorCopy (ent->fields.server->origin, oldorg);
1038 for (i=0 ; i<8 ; i++)
1040 // try pushing a little in an axial direction
1043 case 0: dir[0] = 2; dir[1] = 0; break;
1044 case 1: dir[0] = 0; dir[1] = 2; break;
1045 case 2: dir[0] = -2; dir[1] = 0; break;
1046 case 3: dir[0] = 0; dir[1] = -2; break;
1047 case 4: dir[0] = 2; dir[1] = 2; break;
1048 case 5: dir[0] = -2; dir[1] = 2; break;
1049 case 6: dir[0] = 2; dir[1] = -2; break;
1050 case 7: dir[0] = -2; dir[1] = -2; break;
1053 SV_PushEntity (ent, dir);
1055 // retry the original move
1056 ent->fields.server->velocity[0] = oldvel[0];
1057 ent->fields.server->velocity[1] = oldvel[1];
1058 ent->fields.server->velocity[2] = 0;
1059 clip = SV_FlyMove (ent, 0.1, NULL);
1061 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1062 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1064 Con_DPrint("TryUnstick - success.\n");
1068 // go back to the original pos and try again
1069 VectorCopy (oldorg, ent->fields.server->origin);
1073 VectorClear (ent->fields.server->velocity);
1074 Con_DPrint("TryUnstick - failure.\n");
1079 =====================
1082 Only used by players
1083 ======================
1085 void SV_WalkMove (prvm_edict_t *ent)
1087 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1088 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1091 SV_CheckVelocity(ent);
1093 // do a regular slide move unless it looks like you ran into a step
1094 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1095 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1097 VectorCopy (ent->fields.server->origin, start_origin);
1098 VectorCopy (ent->fields.server->velocity, start_velocity);
1100 clip = SV_FlyMove (ent, sv.frametime, NULL);
1102 SV_CheckVelocity(ent);
1104 VectorCopy(ent->fields.server->origin, originalmove_origin);
1105 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1106 originalmove_clip = clip;
1107 originalmove_flags = (int)ent->fields.server->flags;
1108 originalmove_groundentity = ent->fields.server->groundentity;
1110 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1113 if (sv_nostep.integer)
1116 // if move didn't block on a step, return
1119 // if move was not trying to move into the step, return
1120 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1123 if (ent->fields.server->movetype != MOVETYPE_FLY)
1125 // return if gibbed by a trigger
1126 if (ent->fields.server->movetype != MOVETYPE_WALK)
1129 // only step up while jumping if that is enabled
1130 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1131 if (!oldonground && ent->fields.server->waterlevel == 0)
1135 // try moving up and forward to go up a step
1136 // back to start pos
1137 VectorCopy (start_origin, ent->fields.server->origin);
1138 VectorCopy (start_velocity, ent->fields.server->velocity);
1141 VectorClear (upmove);
1142 upmove[2] = sv_stepheight.value;
1143 // FIXME: don't link?
1144 SV_PushEntity(ent, upmove);
1147 ent->fields.server->velocity[2] = 0;
1148 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1149 ent->fields.server->velocity[2] += start_velocity[2];
1151 SV_CheckVelocity(ent);
1153 // check for stuckness, possibly due to the limited precision of floats
1154 // in the clipping hulls
1156 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1157 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1159 //Con_Printf("wall\n");
1160 // stepping up didn't make any progress, revert to original move
1161 VectorCopy(originalmove_origin, ent->fields.server->origin);
1162 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1163 //clip = originalmove_clip;
1164 ent->fields.server->flags = originalmove_flags;
1165 ent->fields.server->groundentity = originalmove_groundentity;
1166 // now try to unstick if needed
1167 //clip = SV_TryUnstick (ent, oldvel);
1171 //Con_Printf("step - ");
1173 // extra friction based on view angle
1174 if (clip & 2 && sv_wallfriction.integer)
1175 SV_WallFriction (ent, stepnormal);
1177 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1178 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)))
1182 VectorClear (downmove);
1183 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1184 // FIXME: don't link?
1185 downtrace = SV_PushEntity (ent, downmove);
1187 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1189 // this has been disabled so that you can't jump when you are stepping
1190 // up while already jumping (also known as the Quake2 stair jump bug)
1192 // LordHavoc: disabled this check so you can walk on monsters/players
1193 //if (ent->fields.server->solid == SOLID_BSP)
1195 //Con_Printf("onground\n");
1196 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1197 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1203 //Con_Printf("slope\n");
1204 // if the push down didn't end up on good ground, use the move without
1205 // the step up. This happens near wall / slope combinations, and can
1206 // cause the player to hop up higher on a slope too steep to climb
1207 VectorCopy(originalmove_origin, ent->fields.server->origin);
1208 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1209 //clip = originalmove_clip;
1210 ent->fields.server->flags = originalmove_flags;
1211 ent->fields.server->groundentity = originalmove_groundentity;
1214 SV_CheckVelocity(ent);
1217 //============================================================================
1223 Entities that are "stuck" to another entity
1226 void SV_Physics_Follow (prvm_edict_t *ent)
1228 vec3_t vf, vr, vu, angles, v;
1232 if (!SV_RunThink (ent))
1235 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1236 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1237 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])
1239 // quick case for no rotation
1240 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1244 angles[0] = -ent->fields.server->punchangle[0];
1245 angles[1] = ent->fields.server->punchangle[1];
1246 angles[2] = ent->fields.server->punchangle[2];
1247 AngleVectors (angles, vf, vr, vu);
1248 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];
1249 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];
1250 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];
1251 angles[0] = -e->fields.server->angles[0];
1252 angles[1] = e->fields.server->angles[1];
1253 angles[2] = e->fields.server->angles[2];
1254 AngleVectors (angles, vf, vr, vu);
1255 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1256 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1257 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1259 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1260 SV_LinkEdict (ent, true);
1264 ==============================================================================
1268 ==============================================================================
1273 SV_CheckWaterTransition
1277 void SV_CheckWaterTransition (prvm_edict_t *ent)
1280 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1281 if (!ent->fields.server->watertype)
1283 // just spawned here
1284 ent->fields.server->watertype = cont;
1285 ent->fields.server->waterlevel = 1;
1289 // check if the entity crossed into or out of water
1290 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1291 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1293 if (cont <= CONTENTS_WATER)
1295 ent->fields.server->watertype = cont;
1296 ent->fields.server->waterlevel = 1;
1300 ent->fields.server->watertype = CONTENTS_EMPTY;
1301 ent->fields.server->waterlevel = 0;
1309 Toss, bounce, and fly movement. When onground, do nothing.
1312 void SV_Physics_Toss (prvm_edict_t *ent)
1317 // if onground, return without moving
1318 if ((int)ent->fields.server->flags & FL_ONGROUND)
1320 // don't stick to ground if onground and moving upward
1321 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1322 ent->fields.server->flags -= FL_ONGROUND;
1325 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1326 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1328 // if ent was supported by a brush model on previous frame,
1329 // and groundentity is now freed, set groundentity to 0 (floating)
1330 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1332 // leave it suspended in the air
1333 ent->fields.server->groundentity = 0;
1338 ent->priv.server->suspendedinairflag = false;
1340 SV_CheckVelocity (ent);
1343 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1344 SV_AddGravity (ent);
1347 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1350 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1351 trace = SV_PushEntity (ent, move);
1352 if (ent->priv.server->free)
1354 if (trace.bmodelstartsolid)
1356 // try to unstick the entity
1357 SV_UnstickEntity(ent);
1358 trace = SV_PushEntity (ent, move);
1359 if (ent->priv.server->free)
1363 if (trace.fraction < 1)
1365 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1367 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1368 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1370 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1373 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1374 // LordHavoc: fixed grenades not bouncing when fired down a slope
1375 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1377 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1378 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1380 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1381 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1382 VectorClear (ent->fields.server->velocity);
1383 VectorClear (ent->fields.server->avelocity);
1386 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1390 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1392 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1393 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1394 VectorClear (ent->fields.server->velocity);
1395 VectorClear (ent->fields.server->avelocity);
1398 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1403 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1404 if (trace.plane.normal[2] > 0.7)
1406 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1407 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1408 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1409 ent->priv.server->suspendedinairflag = true;
1410 VectorClear (ent->fields.server->velocity);
1411 VectorClear (ent->fields.server->avelocity);
1414 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1418 // check for in water
1419 SV_CheckWaterTransition (ent);
1423 ===============================================================================
1427 ===============================================================================
1434 Monsters freefall when they don't have a ground entity, otherwise
1435 all movement is done with discrete steps.
1437 This is also used for objects that have become still on the ground, but
1438 will fall if the floor is pulled out from under them.
1441 void SV_Physics_Step (prvm_edict_t *ent)
1443 int flags = (int)ent->fields.server->flags;
1444 // don't fall at all if fly/swim
1445 if (!(flags & (FL_FLY | FL_SWIM)))
1447 if (flags & FL_ONGROUND)
1449 // freefall if onground and moving upward
1450 // freefall if not standing on a world surface (it may be a lift)
1451 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1452 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1454 ent->fields.server->flags -= FL_ONGROUND;
1456 SV_CheckVelocity(ent);
1457 SV_FlyMove(ent, sv.frametime, NULL);
1458 SV_LinkEdict(ent, true);
1463 // freefall if not onground
1464 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1467 SV_CheckVelocity(ent);
1468 SV_FlyMove(ent, sv.frametime, NULL);
1469 SV_LinkEdict(ent, true);
1472 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1473 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1480 SV_CheckWaterTransition(ent);
1483 //============================================================================
1485 static void SV_Physics_Entity (prvm_edict_t *ent)
1487 // don't run a move on newly spawned projectiles as it messes up movement
1488 // interpolation and rocket trails
1489 qboolean runmove = ent->priv.server->move;
1490 ent->priv.server->move = true;
1491 switch ((int) ent->fields.server->movetype)
1494 case MOVETYPE_FAKEPUSH:
1495 SV_Physics_Pusher (ent);
1498 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1499 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1502 case MOVETYPE_FOLLOW:
1503 SV_Physics_Follow (ent);
1505 case MOVETYPE_NOCLIP:
1506 if (SV_RunThink(ent))
1509 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1510 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1512 SV_LinkEdict(ent, false);
1515 SV_Physics_Step (ent);
1518 if (SV_RunThink (ent))
1520 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1521 SV_AddGravity (ent);
1522 SV_CheckStuck (ent);
1524 SV_LinkEdict (ent, true);
1528 case MOVETYPE_BOUNCE:
1529 case MOVETYPE_BOUNCEMISSILE:
1530 case MOVETYPE_FLYMISSILE:
1533 if (SV_RunThink (ent) && runmove)
1534 SV_Physics_Toss (ent);
1537 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1542 void SV_ApplyClientMove (void);
1543 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1545 SV_ApplyClientMove();
1546 // make sure the velocity is sane (not a NaN)
1547 SV_CheckVelocity(ent);
1548 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1549 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1551 prog->globals.server->time = sv.time;
1552 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1553 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1557 // make sure the velocity is sane (not a NaN)
1558 SV_CheckVelocity(ent);
1559 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1560 // player_run/player_stand1 does not horribly malfunction if the
1561 // velocity becomes a number that is both == 0 and != 0
1562 // (sounds to me like NaN but to be absolutely safe...)
1563 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1564 VectorClear(ent->fields.server->velocity);
1565 // call standard client pre-think
1566 prog->globals.server->time = sv.time;
1567 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1568 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1569 SV_CheckVelocity (ent);
1571 switch ((int) ent->fields.server->movetype)
1574 case MOVETYPE_FAKEPUSH:
1575 SV_Physics_Pusher (ent);
1578 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1579 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1582 case MOVETYPE_FOLLOW:
1583 SV_Physics_Follow (ent);
1585 case MOVETYPE_NOCLIP:
1586 if (SV_RunThink(ent))
1589 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1590 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1594 SV_Physics_Step (ent);
1597 if (SV_RunThink (ent))
1599 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1600 SV_AddGravity (ent);
1601 SV_CheckStuck (ent);
1606 case MOVETYPE_BOUNCE:
1607 case MOVETYPE_BOUNCEMISSILE:
1608 case MOVETYPE_FLYMISSILE:
1610 if (SV_RunThink (ent))
1611 SV_Physics_Toss (ent);
1614 if (SV_RunThink (ent))
1616 SV_CheckWater (ent);
1621 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1625 SV_CheckVelocity (ent);
1627 // call standard player post-think
1628 SV_LinkEdict (ent, true);
1630 SV_CheckVelocity (ent);
1632 prog->globals.server->time = sv.time;
1633 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1634 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1643 void SV_Physics (void)
1648 // let the progs know that a new frame has started
1649 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1650 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1651 prog->globals.server->time = sv.time;
1652 prog->globals.server->frametime = sv.frametime;
1653 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1656 // treat each object in turn
1659 // if force_retouch, relink all the entities
1660 if (prog->globals.server->force_retouch > 0)
1661 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1662 if (!ent->priv.server->free)
1663 SV_LinkEdict (ent, true); // force retouch even for stationary
1665 // run physics on the client entities
1666 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1668 if (!ent->priv.server->free)
1670 // don't do physics on disconnected clients, FrikBot relies on this
1671 if (!host_client->spawned)
1672 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1673 // don't run physics here if running asynchronously
1674 else if (host_client->clmovement_skipphysicsframes > 0)
1675 host_client->clmovement_skipphysicsframes--;
1677 SV_Physics_ClientEntity(ent);
1681 // run physics on all the non-client entities
1682 if (!sv_freezenonclients.integer)
1683 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1684 if (!ent->priv.server->free)
1685 SV_Physics_Entity(ent);
1687 if (prog->globals.server->force_retouch > 0)
1688 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1690 // LordHavoc: endframe support
1693 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1694 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1695 prog->globals.server->time = sv.time;
1696 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1699 // decrement prog->num_edicts if the highest number entities died
1700 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1702 if (!sv_freezenonclients.integer)
1703 sv.time += sv.frametime;
1707 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1712 vec3_t original_origin;
1713 vec3_t original_velocity;
1714 vec3_t original_angles;
1715 vec3_t original_avelocity;
1719 VectorCopy(tossent->fields.server->origin , original_origin );
1720 VectorCopy(tossent->fields.server->velocity , original_velocity );
1721 VectorCopy(tossent->fields.server->angles , original_angles );
1722 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1724 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1725 if (val != NULL && val->_float != 0)
1726 gravity = val->_float;
1729 gravity *= sv_gravity.value * 0.05;
1731 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1733 SV_CheckVelocity (tossent);
1734 tossent->fields.server->velocity[2] -= gravity;
1735 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1736 VectorScale (tossent->fields.server->velocity, 0.05, move);
1737 VectorAdd (tossent->fields.server->origin, move, end);
1738 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1739 VectorCopy (trace.endpos, tossent->fields.server->origin);
1741 if (trace.fraction < 1)
1745 VectorCopy(original_origin , tossent->fields.server->origin );
1746 VectorCopy(original_velocity , tossent->fields.server->velocity );
1747 VectorCopy(original_angles , tossent->fields.server->angles );
1748 VectorCopy(original_avelocity, tossent->fields.server->avelocity);