From 64d26b68b9685601ad91cbe06b100bd9cedf45a1 Mon Sep 17 00:00:00 2001 From: otta8634 Date: Tue, 3 Sep 2024 14:41:39 +0800 Subject: [PATCH] Use proper angles for strafehud calculations Previously an incorrect value was used for the accel-deaccel edge, and for preaccel when on-ground Consequently, a new cvar (hud_panel_strafehud_onground_mode) is required to handle how to draw the hud when onground at high speed --- _hud_common.cfg | 1 + qcsrc/client/hud/panel/strafehud.qc | 145 +++++++++++++++++++++------- qcsrc/client/hud/panel/strafehud.qh | 1 + 3 files changed, 114 insertions(+), 33 deletions(-) diff --git a/_hud_common.cfg b/_hud_common.cfg index fb2f22780..a6c2f0bfa 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -172,6 +172,7 @@ seta hud_panel_strafehud_range_cpma "90" "the angle range up to 360 degrees disp seta hud_panel_strafehud_style "2" "\"0\" = no styling, \"1\" = progress bar style for the strafe bar, \"2\" = gradient for the strafe bar" seta hud_panel_strafehud_unit_show "1" "show units" seta hud_panel_strafehud_uncapped "0" "set to \"1\" to remove some safety restrictions, useful to set thinner indicator lines down to 1px or for trying out higher values for some performance degrading operations (warning: elements may turn invisible if too thin, other configurations may crash your game or look horribly ugly)" +seta hud_panel_strafehud_onground_mode "2" "handling of landing at speeds where friction is higher than optimal acceleration, 0 = fill the whole hud with overturn, 1 = show zones regardless, 2 = show the zones as if airborne (useful for quake2 and quake3 physics)" seta hud_panel_strafehud_bar_preaccel "1" "set to \"1\" to extend the acceleration zone by the strafe meter zone before full acceleration can be achieved" seta hud_panel_strafehud_bar_preaccel_color "0 1 0" "color of the strafe meter pre-acceleration zone" seta hud_panel_strafehud_bar_preaccel_alpha "0.5" "opacity of the strafe meter pre-acceleration zone" diff --git a/qcsrc/client/hud/panel/strafehud.qc b/qcsrc/client/hud/panel/strafehud.qc index 6ba9e4e34..b3e04250e 100644 --- a/qcsrc/client/hud/panel/strafehud.qc +++ b/qcsrc/client/hud/panel/strafehud.qc @@ -140,6 +140,7 @@ void HUD_StrafeHUD() float bestspeed; float maxaccel_phys = onground ? PHYS_ACCELERATE(strafeplayer) : PHYS_AIRACCELERATE(strafeplayer); float maxaccel = !autocvar__hud_configure ? maxaccel_phys : 1; + float airstopaccel = PHYS_AIRSTOPACCELERATE(strafeplayer); 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 @@ -172,6 +173,7 @@ void HUD_StrafeHUD() vector currentangle_size; float bestangle; float prebestangle; + float overturn_angle; float odd_bestangle; float bestangle_offset = 0; float switch_bestangle_offset = 0; @@ -201,6 +203,7 @@ void HUD_StrafeHUD() // real_* variables which are always positive with no wishangle offset float real_bestangle; float real_prebestangle; + float real_overturn_angle; float real_wturn_bestangle = 0; if(autocvar_hud_panel_strafehud_mode >= 0 && autocvar_hud_panel_strafehud_mode <= 1) @@ -545,9 +548,82 @@ void HUD_StrafeHUD() } // best angle to strafe at - // 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); + if(immobile) + { + // these are unused (neutral fills whole strafe bar) + prebestangle = bestangle = 0; + overturn_angle = 180; + } + else + { + { + // delta_opt = acos((s - a) / v_f), same in air + bestangle = strafespeed > bestspeed + ? acos(bestspeed / strafespeed) * RAD2DEG // case 1 + : 0; // case 2 + // case 1: normal. case 2: low speed, best angle is forwards + } + { + float prebestangle_sqrt = movespeed * movespeed + strafespeed * strafespeed - speed * speed; + // delta_min = acos(sqrt(s^2 - v_f^2 + v^2) / v_f), or just acos(s / v) in air + prebestangle = (prebestangle_sqrt > 0 && strafespeed > sqrt(prebestangle_sqrt)) + ? acos(sqrt(prebestangle_sqrt) / strafespeed) * RAD2DEG // case 1 + : (prebestangle_sqrt > 0 ? 0 : 90); // case 2 : case 3 + // case 1: normal. case 2: low speed, best angle is forwards. case 3: landed at high speed, neutral zone is very large (see explanation below) + } + if(airstopaccel && !onground) + // delta_max = acos(-1.25a / v), only in air + overturn_angle = speed > maxaccel * airstopaccel / 2 + ? acos(-(maxaccel * airstopaccel / 2) / speed) * RAD2DEG // case 1 + : 180; // case 2 + // case 1: normal. case 2: low speed, turning anywhere will gain speed + else + { + float overturn_numer = speed * speed - strafespeed * strafespeed - maxaccel * maxaccel; + float overturn_denom = 2 * maxaccel * strafespeed; + // delta_max = acos((v^2 - v_f^2 - a^2) / (2av_f)), or just acos(-a / 2v) if in air + overturn_angle = overturn_denom > fabs(overturn_numer) + ? acos(overturn_numer / overturn_denom) * RAD2DEG // case 1 + : (overturn_numer > 0 ? 0 : 180); // case 2 : case 3 + // case 1: normal. case 2: landed at high speed, turning anywhere will lose speed (due to friction). case 3: low speed, turning anywhere will gain speed + } + if(onground && overturn_angle < bestangle) + { + // these conditions occur when you land at high speed (above max onground speed), such that every wishangle will result in a speed loss due to friction + // the latter condition should only ever happen when onground, but check for sanity + // if these conditions are met, prebestangle will be less than bestangle, all 3 cases deal with that + if(autocvar_hud_panel_strafehud_onground_mode == 0) + { + // make overturn fill the whole strafe bar + // most correct option by the true definition of accel, since every angle results in deceleration + prebestangle = bestangle = 0; + overturn_angle = 0; + } + else if(autocvar_hud_panel_strafehud_onground_mode == 1) + { + /* k9er: these aren't the true angles -- the real ones are very convoluted and difficult to understand + * essentially the prior definitions of the zones now overlap, + * ... with the overturn zone extending below bestangle, and eventually covering the whole hud + * ... and somehow the neutral zone extends above bestangle, and eventually covers the whole hud (i think) + * overall showing it accurately is just confusing and unnecessary to add + * thankfully the bestangle formula is unchanged, so the least confusing option is likely as follows: + */ + overturn_angle = bestangle; + prebestangle = bestangle; + } + else + { + // use angles as if in air + // no need to check if numerator < denominator, since all numerators < max onground speed < speed = all denominators + bestangle = acos(bestspeed / speed) * RAD2DEG; + prebestangle = acos(movespeed / speed) * RAD2DEG; + overturn_angle = airstopaccel ? acos(-(maxaccel * airstopaccel / 2) / speed) * RAD2DEG : acos(-(maxaccel / 2) / speed) * RAD2DEG; + } + } + } + real_bestangle = bestangle; + real_prebestangle = prebestangle; + real_overturn_angle = overturn_angle; /* * k9er: proper W-turn angle is acos(-speed/g * (cos((acos(V) + M_PI * 2) / 3) * 2 + 1)) rad, @@ -581,10 +657,12 @@ void HUD_StrafeHUD() { bestangle *= -1; prebestangle *= -1; + overturn_angle *= -1; } odd_bestangle = -bestangle - wishangle; bestangle -= wishangle; prebestangle -= wishangle; + overturn_angle -= wishangle; // various offsets and size calculations of hud indicator elements // how much is hidden by the current hud angle @@ -641,8 +719,8 @@ void HUD_StrafeHUD() : 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) + ? real_wturn_bestangle < real_prebestangle + : speed * speed * 2 > movespeed * movespeed * 3; // acos(sqrt(2 / 3)) < acos(movepeed / speed) if(wturn_show && wturn_neutralzone && !onground) { wturn_left_bestangle_offset = wturn_bestangle / hudangle * panel_size.x + panel_size.x / 2; @@ -683,11 +761,11 @@ void HUD_StrafeHUD() { // calculate various zones of the strafe-o-meter if(autocvar_hud_panel_strafehud_bar_preaccel) - preaccelzone_width = (fabs(bestangle - prebestangle)) / hudangle * panel_size.x; + preaccelzone_width = fabs(real_bestangle - real_prebestangle) / hudangle * panel_size.x; else preaccelzone_width = 0; - accelzone_width = (90 - fabs(bestangle + wishangle)) / hudangle * panel_size.x; - overturn_width = 180 / hudangle * panel_size.x; + accelzone_width = (real_overturn_angle - real_bestangle) / hudangle * panel_size.x; + overturn_width = (360 - real_overturn_angle * 2) / hudangle * panel_size.x; neutral_width = 360 / hudangle * panel_size.x - accelzone_width * 2 - preaccelzone_width * 2 - overturn_width; { @@ -737,13 +815,14 @@ void HUD_StrafeHUD() overturn_offset += shift_offset; // draw left acceleration zone - HUD_Panel_DrawStrafeHUD( - 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, false); + if(accelzone_width > 0) + HUD_Panel_DrawStrafeHUD( + 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, false); - if(autocvar_hud_panel_strafehud_bar_preaccel) + if(autocvar_hud_panel_strafehud_bar_preaccel && preaccelzone_width > 0) HUD_Panel_DrawStrafeHUD( preaccelzone_left_offset, preaccelzone_width, hidden_width, autocvar_hud_panel_strafehud_bar_preaccel_color, @@ -751,13 +830,14 @@ void HUD_StrafeHUD() 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, false); + if(accelzone_width > 0) + 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, false); - if(autocvar_hud_panel_strafehud_bar_preaccel) + if(autocvar_hud_panel_strafehud_bar_preaccel && preaccelzone_width > 0) HUD_Panel_DrawStrafeHUD( preaccelzone_right_offset, preaccelzone_width, hidden_width, autocvar_hud_panel_strafehud_bar_preaccel_color, @@ -765,21 +845,20 @@ void HUD_StrafeHUD() autocvar_hud_panel_strafehud_style, STRAFEHUD_GRADIENT_LEFT, false); // draw overturn zone - // this is technically incorrect - // acceleration decreases at 90 degrees but speed loss happens a little bit after 90 degrees, - // however due to sv_airstopaccelerate that's hard to calculate - HUD_Panel_DrawStrafeHUD( - 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, false); + if(overturn_width > 0) + HUD_Panel_DrawStrafeHUD( + 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, 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, false); + if(neutral_width > 0) + 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, false); // only draw switch indicators if minspeed is reached (switch_bestangle_width init to 0) if(autocvar_hud_panel_strafehud_switch && switch_bestangle_width > 0 && autocvar_hud_panel_strafehud_switch_alpha > 0) diff --git a/qcsrc/client/hud/panel/strafehud.qh b/qcsrc/client/hud/panel/strafehud.qh index ef205d778..8e8847132 100644 --- a/qcsrc/client/hud/panel/strafehud.qh +++ b/qcsrc/client/hud/panel/strafehud.qh @@ -10,6 +10,7 @@ float autocvar_hud_panel_strafehud_range_cpma = 90; int autocvar_hud_panel_strafehud_style = 2; bool autocvar_hud_panel_strafehud_unit_show = true; bool autocvar_hud_panel_strafehud_uncapped = false; +int autocvar_hud_panel_strafehud_onground_mode = 2; bool autocvar_hud_panel_strafehud_bar_preaccel = true; vector autocvar_hud_panel_strafehud_bar_preaccel_color = '0 1 0'; float autocvar_hud_panel_strafehud_bar_preaccel_alpha = 0.5; -- 2.39.2