]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/physics/player.qc
Merge branch 'martin-t/warns' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / physics / player.qc
index 74a44252fe8bd6d7fc8a11a177ca7ad9c1dfd8ee..7d3cab007b508fa6dafceb84a14585d0fd68f327 100644 (file)
 // client side physics
 bool Physics_Valid(string thecvar)
 {
-       return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
+       return autocvar_g_physics_clientselect && thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar);
 }
 
 float Physics_ClientOption(entity this, string option, float defaultval)
 {
-       if(Physics_Valid(this.cvar_cl_physics))
+       if(IS_REAL_CLIENT(this) && Physics_Valid(CS(this).cvar_cl_physics))
        {
-               string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option);
+               string s = strcat("g_physics_", CS(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)
+       if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "")
        {
-               string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option);
+               string s = strcat("g_physics_", autocvar_g_physics_clientselect_default, "_", option);
                if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
                        return cvar(s);
        }
        return defaultval;
 }
 
-void Physics_UpdateStats(entity this, float maxspd_mod)
+void Physics_UpdateStats(entity this)
 {
+       // update this first, as it's used on all stats (wouldn't want to update them all manually from a mutator hook now, would we?)
+       STAT(MOVEVARS_HIGHSPEED, this) = autocvar_g_movement_highspeed;
+
+       MUTATOR_CALLHOOK(PlayerPhysics_UpdateStats, this);
+       float maxspd_mod = PHYS_HIGHSPEED(this);
+
        STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw), maxspd_mod);
        STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw))
                ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw), maxspd_mod)
@@ -39,6 +45,13 @@ void Physics_UpdateStats(entity this, float maxspd_mod)
        STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
        STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed", autocvar_sv_maxspeed) * maxspd_mod; // also slow walking
 
+       STAT(PL_MIN, this) = autocvar_sv_player_mins;
+       STAT(PL_MAX, this) = autocvar_sv_player_maxs;
+       STAT(PL_VIEW_OFS, this) = autocvar_sv_player_viewoffset;
+       STAT(PL_CROUCH_MIN, this) = autocvar_sv_player_crouch_mins;
+       STAT(PL_CROUCH_MAX, this) = autocvar_sv_player_crouch_maxs;
+       STAT(PL_CROUCH_VIEW_OFS, this) = autocvar_sv_player_crouch_viewoffset;
+
        // old stats
        // fix some new settings
        STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor", autocvar_sv_airaccel_qw_stretchfactor);
@@ -49,6 +62,8 @@ void Physics_UpdateStats(entity this, float maxspd_mod)
        STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction", autocvar_sv_airaccel_sideways_friction);
        STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol", autocvar_sv_aircontrol);
        STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power", autocvar_sv_aircontrol_power);
+       STAT(MOVEVARS_AIRCONTROL_BACKWARDS, this) = Physics_ClientOption(this, "aircontrol_backwards", autocvar_sv_aircontrol_backwards);
+       STAT(MOVEVARS_AIRCONTROL_SIDEWARDS, this) = Physics_ClientOption(this, "aircontrol_sidewards", autocvar_sv_aircontrol_sidewards);
        STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty", autocvar_sv_aircontrol_penalty);
        STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel", autocvar_sv_warsowbunny_airforwardaccel);
        STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed", autocvar_sv_warsowbunny_topspeed);
@@ -60,6 +75,7 @@ void Physics_UpdateStats(entity this, float maxspd_mod)
        STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate);
        STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate);
        STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
+       STAT(MOVEVARS_JUMPVELOCITY_CROUCH, this) = Physics_ClientOption(this, "jumpvelocity_crouch", autocvar_sv_jumpvelocity_crouch);
        STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
 }
 #endif
@@ -77,45 +93,7 @@ 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, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL), neworigin, MOVE_NORMAL, this); \
-               if (!trace_startsolid) \
-               { \
-                       setorigin(this, neworigin); \
-                       return; \
-               } \
-       }
-       unstick_offsets(X);
-       #undef X
+               : a * (fabs(b / a) ** _lerp);
 }
 
 void PM_ClientMovement_UpdateStatus(entity this)
@@ -126,18 +104,21 @@ void PM_ClientMovement_UpdateStatus(entity this)
 
        // set crouched
        bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
-       if(this.hook && !wasfreed(this.hook))
-               do_crouch = false;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               entity wep = viewmodels[slot];
+               if(wep.hook && !wasfreed(wep.hook))
+               {
+                       do_crouch = false;
+                       break; // don't bother checking the others
+               }
+       }
        if(this.waterlevel >= WATERLEVEL_SWIMMING)
                do_crouch = false;
        if(hud != HUD_NORMAL)
                do_crouch = false;
        if(STAT(FROZEN, this))
                do_crouch = false;
-       if((activeweapon.spawnflags & WEP_TYPE_MELEE_PRI) && viewmodel.animstate_startframe == viewmodel.anim_fire1_x && time < viewmodel.weapon_nextthink)
-               do_crouch = false;
-       if((activeweapon.spawnflags & WEP_TYPE_MELEE_SEC) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink)
-               do_crouch = false;
 
        if (do_crouch)
        {
@@ -149,7 +130,7 @@ void PM_ClientMovement_UpdateStatus(entity this)
                // wants to stand, if currently crouching we need to check for a low ceiling first
                if (IS_DUCKED(this))
                {
-                       tracebox(this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), this.origin, MOVE_NORMAL, this);
+                       tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this);
                        if (!trace_startsolid) UNSET_DUCKED(this);
                }
        }
@@ -159,9 +140,18 @@ void PM_ClientMovement_UpdateStatus(entity this)
 #endif
 }
 
-void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
+void CPM_PM_Aircontrol(entity this, float dt, vector wishdir, float wishspeed)
 {
-       float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
+       float movity = IsMoveInDirection(PHYS_CS(this).movement, 0);
+       if(PHYS_AIRCONTROL_BACKWARDS(this))
+               movity += IsMoveInDirection(PHYS_CS(this).movement, 180);
+       if(PHYS_AIRCONTROL_SIDEWARDS(this))
+       {
+               movity += IsMoveInDirection(PHYS_CS(this).movement, 90);
+               movity += IsMoveInDirection(PHYS_CS(this).movement, -90);
+       }
+
+       float k = 32 * (2 * movity - 1);
        if (k <= 0)
                return;
 
@@ -176,7 +166,7 @@ void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
 
        if (dot > 0) // we can't change direction while slowing down
        {
-               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
+               k *= (dot ** PHYS_AIRCONTROL_POWER(this)) * dt;
                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);
@@ -196,7 +186,7 @@ float AdjustAirAccelQW(float accelqw, float factor)
 //   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)
+void PM_Accelerate(entity this, float dt, 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
@@ -212,7 +202,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        vector vel_xy = vec2(this.velocity);
        vector vel_perpend = vel_xy - vel_straight * wishdir;
 
-       float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+       float step = accel * dt * wishspeed0;
 
        float vel_xy_current  = vlen(vel_xy);
        if (speedlimit)
@@ -225,7 +215,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        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 f = max(0, 1 + dt * 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
@@ -241,7 +231,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
                }
        }
        else
-               vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               vel_perpend *= max(0, 1 - dt * wishspeed * sidefric);
 
        vel_xy = vel_straight * wishdir + vel_perpend;
 
@@ -260,7 +250,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        this.velocity = vel_xy + vel_z * '0 0 1';
 }
 
-void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
+void PM_AirAccelerate(entity this, float dt, vector wishdir, float wishspeed)
 {
        if (wishspeed == 0)
                return;
@@ -270,18 +260,18 @@ void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
        float curspeed = vlen(curvel);
 
        if (wishspeed > curspeed * 1.01)
-               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * dt);
        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;
+               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * dt;
        }
        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);
+       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * dt);
 
        if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
        {
@@ -314,7 +304,8 @@ bool PlayerJump(entity this)
 #endif
 
        bool doublejump = false;
-       float mjumpheight = PHYS_JUMPVELOCITY(this);
+       float mjumpheight = ((PHYS_JUMPVELOCITY_CROUCH(this) && IS_DUCKED(this)) ? PHYS_JUMPVELOCITY_CROUCH(this) : PHYS_JUMPVELOCITY(this));
+       bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
 
        if (MUTATOR_CALLHOOK(PlayerJump, this, mjumpheight, doublejump))
                return true;
@@ -328,6 +319,7 @@ bool PlayerJump(entity this)
                {
                        doublejump = true;
                        mjumpheight *= 0.7;
+                       track_jump = true;
                }
                else
                {
@@ -337,10 +329,9 @@ bool PlayerJump(entity this)
        }
 
        if (!doublejump)
-               if (!IS_ONGROUND(this))
+               if (!IS_ONGROUND(this) && !IS_ONSLICK(this))
                        return IS_JUMP_HELD(this);
 
-       bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
        if(PHYS_TRACK_CANJUMP(this))
                track_jump = true;
 
@@ -374,11 +365,11 @@ bool PlayerJump(entity this)
                }
        }
 
-       if (!WAS_ONGROUND(this))
+       if (!WAS_ONGROUND(this) && !WAS_ONSLICK(this))
        {
 #ifdef SVQC
                if(autocvar_speedmeter)
-                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+                       LOG_TRACE("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")");
 #endif
                if(this.lastground < time - 0.3)
                {
@@ -388,7 +379,7 @@ bool PlayerJump(entity this)
                }
 #ifdef SVQC
                if(this.jumppadcount > 1)
-                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+                       LOG_TRACE(ftos(this.jumppadcount), "x jumppad combo");
                this.jumppadcount = 0;
 #endif
        }
@@ -396,6 +387,7 @@ bool PlayerJump(entity this)
        this.velocity_z += mjumpheight;
 
        UNSET_ONGROUND(this);
+       UNSET_ONSLICK(this);
        SET_JUMP_HELD(this);
 
 #ifdef SVQC
@@ -442,7 +434,7 @@ void CheckWaterJump(entity this)
 
 
 #ifdef SVQC
-       #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump
+       #define JETPACK_JUMP(s) CS(s).cvar_cl_jetpack_jump
 #elif defined(CSQC)
        float autocvar_cl_jetpack_jump;
        #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump
@@ -492,33 +484,20 @@ void CheckPlayerJump(entity this)
                CheckWaterJump(this);
 }
 
-float racecar_angle(float forward, float down)
+#ifdef SVQC
+string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
+.float specialcommand_pos;
+void SpecialCommand(entity this)
 {
-       if (forward < 0)
+       if(autocvar_sv_cheats || this.maycheat)
        {
-               forward = -forward;
-               down = -down;
+               if (!CheatImpulse(this, CHIMPULSE_GIVE_ALL.impulse))
+                       LOG_INFO("A hollow voice says \"Plugh\".");
        }
-
-       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;
+               STAT(MOVEVARS_SPECIALCOMMAND, this) = true;
 }
-
-string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
-.float specialcommand_pos;
-void SpecialCommand(entity this)
-{
-#ifdef SVQC
-       if (!CheatImpulse(this, CHIMPULSE_GIVE_ALL.impulse))
-               LOG_INFO("A hollow voice says \"Plugh\".\n");
 #endif
-}
 
 bool PM_check_specialcommand(entity this, int buttons)
 {
@@ -541,18 +520,18 @@ bool PM_check_specialcommand(entity this, int buttons)
        else
                c = "?";
 
-       if (c == substring(specialcommand, this.specialcommand_pos, 1))
+       if (c == substring(specialcommand, CS(this).specialcommand_pos, 1))
        {
-               this.specialcommand_pos += 1;
-               if (this.specialcommand_pos >= strlen(specialcommand))
+               CS(this).specialcommand_pos += 1;
+               if (CS(this).specialcommand_pos >= strlen(specialcommand))
                {
-                       this.specialcommand_pos = 0;
+                       CS(this).specialcommand_pos = 0;
                        SpecialCommand(this);
                        return true;
                }
        }
-       else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1)))
-               this.specialcommand_pos = 0;
+       else if (CS(this).specialcommand_pos && (c != substring(specialcommand, CS(this).specialcommand_pos - 1, 1)))
+               CS(this).specialcommand_pos = 0;
 #endif
        return false;
 }
@@ -565,7 +544,7 @@ void PM_check_nickspam(entity this)
        if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow)
        {
                // slight annoyance for nick change scripts
-               this.movement = -1 * this.movement;
+               PHYS_CS(this).movement = -1 * PHYS_CS(this).movement;
                PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = PHYS_INPUT_BUTTON_HOOK(this) = PHYS_INPUT_BUTTON_USE(this) = false;
 
                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!
@@ -579,12 +558,12 @@ void PM_check_nickspam(entity this)
 #endif
 }
 
-void PM_check_punch(entity this)
+void PM_check_punch(entity this, float dt)
 {
 #ifdef SVQC
        if (this.punchangle != '0 0 0')
        {
-               float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchangle) - 10 * dt;
                if (f > 0)
                        this.punchangle = normalize(this.punchangle) * f;
                else
@@ -593,7 +572,7 @@ void PM_check_punch(entity this)
 
        if (this.punchvector != '0 0 0')
        {
-               float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchvector) - 30 * dt;
                if (f > 0)
                        this.punchvector = normalize(this.punchvector) * f;
                else
@@ -613,12 +592,12 @@ void PM_check_frozen(entity 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);
+               PHYS_CS(this).movement_x = bound(-5, PHYS_CS(this).movement.x, 5);
+               PHYS_CS(this).movement_y = bound(-5, PHYS_CS(this).movement.y, 5);
+               PHYS_CS(this).movement_z = bound(-5, PHYS_CS(this).movement.z, 5);
        }
        else
-               this.movement = '0 0 0';
+               PHYS_CS(this).movement = '0 0 0';
 
        vector midpoint = ((this.absmin + this.absmax) * 0.5);
        if (pointcontents(midpoint) == CONTENT_WATER)
@@ -637,7 +616,12 @@ void PM_check_hitground(entity this)
     this.wasFlying = false;
     if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
     if (time < this.ladder_time) return;
-    if (this.hook) return;
+    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+    {
+       .entity weaponentity = weaponentities[slot];
+       if(this.(weaponentity).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);
@@ -671,24 +655,40 @@ void PM_Footsteps(entity this)
 #endif
 }
 
+void PM_check_slick(entity this)
+{
+       if(!IS_ONGROUND(this))
+               return;
+
+       if(!PHYS_SLICK_APPLYGRAVITY(this))
+               return;
+
+       tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+       {
+               UNSET_ONGROUND(this);
+               SET_ONSLICK(this);
+       }
+       else
+               UNSET_ONSLICK(this);
+}
+
 void PM_check_blocked(entity this)
 {
 #ifdef SVQC
        if (!this.player_blocked)
                return;
-       this.movement = '0 0 0';
+       PHYS_CS(this).movement = '0 0 0';
        this.disableclientprediction = 1;
 #endif
 }
 
-.vector oldmovement;
-
-void PM_jetpack(entity this, float maxspd_mod)
+void PM_jetpack(entity this, float maxspd_mod, float dt)
 {
        //makevectors(this.v_angle.y * '0 1 0');
        makevectors(this.v_angle);
-       vector wishvel = v_forward * this.movement_x
-                                       + v_right * this.movement_y;
+       vector wishvel = v_forward * PHYS_CS(this).movement_x
+                                       + v_right * PHYS_CS(this).movement_y;
        // add remaining speed as Z component
        float maxairspd = PHYS_MAXAIRSPEED(this) * max(1, maxspd_mod);
        // fix speedhacks :P
@@ -704,14 +704,14 @@ void PM_jetpack(entity this, float maxspd_mod)
        float a_up = PHYS_JETPACK_ACCEL_UP(this);
        float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
 
-       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { a_up = PHYS_JETPACK_REVERSE_THRUST(this); }
+       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(this)) { a_up = PHYS_JETPACK_REVERSE_THRUST(this); }
 
        wishvel_x *= a_side;
        wishvel_y *= a_side;
        wishvel_z *= a_up;
        wishvel_z += a_add;
 
-       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { wishvel_z *= -1; }
+       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(this)) { wishvel_z *= -1; }
 
        float best = 0;
        //////////////////////////////////////////////////////////////////////////////////////
@@ -768,7 +768,7 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        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));
+               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * dt * fvel));
        else
                f = 1;
 
@@ -776,12 +776,12 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        if (f > 0 && wishvel != '0 0 0')
        {
-               this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+               this.velocity = this.velocity + wishvel * f * dt;
                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;
+                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f;
 
                ITEMS_STAT(this) |= IT_USING_JETPACK;
 
@@ -812,9 +812,20 @@ void SV_PlayerPhysics(entity this)
 void CSQC_ClientMovement_PlayerMove_Frame(entity this)
 #endif
 {
+#ifdef SVQC
+       // needs to be called before physics are run!
+       if(IS_REAL_CLIENT(this))
+               PM_UpdateButtons(this, CS(this));
+#endif
+
        sys_phys_update(this, PHYS_INPUT_TIMELENGTH);
 
 #ifdef SVQC
-       this.pm_frametime = frametime;
+       CS(this).pm_frametime = frametime;
+#elif defined(CSQC)
+       if((ITEMS_STAT(this) & IT_USING_JETPACK) && !IS_DEAD(this) && !intermission)
+               this.csqcmodel_modelflags |= MF_ROCKET;
+       else
+               this.csqcmodel_modelflags &= ~MF_ROCKET;
 #endif
 }