From: otta8634 Date: Sat, 31 Aug 2024 09:50:35 +0000 (+0800) Subject: Add fastest-W-turning angle to strafehud X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=4e95149c9333f7968aa68ff998c09e8bbafdbec1;p=xonotic%2Fxonotic-data.pk3dir.git Add fastest-W-turning angle to strafehud 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 --- diff --git a/_hud_common.cfg b/_hud_common.cfg index 954ffbd80..f8936dfbf 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -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" diff --git a/qcsrc/client/hud/panel/strafehud.qc b/qcsrc/client/hud/panel/strafehud.qc index 66fd55bf0..802257c9a 100644 --- a/qcsrc/client/hud/panel/strafehud.qc +++ b/qcsrc/client/hud/panel/strafehud.qc @@ -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; diff --git a/qcsrc/client/hud/panel/strafehud.qh b/qcsrc/client/hud/panel/strafehud.qh index 834c5a5e7..3e3fad5b3 100644 --- a/qcsrc/client/hud/panel/strafehud.qh +++ b/qcsrc/client/hud/panel/strafehud.qh @@ -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);