+++ /dev/null
-#include "../server/movelib.qc"
+++ /dev/null
-#include "../server/movelib.qh"
#include "main.qc"
#include "mapvoting.qc"
#include "miscfunctions.qc"
-#include "movelib.qc"
#include "player_skeleton.qc"
#include "scoreboard.qc"
#include "shownames.qc"
#endif
#ifndef MENUQC
-#include "physics.qc"
-#include "movetypes/include.qc"
+#include "physics/all.inc"
#include "triggers/include.qc"
#include "viewloc.qc"
#endif
+++ /dev/null
-void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow
-{
- entity e = this.move_aiment; // TODO: networking?
-
- // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
- if(this.move_angles == this.move_punchangle)
- {
- this.move_origin = e.move_origin + this.view_ofs;
- }
- else
- {
- vector ang, v;
- ang_x = -this.move_punchangle_x;
- ang_y = this.move_punchangle_y;
- ang_z = this.move_punchangle_z;
- makevectors(ang);
- v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x;
- v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y;
- v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z;
- ang_x = -e.move_angles_x;
- ang_y = e.move_angles_y;
- ang_z = e.move_angles_z;
- makevectors(ang);
- this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
- this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
- this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
- }
-
- this.move_angles = e.move_angles + this.v_angle;
- _Movetype_LinkEdict(this, false);
-}
+++ /dev/null
-#include "push.qc"
-#include "toss.qc"
-#include "walk.qc"
-#include "step.qc"
-#include "follow.qc"
-
-#include "movetypes.qc"
+++ /dev/null
-#ifndef MOVETYPES_INCLUDE_H
-#define MOVETYPES_INCLUDE_H
-
-#include "push.qh"
-#include "toss.qh"
-#include "walk.qh"
-
-#endif
+++ /dev/null
-#include "include.qh"
-#include "../physics.qh"
-
-#if defined(CSQC)
- #include "../../client/defs.qh"
- #include "../stats.qh"
- #include "../util.qh"
- #include "movetypes.qh"
- #include "../../lib/csqcmodel/common.qh"
- #include "../../server/t_items.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../../server/autocvars.qh"
-#endif
-
-void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction
-{
- /*float d, i;
- vector into, side;
- makevectors(this.v_angle);
- d = (stepnormal * v_forward) + 0.5;
-
- if(d < 0)
- {
- i = (stepnormal * this.move_velocity);
- into = i * stepnormal;
- side = this.move_velocity - into;
- this.move_velocity_x = side.x * (1 * d);
- this.move_velocity_y = side.y * (1 * d);
- }*/
-}
-
-vector planes[MAX_CLIP_PLANES];
-int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove
-{
- int blocked = 0, bumpcount;
- int i, j, numplanes = 0;
- float time_left = dt, grav = 0;
- vector push;
- vector primal_velocity, original_velocity, restore_velocity;
-
- for(i = 0; i < MAX_CLIP_PLANES; ++i)
- planes[i] = '0 0 0';
-
- if(applygravity)
- {
- this.move_didgravity = 1;
- grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this);
-
- if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
- {
- if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.move_velocity_z -= grav * 0.5;
- else
- this.move_velocity_z -= grav;
- }
- }
-
- original_velocity = primal_velocity = restore_velocity = this.move_velocity;
-
- for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
- {
- if(this.move_velocity == '0 0 0')
- break;
-
- push = this.move_velocity * time_left;
- _Movetype_PushEntity(this, push, true);
- if(trace_startsolid)
- {
- // we got teleported by a touch function
- // let's abort the move
- blocked |= 8;
- break;
- }
-
- // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
- // abort move if we're stuck in the world (and didn't make it out)
- if(trace_startsolid && trace_allsolid)
- {
- this.move_velocity = restore_velocity;
- return 3;
- }
-
- if(trace_fraction == 1)
- break;
-
- float my_trace_fraction = trace_fraction;
- vector my_trace_plane_normal = trace_plane_normal;
-
- if(trace_plane_normal.z)
- {
- if(trace_plane_normal.z > 0.7)
- {
- // floor
- blocked |= 1;
-
- if(!trace_ent)
- {
- //dprint("_Movetype_FlyMove: !trace_ent\n");
- trace_ent = world;
- }
-
- this.move_flags |= FL_ONGROUND;
- this.move_groundentity = trace_ent;
- }
- }
- else if(stepheight)
- {
- // step - handle it immediately
- vector org = this.move_origin;
- vector steppush = '0 0 1' * stepheight;
-
- _Movetype_PushEntity(this, steppush, true);
- if(trace_startsolid)
- {
- blocked |= 8;
- break;
- }
- _Movetype_PushEntity(this, push, true);
- if(trace_startsolid)
- {
- blocked |= 8;
- break;
- }
- float trace2_fraction = trace_fraction;
- steppush = '0 0 1' * (org_z - this.move_origin_z);
- _Movetype_PushEntity(this, steppush, true);
- if(trace_startsolid)
- {
- blocked |= 8;
- break;
- }
-
- // accept the new position if it made some progress...
- if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
- {
- trace_endpos = this.move_origin;
- time_left *= 1 - trace2_fraction;
- numplanes = 0;
- continue;
- }
- else
- this.move_origin = org;
- }
- else
- {
- // step - return it to caller
- blocked |= 2;
- // save the trace for player extrafriction
- if(stepnormal)
- stepnormal = trace_plane_normal;
- }
-
- if(my_trace_fraction >= 0.001)
- {
- // actually covered some distance
- original_velocity = this.move_velocity;
- numplanes = 0;
- }
-
- time_left *= 1 - my_trace_fraction;
-
- // clipped to another plane
- if(numplanes >= MAX_CLIP_PLANES)
- {
- // this shouldn't really happen
- this.move_velocity = '0 0 0';
- blocked = 3;
- break;
- }
-
- planes[numplanes] = my_trace_plane_normal;
- numplanes++;
-
- // modify original_velocity so it parallels all of the clip planes
- vector new_velocity = '0 0 0';
- for (i = 0;i < numplanes;i++)
- {
- new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
- for (j = 0;j < numplanes;j++)
- {
- if(j != i)
- {
- // not ok
- if((new_velocity * planes[j]) < 0)
- break;
- }
- }
- if(j == numplanes)
- break;
- }
-
- if(i != numplanes)
- {
- // go along this plane
- this.move_velocity = new_velocity;
- }
- else
- {
- // go along the crease
- if(numplanes != 2)
- {
- this.move_velocity = '0 0 0';
- blocked = 7;
- break;
- }
- vector dir = cross(planes[0], planes[1]);
- // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
- float ilength = sqrt((dir * dir));
- if(ilength)
- ilength = 1.0 / ilength;
- dir.x *= ilength;
- dir.y *= ilength;
- dir.z *= ilength;
- float d = (dir * this.move_velocity);
- this.move_velocity = dir * d;
- }
-
- // if current velocity is against the original velocity,
- // stop dead to avoid tiny occilations in sloping corners
- if((this.move_velocity * primal_velocity) <= 0)
- {
- this.move_velocity = '0 0 0';
- break;
- }
- }
-
- // LordHavoc: this came from QW and allows you to get out of water more easily
- if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
- this.move_velocity = primal_velocity;
-
- if(applygravity)
- {
- if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
- {
- if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.move_velocity_z -= grav * 0.5f;
- }
- }
-
- return blocked;
-}
-
-void _Movetype_CheckVelocity(entity this) // SV_CheckVelocity
-{
- // if(vlen(this.move_velocity) < 0.0001)
- // this.move_velocity = '0 0 0';
-}
-
-bool _Movetype_CheckWater(entity this) // SV_CheckWater
-{
- vector point = this.move_origin;
- point.z += this.mins.z + 1;
-
- int nativecontents = pointcontents(point);
- if(this.move_watertype && this.move_watertype != nativecontents)
- {
- // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents);
- if(this.contentstransition)
- this.contentstransition(this.move_watertype, nativecontents);
- }
-
- this.move_waterlevel = WATERLEVEL_NONE;
- this.move_watertype = CONTENT_EMPTY;
-
- int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
- if(supercontents & DPCONTENTS_LIQUIDSMASK)
- {
- this.move_watertype = nativecontents;
- this.move_waterlevel = WATERLEVEL_WETFEET;
- point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5;
- if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
- {
- this.move_waterlevel = WATERLEVEL_SWIMMING;
- point.z = this.move_origin.z + this.view_ofs.z;
- if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
- this.move_waterlevel = WATERLEVEL_SUBMERGED;
- }
- }
-
- return this.move_waterlevel > 1;
-}
-
-void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
-{
- int contents = pointcontents(ent.move_origin);
-
- if(!ent.move_watertype)
- {
- // just spawned here
- if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
- {
- ent.move_watertype = contents;
- ent.move_waterlevel = 1;
- return;
- }
- }
- else if(ent.move_watertype != contents)
- {
- // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents);
- if(ent.contentstransition)
- ent.contentstransition(ent.move_watertype, contents);
- }
-
- if(contents <= CONTENT_WATER)
- {
- ent.move_watertype = contents;
- ent.move_waterlevel = 1;
- }
- else
- {
- ent.move_watertype = CONTENT_EMPTY;
- ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
- }
-}
-
-void _Movetype_Impact(entity this, entity oth) // SV_Impact
-{
- entity oldother = other;
-
- if(this.move_touch)
- {
- other = oth;
-
- WITH(entity, self, this, this.move_touch());
-
- other = oldother;
- }
-
- if(oth.move_touch)
- {
- other = this;
-
- WITH(entity, self, oth, oth.move_touch());
-
- other = oldother;
- }
-}
-
-void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGrid
-{
- entity oldother = other;
-
- for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain)
- {
- if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY)
- if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax))
- {
- other = this;
-
- trace_allsolid = false;
- trace_startsolid = false;
- trace_fraction = 1;
- trace_inwater = false;
- trace_inopen = true;
- trace_endpos = e.move_origin;
- trace_plane_normal = '0 0 1';
- trace_plane_dist = 0;
- trace_ent = this;
-
- WITH(entity, self, e, e.move_touch());
- }
- }
-
- other = oldother;
-}
-
-void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict
-{
- vector mi, ma;
- if(this.solid == SOLID_BSP)
- {
- // TODO set the absolute bbox
- mi = this.mins;
- ma = this.maxs;
- }
- else
- {
- mi = this.mins;
- ma = this.maxs;
- }
- mi += this.move_origin;
- ma += this.move_origin;
-
- if(this.move_flags & FL_ITEM)
- {
- mi.x -= 15;
- mi.y -= 15;
- mi.z -= 1;
- ma.x += 15;
- ma.y += 15;
- ma.z += 1;
- }
- else
- {
- mi.x -= 1;
- mi.y -= 1;
- mi.z -= 1;
- ma.x += 1;
- ma.y += 1;
- ma.z += 1;
- }
-
- this.absmin = mi;
- this.absmax = ma;
-
- if(touch_triggers)
- _Movetype_LinkEdict_TouchAreaGrid(this);
-}
-
-bool _Movetype_TestEntityPosition(entity this, vector ofs) // SV_TestEntityPosition
-{
-// vector org = this.move_origin + ofs;
-
- int cont = this.dphitcontentsmask;
- this.dphitcontentsmask = DPCONTENTS_SOLID;
- tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
- this.dphitcontentsmask = cont;
-
- if(trace_startsolid)
- return true;
-
- if(vlen(trace_endpos - this.move_origin) > 0.0001)
- this.move_origin = trace_endpos;
- return false;
-}
-
-bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity
-{
- if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true;
- if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success;
- if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success;
- for (int i = 1; i <= 17; ++i)
- {
- if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success;
- if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success;
- }
- LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
- etof(this), this.classname, vtos(this.move_origin));
- return false;
- : success;
- LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
- etof(this), this.classname, vtos(this.move_origin));
- _Movetype_LinkEdict(this, true);
- return true;
-}
-
-vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
-{
- vel -= ((vel * norm) * norm) * f;
-
- if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
- if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
- if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
-
- return vel;
-}
-
-void _Movetype_PushEntityTrace(entity this, vector push)
-{
- vector end = this.move_origin + push;
- int type;
- if(this.move_nomonsters)
- type = max(0, this.move_nomonsters);
- else if(this.move_movetype == MOVETYPE_FLYMISSILE)
- type = MOVE_MISSILE;
- else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
- type = MOVE_WORLDONLY;
- else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
- type = MOVE_NOMONSTERS;
- else
- type = MOVE_NORMAL;
-
- tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
-}
-
-float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity
-{
- _Movetype_PushEntityTrace(this, push);
-
- if(trace_startsolid && failonstartsolid)
- return trace_fraction;
-
- this.move_origin = trace_endpos;
-
- if(trace_fraction < 1)
- if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
- _Movetype_Impact(this, trace_ent);
-
- return trace_fraction;
-}
-
-
-.float ltime;
-.void() blocked;
-// matrix version of makevectors, sets v_forward, v_right and v_up
-void makevectors_matrix(vector myangles) // AngleVectorsFLU
-{
- v_forward = v_right = v_up = '0 0 0';
-
- float y = myangles.y * (M_PI * 2 / 360);
- float sy = sin(y);
- float cy = cos(y);
- float p = myangles.x * (M_PI * 2 / 360);
- float sp = sin(p);
- float cp = cos(p);
- if(v_forward)
- {
- v_forward.x = cp * cy;
- v_forward.y = cp * sy;
- v_forward.z = -sp;
- }
- if(v_right || v_up)
- {
- if(myangles.z)
- {
- float r = myangles.z * (M_PI * 2 / 360);
- float sr = sin(r);
- float cr = cos(r);
- if(v_right)
- {
- v_right.x = sr * sp * cy + cr * -sy;
- v_right.y = sr * sp * sy + cr * cy;
- v_right.z = sr * cp;
- }
- if(v_up)
- {
- v_up.x = cr * sp * cy + -sr * -sy;
- v_up.y = cr * sp * sy + -sr * cy;
- v_up.z = cr * cp;
- }
- }
- else
- {
- if(v_right)
- {
- v_right.x = -sy;
- v_right.y = cy;
- v_right.z = 0;
- }
- if(v_up)
- {
- v_up.x = sp * cy;
- v_up.y = sp * sy;
- v_up.z = cp;
- }
- }
- }
-}
-
-void _Movetype_Physics_Frame(entity this, float movedt)
-{
- this.move_didgravity = -1;
- switch (this.move_movetype)
- {
- case MOVETYPE_PUSH:
- case MOVETYPE_FAKEPUSH:
- _Movetype_Physics_Pusher(this, movedt);
- break;
- case MOVETYPE_NONE:
- break;
- case MOVETYPE_FOLLOW:
- _Movetype_Physics_Follow(this);
- break;
- case MOVETYPE_NOCLIP:
- _Movetype_CheckWater(this);
- this.move_origin = this.move_origin + TICRATE * this.move_velocity;
- this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
- _Movetype_LinkEdict(this, false);
- break;
- case MOVETYPE_STEP:
- _Movetype_Physics_Step(this, movedt);
- break;
- case MOVETYPE_WALK:
- _Movetype_Physics_Walk(this, movedt);
- break;
- case MOVETYPE_TOSS:
- case MOVETYPE_BOUNCE:
- case MOVETYPE_BOUNCEMISSILE:
- case MOVETYPE_FLYMISSILE:
- case MOVETYPE_FLY:
- case MOVETYPE_FLY_WORLDONLY:
- _Movetype_Physics_Toss(this, movedt);
- _Movetype_LinkEdict(this, true);
- break;
- case MOVETYPE_PHYSICS:
- break;
- }
-}
-
-void _Movetype_Physics_ClientFrame(entity this, float movedt)
-{
- this.move_didgravity = -1;
- switch (this.move_movetype)
- {
- case MOVETYPE_PUSH:
- case MOVETYPE_FAKEPUSH:
- _Movetype_Physics_Pusher(this, movedt);
- break;
- case MOVETYPE_NONE:
- break;
- case MOVETYPE_FOLLOW:
- _Movetype_Physics_Follow(this);
- break;
- case MOVETYPE_NOCLIP:
- _Movetype_CheckWater(this);
- this.move_origin = this.move_origin + TICRATE * this.move_velocity;
- this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
- _Movetype_LinkEdict(this, false);
- break;
- case MOVETYPE_STEP:
- _Movetype_Physics_Step(this, movedt);
- break;
- case MOVETYPE_WALK:
- case MOVETYPE_FLY:
- case MOVETYPE_FLY_WORLDONLY:
- _Movetype_Physics_Walk(this, movedt);
- break;
- case MOVETYPE_TOSS:
- case MOVETYPE_BOUNCE:
- case MOVETYPE_BOUNCEMISSILE:
- case MOVETYPE_FLYMISSILE:
- _Movetype_Physics_Toss(this, movedt);
- break;
- case MOVETYPE_PHYSICS:
- break;
- }
-}
-
-void Movetype_Physics_NoMatchServer(entity this) // optimized
-{
- float movedt = time - this.move_time;
- this.move_time = time;
-
- _Movetype_Physics_Frame(this, movedt);
- if(wasfreed(this))
- return;
-
- this.avelocity = this.move_avelocity;
- this.velocity = this.move_velocity;
- this.angles = this.move_angles;
- setorigin(this, this.move_origin);
-}
-
-void Movetype_Physics_MatchServer(entity this, bool sloppy)
-{
- Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
-}
-
-void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Physics_Entity
-{
- if(tr <= 0)
- {
- Movetype_Physics_NoMatchServer(this);
- return;
- }
-
- float dt = time - this.move_time;
-
- int n = max(0, floor(dt / tr));
- dt -= n * tr;
- this.move_time += n * tr;
-
- if(!this.move_didgravity)
- this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
-
- for (int i = 0; i < n; ++i)
- {
- _Movetype_Physics_Frame(this, tr);
- if(wasfreed(this))
- return;
- }
-
- this.avelocity = this.move_avelocity;
-
- if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
- {
- // now continue the move from move_time to time
- this.velocity = this.move_velocity;
-
- if(this.move_didgravity > 0)
- {
- this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
- * dt
- * (this.gravity ? this.gravity : 1)
- * PHYS_GRAVITY(this);
- }
-
- this.angles = this.move_angles + dt * this.avelocity;
-
- if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
- {
- setorigin(this, this.move_origin + dt * this.velocity);
- }
- else
- {
- _Movetype_PushEntityTrace(this, dt * this.velocity);
- if(!trace_startsolid)
- setorigin(this, trace_endpos);
- }
-
- if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
- }
- else
- {
- this.velocity = this.move_velocity;
- this.angles = this.move_angles;
- setorigin(this, this.move_origin);
- }
-}
+++ /dev/null
-#ifndef MOVETYPES_H
-#define MOVETYPES_H
-
-.float move_ltime;
-.void()move_think;
-.float move_nextthink;
-.void()move_blocked;
-
-.float move_movetype;
-.float move_time;
-.vector move_origin;
-.vector move_angles;
-.vector move_velocity;
-.vector move_avelocity;
-.int move_flags;
-.int move_watertype;
-.int move_waterlevel;
-.void()move_touch;
-.void(float, float)contentstransition;
-.float move_bounce_factor;
-.float move_bounce_stopspeed;
-.float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant
-
-.entity move_aiment;
-.vector move_punchangle;
-
-// should match sv_gameplayfix_fixedcheckwatertransition
-float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1;
-
-.entity move_groundentity; // FIXME add move_groundnetworkentity?
-.float move_suspendedinair;
-.float move_didgravity;
-
-void _Movetype_WallFriction(entity this, vector stepnormal);
-int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight);
-void _Movetype_CheckVelocity(entity this);
-void _Movetype_CheckWaterTransition(entity ent);
-float _Movetype_CheckWater(entity ent);
-void _Movetype_LinkEdict_TouchAreaGrid(entity this);
-void _Movetype_LinkEdict(entity this, float touch_triggers);
-float _Movetype_TestEntityPosition(entity this, vector ofs);
-float _Movetype_UnstickEntity(entity this);
-vector _Movetype_ClipVelocity(vector vel, vector norm, float f);
-void _Movetype_PushEntityTrace(entity this, vector push);
-float _Movetype_PushEntity(entity this, vector push, float failonstartsolid);
-void makevectors_matrix(vector myangles);
-
-void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy);
-void Movetype_Physics_MatchServer(entity this, bool sloppy);
-void Movetype_Physics_NoMatchServer(entity this);
-void _Movetype_LinkEdict(entity this, float touch_triggers);
-void _Movetype_LinkEdict_TouchAreaGrid(entity this);
-
-float _Movetype_UnstickEntity(entity this);
-
-const int MAX_CLIP_PLANES = 5;
-
-#ifdef CSQC
-const int MOVETYPE_NONE = 0;
-const int MOVETYPE_ANGLENOCLIP = 1;
-const int MOVETYPE_ANGLECLIP = 2;
-const int MOVETYPE_WALK = 3;
-const int MOVETYPE_STEP = 4;
-const int MOVETYPE_FLY = 5;
-const int MOVETYPE_TOSS = 6;
-const int MOVETYPE_PUSH = 7;
-const int MOVETYPE_NOCLIP = 8;
-const int MOVETYPE_FLYMISSILE = 9;
-const int MOVETYPE_BOUNCE = 10;
-const int MOVETYPE_BOUNCEMISSILE = 11; // Like bounce but doesn't lose speed on bouncing
-const int MOVETYPE_FOLLOW = 12;
-const int MOVETYPE_PHYSICS = 32;
-const int MOVETYPE_FLY_WORLDONLY = 33;
-
-const int FL_ITEM = 256;
-const int FL_ONGROUND = 512;
-#endif
-
-const int MOVETYPE_FAKEPUSH = 13;
-
-const int MOVEFLAG_VALID = BIT(23);
-const int MOVEFLAG_Q2AIRACCELERATE = BIT(0);
-const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1);
-const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
-
-#ifdef CSQC
-#define moveflags STAT(MOVEFLAGS)
-#endif
-
-#endif
+++ /dev/null
-void _Movetype_PushMove(entity this, float dt) // SV_PushMove
-{
- if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0')
- {
- this.move_ltime += dt;
- return;
- }
-
- switch (this.solid)
- {
- // LordHavoc: valid pusher types
- case SOLID_BSP:
- case SOLID_BBOX:
- case SOLID_SLIDEBOX:
- case SOLID_CORPSE: // LordHavoc: this would be weird...
- break;
- // LordHavoc: no collisions
- case SOLID_NOT:
- case SOLID_TRIGGER:
- this.move_origin = this.move_origin + dt * this.move_velocity;
- this.move_angles = this.move_angles + dt * this.move_avelocity;
- this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
- this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
- this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
- this.move_ltime += dt;
- _Movetype_LinkEdict(this, true);
- return;
- default:
- LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid);
- return;
- }
-
- bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0;
-
- vector move1 = this.move_velocity * dt;
- vector moveangle = this.move_avelocity * dt;
-
- makevectors_matrix(-moveangle);
-
-// vector pushorig = this.move_origin;
-// vector pushang = this.move_angles;
-// float pushltime = this.move_ltime;
-
-// move the pusher to its final position
-
- this.move_origin = this.move_origin + dt * this.move_velocity;
- this.move_angles = this.move_angles + dt * this.move_avelocity;
-
- this.move_ltime += dt;
- _Movetype_LinkEdict(this, true);
-
- int savesolid = this.solid;
-
- if (this.move_movetype != MOVETYPE_FAKEPUSH)
- {
- for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); check; check = check.chain)
- {
- switch (check.move_movetype)
- {
- case MOVETYPE_NONE:
- case MOVETYPE_PUSH:
- case MOVETYPE_FOLLOW:
- case MOVETYPE_NOCLIP:
- case MOVETYPE_FLY_WORLDONLY:
- continue;
- default:
- break;
- }
-
- if (check.owner == this)
- continue;
-
- if (this.owner == check)
- continue;
-
- vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
- vector move;
- if (rotated)
- {
- vector org = (check.move_origin - this.move_origin) + pivot;
- vector org2;
- org2.x = org * v_forward;
- org2.y = org * v_right;
- org2.z = org * v_up;
- move = (org2 - org) + move1;
- }
- else
- {
- move = move1;
- }
-
- // physics objects need better collisions than this code can do
- if (check.move_movetype == 32) // MOVETYPE_PHYSICS
- {
- check.move_origin = check.move_origin + move;
- WITH(entity, this, check, _Movetype_LinkEdict(this, true));
- continue;
- }
-
- // try moving the contacted entity
- this.solid = SOLID_NOT;
- bool flag = false;
- WITH(entity, this, check, {
- flag = _Movetype_PushEntity(this, move, true);
- });
- if (!flag)
- {
- // entity "check" got teleported
- check.move_angles_y += trace_fraction * moveangle.y;
- this.solid = savesolid;
- continue; // pushed enough
- }
- // FIXME: turn players specially
- check.move_angles_y += trace_fraction * moveangle.y;
- this.solid = savesolid;
-
- // this trace.fraction < 1 check causes items to fall off of pushers
- // if they pass under or through a wall
- // the groundentity check causes items to fall off of ledges
- if (check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != this))
- check.move_flags &= ~FL_ONGROUND;
- }
- }
-
- this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
- this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
- this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
-}
-
-void _Movetype_Physics_Pusher(entity this, float dt) // SV_Physics_Pusher
-{
- float oldltime = this.move_ltime;
- float thinktime = this.move_nextthink;
- float movetime;
- if (thinktime < this.move_ltime + dt)
- {
- movetime = thinktime - this.move_ltime;
- if (movetime < 0)
- movetime = 0;
- }
- else
- {
- movetime = dt;
- }
-
- if (movetime)
- // advances this.move_ltime if not blocked
- _Movetype_PushMove(this, movetime);
-
- if (thinktime > oldltime && thinktime <= this.move_ltime)
- {
- this.move_nextthink = 0;
- this.move_time = time;
- other = world;
- WITH(entity, self, this, this.move_think());
- }
-}
+++ /dev/null
-#ifndef MOVETYPE_PUSH_H
-#define MOVETYPE_PUSH_H
-
-void _Movetype_Physics_Pusher(entity this, float dt);
-
-#endif
+++ /dev/null
-void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step
-{
- if(this.move_flags & FL_ONGROUND)
- {
- if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
- {
- this.move_flags &= ~FL_ONGROUND;
- _Movetype_CheckVelocity(this);
- _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
- _Movetype_LinkEdict(this, true);
- }
- }
- else
- {
- _Movetype_CheckVelocity(this);
- _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
- _Movetype_LinkEdict(this, true);
-
- // TODO? movetypesteplandevent
- }
-
- _Movetype_CheckWaterTransition(this);
-}
+++ /dev/null
-#include "../physics.qh"
-
-void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss
-{
- if (this.move_flags & FL_ONGROUND)
- {
- if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
- {
- this.move_flags &= ~FL_ONGROUND;
- }
- else if (!this.move_groundentity)
- {
- return;
- }
- else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
- {
- this.move_groundentity = world;
- return;
- }
- }
-
- this.move_suspendedinair = false;
-
- _Movetype_CheckVelocity(this);
-
- if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
- {
- this.move_didgravity = 1;
- this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
- * dt
- * (this.gravity ? this.gravity : 1)
- * PHYS_GRAVITY(this);
- }
-
- this.move_angles = this.move_angles + this.move_avelocity * dt;
-
- float movetime = dt;
- for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
- {
- vector move = this.move_velocity * movetime;
- _Movetype_PushEntity(this, move, true);
- if (wasfreed(this))
- return;
-
- if (trace_startsolid)
- {
- _Movetype_UnstickEntity(this);
- _Movetype_PushEntity(this, move, false);
- if (wasfreed(this))
- return;
- }
-
- if (trace_fraction == 1)
- break;
-
- movetime *= 1 - min(1, trace_fraction);
-
- if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
- {
- this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0);
- this.move_flags &= ~FL_ONGROUND;
- }
- else if (this.move_movetype == MOVETYPE_BOUNCE)
- {
- float bouncefac = this.move_bounce_factor; if (!bouncefac) bouncefac = 0.5;
- float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
- bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
-
- this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac);
-
- float d = trace_plane_normal * this.move_velocity;
- if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
- {
- this.move_flags |= FL_ONGROUND;
- this.move_groundentity = trace_ent;
- this.move_velocity = '0 0 0';
- this.move_avelocity = '0 0 0';
- }
- else
- {
- this.move_flags &= ~FL_ONGROUND;
- }
- }
- else
- {
- this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0);
- if (trace_plane_normal.z > 0.7)
- {
- this.move_flags |= FL_ONGROUND;
- this.move_groundentity = trace_ent;
- if (trace_ent.solid == SOLID_BSP)
- this.move_suspendedinair = true;
- this.move_velocity = '0 0 0';
- this.move_avelocity = '0 0 0';
- }
- else
- {
- this.move_flags &= ~FL_ONGROUND;
- }
- }
-
- // DP revision 8905 (just, WHY...)
- if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
- break;
-
- // DP revision 8918 (WHY...)
- if (this.move_flags & FL_ONGROUND)
- break;
- }
-
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND))
- this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
-
- _Movetype_CheckWaterTransition(this);
-}
+++ /dev/null
-#ifndef MOVETYPE_TOSS_H
-#define MOVETYPE_TOSS_H
-
-void _Movetype_Physics_Toss(entity this, float dt);
-
-#endif
+++ /dev/null
-void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove
-{
- vector stepnormal = '0 0 0';
-
- // if frametime is 0 (due to client sending the same timestamp twice), don't move
- if (dt <= 0)
- return;
-
- if (GAMEPLAYFIX_UNSTICKPLAYERS(this))
- _Movetype_UnstickEntity(this);
-
- bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP));
-
- _Movetype_CheckVelocity(this);
-
- // do a regular slide move unless it looks like you ran into a step
- bool oldonground = (this.move_flags & FL_ONGROUND);
-
- vector start_origin = this.move_origin;
- vector start_velocity = this.move_velocity;
-
- int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0);
-
- if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(clip & 1))
- {
- // only try this if there was no floor in the way in the trace (no,
- // this check seems to be not REALLY necessary, because if clip & 1,
- // our trace will hit that thing too)
- vector upmove = this.move_origin + '0 0 1';
- vector downmove = this.move_origin - '0 0 1';
- int type;
- if (this.move_movetype == MOVETYPE_FLYMISSILE)
- type = MOVE_MISSILE;
- else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY)
- type = MOVE_WORLDONLY;
- else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
- type = MOVE_NOMONSTERS;
- else type = MOVE_NORMAL;
- tracebox(upmove, this.mins, this.maxs, downmove, type, this);
- if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
- clip |= 1; // but we HAVE found a floor
- }
-
- // if the move did not hit the ground at any point, we're not on ground
- if (!(clip & 1))
- this.move_flags &= ~FL_ONGROUND;
-
- _Movetype_CheckVelocity(this);
- _Movetype_LinkEdict(this, true);
-
- if (clip & 8) // teleport
- return;
-
- if (this.move_flags & FL_WATERJUMP)
- return;
-
- if (PHYS_NOSTEP(this))
- return;
-
- vector originalmove_origin = this.move_origin;
- vector originalmove_velocity = this.move_velocity;
- // originalmove_clip = clip;
- int originalmove_flags = this.move_flags;
- entity originalmove_groundentity = this.move_groundentity;
-
- // if move didn't block on a step, return
- if (clip & 2)
- {
- // if move was not trying to move into the step, return
- if (fabs(start_velocity.x) < 0.03125 && fabs(start_velocity.y) < 0.03125)
- return;
-
- if (this.move_movetype != MOVETYPE_FLY)
- {
- // return if gibbed by a trigger
- if (this.move_movetype != MOVETYPE_WALK)
- return;
-
- // return if attempting to jump while airborn (unless sv_jumpstep)
- if (!PHYS_JUMPSTEP(this))
- if (!oldonground && this.move_waterlevel == 0)
- return;
- }
-
- // try moving up and forward to go up a step
- // back to start pos
- this.move_origin = start_origin;
- this.move_velocity = start_velocity;
-
- // move up
- vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this);
- _Movetype_PushEntity(this, upmove, true);
- if(wasfreed(this))
- return;
- if(trace_startsolid)
- {
- // we got teleported when upstepping... must abort the move
- return;
- }
-
- // move forward
- this.move_velocity_z = 0;
- clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0);
- this.move_velocity_z += start_velocity.z;
- if (clip & 8)
- {
- // we got teleported when upstepping... must abort the move
- // note that z velocity handling may not be what QC expects here, but we cannot help it
- return;
- }
-
- _Movetype_CheckVelocity(this);
- _Movetype_LinkEdict(this, true);
-
- // check for stuckness, possibly due to the limited precision of floats
- // in the clipping hulls
- if (clip
- && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125
- && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125)
- {
- // Con_Printf("wall\n");
- // stepping up didn't make any progress, revert to original move
- this.move_origin = originalmove_origin;
- this.move_velocity = originalmove_velocity;
- // clip = originalmove_clip;
- this.move_flags = originalmove_flags;
- this.move_groundentity = originalmove_groundentity;
- // now try to unstick if needed
- // clip = SV_TryUnstick (ent, oldvel);
- return;
- }
-
- // Con_Printf("step - ");
-
- // extra friction based on view angle
- if ((clip & 2) && PHYS_WALLFRICTION(this))
- _Movetype_WallFriction(this, stepnormal);
- }
- // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
- else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND))
- {
- return;
- }
-
- // move down
- vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt);
- _Movetype_PushEntity(this, downmove, true);
- if(wasfreed(this))
- return;
-
- if(trace_startsolid)
- {
- // we got teleported when downstepping... must abort the move
- return;
- }
-
- if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
- {
- // this has been disabled so that you can't jump when you are stepping
- // up while already jumping (also known as the Quake2 double jump bug)
- }
- else
- {
- // Con_Printf("slope\n");
- // if the push down didn't end up on good ground, use the move without
- // the step up. This happens near wall / slope combinations, and can
- // cause the player to hop up higher on a slope too steep to climb
- this.move_origin = originalmove_origin;
- this.move_velocity = originalmove_velocity;
- this.move_flags = originalmove_flags;
- this.move_groundentity = originalmove_groundentity;
- }
-
- _Movetype_CheckVelocity(this);
- _Movetype_LinkEdict(this, true);
-}
+++ /dev/null
-#ifndef MOVETYPE_WALK_H
-#define MOVETYPE_WALK_H
-
-void _Movetype_Physics_Walk(entity this, float dt);
-
-#endif
+++ /dev/null
-#include "physics.qh"
-#include "triggers/include.qh"
-#include "viewloc.qh"
-
-#ifdef SVQC
-
-#include "../server/miscfunctions.qh"
-#include "triggers/trigger/viewloc.qh"
-
-// client side physics
-bool Physics_Valid(string thecvar)
-{
- return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
-}
-
-float Physics_ClientOption(entity this, string option)
-{
- if(Physics_Valid(this.cvar_cl_physics))
- {
- string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option);
- if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
- return cvar(s);
- }
- if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
- {
- string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
- if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
- return cvar(s);
- }
- return cvar(strcat("sv_", option));
-}
-
-void Physics_UpdateStats(entity this, float maxspd_mod)
-{
- STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod);
- STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw"))
- ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod)
- : 0;
- STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod;
- STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking
-
- // old stats
- // fix some new settings
- STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor");
- STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed");
- STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed");
- STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate");
- STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel");
- STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction");
- STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol");
- STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power");
- STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty");
- STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel");
- STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed");
- STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel");
- STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio");
- STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction");
- STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate");
- STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed");
- STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate");
- STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate");
- STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity");
- STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump");
-}
-#endif
-
-float IsMoveInDirection(vector mv, float ang) // key mix factor
-{
- if (mv_x == 0 && mv_y == 0)
- return 0; // avoid division by zero
- ang -= RAD2DEG * atan2(mv_y, mv_x);
- ang = remainder(ang, 360) / 45;
- return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang);
-}
-
-float GeomLerp(float a, float lerp, float b)
-{
- return a == 0 ? (lerp < 1 ? 0 : b)
- : b == 0 ? (lerp > 0 ? 0 : a)
- : a * pow(fabs(b / a), lerp);
-}
-
-#define unstick_offsets(X) \
-/* 1 no nudge (just return the original if this test passes) */ \
- X(' 0.000 0.000 0.000') \
-/* 6 simple nudges */ \
- X(' 0.000 0.000 0.125') X('0.000 0.000 -0.125') \
- X('-0.125 0.000 0.000') X('0.125 0.000 0.000') \
- X(' 0.000 -0.125 0.000') X('0.000 0.125 0.000') \
-/* 4 diagonal flat nudges */ \
- X('-0.125 -0.125 0.000') X('0.125 -0.125 0.000') \
- X('-0.125 0.125 0.000') X('0.125 0.125 0.000') \
-/* 8 diagonal upward nudges */ \
- X('-0.125 0.000 0.125') X('0.125 0.000 0.125') \
- X(' 0.000 -0.125 0.125') X('0.000 0.125 0.125') \
- X('-0.125 -0.125 0.125') X('0.125 -0.125 0.125') \
- X('-0.125 0.125 0.125') X('0.125 0.125 0.125') \
-/* 8 diagonal downward nudges */ \
- X('-0.125 0.000 -0.125') X('0.125 0.000 -0.125') \
- X(' 0.000 -0.125 -0.125') X('0.000 0.125 -0.125') \
- X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \
- X('-0.125 0.125 -0.125') X('0.125 0.125 -0.125') \
-/**/
-
-void PM_ClientMovement_Unstick(entity this)
-{
- #define X(unstick_offset) \
- { \
- vector neworigin = unstick_offset + this.origin; \
- tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \
- if (!trace_startsolid) \
- { \
- setorigin(this, neworigin); \
- return; \
- } \
- }
- unstick_offsets(X);
- #undef X
-}
-
-void PM_ClientMovement_UpdateStatus(entity this, bool ground)
-{
-#ifdef CSQC
- if(!IS_PLAYER(this))
- return;
- // make sure player is not stuck
- if(autocvar_cl_movement == 3)
- PM_ClientMovement_Unstick(this);
-
- // set crouched
- if (PHYS_INPUT_BUTTON_CROUCH(this))
- {
- // wants to crouch, this always works
- if (!IS_DUCKED(this)) SET_DUCKED(this);
- }
- else
- {
- // wants to stand, if currently crouching we need to check for a low ceiling first
- if (IS_DUCKED(this))
- {
- tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this);
- if (!trace_startsolid) UNSET_DUCKED(this);
- }
- }
-
- // set onground
- vector origin1 = this.origin + '0 0 1';
- vector origin2 = this.origin - '0 0 1';
-
- if (ground && autocvar_cl_movement == 3)
- {
- tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this);
- if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7)
- {
- SET_ONGROUND(this);
-
- // this code actually "predicts" an impact; so let's clip velocity first
- this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal;
- }
- else
- UNSET_ONGROUND(this);
- }
-
- if(autocvar_cl_movement == 3)
- {
- // set watertype/waterlevel
- origin1 = this.origin;
- origin1.z += this.mins_z + 1;
- this.waterlevel = WATERLEVEL_NONE;
-
- int thepoint = pointcontents(origin1);
-
- this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
-
- if (this.watertype)
- {
- this.waterlevel = WATERLEVEL_WETFEET;
- origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
- thepoint = pointcontents(origin1);
- if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
- {
- this.waterlevel = WATERLEVEL_SWIMMING;
- origin1.z = this.origin.z + 22;
- thepoint = pointcontents(origin1);
- if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
- this.waterlevel = WATERLEVEL_SUBMERGED;
- }
- }
- }
-
- if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
- pmove_waterjumptime = 0;
-#endif
-}
-
-void PM_ClientMovement_Move(entity this)
-{
-#ifdef CSQC
-
- PM_ClientMovement_UpdateStatus(this, false);
- if(autocvar_cl_movement == 1)
- return;
-
- int bump;
- float t;
- float f;
- vector neworigin;
- vector currentorigin2;
- vector neworigin2;
- vector primalvelocity;
-
- vector trace1_endpos = '0 0 0';
- vector trace2_endpos = '0 0 0';
- vector trace3_endpos = '0 0 0';
- float trace1_fraction = 0;
- float trace2_fraction = 0;
- float trace3_fraction = 0;
- vector trace1_plane_normal = '0 0 0';
- vector trace2_plane_normal = '0 0 0';
- vector trace3_plane_normal = '0 0 0';
-
- primalvelocity = this.velocity;
- for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++)
- {
- neworigin = this.origin + t * this.velocity;
- tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this);
- trace1_endpos = trace_endpos;
- trace1_fraction = trace_fraction;
- trace1_plane_normal = trace_plane_normal;
- if(trace1_fraction < 1 && trace1_plane_normal_z == 0)
- {
- // may be a step or wall, try stepping up
- // first move forward at a higher level
- currentorigin2 = this.origin;
- currentorigin2_z += PHYS_STEPHEIGHT(this);
- neworigin2 = neworigin;
- neworigin2_z += PHYS_STEPHEIGHT(this);
- tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
- trace2_endpos = trace_endpos;
- trace2_fraction = trace_fraction;
- trace2_plane_normal = trace_plane_normal;
- if(!trace_startsolid)
- {
- // then move down from there
- currentorigin2 = trace2_endpos;
- neworigin2 = trace2_endpos;
- neworigin2_z = this.origin_z;
- tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
- trace3_endpos = trace_endpos;
- trace3_fraction = trace_fraction;
- trace3_plane_normal = trace_plane_normal;
- // accept the new trace if it made some progress
- if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125)
- {
- trace1_endpos = trace2_endpos;
- trace1_fraction = trace2_fraction;
- trace1_plane_normal = trace2_plane_normal;
- trace1_endpos = trace3_endpos;
- }
- }
- }
-
- // check if it moved at all
- if(trace1_fraction >= 0.001)
- setorigin(this, trace1_endpos);
-
- // check if it moved all the way
- if(trace1_fraction == 1)
- break;
-
- // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
- // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
- // this got commented out in a change that supposedly makes the code match QW better
- // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
- if(trace1_plane_normal_z > 0.7)
- SET_ONGROUND(this);
-
- t -= t * trace1_fraction;
-
- f = (this.velocity * trace1_plane_normal);
- this.velocity = this.velocity + -f * trace1_plane_normal;
- }
- if(PHYS_TELEPORT_TIME(this) > 0)
- this.velocity = primalvelocity;
-#endif
-}
-
-void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
-{
- float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
- if (k <= 0)
- return;
-
- k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1);
-
- float zspeed = this.velocity_z;
- this.velocity_z = 0;
- float xyspeed = vlen(this.velocity);
- this.velocity = normalize(this.velocity);
-
- float dot = this.velocity * wishdir;
-
- if (dot > 0) // we can't change direction while slowing down
- {
- k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
- xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
- k *= PHYS_AIRCONTROL(this);
- this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
- }
-
- this.velocity = this.velocity * xyspeed;
- this.velocity_z = zspeed;
-}
-
-float AdjustAirAccelQW(float accelqw, float factor)
-{
- return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
-}
-
-// example config for alternate speed clamping:
-// sv_airaccel_qw 0.8
-// sv_airaccel_sideways_friction 0
-// prvm_globalset server speedclamp_mode 1
-// (or 2)
-void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
-{
- float speedclamp = stretchfactor > 0 ? stretchfactor
- : accelqw < 0 ? 1 // full clamping, no stretch
- : -1; // no clamping
-
- accelqw = fabs(accelqw);
-
- if (GAMEPLAYFIX_Q2AIRACCELERATE)
- wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
-
- float vel_straight = this.velocity * wishdir;
- float vel_z = this.velocity_z;
- vector vel_xy = vec2(this.velocity);
- vector vel_perpend = vel_xy - vel_straight * wishdir;
-
- float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
-
- float vel_xy_current = vlen(vel_xy);
- if (speedlimit)
- accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
- float vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
- float vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
- vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards
- vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);
-
- if (sidefric < 0 && (vel_perpend*vel_perpend))
- // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
- {
- float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
- float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
- // assume: themin > 1
- // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
- // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
- // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
- // obviously, this cannot be
- if (themin <= 0)
- vel_perpend *= f;
- else
- {
- themin = sqrt(themin);
- vel_perpend *= max(themin, f);
- }
- }
- else
- vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
-
- vel_xy = vel_straight * wishdir + vel_perpend;
-
- if (speedclamp >= 0)
- {
- float vel_xy_preclamp;
- vel_xy_preclamp = vlen(vel_xy);
- if (vel_xy_preclamp > 0) // prevent division by zero
- {
- vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
- if (vel_xy_current < vel_xy_preclamp)
- vel_xy *= (vel_xy_current / vel_xy_preclamp);
- }
- }
-
- this.velocity = vel_xy + vel_z * '0 0 1';
-}
-
-void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
-{
- if (wishspeed == 0)
- return;
-
- vector curvel = this.velocity;
- curvel_z = 0;
- float curspeed = vlen(curvel);
-
- if (wishspeed > curspeed * 1.01)
- wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
- else
- {
- float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
- wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH;
- }
- vector wishvel = wishdir * wishspeed;
- vector acceldir = wishvel - curvel;
- float addspeed = vlen(acceldir);
- acceldir = normalize(acceldir);
-
- float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
-
- if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
- {
- vector curdir = normalize(curvel);
- float dot = acceldir * curdir;
- if (dot < 0)
- acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir;
- }
-
- this.velocity += accelspeed * acceldir;
-}
-
-
-/*
-=============
-PlayerJump
-
-When you press the jump key
-returns true if handled
-=============
-*/
-bool PlayerJump(entity this)
-{
- if (PHYS_FROZEN(this))
- return true; // no jumping in freezetag when frozen
-
-#ifdef SVQC
- if (this.player_blocked)
- return true; // no jumping while blocked
-#endif
-
- bool doublejump = false;
- float mjumpheight = PHYS_JUMPVELOCITY(this);
-
- if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight))
- return true;
-
- doublejump = player_multijump;
- mjumpheight = player_jumpheight;
-
- if (this.waterlevel >= WATERLEVEL_SWIMMING)
- {
- if(this.viewloc)
- {
- doublejump = true;
- mjumpheight *= 0.7;
- }
- else
- {
- this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
- return true;
- }
- }
-
- if (!doublejump)
- if (!IS_ONGROUND(this))
- return IS_JUMP_HELD(this);
-
- bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
- if(PHYS_TRACK_CANJUMP(this))
- track_jump = true;
-
- if (track_jump)
- if (IS_JUMP_HELD(this))
- return true;
-
- // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline
- // velocity bounds. Final velocity is bound between (jumpheight *
- // min + jumpheight) and (jumpheight * max + jumpheight);
-
- if(PHYS_JUMPSPEEDCAP_MIN != "")
- {
- float minjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MIN);
-
- if (this.velocity_z < minjumpspeed)
- mjumpheight += minjumpspeed - this.velocity_z;
- }
-
- if(PHYS_JUMPSPEEDCAP_MAX != "")
- {
- // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
- tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this);
-
- if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this)))
- {
- float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX);
-
- if (this.velocity_z > maxjumpspeed)
- mjumpheight -= this.velocity_z - maxjumpspeed;
- }
- }
-
- if (!WAS_ONGROUND(this))
- {
-#ifdef SVQC
- if(autocvar_speedmeter)
- LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
- if(this.lastground < time - 0.3)
- {
- float f = (1 - PHYS_FRICTION_ONLAND(this));
- this.velocity_x *= f;
- this.velocity_y *= f;
- }
-#ifdef SVQC
- if(this.jumppadcount > 1)
- LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
- this.jumppadcount = 0;
-#endif
- }
-
- this.velocity_z += mjumpheight;
-
- UNSET_ONGROUND(this);
- SET_JUMP_HELD(this);
-
-#ifdef SVQC
-
- this.oldvelocity_z = this.velocity_z;
-
- animdecide_setaction(this, ANIMACTION_JUMP, true);
-
- if (autocvar_g_jump_grunt)
- PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-#endif
- return true;
-}
-
-void CheckWaterJump(entity this)
-{
-// check for a jump-out-of-water
- makevectors(this.v_angle);
- vector start = this.origin;
- start_z += 8;
- v_forward_z = 0;
- normalize(v_forward);
- vector end = start + v_forward*24;
- traceline (start, end, true, this);
- if (trace_fraction < 1)
- { // solid at waist
- start_z = start_z + this.maxs_z - 8;
- end = start + v_forward*24;
- this.movedir = trace_plane_normal * -50;
- traceline(start, end, true, this);
- if (trace_fraction == 1)
- { // open at eye level
- this.velocity_z = 225;
- this.flags |= FL_WATERJUMP;
- SET_JUMP_HELD(this);
- #ifdef SVQC
- PHYS_TELEPORT_TIME(this) = time + 2; // safety net
- #elif defined(CSQC)
- pmove_waterjumptime = time + 2;
- #endif
- }
- }
-}
-
-
-#ifdef SVQC
- #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump
-#elif defined(CSQC)
- float autocvar_cl_jetpack_jump;
- #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump
-#endif
-.float jetpack_stopped;
-void CheckPlayerJump(entity this)
-{
-#ifdef SVQC
- float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK;
-#endif
- if (JETPACK_JUMP(this) < 2)
- ITEMS_STAT(this) &= ~IT_USING_JETPACK;
-
- if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this))
- {
- float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects
- float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this);
- float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO;
-
- if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { }
- else if (this.jetpack_stopped) { }
- else if (!has_fuel)
- {
-#ifdef SVQC
- if (was_flying) // TODO: ran out of fuel message
- Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
- else if (activate)
- Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
-#endif
- this.jetpack_stopped = true;
- ITEMS_STAT(this) &= ~IT_USING_JETPACK;
- }
- else if (activate && !PHYS_FROZEN(this))
- ITEMS_STAT(this) |= IT_USING_JETPACK;
- }
- else
- {
- this.jetpack_stopped = false;
- ITEMS_STAT(this) &= ~IT_USING_JETPACK;
- }
- if (!PHYS_INPUT_BUTTON_JUMP(this))
- UNSET_JUMP_HELD(this);
-
- if (this.waterlevel == WATERLEVEL_SWIMMING)
- CheckWaterJump(this);
-}
-
-float racecar_angle(float forward, float down)
-{
- if (forward < 0)
- {
- forward = -forward;
- down = -down;
- }
-
- float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);
-
- float angle_mult = forward / (800 + forward);
-
- if (ret > 180)
- return ret * angle_mult + 360 * (1 - angle_mult);
- else
- return ret * angle_mult;
-}
-
-string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
-.float specialcommand_pos;
-void SpecialCommand()
-{
-#ifdef SVQC
- if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse))
- LOG_INFO("A hollow voice says \"Plugh\".\n");
-#endif
-}
-
-float PM_check_specialcommand(entity this, float buttons)
-{
-#ifdef SVQC
- string c;
- if (!buttons)
- c = "x";
- else if (buttons == 1)
- c = "1";
- else if (buttons == 2)
- c = " ";
- else if (buttons == 128)
- c = "s";
- else if (buttons == 256)
- c = "w";
- else if (buttons == 512)
- c = "a";
- else if (buttons == 1024)
- c = "d";
- else
- c = "?";
-
- if (c == substring(specialcommand, this.specialcommand_pos, 1))
- {
- this.specialcommand_pos += 1;
- if (this.specialcommand_pos >= strlen(specialcommand))
- {
- this.specialcommand_pos = 0;
- SpecialCommand();
- return true;
- }
- }
- else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1)))
- this.specialcommand_pos = 0;
-#endif
- return false;
-}
-
-void PM_check_nickspam(entity this)
-{
-#ifdef SVQC
- if (time >= this.nickspamtime)
- return;
- if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
- {
- // slight annoyance for nick change scripts
- this.movement = -1 * this.movement;
- this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0;
-
- if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
- {
- this.v_angle_x = random() * 360;
- this.v_angle_y = random() * 360;
- // at least I'm not forcing retardedview by also assigning to angles_z
- this.fixangle = true;
- }
- }
-#endif
-}
-
-void PM_check_punch(entity this)
-{
-#ifdef SVQC
- if (this.punchangle != '0 0 0')
- {
- float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
- if (f > 0)
- this.punchangle = normalize(this.punchangle) * f;
- else
- this.punchangle = '0 0 0';
- }
-
- if (this.punchvector != '0 0 0')
- {
- float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
- if (f > 0)
- this.punchvector = normalize(this.punchvector) * f;
- else
- this.punchvector = '0 0 0';
- }
-#endif
-}
-
-// predict frozen movement, as frozen players CAN move in some cases
-void PM_check_frozen(entity this)
-{
- if (!PHYS_FROZEN(this))
- return;
- if (PHYS_DODGING_FROZEN(this)
-#ifdef SVQC
- && IS_REAL_CLIENT(this)
-#endif
- )
- {
- this.movement_x = bound(-5, this.movement.x, 5);
- this.movement_y = bound(-5, this.movement.y, 5);
- this.movement_z = bound(-5, this.movement.z, 5);
- }
- else
- this.movement = '0 0 0';
-
- vector midpoint = ((this.absmin + this.absmax) * 0.5);
- if (pointcontents(midpoint) == CONTENT_WATER)
- {
- this.velocity = this.velocity * 0.5;
-
- if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
- this.velocity_z = 200;
- }
-}
-
-void PM_check_hitground(entity this)
-{
-#ifdef SVQC
- if (!this.wasFlying) return;
- this.wasFlying = false;
- if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
- if (time < this.ladder_time) return;
- if (this.hook) return;
- this.nextstep = time + 0.3 + random() * 0.1;
- trace_dphitq3surfaceflags = 0;
- tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
- if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
- entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
- ? GS_FALL_METAL
- : GS_FALL;
- GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-#endif
-}
-
-void PM_Footsteps(entity this)
-{
-#ifdef SVQC
- if (!g_footsteps) return;
- if (IS_DUCKED(this)) return;
- if (time >= this.lastground + 0.2) return;
- if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return;
- if ((time > this.nextstep) || (time < (this.nextstep - 10.0)))
- {
- this.nextstep = time + 0.3 + random() * 0.1;
- trace_dphitq3surfaceflags = 0;
- tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
- if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
- entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
- ? GS_STEP_METAL
- : GS_STEP;
- GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
- }
-#endif
-}
-
-void PM_check_blocked(entity this)
-{
-#ifdef SVQC
- if (!this.player_blocked)
- return;
- this.movement = '0 0 0';
- this.disableclientprediction = 1;
-#endif
-}
-
-void PM_fly(entity this, float maxspd_mod)
-{
- // noclipping or flying
- UNSET_ONGROUND(this);
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * this.movement.z;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#endif
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
- PM_ClientMovement_Move(this);
-}
-
-void PM_swim(entity this, float maxspd_mod)
-{
- // swimming
- UNSET_ONGROUND(this);
-
- float jump = PHYS_INPUT_BUTTON_JUMP(this);
- // water jump only in certain situations
- // this mimics quakeworld code
- if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
- {
- vector yawangles = '0 1 0' * this.v_angle.y;
- makevectors(yawangles);
- vector forward = v_forward;
- vector spot = this.origin + 24 * forward;
- spot_z += 8;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (trace_startsolid)
- {
- spot_z += 24;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (!trace_startsolid)
- {
- this.velocity = forward * 50;
- this.velocity_z = 310;
- #ifdef CSQC
- pmove_waterjumptime = 2;
- #endif
- UNSET_ONGROUND(this);
- SET_JUMP_HELD(this);
- }
- }
- }
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * this.movement.z;
- if(this.viewloc)
- wishvel.z = -160; // drift anyway
- else if (wishvel == '0 0 0')
- wishvel = '0 0 -60'; // drift towards bottom
-
-
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
-
- if (IS_DUCKED(this))
- wishspeed *= 0.5;
-
-// if (pmove_waterjumptime <= 0) // TODO: use
- {
- // water friction
- float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
- f = min(max(0, f), 1);
- this.velocity *= f;
-
- f = wishspeed - this.velocity * wishdir;
- if (f > 0)
- {
- float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
- this.velocity += accelspeed * wishdir;
- }
-
- // holding jump button swims upward slowly
- if (jump && !this.viewloc)
- {
-#if 0
- if (this.watertype & CONTENT_LAVA)
- this.velocity_z = 50;
- else if (this.watertype & CONTENT_SLIME)
- this.velocity_z = 80;
- else
- {
- if (IS_NEXUIZ_DERIVED(gamemode))
-#endif
- this.velocity_z = 200;
-#if 0
- else
- this.velocity_z = 100;
- }
-#endif
- }
- }
- if(this.viewloc)
- {
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
- }
- else
- {
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
- PM_ClientMovement_Move(this);
- }
-}
-
-.vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
-{
- // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
- UNSET_ONGROUND(this);
-
- float g;
- g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if (PHYS_ENTGRAVITY(this))
- g *= PHYS_ENTGRAVITY(this);
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- {
- g *= 0.5;
- this.velocity_z += g;
- }
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement_x
- + v_right * this.movement_y
- + '0 0 1' * this.movement_z;
- if(this.viewloc)
- wishvel.z = this.oldmovement.x;
- this.velocity_z += g;
- if (this.ladder_entity.classname == "func_water")
- {
- float f = vlen(wishvel);
- if (f > this.ladder_entity.speed)
- wishvel *= (this.ladder_entity.speed / f);
-
- this.watertype = this.ladder_entity.skin;
- f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
- if ((this.origin_z + this.view_ofs_z) < f)
- this.waterlevel = WATERLEVEL_SUBMERGED;
- else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
- this.waterlevel = WATERLEVEL_SWIMMING;
- else if ((this.origin_z + this.mins_z + 1) < f)
- this.waterlevel = WATERLEVEL_WETFEET;
- else
- {
- this.waterlevel = WATERLEVEL_NONE;
- this.watertype = CONTENT_EMPTY;
- }
- }
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
- if(time >= PHYS_TELEPORT_TIME(this))
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
- PM_ClientMovement_Move(this);
-}
-
-void PM_jetpack(entity this, float maxspd_mod)
-{
- //makevectors(this.v_angle.y * '0 1 0');
- makevectors(this.v_angle);
- vector wishvel = v_forward * this.movement_x
- + v_right * this.movement_y;
- // add remaining speed as Z component
- float maxairspd = PHYS_MAXAIRSPEED(this) * max(1, maxspd_mod);
- // fix speedhacks :P
- wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd);
- // add the unused velocity as up component
- wishvel_z = 0;
-
- // if (this.BUTTON_JUMP)
- wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));
-
- // it is now normalized, so...
- float a_side = PHYS_JETPACK_ACCEL_SIDE(this);
- float a_up = PHYS_JETPACK_ACCEL_UP(this);
- float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
-
- wishvel_x *= a_side;
- wishvel_y *= a_side;
- wishvel_z *= a_up;
- wishvel_z += a_add;
-
- float best = 0;
- //////////////////////////////////////////////////////////////////////////////////////
- // finding the maximum over all vectors of above form
- // with wishvel having an absolute value of 1
- //////////////////////////////////////////////////////////////////////////////////////
- // we're finding the maximum over
- // f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2;
- // for z in the range from -1 to 1
- //////////////////////////////////////////////////////////////////////////////////////
- // maximum is EITHER attained at the single extreme point:
- float a_diff = a_side * a_side - a_up * a_up;
- float f;
- if (a_diff != 0)
- {
- f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z)
- if (f > -1 && f < 1) // can it be attained?
- {
- best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff;
- //print("middle\n");
- }
- }
- // OR attained at z = 1:
- f = (a_up + a_add) * (a_up + a_add);
- if (f > best)
- {
- best = f;
- //print("top\n");
- }
- // OR attained at z = -1:
- f = (a_up - a_add) * (a_up - a_add);
- if (f > best)
- {
- best = f;
- //print("bottom\n");
- }
- best = sqrt(best);
- //////////////////////////////////////////////////////////////////////////////////////
-
- //print("best possible acceleration: ", ftos(best), "\n");
-
- float fxy, fz;
- fxy = bound(0, 1 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1);
- if (wishvel_z - PHYS_GRAVITY(this) > 0)
- fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
- else
- fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
-
- float fvel;
- fvel = vlen(wishvel);
- wishvel_x *= fxy;
- wishvel_y *= fxy;
- wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this);
-
- fvel = min(1, vlen(wishvel) / best);
- if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
- f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
- else
- f = 1;
-
- //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
-
- if (f > 0 && wishvel != '0 0 0')
- {
- this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
- UNSET_ONGROUND(this);
-
-#ifdef SVQC
- if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
- this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
-
- ITEMS_STAT(this) |= IT_USING_JETPACK;
-
- // jetpack also inhibits health regeneration, but only for 1 second
- this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
-#endif
- }
-
-#ifdef CSQC
- float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if(autocvar_cl_movement == 3)
- {
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.velocity_z -= g * 0.5;
- else
- this.velocity_z -= g;
- }
- PM_ClientMovement_Move(this);
- if(autocvar_cl_movement == 3)
- {
- if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.velocity_z -= g * 0.5;
- }
-#endif
-}
-
-void PM_walk(entity this, float maxspd_mod)
-{
- if (!WAS_ONGROUND(this))
- {
-#ifdef SVQC
- if (autocvar_speedmeter)
- LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
- if (this.lastground < time - 0.3)
- this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
-#ifdef SVQC
- if (this.jumppadcount > 1)
- LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
- this.jumppadcount = 0;
-#endif
- }
-
- // walking
- makevectors(this.v_angle.y * '0 1 0');
- const vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- const vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
- wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
- if (IS_DUCKED(this)) wishspeed *= 0.5;
-
- // apply edge friction
- const float f2 = vlen2(vec2(this.velocity));
- if (f2 > 0)
- {
- trace_dphitq3surfaceflags = 0;
- tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
- // TODO: apply edge friction
- // apply ground friction
- const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
- ? PHYS_FRICTION_SLICK(this)
- : PHYS_FRICTION(this);
-
- float f = sqrt(f2);
- f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
- f = max(0, f);
- this.velocity *= f;
- /*
- Mathematical analysis time!
-
- Our goal is to invert this mess.
-
- For the two cases we get:
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
- = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- and
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-
- These cases would be chosen ONLY if:
- v0 < PHYS_STOPSPEED(this)
- v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
- v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- and, respectively:
- v0 >= PHYS_STOPSPEED(this)
- v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
- v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- */
- }
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
-#ifdef CSQC
- float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if(autocvar_cl_movement == 3)
- {
- if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
- this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
- }
- if (vdist(this.velocity, >, 0))
- PM_ClientMovement_Move(this);
- if(autocvar_cl_movement == 3)
- {
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
- this.velocity_z -= g * 0.5;
- }
-#endif
-}
-
-void PM_air(entity this, float buttons_prev, float maxspd_mod)
-{
- makevectors(this.v_angle.y * '0 1 0');
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
-
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- {
- float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
-
- // apply air speed limit
- float airaccelqw = PHYS_AIRACCEL_QW(this);
- float wishspeed0 = wishspeed;
- wishspeed = min(wishspeed, maxairspd);
- if (IS_DUCKED(this))
- wishspeed *= 0.5;
- float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
-
- float accelerating = (this.velocity * wishdir > 0);
- float wishspeed2 = wishspeed;
-
- // CPM: air control
- if (PHYS_AIRSTOPACCELERATE(this))
- {
- vector curdir = normalize(vec2(this.velocity));
- airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
- }
- // note that for straight forward jumping:
- // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
- // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
- // -->
- // dv/dt = accel * maxspeed (when slow)
- // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
- // log dv/dt = logaccel + logmaxspeed (when slow)
- // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
- float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
- if (PHYS_MAXAIRSTRAFESPEED(this))
- wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
- if (PHYS_AIRSTRAFEACCELERATE(this))
- airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
- if (PHYS_AIRSTRAFEACCEL_QW(this))
- airaccelqw =
- (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
- *
- (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
- // !CPM
-
- if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
- PM_AirAccelerate(this, wishdir, wishspeed2);
- else
- PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this));
-
- if (PHYS_AIRCONTROL(this))
- CPM_PM_Aircontrol(this, wishdir, wishspeed2);
- }
-#ifdef CSQC
- float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if(autocvar_cl_movement == 3)
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.velocity_z -= g * 0.5;
- else
- this.velocity_z -= g;
-#endif
- PM_ClientMovement_Move(this);
-#ifdef CSQC
- if(autocvar_cl_movement == 3)
- if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- this.velocity_z -= g * 0.5;
-#endif
-}
-
-// used for calculating airshots
-bool IsFlying(entity this)
-{
- if(IS_ONGROUND(this))
- return false;
- if(this.waterlevel >= WATERLEVEL_SWIMMING)
- return false;
- traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
- if(trace_fraction < 1)
- return false;
- return true;
-}
-
-void PM_Main(entity this)
-{
- int buttons = PHYS_INPUT_BUTTON_MASK(this);
-#ifdef CSQC
- this.items = STAT(ITEMS);
-
- this.movement = PHYS_INPUT_MOVEVALUES(this);
-
- this.spectatorspeed = STAT(SPECTATORSPEED);
-
- vector oldv_angle = this.v_angle;
- vector oldangles = this.angles; // we need to save these, as they're abused by other code
- this.v_angle = PHYS_INPUT_ANGLES(this);
- this.angles = PHYS_WORLD_ANGLES(this);
-
- this.team = myteam + 1; // is this correct?
- if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
- UNSET_JUMP_HELD(this); // canjump = true
- pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
-
- PM_ClientMovement_UpdateStatus(this, true);
-#endif
-
- this.oldmovement = this.movement;
-
-
-#ifdef SVQC
- WarpZone_PlayerPhysics_FixVAngle();
-#endif
- float maxspeed_mod = 1;
- maxspeed_mod *= PHYS_HIGHSPEED(this);
-
-#ifdef SVQC
- Physics_UpdateStats(this, maxspeed_mod);
-
- if (this.PlayerPhysplug)
- if (this.PlayerPhysplug())
- return;
-#endif
-
-#ifdef SVQC
- anticheat_physics(this);
-#endif
-
- if (PM_check_specialcommand(this, buttons))
- return;
-#ifdef SVQC
- if (sv_maxidle > 0)
- {
- if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
- this.parm_idlesince = time;
- }
-#endif
- int buttons_prev = this.buttons_old;
- this.buttons_old = buttons;
- this.movement_old = this.movement;
- this.v_angle_old = this.v_angle;
-
- PM_check_nickspam(this);
-
- PM_check_punch(this);
-#ifdef SVQC
- if (IS_BOT_CLIENT(this))
- {
- if (playerdemo_read(this))
- return;
- WITH(entity, self, this, bot_think());
- }
-#endif
-
-#ifdef SVQC
- if (IS_PLAYER(this))
- {
- const bool allowed_to_move = (time >= game_starttime);
- if (!allowed_to_move)
- {
- this.velocity = '0 0 0';
- this.movetype = MOVETYPE_NONE;
- this.disableclientprediction = 2;
- }
- else if (this.disableclientprediction == 2)
- {
- if (this.movetype == MOVETYPE_NONE)
- this.movetype = MOVETYPE_WALK;
- this.disableclientprediction = 0;
- }
- }
-#endif
-
-#ifdef SVQC
- if (this.movetype == MOVETYPE_NONE)
- return;
-
- // when we get here, disableclientprediction cannot be 2
- this.disableclientprediction = 0;
-#endif
-
- viewloc_PlayerPhysics(this);
-
- PM_check_frozen(this);
-
- PM_check_blocked(this);
-
- maxspeed_mod = 1;
-
- if (this.in_swamp)
- maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-
- // conveyors: first fix velocity
- if (this.conveyor.state)
- this.velocity -= this.conveyor.movedir;
-
- MUTATOR_CALLHOOK(PlayerPhysics, this);
-
- if (!IS_PLAYER(this))
- {
-#ifdef SVQC
- maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
- if (!this.spectatorspeed)
- this.spectatorspeed = maxspeed_mod;
- if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
- {
- if (this.lastclassname != "player")
- {
- if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
- this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
- else if (this.impulse == 11)
- this.spectatorspeed = maxspeed_mod;
- else if (this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
- this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
- else if (this.impulse >= 1 && this.impulse <= 9)
- this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
- } // otherwise just clear
- this.impulse = 0;
- }
-#endif
- maxspeed_mod = this.spectatorspeed;
- }
-#ifdef SVQC
-
- float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
- if(this.speed != spd)
- {
- this.speed = spd;
- string temps = ftos(spd);
- stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
- }
-
- if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
- {
- this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
- }
- if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
- {
- this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
- }
-#endif
-
- if(PHYS_DEAD(this))
- {
- // handle water here
- vector midpoint = ((this.absmin + this.absmax) * 0.5);
- if(pointcontents(midpoint) == CONTENT_WATER)
- {
- this.velocity = this.velocity * 0.5;
-
- // do we want this?
- //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
- //{ this.velocity_z = 70; }
- }
- goto end;
- }
-
-#ifdef SVQC
- if (!this.fixangle)
- this.angles = '0 1 0' * this.v_angle.y;
-#endif
-
- if (IS_PLAYER(this) && IS_ONGROUND(this))
- {
- PM_check_hitground(this);
- PM_Footsteps(this);
- }
-
- if(IsFlying(this))
- this.wasFlying = 1;
-
- if (IS_PLAYER(this))
- CheckPlayerJump(this);
-
- if (this.flags & FL_WATERJUMP)
- {
- this.velocity_x = this.movedir.x;
- this.velocity_y = this.movedir.y;
- if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE)
- {
- this.flags &= ~FL_WATERJUMP;
- PHYS_TELEPORT_TIME(this) = 0;
- }
- }
-
- else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
- { }
-
-#ifdef SVQC
- else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#elif defined(CSQC)
- else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#endif
- PM_fly(this, maxspeed_mod);
-
- else if (this.waterlevel >= WATERLEVEL_SWIMMING)
- PM_swim(this, maxspeed_mod);
-
- else if (time < this.ladder_time)
- PM_ladder(this, maxspeed_mod);
-
- else if (ITEMS_STAT(this) & IT_USING_JETPACK)
- PM_jetpack(this, maxspeed_mod);
-
- else if (IS_ONGROUND(this))
- PM_walk(this, maxspeed_mod);
-
- else
- PM_air(this, buttons_prev, maxspeed_mod);
-
-:end
- if (IS_ONGROUND(this))
- this.lastground = time;
-
- // conveyors: then break velocity again
- if(this.conveyor.state)
- this.velocity += this.conveyor.movedir;
-
- this.lastflags = this.flags;
-
- this.lastclassname = this.classname;
-
-#ifdef CSQC
- this.v_angle = oldv_angle;
- this.angles = oldangles;
-#endif
-}
-
-#if defined(SVQC)
-void SV_PlayerPhysics()
-#elif defined(CSQC)
-void CSQC_ClientMovement_PlayerMove_Frame(entity this)
-#endif
-{
-#ifdef SVQC
- SELFPARAM();
-#endif
- PM_Main(this);
-}
+++ /dev/null
-#ifndef COMMON_PHYSICS_H
-#define COMMON_PHYSICS_H
-
-// Client/server mappings
-
-.entity conveyor;
-
-.float race_penalty;
-
-.float gravity;
-.float swamp_slowdown;
-.float lastflags;
-.float lastground;
-.float wasFlying;
-#ifdef SVQC
-.float spectatorspeed = _STAT(SPECTATORSPEED);
-#elif defined(CSQC)
-.float spectatorspeed;
-#endif
-
-.vector movement_old;
-.float buttons_old;
-.vector v_angle_old;
-.string lastclassname;
-
-.float() PlayerPhysplug;
-float AdjustAirAccelQW(float accelqw, float factor);
-
-bool IsFlying(entity a);
-
-#define BUFFS_STAT(s) STAT(BUFFS, s)
-
-#define GAMEPLAYFIX_DOWNTRACEONGROUND(s) STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, s)
-#define GAMEPLAYFIX_EASIERWATERJUMP(s) STAT(GAMEPLAYFIX_EASIERWATERJUMP, s)
-#define GAMEPLAYFIX_STEPDOWN(s) STAT(GAMEPLAYFIX_STEPDOWN, s)
-#define GAMEPLAYFIX_STEPMULTIPLETIMES(s) STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s)
-#define GAMEPLAYFIX_UNSTICKPLAYERS(s) STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s)
-
-#define PHYS_ACCELERATE(s) STAT(MOVEVARS_ACCELERATE, s)
-#define PHYS_AIRACCELERATE(s) STAT(MOVEVARS_AIRACCELERATE, s)
-#define PHYS_AIRACCEL_QW(s) STAT(MOVEVARS_AIRACCEL_QW, s)
-#define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, s)
-#define PHYS_AIRACCEL_SIDEWAYS_FRICTION(s) STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, s)
-#define PHYS_AIRCONTROL(s) STAT(MOVEVARS_AIRCONTROL, s)
-#define PHYS_AIRCONTROL_PENALTY(s) STAT(MOVEVARS_AIRCONTROL_PENALTY, s)
-#define PHYS_AIRCONTROL_POWER(s) STAT(MOVEVARS_AIRCONTROL_POWER, s)
-#define PHYS_AIRSPEEDLIMIT_NONQW(s) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s)
-#define PHYS_AIRSTOPACCELERATE(s) STAT(MOVEVARS_AIRSTOPACCELERATE, s)
-#define PHYS_AIRSTRAFEACCELERATE(s) STAT(MOVEVARS_AIRSTRAFEACCELERATE, s)
-#define PHYS_AIRSTRAFEACCEL_QW(s) STAT(MOVEVARS_AIRSTRAFEACCEL_QW, s)
-
-#define PHYS_AMMO_FUEL(s) STAT(FUEL, s)
-
-#define PHYS_DODGING_FROZEN(s) STAT(DODGING_FROZEN, s)
-
-#define PHYS_FRICTION(s) STAT(MOVEVARS_FRICTION, s)
-#define PHYS_FRICTION_ONLAND(s) STAT(MOVEVARS_FRICTION_ONLAND, s)
-#define PHYS_FRICTION_SLICK(s) STAT(MOVEVARS_FRICTION_SLICK, s)
-
-#define PHYS_FROZEN(s) STAT(FROZEN, s)
-
-#define PHYS_HIGHSPEED(s) STAT(MOVEVARS_HIGHSPEED, s)
-
-#define PHYS_JETPACK_ACCEL_SIDE(s) STAT(JETPACK_ACCEL_SIDE, s)
-#define PHYS_JETPACK_ACCEL_UP(s) STAT(JETPACK_ACCEL_UP, s)
-#define PHYS_JETPACK_ANTIGRAVITY(s) STAT(JETPACK_ANTIGRAVITY, s)
-#define PHYS_JETPACK_FUEL(s) STAT(JETPACK_FUEL, s)
-#define PHYS_JETPACK_MAXSPEED_SIDE(s) STAT(JETPACK_MAXSPEED_SIDE, s)
-#define PHYS_JETPACK_MAXSPEED_UP(s) STAT(JETPACK_MAXSPEED_UP, s)
-
-#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s)
-#define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP, s)
-#define PHYS_JUMPVELOCITY(s) STAT(MOVEVARS_JUMPVELOCITY, s)
-
-#define PHYS_MAXAIRSPEED(s) STAT(MOVEVARS_MAXAIRSPEED, s)
-#define PHYS_MAXAIRSTRAFESPEED(s) STAT(MOVEVARS_MAXAIRSTRAFESPEED, s)
-#define PHYS_MAXSPEED(s) STAT(MOVEVARS_MAXSPEED, s)
-
-#define PHYS_NOSTEP(s) STAT(NOSTEP, s)
-#define PHYS_STEPHEIGHT(s) STAT(MOVEVARS_STEPHEIGHT, s)
-
-#define PHYS_STOPSPEED(s) STAT(MOVEVARS_STOPSPEED, s)
-
-#define PHYS_TRACK_CANJUMP(s) STAT(MOVEVARS_TRACK_CANJUMP, s)
-
-#define PHYS_WALLFRICTION(s) STAT(MOVEVARS_WALLFRICTION, s)
-
-#define PHYS_WARSOWBUNNY_ACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_ACCEL, s)
-#define PHYS_WARSOWBUNNY_AIRFORWARDACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, s)
-#define PHYS_WARSOWBUNNY_BACKTOSIDERATIO(s) STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, s)
-#define PHYS_WARSOWBUNNY_TOPSPEED(s) STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s)
-#define PHYS_WARSOWBUNNY_TURNACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s)
-
-#define UPWARD_VELOCITY_CLEARS_ONGROUND(s) STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s)
-
-#define PHYS_INPUT_BUTTON_ATCK(s) PHYS_INPUT_BUTTON_BUTTON1(s)
-#define PHYS_INPUT_BUTTON_JUMP(s) PHYS_INPUT_BUTTON_BUTTON2(s)
-#define PHYS_INPUT_BUTTON_ATCK2(s) PHYS_INPUT_BUTTON_BUTTON3(s)
-#define PHYS_INPUT_BUTTON_ZOOM(s) PHYS_INPUT_BUTTON_BUTTON4(s)
-#define PHYS_INPUT_BUTTON_CROUCH(s) PHYS_INPUT_BUTTON_BUTTON5(s)
-#define PHYS_INPUT_BUTTON_HOOK(s) PHYS_INPUT_BUTTON_BUTTON6(s)
-#define PHYS_INPUT_BUTTON_INFO(s) PHYS_INPUT_BUTTON_BUTTON7(s)
-#define PHYS_INPUT_BUTTON_DRAG(s) PHYS_INPUT_BUTTON_BUTTON8(s)
-#define PHYS_INPUT_BUTTON_USE(s) PHYS_INPUT_BUTTON_BUTTON_USE(s)
-#define PHYS_INPUT_BUTTON_CHAT(s) PHYS_INPUT_BUTTON_BUTTON_CHAT(s)
-#define PHYS_INPUT_BUTTON_PRYDON(s) PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)
-#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s) PHYS_INPUT_BUTTON_BUTTON9(s)
-#define PHYS_INPUT_BUTTON_JETPACK(s) PHYS_INPUT_BUTTON_BUTTON10(s)
-
-// if more buttons are needed, start using impulse bits as buttons
-
-#define PHYS_INPUT_BUTTON_BACKWARD(s) (PHYS_INPUT_MOVEVALUES(s).x < 0)
-#define PHYS_INPUT_BUTTON_FORWARD(s) (PHYS_INPUT_MOVEVALUES(s).x > 0)
-#define PHYS_INPUT_BUTTON_LEFT(s) (PHYS_INPUT_MOVEVALUES(s).y < 0)
-#define PHYS_INPUT_BUTTON_RIGHT(s) (PHYS_INPUT_MOVEVALUES(s).y > 0)
-
-// used for special commands and idle checking, not from the engine
-// TODO: cache
-#define PHYS_INPUT_BUTTON_MASK(s) ( \
- (1 << 0) * PHYS_INPUT_BUTTON_ATCK(s) \
- | (1 << 1) * PHYS_INPUT_BUTTON_JUMP(s) \
- | (1 << 2) * PHYS_INPUT_BUTTON_ATCK2(s) \
- | (1 << 3) * PHYS_INPUT_BUTTON_ZOOM(s) \
- | (1 << 4) * PHYS_INPUT_BUTTON_CROUCH(s) \
- | (1 << 5) * PHYS_INPUT_BUTTON_HOOK(s) \
- | (1 << 6) * PHYS_INPUT_BUTTON_USE(s) \
- | (1 << 7) * PHYS_INPUT_BUTTON_BACKWARD(s) \
- | (1 << 8) * PHYS_INPUT_BUTTON_FORWARD(s) \
- | (1 << 9) * PHYS_INPUT_BUTTON_LEFT(s) \
- | (1 << 10) * PHYS_INPUT_BUTTON_RIGHT(s) \
- )
-
-#define IS_JUMP_HELD(s) (!((s).flags & FL_JUMPRELEASED))
-#define SET_JUMP_HELD(s) ((s).flags &= ~FL_JUMPRELEASED)
-#define UNSET_JUMP_HELD(s) ((s).flags |= FL_JUMPRELEASED)
-
-#define IS_ONGROUND(s) boolean((s).flags & FL_ONGROUND)
-#define SET_ONGROUND(s) ((s).flags |= FL_ONGROUND)
-#define UNSET_ONGROUND(s) ((s).flags &= ~FL_ONGROUND)
-
-#define WAS_ONGROUND(s) boolean((s).lastflags & FL_ONGROUND)
-
-#define ITEMS_STAT(s) ((s).items)
-
-#ifdef CSQC
-
- string autocvar_cl_jumpspeedcap_min;
- string autocvar_cl_jumpspeedcap_max;
-
- noref float pmove_waterjumptime;
-
- const int FL_WATERJUMP = 2048; // player jumping out of water
- const int FL_JUMPRELEASED = 4096; // for jump debouncing
-
- .float watertype;
- .float waterlevel;
- .int items;
-
- .vector movement;
- .vector v_angle;
-
-// TODO
- #define IS_CLIENT(s) ((s).isplayermodel)
- #define IS_PLAYER(s) ((s).isplayermodel)
- #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel)
- #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE))
-
- //float player_multijump;
- //float player_jumpheight;
-
- #define PHYS_GRAVITY(s) STAT(MOVEVARS_GRAVITY, s)
-
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
- #define TICRATE ticrate
-
- #define PHYS_INPUT_ANGLES(s) input_angles
-// TODO
- #define PHYS_WORLD_ANGLES(s) input_angles
-
- #define PHYS_INPUT_TIMELENGTH input_timelength
- #define PHYS_INPUT_FRAMETIME serverdeltatime
-
- #define PHYS_INPUT_MOVEVALUES(s) input_movevalues
-
- #define PHYS_INPUT_BUTTON_BUTTON1(s) boolean(input_buttons & BIT(0))
- #define PHYS_INPUT_BUTTON_BUTTON2(s) boolean(input_buttons & BIT(1))
- #define PHYS_INPUT_BUTTON_BUTTON3(s) boolean(input_buttons & BIT(2))
- #define PHYS_INPUT_BUTTON_BUTTON4(s) boolean(input_buttons & BIT(3))
- #define PHYS_INPUT_BUTTON_BUTTON5(s) boolean(input_buttons & BIT(4))
- #define PHYS_INPUT_BUTTON_BUTTON6(s) boolean(input_buttons & BIT(5))
- #define PHYS_INPUT_BUTTON_BUTTON7(s) boolean(input_buttons & BIT(6))
- #define PHYS_INPUT_BUTTON_BUTTON8(s) boolean(input_buttons & BIT(7))
- #define PHYS_INPUT_BUTTON_BUTTON_USE(s) boolean(input_buttons & BIT(8))
- #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) boolean(input_buttons & BIT(9))
- #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) boolean(input_buttons & BIT(10))
- #define PHYS_INPUT_BUTTON_BUTTON9(s) boolean(input_buttons & BIT(11))
- #define PHYS_INPUT_BUTTON_BUTTON10(s) boolean(input_buttons & BIT(12))
- #define PHYS_INPUT_BUTTON_BUTTON11(s) boolean(input_buttons & BIT(13))
- #define PHYS_INPUT_BUTTON_BUTTON12(s) boolean(input_buttons & BIT(14))
- #define PHYS_INPUT_BUTTON_BUTTON13(s) boolean(input_buttons & BIT(15))
- #define PHYS_INPUT_BUTTON_BUTTON14(s) boolean(input_buttons & BIT(16))
- #define PHYS_INPUT_BUTTON_BUTTON15(s) boolean(input_buttons & BIT(17))
- #define PHYS_INPUT_BUTTON_BUTTON16(s) boolean(input_buttons & BIT(18))
-
- #define PHYS_DEAD(s) ((s).csqcmodel_isdead)
-
- #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE))
- #define GAMEPLAYFIX_NOGRAVITYONGROUND (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND))
- #define GAMEPLAYFIX_Q2AIRACCELERATE (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE))
-
- #define IS_DUCKED(s) (boolean((s).flags & FL_DUCKED))
- #define SET_DUCKED(s) ((s).flags |= FL_DUCKED)
- #define UNSET_DUCKED(s) ((s).flags &= ~FL_DUCKED)
-
- #define PHYS_JUMPSPEEDCAP_MIN autocvar_cl_jumpspeedcap_min
- #define PHYS_JUMPSPEEDCAP_MAX autocvar_cl_jumpspeedcap_max
-
- #define PHYS_CL_TRACK_CANJUMP(s) STAT(MOVEVARS_CL_TRACK_CANJUMP, s)
- // FIXME: 0 doesn't mean zero gravity
- #define PHYS_ENTGRAVITY(s) STAT(MOVEVARS_ENTGRAVITY, s)
-
-#elif defined(SVQC)
-
- bool Physics_Valid(string thecvar);
-
- .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
- .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED);
-
- /** Not real stats */
- .string jumpspeedcap_min;
- .string jumpspeedcap_max;
-
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
- #define PHYS_GRAVITY(s) autocvar_sv_gravity
-
- #define TICRATE sys_frametime
-
- #define PHYS_INPUT_ANGLES(s) ((s).v_angle)
- #define PHYS_WORLD_ANGLES(s) ((s).angles)
-
- #define PHYS_INPUT_TIMELENGTH frametime
- #define PHYS_INPUT_FRAMETIME sys_frametime
-
- #define PHYS_INPUT_MOVEVALUES(s) ((s).movement)
-
- #define PHYS_INPUT_BUTTON_BUTTON1(s) ((s).button0)
- #define PHYS_INPUT_BUTTON_BUTTON2(s) ((s).button2)
- #define PHYS_INPUT_BUTTON_BUTTON3(s) ((s).button3)
- #define PHYS_INPUT_BUTTON_BUTTON4(s) ((s).button4)
- #define PHYS_INPUT_BUTTON_BUTTON5(s) ((s).button5)
- #define PHYS_INPUT_BUTTON_BUTTON6(s) ((s).button6)
- #define PHYS_INPUT_BUTTON_BUTTON7(s) ((s).button7)
- #define PHYS_INPUT_BUTTON_BUTTON8(s) ((s).button8)
- #define PHYS_INPUT_BUTTON_BUTTON_USE(s) ((s).buttonuse)
- #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) ((s).buttonchat)
- #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) ((s).cursor_active)
- #define PHYS_INPUT_BUTTON_BUTTON9(s) ((s).button9)
- #define PHYS_INPUT_BUTTON_BUTTON10(s) ((s).button10)
- #define PHYS_INPUT_BUTTON_BUTTON11(s) ((s).button11)
- #define PHYS_INPUT_BUTTON_BUTTON12(s) ((s).button12)
- #define PHYS_INPUT_BUTTON_BUTTON13(s) ((s).button13)
- #define PHYS_INPUT_BUTTON_BUTTON14(s) ((s).button14)
- #define PHYS_INPUT_BUTTON_BUTTON15(s) ((s).button15)
- #define PHYS_INPUT_BUTTON_BUTTON16(s) ((s).button16)
-
- #define PHYS_DEAD(s) ((s).deadflag != DEAD_NO)
-
- #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
- #define GAMEPLAYFIX_NOGRAVITYONGROUND autocvar_sv_gameplayfix_nogravityonground
- #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate
-
- #define IS_DUCKED(s) ((s).crouch)
- #define SET_DUCKED(s) ((s).crouch = true)
- #define UNSET_DUCKED(s) ((s).crouch = false)
-
- #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min
- #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max
-
- #define PHYS_CL_TRACK_CANJUMP(s) ((s).cvar_cl_movement_track_canjump)
- #define PHYS_ENTGRAVITY(s) ((s).gravity)
-
-#endif
-
-REGISTER_NET_C2S(setpause)
-#ifdef CSQC
-void unpause_update()
-{
- static bool waspaused;
- bool ispaused = PHYS_INPUT_BUTTON_CHAT(this);
- if (ispaused == waspaused) return;
- waspaused = ispaused;
- // if (!serverispaused) return; // TODO: find out somehow
- if (ispaused) return; // ignore setting pause, server will get those presses anyway, but it won't get releases
- int channel = MSG_C2S;
- WriteHeader(channel, setpause);
- WriteByte(channel, ispaused);
-}
-#endif
-#ifdef SVQC
-NET_HANDLE(setpause, bool)
-{
- bool ispaused = boolean(ReadByte());
- PHYS_INPUT_BUTTON_CHAT(sender) = ispaused;
- return true;
-}
-#endif
-
-#endif
--- /dev/null
+#include "player.qc"
+#include "movelib.qc"
+#include "movetypes/include.qc"
--- /dev/null
+#include "movelib.qh"
+
+#ifdef SVQC
+.vector moveto;
+
+/**
+ Simulate drag
+ self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
+**/
+vector movelib_dragvec(float drag, float exp_)
+{SELFPARAM();
+ float lspeed,ldrag;
+
+ lspeed = vlen(self.velocity);
+ ldrag = lspeed * drag;
+ ldrag = ldrag * (drag * exp_);
+ ldrag = 1 - (ldrag / lspeed);
+
+ return self.velocity * ldrag;
+}
+
+/**
+ Simulate drag
+ self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
+**/
+float movelib_dragflt(float fspeed,float drag,float exp_)
+{
+ float ldrag;
+
+ ldrag = fspeed * drag;
+ ldrag = ldrag * ldrag * exp_;
+ ldrag = 1 - (ldrag / fspeed);
+
+ return ldrag;
+}
+
+/**
+ Do a inertia simulation based on velocity.
+ Basicaly, this allows you to simulate loss of steering with higher speed.
+ self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
+**/
+vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax)
+{SELFPARAM();
+ float influense;
+
+ influense = vlen(self.velocity) * (1 / vel_max);
+
+ influense = bound(newmin,influense,oldmax);
+
+ return (vel_new * (1 - influense)) + (self.velocity * influense);
+}
+
+vector movelib_inertmove(vector new_vel,float new_bias)
+{SELFPARAM();
+ return new_vel * new_bias + self.velocity * (1-new_bias);
+}
+
+void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce)
+{SELFPARAM();
+ float deltatime;
+ float acceleration;
+ float mspeed;
+ vector breakvec;
+
+ deltatime = time - self.movelib_lastupdate;
+ if (deltatime > 0.15) deltatime = 0;
+ self.movelib_lastupdate = time;
+ if (!deltatime) return;
+
+ mspeed = vlen(self.velocity);
+
+ if (theMass)
+ acceleration = vlen(force) / theMass;
+ else
+ acceleration = vlen(force);
+
+ if (self.flags & FL_ONGROUND)
+ {
+ if (breakforce)
+ {
+ breakvec = (normalize(self.velocity) * (breakforce / theMass) * deltatime);
+ self.velocity = self.velocity - breakvec;
+ }
+
+ self.velocity = self.velocity + force * (acceleration * deltatime);
+ }
+
+ if (drag)
+ self.velocity = movelib_dragvec(drag, 1);
+
+ if (self.waterlevel > 1)
+ {
+ self.velocity = self.velocity + force * (acceleration * deltatime);
+ self.velocity = self.velocity + '0 0 0.05' * autocvar_sv_gravity * deltatime;
+ }
+ else
+ self.velocity = self.velocity + '0 0 -1' * autocvar_sv_gravity * deltatime;
+
+ mspeed = vlen(self.velocity);
+
+ if (max_velocity)
+ if (mspeed > max_velocity)
+ self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity;
+}
+
+/*
+.float mass;
+.float side_friction;
+.float ground_friction;
+.float air_friction;
+.float water_friction;
+.float buoyancy;
+float movelib_deltatime;
+
+void movelib_startupdate()
+{
+ movelib_deltatime = time - self.movelib_lastupdate;
+
+ if (movelib_deltatime > 0.5)
+ movelib_deltatime = 0;
+
+ self.movelib_lastupdate = time;
+}
+
+void movelib_update(vector dir,float force)
+{
+ vector acceleration;
+ float old_speed;
+ float ffriction,v_z;
+
+ vector breakvec;
+ vector old_dir;
+ vector ggravity;
+ vector old;
+
+ if(!movelib_deltatime)
+ return;
+ v_z = self.velocity_z;
+ old_speed = vlen(self.velocity);
+ old_dir = normalize(self.velocity);
+
+ //ggravity = (autocvar_sv_gravity / self.mass) * '0 0 100';
+ acceleration = (force / self.mass) * dir;
+ //acceleration -= old_dir * (old_speed / self.mass);
+ acceleration -= ggravity;
+
+ if(self.waterlevel > 1)
+ {
+ ffriction = self.water_friction;
+ acceleration += self.buoyancy * '0 0 1';
+ }
+ else
+ if(self.flags & FL_ONGROUND)
+ ffriction = self.ground_friction;
+ else
+ ffriction = self.air_friction;
+
+ acceleration *= ffriction;
+ //self.velocity = self.velocity * (ffriction * movelib_deltatime);
+ self.velocity += acceleration * movelib_deltatime;
+ self.velocity_z = v_z;
+
+}
+*/
+
+void movelib_beak_simple(float force)
+{SELFPARAM();
+ float mspeed;
+ vector mdir;
+ float vz;
+
+ mspeed = max(0,vlen(self.velocity) - force);
+ mdir = normalize(self.velocity);
+ vz = self.velocity.z;
+ self.velocity = mdir * mspeed;
+ self.velocity_z = vz;
+}
+
+/**
+Pitches and rolls the entity to match the gound.
+Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
+**/
+#endif
+
+void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max)
+{SELFPARAM();
+ vector a, b, c, d, e, r, push_angle, ahead, side;
+
+ push_angle.y = 0;
+ r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up);
+ e = v_up * spring_length;
+
+ // Put springs slightly inside bbox
+ ahead = v_forward * (self.maxs.x * 0.8);
+ side = v_right * (self.maxs.y * 0.8);
+
+ a = r + ahead + side;
+ b = r + ahead - side;
+ c = r - ahead + side;
+ d = r - ahead - side;
+
+ traceline(a, a - e,MOVE_NORMAL,self);
+ a.z = (1 - trace_fraction);
+ r = trace_endpos;
+
+ traceline(b, b - e,MOVE_NORMAL,self);
+ b.z = (1 - trace_fraction);
+ r += trace_endpos;
+
+ traceline(c, c - e,MOVE_NORMAL,self);
+ c.z = (1 - trace_fraction);
+ r += trace_endpos;
+
+ traceline(d, d - e,MOVE_NORMAL,self);
+ d.z = (1 - trace_fraction);
+ r += trace_endpos;
+
+ a.x = r.z;
+ r = self.origin;
+ r.z = r.z;
+
+ push_angle.x = (a.z - c.z) * _max;
+ push_angle.x += (b.z - d.z) * _max;
+
+ push_angle.z = (b.z - a.z) * _max;
+ push_angle.z += (d.z - c.z) * _max;
+
+ //self.angles_x += push_angle_x * 0.95;
+ //self.angles_z += push_angle_z * 0.95;
+
+ self.angles_x = ((1-blendrate) * self.angles.x) + (push_angle.x * blendrate);
+ self.angles_z = ((1-blendrate) * self.angles.z) + (push_angle.z * blendrate);
+
+ //a = self.origin;
+ setorigin(self,r);
+}
+
--- /dev/null
+#ifndef MOVELIB_H
+#define MOVELIB_H
+
+#ifdef SVQC
+.vector moveto;
+
+/**
+ Simulate drag
+ self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
+**/
+vector movelib_dragvec(float drag, float exp_);
+
+/**
+ Simulate drag
+ self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
+**/
+float movelib_dragflt(float fspeed,float drag,float exp_);
+
+/**
+ Do a inertia simulation based on velocity.
+ Basicaly, this allows you to simulate loss of steering with higher speed.
+ self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
+**/
+vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax);
+
+vector movelib_inertmove(vector new_vel,float new_bias);
+
+.float movelib_lastupdate;
+void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce);
+
+/*
+void movelib_move_simple(vector newdir,float velo,float blendrate)
+{
+ self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo;
+}
+*/
+#define movelib_move_simple(newdir,velo,blendrate) \
+ self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo
+
+#define movelib_move_simple_gravity(newdir,velo,blendrate) \
+ if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate)
+
+void movelib_beak_simple(float force);
+
+/**
+Pitches and rolls the entity to match the gound.
+Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
+**/
+#endif
+
+void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max);
+
+#endif
--- /dev/null
+void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow
+{
+ entity e = this.move_aiment; // TODO: networking?
+
+ // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
+ if(this.move_angles == this.move_punchangle)
+ {
+ this.move_origin = e.move_origin + this.view_ofs;
+ }
+ else
+ {
+ vector ang, v;
+ ang_x = -this.move_punchangle_x;
+ ang_y = this.move_punchangle_y;
+ ang_z = this.move_punchangle_z;
+ makevectors(ang);
+ v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x;
+ v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y;
+ v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z;
+ ang_x = -e.move_angles_x;
+ ang_y = e.move_angles_y;
+ ang_z = e.move_angles_z;
+ makevectors(ang);
+ this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
+ this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
+ this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
+ }
+
+ this.move_angles = e.move_angles + this.v_angle;
+ _Movetype_LinkEdict(this, false);
+}
--- /dev/null
+#include "push.qc"
+#include "toss.qc"
+#include "walk.qc"
+#include "step.qc"
+#include "follow.qc"
+
+#include "movetypes.qc"
--- /dev/null
+#ifndef MOVETYPES_INCLUDE_H
+#define MOVETYPES_INCLUDE_H
+
+#include "push.qh"
+#include "toss.qh"
+#include "walk.qh"
+
+#endif
--- /dev/null
+#include "include.qh"
+#include "../physics.qh"
+
+#if defined(CSQC)
+ #include "../../client/defs.qh"
+ #include "../stats.qh"
+ #include "../util.qh"
+ #include "movetypes.qh"
+ #include "../../lib/csqcmodel/common.qh"
+ #include "../../server/t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../server/autocvars.qh"
+#endif
+
+void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction
+{
+ /*float d, i;
+ vector into, side;
+ makevectors(this.v_angle);
+ d = (stepnormal * v_forward) + 0.5;
+
+ if(d < 0)
+ {
+ i = (stepnormal * this.move_velocity);
+ into = i * stepnormal;
+ side = this.move_velocity - into;
+ this.move_velocity_x = side.x * (1 * d);
+ this.move_velocity_y = side.y * (1 * d);
+ }*/
+}
+
+vector planes[MAX_CLIP_PLANES];
+int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove
+{
+ int blocked = 0, bumpcount;
+ int i, j, numplanes = 0;
+ float time_left = dt, grav = 0;
+ vector push;
+ vector primal_velocity, original_velocity, restore_velocity;
+
+ for(i = 0; i < MAX_CLIP_PLANES; ++i)
+ planes[i] = '0 0 0';
+
+ if(applygravity)
+ {
+ this.move_didgravity = 1;
+ grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this);
+
+ if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+ {
+ if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.move_velocity_z -= grav * 0.5;
+ else
+ this.move_velocity_z -= grav;
+ }
+ }
+
+ original_velocity = primal_velocity = restore_velocity = this.move_velocity;
+
+ for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
+ {
+ if(this.move_velocity == '0 0 0')
+ break;
+
+ push = this.move_velocity * time_left;
+ _Movetype_PushEntity(this, push, true);
+ if(trace_startsolid)
+ {
+ // we got teleported by a touch function
+ // let's abort the move
+ blocked |= 8;
+ break;
+ }
+
+ // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
+ // abort move if we're stuck in the world (and didn't make it out)
+ if(trace_startsolid && trace_allsolid)
+ {
+ this.move_velocity = restore_velocity;
+ return 3;
+ }
+
+ if(trace_fraction == 1)
+ break;
+
+ float my_trace_fraction = trace_fraction;
+ vector my_trace_plane_normal = trace_plane_normal;
+
+ if(trace_plane_normal.z)
+ {
+ if(trace_plane_normal.z > 0.7)
+ {
+ // floor
+ blocked |= 1;
+
+ if(!trace_ent)
+ {
+ //dprint("_Movetype_FlyMove: !trace_ent\n");
+ trace_ent = world;
+ }
+
+ this.move_flags |= FL_ONGROUND;
+ this.move_groundentity = trace_ent;
+ }
+ }
+ else if(stepheight)
+ {
+ // step - handle it immediately
+ vector org = this.move_origin;
+ vector steppush = '0 0 1' * stepheight;
+
+ _Movetype_PushEntity(this, steppush, true);
+ if(trace_startsolid)
+ {
+ blocked |= 8;
+ break;
+ }
+ _Movetype_PushEntity(this, push, true);
+ if(trace_startsolid)
+ {
+ blocked |= 8;
+ break;
+ }
+ float trace2_fraction = trace_fraction;
+ steppush = '0 0 1' * (org_z - this.move_origin_z);
+ _Movetype_PushEntity(this, steppush, true);
+ if(trace_startsolid)
+ {
+ blocked |= 8;
+ break;
+ }
+
+ // accept the new position if it made some progress...
+ if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
+ {
+ trace_endpos = this.move_origin;
+ time_left *= 1 - trace2_fraction;
+ numplanes = 0;
+ continue;
+ }
+ else
+ this.move_origin = org;
+ }
+ else
+ {
+ // step - return it to caller
+ blocked |= 2;
+ // save the trace for player extrafriction
+ if(stepnormal)
+ stepnormal = trace_plane_normal;
+ }
+
+ if(my_trace_fraction >= 0.001)
+ {
+ // actually covered some distance
+ original_velocity = this.move_velocity;
+ numplanes = 0;
+ }
+
+ time_left *= 1 - my_trace_fraction;
+
+ // clipped to another plane
+ if(numplanes >= MAX_CLIP_PLANES)
+ {
+ // this shouldn't really happen
+ this.move_velocity = '0 0 0';
+ blocked = 3;
+ break;
+ }
+
+ planes[numplanes] = my_trace_plane_normal;
+ numplanes++;
+
+ // modify original_velocity so it parallels all of the clip planes
+ vector new_velocity = '0 0 0';
+ for (i = 0;i < numplanes;i++)
+ {
+ new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
+ for (j = 0;j < numplanes;j++)
+ {
+ if(j != i)
+ {
+ // not ok
+ if((new_velocity * planes[j]) < 0)
+ break;
+ }
+ }
+ if(j == numplanes)
+ break;
+ }
+
+ if(i != numplanes)
+ {
+ // go along this plane
+ this.move_velocity = new_velocity;
+ }
+ else
+ {
+ // go along the crease
+ if(numplanes != 2)
+ {
+ this.move_velocity = '0 0 0';
+ blocked = 7;
+ break;
+ }
+ vector dir = cross(planes[0], planes[1]);
+ // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
+ float ilength = sqrt((dir * dir));
+ if(ilength)
+ ilength = 1.0 / ilength;
+ dir.x *= ilength;
+ dir.y *= ilength;
+ dir.z *= ilength;
+ float d = (dir * this.move_velocity);
+ this.move_velocity = dir * d;
+ }
+
+ // if current velocity is against the original velocity,
+ // stop dead to avoid tiny occilations in sloping corners
+ if((this.move_velocity * primal_velocity) <= 0)
+ {
+ this.move_velocity = '0 0 0';
+ break;
+ }
+ }
+
+ // LordHavoc: this came from QW and allows you to get out of water more easily
+ if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
+ this.move_velocity = primal_velocity;
+
+ if(applygravity)
+ {
+ if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+ {
+ if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.move_velocity_z -= grav * 0.5f;
+ }
+ }
+
+ return blocked;
+}
+
+void _Movetype_CheckVelocity(entity this) // SV_CheckVelocity
+{
+ // if(vlen(this.move_velocity) < 0.0001)
+ // this.move_velocity = '0 0 0';
+}
+
+bool _Movetype_CheckWater(entity this) // SV_CheckWater
+{
+ vector point = this.move_origin;
+ point.z += this.mins.z + 1;
+
+ int nativecontents = pointcontents(point);
+ if(this.move_watertype && this.move_watertype != nativecontents)
+ {
+ // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents);
+ if(this.contentstransition)
+ this.contentstransition(this.move_watertype, nativecontents);
+ }
+
+ this.move_waterlevel = WATERLEVEL_NONE;
+ this.move_watertype = CONTENT_EMPTY;
+
+ int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
+ if(supercontents & DPCONTENTS_LIQUIDSMASK)
+ {
+ this.move_watertype = nativecontents;
+ this.move_waterlevel = WATERLEVEL_WETFEET;
+ point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5;
+ if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+ {
+ this.move_waterlevel = WATERLEVEL_SWIMMING;
+ point.z = this.move_origin.z + this.view_ofs.z;
+ if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
+ this.move_waterlevel = WATERLEVEL_SUBMERGED;
+ }
+ }
+
+ return this.move_waterlevel > 1;
+}
+
+void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
+{
+ int contents = pointcontents(ent.move_origin);
+
+ if(!ent.move_watertype)
+ {
+ // just spawned here
+ if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
+ {
+ ent.move_watertype = contents;
+ ent.move_waterlevel = 1;
+ return;
+ }
+ }
+ else if(ent.move_watertype != contents)
+ {
+ // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents);
+ if(ent.contentstransition)
+ ent.contentstransition(ent.move_watertype, contents);
+ }
+
+ if(contents <= CONTENT_WATER)
+ {
+ ent.move_watertype = contents;
+ ent.move_waterlevel = 1;
+ }
+ else
+ {
+ ent.move_watertype = CONTENT_EMPTY;
+ ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
+ }
+}
+
+void _Movetype_Impact(entity this, entity oth) // SV_Impact
+{
+ entity oldother = other;
+
+ if(this.move_touch)
+ {
+ other = oth;
+
+ WITH(entity, self, this, this.move_touch());
+
+ other = oldother;
+ }
+
+ if(oth.move_touch)
+ {
+ other = this;
+
+ WITH(entity, self, oth, oth.move_touch());
+
+ other = oldother;
+ }
+}
+
+void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGrid
+{
+ entity oldother = other;
+
+ for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain)
+ {
+ if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY)
+ if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax))
+ {
+ other = this;
+
+ trace_allsolid = false;
+ trace_startsolid = false;
+ trace_fraction = 1;
+ trace_inwater = false;
+ trace_inopen = true;
+ trace_endpos = e.move_origin;
+ trace_plane_normal = '0 0 1';
+ trace_plane_dist = 0;
+ trace_ent = this;
+
+ WITH(entity, self, e, e.move_touch());
+ }
+ }
+
+ other = oldother;
+}
+
+void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict
+{
+ vector mi, ma;
+ if(this.solid == SOLID_BSP)
+ {
+ // TODO set the absolute bbox
+ mi = this.mins;
+ ma = this.maxs;
+ }
+ else
+ {
+ mi = this.mins;
+ ma = this.maxs;
+ }
+ mi += this.move_origin;
+ ma += this.move_origin;
+
+ if(this.move_flags & FL_ITEM)
+ {
+ mi.x -= 15;
+ mi.y -= 15;
+ mi.z -= 1;
+ ma.x += 15;
+ ma.y += 15;
+ ma.z += 1;
+ }
+ else
+ {
+ mi.x -= 1;
+ mi.y -= 1;
+ mi.z -= 1;
+ ma.x += 1;
+ ma.y += 1;
+ ma.z += 1;
+ }
+
+ this.absmin = mi;
+ this.absmax = ma;
+
+ if(touch_triggers)
+ _Movetype_LinkEdict_TouchAreaGrid(this);
+}
+
+bool _Movetype_TestEntityPosition(entity this, vector ofs) // SV_TestEntityPosition
+{
+// vector org = this.move_origin + ofs;
+
+ int cont = this.dphitcontentsmask;
+ this.dphitcontentsmask = DPCONTENTS_SOLID;
+ tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
+ this.dphitcontentsmask = cont;
+
+ if(trace_startsolid)
+ return true;
+
+ if(vlen(trace_endpos - this.move_origin) > 0.0001)
+ this.move_origin = trace_endpos;
+ return false;
+}
+
+bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity
+{
+ if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true;
+ if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success;
+ if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success;
+ for (int i = 1; i <= 17; ++i)
+ {
+ if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success;
+ if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success;
+ }
+ LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
+ etof(this), this.classname, vtos(this.move_origin));
+ return false;
+ : success;
+ LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
+ etof(this), this.classname, vtos(this.move_origin));
+ _Movetype_LinkEdict(this, true);
+ return true;
+}
+
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
+{
+ vel -= ((vel * norm) * norm) * f;
+
+ if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
+ if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
+ if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
+
+ return vel;
+}
+
+void _Movetype_PushEntityTrace(entity this, vector push)
+{
+ vector end = this.move_origin + push;
+ int type;
+ if(this.move_nomonsters)
+ type = max(0, this.move_nomonsters);
+ else if(this.move_movetype == MOVETYPE_FLYMISSILE)
+ type = MOVE_MISSILE;
+ else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
+ type = MOVE_WORLDONLY;
+ else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
+ type = MOVE_NOMONSTERS;
+ else
+ type = MOVE_NORMAL;
+
+ tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
+}
+
+float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity
+{
+ _Movetype_PushEntityTrace(this, push);
+
+ if(trace_startsolid && failonstartsolid)
+ return trace_fraction;
+
+ this.move_origin = trace_endpos;
+
+ if(trace_fraction < 1)
+ if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
+ _Movetype_Impact(this, trace_ent);
+
+ return trace_fraction;
+}
+
+
+.float ltime;
+.void() blocked;
+// matrix version of makevectors, sets v_forward, v_right and v_up
+void makevectors_matrix(vector myangles) // AngleVectorsFLU
+{
+ v_forward = v_right = v_up = '0 0 0';
+
+ float y = myangles.y * (M_PI * 2 / 360);
+ float sy = sin(y);
+ float cy = cos(y);
+ float p = myangles.x * (M_PI * 2 / 360);
+ float sp = sin(p);
+ float cp = cos(p);
+ if(v_forward)
+ {
+ v_forward.x = cp * cy;
+ v_forward.y = cp * sy;
+ v_forward.z = -sp;
+ }
+ if(v_right || v_up)
+ {
+ if(myangles.z)
+ {
+ float r = myangles.z * (M_PI * 2 / 360);
+ float sr = sin(r);
+ float cr = cos(r);
+ if(v_right)
+ {
+ v_right.x = sr * sp * cy + cr * -sy;
+ v_right.y = sr * sp * sy + cr * cy;
+ v_right.z = sr * cp;
+ }
+ if(v_up)
+ {
+ v_up.x = cr * sp * cy + -sr * -sy;
+ v_up.y = cr * sp * sy + -sr * cy;
+ v_up.z = cr * cp;
+ }
+ }
+ else
+ {
+ if(v_right)
+ {
+ v_right.x = -sy;
+ v_right.y = cy;
+ v_right.z = 0;
+ }
+ if(v_up)
+ {
+ v_up.x = sp * cy;
+ v_up.y = sp * sy;
+ v_up.z = cp;
+ }
+ }
+ }
+}
+
+void _Movetype_Physics_Frame(entity this, float movedt)
+{
+ this.move_didgravity = -1;
+ switch (this.move_movetype)
+ {
+ case MOVETYPE_PUSH:
+ case MOVETYPE_FAKEPUSH:
+ _Movetype_Physics_Pusher(this, movedt);
+ break;
+ case MOVETYPE_NONE:
+ break;
+ case MOVETYPE_FOLLOW:
+ _Movetype_Physics_Follow(this);
+ break;
+ case MOVETYPE_NOCLIP:
+ _Movetype_CheckWater(this);
+ this.move_origin = this.move_origin + TICRATE * this.move_velocity;
+ this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+ _Movetype_LinkEdict(this, false);
+ break;
+ case MOVETYPE_STEP:
+ _Movetype_Physics_Step(this, movedt);
+ break;
+ case MOVETYPE_WALK:
+ _Movetype_Physics_Walk(this, movedt);
+ break;
+ case MOVETYPE_TOSS:
+ case MOVETYPE_BOUNCE:
+ case MOVETYPE_BOUNCEMISSILE:
+ case MOVETYPE_FLYMISSILE:
+ case MOVETYPE_FLY:
+ case MOVETYPE_FLY_WORLDONLY:
+ _Movetype_Physics_Toss(this, movedt);
+ _Movetype_LinkEdict(this, true);
+ break;
+ case MOVETYPE_PHYSICS:
+ break;
+ }
+}
+
+void _Movetype_Physics_ClientFrame(entity this, float movedt)
+{
+ this.move_didgravity = -1;
+ switch (this.move_movetype)
+ {
+ case MOVETYPE_PUSH:
+ case MOVETYPE_FAKEPUSH:
+ _Movetype_Physics_Pusher(this, movedt);
+ break;
+ case MOVETYPE_NONE:
+ break;
+ case MOVETYPE_FOLLOW:
+ _Movetype_Physics_Follow(this);
+ break;
+ case MOVETYPE_NOCLIP:
+ _Movetype_CheckWater(this);
+ this.move_origin = this.move_origin + TICRATE * this.move_velocity;
+ this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+ _Movetype_LinkEdict(this, false);
+ break;
+ case MOVETYPE_STEP:
+ _Movetype_Physics_Step(this, movedt);
+ break;
+ case MOVETYPE_WALK:
+ case MOVETYPE_FLY:
+ case MOVETYPE_FLY_WORLDONLY:
+ _Movetype_Physics_Walk(this, movedt);
+ break;
+ case MOVETYPE_TOSS:
+ case MOVETYPE_BOUNCE:
+ case MOVETYPE_BOUNCEMISSILE:
+ case MOVETYPE_FLYMISSILE:
+ _Movetype_Physics_Toss(this, movedt);
+ break;
+ case MOVETYPE_PHYSICS:
+ break;
+ }
+}
+
+void Movetype_Physics_NoMatchServer(entity this) // optimized
+{
+ float movedt = time - this.move_time;
+ this.move_time = time;
+
+ _Movetype_Physics_Frame(this, movedt);
+ if(wasfreed(this))
+ return;
+
+ this.avelocity = this.move_avelocity;
+ this.velocity = this.move_velocity;
+ this.angles = this.move_angles;
+ setorigin(this, this.move_origin);
+}
+
+void Movetype_Physics_MatchServer(entity this, bool sloppy)
+{
+ Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
+}
+
+void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Physics_Entity
+{
+ if(tr <= 0)
+ {
+ Movetype_Physics_NoMatchServer(this);
+ return;
+ }
+
+ float dt = time - this.move_time;
+
+ int n = max(0, floor(dt / tr));
+ dt -= n * tr;
+ this.move_time += n * tr;
+
+ if(!this.move_didgravity)
+ this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
+
+ for (int i = 0; i < n; ++i)
+ {
+ _Movetype_Physics_Frame(this, tr);
+ if(wasfreed(this))
+ return;
+ }
+
+ this.avelocity = this.move_avelocity;
+
+ if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
+ {
+ // now continue the move from move_time to time
+ this.velocity = this.move_velocity;
+
+ if(this.move_didgravity > 0)
+ {
+ this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
+ * dt
+ * (this.gravity ? this.gravity : 1)
+ * PHYS_GRAVITY(this);
+ }
+
+ this.angles = this.move_angles + dt * this.avelocity;
+
+ if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
+ {
+ setorigin(this, this.move_origin + dt * this.velocity);
+ }
+ else
+ {
+ _Movetype_PushEntityTrace(this, dt * this.velocity);
+ if(!trace_startsolid)
+ setorigin(this, trace_endpos);
+ }
+
+ if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+ }
+ else
+ {
+ this.velocity = this.move_velocity;
+ this.angles = this.move_angles;
+ setorigin(this, this.move_origin);
+ }
+}
--- /dev/null
+#ifndef MOVETYPES_H
+#define MOVETYPES_H
+
+.float move_ltime;
+.void()move_think;
+.float move_nextthink;
+.void()move_blocked;
+
+.float move_movetype;
+.float move_time;
+.vector move_origin;
+.vector move_angles;
+.vector move_velocity;
+.vector move_avelocity;
+.int move_flags;
+.int move_watertype;
+.int move_waterlevel;
+.void()move_touch;
+.void(float, float)contentstransition;
+.float move_bounce_factor;
+.float move_bounce_stopspeed;
+.float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant
+
+.entity move_aiment;
+.vector move_punchangle;
+
+// should match sv_gameplayfix_fixedcheckwatertransition
+float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1;
+
+.entity move_groundentity; // FIXME add move_groundnetworkentity?
+.float move_suspendedinair;
+.float move_didgravity;
+
+void _Movetype_WallFriction(entity this, vector stepnormal);
+int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight);
+void _Movetype_CheckVelocity(entity this);
+void _Movetype_CheckWaterTransition(entity ent);
+float _Movetype_CheckWater(entity ent);
+void _Movetype_LinkEdict_TouchAreaGrid(entity this);
+void _Movetype_LinkEdict(entity this, float touch_triggers);
+float _Movetype_TestEntityPosition(entity this, vector ofs);
+float _Movetype_UnstickEntity(entity this);
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f);
+void _Movetype_PushEntityTrace(entity this, vector push);
+float _Movetype_PushEntity(entity this, vector push, float failonstartsolid);
+void makevectors_matrix(vector myangles);
+
+void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy);
+void Movetype_Physics_MatchServer(entity this, bool sloppy);
+void Movetype_Physics_NoMatchServer(entity this);
+void _Movetype_LinkEdict(entity this, float touch_triggers);
+void _Movetype_LinkEdict_TouchAreaGrid(entity this);
+
+float _Movetype_UnstickEntity(entity this);
+
+const int MAX_CLIP_PLANES = 5;
+
+#ifdef CSQC
+const int MOVETYPE_NONE = 0;
+const int MOVETYPE_ANGLENOCLIP = 1;
+const int MOVETYPE_ANGLECLIP = 2;
+const int MOVETYPE_WALK = 3;
+const int MOVETYPE_STEP = 4;
+const int MOVETYPE_FLY = 5;
+const int MOVETYPE_TOSS = 6;
+const int MOVETYPE_PUSH = 7;
+const int MOVETYPE_NOCLIP = 8;
+const int MOVETYPE_FLYMISSILE = 9;
+const int MOVETYPE_BOUNCE = 10;
+const int MOVETYPE_BOUNCEMISSILE = 11; // Like bounce but doesn't lose speed on bouncing
+const int MOVETYPE_FOLLOW = 12;
+const int MOVETYPE_PHYSICS = 32;
+const int MOVETYPE_FLY_WORLDONLY = 33;
+
+const int FL_ITEM = 256;
+const int FL_ONGROUND = 512;
+#endif
+
+const int MOVETYPE_FAKEPUSH = 13;
+
+const int MOVEFLAG_VALID = BIT(23);
+const int MOVEFLAG_Q2AIRACCELERATE = BIT(0);
+const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1);
+const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
+
+#ifdef CSQC
+#define moveflags STAT(MOVEFLAGS)
+#endif
+
+#endif
--- /dev/null
+void _Movetype_PushMove(entity this, float dt) // SV_PushMove
+{
+ if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0')
+ {
+ this.move_ltime += dt;
+ return;
+ }
+
+ switch (this.solid)
+ {
+ // LordHavoc: valid pusher types
+ case SOLID_BSP:
+ case SOLID_BBOX:
+ case SOLID_SLIDEBOX:
+ case SOLID_CORPSE: // LordHavoc: this would be weird...
+ break;
+ // LordHavoc: no collisions
+ case SOLID_NOT:
+ case SOLID_TRIGGER:
+ this.move_origin = this.move_origin + dt * this.move_velocity;
+ this.move_angles = this.move_angles + dt * this.move_avelocity;
+ this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
+ this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
+ this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
+ this.move_ltime += dt;
+ _Movetype_LinkEdict(this, true);
+ return;
+ default:
+ LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid);
+ return;
+ }
+
+ bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0;
+
+ vector move1 = this.move_velocity * dt;
+ vector moveangle = this.move_avelocity * dt;
+
+ makevectors_matrix(-moveangle);
+
+// vector pushorig = this.move_origin;
+// vector pushang = this.move_angles;
+// float pushltime = this.move_ltime;
+
+// move the pusher to its final position
+
+ this.move_origin = this.move_origin + dt * this.move_velocity;
+ this.move_angles = this.move_angles + dt * this.move_avelocity;
+
+ this.move_ltime += dt;
+ _Movetype_LinkEdict(this, true);
+
+ int savesolid = this.solid;
+
+ if (this.move_movetype != MOVETYPE_FAKEPUSH)
+ {
+ for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); check; check = check.chain)
+ {
+ switch (check.move_movetype)
+ {
+ case MOVETYPE_NONE:
+ case MOVETYPE_PUSH:
+ case MOVETYPE_FOLLOW:
+ case MOVETYPE_NOCLIP:
+ case MOVETYPE_FLY_WORLDONLY:
+ continue;
+ default:
+ break;
+ }
+
+ if (check.owner == this)
+ continue;
+
+ if (this.owner == check)
+ continue;
+
+ vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
+ vector move;
+ if (rotated)
+ {
+ vector org = (check.move_origin - this.move_origin) + pivot;
+ vector org2;
+ org2.x = org * v_forward;
+ org2.y = org * v_right;
+ org2.z = org * v_up;
+ move = (org2 - org) + move1;
+ }
+ else
+ {
+ move = move1;
+ }
+
+ // physics objects need better collisions than this code can do
+ if (check.move_movetype == 32) // MOVETYPE_PHYSICS
+ {
+ check.move_origin = check.move_origin + move;
+ WITH(entity, this, check, _Movetype_LinkEdict(this, true));
+ continue;
+ }
+
+ // try moving the contacted entity
+ this.solid = SOLID_NOT;
+ bool flag = false;
+ WITH(entity, this, check, {
+ flag = _Movetype_PushEntity(this, move, true);
+ });
+ if (!flag)
+ {
+ // entity "check" got teleported
+ check.move_angles_y += trace_fraction * moveangle.y;
+ this.solid = savesolid;
+ continue; // pushed enough
+ }
+ // FIXME: turn players specially
+ check.move_angles_y += trace_fraction * moveangle.y;
+ this.solid = savesolid;
+
+ // this trace.fraction < 1 check causes items to fall off of pushers
+ // if they pass under or through a wall
+ // the groundentity check causes items to fall off of ledges
+ if (check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != this))
+ check.move_flags &= ~FL_ONGROUND;
+ }
+ }
+
+ this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
+ this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
+ this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
+}
+
+void _Movetype_Physics_Pusher(entity this, float dt) // SV_Physics_Pusher
+{
+ float oldltime = this.move_ltime;
+ float thinktime = this.move_nextthink;
+ float movetime;
+ if (thinktime < this.move_ltime + dt)
+ {
+ movetime = thinktime - this.move_ltime;
+ if (movetime < 0)
+ movetime = 0;
+ }
+ else
+ {
+ movetime = dt;
+ }
+
+ if (movetime)
+ // advances this.move_ltime if not blocked
+ _Movetype_PushMove(this, movetime);
+
+ if (thinktime > oldltime && thinktime <= this.move_ltime)
+ {
+ this.move_nextthink = 0;
+ this.move_time = time;
+ other = world;
+ WITH(entity, self, this, this.move_think());
+ }
+}
--- /dev/null
+#ifndef MOVETYPE_PUSH_H
+#define MOVETYPE_PUSH_H
+
+void _Movetype_Physics_Pusher(entity this, float dt);
+
+#endif
--- /dev/null
+void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step
+{
+ if(this.move_flags & FL_ONGROUND)
+ {
+ if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
+ {
+ this.move_flags &= ~FL_ONGROUND;
+ _Movetype_CheckVelocity(this);
+ _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
+ _Movetype_LinkEdict(this, true);
+ }
+ }
+ else
+ {
+ _Movetype_CheckVelocity(this);
+ _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
+ _Movetype_LinkEdict(this, true);
+
+ // TODO? movetypesteplandevent
+ }
+
+ _Movetype_CheckWaterTransition(this);
+}
--- /dev/null
+#include "../physics.qh"
+
+void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss
+{
+ if (this.move_flags & FL_ONGROUND)
+ {
+ if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
+ {
+ this.move_flags &= ~FL_ONGROUND;
+ }
+ else if (!this.move_groundentity)
+ {
+ return;
+ }
+ else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
+ {
+ this.move_groundentity = world;
+ return;
+ }
+ }
+
+ this.move_suspendedinair = false;
+
+ _Movetype_CheckVelocity(this);
+
+ if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
+ {
+ this.move_didgravity = 1;
+ this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
+ * dt
+ * (this.gravity ? this.gravity : 1)
+ * PHYS_GRAVITY(this);
+ }
+
+ this.move_angles = this.move_angles + this.move_avelocity * dt;
+
+ float movetime = dt;
+ for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
+ {
+ vector move = this.move_velocity * movetime;
+ _Movetype_PushEntity(this, move, true);
+ if (wasfreed(this))
+ return;
+
+ if (trace_startsolid)
+ {
+ _Movetype_UnstickEntity(this);
+ _Movetype_PushEntity(this, move, false);
+ if (wasfreed(this))
+ return;
+ }
+
+ if (trace_fraction == 1)
+ break;
+
+ movetime *= 1 - min(1, trace_fraction);
+
+ if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
+ {
+ this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0);
+ this.move_flags &= ~FL_ONGROUND;
+ }
+ else if (this.move_movetype == MOVETYPE_BOUNCE)
+ {
+ float bouncefac = this.move_bounce_factor; if (!bouncefac) bouncefac = 0.5;
+ float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
+ bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+
+ this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac);
+
+ float d = trace_plane_normal * this.move_velocity;
+ if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
+ {
+ this.move_flags |= FL_ONGROUND;
+ this.move_groundentity = trace_ent;
+ this.move_velocity = '0 0 0';
+ this.move_avelocity = '0 0 0';
+ }
+ else
+ {
+ this.move_flags &= ~FL_ONGROUND;
+ }
+ }
+ else
+ {
+ this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0);
+ if (trace_plane_normal.z > 0.7)
+ {
+ this.move_flags |= FL_ONGROUND;
+ this.move_groundentity = trace_ent;
+ if (trace_ent.solid == SOLID_BSP)
+ this.move_suspendedinair = true;
+ this.move_velocity = '0 0 0';
+ this.move_avelocity = '0 0 0';
+ }
+ else
+ {
+ this.move_flags &= ~FL_ONGROUND;
+ }
+ }
+
+ // DP revision 8905 (just, WHY...)
+ if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
+ break;
+
+ // DP revision 8918 (WHY...)
+ if (this.move_flags & FL_ONGROUND)
+ break;
+ }
+
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND))
+ this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+
+ _Movetype_CheckWaterTransition(this);
+}
--- /dev/null
+#ifndef MOVETYPE_TOSS_H
+#define MOVETYPE_TOSS_H
+
+void _Movetype_Physics_Toss(entity this, float dt);
+
+#endif
--- /dev/null
+void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove
+{
+ vector stepnormal = '0 0 0';
+
+ // if frametime is 0 (due to client sending the same timestamp twice), don't move
+ if (dt <= 0)
+ return;
+
+ if (GAMEPLAYFIX_UNSTICKPLAYERS(this))
+ _Movetype_UnstickEntity(this);
+
+ bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP));
+
+ _Movetype_CheckVelocity(this);
+
+ // do a regular slide move unless it looks like you ran into a step
+ bool oldonground = (this.move_flags & FL_ONGROUND);
+
+ vector start_origin = this.move_origin;
+ vector start_velocity = this.move_velocity;
+
+ int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0);
+
+ if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(clip & 1))
+ {
+ // only try this if there was no floor in the way in the trace (no,
+ // this check seems to be not REALLY necessary, because if clip & 1,
+ // our trace will hit that thing too)
+ vector upmove = this.move_origin + '0 0 1';
+ vector downmove = this.move_origin - '0 0 1';
+ int type;
+ if (this.move_movetype == MOVETYPE_FLYMISSILE)
+ type = MOVE_MISSILE;
+ else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY)
+ type = MOVE_WORLDONLY;
+ else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
+ type = MOVE_NOMONSTERS;
+ else type = MOVE_NORMAL;
+ tracebox(upmove, this.mins, this.maxs, downmove, type, this);
+ if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
+ clip |= 1; // but we HAVE found a floor
+ }
+
+ // if the move did not hit the ground at any point, we're not on ground
+ if (!(clip & 1))
+ this.move_flags &= ~FL_ONGROUND;
+
+ _Movetype_CheckVelocity(this);
+ _Movetype_LinkEdict(this, true);
+
+ if (clip & 8) // teleport
+ return;
+
+ if (this.move_flags & FL_WATERJUMP)
+ return;
+
+ if (PHYS_NOSTEP(this))
+ return;
+
+ vector originalmove_origin = this.move_origin;
+ vector originalmove_velocity = this.move_velocity;
+ // originalmove_clip = clip;
+ int originalmove_flags = this.move_flags;
+ entity originalmove_groundentity = this.move_groundentity;
+
+ // if move didn't block on a step, return
+ if (clip & 2)
+ {
+ // if move was not trying to move into the step, return
+ if (fabs(start_velocity.x) < 0.03125 && fabs(start_velocity.y) < 0.03125)
+ return;
+
+ if (this.move_movetype != MOVETYPE_FLY)
+ {
+ // return if gibbed by a trigger
+ if (this.move_movetype != MOVETYPE_WALK)
+ return;
+
+ // return if attempting to jump while airborn (unless sv_jumpstep)
+ if (!PHYS_JUMPSTEP(this))
+ if (!oldonground && this.move_waterlevel == 0)
+ return;
+ }
+
+ // try moving up and forward to go up a step
+ // back to start pos
+ this.move_origin = start_origin;
+ this.move_velocity = start_velocity;
+
+ // move up
+ vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this);
+ _Movetype_PushEntity(this, upmove, true);
+ if(wasfreed(this))
+ return;
+ if(trace_startsolid)
+ {
+ // we got teleported when upstepping... must abort the move
+ return;
+ }
+
+ // move forward
+ this.move_velocity_z = 0;
+ clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0);
+ this.move_velocity_z += start_velocity.z;
+ if (clip & 8)
+ {
+ // we got teleported when upstepping... must abort the move
+ // note that z velocity handling may not be what QC expects here, but we cannot help it
+ return;
+ }
+
+ _Movetype_CheckVelocity(this);
+ _Movetype_LinkEdict(this, true);
+
+ // check for stuckness, possibly due to the limited precision of floats
+ // in the clipping hulls
+ if (clip
+ && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125
+ && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125)
+ {
+ // Con_Printf("wall\n");
+ // stepping up didn't make any progress, revert to original move
+ this.move_origin = originalmove_origin;
+ this.move_velocity = originalmove_velocity;
+ // clip = originalmove_clip;
+ this.move_flags = originalmove_flags;
+ this.move_groundentity = originalmove_groundentity;
+ // now try to unstick if needed
+ // clip = SV_TryUnstick (ent, oldvel);
+ return;
+ }
+
+ // Con_Printf("step - ");
+
+ // extra friction based on view angle
+ if ((clip & 2) && PHYS_WALLFRICTION(this))
+ _Movetype_WallFriction(this, stepnormal);
+ }
+ // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
+ else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND))
+ {
+ return;
+ }
+
+ // move down
+ vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt);
+ _Movetype_PushEntity(this, downmove, true);
+ if(wasfreed(this))
+ return;
+
+ if(trace_startsolid)
+ {
+ // we got teleported when downstepping... must abort the move
+ return;
+ }
+
+ if (trace_fraction < 1 && trace_plane_normal.z > 0.7)
+ {
+ // this has been disabled so that you can't jump when you are stepping
+ // up while already jumping (also known as the Quake2 double jump bug)
+ }
+ else
+ {
+ // Con_Printf("slope\n");
+ // if the push down didn't end up on good ground, use the move without
+ // the step up. This happens near wall / slope combinations, and can
+ // cause the player to hop up higher on a slope too steep to climb
+ this.move_origin = originalmove_origin;
+ this.move_velocity = originalmove_velocity;
+ this.move_flags = originalmove_flags;
+ this.move_groundentity = originalmove_groundentity;
+ }
+
+ _Movetype_CheckVelocity(this);
+ _Movetype_LinkEdict(this, true);
+}
--- /dev/null
+#ifndef MOVETYPE_WALK_H
+#define MOVETYPE_WALK_H
+
+void _Movetype_Physics_Walk(entity this, float dt);
+
+#endif
--- /dev/null
+#include "physics.qh"
+#include "triggers/include.qh"
+#include "viewloc.qh"
+
+#ifdef SVQC
+
+#include "../server/miscfunctions.qh"
+#include "triggers/trigger/viewloc.qh"
+
+// client side physics
+bool Physics_Valid(string thecvar)
+{
+ return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
+}
+
+float Physics_ClientOption(entity this, string option)
+{
+ if(Physics_Valid(this.cvar_cl_physics))
+ {
+ string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option);
+ if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
+ return cvar(s);
+ }
+ if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
+ {
+ string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
+ if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
+ return cvar(s);
+ }
+ return cvar(strcat("sv_", option));
+}
+
+void Physics_UpdateStats(entity this, float maxspd_mod)
+{
+ STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod);
+ STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw"))
+ ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod)
+ : 0;
+ STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod;
+ STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking
+
+ // old stats
+ // fix some new settings
+ STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor");
+ STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed");
+ STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed");
+ STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate");
+ STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel");
+ STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction");
+ STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol");
+ STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power");
+ STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty");
+ STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel");
+ STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed");
+ STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel");
+ STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio");
+ STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction");
+ STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate");
+ STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed");
+ STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate");
+ STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate");
+ STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity");
+ STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump");
+}
+#endif
+
+float IsMoveInDirection(vector mv, float ang) // key mix factor
+{
+ if (mv_x == 0 && mv_y == 0)
+ return 0; // avoid division by zero
+ ang -= RAD2DEG * atan2(mv_y, mv_x);
+ ang = remainder(ang, 360) / 45;
+ return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang);
+}
+
+float GeomLerp(float a, float lerp, float b)
+{
+ return a == 0 ? (lerp < 1 ? 0 : b)
+ : b == 0 ? (lerp > 0 ? 0 : a)
+ : a * pow(fabs(b / a), lerp);
+}
+
+#define unstick_offsets(X) \
+/* 1 no nudge (just return the original if this test passes) */ \
+ X(' 0.000 0.000 0.000') \
+/* 6 simple nudges */ \
+ X(' 0.000 0.000 0.125') X('0.000 0.000 -0.125') \
+ X('-0.125 0.000 0.000') X('0.125 0.000 0.000') \
+ X(' 0.000 -0.125 0.000') X('0.000 0.125 0.000') \
+/* 4 diagonal flat nudges */ \
+ X('-0.125 -0.125 0.000') X('0.125 -0.125 0.000') \
+ X('-0.125 0.125 0.000') X('0.125 0.125 0.000') \
+/* 8 diagonal upward nudges */ \
+ X('-0.125 0.000 0.125') X('0.125 0.000 0.125') \
+ X(' 0.000 -0.125 0.125') X('0.000 0.125 0.125') \
+ X('-0.125 -0.125 0.125') X('0.125 -0.125 0.125') \
+ X('-0.125 0.125 0.125') X('0.125 0.125 0.125') \
+/* 8 diagonal downward nudges */ \
+ X('-0.125 0.000 -0.125') X('0.125 0.000 -0.125') \
+ X(' 0.000 -0.125 -0.125') X('0.000 0.125 -0.125') \
+ X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \
+ X('-0.125 0.125 -0.125') X('0.125 0.125 -0.125') \
+/**/
+
+void PM_ClientMovement_Unstick(entity this)
+{
+ #define X(unstick_offset) \
+ { \
+ vector neworigin = unstick_offset + this.origin; \
+ tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \
+ if (!trace_startsolid) \
+ { \
+ setorigin(this, neworigin); \
+ return; \
+ } \
+ }
+ unstick_offsets(X);
+ #undef X
+}
+
+void PM_ClientMovement_UpdateStatus(entity this, bool ground)
+{
+#ifdef CSQC
+ if(!IS_PLAYER(this))
+ return;
+ // make sure player is not stuck
+ if(autocvar_cl_movement == 3)
+ PM_ClientMovement_Unstick(this);
+
+ // set crouched
+ if (PHYS_INPUT_BUTTON_CROUCH(this))
+ {
+ // wants to crouch, this always works
+ if (!IS_DUCKED(this)) SET_DUCKED(this);
+ }
+ else
+ {
+ // wants to stand, if currently crouching we need to check for a low ceiling first
+ if (IS_DUCKED(this))
+ {
+ tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this);
+ if (!trace_startsolid) UNSET_DUCKED(this);
+ }
+ }
+
+ // set onground
+ vector origin1 = this.origin + '0 0 1';
+ vector origin2 = this.origin - '0 0 1';
+
+ if (ground && autocvar_cl_movement == 3)
+ {
+ tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this);
+ if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7)
+ {
+ SET_ONGROUND(this);
+
+ // this code actually "predicts" an impact; so let's clip velocity first
+ this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal;
+ }
+ else
+ UNSET_ONGROUND(this);
+ }
+
+ if(autocvar_cl_movement == 3)
+ {
+ // set watertype/waterlevel
+ origin1 = this.origin;
+ origin1.z += this.mins_z + 1;
+ this.waterlevel = WATERLEVEL_NONE;
+
+ int thepoint = pointcontents(origin1);
+
+ this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
+
+ if (this.watertype)
+ {
+ this.waterlevel = WATERLEVEL_WETFEET;
+ origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
+ thepoint = pointcontents(origin1);
+ if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+ {
+ this.waterlevel = WATERLEVEL_SWIMMING;
+ origin1.z = this.origin.z + 22;
+ thepoint = pointcontents(origin1);
+ if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
+ this.waterlevel = WATERLEVEL_SUBMERGED;
+ }
+ }
+ }
+
+ if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
+ pmove_waterjumptime = 0;
+#endif
+}
+
+void PM_ClientMovement_Move(entity this)
+{
+#ifdef CSQC
+
+ PM_ClientMovement_UpdateStatus(this, false);
+ if(autocvar_cl_movement == 1)
+ return;
+
+ int bump;
+ float t;
+ float f;
+ vector neworigin;
+ vector currentorigin2;
+ vector neworigin2;
+ vector primalvelocity;
+
+ vector trace1_endpos = '0 0 0';
+ vector trace2_endpos = '0 0 0';
+ vector trace3_endpos = '0 0 0';
+ float trace1_fraction = 0;
+ float trace2_fraction = 0;
+ float trace3_fraction = 0;
+ vector trace1_plane_normal = '0 0 0';
+ vector trace2_plane_normal = '0 0 0';
+ vector trace3_plane_normal = '0 0 0';
+
+ primalvelocity = this.velocity;
+ for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++)
+ {
+ neworigin = this.origin + t * this.velocity;
+ tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this);
+ trace1_endpos = trace_endpos;
+ trace1_fraction = trace_fraction;
+ trace1_plane_normal = trace_plane_normal;
+ if(trace1_fraction < 1 && trace1_plane_normal_z == 0)
+ {
+ // may be a step or wall, try stepping up
+ // first move forward at a higher level
+ currentorigin2 = this.origin;
+ currentorigin2_z += PHYS_STEPHEIGHT(this);
+ neworigin2 = neworigin;
+ neworigin2_z += PHYS_STEPHEIGHT(this);
+ tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
+ trace2_endpos = trace_endpos;
+ trace2_fraction = trace_fraction;
+ trace2_plane_normal = trace_plane_normal;
+ if(!trace_startsolid)
+ {
+ // then move down from there
+ currentorigin2 = trace2_endpos;
+ neworigin2 = trace2_endpos;
+ neworigin2_z = this.origin_z;
+ tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
+ trace3_endpos = trace_endpos;
+ trace3_fraction = trace_fraction;
+ trace3_plane_normal = trace_plane_normal;
+ // accept the new trace if it made some progress
+ if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125)
+ {
+ trace1_endpos = trace2_endpos;
+ trace1_fraction = trace2_fraction;
+ trace1_plane_normal = trace2_plane_normal;
+ trace1_endpos = trace3_endpos;
+ }
+ }
+ }
+
+ // check if it moved at all
+ if(trace1_fraction >= 0.001)
+ setorigin(this, trace1_endpos);
+
+ // check if it moved all the way
+ if(trace1_fraction == 1)
+ break;
+
+ // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
+ // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
+ // this got commented out in a change that supposedly makes the code match QW better
+ // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
+ if(trace1_plane_normal_z > 0.7)
+ SET_ONGROUND(this);
+
+ t -= t * trace1_fraction;
+
+ f = (this.velocity * trace1_plane_normal);
+ this.velocity = this.velocity + -f * trace1_plane_normal;
+ }
+ if(PHYS_TELEPORT_TIME(this) > 0)
+ this.velocity = primalvelocity;
+#endif
+}
+
+void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
+{
+ float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
+ if (k <= 0)
+ return;
+
+ k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1);
+
+ float zspeed = this.velocity_z;
+ this.velocity_z = 0;
+ float xyspeed = vlen(this.velocity);
+ this.velocity = normalize(this.velocity);
+
+ float dot = this.velocity * wishdir;
+
+ if (dot > 0) // we can't change direction while slowing down
+ {
+ k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
+ xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
+ k *= PHYS_AIRCONTROL(this);
+ this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
+ }
+
+ this.velocity = this.velocity * xyspeed;
+ this.velocity_z = zspeed;
+}
+
+float AdjustAirAccelQW(float accelqw, float factor)
+{
+ return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw);
+}
+
+// example config for alternate speed clamping:
+// sv_airaccel_qw 0.8
+// sv_airaccel_sideways_friction 0
+// prvm_globalset server speedclamp_mode 1
+// (or 2)
+void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
+{
+ float speedclamp = stretchfactor > 0 ? stretchfactor
+ : accelqw < 0 ? 1 // full clamping, no stretch
+ : -1; // no clamping
+
+ accelqw = fabs(accelqw);
+
+ if (GAMEPLAYFIX_Q2AIRACCELERATE)
+ wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
+
+ float vel_straight = this.velocity * wishdir;
+ float vel_z = this.velocity_z;
+ vector vel_xy = vec2(this.velocity);
+ vector vel_perpend = vel_xy - vel_straight * wishdir;
+
+ float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+
+ float vel_xy_current = vlen(vel_xy);
+ if (speedlimit)
+ accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed));
+ float vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+ float vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);
+ vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards
+ vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);
+
+ if (sidefric < 0 && (vel_perpend*vel_perpend))
+ // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
+ {
+ float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+ float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
+ // assume: themin > 1
+ // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
+ // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend
+ // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy
+ // obviously, this cannot be
+ if (themin <= 0)
+ vel_perpend *= f;
+ else
+ {
+ themin = sqrt(themin);
+ vel_perpend *= max(themin, f);
+ }
+ }
+ else
+ vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+
+ vel_xy = vel_straight * wishdir + vel_perpend;
+
+ if (speedclamp >= 0)
+ {
+ float vel_xy_preclamp;
+ vel_xy_preclamp = vlen(vel_xy);
+ if (vel_xy_preclamp > 0) // prevent division by zero
+ {
+ vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp;
+ if (vel_xy_current < vel_xy_preclamp)
+ vel_xy *= (vel_xy_current / vel_xy_preclamp);
+ }
+ }
+
+ this.velocity = vel_xy + vel_z * '0 0 1';
+}
+
+void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
+{
+ if (wishspeed == 0)
+ return;
+
+ vector curvel = this.velocity;
+ curvel_z = 0;
+ float curspeed = vlen(curvel);
+
+ if (wishspeed > curspeed * 1.01)
+ wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+ else
+ {
+ float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
+ wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH;
+ }
+ vector wishvel = wishdir * wishspeed;
+ vector acceldir = wishvel - curvel;
+ float addspeed = vlen(acceldir);
+ acceldir = normalize(acceldir);
+
+ float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+
+ if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
+ {
+ vector curdir = normalize(curvel);
+ float dot = acceldir * curdir;
+ if (dot < 0)
+ acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir;
+ }
+
+ this.velocity += accelspeed * acceldir;
+}
+
+
+/*
+=============
+PlayerJump
+
+When you press the jump key
+returns true if handled
+=============
+*/
+bool PlayerJump(entity this)
+{
+ if (PHYS_FROZEN(this))
+ return true; // no jumping in freezetag when frozen
+
+#ifdef SVQC
+ if (this.player_blocked)
+ return true; // no jumping while blocked
+#endif
+
+ bool doublejump = false;
+ float mjumpheight = PHYS_JUMPVELOCITY(this);
+
+ if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight))
+ return true;
+
+ doublejump = player_multijump;
+ mjumpheight = player_jumpheight;
+
+ if (this.waterlevel >= WATERLEVEL_SWIMMING)
+ {
+ if(this.viewloc)
+ {
+ doublejump = true;
+ mjumpheight *= 0.7;
+ }
+ else
+ {
+ this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
+ return true;
+ }
+ }
+
+ if (!doublejump)
+ if (!IS_ONGROUND(this))
+ return IS_JUMP_HELD(this);
+
+ bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
+ if(PHYS_TRACK_CANJUMP(this))
+ track_jump = true;
+
+ if (track_jump)
+ if (IS_JUMP_HELD(this))
+ return true;
+
+ // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline
+ // velocity bounds. Final velocity is bound between (jumpheight *
+ // min + jumpheight) and (jumpheight * max + jumpheight);
+
+ if(PHYS_JUMPSPEEDCAP_MIN != "")
+ {
+ float minjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MIN);
+
+ if (this.velocity_z < minjumpspeed)
+ mjumpheight += minjumpspeed - this.velocity_z;
+ }
+
+ if(PHYS_JUMPSPEEDCAP_MAX != "")
+ {
+ // don't do jump speedcaps on ramps to preserve old xonotic ramjump style
+ tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this);
+
+ if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this)))
+ {
+ float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX);
+
+ if (this.velocity_z > maxjumpspeed)
+ mjumpheight -= this.velocity_z - maxjumpspeed;
+ }
+ }
+
+ if (!WAS_ONGROUND(this))
+ {
+#ifdef SVQC
+ if(autocvar_speedmeter)
+ LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+#endif
+ if(this.lastground < time - 0.3)
+ {
+ float f = (1 - PHYS_FRICTION_ONLAND(this));
+ this.velocity_x *= f;
+ this.velocity_y *= f;
+ }
+#ifdef SVQC
+ if(this.jumppadcount > 1)
+ LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+ this.jumppadcount = 0;
+#endif
+ }
+
+ this.velocity_z += mjumpheight;
+
+ UNSET_ONGROUND(this);
+ SET_JUMP_HELD(this);
+
+#ifdef SVQC
+
+ this.oldvelocity_z = this.velocity_z;
+
+ animdecide_setaction(this, ANIMACTION_JUMP, true);
+
+ if (autocvar_g_jump_grunt)
+ PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+#endif
+ return true;
+}
+
+void CheckWaterJump(entity this)
+{
+// check for a jump-out-of-water
+ makevectors(this.v_angle);
+ vector start = this.origin;
+ start_z += 8;
+ v_forward_z = 0;
+ normalize(v_forward);
+ vector end = start + v_forward*24;
+ traceline (start, end, true, this);
+ if (trace_fraction < 1)
+ { // solid at waist
+ start_z = start_z + this.maxs_z - 8;
+ end = start + v_forward*24;
+ this.movedir = trace_plane_normal * -50;
+ traceline(start, end, true, this);
+ if (trace_fraction == 1)
+ { // open at eye level
+ this.velocity_z = 225;
+ this.flags |= FL_WATERJUMP;
+ SET_JUMP_HELD(this);
+ #ifdef SVQC
+ PHYS_TELEPORT_TIME(this) = time + 2; // safety net
+ #elif defined(CSQC)
+ pmove_waterjumptime = time + 2;
+ #endif
+ }
+ }
+}
+
+
+#ifdef SVQC
+ #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump
+#elif defined(CSQC)
+ float autocvar_cl_jetpack_jump;
+ #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump
+#endif
+.float jetpack_stopped;
+void CheckPlayerJump(entity this)
+{
+#ifdef SVQC
+ float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK;
+#endif
+ if (JETPACK_JUMP(this) < 2)
+ ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+
+ if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this))
+ {
+ float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects
+ float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this);
+ float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO;
+
+ if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { }
+ else if (this.jetpack_stopped) { }
+ else if (!has_fuel)
+ {
+#ifdef SVQC
+ if (was_flying) // TODO: ran out of fuel message
+ Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
+ else if (activate)
+ Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL);
+#endif
+ this.jetpack_stopped = true;
+ ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+ }
+ else if (activate && !PHYS_FROZEN(this))
+ ITEMS_STAT(this) |= IT_USING_JETPACK;
+ }
+ else
+ {
+ this.jetpack_stopped = false;
+ ITEMS_STAT(this) &= ~IT_USING_JETPACK;
+ }
+ if (!PHYS_INPUT_BUTTON_JUMP(this))
+ UNSET_JUMP_HELD(this);
+
+ if (this.waterlevel == WATERLEVEL_SWIMMING)
+ CheckWaterJump(this);
+}
+
+float racecar_angle(float forward, float down)
+{
+ if (forward < 0)
+ {
+ forward = -forward;
+ down = -down;
+ }
+
+ float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);
+
+ float angle_mult = forward / (800 + forward);
+
+ if (ret > 180)
+ return ret * angle_mult + 360 * (1 - angle_mult);
+ else
+ return ret * angle_mult;
+}
+
+string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
+.float specialcommand_pos;
+void SpecialCommand()
+{
+#ifdef SVQC
+ if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse))
+ LOG_INFO("A hollow voice says \"Plugh\".\n");
+#endif
+}
+
+float PM_check_specialcommand(entity this, float buttons)
+{
+#ifdef SVQC
+ string c;
+ if (!buttons)
+ c = "x";
+ else if (buttons == 1)
+ c = "1";
+ else if (buttons == 2)
+ c = " ";
+ else if (buttons == 128)
+ c = "s";
+ else if (buttons == 256)
+ c = "w";
+ else if (buttons == 512)
+ c = "a";
+ else if (buttons == 1024)
+ c = "d";
+ else
+ c = "?";
+
+ if (c == substring(specialcommand, this.specialcommand_pos, 1))
+ {
+ this.specialcommand_pos += 1;
+ if (this.specialcommand_pos >= strlen(specialcommand))
+ {
+ this.specialcommand_pos = 0;
+ SpecialCommand();
+ return true;
+ }
+ }
+ else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1)))
+ this.specialcommand_pos = 0;
+#endif
+ return false;
+}
+
+void PM_check_nickspam(entity this)
+{
+#ifdef SVQC
+ if (time >= this.nickspamtime)
+ return;
+ if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
+ {
+ // slight annoyance for nick change scripts
+ this.movement = -1 * this.movement;
+ this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0;
+
+ if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you!
+ {
+ this.v_angle_x = random() * 360;
+ this.v_angle_y = random() * 360;
+ // at least I'm not forcing retardedview by also assigning to angles_z
+ this.fixangle = true;
+ }
+ }
+#endif
+}
+
+void PM_check_punch(entity this)
+{
+#ifdef SVQC
+ if (this.punchangle != '0 0 0')
+ {
+ float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+ if (f > 0)
+ this.punchangle = normalize(this.punchangle) * f;
+ else
+ this.punchangle = '0 0 0';
+ }
+
+ if (this.punchvector != '0 0 0')
+ {
+ float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+ if (f > 0)
+ this.punchvector = normalize(this.punchvector) * f;
+ else
+ this.punchvector = '0 0 0';
+ }
+#endif
+}
+
+// predict frozen movement, as frozen players CAN move in some cases
+void PM_check_frozen(entity this)
+{
+ if (!PHYS_FROZEN(this))
+ return;
+ if (PHYS_DODGING_FROZEN(this)
+#ifdef SVQC
+ && IS_REAL_CLIENT(this)
+#endif
+ )
+ {
+ this.movement_x = bound(-5, this.movement.x, 5);
+ this.movement_y = bound(-5, this.movement.y, 5);
+ this.movement_z = bound(-5, this.movement.z, 5);
+ }
+ else
+ this.movement = '0 0 0';
+
+ vector midpoint = ((this.absmin + this.absmax) * 0.5);
+ if (pointcontents(midpoint) == CONTENT_WATER)
+ {
+ this.velocity = this.velocity * 0.5;
+
+ if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ this.velocity_z = 200;
+ }
+}
+
+void PM_check_hitground(entity this)
+{
+#ifdef SVQC
+ if (!this.wasFlying) return;
+ this.wasFlying = false;
+ if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
+ if (time < this.ladder_time) return;
+ if (this.hook) return;
+ this.nextstep = time + 0.3 + random() * 0.1;
+ trace_dphitq3surfaceflags = 0;
+ tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
+ entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+ ? GS_FALL_METAL
+ : GS_FALL;
+ GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+#endif
+}
+
+void PM_Footsteps(entity this)
+{
+#ifdef SVQC
+ if (!g_footsteps) return;
+ if (IS_DUCKED(this)) return;
+ if (time >= this.lastground + 0.2) return;
+ if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return;
+ if ((time > this.nextstep) || (time < (this.nextstep - 10.0)))
+ {
+ this.nextstep = time + 0.3 + random() * 0.1;
+ trace_dphitq3surfaceflags = 0;
+ tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return;
+ entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+ ? GS_STEP_METAL
+ : GS_STEP;
+ GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ }
+#endif
+}
+
+void PM_check_blocked(entity this)
+{
+#ifdef SVQC
+ if (!this.player_blocked)
+ return;
+ this.movement = '0 0 0';
+ this.disableclientprediction = 1;
+#endif
+}
+
+void PM_fly(entity this, float maxspd_mod)
+{
+ // noclipping or flying
+ UNSET_ONGROUND(this);
+
+ this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
+ makevectors(this.v_angle);
+ //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+ vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y
+ + '0 0 1' * this.movement.z;
+ // acceleration
+ vector wishdir = normalize(wishvel);
+ float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
+#ifdef SVQC
+ if(time >= PHYS_TELEPORT_TIME(this))
+#endif
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
+ PM_ClientMovement_Move(this);
+}
+
+void PM_swim(entity this, float maxspd_mod)
+{
+ // swimming
+ UNSET_ONGROUND(this);
+
+ float jump = PHYS_INPUT_BUTTON_JUMP(this);
+ // water jump only in certain situations
+ // this mimics quakeworld code
+ if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
+ {
+ vector yawangles = '0 1 0' * this.v_angle.y;
+ makevectors(yawangles);
+ vector forward = v_forward;
+ vector spot = this.origin + 24 * forward;
+ spot_z += 8;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (trace_startsolid)
+ {
+ spot_z += 24;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (!trace_startsolid)
+ {
+ this.velocity = forward * 50;
+ this.velocity_z = 310;
+ #ifdef CSQC
+ pmove_waterjumptime = 2;
+ #endif
+ UNSET_ONGROUND(this);
+ SET_JUMP_HELD(this);
+ }
+ }
+ }
+ makevectors(this.v_angle);
+ //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+ vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y
+ + '0 0 1' * this.movement.z;
+ if(this.viewloc)
+ wishvel.z = -160; // drift anyway
+ else if (wishvel == '0 0 0')
+ wishvel = '0 0 -60'; // drift towards bottom
+
+
+ vector wishdir = normalize(wishvel);
+ float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
+
+ if (IS_DUCKED(this))
+ wishspeed *= 0.5;
+
+// if (pmove_waterjumptime <= 0) // TODO: use
+ {
+ // water friction
+ float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
+ f = min(max(0, f), 1);
+ this.velocity *= f;
+
+ f = wishspeed - this.velocity * wishdir;
+ if (f > 0)
+ {
+ float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
+ this.velocity += accelspeed * wishdir;
+ }
+
+ // holding jump button swims upward slowly
+ if (jump && !this.viewloc)
+ {
+#if 0
+ if (this.watertype & CONTENT_LAVA)
+ this.velocity_z = 50;
+ else if (this.watertype & CONTENT_SLIME)
+ this.velocity_z = 80;
+ else
+ {
+ if (IS_NEXUIZ_DERIVED(gamemode))
+#endif
+ this.velocity_z = 200;
+#if 0
+ else
+ this.velocity_z = 100;
+ }
+#endif
+ }
+ }
+ if(this.viewloc)
+ {
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0)
+ {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+ }
+ else
+ {
+ // water acceleration
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
+ PM_ClientMovement_Move(this);
+ }
+}
+
+.vector oldmovement;
+void PM_ladder(entity this, float maxspd_mod)
+{
+ // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
+ UNSET_ONGROUND(this);
+
+ float g;
+ g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+ if (PHYS_ENTGRAVITY(this))
+ g *= PHYS_ENTGRAVITY(this);
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ {
+ g *= 0.5;
+ this.velocity_z += g;
+ }
+
+ this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
+ makevectors(this.v_angle);
+ //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+ vector wishvel = v_forward * this.movement_x
+ + v_right * this.movement_y
+ + '0 0 1' * this.movement_z;
+ if(this.viewloc)
+ wishvel.z = this.oldmovement.x;
+ this.velocity_z += g;
+ if (this.ladder_entity.classname == "func_water")
+ {
+ float f = vlen(wishvel);
+ if (f > this.ladder_entity.speed)
+ wishvel *= (this.ladder_entity.speed / f);
+
+ this.watertype = this.ladder_entity.skin;
+ f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
+ if ((this.origin_z + this.view_ofs_z) < f)
+ this.waterlevel = WATERLEVEL_SUBMERGED;
+ else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
+ this.waterlevel = WATERLEVEL_SWIMMING;
+ else if ((this.origin_z + this.mins_z + 1) < f)
+ this.waterlevel = WATERLEVEL_WETFEET;
+ else
+ {
+ this.waterlevel = WATERLEVEL_NONE;
+ this.watertype = CONTENT_EMPTY;
+ }
+ }
+ // acceleration
+ vector wishdir = normalize(wishvel);
+ float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
+ if(time >= PHYS_TELEPORT_TIME(this))
+ // water acceleration
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
+ PM_ClientMovement_Move(this);
+}
+
+void PM_jetpack(entity this, float maxspd_mod)
+{
+ //makevectors(this.v_angle.y * '0 1 0');
+ makevectors(this.v_angle);
+ vector wishvel = v_forward * this.movement_x
+ + v_right * this.movement_y;
+ // add remaining speed as Z component
+ float maxairspd = PHYS_MAXAIRSPEED(this) * max(1, maxspd_mod);
+ // fix speedhacks :P
+ wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd);
+ // add the unused velocity as up component
+ wishvel_z = 0;
+
+ // if (this.BUTTON_JUMP)
+ wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));
+
+ // it is now normalized, so...
+ float a_side = PHYS_JETPACK_ACCEL_SIDE(this);
+ float a_up = PHYS_JETPACK_ACCEL_UP(this);
+ float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
+
+ wishvel_x *= a_side;
+ wishvel_y *= a_side;
+ wishvel_z *= a_up;
+ wishvel_z += a_add;
+
+ float best = 0;
+ //////////////////////////////////////////////////////////////////////////////////////
+ // finding the maximum over all vectors of above form
+ // with wishvel having an absolute value of 1
+ //////////////////////////////////////////////////////////////////////////////////////
+ // we're finding the maximum over
+ // f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2;
+ // for z in the range from -1 to 1
+ //////////////////////////////////////////////////////////////////////////////////////
+ // maximum is EITHER attained at the single extreme point:
+ float a_diff = a_side * a_side - a_up * a_up;
+ float f;
+ if (a_diff != 0)
+ {
+ f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z)
+ if (f > -1 && f < 1) // can it be attained?
+ {
+ best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff;
+ //print("middle\n");
+ }
+ }
+ // OR attained at z = 1:
+ f = (a_up + a_add) * (a_up + a_add);
+ if (f > best)
+ {
+ best = f;
+ //print("top\n");
+ }
+ // OR attained at z = -1:
+ f = (a_up - a_add) * (a_up - a_add);
+ if (f > best)
+ {
+ best = f;
+ //print("bottom\n");
+ }
+ best = sqrt(best);
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ //print("best possible acceleration: ", ftos(best), "\n");
+
+ float fxy, fz;
+ fxy = bound(0, 1 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1);
+ if (wishvel_z - PHYS_GRAVITY(this) > 0)
+ fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
+ else
+ fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1);
+
+ float fvel;
+ fvel = vlen(wishvel);
+ wishvel_x *= fxy;
+ wishvel_y *= fxy;
+ wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this);
+
+ fvel = min(1, vlen(wishvel) / best);
+ if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
+ f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
+ else
+ f = 1;
+
+ //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");
+
+ if (f > 0 && wishvel != '0 0 0')
+ {
+ this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+ UNSET_ONGROUND(this);
+
+#ifdef SVQC
+ if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
+ this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
+
+ ITEMS_STAT(this) |= IT_USING_JETPACK;
+
+ // jetpack also inhibits health regeneration, but only for 1 second
+ this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
+#endif
+ }
+
+#ifdef CSQC
+ float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+ if(autocvar_cl_movement == 3)
+ {
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.velocity_z -= g * 0.5;
+ else
+ this.velocity_z -= g;
+ }
+ PM_ClientMovement_Move(this);
+ if(autocvar_cl_movement == 3)
+ {
+ if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.velocity_z -= g * 0.5;
+ }
+#endif
+}
+
+void PM_walk(entity this, float maxspd_mod)
+{
+ if (!WAS_ONGROUND(this))
+ {
+#ifdef SVQC
+ if (autocvar_speedmeter)
+ LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+#endif
+ if (this.lastground < time - 0.3)
+ this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
+#ifdef SVQC
+ if (this.jumppadcount > 1)
+ LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+ this.jumppadcount = 0;
+#endif
+ }
+
+ // walking
+ makevectors(this.v_angle.y * '0 1 0');
+ const vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y;
+ // acceleration
+ const vector wishdir = normalize(wishvel);
+ float wishspeed = vlen(wishvel);
+ wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
+ if (IS_DUCKED(this)) wishspeed *= 0.5;
+
+ // apply edge friction
+ const float f2 = vlen2(vec2(this.velocity));
+ if (f2 > 0)
+ {
+ trace_dphitq3surfaceflags = 0;
+ tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+ // TODO: apply edge friction
+ // apply ground friction
+ const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+ ? PHYS_FRICTION_SLICK(this)
+ : PHYS_FRICTION(this);
+
+ float f = sqrt(f2);
+ f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
+ f = max(0, f);
+ this.velocity *= f;
+ /*
+ Mathematical analysis time!
+
+ Our goal is to invert this mess.
+
+ For the two cases we get:
+ v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
+ = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ and
+ v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+ v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+
+ These cases would be chosen ONLY if:
+ v0 < PHYS_STOPSPEED(this)
+ v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
+ v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+ and, respectively:
+ v0 >= PHYS_STOPSPEED(this)
+ v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
+ v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
+ */
+ }
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0)
+ {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+#ifdef CSQC
+ float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+ if(autocvar_cl_movement == 3)
+ {
+ if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
+ this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
+ }
+ if (vdist(this.velocity, >, 0))
+ PM_ClientMovement_Move(this);
+ if(autocvar_cl_movement == 3)
+ {
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
+ this.velocity_z -= g * 0.5;
+ }
+#endif
+}
+
+void PM_air(entity this, float buttons_prev, float maxspd_mod)
+{
+ makevectors(this.v_angle.y * '0 1 0');
+ vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y;
+ // acceleration
+ vector wishdir = normalize(wishvel);
+ float wishspeed = vlen(wishvel);
+
+#ifdef SVQC
+ if(time >= PHYS_TELEPORT_TIME(this))
+#elif defined(CSQC)
+ if(pmove_waterjumptime <= 0)
+#endif
+ {
+ float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
+
+ // apply air speed limit
+ float airaccelqw = PHYS_AIRACCEL_QW(this);
+ float wishspeed0 = wishspeed;
+ wishspeed = min(wishspeed, maxairspd);
+ if (IS_DUCKED(this))
+ wishspeed *= 0.5;
+ float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
+
+ float accelerating = (this.velocity * wishdir > 0);
+ float wishspeed2 = wishspeed;
+
+ // CPM: air control
+ if (PHYS_AIRSTOPACCELERATE(this))
+ {
+ vector curdir = normalize(vec2(this.velocity));
+ airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
+ }
+ // note that for straight forward jumping:
+ // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+ // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+ // -->
+ // dv/dt = accel * maxspeed (when slow)
+ // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
+ // log dv/dt = logaccel + logmaxspeed (when slow)
+ // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
+ float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
+ if (PHYS_MAXAIRSTRAFESPEED(this))
+ wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
+ if (PHYS_AIRSTRAFEACCELERATE(this))
+ airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
+ if (PHYS_AIRSTRAFEACCEL_QW(this))
+ airaccelqw =
+ (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
+ *
+ (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
+ // !CPM
+
+ if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
+ PM_AirAccelerate(this, wishdir, wishspeed2);
+ else
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this));
+
+ if (PHYS_AIRCONTROL(this))
+ CPM_PM_Aircontrol(this, wishdir, wishspeed2);
+ }
+#ifdef CSQC
+ float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
+ if(autocvar_cl_movement == 3)
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.velocity_z -= g * 0.5;
+ else
+ this.velocity_z -= g;
+#endif
+ PM_ClientMovement_Move(this);
+#ifdef CSQC
+ if(autocvar_cl_movement == 3)
+ if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
+ if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
+ this.velocity_z -= g * 0.5;
+#endif
+}
+
+// used for calculating airshots
+bool IsFlying(entity this)
+{
+ if(IS_ONGROUND(this))
+ return false;
+ if(this.waterlevel >= WATERLEVEL_SWIMMING)
+ return false;
+ traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
+ if(trace_fraction < 1)
+ return false;
+ return true;
+}
+
+void PM_Main(entity this)
+{
+ int buttons = PHYS_INPUT_BUTTON_MASK(this);
+#ifdef CSQC
+ this.items = STAT(ITEMS);
+
+ this.movement = PHYS_INPUT_MOVEVALUES(this);
+
+ this.spectatorspeed = STAT(SPECTATORSPEED);
+
+ vector oldv_angle = this.v_angle;
+ vector oldangles = this.angles; // we need to save these, as they're abused by other code
+ this.v_angle = PHYS_INPUT_ANGLES(this);
+ this.angles = PHYS_WORLD_ANGLES(this);
+
+ this.team = myteam + 1; // is this correct?
+ if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
+ UNSET_JUMP_HELD(this); // canjump = true
+ pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
+
+ PM_ClientMovement_UpdateStatus(this, true);
+#endif
+
+ this.oldmovement = this.movement;
+
+
+#ifdef SVQC
+ WarpZone_PlayerPhysics_FixVAngle();
+#endif
+ float maxspeed_mod = 1;
+ maxspeed_mod *= PHYS_HIGHSPEED(this);
+
+#ifdef SVQC
+ Physics_UpdateStats(this, maxspeed_mod);
+
+ if (this.PlayerPhysplug)
+ if (this.PlayerPhysplug())
+ return;
+#endif
+
+#ifdef SVQC
+ anticheat_physics(this);
+#endif
+
+ if (PM_check_specialcommand(this, buttons))
+ return;
+#ifdef SVQC
+ if (sv_maxidle > 0)
+ {
+ if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
+ this.parm_idlesince = time;
+ }
+#endif
+ int buttons_prev = this.buttons_old;
+ this.buttons_old = buttons;
+ this.movement_old = this.movement;
+ this.v_angle_old = this.v_angle;
+
+ PM_check_nickspam(this);
+
+ PM_check_punch(this);
+#ifdef SVQC
+ if (IS_BOT_CLIENT(this))
+ {
+ if (playerdemo_read(this))
+ return;
+ WITH(entity, self, this, bot_think());
+ }
+#endif
+
+#ifdef SVQC
+ if (IS_PLAYER(this))
+ {
+ const bool allowed_to_move = (time >= game_starttime);
+ if (!allowed_to_move)
+ {
+ this.velocity = '0 0 0';
+ this.movetype = MOVETYPE_NONE;
+ this.disableclientprediction = 2;
+ }
+ else if (this.disableclientprediction == 2)
+ {
+ if (this.movetype == MOVETYPE_NONE)
+ this.movetype = MOVETYPE_WALK;
+ this.disableclientprediction = 0;
+ }
+ }
+#endif
+
+#ifdef SVQC
+ if (this.movetype == MOVETYPE_NONE)
+ return;
+
+ // when we get here, disableclientprediction cannot be 2
+ this.disableclientprediction = 0;
+#endif
+
+ viewloc_PlayerPhysics(this);
+
+ PM_check_frozen(this);
+
+ PM_check_blocked(this);
+
+ maxspeed_mod = 1;
+
+ if (this.in_swamp)
+ maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
+
+ // conveyors: first fix velocity
+ if (this.conveyor.state)
+ this.velocity -= this.conveyor.movedir;
+
+ MUTATOR_CALLHOOK(PlayerPhysics, this);
+
+ if (!IS_PLAYER(this))
+ {
+#ifdef SVQC
+ maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+ if (!this.spectatorspeed)
+ this.spectatorspeed = maxspeed_mod;
+ if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
+ {
+ if (this.lastclassname != "player")
+ {
+ if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
+ this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
+ else if (this.impulse == 11)
+ this.spectatorspeed = maxspeed_mod;
+ else if (this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
+ this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
+ else if (this.impulse >= 1 && this.impulse <= 9)
+ this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
+ } // otherwise just clear
+ this.impulse = 0;
+ }
+#endif
+ maxspeed_mod = this.spectatorspeed;
+ }
+#ifdef SVQC
+
+ float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
+ if(this.speed != spd)
+ {
+ this.speed = spd;
+ string temps = ftos(spd);
+ stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
+ }
+
+ if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
+ {
+ this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
+ }
+ if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
+ {
+ this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
+ }
+#endif
+
+ if(PHYS_DEAD(this))
+ {
+ // handle water here
+ vector midpoint = ((this.absmin + this.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ this.velocity = this.velocity * 0.5;
+
+ // do we want this?
+ //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
+ //{ this.velocity_z = 70; }
+ }
+ goto end;
+ }
+
+#ifdef SVQC
+ if (!this.fixangle)
+ this.angles = '0 1 0' * this.v_angle.y;
+#endif
+
+ if (IS_PLAYER(this) && IS_ONGROUND(this))
+ {
+ PM_check_hitground(this);
+ PM_Footsteps(this);
+ }
+
+ if(IsFlying(this))
+ this.wasFlying = 1;
+
+ if (IS_PLAYER(this))
+ CheckPlayerJump(this);
+
+ if (this.flags & FL_WATERJUMP)
+ {
+ this.velocity_x = this.movedir.x;
+ this.velocity_y = this.movedir.y;
+ if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE)
+ {
+ this.flags &= ~FL_WATERJUMP;
+ PHYS_TELEPORT_TIME(this) = 0;
+ }
+ }
+
+ else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
+ { }
+
+#ifdef SVQC
+ else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
+#elif defined(CSQC)
+ else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
+#endif
+ PM_fly(this, maxspeed_mod);
+
+ else if (this.waterlevel >= WATERLEVEL_SWIMMING)
+ PM_swim(this, maxspeed_mod);
+
+ else if (time < this.ladder_time)
+ PM_ladder(this, maxspeed_mod);
+
+ else if (ITEMS_STAT(this) & IT_USING_JETPACK)
+ PM_jetpack(this, maxspeed_mod);
+
+ else if (IS_ONGROUND(this))
+ PM_walk(this, maxspeed_mod);
+
+ else
+ PM_air(this, buttons_prev, maxspeed_mod);
+
+:end
+ if (IS_ONGROUND(this))
+ this.lastground = time;
+
+ // conveyors: then break velocity again
+ if(this.conveyor.state)
+ this.velocity += this.conveyor.movedir;
+
+ this.lastflags = this.flags;
+
+ this.lastclassname = this.classname;
+
+#ifdef CSQC
+ this.v_angle = oldv_angle;
+ this.angles = oldangles;
+#endif
+}
+
+#if defined(SVQC)
+void SV_PlayerPhysics()
+#elif defined(CSQC)
+void CSQC_ClientMovement_PlayerMove_Frame(entity this)
+#endif
+{
+#ifdef SVQC
+ SELFPARAM();
+#endif
+ PM_Main(this);
+}
--- /dev/null
+#ifndef COMMON_PHYSICS_H
+#define COMMON_PHYSICS_H
+
+// Client/server mappings
+
+.entity conveyor;
+
+.float race_penalty;
+
+.float gravity;
+.float swamp_slowdown;
+.float lastflags;
+.float lastground;
+.float wasFlying;
+#ifdef SVQC
+.float spectatorspeed = _STAT(SPECTATORSPEED);
+#elif defined(CSQC)
+.float spectatorspeed;
+#endif
+
+.vector movement_old;
+.float buttons_old;
+.vector v_angle_old;
+.string lastclassname;
+
+.float() PlayerPhysplug;
+float AdjustAirAccelQW(float accelqw, float factor);
+
+bool IsFlying(entity a);
+
+#define BUFFS_STAT(s) STAT(BUFFS, s)
+
+#define GAMEPLAYFIX_DOWNTRACEONGROUND(s) STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, s)
+#define GAMEPLAYFIX_EASIERWATERJUMP(s) STAT(GAMEPLAYFIX_EASIERWATERJUMP, s)
+#define GAMEPLAYFIX_STEPDOWN(s) STAT(GAMEPLAYFIX_STEPDOWN, s)
+#define GAMEPLAYFIX_STEPMULTIPLETIMES(s) STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s)
+#define GAMEPLAYFIX_UNSTICKPLAYERS(s) STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s)
+
+#define PHYS_ACCELERATE(s) STAT(MOVEVARS_ACCELERATE, s)
+#define PHYS_AIRACCELERATE(s) STAT(MOVEVARS_AIRACCELERATE, s)
+#define PHYS_AIRACCEL_QW(s) STAT(MOVEVARS_AIRACCEL_QW, s)
+#define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, s)
+#define PHYS_AIRACCEL_SIDEWAYS_FRICTION(s) STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, s)
+#define PHYS_AIRCONTROL(s) STAT(MOVEVARS_AIRCONTROL, s)
+#define PHYS_AIRCONTROL_PENALTY(s) STAT(MOVEVARS_AIRCONTROL_PENALTY, s)
+#define PHYS_AIRCONTROL_POWER(s) STAT(MOVEVARS_AIRCONTROL_POWER, s)
+#define PHYS_AIRSPEEDLIMIT_NONQW(s) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s)
+#define PHYS_AIRSTOPACCELERATE(s) STAT(MOVEVARS_AIRSTOPACCELERATE, s)
+#define PHYS_AIRSTRAFEACCELERATE(s) STAT(MOVEVARS_AIRSTRAFEACCELERATE, s)
+#define PHYS_AIRSTRAFEACCEL_QW(s) STAT(MOVEVARS_AIRSTRAFEACCEL_QW, s)
+
+#define PHYS_AMMO_FUEL(s) STAT(FUEL, s)
+
+#define PHYS_DODGING_FROZEN(s) STAT(DODGING_FROZEN, s)
+
+#define PHYS_FRICTION(s) STAT(MOVEVARS_FRICTION, s)
+#define PHYS_FRICTION_ONLAND(s) STAT(MOVEVARS_FRICTION_ONLAND, s)
+#define PHYS_FRICTION_SLICK(s) STAT(MOVEVARS_FRICTION_SLICK, s)
+
+#define PHYS_FROZEN(s) STAT(FROZEN, s)
+
+#define PHYS_HIGHSPEED(s) STAT(MOVEVARS_HIGHSPEED, s)
+
+#define PHYS_JETPACK_ACCEL_SIDE(s) STAT(JETPACK_ACCEL_SIDE, s)
+#define PHYS_JETPACK_ACCEL_UP(s) STAT(JETPACK_ACCEL_UP, s)
+#define PHYS_JETPACK_ANTIGRAVITY(s) STAT(JETPACK_ANTIGRAVITY, s)
+#define PHYS_JETPACK_FUEL(s) STAT(JETPACK_FUEL, s)
+#define PHYS_JETPACK_MAXSPEED_SIDE(s) STAT(JETPACK_MAXSPEED_SIDE, s)
+#define PHYS_JETPACK_MAXSPEED_UP(s) STAT(JETPACK_MAXSPEED_UP, s)
+
+#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s)
+#define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP, s)
+#define PHYS_JUMPVELOCITY(s) STAT(MOVEVARS_JUMPVELOCITY, s)
+
+#define PHYS_MAXAIRSPEED(s) STAT(MOVEVARS_MAXAIRSPEED, s)
+#define PHYS_MAXAIRSTRAFESPEED(s) STAT(MOVEVARS_MAXAIRSTRAFESPEED, s)
+#define PHYS_MAXSPEED(s) STAT(MOVEVARS_MAXSPEED, s)
+
+#define PHYS_NOSTEP(s) STAT(NOSTEP, s)
+#define PHYS_STEPHEIGHT(s) STAT(MOVEVARS_STEPHEIGHT, s)
+
+#define PHYS_STOPSPEED(s) STAT(MOVEVARS_STOPSPEED, s)
+
+#define PHYS_TRACK_CANJUMP(s) STAT(MOVEVARS_TRACK_CANJUMP, s)
+
+#define PHYS_WALLFRICTION(s) STAT(MOVEVARS_WALLFRICTION, s)
+
+#define PHYS_WARSOWBUNNY_ACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_ACCEL, s)
+#define PHYS_WARSOWBUNNY_AIRFORWARDACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, s)
+#define PHYS_WARSOWBUNNY_BACKTOSIDERATIO(s) STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, s)
+#define PHYS_WARSOWBUNNY_TOPSPEED(s) STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s)
+#define PHYS_WARSOWBUNNY_TURNACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s)
+
+#define UPWARD_VELOCITY_CLEARS_ONGROUND(s) STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s)
+
+#define PHYS_INPUT_BUTTON_ATCK(s) PHYS_INPUT_BUTTON_BUTTON1(s)
+#define PHYS_INPUT_BUTTON_JUMP(s) PHYS_INPUT_BUTTON_BUTTON2(s)
+#define PHYS_INPUT_BUTTON_ATCK2(s) PHYS_INPUT_BUTTON_BUTTON3(s)
+#define PHYS_INPUT_BUTTON_ZOOM(s) PHYS_INPUT_BUTTON_BUTTON4(s)
+#define PHYS_INPUT_BUTTON_CROUCH(s) PHYS_INPUT_BUTTON_BUTTON5(s)
+#define PHYS_INPUT_BUTTON_HOOK(s) PHYS_INPUT_BUTTON_BUTTON6(s)
+#define PHYS_INPUT_BUTTON_INFO(s) PHYS_INPUT_BUTTON_BUTTON7(s)
+#define PHYS_INPUT_BUTTON_DRAG(s) PHYS_INPUT_BUTTON_BUTTON8(s)
+#define PHYS_INPUT_BUTTON_USE(s) PHYS_INPUT_BUTTON_BUTTON_USE(s)
+#define PHYS_INPUT_BUTTON_CHAT(s) PHYS_INPUT_BUTTON_BUTTON_CHAT(s)
+#define PHYS_INPUT_BUTTON_PRYDON(s) PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)
+#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s) PHYS_INPUT_BUTTON_BUTTON9(s)
+#define PHYS_INPUT_BUTTON_JETPACK(s) PHYS_INPUT_BUTTON_BUTTON10(s)
+
+// if more buttons are needed, start using impulse bits as buttons
+
+#define PHYS_INPUT_BUTTON_BACKWARD(s) (PHYS_INPUT_MOVEVALUES(s).x < 0)
+#define PHYS_INPUT_BUTTON_FORWARD(s) (PHYS_INPUT_MOVEVALUES(s).x > 0)
+#define PHYS_INPUT_BUTTON_LEFT(s) (PHYS_INPUT_MOVEVALUES(s).y < 0)
+#define PHYS_INPUT_BUTTON_RIGHT(s) (PHYS_INPUT_MOVEVALUES(s).y > 0)
+
+// used for special commands and idle checking, not from the engine
+// TODO: cache
+#define PHYS_INPUT_BUTTON_MASK(s) ( \
+ (1 << 0) * PHYS_INPUT_BUTTON_ATCK(s) \
+ | (1 << 1) * PHYS_INPUT_BUTTON_JUMP(s) \
+ | (1 << 2) * PHYS_INPUT_BUTTON_ATCK2(s) \
+ | (1 << 3) * PHYS_INPUT_BUTTON_ZOOM(s) \
+ | (1 << 4) * PHYS_INPUT_BUTTON_CROUCH(s) \
+ | (1 << 5) * PHYS_INPUT_BUTTON_HOOK(s) \
+ | (1 << 6) * PHYS_INPUT_BUTTON_USE(s) \
+ | (1 << 7) * PHYS_INPUT_BUTTON_BACKWARD(s) \
+ | (1 << 8) * PHYS_INPUT_BUTTON_FORWARD(s) \
+ | (1 << 9) * PHYS_INPUT_BUTTON_LEFT(s) \
+ | (1 << 10) * PHYS_INPUT_BUTTON_RIGHT(s) \
+ )
+
+#define IS_JUMP_HELD(s) (!((s).flags & FL_JUMPRELEASED))
+#define SET_JUMP_HELD(s) ((s).flags &= ~FL_JUMPRELEASED)
+#define UNSET_JUMP_HELD(s) ((s).flags |= FL_JUMPRELEASED)
+
+#define IS_ONGROUND(s) boolean((s).flags & FL_ONGROUND)
+#define SET_ONGROUND(s) ((s).flags |= FL_ONGROUND)
+#define UNSET_ONGROUND(s) ((s).flags &= ~FL_ONGROUND)
+
+#define WAS_ONGROUND(s) boolean((s).lastflags & FL_ONGROUND)
+
+#define ITEMS_STAT(s) ((s).items)
+
+#ifdef CSQC
+
+ string autocvar_cl_jumpspeedcap_min;
+ string autocvar_cl_jumpspeedcap_max;
+
+ noref float pmove_waterjumptime;
+
+ const int FL_WATERJUMP = 2048; // player jumping out of water
+ const int FL_JUMPRELEASED = 4096; // for jump debouncing
+
+ .float watertype;
+ .float waterlevel;
+ .int items;
+
+ .vector movement;
+ .vector v_angle;
+
+// TODO
+ #define IS_CLIENT(s) ((s).isplayermodel)
+ #define IS_PLAYER(s) ((s).isplayermodel)
+ #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel)
+ #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE))
+
+ //float player_multijump;
+ //float player_jumpheight;
+
+ #define PHYS_GRAVITY(s) STAT(MOVEVARS_GRAVITY, s)
+
+ #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
+
+ #define TICRATE ticrate
+
+ #define PHYS_INPUT_ANGLES(s) input_angles
+// TODO
+ #define PHYS_WORLD_ANGLES(s) input_angles
+
+ #define PHYS_INPUT_TIMELENGTH input_timelength
+ #define PHYS_INPUT_FRAMETIME serverdeltatime
+
+ #define PHYS_INPUT_MOVEVALUES(s) input_movevalues
+
+ #define PHYS_INPUT_BUTTON_BUTTON1(s) boolean(input_buttons & BIT(0))
+ #define PHYS_INPUT_BUTTON_BUTTON2(s) boolean(input_buttons & BIT(1))
+ #define PHYS_INPUT_BUTTON_BUTTON3(s) boolean(input_buttons & BIT(2))
+ #define PHYS_INPUT_BUTTON_BUTTON4(s) boolean(input_buttons & BIT(3))
+ #define PHYS_INPUT_BUTTON_BUTTON5(s) boolean(input_buttons & BIT(4))
+ #define PHYS_INPUT_BUTTON_BUTTON6(s) boolean(input_buttons & BIT(5))
+ #define PHYS_INPUT_BUTTON_BUTTON7(s) boolean(input_buttons & BIT(6))
+ #define PHYS_INPUT_BUTTON_BUTTON8(s) boolean(input_buttons & BIT(7))
+ #define PHYS_INPUT_BUTTON_BUTTON_USE(s) boolean(input_buttons & BIT(8))
+ #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) boolean(input_buttons & BIT(9))
+ #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) boolean(input_buttons & BIT(10))
+ #define PHYS_INPUT_BUTTON_BUTTON9(s) boolean(input_buttons & BIT(11))
+ #define PHYS_INPUT_BUTTON_BUTTON10(s) boolean(input_buttons & BIT(12))
+ #define PHYS_INPUT_BUTTON_BUTTON11(s) boolean(input_buttons & BIT(13))
+ #define PHYS_INPUT_BUTTON_BUTTON12(s) boolean(input_buttons & BIT(14))
+ #define PHYS_INPUT_BUTTON_BUTTON13(s) boolean(input_buttons & BIT(15))
+ #define PHYS_INPUT_BUTTON_BUTTON14(s) boolean(input_buttons & BIT(16))
+ #define PHYS_INPUT_BUTTON_BUTTON15(s) boolean(input_buttons & BIT(17))
+ #define PHYS_INPUT_BUTTON_BUTTON16(s) boolean(input_buttons & BIT(18))
+
+ #define PHYS_DEAD(s) ((s).csqcmodel_isdead)
+
+ #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE))
+ #define GAMEPLAYFIX_NOGRAVITYONGROUND (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND))
+ #define GAMEPLAYFIX_Q2AIRACCELERATE (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE))
+
+ #define IS_DUCKED(s) (boolean((s).flags & FL_DUCKED))
+ #define SET_DUCKED(s) ((s).flags |= FL_DUCKED)
+ #define UNSET_DUCKED(s) ((s).flags &= ~FL_DUCKED)
+
+ #define PHYS_JUMPSPEEDCAP_MIN autocvar_cl_jumpspeedcap_min
+ #define PHYS_JUMPSPEEDCAP_MAX autocvar_cl_jumpspeedcap_max
+
+ #define PHYS_CL_TRACK_CANJUMP(s) STAT(MOVEVARS_CL_TRACK_CANJUMP, s)
+ // FIXME: 0 doesn't mean zero gravity
+ #define PHYS_ENTGRAVITY(s) STAT(MOVEVARS_ENTGRAVITY, s)
+
+#elif defined(SVQC)
+
+ bool Physics_Valid(string thecvar);
+
+ .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
+ .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED);
+
+ /** Not real stats */
+ .string jumpspeedcap_min;
+ .string jumpspeedcap_max;
+
+ #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
+
+ #define PHYS_GRAVITY(s) autocvar_sv_gravity
+
+ #define TICRATE sys_frametime
+
+ #define PHYS_INPUT_ANGLES(s) ((s).v_angle)
+ #define PHYS_WORLD_ANGLES(s) ((s).angles)
+
+ #define PHYS_INPUT_TIMELENGTH frametime
+ #define PHYS_INPUT_FRAMETIME sys_frametime
+
+ #define PHYS_INPUT_MOVEVALUES(s) ((s).movement)
+
+ #define PHYS_INPUT_BUTTON_BUTTON1(s) ((s).button0)
+ #define PHYS_INPUT_BUTTON_BUTTON2(s) ((s).button2)
+ #define PHYS_INPUT_BUTTON_BUTTON3(s) ((s).button3)
+ #define PHYS_INPUT_BUTTON_BUTTON4(s) ((s).button4)
+ #define PHYS_INPUT_BUTTON_BUTTON5(s) ((s).button5)
+ #define PHYS_INPUT_BUTTON_BUTTON6(s) ((s).button6)
+ #define PHYS_INPUT_BUTTON_BUTTON7(s) ((s).button7)
+ #define PHYS_INPUT_BUTTON_BUTTON8(s) ((s).button8)
+ #define PHYS_INPUT_BUTTON_BUTTON_USE(s) ((s).buttonuse)
+ #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) ((s).buttonchat)
+ #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) ((s).cursor_active)
+ #define PHYS_INPUT_BUTTON_BUTTON9(s) ((s).button9)
+ #define PHYS_INPUT_BUTTON_BUTTON10(s) ((s).button10)
+ #define PHYS_INPUT_BUTTON_BUTTON11(s) ((s).button11)
+ #define PHYS_INPUT_BUTTON_BUTTON12(s) ((s).button12)
+ #define PHYS_INPUT_BUTTON_BUTTON13(s) ((s).button13)
+ #define PHYS_INPUT_BUTTON_BUTTON14(s) ((s).button14)
+ #define PHYS_INPUT_BUTTON_BUTTON15(s) ((s).button15)
+ #define PHYS_INPUT_BUTTON_BUTTON16(s) ((s).button16)
+
+ #define PHYS_DEAD(s) ((s).deadflag != DEAD_NO)
+
+ #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
+ #define GAMEPLAYFIX_NOGRAVITYONGROUND autocvar_sv_gameplayfix_nogravityonground
+ #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate
+
+ #define IS_DUCKED(s) ((s).crouch)
+ #define SET_DUCKED(s) ((s).crouch = true)
+ #define UNSET_DUCKED(s) ((s).crouch = false)
+
+ #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min
+ #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max
+
+ #define PHYS_CL_TRACK_CANJUMP(s) ((s).cvar_cl_movement_track_canjump)
+ #define PHYS_ENTGRAVITY(s) ((s).gravity)
+
+#endif
+
+REGISTER_NET_C2S(setpause)
+#ifdef CSQC
+void unpause_update()
+{
+ static bool waspaused;
+ bool ispaused = PHYS_INPUT_BUTTON_CHAT(this);
+ if (ispaused == waspaused) return;
+ waspaused = ispaused;
+ // if (!serverispaused) return; // TODO: find out somehow
+ if (ispaused) return; // ignore setting pause, server will get those presses anyway, but it won't get releases
+ int channel = MSG_C2S;
+ WriteHeader(channel, setpause);
+ WriteByte(channel, ispaused);
+}
+#endif
+#ifdef SVQC
+NET_HANDLE(setpause, bool)
+{
+ bool ispaused = boolean(ReadByte());
+ PHYS_INPUT_BUTTON_CHAT(sender) = ispaused;
+ return true;
+}
+#endif
+
+#endif
+++ /dev/null
-#include "movelib.qh"
-
-#ifdef SVQC
-.vector moveto;
-
-/**
- Simulate drag
- self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
-**/
-vector movelib_dragvec(float drag, float exp_)
-{SELFPARAM();
- float lspeed,ldrag;
-
- lspeed = vlen(self.velocity);
- ldrag = lspeed * drag;
- ldrag = ldrag * (drag * exp_);
- ldrag = 1 - (ldrag / lspeed);
-
- return self.velocity * ldrag;
-}
-
-/**
- Simulate drag
- self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
-**/
-float movelib_dragflt(float fspeed,float drag,float exp_)
-{
- float ldrag;
-
- ldrag = fspeed * drag;
- ldrag = ldrag * ldrag * exp_;
- ldrag = 1 - (ldrag / fspeed);
-
- return ldrag;
-}
-
-/**
- Do a inertia simulation based on velocity.
- Basicaly, this allows you to simulate loss of steering with higher speed.
- self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
-**/
-vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax)
-{SELFPARAM();
- float influense;
-
- influense = vlen(self.velocity) * (1 / vel_max);
-
- influense = bound(newmin,influense,oldmax);
-
- return (vel_new * (1 - influense)) + (self.velocity * influense);
-}
-
-vector movelib_inertmove(vector new_vel,float new_bias)
-{SELFPARAM();
- return new_vel * new_bias + self.velocity * (1-new_bias);
-}
-
-void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce)
-{SELFPARAM();
- float deltatime;
- float acceleration;
- float mspeed;
- vector breakvec;
-
- deltatime = time - self.movelib_lastupdate;
- if (deltatime > 0.15) deltatime = 0;
- self.movelib_lastupdate = time;
- if (!deltatime) return;
-
- mspeed = vlen(self.velocity);
-
- if (theMass)
- acceleration = vlen(force) / theMass;
- else
- acceleration = vlen(force);
-
- if (self.flags & FL_ONGROUND)
- {
- if (breakforce)
- {
- breakvec = (normalize(self.velocity) * (breakforce / theMass) * deltatime);
- self.velocity = self.velocity - breakvec;
- }
-
- self.velocity = self.velocity + force * (acceleration * deltatime);
- }
-
- if (drag)
- self.velocity = movelib_dragvec(drag, 1);
-
- if (self.waterlevel > 1)
- {
- self.velocity = self.velocity + force * (acceleration * deltatime);
- self.velocity = self.velocity + '0 0 0.05' * autocvar_sv_gravity * deltatime;
- }
- else
- self.velocity = self.velocity + '0 0 -1' * autocvar_sv_gravity * deltatime;
-
- mspeed = vlen(self.velocity);
-
- if (max_velocity)
- if (mspeed > max_velocity)
- self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity;
-}
-
-/*
-.float mass;
-.float side_friction;
-.float ground_friction;
-.float air_friction;
-.float water_friction;
-.float buoyancy;
-float movelib_deltatime;
-
-void movelib_startupdate()
-{
- movelib_deltatime = time - self.movelib_lastupdate;
-
- if (movelib_deltatime > 0.5)
- movelib_deltatime = 0;
-
- self.movelib_lastupdate = time;
-}
-
-void movelib_update(vector dir,float force)
-{
- vector acceleration;
- float old_speed;
- float ffriction,v_z;
-
- vector breakvec;
- vector old_dir;
- vector ggravity;
- vector old;
-
- if(!movelib_deltatime)
- return;
- v_z = self.velocity_z;
- old_speed = vlen(self.velocity);
- old_dir = normalize(self.velocity);
-
- //ggravity = (autocvar_sv_gravity / self.mass) * '0 0 100';
- acceleration = (force / self.mass) * dir;
- //acceleration -= old_dir * (old_speed / self.mass);
- acceleration -= ggravity;
-
- if(self.waterlevel > 1)
- {
- ffriction = self.water_friction;
- acceleration += self.buoyancy * '0 0 1';
- }
- else
- if(self.flags & FL_ONGROUND)
- ffriction = self.ground_friction;
- else
- ffriction = self.air_friction;
-
- acceleration *= ffriction;
- //self.velocity = self.velocity * (ffriction * movelib_deltatime);
- self.velocity += acceleration * movelib_deltatime;
- self.velocity_z = v_z;
-
-}
-*/
-
-void movelib_beak_simple(float force)
-{SELFPARAM();
- float mspeed;
- vector mdir;
- float vz;
-
- mspeed = max(0,vlen(self.velocity) - force);
- mdir = normalize(self.velocity);
- vz = self.velocity.z;
- self.velocity = mdir * mspeed;
- self.velocity_z = vz;
-}
-
-/**
-Pitches and rolls the entity to match the gound.
-Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
-**/
-#endif
-
-void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max)
-{SELFPARAM();
- vector a, b, c, d, e, r, push_angle, ahead, side;
-
- push_angle.y = 0;
- r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up);
- e = v_up * spring_length;
-
- // Put springs slightly inside bbox
- ahead = v_forward * (self.maxs.x * 0.8);
- side = v_right * (self.maxs.y * 0.8);
-
- a = r + ahead + side;
- b = r + ahead - side;
- c = r - ahead + side;
- d = r - ahead - side;
-
- traceline(a, a - e,MOVE_NORMAL,self);
- a.z = (1 - trace_fraction);
- r = trace_endpos;
-
- traceline(b, b - e,MOVE_NORMAL,self);
- b.z = (1 - trace_fraction);
- r += trace_endpos;
-
- traceline(c, c - e,MOVE_NORMAL,self);
- c.z = (1 - trace_fraction);
- r += trace_endpos;
-
- traceline(d, d - e,MOVE_NORMAL,self);
- d.z = (1 - trace_fraction);
- r += trace_endpos;
-
- a.x = r.z;
- r = self.origin;
- r.z = r.z;
-
- push_angle.x = (a.z - c.z) * _max;
- push_angle.x += (b.z - d.z) * _max;
-
- push_angle.z = (b.z - a.z) * _max;
- push_angle.z += (d.z - c.z) * _max;
-
- //self.angles_x += push_angle_x * 0.95;
- //self.angles_z += push_angle_z * 0.95;
-
- self.angles_x = ((1-blendrate) * self.angles.x) + (push_angle.x * blendrate);
- self.angles_z = ((1-blendrate) * self.angles.z) + (push_angle.z * blendrate);
-
- //a = self.origin;
- setorigin(self,r);
-}
-
+++ /dev/null
-#ifndef MOVELIB_H
-#define MOVELIB_H
-
-#ifdef SVQC
-.vector moveto;
-
-/**
- Simulate drag
- self.velocity = movelib_dragvec(self.velocity,0.02,0.5);
-**/
-vector movelib_dragvec(float drag, float exp_);
-
-/**
- Simulate drag
- self.velocity *= movelib_dragflt(somespeed,0.01,0.7);
-**/
-float movelib_dragflt(float fspeed,float drag,float exp_);
-
-/**
- Do a inertia simulation based on velocity.
- Basicaly, this allows you to simulate loss of steering with higher speed.
- self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9);
-**/
-vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax);
-
-vector movelib_inertmove(vector new_vel,float new_bias);
-
-.float movelib_lastupdate;
-void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce);
-
-/*
-void movelib_move_simple(vector newdir,float velo,float blendrate)
-{
- self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo;
-}
-*/
-#define movelib_move_simple(newdir,velo,blendrate) \
- self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo
-
-#define movelib_move_simple_gravity(newdir,velo,blendrate) \
- if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate)
-
-void movelib_beak_simple(float force);
-
-/**
-Pitches and rolls the entity to match the gound.
-Yed need to set v_up and v_forward (generally by calling makevectors) before calling this.
-**/
-#endif
-
-void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max);
-
-#endif
#include "item_key.qc"
#include "mapvoting.qc"
#include "miscfunctions.qc"
-#include "movelib.qc"
#include "playerdemo.qc"
#include "portals.qc"
#include "race.qc"