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)
95 if (sv.worldmodel->brushq1.hulls && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
97 // q1bsp/hlbsp use hulls and if the entity does not exactly match
98 // a hull size it is incorrectly tested, so this code tries to
99 // 'fix' it slightly...
102 for (i = 0;i < 8;i++)
104 v[0] = (i & 1) ? ent->fields.server->maxs[0] : ent->fields.server->mins[0];
105 v[1] = (i & 2) ? ent->fields.server->maxs[1] : ent->fields.server->mins[1];
106 v[2] = (i & 4) ? ent->fields.server->maxs[2] : ent->fields.server->mins[2];
107 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
120 void SV_CheckAllEnts (void)
125 // see if any solid entities are inside the final position
126 check = PRVM_NEXT_EDICT(prog->edicts);
127 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
129 if (check->priv.server->free)
131 if (check->fields.server->movetype == MOVETYPE_PUSH
132 || check->fields.server->movetype == MOVETYPE_NONE
133 || check->fields.server->movetype == MOVETYPE_FOLLOW
134 || check->fields.server->movetype == MOVETYPE_NOCLIP)
137 if (SV_TestEntityPosition (check))
138 Con_Print("entity in invalid position\n");
142 // DRESK - Support for Entity Contents Transition Event
145 SV_CheckContentsTransition
147 returns true if entity had a valid contentstransition function call
150 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
152 int bValidFunctionCall;
153 prvm_eval_t *contentstransition;
155 // Default Valid Function Call to False
156 bValidFunctionCall = false;
158 if(ent->fields.server->watertype != nContents)
159 { // Changed Contents
160 // Acquire Contents Transition Function from QC
161 contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition);
163 if(contentstransition->function)
164 { // Valid Function; Execute
165 // Assign Valid Function
166 bValidFunctionCall = true;
167 // Prepare Parameters (Original Contents, New Contents)
169 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
171 PRVM_G_FLOAT(OFS_PARM1) = nContents;
172 // Execute VM Function
173 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
177 // Return if Function Call was Valid
178 return bValidFunctionCall;
187 void SV_CheckVelocity (prvm_edict_t *ent)
195 for (i=0 ; i<3 ; i++)
197 if (IS_NAN(ent->fields.server->velocity[i]))
199 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
200 ent->fields.server->velocity[i] = 0;
202 if (IS_NAN(ent->fields.server->origin[i]))
204 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
205 ent->fields.server->origin[i] = 0;
209 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
210 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
211 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
213 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
214 ent->fields.server->velocity[0] *= wishspeed;
215 ent->fields.server->velocity[1] *= wishspeed;
216 ent->fields.server->velocity[2] *= wishspeed;
224 Runs thinking code if time. There is some play in the exact time the think
225 function will be called, because it is called before any movement is done
226 in a frame. Not used for pushmove objects, because they must be exact.
227 Returns false if the entity removed itself.
230 qboolean SV_RunThink (prvm_edict_t *ent)
234 thinktime = ent->fields.server->nextthink;
235 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
238 // don't let things stay in the past.
239 // it is possible to start that way by a trigger with a local time.
240 if (thinktime < sv.time)
243 ent->fields.server->nextthink = 0;
244 prog->globals.server->time = thinktime;
245 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
246 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
247 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
248 return !ent->priv.server->free;
255 Two entities have touched, so run their touch functions
258 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
260 int old_self, old_other;
261 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
264 old_self = prog->globals.server->self;
265 old_other = prog->globals.server->other;
267 prog->globals.server->time = sv.time;
268 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
270 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
271 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
272 prog->globals.server->trace_allsolid = trace->allsolid;
273 prog->globals.server->trace_startsolid = trace->startsolid;
274 prog->globals.server->trace_fraction = trace->fraction;
275 prog->globals.server->trace_inwater = trace->inwater;
276 prog->globals.server->trace_inopen = trace->inopen;
277 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
278 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
279 prog->globals.server->trace_plane_dist = trace->plane.dist;
281 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
283 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
284 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
285 val->_float = trace->startsupercontents;
286 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
287 val->_float = trace->hitsupercontents;
288 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
289 val->_float = trace->hitq3surfaceflags;
290 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
292 if (trace->hittexture)
294 char *s = VM_GetTempString();
295 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
296 val->string = PRVM_SetEngineString(s);
301 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
304 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
306 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
307 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
308 prog->globals.server->trace_allsolid = false;
309 prog->globals.server->trace_startsolid = false;
310 prog->globals.server->trace_fraction = 1;
311 prog->globals.server->trace_inwater = false;
312 prog->globals.server->trace_inopen = true;
313 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
314 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
315 prog->globals.server->trace_plane_dist = 0;
316 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
317 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
319 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
321 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
323 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
325 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
328 prog->globals.server->self = old_self;
329 prog->globals.server->other = old_other;
337 Slide off of the impacting object
338 returns the blocked flags (1 = floor, 2 = step / wall)
341 #define STOP_EPSILON 0.1
342 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
347 backoff = -DotProduct (in, normal) * overbounce;
348 VectorMA(in, backoff, normal, out);
350 for (i = 0;i < 3;i++)
351 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
360 The basic solid body movement clip that slides along multiple planes
361 Returns the clipflags if the velocity was modified (hit something solid)
365 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
368 // LordHavoc: increased from 5 to 32
369 #define MAX_CLIP_PLANES 32
370 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
372 int blocked, bumpcount;
373 int i, j, impact, numplanes;
375 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
378 VectorCopy(ent->fields.server->velocity, original_velocity);
379 VectorCopy(ent->fields.server->velocity, primal_velocity);
382 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
384 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
387 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
388 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
390 //if (trace.fraction < 0.002)
395 VectorCopy(ent->fields.server->origin, start);
396 start[2] += 3;//0.03125;
397 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
398 end[2] += 3;//0.03125;
399 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
400 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)))
402 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
408 for (i = 0;i < numplanes;i++)
410 VectorCopy(ent->fields.server->origin, start);
411 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
412 VectorMA(start, 3, planes[i], start);
413 VectorMA(end, 3, planes[i], end);
414 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
415 if (trace.fraction < testtrace.fraction)
418 VectorCopy(start, ent->fields.server->origin);
423 // VectorAdd(ent->fields.server->origin, planes[j], start);
429 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);
430 if (trace.fraction < 1)
431 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
436 if (trace.bmodelstartsolid)
438 // LordHavoc: note: this code is what makes entities stick in place
439 // if embedded in world only (you can walk through other objects if
441 // entity is trapped in another solid
442 VectorClear(ent->fields.server->velocity);
447 // break if it moved the entire distance
448 if (trace.fraction == 1)
450 VectorCopy(trace.endpos, ent->fields.server->origin);
456 Con_Printf ("SV_FlyMove: !trace.ent");
457 trace.ent = prog->edicts;
460 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
464 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
468 if (trace.plane.normal[2])
470 if (trace.plane.normal[2] > 0.7)
474 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
475 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
482 // save the trace for player extrafriction
484 VectorCopy(trace.plane.normal, stepnormal);
487 if (trace.fraction >= 0.001)
489 // actually covered some distance
490 VectorCopy(trace.endpos, ent->fields.server->origin);
491 VectorCopy(ent->fields.server->velocity, original_velocity);
495 // run the impact function
498 SV_Impact(ent, &trace);
500 // break if removed by the impact function
501 if (ent->priv.server->free)
505 time_left *= 1 - trace.fraction;
507 // clipped to another plane
508 if (numplanes >= MAX_CLIP_PLANES)
510 // this shouldn't really happen
511 VectorClear(ent->fields.server->velocity);
517 for (i = 0;i < numplanes;i++)
518 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
522 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
527 VectorCopy(trace.plane.normal, planes[numplanes]);
530 if (sv_newflymove.integer)
531 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
534 // modify original_velocity so it parallels all of the clip planes
535 for (i = 0;i < numplanes;i++)
537 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
538 for (j = 0;j < numplanes;j++)
543 if (DotProduct(new_velocity, planes[j]) < 0)
553 // go along this plane
554 VectorCopy(new_velocity, ent->fields.server->velocity);
558 // go along the crease
561 VectorClear(ent->fields.server->velocity);
565 CrossProduct(planes[0], planes[1], dir);
566 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
567 VectorNormalize(dir);
568 d = DotProduct(dir, ent->fields.server->velocity);
569 VectorScale(dir, d, ent->fields.server->velocity);
573 // if current velocity is against the original velocity,
574 // stop dead to avoid tiny occilations in sloping corners
575 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
577 VectorClear(ent->fields.server->velocity);
582 //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]);
585 if ((blocked & 1) == 0 && bumpcount > 1)
587 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
588 // flag ONGROUND if there's ground under it
589 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
593 // LordHavoc: this came from QW and allows you to get out of water more easily
594 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
595 VectorCopy(primal_velocity, ent->fields.server->velocity);
605 void SV_AddGravity (prvm_edict_t *ent)
610 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
611 if (val!=0 && val->_float)
612 ent_gravity = val->_float;
615 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
620 ===============================================================================
624 ===============================================================================
631 Does not change the entities velocity at all
634 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
640 VectorAdd (ent->fields.server->origin, push, end);
642 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
644 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
645 type = MOVE_NOMONSTERS; // only clip against bmodels
649 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
650 if (trace.bmodelstartsolid && failonbmodelstartsolid)
653 VectorCopy (trace.endpos, ent->fields.server->origin);
654 SV_LinkEdict (ent, true);
656 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)))
657 SV_Impact (ent, &trace);
668 void SV_PushMove (prvm_edict_t *pusher, float movetime)
671 float savesolid, movetime2, pushltime;
672 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
674 int numcheckentities;
675 static prvm_edict_t *checkentities[MAX_EDICTS];
676 model_t *pushermodel;
679 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])
681 pusher->fields.server->ltime += movetime;
685 switch ((int) pusher->fields.server->solid)
687 // LordHavoc: valid pusher types
691 case SOLID_CORPSE: // LordHavoc: this would be weird...
693 // LordHavoc: no collisions
696 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
697 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
698 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
699 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
700 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
701 pusher->fields.server->ltime += movetime;
702 SV_LinkEdict (pusher, false);
705 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
708 index = (int) pusher->fields.server->modelindex;
709 if (index < 1 || index >= MAX_MODELS)
711 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
714 pushermodel = sv.models[index];
716 movetime2 = movetime;
717 VectorScale(pusher->fields.server->velocity, movetime2, move1);
718 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
719 if (moveangle[0] || moveangle[2])
721 for (i = 0;i < 3;i++)
725 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
726 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
730 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
731 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
735 else if (moveangle[1])
737 for (i = 0;i < 3;i++)
741 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
742 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
746 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
747 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
753 for (i = 0;i < 3;i++)
757 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
758 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
762 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
763 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
768 VectorNegate (moveangle, a);
769 AngleVectorsFLU (a, forward, left, up);
771 VectorCopy (pusher->fields.server->origin, pushorig);
772 VectorCopy (pusher->fields.server->angles, pushang);
773 pushltime = pusher->fields.server->ltime;
775 // move the pusher to its final position
777 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
778 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
779 pusher->fields.server->ltime += movetime;
780 SV_LinkEdict (pusher, false);
782 savesolid = pusher->fields.server->solid;
784 // see if any solid entities are inside the final position
787 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
788 for (e = 0;e < numcheckentities;e++)
790 prvm_edict_t *check = checkentities[e];
791 if (check->fields.server->movetype == MOVETYPE_NONE
792 || check->fields.server->movetype == MOVETYPE_PUSH
793 || check->fields.server->movetype == MOVETYPE_FOLLOW
794 || check->fields.server->movetype == MOVETYPE_NOCLIP
795 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
798 // if the entity is standing on the pusher, it will definitely be moved
799 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
801 // remove the onground flag for non-players
802 if (check->fields.server->movetype != MOVETYPE_WALK)
803 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
807 // if the entity is not inside the pusher's final position, leave it alone
808 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)
813 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
816 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
817 org2[0] = DotProduct (org, forward);
818 org2[1] = DotProduct (org, left);
819 org2[2] = DotProduct (org, up);
820 VectorSubtract (org2, org, move);
821 VectorAdd (move, move1, move);
824 VectorCopy (move1, move);
826 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
827 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
828 sv.moved_edicts[num_moved++] = check;
830 // try moving the contacted entity
831 pusher->fields.server->solid = SOLID_NOT;
832 trace = SV_PushEntity (check, move, true);
833 // FIXME: turn players specially
834 check->fields.server->angles[1] += trace.fraction * moveangle[1];
835 pusher->fields.server->solid = savesolid; // was SOLID_BSP
836 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
838 // if it is still inside the pusher, block
839 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)
841 // try moving the contacted entity a tiny bit further to account for precision errors
843 pusher->fields.server->solid = SOLID_NOT;
844 VectorScale(move, 1.1, move2);
845 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
846 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
847 SV_PushEntity (check, move2, true);
848 pusher->fields.server->solid = savesolid;
849 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)
851 // try moving the contacted entity a tiny bit less to account for precision errors
852 pusher->fields.server->solid = SOLID_NOT;
853 VectorScale(move, 0.9, move2);
854 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
855 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
856 SV_PushEntity (check, move2, true);
857 pusher->fields.server->solid = savesolid;
858 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)
860 // still inside pusher, so it's really blocked
863 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
865 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
868 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
869 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
873 VectorCopy (pushorig, pusher->fields.server->origin);
874 VectorCopy (pushang, pusher->fields.server->angles);
875 pusher->fields.server->ltime = pushltime;
876 SV_LinkEdict (pusher, false);
878 // move back any entities we already moved
879 for (i = 0;i < num_moved;i++)
881 prvm_edict_t *ed = sv.moved_edicts[i];
882 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
883 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
884 SV_LinkEdict (ed, false);
887 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
888 if (pusher->fields.server->blocked)
890 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
891 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
892 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
899 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
900 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
901 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
910 void SV_Physics_Pusher (prvm_edict_t *ent)
912 float thinktime, oldltime, movetime;
914 oldltime = ent->fields.server->ltime;
916 thinktime = ent->fields.server->nextthink;
917 if (thinktime < ent->fields.server->ltime + sv.frametime)
919 movetime = thinktime - ent->fields.server->ltime;
924 movetime = sv.frametime;
927 // advances ent->fields.server->ltime if not blocked
928 SV_PushMove (ent, movetime);
930 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
932 ent->fields.server->nextthink = 0;
933 prog->globals.server->time = sv.time;
934 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
935 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
936 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
942 ===============================================================================
946 ===============================================================================
953 This is a big hack to try and fix the rare case of getting stuck in the world
957 void SV_CheckStuck (prvm_edict_t *ent)
962 if (!SV_TestEntityPosition(ent))
964 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
968 VectorCopy (ent->fields.server->origin, org);
969 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
970 if (!SV_TestEntityPosition(ent))
972 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
973 SV_LinkEdict (ent, true);
977 for (z=-1 ; z< 18 ; z++)
978 for (i=-1 ; i <= 1 ; i++)
979 for (j=-1 ; j <= 1 ; j++)
981 ent->fields.server->origin[0] = org[0] + i;
982 ent->fields.server->origin[1] = org[1] + j;
983 ent->fields.server->origin[2] = org[2] + z;
984 if (!SV_TestEntityPosition(ent))
986 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);
987 SV_LinkEdict (ent, true);
992 VectorCopy (org, ent->fields.server->origin);
993 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
996 static void SV_UnstickEntity (prvm_edict_t *ent)
1001 // if not stuck in a bmodel, just return
1002 if (!SV_TestEntityPosition(ent))
1005 VectorCopy (ent->fields.server->origin, org);
1007 for (z=-1 ; z< 18 ; z += 6)
1008 for (i=-1 ; i <= 1 ; i++)
1009 for (j=-1 ; j <= 1 ; j++)
1011 ent->fields.server->origin[0] = org[0] + i;
1012 ent->fields.server->origin[1] = org[1] + j;
1013 ent->fields.server->origin[2] = org[2] + z;
1014 if (!SV_TestEntityPosition(ent))
1016 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);
1017 SV_LinkEdict (ent, true);
1022 VectorCopy (org, ent->fields.server->origin);
1023 if (developer.integer >= 100)
1024 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1033 qboolean SV_CheckWater (prvm_edict_t *ent)
1036 int nNativeContents;
1039 point[0] = ent->fields.server->origin[0];
1040 point[1] = ent->fields.server->origin[1];
1041 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1043 // DRESK - Support for Entity Contents Transition Event
1044 // NOTE: Some logic needed to be slightly re-ordered
1045 // to not affect performance and allow for the feature.
1047 // Acquire Super Contents Prior to Resets
1048 cont = SV_PointSuperContents(point);
1049 // Acquire Native Contents Here
1050 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1052 // DRESK - Support for Entity Contents Transition Event
1053 if(ent->fields.server->watertype)
1054 // Entity did NOT Spawn; Check
1055 SV_CheckContentsTransition(ent, nNativeContents);
1058 ent->fields.server->waterlevel = 0;
1059 ent->fields.server->watertype = CONTENTS_EMPTY;
1060 cont = SV_PointSuperContents(point);
1061 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1063 ent->fields.server->watertype = nNativeContents;
1064 ent->fields.server->waterlevel = 1;
1065 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1066 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1068 ent->fields.server->waterlevel = 2;
1069 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1070 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1071 ent->fields.server->waterlevel = 3;
1075 return ent->fields.server->waterlevel > 1;
1084 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1087 vec3_t forward, into, side;
1089 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1090 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1092 // cut the tangential velocity
1093 i = DotProduct (stepnormal, ent->fields.server->velocity);
1094 VectorScale (stepnormal, i, into);
1095 VectorSubtract (ent->fields.server->velocity, into, side);
1096 ent->fields.server->velocity[0] = side[0] * (1 + d);
1097 ent->fields.server->velocity[1] = side[1] * (1 + d);
1102 =====================
1105 Player has come to a dead stop, possibly due to the problem with limited
1106 float precision at some angle joins in the BSP hull.
1108 Try fixing by pushing one pixel in each direction.
1110 This is a hack, but in the interest of good gameplay...
1111 ======================
1113 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1118 VectorCopy (ent->fields.server->origin, oldorg);
1121 for (i=0 ; i<8 ; i++)
1123 // try pushing a little in an axial direction
1126 case 0: dir[0] = 2; dir[1] = 0; break;
1127 case 1: dir[0] = 0; dir[1] = 2; break;
1128 case 2: dir[0] = -2; dir[1] = 0; break;
1129 case 3: dir[0] = 0; dir[1] = -2; break;
1130 case 4: dir[0] = 2; dir[1] = 2; break;
1131 case 5: dir[0] = -2; dir[1] = 2; break;
1132 case 6: dir[0] = 2; dir[1] = -2; break;
1133 case 7: dir[0] = -2; dir[1] = -2; break;
1136 SV_PushEntity (ent, dir, false);
1138 // retry the original move
1139 ent->fields.server->velocity[0] = oldvel[0];
1140 ent->fields.server->velocity[1] = oldvel[1];
1141 ent->fields.server->velocity[2] = 0;
1142 clip = SV_FlyMove (ent, 0.1, NULL);
1144 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1145 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1147 Con_DPrint("TryUnstick - success.\n");
1151 // go back to the original pos and try again
1152 VectorCopy (oldorg, ent->fields.server->origin);
1156 VectorClear (ent->fields.server->velocity);
1157 Con_DPrint("TryUnstick - failure.\n");
1162 =====================
1165 Only used by players
1166 ======================
1168 void SV_WalkMove (prvm_edict_t *ent)
1170 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1171 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1174 SV_CheckVelocity(ent);
1176 // do a regular slide move unless it looks like you ran into a step
1177 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1178 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1180 VectorCopy (ent->fields.server->origin, start_origin);
1181 VectorCopy (ent->fields.server->velocity, start_velocity);
1183 clip = SV_FlyMove (ent, sv.frametime, NULL);
1185 SV_CheckVelocity(ent);
1187 VectorCopy(ent->fields.server->origin, originalmove_origin);
1188 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1189 originalmove_clip = clip;
1190 originalmove_flags = (int)ent->fields.server->flags;
1191 originalmove_groundentity = ent->fields.server->groundentity;
1193 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1196 if (sv_nostep.integer)
1199 // if move didn't block on a step, return
1202 // if move was not trying to move into the step, return
1203 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1206 if (ent->fields.server->movetype != MOVETYPE_FLY)
1208 // return if gibbed by a trigger
1209 if (ent->fields.server->movetype != MOVETYPE_WALK)
1212 // only step up while jumping if that is enabled
1213 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1214 if (!oldonground && ent->fields.server->waterlevel == 0)
1218 // try moving up and forward to go up a step
1219 // back to start pos
1220 VectorCopy (start_origin, ent->fields.server->origin);
1221 VectorCopy (start_velocity, ent->fields.server->velocity);
1224 VectorClear (upmove);
1225 upmove[2] = sv_stepheight.value;
1226 // FIXME: don't link?
1227 SV_PushEntity(ent, upmove, false);
1230 ent->fields.server->velocity[2] = 0;
1231 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1232 ent->fields.server->velocity[2] += start_velocity[2];
1234 SV_CheckVelocity(ent);
1236 // check for stuckness, possibly due to the limited precision of floats
1237 // in the clipping hulls
1239 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1240 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1242 //Con_Printf("wall\n");
1243 // stepping up didn't make any progress, revert to original move
1244 VectorCopy(originalmove_origin, ent->fields.server->origin);
1245 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1246 //clip = originalmove_clip;
1247 ent->fields.server->flags = originalmove_flags;
1248 ent->fields.server->groundentity = originalmove_groundentity;
1249 // now try to unstick if needed
1250 //clip = SV_TryUnstick (ent, oldvel);
1254 //Con_Printf("step - ");
1256 // extra friction based on view angle
1257 if (clip & 2 && sv_wallfriction.integer)
1258 SV_WallFriction (ent, stepnormal);
1260 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1261 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)))
1265 VectorClear (downmove);
1266 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1267 // FIXME: don't link?
1268 downtrace = SV_PushEntity (ent, downmove, false);
1270 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1272 // this has been disabled so that you can't jump when you are stepping
1273 // up while already jumping (also known as the Quake2 stair jump bug)
1275 // LordHavoc: disabled this check so you can walk on monsters/players
1276 //if (ent->fields.server->solid == SOLID_BSP)
1278 //Con_Printf("onground\n");
1279 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1280 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1286 //Con_Printf("slope\n");
1287 // if the push down didn't end up on good ground, use the move without
1288 // the step up. This happens near wall / slope combinations, and can
1289 // cause the player to hop up higher on a slope too steep to climb
1290 VectorCopy(originalmove_origin, ent->fields.server->origin);
1291 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1292 //clip = originalmove_clip;
1293 ent->fields.server->flags = originalmove_flags;
1294 ent->fields.server->groundentity = originalmove_groundentity;
1297 SV_CheckVelocity(ent);
1300 //============================================================================
1306 Entities that are "stuck" to another entity
1309 void SV_Physics_Follow (prvm_edict_t *ent)
1311 vec3_t vf, vr, vu, angles, v;
1315 if (!SV_RunThink (ent))
1318 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1319 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1320 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])
1322 // quick case for no rotation
1323 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1327 angles[0] = -ent->fields.server->punchangle[0];
1328 angles[1] = ent->fields.server->punchangle[1];
1329 angles[2] = ent->fields.server->punchangle[2];
1330 AngleVectors (angles, vf, vr, vu);
1331 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];
1332 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];
1333 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];
1334 angles[0] = -e->fields.server->angles[0];
1335 angles[1] = e->fields.server->angles[1];
1336 angles[2] = e->fields.server->angles[2];
1337 AngleVectors (angles, vf, vr, vu);
1338 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1339 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1340 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1342 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1343 SV_LinkEdict (ent, true);
1347 ==============================================================================
1351 ==============================================================================
1356 SV_CheckWaterTransition
1360 void SV_CheckWaterTransition (prvm_edict_t *ent)
1363 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1364 if (!ent->fields.server->watertype)
1366 // just spawned here
1367 ent->fields.server->watertype = cont;
1368 ent->fields.server->waterlevel = 1;
1372 // DRESK - Support for Entity Contents Transition Event
1373 // NOTE: Call here BEFORE updating the watertype below,
1374 // and suppress watersplash sound if a valid function
1375 // call was made to allow for custom "splash" sounds.
1376 if( !SV_CheckContentsTransition(ent, cont) )
1377 { // Contents Transition Function Invalid; Potentially Play Water Sound
1378 // check if the entity crossed into or out of water
1379 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1380 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1383 if (cont <= CONTENTS_WATER)
1385 ent->fields.server->watertype = cont;
1386 ent->fields.server->waterlevel = 1;
1390 ent->fields.server->watertype = CONTENTS_EMPTY;
1391 ent->fields.server->waterlevel = 0;
1399 Toss, bounce, and fly movement. When onground, do nothing.
1402 void SV_Physics_Toss (prvm_edict_t *ent)
1407 // if onground, return without moving
1408 if ((int)ent->fields.server->flags & FL_ONGROUND)
1410 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1412 // don't stick to ground if onground and moving upward
1413 ent->fields.server->flags -= FL_ONGROUND;
1415 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1417 // we can trust FL_ONGROUND if groundentity is world because it never moves
1420 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1422 // if ent was supported by a brush model on previous frame,
1423 // and groundentity is now freed, set groundentity to 0 (world)
1424 // which leaves it suspended in the air
1425 ent->fields.server->groundentity = 0;
1429 ent->priv.server->suspendedinairflag = false;
1431 SV_CheckVelocity (ent);
1434 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1435 SV_AddGravity (ent);
1438 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1441 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1442 trace = SV_PushEntity (ent, move, true);
1443 if (ent->priv.server->free)
1445 if (trace.bmodelstartsolid)
1447 // try to unstick the entity
1448 SV_UnstickEntity(ent);
1449 trace = SV_PushEntity (ent, move, false);
1450 if (ent->priv.server->free)
1454 if (trace.fraction < 1)
1456 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1458 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1459 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1461 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1464 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1465 // LordHavoc: fixed grenades not bouncing when fired down a slope
1466 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1468 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1469 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1471 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1472 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1473 VectorClear (ent->fields.server->velocity);
1474 VectorClear (ent->fields.server->avelocity);
1477 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1481 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1483 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1484 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1485 VectorClear (ent->fields.server->velocity);
1486 VectorClear (ent->fields.server->avelocity);
1489 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1494 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1495 if (trace.plane.normal[2] > 0.7)
1497 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1498 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1499 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1500 ent->priv.server->suspendedinairflag = true;
1501 VectorClear (ent->fields.server->velocity);
1502 VectorClear (ent->fields.server->avelocity);
1505 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1509 // check for in water
1510 SV_CheckWaterTransition (ent);
1514 ===============================================================================
1518 ===============================================================================
1525 Monsters freefall when they don't have a ground entity, otherwise
1526 all movement is done with discrete steps.
1528 This is also used for objects that have become still on the ground, but
1529 will fall if the floor is pulled out from under them.
1532 void SV_Physics_Step (prvm_edict_t *ent)
1534 int flags = (int)ent->fields.server->flags;
1535 // don't fall at all if fly/swim
1536 if (!(flags & (FL_FLY | FL_SWIM)))
1538 if (flags & FL_ONGROUND)
1540 // freefall if onground and moving upward
1541 // freefall if not standing on a world surface (it may be a lift or trap door)
1542 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1544 ent->fields.server->flags -= FL_ONGROUND;
1546 SV_CheckVelocity(ent);
1547 SV_FlyMove(ent, sv.frametime, NULL);
1548 SV_LinkEdict(ent, true);
1553 // freefall if not onground
1554 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1557 SV_CheckVelocity(ent);
1558 SV_FlyMove(ent, sv.frametime, NULL);
1559 SV_LinkEdict(ent, true);
1562 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1563 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1570 SV_CheckWaterTransition(ent);
1573 //============================================================================
1575 static void SV_Physics_Entity (prvm_edict_t *ent)
1577 // don't run a move on newly spawned projectiles as it messes up movement
1578 // interpolation and rocket trails
1579 qboolean runmove = ent->priv.server->move;
1580 ent->priv.server->move = true;
1581 switch ((int) ent->fields.server->movetype)
1584 case MOVETYPE_FAKEPUSH:
1585 SV_Physics_Pusher (ent);
1588 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1589 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1592 case MOVETYPE_FOLLOW:
1593 SV_Physics_Follow (ent);
1595 case MOVETYPE_NOCLIP:
1596 if (SV_RunThink(ent))
1599 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1600 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1602 SV_LinkEdict(ent, false);
1605 SV_Physics_Step (ent);
1608 if (SV_RunThink (ent))
1610 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1611 SV_AddGravity (ent);
1612 SV_CheckStuck (ent);
1614 SV_LinkEdict (ent, true);
1618 case MOVETYPE_BOUNCE:
1619 case MOVETYPE_BOUNCEMISSILE:
1620 case MOVETYPE_FLYMISSILE:
1623 if (SV_RunThink (ent) && runmove)
1624 SV_Physics_Toss (ent);
1627 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1632 void SV_ApplyClientMove (void);
1633 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1635 SV_ApplyClientMove();
1636 // make sure the velocity is sane (not a NaN)
1637 SV_CheckVelocity(ent);
1638 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1639 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1641 prog->globals.server->time = sv.time;
1642 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1643 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1647 // make sure the velocity is sane (not a NaN)
1648 SV_CheckVelocity(ent);
1649 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1650 // player_run/player_stand1 does not horribly malfunction if the
1651 // velocity becomes a number that is both == 0 and != 0
1652 // (sounds to me like NaN but to be absolutely safe...)
1653 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1654 VectorClear(ent->fields.server->velocity);
1655 // call standard client pre-think
1656 prog->globals.server->time = sv.time;
1657 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1658 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1659 SV_CheckVelocity (ent);
1661 switch ((int) ent->fields.server->movetype)
1664 case MOVETYPE_FAKEPUSH:
1665 SV_Physics_Pusher (ent);
1668 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1669 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1672 case MOVETYPE_FOLLOW:
1673 SV_Physics_Follow (ent);
1675 case MOVETYPE_NOCLIP:
1676 if (SV_RunThink(ent))
1679 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1680 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1684 SV_Physics_Step (ent);
1687 if (SV_RunThink (ent))
1689 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1690 SV_AddGravity (ent);
1691 SV_CheckStuck (ent);
1696 case MOVETYPE_BOUNCE:
1697 case MOVETYPE_BOUNCEMISSILE:
1698 case MOVETYPE_FLYMISSILE:
1700 if (SV_RunThink (ent))
1701 SV_Physics_Toss (ent);
1704 if (SV_RunThink (ent))
1706 SV_CheckWater (ent);
1711 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1715 SV_CheckVelocity (ent);
1717 // call standard player post-think
1718 SV_LinkEdict (ent, true);
1720 SV_CheckVelocity (ent);
1722 prog->globals.server->time = sv.time;
1723 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1724 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1733 void SV_Physics (void)
1738 // let the progs know that a new frame has started
1739 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1740 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1741 prog->globals.server->time = sv.time;
1742 prog->globals.server->frametime = sv.frametime;
1743 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1746 // treat each object in turn
1749 // if force_retouch, relink all the entities
1750 if (prog->globals.server->force_retouch > 0)
1751 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1752 if (!ent->priv.server->free)
1753 SV_LinkEdict (ent, true); // force retouch even for stationary
1755 // run physics on the client entities
1756 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1758 if (!ent->priv.server->free)
1760 // don't do physics on disconnected clients, FrikBot relies on this
1761 if (!host_client->spawned)
1762 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1763 // don't run physics here if running asynchronously
1764 else if (host_client->clmovement_skipphysicsframes > 0)
1765 host_client->clmovement_skipphysicsframes--;
1767 SV_Physics_ClientEntity(ent);
1771 // run physics on all the non-client entities
1772 if (!sv_freezenonclients.integer)
1773 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1774 if (!ent->priv.server->free)
1775 SV_Physics_Entity(ent);
1777 if (prog->globals.server->force_retouch > 0)
1778 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1780 // LordHavoc: endframe support
1783 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1784 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1785 prog->globals.server->time = sv.time;
1786 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1789 // decrement prog->num_edicts if the highest number entities died
1790 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1792 if (!sv_freezenonclients.integer)
1793 sv.time += sv.frametime;
1797 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1802 vec3_t original_origin;
1803 vec3_t original_velocity;
1804 vec3_t original_angles;
1805 vec3_t original_avelocity;
1809 VectorCopy(tossent->fields.server->origin , original_origin );
1810 VectorCopy(tossent->fields.server->velocity , original_velocity );
1811 VectorCopy(tossent->fields.server->angles , original_angles );
1812 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1814 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1815 if (val != NULL && val->_float != 0)
1816 gravity = val->_float;
1819 gravity *= sv_gravity.value * 0.05;
1821 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1823 SV_CheckVelocity (tossent);
1824 tossent->fields.server->velocity[2] -= gravity;
1825 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1826 VectorScale (tossent->fields.server->velocity, 0.05, move);
1827 VectorAdd (tossent->fields.server->origin, move, end);
1828 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1829 VectorCopy (trace.endpos, tossent->fields.server->origin);
1831 if (trace.fraction < 1)
1835 VectorCopy(original_origin , tossent->fields.server->origin );
1836 VectorCopy(original_velocity , tossent->fields.server->velocity );
1837 VectorCopy(original_angles , tossent->fields.server->angles );
1838 VectorCopy(original_avelocity, tossent->fields.server->avelocity);