]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/hud/panel/strafehud.qc
strafehud: properly calculate per-frame acceleration
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / panel / strafehud.qc
index 99c02029665f3578f120bc4f96944f783c1c9056..41059c5271089d3749c67509097c0fa27f0adec1 100644 (file)
@@ -22,20 +22,6 @@ void HUD_StrafeHUD_Export(int fh)
 
 float hidden_width;
 int direction;
-float demo_angle = -37;
-float demo_direction = 1;
-float demo_time = 0;
-bool state_onground = false;
-float state_onground_time = 0;
-bool state_strafekeys = false;
-float state_strafekeys_time = 0;
-bool turn = false;
-float turnangle;
-float turnspeed;
-float turnaccel;
-bool fwd = true;
-float starttime = 0;
-float startspeed = -1;
 
 // provide basic panel cvars to old clients
 // TODO remove them after a future release (0.8.2+)
@@ -95,6 +81,24 @@ void HUD_StrafeHUD()
     // draw strafehud
     if(csqcplayer && strafeplayer)
     {
+        // presistent
+        static float demo_angle = -37;
+        static float demo_direction = 1;
+        static float demo_time = 0;
+        static bool state_onground = false;
+        static float state_onground_time = 0;
+        static bool state_strafekeys = false;
+        static float state_strafekeys_time = 0;
+        static bool turn = false;
+        static float turnangle;
+        static float turnspeed;
+        static float turnaccel;
+        static bool fwd = true;
+        static float strafe_dt_time = 0;
+        static int strafe_dt_count = 0;
+        static float strafe_dt_sum = 0;
+        static float strafe_dt_avg = 0;
+
         // physics
         bool   onground                      = islocal ? IS_ONGROUND(strafeplayer) : !(strafeplayer.anim_implicit_state & ANIMIMPLICITSTATE_INAIR);
         bool   strafekeys;
@@ -106,8 +110,10 @@ void HUD_StrafeHUD()
         float  maxspeed_phys                 = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
         float  maxspeed                      = !autocvar__hud_configure ? maxspeed_phys * crouch_mod * water_mod : 320;
         float  movespeed;
+        float  bestspeed;
         float  maxaccel_phys                 = onground ? PHYS_ACCELERATE(strafeplayer) : PHYS_AIRACCELERATE(strafeplayer);
         float  maxaccel                      = !autocvar__hud_configure ? maxaccel_phys * crouch_mod * water_mod : 1;
+        float  frametime_phys;
         float  vel_angle                     = vectoangles(strafeplayer.velocity).y - (vectoangles(strafeplayer.velocity).y > 180 ? 360 : 0); // change the range from 0° - 360° to -180° - 180° to match how view_angle represents angles
         float  view_angle                    = PHYS_INPUT_ANGLES(strafeplayer).y;
         float  angle;
@@ -160,6 +166,27 @@ void HUD_StrafeHUD()
         if(!autocvar_hud_panel_strafehud_uncapped)
             arrow_size = max(arrow_size, 1);
 
+        // determine frametime
+        if((csqcplayer_status == CSQCPLAYERSTATUS_PREDICTED) && (input_timelength > 0))
+            frametime_phys = input_timelength;
+        else
+            frametime_phys = ticrate;
+
+        if(frametime_phys > .05) // server splits frames longer than 50 ms into two moves
+            frametime_phys /= 2;
+
+        // calculate average frametime
+        strafe_dt_sum += frametime_phys;
+        ++strafe_dt_count;
+
+        if(((time - strafe_dt_time) > autocvar_hud_panel_strafehud_fps_update) || (strafe_dt_time == 0))
+        {
+            strafe_dt_avg = strafe_dt_sum / strafe_dt_count;
+
+            strafe_dt_time = time;
+            strafe_dt_count = strafe_dt_sum = 0;
+        }
+
         // determine whether the player is pressing forwards or backwards keys
         if(islocal) // if entity is local player
         {
@@ -367,7 +394,9 @@ void HUD_StrafeHUD()
             }
         }
 
-        minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? (movespeed - maxaccel) + antiflicker_speed : autocvar_hud_panel_strafehud_switch_minspeed;
+        maxaccel *= strafe_dt_avg * movespeed;
+        bestspeed = max(movespeed - maxaccel, 0);
+        minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? bestspeed : autocvar_hud_panel_strafehud_switch_minspeed;
 
         // get current strafing angle ranging from -180° to +180°
         if(!autocvar__hud_configure)
@@ -469,8 +498,8 @@ void HUD_StrafeHUD()
         }
 
         // best angle to strafe at
-        bestangle = (speed > fabs(movespeed - maxaccel) ? acos(fabs(movespeed - maxaccel) / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
-        prebestangle = (speed > fabs(movespeed) ? acos(fabs(movespeed) / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
+        bestangle = (speed > bestspeed ? acos(bestspeed / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
+        prebestangle = (speed > movespeed ? acos(movespeed / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
         odd_bestangle = -bestangle - wishangle;
         bestangle -= wishangle;
         prebestangle -= wishangle;
@@ -696,7 +725,7 @@ void HUD_StrafeHUD()
             drawfill(panel_pos + eX * (indicator_direction ? 0 : panel_size.x - direction_size_horizontal.x) + eY * panel_size.y, direction_size_horizontal, autocvar_hud_panel_strafehud_direction_color, autocvar_hud_panel_strafehud_direction_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
         }
 
-        if(speed <= (fabs(movespeed - maxaccel) + antiflicker_speed) && !immobile)
+        if(speed <= bestspeed && !immobile)
         {
             bestangle_anywhere = true; // moving forward should suffice to gain speed
         }
@@ -812,7 +841,8 @@ void HUD_StrafeHUD()
         // show speed when crossing the start trigger
         if(autocvar_hud_panel_strafehud_startspeed_fade > 0)
         {
-            float text_alpha = 0;
+            static float startspeed = 0, starttime = 0; // displayed value and timestamp for fade out
+
             if((race_nextcheckpoint == 1) || (race_checkpoint == 254 && race_nextcheckpoint == 255)) // check if the start trigger was hit (will also trigger if the finish trigger was hit if those have the same ID)
             {
                 if(starttime != race_checkpointtime)
@@ -821,16 +851,10 @@ void HUD_StrafeHUD()
                     startspeed = speed;
                 }
             }
-            if(startspeed >= 0)
-            {
-                text_alpha = cos(((time - starttime) / autocvar_hud_panel_strafehud_startspeed_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does
-                if((time - starttime) > autocvar_hud_panel_strafehud_startspeed_fade)
-                {
-                    startspeed = -1;
-                }
-            }
-            if(startspeed >= 0 && text_alpha > 0 && autocvar_hud_panel_strafehud_startspeed_size > 0)
+
+            if((starttime > 0) && ((time - starttime) <= autocvar_hud_panel_strafehud_startspeed_fade) && autocvar_hud_panel_strafehud_startspeed_size > 0)
             {
+                float text_alpha = cos(((time - starttime) / autocvar_hud_panel_strafehud_startspeed_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does
                 vector startspeed_size = panel_size;
                 startspeed_size.y = autocvar_hud_panel_strafehud_startspeed_size;
                 if(!autocvar_hud_panel_strafehud_uncapped)
@@ -853,33 +877,33 @@ void HUD_StrafeHUD()
                 drawstring_aspect(panel_pos + eY * (panel_size.y + text_offset), strcat(ftos_decimals(startspeed * speed_conversion_factor, 2), autocvar_hud_panel_strafehud_unit_show ? speed_unit : ""), startspeed_size, autocvar_hud_panel_strafehud_startspeed_color, text_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
             }
         }
-        else
-        {
-            starttime = 0;
-            startspeed = -1;
-        }
 
         // show height achieved by a single jump
+        // FIXME: checking z position differences is unreliable (warpzones, teleporter, kill, etc) but using velocity to calculate jump height would be
+        //        inaccurate in hud code (possibly different tick rate, doesn't run when hud isn't drawn, rounding errors)
         if(autocvar_hud_panel_strafehud_jumpheight_fade > 0 && autocvar_hud_panel_strafehud_jumpheight_size > 0)
         {
             static float height_min = 0, height_max = 0; // ground and peak of jump z coordinates
             static float jumpheight = 0, jumptime = 0;   // displayed value and timestamp for fade out
 
-            // tries to catch kill and spectate but those are not reliable, should just hook to kill/spectate/teleport and reset jump height there
-            if((strafeplayer.velocity.z <= 0 && height_max >= strafeplayer.origin.z) || onground || swimming || IS_DEAD(strafeplayer) || spectating)
+            // tries to catch kill and spectate but those are not reliable
+            if((strafeplayer.velocity.z <= 0) || onground || swimming || IS_DEAD(strafeplayer) || spectating)
             {
                 height_min = height_max = strafeplayer.origin.z;
             }
             else if(strafeplayer.origin.z > height_max)
             {
                 height_max = strafeplayer.origin.z;
-                jumpheight = (height_max - height_min) * length_conversion_factor;
+                float jumpheight_new = (height_max - height_min) * length_conversion_factor;
 
-                if(jumpheight > max(autocvar_hud_panel_strafehud_jumpheight_min, 0))
+                if(jumpheight_new > max(autocvar_hud_panel_strafehud_jumpheight_min, 0))
+                {
+                    jumpheight = jumpheight_new;
                     jumptime = time;
+                }
             }
 
-            if((time - jumptime) <= autocvar_hud_panel_strafehud_jumpheight_fade)
+            if((jumptime > 0) && (time - jumptime) <= autocvar_hud_panel_strafehud_jumpheight_fade)
             {
                 float text_alpha = cos(((time - jumptime) / autocvar_hud_panel_strafehud_jumpheight_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does
                 vector jumpheight_size = panel_size;