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.
27 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.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 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"};
44 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
45 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
46 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
47 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
48 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
49 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
50 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
51 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
52 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
53 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
55 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)"};
56 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)"};
58 // TODO: move this extern to server.h
59 extern cvar_t sv_clmovement_waitforinput;
61 #define MOVE_EPSILON 0.01
63 void SV_Physics_Toss (prvm_edict_t *ent);
65 void SV_Phys_Init (void)
67 Cvar_RegisterVariable(&sv_stepheight);
68 Cvar_RegisterVariable(&sv_jumpstep);
69 Cvar_RegisterVariable(&sv_wallfriction);
70 Cvar_RegisterVariable(&sv_newflymove);
71 Cvar_RegisterVariable(&sv_freezenonclients);
73 Cvar_RegisterVariable(&sv_playerphysicsqc);
75 Cvar_RegisterVariable(&sv_sound_watersplash);
76 Cvar_RegisterVariable(&sv_sound_land);
83 returns true if the entity is in solid currently
86 static int SV_TestEntityPosition (prvm_edict_t *ent)
88 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
89 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
93 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
95 // q1bsp/hlbsp use hulls and if the entity does not exactly match
96 // a hull size it is incorrectly tested, so this code tries to
97 // 'fix' it slightly...
100 for (i = 0;i < 8;i++)
102 v[0] = ent->fields.server->origin[0] + ((i & 1) ? ent->fields.server->maxs[0] : ent->fields.server->mins[0]);
103 v[1] = ent->fields.server->origin[1] + ((i & 2) ? ent->fields.server->maxs[1] : ent->fields.server->mins[1]);
104 v[2] = ent->fields.server->origin[2] + ((i & 4) ? ent->fields.server->maxs[2] : ent->fields.server->mins[2]);
105 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
118 void SV_CheckAllEnts (void)
123 // see if any solid entities are inside the final position
124 check = PRVM_NEXT_EDICT(prog->edicts);
125 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
127 if (check->priv.server->free)
129 if (check->fields.server->movetype == MOVETYPE_PUSH
130 || check->fields.server->movetype == MOVETYPE_NONE
131 || check->fields.server->movetype == MOVETYPE_FOLLOW
132 || check->fields.server->movetype == MOVETYPE_NOCLIP)
135 if (SV_TestEntityPosition (check))
136 Con_Print("entity in invalid position\n");
140 // DRESK - Support for Entity Contents Transition Event
143 SV_CheckContentsTransition
145 returns true if entity had a valid contentstransition function call
148 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
150 int bValidFunctionCall;
151 prvm_eval_t *contentstransition;
153 // Default Valid Function Call to False
154 bValidFunctionCall = false;
156 if(ent->fields.server->watertype != nContents)
157 { // Changed Contents
158 // Acquire Contents Transition Function from QC
159 contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition);
161 if(contentstransition->function)
162 { // Valid Function; Execute
163 // Assign Valid Function
164 bValidFunctionCall = true;
165 // Prepare Parameters (Original Contents, New Contents)
167 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
169 PRVM_G_FLOAT(OFS_PARM1) = nContents;
170 // Execute VM Function
171 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
175 // Return if Function Call was Valid
176 return bValidFunctionCall;
185 void SV_CheckVelocity (prvm_edict_t *ent)
193 for (i=0 ; i<3 ; i++)
195 if (IS_NAN(ent->fields.server->velocity[i]))
197 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
198 ent->fields.server->velocity[i] = 0;
200 if (IS_NAN(ent->fields.server->origin[i]))
202 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
203 ent->fields.server->origin[i] = 0;
207 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
208 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
209 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
211 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
212 ent->fields.server->velocity[0] *= wishspeed;
213 ent->fields.server->velocity[1] *= wishspeed;
214 ent->fields.server->velocity[2] *= wishspeed;
222 Runs thinking code if time. There is some play in the exact time the think
223 function will be called, because it is called before any movement is done
224 in a frame. Not used for pushmove objects, because they must be exact.
225 Returns false if the entity removed itself.
228 qboolean SV_RunThink (prvm_edict_t *ent)
232 thinktime = ent->fields.server->nextthink;
233 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
236 // don't let things stay in the past.
237 // it is possible to start that way by a trigger with a local time.
238 if (thinktime < sv.time)
241 ent->fields.server->nextthink = 0;
242 prog->globals.server->time = thinktime;
243 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
244 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
245 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
246 return !ent->priv.server->free;
253 Two entities have touched, so run their touch functions
256 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
258 int old_self, old_other;
259 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
262 old_self = prog->globals.server->self;
263 old_other = prog->globals.server->other;
265 prog->globals.server->time = sv.time;
266 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
268 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
269 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
270 prog->globals.server->trace_allsolid = trace->allsolid;
271 prog->globals.server->trace_startsolid = trace->startsolid;
272 prog->globals.server->trace_fraction = trace->fraction;
273 prog->globals.server->trace_inwater = trace->inwater;
274 prog->globals.server->trace_inopen = trace->inopen;
275 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
276 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
277 prog->globals.server->trace_plane_dist = trace->plane.dist;
279 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
281 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
282 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
283 val->_float = trace->startsupercontents;
284 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
285 val->_float = trace->hitsupercontents;
286 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
287 val->_float = trace->hitq3surfaceflags;
288 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
290 if (trace->hittexture)
291 val->string = PRVM_SetTempString(trace->hittexture->name);
295 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
298 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
300 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
301 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
302 prog->globals.server->trace_allsolid = false;
303 prog->globals.server->trace_startsolid = false;
304 prog->globals.server->trace_fraction = 1;
305 prog->globals.server->trace_inwater = false;
306 prog->globals.server->trace_inopen = true;
307 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
308 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
309 prog->globals.server->trace_plane_dist = 0;
310 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
311 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
313 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
315 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
317 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
319 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
322 prog->globals.server->self = old_self;
323 prog->globals.server->other = old_other;
331 Slide off of the impacting object
332 returns the blocked flags (1 = floor, 2 = step / wall)
335 #define STOP_EPSILON 0.1
336 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
341 backoff = -DotProduct (in, normal) * overbounce;
342 VectorMA(in, backoff, normal, out);
344 for (i = 0;i < 3;i++)
345 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
354 The basic solid body movement clip that slides along multiple planes
355 Returns the clipflags if the velocity was modified (hit something solid)
359 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
362 // LordHavoc: increased from 5 to 32
363 #define MAX_CLIP_PLANES 32
364 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
366 int blocked, bumpcount;
367 int i, j, impact, numplanes;
369 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
372 VectorCopy(ent->fields.server->velocity, original_velocity);
373 VectorCopy(ent->fields.server->velocity, primal_velocity);
376 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
378 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
381 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
382 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
384 //if (trace.fraction < 0.002)
389 VectorCopy(ent->fields.server->origin, start);
390 start[2] += 3;//0.03125;
391 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
392 end[2] += 3;//0.03125;
393 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
394 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)))
396 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
402 for (i = 0;i < numplanes;i++)
404 VectorCopy(ent->fields.server->origin, start);
405 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
406 VectorMA(start, 3, planes[i], start);
407 VectorMA(end, 3, planes[i], end);
408 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
409 if (trace.fraction < testtrace.fraction)
412 VectorCopy(start, ent->fields.server->origin);
417 // VectorAdd(ent->fields.server->origin, planes[j], start);
423 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);
424 if (trace.fraction < 1)
425 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
430 if (trace.bmodelstartsolid)
432 // LordHavoc: note: this code is what makes entities stick in place
433 // if embedded in world only (you can walk through other objects if
435 // entity is trapped in another solid
436 VectorClear(ent->fields.server->velocity);
441 // break if it moved the entire distance
442 if (trace.fraction == 1)
444 VectorCopy(trace.endpos, ent->fields.server->origin);
450 Con_Printf ("SV_FlyMove: !trace.ent");
451 trace.ent = prog->edicts;
454 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
458 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
462 if (trace.plane.normal[2])
464 if (trace.plane.normal[2] > 0.7)
468 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
469 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
476 // save the trace for player extrafriction
478 VectorCopy(trace.plane.normal, stepnormal);
481 if (trace.fraction >= 0.001)
483 // actually covered some distance
484 VectorCopy(trace.endpos, ent->fields.server->origin);
485 VectorCopy(ent->fields.server->velocity, original_velocity);
489 // run the impact function
492 SV_Impact(ent, &trace);
494 // break if removed by the impact function
495 if (ent->priv.server->free)
499 time_left *= 1 - trace.fraction;
501 // clipped to another plane
502 if (numplanes >= MAX_CLIP_PLANES)
504 // this shouldn't really happen
505 VectorClear(ent->fields.server->velocity);
511 for (i = 0;i < numplanes;i++)
512 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
516 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
521 VectorCopy(trace.plane.normal, planes[numplanes]);
524 if (sv_newflymove.integer)
525 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
528 // modify original_velocity so it parallels all of the clip planes
529 for (i = 0;i < numplanes;i++)
531 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
532 for (j = 0;j < numplanes;j++)
537 if (DotProduct(new_velocity, planes[j]) < 0)
547 // go along this plane
548 VectorCopy(new_velocity, ent->fields.server->velocity);
552 // go along the crease
555 VectorClear(ent->fields.server->velocity);
559 CrossProduct(planes[0], planes[1], dir);
560 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
561 VectorNormalize(dir);
562 d = DotProduct(dir, ent->fields.server->velocity);
563 VectorScale(dir, d, ent->fields.server->velocity);
567 // if current velocity is against the original velocity,
568 // stop dead to avoid tiny occilations in sloping corners
569 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
571 VectorClear(ent->fields.server->velocity);
576 //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]);
579 if ((blocked & 1) == 0 && bumpcount > 1)
581 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
582 // flag ONGROUND if there's ground under it
583 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
587 // LordHavoc: this came from QW and allows you to get out of water more easily
588 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
589 VectorCopy(primal_velocity, ent->fields.server->velocity);
599 void SV_AddGravity (prvm_edict_t *ent)
604 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
605 if (val!=0 && val->_float)
606 ent_gravity = val->_float;
609 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
614 ===============================================================================
618 ===============================================================================
625 Does not change the entities velocity at all
628 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
634 VectorAdd (ent->fields.server->origin, push, end);
636 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
638 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
639 type = MOVE_NOMONSTERS; // only clip against bmodels
643 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
644 if (trace.bmodelstartsolid && failonbmodelstartsolid)
647 VectorCopy (trace.endpos, ent->fields.server->origin);
648 SV_LinkEdict (ent, true);
650 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)))
651 SV_Impact (ent, &trace);
662 void SV_PushMove (prvm_edict_t *pusher, float movetime)
665 float savesolid, movetime2, pushltime;
666 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
668 int numcheckentities;
669 static prvm_edict_t *checkentities[MAX_EDICTS];
670 model_t *pushermodel;
673 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])
675 pusher->fields.server->ltime += movetime;
679 switch ((int) pusher->fields.server->solid)
681 // LordHavoc: valid pusher types
685 case SOLID_CORPSE: // LordHavoc: this would be weird...
687 // LordHavoc: no collisions
690 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
691 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
692 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
693 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
694 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
695 pusher->fields.server->ltime += movetime;
696 SV_LinkEdict (pusher, false);
699 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
702 index = (int) pusher->fields.server->modelindex;
703 if (index < 1 || index >= MAX_MODELS)
705 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
708 pushermodel = sv.models[index];
710 movetime2 = movetime;
711 VectorScale(pusher->fields.server->velocity, movetime2, move1);
712 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
713 if (moveangle[0] || moveangle[2])
715 for (i = 0;i < 3;i++)
719 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
720 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
724 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
725 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
729 else if (moveangle[1])
731 for (i = 0;i < 3;i++)
735 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
736 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
740 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
741 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
747 for (i = 0;i < 3;i++)
751 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
752 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
756 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
757 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
762 VectorNegate (moveangle, a);
763 AngleVectorsFLU (a, forward, left, up);
765 VectorCopy (pusher->fields.server->origin, pushorig);
766 VectorCopy (pusher->fields.server->angles, pushang);
767 pushltime = pusher->fields.server->ltime;
769 // move the pusher to its final position
771 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
772 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
773 pusher->fields.server->ltime += movetime;
774 SV_LinkEdict (pusher, false);
776 savesolid = pusher->fields.server->solid;
778 // see if any solid entities are inside the final position
781 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
782 for (e = 0;e < numcheckentities;e++)
784 prvm_edict_t *check = checkentities[e];
785 if (check->fields.server->movetype == MOVETYPE_NONE
786 || check->fields.server->movetype == MOVETYPE_PUSH
787 || check->fields.server->movetype == MOVETYPE_FOLLOW
788 || check->fields.server->movetype == MOVETYPE_NOCLIP
789 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
792 // if the entity is standing on the pusher, it will definitely be moved
793 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
795 // remove the onground flag for non-players
796 if (check->fields.server->movetype != MOVETYPE_WALK)
797 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
801 // if the entity is not inside the pusher's final position, leave it alone
802 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)
807 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
810 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
811 org2[0] = DotProduct (org, forward);
812 org2[1] = DotProduct (org, left);
813 org2[2] = DotProduct (org, up);
814 VectorSubtract (org2, org, move);
815 VectorAdd (move, move1, move);
818 VectorCopy (move1, move);
820 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
821 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
822 sv.moved_edicts[num_moved++] = check;
824 // try moving the contacted entity
825 pusher->fields.server->solid = SOLID_NOT;
826 trace = SV_PushEntity (check, move, true);
827 // FIXME: turn players specially
828 check->fields.server->angles[1] += trace.fraction * moveangle[1];
829 pusher->fields.server->solid = savesolid; // was SOLID_BSP
830 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
832 // if it is still inside the pusher, block
833 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)
835 // try moving the contacted entity a tiny bit further to account for precision errors
837 pusher->fields.server->solid = SOLID_NOT;
838 VectorScale(move, 1.1, move2);
839 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
840 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
841 SV_PushEntity (check, move2, true);
842 pusher->fields.server->solid = savesolid;
843 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)
845 // try moving the contacted entity a tiny bit less to account for precision errors
846 pusher->fields.server->solid = SOLID_NOT;
847 VectorScale(move, 0.9, move2);
848 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
849 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
850 SV_PushEntity (check, move2, true);
851 pusher->fields.server->solid = savesolid;
852 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)
854 // still inside pusher, so it's really blocked
857 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
859 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
862 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
863 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
867 VectorCopy (pushorig, pusher->fields.server->origin);
868 VectorCopy (pushang, pusher->fields.server->angles);
869 pusher->fields.server->ltime = pushltime;
870 SV_LinkEdict (pusher, false);
872 // move back any entities we already moved
873 for (i = 0;i < num_moved;i++)
875 prvm_edict_t *ed = sv.moved_edicts[i];
876 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
877 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
878 SV_LinkEdict (ed, false);
881 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
882 if (pusher->fields.server->blocked)
884 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
885 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
886 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
893 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
894 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
895 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
904 void SV_Physics_Pusher (prvm_edict_t *ent)
906 float thinktime, oldltime, movetime;
908 oldltime = ent->fields.server->ltime;
910 thinktime = ent->fields.server->nextthink;
911 if (thinktime < ent->fields.server->ltime + sv.frametime)
913 movetime = thinktime - ent->fields.server->ltime;
918 movetime = sv.frametime;
921 // advances ent->fields.server->ltime if not blocked
922 SV_PushMove (ent, movetime);
924 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
926 ent->fields.server->nextthink = 0;
927 prog->globals.server->time = sv.time;
928 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
929 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
930 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
936 ===============================================================================
940 ===============================================================================
947 This is a big hack to try and fix the rare case of getting stuck in the world
951 void SV_CheckStuck (prvm_edict_t *ent)
956 if (!SV_TestEntityPosition(ent))
958 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
962 VectorCopy (ent->fields.server->origin, org);
963 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
964 if (!SV_TestEntityPosition(ent))
966 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
967 SV_LinkEdict (ent, true);
971 for (z=-1 ; z< 18 ; z++)
972 for (i=-1 ; i <= 1 ; i++)
973 for (j=-1 ; j <= 1 ; j++)
975 ent->fields.server->origin[0] = org[0] + i;
976 ent->fields.server->origin[1] = org[1] + j;
977 ent->fields.server->origin[2] = org[2] + z;
978 if (!SV_TestEntityPosition(ent))
980 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);
981 SV_LinkEdict (ent, true);
986 VectorCopy (org, ent->fields.server->origin);
987 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
990 static void SV_UnstickEntity (prvm_edict_t *ent)
995 // if not stuck in a bmodel, just return
996 if (!SV_TestEntityPosition(ent))
999 VectorCopy (ent->fields.server->origin, org);
1001 for (z=-1 ; z< 18 ; z += 6)
1002 for (i=-1 ; i <= 1 ; i++)
1003 for (j=-1 ; j <= 1 ; j++)
1005 ent->fields.server->origin[0] = org[0] + i;
1006 ent->fields.server->origin[1] = org[1] + j;
1007 ent->fields.server->origin[2] = org[2] + z;
1008 if (!SV_TestEntityPosition(ent))
1010 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);
1011 SV_LinkEdict (ent, true);
1016 VectorCopy (org, ent->fields.server->origin);
1017 if (developer.integer >= 100)
1018 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1027 qboolean SV_CheckWater (prvm_edict_t *ent)
1030 int nNativeContents;
1033 point[0] = ent->fields.server->origin[0];
1034 point[1] = ent->fields.server->origin[1];
1035 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1037 // DRESK - Support for Entity Contents Transition Event
1038 // NOTE: Some logic needed to be slightly re-ordered
1039 // to not affect performance and allow for the feature.
1041 // Acquire Super Contents Prior to Resets
1042 cont = SV_PointSuperContents(point);
1043 // Acquire Native Contents Here
1044 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1046 // DRESK - Support for Entity Contents Transition Event
1047 if(ent->fields.server->watertype)
1048 // Entity did NOT Spawn; Check
1049 SV_CheckContentsTransition(ent, nNativeContents);
1052 ent->fields.server->waterlevel = 0;
1053 ent->fields.server->watertype = CONTENTS_EMPTY;
1054 cont = SV_PointSuperContents(point);
1055 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1057 ent->fields.server->watertype = nNativeContents;
1058 ent->fields.server->waterlevel = 1;
1059 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1060 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1062 ent->fields.server->waterlevel = 2;
1063 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1064 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1065 ent->fields.server->waterlevel = 3;
1069 return ent->fields.server->waterlevel > 1;
1078 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1081 vec3_t forward, into, side;
1083 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1084 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1086 // cut the tangential velocity
1087 i = DotProduct (stepnormal, ent->fields.server->velocity);
1088 VectorScale (stepnormal, i, into);
1089 VectorSubtract (ent->fields.server->velocity, into, side);
1090 ent->fields.server->velocity[0] = side[0] * (1 + d);
1091 ent->fields.server->velocity[1] = side[1] * (1 + d);
1096 =====================
1099 Player has come to a dead stop, possibly due to the problem with limited
1100 float precision at some angle joins in the BSP hull.
1102 Try fixing by pushing one pixel in each direction.
1104 This is a hack, but in the interest of good gameplay...
1105 ======================
1107 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1112 VectorCopy (ent->fields.server->origin, oldorg);
1115 for (i=0 ; i<8 ; i++)
1117 // try pushing a little in an axial direction
1120 case 0: dir[0] = 2; dir[1] = 0; break;
1121 case 1: dir[0] = 0; dir[1] = 2; break;
1122 case 2: dir[0] = -2; dir[1] = 0; break;
1123 case 3: dir[0] = 0; dir[1] = -2; break;
1124 case 4: dir[0] = 2; dir[1] = 2; break;
1125 case 5: dir[0] = -2; dir[1] = 2; break;
1126 case 6: dir[0] = 2; dir[1] = -2; break;
1127 case 7: dir[0] = -2; dir[1] = -2; break;
1130 SV_PushEntity (ent, dir, false);
1132 // retry the original move
1133 ent->fields.server->velocity[0] = oldvel[0];
1134 ent->fields.server->velocity[1] = oldvel[1];
1135 ent->fields.server->velocity[2] = 0;
1136 clip = SV_FlyMove (ent, 0.1, NULL);
1138 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1139 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1141 Con_DPrint("TryUnstick - success.\n");
1145 // go back to the original pos and try again
1146 VectorCopy (oldorg, ent->fields.server->origin);
1150 VectorClear (ent->fields.server->velocity);
1151 Con_DPrint("TryUnstick - failure.\n");
1156 =====================
1159 Only used by players
1160 ======================
1162 void SV_WalkMove (prvm_edict_t *ent)
1164 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1165 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1168 SV_CheckVelocity(ent);
1170 // do a regular slide move unless it looks like you ran into a step
1171 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1172 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1174 VectorCopy (ent->fields.server->origin, start_origin);
1175 VectorCopy (ent->fields.server->velocity, start_velocity);
1177 clip = SV_FlyMove (ent, sv.frametime, NULL);
1179 SV_CheckVelocity(ent);
1181 VectorCopy(ent->fields.server->origin, originalmove_origin);
1182 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1183 originalmove_clip = clip;
1184 originalmove_flags = (int)ent->fields.server->flags;
1185 originalmove_groundentity = ent->fields.server->groundentity;
1187 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1190 if (sv_nostep.integer)
1193 // if move didn't block on a step, return
1196 // if move was not trying to move into the step, return
1197 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1200 if (ent->fields.server->movetype != MOVETYPE_FLY)
1202 // return if gibbed by a trigger
1203 if (ent->fields.server->movetype != MOVETYPE_WALK)
1206 // only step up while jumping if that is enabled
1207 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1208 if (!oldonground && ent->fields.server->waterlevel == 0)
1212 // try moving up and forward to go up a step
1213 // back to start pos
1214 VectorCopy (start_origin, ent->fields.server->origin);
1215 VectorCopy (start_velocity, ent->fields.server->velocity);
1218 VectorClear (upmove);
1219 upmove[2] = sv_stepheight.value;
1220 // FIXME: don't link?
1221 SV_PushEntity(ent, upmove, false);
1224 ent->fields.server->velocity[2] = 0;
1225 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1226 ent->fields.server->velocity[2] += start_velocity[2];
1228 SV_CheckVelocity(ent);
1230 // check for stuckness, possibly due to the limited precision of floats
1231 // in the clipping hulls
1233 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1234 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1236 //Con_Printf("wall\n");
1237 // stepping up didn't make any progress, revert to original move
1238 VectorCopy(originalmove_origin, ent->fields.server->origin);
1239 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1240 //clip = originalmove_clip;
1241 ent->fields.server->flags = originalmove_flags;
1242 ent->fields.server->groundentity = originalmove_groundentity;
1243 // now try to unstick if needed
1244 //clip = SV_TryUnstick (ent, oldvel);
1248 //Con_Printf("step - ");
1250 // extra friction based on view angle
1251 if (clip & 2 && sv_wallfriction.integer)
1252 SV_WallFriction (ent, stepnormal);
1254 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1255 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)))
1259 VectorClear (downmove);
1260 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1261 // FIXME: don't link?
1262 downtrace = SV_PushEntity (ent, downmove, false);
1264 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1266 // this has been disabled so that you can't jump when you are stepping
1267 // up while already jumping (also known as the Quake2 stair jump bug)
1269 // LordHavoc: disabled this check so you can walk on monsters/players
1270 //if (ent->fields.server->solid == SOLID_BSP)
1272 //Con_Printf("onground\n");
1273 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1274 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1280 //Con_Printf("slope\n");
1281 // if the push down didn't end up on good ground, use the move without
1282 // the step up. This happens near wall / slope combinations, and can
1283 // cause the player to hop up higher on a slope too steep to climb
1284 VectorCopy(originalmove_origin, ent->fields.server->origin);
1285 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1286 //clip = originalmove_clip;
1287 ent->fields.server->flags = originalmove_flags;
1288 ent->fields.server->groundentity = originalmove_groundentity;
1291 SV_CheckVelocity(ent);
1294 //============================================================================
1300 Entities that are "stuck" to another entity
1303 void SV_Physics_Follow (prvm_edict_t *ent)
1305 vec3_t vf, vr, vu, angles, v;
1309 if (!SV_RunThink (ent))
1312 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1313 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1314 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])
1316 // quick case for no rotation
1317 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1321 angles[0] = -ent->fields.server->punchangle[0];
1322 angles[1] = ent->fields.server->punchangle[1];
1323 angles[2] = ent->fields.server->punchangle[2];
1324 AngleVectors (angles, vf, vr, vu);
1325 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];
1326 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];
1327 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];
1328 angles[0] = -e->fields.server->angles[0];
1329 angles[1] = e->fields.server->angles[1];
1330 angles[2] = e->fields.server->angles[2];
1331 AngleVectors (angles, vf, vr, vu);
1332 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1333 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1334 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1336 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1337 SV_LinkEdict (ent, true);
1341 ==============================================================================
1345 ==============================================================================
1350 SV_CheckWaterTransition
1354 void SV_CheckWaterTransition (prvm_edict_t *ent)
1357 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1358 if (!ent->fields.server->watertype)
1360 // just spawned here
1361 ent->fields.server->watertype = cont;
1362 ent->fields.server->waterlevel = 1;
1366 // DRESK - Support for Entity Contents Transition Event
1367 // NOTE: Call here BEFORE updating the watertype below,
1368 // and suppress watersplash sound if a valid function
1369 // call was made to allow for custom "splash" sounds.
1370 if( !SV_CheckContentsTransition(ent, cont) )
1371 { // Contents Transition Function Invalid; Potentially Play Water Sound
1372 // check if the entity crossed into or out of water
1373 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1374 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1377 if (cont <= CONTENTS_WATER)
1379 ent->fields.server->watertype = cont;
1380 ent->fields.server->waterlevel = 1;
1384 ent->fields.server->watertype = CONTENTS_EMPTY;
1385 ent->fields.server->waterlevel = 0;
1393 Toss, bounce, and fly movement. When onground, do nothing.
1396 void SV_Physics_Toss (prvm_edict_t *ent)
1401 // if onground, return without moving
1402 if ((int)ent->fields.server->flags & FL_ONGROUND)
1404 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1406 // don't stick to ground if onground and moving upward
1407 ent->fields.server->flags -= FL_ONGROUND;
1409 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1411 // we can trust FL_ONGROUND if groundentity is world because it never moves
1414 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1416 // if ent was supported by a brush model on previous frame,
1417 // and groundentity is now freed, set groundentity to 0 (world)
1418 // which leaves it suspended in the air
1419 ent->fields.server->groundentity = 0;
1423 ent->priv.server->suspendedinairflag = false;
1425 SV_CheckVelocity (ent);
1428 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1429 SV_AddGravity (ent);
1432 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1435 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1436 trace = SV_PushEntity (ent, move, true);
1437 if (ent->priv.server->free)
1439 if (trace.bmodelstartsolid)
1441 // try to unstick the entity
1442 SV_UnstickEntity(ent);
1443 trace = SV_PushEntity (ent, move, false);
1444 if (ent->priv.server->free)
1448 if (trace.fraction < 1)
1450 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1452 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1453 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1455 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1458 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1459 // LordHavoc: fixed grenades not bouncing when fired down a slope
1460 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1462 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1463 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1465 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1466 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1467 VectorClear (ent->fields.server->velocity);
1468 VectorClear (ent->fields.server->avelocity);
1471 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1475 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1477 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1478 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1479 VectorClear (ent->fields.server->velocity);
1480 VectorClear (ent->fields.server->avelocity);
1483 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1488 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1489 if (trace.plane.normal[2] > 0.7)
1491 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1492 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1493 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1494 ent->priv.server->suspendedinairflag = true;
1495 VectorClear (ent->fields.server->velocity);
1496 VectorClear (ent->fields.server->avelocity);
1499 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1503 // check for in water
1504 SV_CheckWaterTransition (ent);
1508 ===============================================================================
1512 ===============================================================================
1519 Monsters freefall when they don't have a ground entity, otherwise
1520 all movement is done with discrete steps.
1522 This is also used for objects that have become still on the ground, but
1523 will fall if the floor is pulled out from under them.
1526 void SV_Physics_Step (prvm_edict_t *ent)
1528 int flags = (int)ent->fields.server->flags;
1529 // don't fall at all if fly/swim
1530 if (!(flags & (FL_FLY | FL_SWIM)))
1532 if (flags & FL_ONGROUND)
1534 // freefall if onground and moving upward
1535 // freefall if not standing on a world surface (it may be a lift or trap door)
1536 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1538 ent->fields.server->flags -= FL_ONGROUND;
1540 SV_CheckVelocity(ent);
1541 SV_FlyMove(ent, sv.frametime, NULL);
1542 SV_LinkEdict(ent, true);
1547 // freefall if not onground
1548 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1551 SV_CheckVelocity(ent);
1552 SV_FlyMove(ent, sv.frametime, NULL);
1553 SV_LinkEdict(ent, true);
1556 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1557 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1564 SV_CheckWaterTransition(ent);
1567 //============================================================================
1569 static void SV_Physics_Entity (prvm_edict_t *ent)
1571 // don't run a move on newly spawned projectiles as it messes up movement
1572 // interpolation and rocket trails
1573 qboolean runmove = ent->priv.server->move;
1574 ent->priv.server->move = true;
1575 switch ((int) ent->fields.server->movetype)
1578 case MOVETYPE_FAKEPUSH:
1579 SV_Physics_Pusher (ent);
1582 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1583 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1586 case MOVETYPE_FOLLOW:
1587 SV_Physics_Follow (ent);
1589 case MOVETYPE_NOCLIP:
1590 if (SV_RunThink(ent))
1593 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1594 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1596 SV_LinkEdict(ent, false);
1599 SV_Physics_Step (ent);
1602 if (SV_RunThink (ent))
1604 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1605 SV_AddGravity (ent);
1606 SV_CheckStuck (ent);
1608 SV_LinkEdict (ent, true);
1612 case MOVETYPE_BOUNCE:
1613 case MOVETYPE_BOUNCEMISSILE:
1614 case MOVETYPE_FLYMISSILE:
1617 if (SV_RunThink (ent) && runmove)
1618 SV_Physics_Toss (ent);
1621 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1626 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1628 SV_ApplyClientMove();
1629 // make sure the velocity is sane (not a NaN)
1630 SV_CheckVelocity(ent);
1631 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1632 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1634 prog->globals.server->time = sv.time;
1635 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1636 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1640 // make sure the velocity is sane (not a NaN)
1641 SV_CheckVelocity(ent);
1642 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1643 // player_run/player_stand1 does not horribly malfunction if the
1644 // velocity becomes a number that is both == 0 and != 0
1645 // (sounds to me like NaN but to be absolutely safe...)
1646 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1647 VectorClear(ent->fields.server->velocity);
1648 // call standard client pre-think
1649 prog->globals.server->time = sv.time;
1650 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1651 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1652 SV_CheckVelocity (ent);
1654 switch ((int) ent->fields.server->movetype)
1657 case MOVETYPE_FAKEPUSH:
1658 SV_Physics_Pusher (ent);
1661 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1662 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1665 case MOVETYPE_FOLLOW:
1666 SV_Physics_Follow (ent);
1668 case MOVETYPE_NOCLIP:
1669 if (SV_RunThink(ent))
1672 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1673 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1677 SV_Physics_Step (ent);
1680 if (SV_RunThink (ent))
1682 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1683 SV_AddGravity (ent);
1684 SV_CheckStuck (ent);
1689 case MOVETYPE_BOUNCE:
1690 case MOVETYPE_BOUNCEMISSILE:
1691 case MOVETYPE_FLYMISSILE:
1693 if (SV_RunThink (ent))
1694 SV_Physics_Toss (ent);
1697 if (SV_RunThink (ent))
1699 SV_CheckWater (ent);
1704 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1708 SV_CheckVelocity (ent);
1710 // call standard player post-think
1711 SV_LinkEdict (ent, true);
1713 SV_CheckVelocity (ent);
1715 prog->globals.server->time = sv.time;
1716 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1717 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1726 void SV_Physics (void)
1731 // let the progs know that a new frame has started
1732 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1733 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1734 prog->globals.server->time = sv.time;
1735 prog->globals.server->frametime = sv.frametime;
1736 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1739 // treat each object in turn
1742 // if force_retouch, relink all the entities
1743 if (prog->globals.server->force_retouch > 0)
1744 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1745 if (!ent->priv.server->free)
1746 SV_LinkEdict (ent, true); // force retouch even for stationary
1748 // run physics on the client entities
1749 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1751 if (!ent->priv.server->free)
1753 // don't do physics on disconnected clients, FrikBot relies on this
1754 if (!host_client->spawned)
1755 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1756 // don't run physics here if running asynchronously
1757 else if (host_client->clmovement_skipphysicsframes > 0)
1758 host_client->clmovement_skipphysicsframes--;
1760 SV_Physics_ClientEntity(ent);
1764 // run physics on all the non-client entities
1765 if (!sv_freezenonclients.integer)
1766 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1767 if (!ent->priv.server->free)
1768 SV_Physics_Entity(ent);
1770 if (prog->globals.server->force_retouch > 0)
1771 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1773 // LordHavoc: endframe support
1776 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1777 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1778 prog->globals.server->time = sv.time;
1779 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1782 // decrement prog->num_edicts if the highest number entities died
1783 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1785 if (!sv_freezenonclients.integer)
1786 sv.time += sv.frametime;
1790 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1795 vec3_t original_origin;
1796 vec3_t original_velocity;
1797 vec3_t original_angles;
1798 vec3_t original_avelocity;
1802 VectorCopy(tossent->fields.server->origin , original_origin );
1803 VectorCopy(tossent->fields.server->velocity , original_velocity );
1804 VectorCopy(tossent->fields.server->angles , original_angles );
1805 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1807 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1808 if (val != NULL && val->_float != 0)
1809 gravity = val->_float;
1812 gravity *= sv_gravity.value * 0.05;
1814 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1816 SV_CheckVelocity (tossent);
1817 tossent->fields.server->velocity[2] -= gravity;
1818 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1819 VectorScale (tossent->fields.server->velocity, 0.05, move);
1820 VectorAdd (tossent->fields.server->origin, move, end);
1821 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1822 VectorCopy (trace.endpos, tossent->fields.server->origin);
1824 if (trace.fraction < 1)
1828 VectorCopy(original_origin , tossent->fields.server->origin );
1829 VectorCopy(original_velocity , tossent->fields.server->velocity );
1830 VectorCopy(original_angles , tossent->fields.server->angles );
1831 VectorCopy(original_avelocity, tossent->fields.server->avelocity);