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;
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;
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;
else if(turn_expired)
turn = false;
- if(turn) // CPMA turning
+ if(turn) // CPMA turning (A/D, not W-turning)
{
if(strafekeys)
{
// 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;
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))
{
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)
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;
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
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;
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);
}
}
}
// 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;
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;