]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Add fastest-W-turning angle to strafehud
authorotta8634 <k9wolf@pm.me>
Sat, 31 Aug 2024 09:50:35 +0000 (17:50 +0800)
committerotta8634 <k9wolf@pm.me>
Sun, 1 Sep 2024 05:52:06 +0000 (13:52 +0800)
Adds angle similar to _switch for where to aim to maximally rotate velocity vector when W-turning
Only operational with sv_airaccel_qw == 1, sv_aircontrol == 150, and some other restrictions

_hud_common.cfg
qcsrc/client/hud/panel/strafehud.qc
qcsrc/client/hud/panel/strafehud.qh

index 954ffbd80885cc3774160376b614e13e3b943b06..f8936dfbf691467eb8a5281a7d4ec31ff8ebe09b 100644 (file)
@@ -196,6 +196,11 @@ seta hud_panel_strafehud_switch_minspeed "-1" "minimum speed in qu/s at which sw
 seta hud_panel_strafehud_switch_color "1 1 0" "color of the switch indicator"
 seta hud_panel_strafehud_switch_alpha "1" "opacity of the switch indicator"
 seta hud_panel_strafehud_switch_width "0.003" "width of the strafe angle indicator(s) (relative to the strafe bar width)"
+seta hud_panel_strafehud_wturn "1" "enable the W-turn indicators showing the angle to rotate your velocity as fast as possible, 1 = only if W-turning, 2 = also while strafing, 3 = always"
+seta hud_panel_strafehud_wturn_color "0 1 1" "color of the W-turn indicators"
+seta hud_panel_strafehud_wturn_alpha "1" "opacity of the W-turn indicators"
+seta hud_panel_strafehud_wturn_width "0.003" "width of the W-turn indicators (relative to the strafe bar width)"
+seta hud_panel_strafehud_wturn_proper "0" "use the proper formula to calculate W-turn indicators (warning: loses accuracy at high speeds)"
 seta hud_panel_strafehud_direction "0" "set to \"1\" to enable the direction caps to see in which direction you are currently strafing"
 seta hud_panel_strafehud_direction_color "0 0.5 1" "color of the direction caps which indicate the direction the player is currently strafing towards"
 seta hud_panel_strafehud_direction_alpha "1" "opacity of the direction caps which indicate the direction the player is currently strafing towards"
index 66fd55bf093383ee69a402560856f6a3239dab57..802257c9a9820e2e9dac8ff64a5b590780f8e87c 100644 (file)
@@ -140,6 +140,8 @@ void HUD_StrafeHUD()
                float  bestspeed;
                float  maxaccel_phys                 = onground ? PHYS_ACCELERATE(strafeplayer) : PHYS_AIRACCELERATE(strafeplayer);
                float  maxaccel                      = !autocvar__hud_configure ? maxaccel_phys : 1;
+               float  aircontrol                    = PHYS_AIRCONTROL(strafeplayer);
+               bool   wturn_no_accel                = PHYS_AIRACCEL_QW(strafeplayer) == 1 && aircontrol == 150; // conditions such that W-turning doesn't provide accel
                // change the range from 0° - 360° to -180° - 180° to match how view_angle represents angles
                float  vel_angle                     = vectoangles(strafeplayer.velocity).y - (vectoangles(strafeplayer.velocity).y > 180 ? 360 : 0);
                float  view_angle                    = PHYS_INPUT_ANGLES(strafeplayer).y;
@@ -176,7 +178,11 @@ void HUD_StrafeHUD()
                bool   odd_angles                    = false;
                float  odd_bestangle_offset          = 0;
                float  switch_odd_bestangle_offset   = 0;
-               float  bestangle_width;
+               float  switch_bestangle_width;
+               float  wturn_bestangle               = 0;
+               float  wturn_left_bestangle_offset   = 0;
+               float  wturn_right_bestangle_offset  = 0;
+               float  wturn_bestangle_width         = 0;
                float  accelzone_left_offset;
                float  accelzone_right_offset;
                float  accelzone_width;
@@ -193,8 +199,9 @@ void HUD_StrafeHUD()
                float  text_offset_bottom            = 0;
 
                // real_* variables which are always positive with no wishangle offset
-               float real_bestangle;
-               float real_prebestangle;
+               float  real_bestangle;
+               float  real_prebestangle;
+               float  real_wturn_bestangle          = 0;
 
                if(autocvar_hud_panel_strafehud_mode >= 0 && autocvar_hud_panel_strafehud_mode <= 1)
                        mode = autocvar_hud_panel_strafehud_mode;
@@ -377,7 +384,7 @@ void HUD_StrafeHUD()
                        else if(turn_expired)
                                turn = false;
 
-                       if(turn) // CPMA turning
+                       if(turn) // CPMA turning (A/D, not W-turning)
                        {
                                if(strafekeys)
                                {
@@ -541,6 +548,35 @@ void HUD_StrafeHUD()
                // in case of ground friction we may decelerate if the acceleration is smaller than the speed loss from friction
                real_bestangle = bestangle = (strafespeed > bestspeed ? acos(bestspeed / strafespeed) * RAD2DEG : 0);
                real_prebestangle = prebestangle = (strafespeed > movespeed ? acos(movespeed / strafespeed) * RAD2DEG : 0);
+
+               /*
+                * k9er: proper W-turn angle is acos(-speed/g * (cos((acos(V) + M_PI * 2) / 3) * 2 + 1)) rad,
+                * ... where g=dt*32*150, and V=1-(g*g)/(speed*speed),
+                * ... but this very quickly loses accuracy -- should be a strictly decreasing function, yet it increases at only speed=722 with 125 fps
+                * also note this is only valid when such angle is not in the accelzone, formula taking acceleration into account is unfathomably complicated
+                * afaik there's no simplified version of this formula that doesn't involve complex numbers, other than one valid for only speed<27.1 roughly
+                * furthermore, this function quite rapidly approaches its asymptote of ~35.26°, e.g. being ~0.68° away when at only speed=600
+                * this asymptote is independent of whether the player is crouching or has haste, although they must be airborne
+                * thus, the best option is to just draw the asymptote (±acos(sqrt(2/3))),
+                * ... but the proper angle can be drawn too if the player wants (autocvar_hud_panel_strafehud_wturn_proper)
+                * for now this is only enabled if sv_airaccel_qw == 1 && sv_aircontrol == 150, since otherwise W-turning gives acceleration
+                */
+               if(wturn_no_accel && !immobile)
+               {
+                       float wturn_g = 32 * aircontrol * dt;
+                       float wturn_V = 1 - (wturn_g * wturn_g) / (speed * speed);
+                       if(autocvar_hud_panel_strafehud_wturn_proper && wturn_g > 1 && wturn_V < 1 && wturn_V > -1)
+                               wturn_bestangle = acos(-speed / wturn_g * (cos((acos(wturn_V) + M_PI * 2) / 3) * 2 + 1)) * RAD2DEG;
+                       else
+                               wturn_bestangle = acos(sqrt(2 / 3)) * RAD2DEG;
+                       real_wturn_bestangle = wturn_bestangle;
+                       if(fwd && turn) // if not W-turning, need to add 45/90° so it aligns to where it would be after changing to W-only
+                               wturn_bestangle += 90;
+                       else if(fwd && wishangle != 0)
+                               wturn_bestangle += 45;
+                       wturn_bestangle -= wishangle;
+               }
+
                if(direction == STRAFEHUD_DIRECTION_LEFT) // the angle becomes negative in case we strafe left
                {
                        bestangle *= -1;
@@ -577,15 +613,14 @@ void HUD_StrafeHUD()
                if(mode == STRAFEHUD_MODE_VIEW_CENTERED)
                        currentangle_offset = angle / hudangle * panel_size.x;
                else
-
                        currentangle_offset = bound(-hudangle / 2, angle, hudangle / 2) / hudangle * panel_size.x + panel_size.x / 2;
 
                // best strafe acceleration angle
                bestangle_offset        =  bestangle / hudangle * panel_size.x + panel_size.x / 2;
                switch_bestangle_offset = -bestangle / hudangle * panel_size.x + panel_size.x / 2;
-               bestangle_width = panel_size.x * autocvar_hud_panel_strafehud_switch_width;
+               switch_bestangle_width  = panel_size.x * autocvar_hud_panel_strafehud_switch_width;
                if(!autocvar_hud_panel_strafehud_uncapped)
-                       bestangle_width = max(bestangle_width, 1);
+                       switch_bestangle_width = max(switch_bestangle_width, 1);
 
                if((angle > -wishangle && direction == STRAFEHUD_DIRECTION_LEFT) || (angle < -wishangle && direction == STRAFEHUD_DIRECTION_RIGHT))
                {
@@ -593,6 +628,28 @@ void HUD_StrafeHUD()
                        odd_bestangle_offset = odd_bestangle / hudangle * panel_size.x + panel_size.x / 2;
                        switch_odd_bestangle_offset = (odd_bestangle + bestangle * 2) / hudangle * panel_size.x + panel_size.x / 2;
                }
+
+               // best angle to aim at when W-turning to maximally rotate velocity vector
+               if(wturn_no_accel && !immobile)
+               {
+                       bool wturn_show = autocvar_hud_panel_strafehud_wturn == 3 ? true
+                               : autocvar_hud_panel_strafehud_wturn == 2 ? (fwd && !turn)
+                               : autocvar_hud_panel_strafehud_wturn == 1 ? (wishangle == 0 && !turn)
+                               : false;
+                       // ensure angle isn't in the accelzone
+                       bool wturn_neutralzone = autocvar_hud_panel_strafehud_wturn_proper
+                               ? real_wturn_bestangle < acos(maxspeed / speed) * RAD2DEG
+                               : speed * speed * 2 > maxspeed * maxspeed * 3; // acos(sqrt(2 / 3)) < acos(maxspeed / speed)
+                       if(wturn_show && wturn_neutralzone && !onground)
+                       {
+                               wturn_left_bestangle_offset  =  wturn_bestangle / hudangle * panel_size.x + panel_size.x / 2;
+                               wturn_right_bestangle_offset = -wturn_bestangle / hudangle * panel_size.x + panel_size.x / 2;
+                               wturn_bestangle_width        = panel_size.x * autocvar_hud_panel_strafehud_wturn_width;
+                               if(!autocvar_hud_panel_strafehud_uncapped)
+                                       wturn_bestangle_width = max(wturn_bestangle_width, 1);
+                       }
+               }
+
                // direction indicator
                direction_size_vertical.x = autocvar_hud_panel_strafehud_direction_width;
                if(!autocvar_hud_panel_strafehud_uncapped)
@@ -672,6 +729,8 @@ void HUD_StrafeHUD()
                                switch_bestangle_offset += shift_offset;
                                odd_bestangle_offset += shift_offset;
                                switch_odd_bestangle_offset += shift_offset;
+                               wturn_left_bestangle_offset += shift_offset;
+                               wturn_right_bestangle_offset += shift_offset;
                        }
                        if(direction == STRAFEHUD_DIRECTION_LEFT)
                                shift_offset += -360 / hudangle * panel_size.x;
@@ -692,28 +751,28 @@ void HUD_StrafeHUD()
                                accelzone_left_offset, accelzone_width, hidden_width,
                                autocvar_hud_panel_strafehud_bar_accel_color,
                                autocvar_hud_panel_strafehud_bar_accel_alpha * panel_fg_alpha,
-                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_LEFT);
+                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_LEFT, false);
 
                        if(autocvar_hud_panel_strafehud_bar_preaccel)
                                HUD_Panel_DrawStrafeHUD(
                                        preaccelzone_left_offset, preaccelzone_width, hidden_width,
                                        autocvar_hud_panel_strafehud_bar_accel_color,
                                        autocvar_hud_panel_strafehud_bar_accel_alpha * panel_fg_alpha,
-                                       autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_RIGHT);
+                                       autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_RIGHT, false);
 
                        // draw right acceleration zone
                        HUD_Panel_DrawStrafeHUD(
                                accelzone_right_offset, accelzone_width, hidden_width,
                                autocvar_hud_panel_strafehud_bar_accel_color,
                                autocvar_hud_panel_strafehud_bar_accel_alpha * panel_fg_alpha,
-                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_RIGHT);
+                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_RIGHT, false);
 
                        if(autocvar_hud_panel_strafehud_bar_preaccel)
                                HUD_Panel_DrawStrafeHUD(
                                        preaccelzone_right_offset, preaccelzone_width, hidden_width,
                                        autocvar_hud_panel_strafehud_bar_accel_color,
                                        autocvar_hud_panel_strafehud_bar_accel_alpha * panel_fg_alpha,
-                                       autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_LEFT);
+                                       autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_LEFT, false);
 
                        // draw overturn zone
                        //   this is technically incorrect
@@ -723,17 +782,17 @@ void HUD_StrafeHUD()
                                overturn_offset, overturn_width, hidden_width,
                                autocvar_hud_panel_strafehud_bar_overturn_color,
                                autocvar_hud_panel_strafehud_bar_overturn_alpha * panel_fg_alpha,
-                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_BOTH);
+                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_BOTH, false);
 
                        // draw neutral zone
                        HUD_Panel_DrawStrafeHUD(
                                neutral_offset, neutral_width, hidden_width,
                                autocvar_hud_panel_strafehud_bar_neutral_color,
                                autocvar_hud_panel_strafehud_bar_neutral_alpha * panel_fg_alpha,
-                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_NONE);
+                               autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_NONE, false);
 
                        // only draw indicators if minspeed is reached
-                       if(autocvar_hud_panel_strafehud_switch && speed >= minspeed && bestangle_width > 0 && autocvar_hud_panel_strafehud_switch_alpha > 0)
+                       if(autocvar_hud_panel_strafehud_switch && speed >= minspeed && switch_bestangle_width > 0 && autocvar_hud_panel_strafehud_switch_alpha > 0)
                        {
                                // draw the switch indicator(s)
                                float offset = !odd_angles ? bestangle_offset : odd_bestangle_offset;
@@ -743,30 +802,46 @@ void HUD_StrafeHUD()
                                if(direction == STRAFEHUD_DIRECTION_LEFT)
                                {
                                        if(!odd_angles)
-                                               offset -= bestangle_width;
+                                               offset -= switch_bestangle_width;
                                        else
-                                               switch_offset -= bestangle_width;
+                                               switch_offset -= switch_bestangle_width;
                                }
                                else
                                {
                                        if(!odd_angles)
-                                               switch_offset -= bestangle_width;
+                                               switch_offset -= switch_bestangle_width;
                                        else
-                                               offset -= bestangle_width;
+                                               offset -= switch_bestangle_width;
                                }
 
                                HUD_Panel_DrawStrafeHUD(
-                                       switch_offset, bestangle_width, hidden_width,
+                                       switch_offset, switch_bestangle_width, hidden_width,
                                        autocvar_hud_panel_strafehud_switch_color,
                                        autocvar_hud_panel_strafehud_switch_alpha * panel_fg_alpha,
-                                       STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE);
+                                       STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE, false);
 
                                if(direction == STRAFEHUD_DIRECTION_NONE)
                                        HUD_Panel_DrawStrafeHUD(
-                                               offset, bestangle_width, hidden_width,
+                                               offset, switch_bestangle_width, hidden_width,
                                                autocvar_hud_panel_strafehud_switch_color,
                                                autocvar_hud_panel_strafehud_switch_alpha * panel_fg_alpha,
-                                               STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE);
+                                               STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE, false);
+                       }
+
+                       // only draw indicators if conditions were met (wturn_bestangle_width init to 0)
+                       if(autocvar_hud_panel_strafehud_wturn && wturn_bestangle_width > 0 && autocvar_hud_panel_strafehud_wturn_alpha > 0)
+                       {
+                               // draw the wturn indicators
+                               HUD_Panel_DrawStrafeHUD(
+                                       wturn_left_bestangle_offset, wturn_bestangle_width, hidden_width,
+                                       autocvar_hud_panel_strafehud_wturn_color,
+                                       autocvar_hud_panel_strafehud_wturn_alpha * panel_fg_alpha,
+                                       STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE, true);
+                               HUD_Panel_DrawStrafeHUD(
+                                       wturn_right_bestangle_offset, wturn_bestangle_width, hidden_width,
+                                       autocvar_hud_panel_strafehud_wturn_color,
+                                       autocvar_hud_panel_strafehud_wturn_alpha * panel_fg_alpha,
+                                       STRAFEHUD_STYLE_DRAWFILL, STRAFEHUD_GRADIENT_NONE, true);
                        }
                }
 
@@ -1089,7 +1164,7 @@ void HUD_StrafeHUD()
 }
 
 // functions to make hud elements align perfectly in the hud area
-void HUD_Panel_DrawStrafeHUD(float offset, float width, float hidden_width, vector color, float alpha, int type, int gradientType)
+void HUD_Panel_DrawStrafeHUD(float offset, float width, float hidden_width, vector color, float alpha, int type, int gradientType, bool offset_centered)
 {
        float mirror_offset, mirror_width;
        vector size = panel_size;
@@ -1097,6 +1172,9 @@ void HUD_Panel_DrawStrafeHUD(float offset, float width, float hidden_width, vect
        float overflow_width = 0, overflow_mirror_width = 0;
        float original_width = width; // required for gradient
 
+       if(offset_centered) // offset gives the center of the bar, not left edge
+               offset -= original_width / 2;
+
        if(type == STRAFEHUD_STYLE_GRADIENT && gradientType == STRAFEHUD_GRADIENT_NONE)
                type = STRAFEHUD_STYLE_DRAWFILL;
 
index 834c5a5e73945a8c623c1b14c3ecd71628df1214..3e3fad5b3fcc61571dab9bc545b618426d2e519d 100644 (file)
@@ -34,6 +34,11 @@ float autocvar_hud_panel_strafehud_switch_minspeed = -1;
 vector autocvar_hud_panel_strafehud_switch_color = '1 1 0';
 float autocvar_hud_panel_strafehud_switch_alpha = 1;
 float autocvar_hud_panel_strafehud_switch_width = 0.003;
+int autocvar_hud_panel_strafehud_wturn = 1;
+vector autocvar_hud_panel_strafehud_wturn_color = '0 1 1';
+float autocvar_hud_panel_strafehud_wturn_alpha = 1;
+float autocvar_hud_panel_strafehud_wturn_width = 0.003;
+bool autocvar_hud_panel_strafehud_wturn_proper = false;
 bool autocvar_hud_panel_strafehud_direction = false;
 vector autocvar_hud_panel_strafehud_direction_color = '0 0.5 1';
 float autocvar_hud_panel_strafehud_direction_alpha = 1;
@@ -59,7 +64,7 @@ float autocvar_hud_panel_strafehud_timeout_turn = 0.1;
 float autocvar_hud_panel_strafehud_antiflicker_angle = 0.01;
 float autocvar_hud_panel_strafehud_fps_update = 0.5;
 
-void HUD_Panel_DrawStrafeHUD(float, float, float, vector, float, int, int);
+void HUD_Panel_DrawStrafeHUD(float, float, float, vector, float, int, int, bool);
 vector StrafeHUD_mixColors(vector, vector, float);
 void StrafeHUD_drawGradient(vector, vector, vector, float, float, float, float, int);
 float GetLengthUnitFactor(int);