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");
124 // DRESK - Support for Entity Contents Transition Event
127 SV_CheckContentsTransition
129 returns true if entity had a valid contentstransition function call
132 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
134 int bValidFunctionCall;
135 prvm_eval_t *contentstransition;
137 // Default Valid Function Call to False
138 bValidFunctionCall = false;
140 if(ent->fields.server->watertype != nContents)
141 { // Changed Contents
142 // Acquire Contents Transition Function from QC
143 contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition);
145 if(contentstransition->function)
146 { // Valid Function; Execute
147 // Assign Valid Function
148 bValidFunctionCall = true;
149 // Prepare Parameters (Original Contents, New Contents)
151 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
153 PRVM_G_FLOAT(OFS_PARM1) = nContents;
154 // Execute VM Function
155 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
159 // Return if Function Call was Valid
160 return bValidFunctionCall;
169 void SV_CheckVelocity (prvm_edict_t *ent)
177 for (i=0 ; i<3 ; i++)
179 if (IS_NAN(ent->fields.server->velocity[i]))
181 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
182 ent->fields.server->velocity[i] = 0;
184 if (IS_NAN(ent->fields.server->origin[i]))
186 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
187 ent->fields.server->origin[i] = 0;
191 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
192 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
193 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
195 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
196 ent->fields.server->velocity[0] *= wishspeed;
197 ent->fields.server->velocity[1] *= wishspeed;
198 ent->fields.server->velocity[2] *= wishspeed;
206 Runs thinking code if time. There is some play in the exact time the think
207 function will be called, because it is called before any movement is done
208 in a frame. Not used for pushmove objects, because they must be exact.
209 Returns false if the entity removed itself.
212 qboolean SV_RunThink (prvm_edict_t *ent)
216 thinktime = ent->fields.server->nextthink;
217 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
220 // don't let things stay in the past.
221 // it is possible to start that way by a trigger with a local time.
222 if (thinktime < sv.time)
225 ent->fields.server->nextthink = 0;
226 prog->globals.server->time = thinktime;
227 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
228 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
229 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
230 return !ent->priv.server->free;
237 Two entities have touched, so run their touch functions
240 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
242 int old_self, old_other;
243 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
246 old_self = prog->globals.server->self;
247 old_other = prog->globals.server->other;
249 prog->globals.server->time = sv.time;
250 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
252 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
253 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
254 prog->globals.server->trace_allsolid = trace->allsolid;
255 prog->globals.server->trace_startsolid = trace->startsolid;
256 prog->globals.server->trace_fraction = trace->fraction;
257 prog->globals.server->trace_inwater = trace->inwater;
258 prog->globals.server->trace_inopen = trace->inopen;
259 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
260 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
261 prog->globals.server->trace_plane_dist = trace->plane.dist;
263 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
265 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
266 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
267 val->_float = trace->startsupercontents;
268 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
269 val->_float = trace->hitsupercontents;
270 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
271 val->_float = trace->hitq3surfaceflags;
272 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
274 if (trace->hittexture)
276 char *s = VM_GetTempString();
277 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
278 val->string = PRVM_SetEngineString(s);
283 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
286 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
288 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
289 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
290 prog->globals.server->trace_allsolid = false;
291 prog->globals.server->trace_startsolid = false;
292 prog->globals.server->trace_fraction = 1;
293 prog->globals.server->trace_inwater = false;
294 prog->globals.server->trace_inopen = true;
295 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
296 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
297 prog->globals.server->trace_plane_dist = 0;
298 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
299 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
301 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
303 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
305 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
307 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
310 prog->globals.server->self = old_self;
311 prog->globals.server->other = old_other;
319 Slide off of the impacting object
320 returns the blocked flags (1 = floor, 2 = step / wall)
323 #define STOP_EPSILON 0.1
324 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
329 backoff = -DotProduct (in, normal) * overbounce;
330 VectorMA(in, backoff, normal, out);
332 for (i = 0;i < 3;i++)
333 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
342 The basic solid body movement clip that slides along multiple planes
343 Returns the clipflags if the velocity was modified (hit something solid)
347 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
350 // LordHavoc: increased from 5 to 32
351 #define MAX_CLIP_PLANES 32
352 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
354 int blocked, bumpcount;
355 int i, j, impact, numplanes;
357 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
360 VectorCopy(ent->fields.server->velocity, original_velocity);
361 VectorCopy(ent->fields.server->velocity, primal_velocity);
364 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
366 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
369 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
370 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
372 //if (trace.fraction < 0.002)
377 VectorCopy(ent->fields.server->origin, start);
378 start[2] += 3;//0.03125;
379 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
380 end[2] += 3;//0.03125;
381 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
382 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)))
384 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
390 for (i = 0;i < numplanes;i++)
392 VectorCopy(ent->fields.server->origin, start);
393 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
394 VectorMA(start, 3, planes[i], start);
395 VectorMA(end, 3, planes[i], end);
396 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
397 if (trace.fraction < testtrace.fraction)
400 VectorCopy(start, ent->fields.server->origin);
405 // VectorAdd(ent->fields.server->origin, planes[j], start);
411 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);
412 if (trace.fraction < 1)
413 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
418 if (trace.bmodelstartsolid)
420 // LordHavoc: note: this code is what makes entities stick in place
421 // if embedded in world only (you can walk through other objects if
423 // entity is trapped in another solid
424 VectorClear(ent->fields.server->velocity);
429 // break if it moved the entire distance
430 if (trace.fraction == 1)
432 VectorCopy(trace.endpos, ent->fields.server->origin);
438 Con_Printf ("SV_FlyMove: !trace.ent");
439 trace.ent = prog->edicts;
442 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
446 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
450 if (trace.plane.normal[2])
452 if (trace.plane.normal[2] > 0.7)
456 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
457 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
464 // save the trace for player extrafriction
466 VectorCopy(trace.plane.normal, stepnormal);
469 if (trace.fraction >= 0.001)
471 // actually covered some distance
472 VectorCopy(trace.endpos, ent->fields.server->origin);
473 VectorCopy(ent->fields.server->velocity, original_velocity);
477 // run the impact function
480 SV_Impact(ent, &trace);
482 // break if removed by the impact function
483 if (ent->priv.server->free)
487 time_left *= 1 - trace.fraction;
489 // clipped to another plane
490 if (numplanes >= MAX_CLIP_PLANES)
492 // this shouldn't really happen
493 VectorClear(ent->fields.server->velocity);
499 for (i = 0;i < numplanes;i++)
500 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
504 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
509 VectorCopy(trace.plane.normal, planes[numplanes]);
512 if (sv_newflymove.integer)
513 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
516 // modify original_velocity so it parallels all of the clip planes
517 for (i = 0;i < numplanes;i++)
519 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
520 for (j = 0;j < numplanes;j++)
525 if (DotProduct(new_velocity, planes[j]) < 0)
535 // go along this plane
536 VectorCopy(new_velocity, ent->fields.server->velocity);
540 // go along the crease
543 VectorClear(ent->fields.server->velocity);
547 CrossProduct(planes[0], planes[1], dir);
548 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
549 VectorNormalize(dir);
550 d = DotProduct(dir, ent->fields.server->velocity);
551 VectorScale(dir, d, ent->fields.server->velocity);
555 // if current velocity is against the original velocity,
556 // stop dead to avoid tiny occilations in sloping corners
557 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
559 VectorClear(ent->fields.server->velocity);
564 //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]);
567 if ((blocked & 1) == 0 && bumpcount > 1)
569 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
570 // flag ONGROUND if there's ground under it
571 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
575 // LordHavoc: this came from QW and allows you to get out of water more easily
576 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
577 VectorCopy(primal_velocity, ent->fields.server->velocity);
587 void SV_AddGravity (prvm_edict_t *ent)
592 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
593 if (val!=0 && val->_float)
594 ent_gravity = val->_float;
597 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
602 ===============================================================================
606 ===============================================================================
613 Does not change the entities velocity at all
616 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
622 VectorAdd (ent->fields.server->origin, push, end);
624 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
626 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
627 type = MOVE_NOMONSTERS; // only clip against bmodels
631 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
632 if (trace.bmodelstartsolid && failonbmodelstartsolid)
635 VectorCopy (trace.endpos, ent->fields.server->origin);
636 SV_LinkEdict (ent, true);
638 if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
639 SV_Impact (ent, &trace);
650 void SV_PushMove (prvm_edict_t *pusher, float movetime)
653 float savesolid, movetime2, pushltime;
654 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
656 int numcheckentities;
657 static prvm_edict_t *checkentities[MAX_EDICTS];
658 model_t *pushermodel;
661 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])
663 pusher->fields.server->ltime += movetime;
667 switch ((int) pusher->fields.server->solid)
669 // LordHavoc: valid pusher types
673 case SOLID_CORPSE: // LordHavoc: this would be weird...
675 // LordHavoc: no collisions
678 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
679 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
680 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
681 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
682 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
683 pusher->fields.server->ltime += movetime;
684 SV_LinkEdict (pusher, false);
687 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
690 index = (int) pusher->fields.server->modelindex;
691 if (index < 1 || index >= MAX_MODELS)
693 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
696 pushermodel = sv.models[index];
698 movetime2 = movetime;
699 VectorScale(pusher->fields.server->velocity, movetime2, move1);
700 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
701 if (moveangle[0] || moveangle[2])
703 for (i = 0;i < 3;i++)
707 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
708 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
712 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
713 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
717 else if (moveangle[1])
719 for (i = 0;i < 3;i++)
723 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
724 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
728 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
729 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
735 for (i = 0;i < 3;i++)
739 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
740 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
744 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
745 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
750 VectorNegate (moveangle, a);
751 AngleVectorsFLU (a, forward, left, up);
753 VectorCopy (pusher->fields.server->origin, pushorig);
754 VectorCopy (pusher->fields.server->angles, pushang);
755 pushltime = pusher->fields.server->ltime;
757 // move the pusher to its final position
759 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
760 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
761 pusher->fields.server->ltime += movetime;
762 SV_LinkEdict (pusher, false);
764 savesolid = pusher->fields.server->solid;
766 // see if any solid entities are inside the final position
769 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
770 for (e = 0;e < numcheckentities;e++)
772 prvm_edict_t *check = checkentities[e];
773 if (check->fields.server->movetype == MOVETYPE_NONE
774 || check->fields.server->movetype == MOVETYPE_PUSH
775 || check->fields.server->movetype == MOVETYPE_FOLLOW
776 || check->fields.server->movetype == MOVETYPE_NOCLIP
777 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
780 // if the entity is standing on the pusher, it will definitely be moved
781 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
783 // remove the onground flag for non-players
784 if (check->fields.server->movetype != MOVETYPE_WALK)
785 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
789 // if the entity is not inside the pusher's final position, leave it alone
790 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
795 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
798 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
799 org2[0] = DotProduct (org, forward);
800 org2[1] = DotProduct (org, left);
801 org2[2] = DotProduct (org, up);
802 VectorSubtract (org2, org, move);
803 VectorAdd (move, move1, move);
806 VectorCopy (move1, move);
808 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
809 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
810 sv.moved_edicts[num_moved++] = check;
812 // try moving the contacted entity
813 pusher->fields.server->solid = SOLID_NOT;
814 trace = SV_PushEntity (check, move, true);
815 // FIXME: turn players specially
816 check->fields.server->angles[1] += trace.fraction * moveangle[1];
817 pusher->fields.server->solid = savesolid; // was SOLID_BSP
818 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
820 // if it is still inside the pusher, block
821 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)
823 // try moving the contacted entity a tiny bit further to account for precision errors
825 pusher->fields.server->solid = SOLID_NOT;
826 VectorScale(move, 1.1, move2);
827 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
828 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
829 SV_PushEntity (check, move2, true);
830 pusher->fields.server->solid = savesolid;
831 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)
833 // try moving the contacted entity a tiny bit less to account for precision errors
834 pusher->fields.server->solid = SOLID_NOT;
835 VectorScale(move, 0.9, move2);
836 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
837 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
838 SV_PushEntity (check, move2, true);
839 pusher->fields.server->solid = savesolid;
840 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)
842 // still inside pusher, so it's really blocked
845 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
847 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
850 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
851 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
855 VectorCopy (pushorig, pusher->fields.server->origin);
856 VectorCopy (pushang, pusher->fields.server->angles);
857 pusher->fields.server->ltime = pushltime;
858 SV_LinkEdict (pusher, false);
860 // move back any entities we already moved
861 for (i = 0;i < num_moved;i++)
863 prvm_edict_t *ed = sv.moved_edicts[i];
864 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
865 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
866 SV_LinkEdict (ed, false);
869 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
870 if (pusher->fields.server->blocked)
872 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
873 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
874 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
881 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
882 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
883 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
892 void SV_Physics_Pusher (prvm_edict_t *ent)
894 float thinktime, oldltime, movetime;
896 oldltime = ent->fields.server->ltime;
898 thinktime = ent->fields.server->nextthink;
899 if (thinktime < ent->fields.server->ltime + sv.frametime)
901 movetime = thinktime - ent->fields.server->ltime;
906 movetime = sv.frametime;
909 // advances ent->fields.server->ltime if not blocked
910 SV_PushMove (ent, movetime);
912 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
914 ent->fields.server->nextthink = 0;
915 prog->globals.server->time = sv.time;
916 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
917 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
918 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
924 ===============================================================================
928 ===============================================================================
935 This is a big hack to try and fix the rare case of getting stuck in the world
939 void SV_CheckStuck (prvm_edict_t *ent)
944 if (!SV_TestEntityPosition(ent))
946 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
950 VectorCopy (ent->fields.server->origin, org);
951 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
952 if (!SV_TestEntityPosition(ent))
954 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
955 SV_LinkEdict (ent, true);
959 for (z=-1 ; z< 18 ; z++)
960 for (i=-1 ; i <= 1 ; i++)
961 for (j=-1 ; j <= 1 ; j++)
963 ent->fields.server->origin[0] = org[0] + i;
964 ent->fields.server->origin[1] = org[1] + j;
965 ent->fields.server->origin[2] = org[2] + z;
966 if (!SV_TestEntityPosition(ent))
968 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);
969 SV_LinkEdict (ent, true);
974 VectorCopy (org, ent->fields.server->origin);
975 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
978 static void SV_UnstickEntity (prvm_edict_t *ent)
983 // if not stuck in a bmodel, just return
984 if (!SV_TestEntityPosition(ent))
987 VectorCopy (ent->fields.server->origin, org);
989 for (z=-1 ; z< 18 ; z += 6)
990 for (i=-1 ; i <= 1 ; i++)
991 for (j=-1 ; j <= 1 ; j++)
993 ent->fields.server->origin[0] = org[0] + i;
994 ent->fields.server->origin[1] = org[1] + j;
995 ent->fields.server->origin[2] = org[2] + z;
996 if (!SV_TestEntityPosition(ent))
998 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);
999 SV_LinkEdict (ent, true);
1004 VectorCopy (org, ent->fields.server->origin);
1005 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1014 qboolean SV_CheckWater (prvm_edict_t *ent)
1017 int nNativeContents;
1020 point[0] = ent->fields.server->origin[0];
1021 point[1] = ent->fields.server->origin[1];
1022 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1024 // DRESK - Support for Entity Contents Transition Event
1025 // NOTE: Some logic needed to be slightly re-ordered
1026 // to not affect performance and allow for the feature.
1028 // Acquire Super Contents Prior to Resets
1029 cont = SV_PointSuperContents(point);
1030 // Acquire Native Contents Here
1031 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1033 // DRESK - Support for Entity Contents Transition Event
1034 if(ent->fields.server->watertype)
1035 // Entity did NOT Spawn; Check
1036 SV_CheckContentsTransition(ent, nNativeContents);
1039 ent->fields.server->waterlevel = 0;
1040 ent->fields.server->watertype = CONTENTS_EMPTY;
1041 cont = SV_PointSuperContents(point);
1042 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1044 ent->fields.server->watertype = nNativeContents;
1045 ent->fields.server->waterlevel = 1;
1046 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1047 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1049 ent->fields.server->waterlevel = 2;
1050 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1051 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1052 ent->fields.server->waterlevel = 3;
1056 return ent->fields.server->waterlevel > 1;
1065 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1068 vec3_t forward, into, side;
1070 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1071 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1073 // cut the tangential velocity
1074 i = DotProduct (stepnormal, ent->fields.server->velocity);
1075 VectorScale (stepnormal, i, into);
1076 VectorSubtract (ent->fields.server->velocity, into, side);
1077 ent->fields.server->velocity[0] = side[0] * (1 + d);
1078 ent->fields.server->velocity[1] = side[1] * (1 + d);
1083 =====================
1086 Player has come to a dead stop, possibly due to the problem with limited
1087 float precision at some angle joins in the BSP hull.
1089 Try fixing by pushing one pixel in each direction.
1091 This is a hack, but in the interest of good gameplay...
1092 ======================
1094 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1099 VectorCopy (ent->fields.server->origin, oldorg);
1102 for (i=0 ; i<8 ; i++)
1104 // try pushing a little in an axial direction
1107 case 0: dir[0] = 2; dir[1] = 0; break;
1108 case 1: dir[0] = 0; dir[1] = 2; break;
1109 case 2: dir[0] = -2; dir[1] = 0; break;
1110 case 3: dir[0] = 0; dir[1] = -2; break;
1111 case 4: dir[0] = 2; dir[1] = 2; break;
1112 case 5: dir[0] = -2; dir[1] = 2; break;
1113 case 6: dir[0] = 2; dir[1] = -2; break;
1114 case 7: dir[0] = -2; dir[1] = -2; break;
1117 SV_PushEntity (ent, dir, false);
1119 // retry the original move
1120 ent->fields.server->velocity[0] = oldvel[0];
1121 ent->fields.server->velocity[1] = oldvel[1];
1122 ent->fields.server->velocity[2] = 0;
1123 clip = SV_FlyMove (ent, 0.1, NULL);
1125 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1126 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1128 Con_DPrint("TryUnstick - success.\n");
1132 // go back to the original pos and try again
1133 VectorCopy (oldorg, ent->fields.server->origin);
1137 VectorClear (ent->fields.server->velocity);
1138 Con_DPrint("TryUnstick - failure.\n");
1143 =====================
1146 Only used by players
1147 ======================
1149 void SV_WalkMove (prvm_edict_t *ent)
1151 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1152 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1155 SV_CheckVelocity(ent);
1157 // do a regular slide move unless it looks like you ran into a step
1158 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1159 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1161 VectorCopy (ent->fields.server->origin, start_origin);
1162 VectorCopy (ent->fields.server->velocity, start_velocity);
1164 clip = SV_FlyMove (ent, sv.frametime, NULL);
1166 SV_CheckVelocity(ent);
1168 VectorCopy(ent->fields.server->origin, originalmove_origin);
1169 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1170 originalmove_clip = clip;
1171 originalmove_flags = (int)ent->fields.server->flags;
1172 originalmove_groundentity = ent->fields.server->groundentity;
1174 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1177 if (sv_nostep.integer)
1180 // if move didn't block on a step, return
1183 // if move was not trying to move into the step, return
1184 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1187 if (ent->fields.server->movetype != MOVETYPE_FLY)
1189 // return if gibbed by a trigger
1190 if (ent->fields.server->movetype != MOVETYPE_WALK)
1193 // only step up while jumping if that is enabled
1194 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1195 if (!oldonground && ent->fields.server->waterlevel == 0)
1199 // try moving up and forward to go up a step
1200 // back to start pos
1201 VectorCopy (start_origin, ent->fields.server->origin);
1202 VectorCopy (start_velocity, ent->fields.server->velocity);
1205 VectorClear (upmove);
1206 upmove[2] = sv_stepheight.value;
1207 // FIXME: don't link?
1208 SV_PushEntity(ent, upmove, false);
1211 ent->fields.server->velocity[2] = 0;
1212 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1213 ent->fields.server->velocity[2] += start_velocity[2];
1215 SV_CheckVelocity(ent);
1217 // check for stuckness, possibly due to the limited precision of floats
1218 // in the clipping hulls
1220 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1221 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1223 //Con_Printf("wall\n");
1224 // stepping up didn't make any progress, revert to original move
1225 VectorCopy(originalmove_origin, ent->fields.server->origin);
1226 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1227 //clip = originalmove_clip;
1228 ent->fields.server->flags = originalmove_flags;
1229 ent->fields.server->groundentity = originalmove_groundentity;
1230 // now try to unstick if needed
1231 //clip = SV_TryUnstick (ent, oldvel);
1235 //Con_Printf("step - ");
1237 // extra friction based on view angle
1238 if (clip & 2 && sv_wallfriction.integer)
1239 SV_WallFriction (ent, stepnormal);
1241 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1242 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)))
1246 VectorClear (downmove);
1247 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1248 // FIXME: don't link?
1249 downtrace = SV_PushEntity (ent, downmove, false);
1251 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1253 // this has been disabled so that you can't jump when you are stepping
1254 // up while already jumping (also known as the Quake2 stair jump bug)
1256 // LordHavoc: disabled this check so you can walk on monsters/players
1257 //if (ent->fields.server->solid == SOLID_BSP)
1259 //Con_Printf("onground\n");
1260 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1261 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1267 //Con_Printf("slope\n");
1268 // if the push down didn't end up on good ground, use the move without
1269 // the step up. This happens near wall / slope combinations, and can
1270 // cause the player to hop up higher on a slope too steep to climb
1271 VectorCopy(originalmove_origin, ent->fields.server->origin);
1272 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1273 //clip = originalmove_clip;
1274 ent->fields.server->flags = originalmove_flags;
1275 ent->fields.server->groundentity = originalmove_groundentity;
1278 SV_CheckVelocity(ent);
1281 //============================================================================
1287 Entities that are "stuck" to another entity
1290 void SV_Physics_Follow (prvm_edict_t *ent)
1292 vec3_t vf, vr, vu, angles, v;
1296 if (!SV_RunThink (ent))
1299 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1300 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1301 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])
1303 // quick case for no rotation
1304 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1308 angles[0] = -ent->fields.server->punchangle[0];
1309 angles[1] = ent->fields.server->punchangle[1];
1310 angles[2] = ent->fields.server->punchangle[2];
1311 AngleVectors (angles, vf, vr, vu);
1312 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];
1313 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];
1314 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];
1315 angles[0] = -e->fields.server->angles[0];
1316 angles[1] = e->fields.server->angles[1];
1317 angles[2] = e->fields.server->angles[2];
1318 AngleVectors (angles, vf, vr, vu);
1319 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1320 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1321 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1323 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1324 SV_LinkEdict (ent, true);
1328 ==============================================================================
1332 ==============================================================================
1337 SV_CheckWaterTransition
1341 void SV_CheckWaterTransition (prvm_edict_t *ent)
1344 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1345 if (!ent->fields.server->watertype)
1347 // just spawned here
1348 ent->fields.server->watertype = cont;
1349 ent->fields.server->waterlevel = 1;
1353 // DRESK - Support for Entity Contents Transition Event
1354 // NOTE: Call here BEFORE updating the watertype below,
1355 // and suppress watersplash sound if a valid function
1356 // call was made to allow for custom "splash" sounds.
1357 if( !SV_CheckContentsTransition(ent, cont) )
1358 { // Contents Transition Function Invalid; Potentially Play Water Sound
1359 // check if the entity crossed into or out of water
1360 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1361 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1364 if (cont <= CONTENTS_WATER)
1366 ent->fields.server->watertype = cont;
1367 ent->fields.server->waterlevel = 1;
1371 ent->fields.server->watertype = CONTENTS_EMPTY;
1372 ent->fields.server->waterlevel = 0;
1380 Toss, bounce, and fly movement. When onground, do nothing.
1383 void SV_Physics_Toss (prvm_edict_t *ent)
1388 // if onground, return without moving
1389 if ((int)ent->fields.server->flags & FL_ONGROUND)
1391 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1393 // don't stick to ground if onground and moving upward
1394 ent->fields.server->flags -= FL_ONGROUND;
1396 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1398 // we can trust FL_ONGROUND if groundentity is world because it never moves
1401 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1403 // if ent was supported by a brush model on previous frame,
1404 // and groundentity is now freed, set groundentity to 0 (world)
1405 // which leaves it suspended in the air
1406 ent->fields.server->groundentity = 0;
1410 ent->priv.server->suspendedinairflag = false;
1412 SV_CheckVelocity (ent);
1415 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1416 SV_AddGravity (ent);
1419 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1422 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1423 trace = SV_PushEntity (ent, move, true);
1424 if (ent->priv.server->free)
1426 if (trace.bmodelstartsolid)
1428 // try to unstick the entity
1429 SV_UnstickEntity(ent);
1430 trace = SV_PushEntity (ent, move, false);
1431 if (ent->priv.server->free)
1435 if (trace.fraction < 1)
1437 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1439 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1440 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1442 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1445 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1446 // LordHavoc: fixed grenades not bouncing when fired down a slope
1447 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1449 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1450 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1452 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1453 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1454 VectorClear (ent->fields.server->velocity);
1455 VectorClear (ent->fields.server->avelocity);
1458 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1462 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1464 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1465 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1466 VectorClear (ent->fields.server->velocity);
1467 VectorClear (ent->fields.server->avelocity);
1470 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1475 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1476 if (trace.plane.normal[2] > 0.7)
1478 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1479 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1480 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1481 ent->priv.server->suspendedinairflag = true;
1482 VectorClear (ent->fields.server->velocity);
1483 VectorClear (ent->fields.server->avelocity);
1486 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1490 // check for in water
1491 SV_CheckWaterTransition (ent);
1495 ===============================================================================
1499 ===============================================================================
1506 Monsters freefall when they don't have a ground entity, otherwise
1507 all movement is done with discrete steps.
1509 This is also used for objects that have become still on the ground, but
1510 will fall if the floor is pulled out from under them.
1513 void SV_Physics_Step (prvm_edict_t *ent)
1515 int flags = (int)ent->fields.server->flags;
1516 // don't fall at all if fly/swim
1517 if (!(flags & (FL_FLY | FL_SWIM)))
1519 if (flags & FL_ONGROUND)
1521 // freefall if onground and moving upward
1522 // freefall if not standing on a world surface (it may be a lift or trap door)
1523 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1525 ent->fields.server->flags -= FL_ONGROUND;
1527 SV_CheckVelocity(ent);
1528 SV_FlyMove(ent, sv.frametime, NULL);
1529 SV_LinkEdict(ent, true);
1534 // freefall if not onground
1535 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1538 SV_CheckVelocity(ent);
1539 SV_FlyMove(ent, sv.frametime, NULL);
1540 SV_LinkEdict(ent, true);
1543 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1544 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1551 SV_CheckWaterTransition(ent);
1554 //============================================================================
1556 static void SV_Physics_Entity (prvm_edict_t *ent)
1558 // don't run a move on newly spawned projectiles as it messes up movement
1559 // interpolation and rocket trails
1560 qboolean runmove = ent->priv.server->move;
1561 ent->priv.server->move = true;
1562 switch ((int) ent->fields.server->movetype)
1565 case MOVETYPE_FAKEPUSH:
1566 SV_Physics_Pusher (ent);
1569 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1570 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1573 case MOVETYPE_FOLLOW:
1574 SV_Physics_Follow (ent);
1576 case MOVETYPE_NOCLIP:
1577 if (SV_RunThink(ent))
1580 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1581 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1583 SV_LinkEdict(ent, false);
1586 SV_Physics_Step (ent);
1589 if (SV_RunThink (ent))
1591 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1592 SV_AddGravity (ent);
1593 SV_CheckStuck (ent);
1595 SV_LinkEdict (ent, true);
1599 case MOVETYPE_BOUNCE:
1600 case MOVETYPE_BOUNCEMISSILE:
1601 case MOVETYPE_FLYMISSILE:
1604 if (SV_RunThink (ent) && runmove)
1605 SV_Physics_Toss (ent);
1608 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1613 void SV_ApplyClientMove (void);
1614 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1616 SV_ApplyClientMove();
1617 // make sure the velocity is sane (not a NaN)
1618 SV_CheckVelocity(ent);
1619 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1620 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1622 prog->globals.server->time = sv.time;
1623 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1624 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1628 // make sure the velocity is sane (not a NaN)
1629 SV_CheckVelocity(ent);
1630 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1631 // player_run/player_stand1 does not horribly malfunction if the
1632 // velocity becomes a number that is both == 0 and != 0
1633 // (sounds to me like NaN but to be absolutely safe...)
1634 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1635 VectorClear(ent->fields.server->velocity);
1636 // call standard client pre-think
1637 prog->globals.server->time = sv.time;
1638 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1639 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1640 SV_CheckVelocity (ent);
1642 switch ((int) ent->fields.server->movetype)
1645 case MOVETYPE_FAKEPUSH:
1646 SV_Physics_Pusher (ent);
1649 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1650 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1653 case MOVETYPE_FOLLOW:
1654 SV_Physics_Follow (ent);
1656 case MOVETYPE_NOCLIP:
1657 if (SV_RunThink(ent))
1660 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1661 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1665 SV_Physics_Step (ent);
1668 if (SV_RunThink (ent))
1670 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1671 SV_AddGravity (ent);
1672 SV_CheckStuck (ent);
1677 case MOVETYPE_BOUNCE:
1678 case MOVETYPE_BOUNCEMISSILE:
1679 case MOVETYPE_FLYMISSILE:
1681 if (SV_RunThink (ent))
1682 SV_Physics_Toss (ent);
1685 if (SV_RunThink (ent))
1687 SV_CheckWater (ent);
1692 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1696 SV_CheckVelocity (ent);
1698 // call standard player post-think
1699 SV_LinkEdict (ent, true);
1701 SV_CheckVelocity (ent);
1703 prog->globals.server->time = sv.time;
1704 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1705 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1714 void SV_Physics (void)
1719 // let the progs know that a new frame has started
1720 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1721 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1722 prog->globals.server->time = sv.time;
1723 prog->globals.server->frametime = sv.frametime;
1724 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1727 // treat each object in turn
1730 // if force_retouch, relink all the entities
1731 if (prog->globals.server->force_retouch > 0)
1732 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1733 if (!ent->priv.server->free)
1734 SV_LinkEdict (ent, true); // force retouch even for stationary
1736 // run physics on the client entities
1737 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1739 if (!ent->priv.server->free)
1741 // don't do physics on disconnected clients, FrikBot relies on this
1742 if (!host_client->spawned)
1743 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1744 // don't run physics here if running asynchronously
1745 else if (host_client->clmovement_skipphysicsframes > 0)
1746 host_client->clmovement_skipphysicsframes--;
1748 SV_Physics_ClientEntity(ent);
1752 // run physics on all the non-client entities
1753 if (!sv_freezenonclients.integer)
1754 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1755 if (!ent->priv.server->free)
1756 SV_Physics_Entity(ent);
1758 if (prog->globals.server->force_retouch > 0)
1759 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1761 // LordHavoc: endframe support
1764 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1765 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1766 prog->globals.server->time = sv.time;
1767 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1770 // decrement prog->num_edicts if the highest number entities died
1771 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1773 if (!sv_freezenonclients.integer)
1774 sv.time += sv.frametime;
1778 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1783 vec3_t original_origin;
1784 vec3_t original_velocity;
1785 vec3_t original_angles;
1786 vec3_t original_avelocity;
1790 VectorCopy(tossent->fields.server->origin , original_origin );
1791 VectorCopy(tossent->fields.server->velocity , original_velocity );
1792 VectorCopy(tossent->fields.server->angles , original_angles );
1793 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1795 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1796 if (val != NULL && val->_float != 0)
1797 gravity = val->_float;
1800 gravity *= sv_gravity.value * 0.05;
1802 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1804 SV_CheckVelocity (tossent);
1805 tossent->fields.server->velocity[2] -= gravity;
1806 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1807 VectorScale (tossent->fields.server->velocity, 0.05, move);
1808 VectorAdd (tossent->fields.server->origin, move, end);
1809 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1810 VectorCopy (trace.endpos, tossent->fields.server->origin);
1812 if (trace.fraction < 1)
1816 VectorCopy(original_origin , tossent->fields.server->origin );
1817 VectorCopy(original_velocity , tossent->fields.server->velocity );
1818 VectorCopy(original_angles , tossent->fields.server->angles );
1819 VectorCopy(original_avelocity, tossent->fields.server->avelocity);