float state_strafekeys_time = 0;
bool turn = false;
float turnangle;
+float turnspeed;
bool fwd = true;
bool state_fwd = true;
bool state_fwd_prev = true;
float maxspeed_water_mod = swimming ? .7 : 1; // very simplified water physics, the hud will not work well (and is not supposed to) while swimming
float maxspeed_phys = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
float maxspeed = !autocvar__hud_configure ? maxspeed_phys * maxspeed_crouch_mod * maxspeed_water_mod : 320;
- float vel_angle = vectoangles(strafeplayer.velocity).y;
- float view_angle = PHYS_INPUT_ANGLES(strafeplayer).y + 180;
+ float movespeed;
+ 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;
vector movement = PHYS_INPUT_MOVEVALUES(strafeplayer);
int keys = STAT(PRESSED_KEYS);
}
}
- strafekeys = fabs(wishangle) == 90;
+ strafekeys = fabs(wishangle) > 45;
// determine minimum required angle to display full strafe range
range_minangle = fabs(wishangle) % 90; // maximum range is 90 degree
}
state_strafekeys = strafekeys;
- if((keys & KEY_FORWARD) || (keys & KEY_BACKWARD) || swimming || autocvar__hud_configure)
+ if((!strafekeys && vlen(vec2(movement)) > 0) || swimming || autocvar__hud_configure)
{
turn = false;
}
{
turn = true; // CPMA turning
turnangle = wishangle;
+
+ // calculate the maximum air strafe speed
+ if(PHYS_MAXAIRSPEED(strafeplayer) == 0){
+ maxspeed = 0;
+ }
+ else if(PHYS_MAXAIRSTRAFESPEED(strafeplayer) == 0 || PHYS_MAXAIRSPEED(strafeplayer) <= PHYS_MAXAIRSTRAFESPEED(strafeplayer)){
+ maxspeed = PHYS_MAXAIRSPEED(strafeplayer);
+ }
+ else{
+ maxspeed = PHYS_MAXAIRSPEED(strafeplayer) * pow(fabs(PHYS_MAXAIRSTRAFESPEED(strafeplayer) / PHYS_MAXAIRSPEED(strafeplayer)), 1 - (90 - fabs(wishangle)) / 45); // no modifiers here because they don't affect air strafing
+ }
+
+ turnspeed = vlen(vec2(movement));
+ if(turnspeed == 0) turnspeed = maxspeed;
+ else turnspeed = min(turnspeed, maxspeed);
}
}
else if((time - state_strafekeys_time) >= autocvar_hud_panel_strafehud_timeout_turn) // timeout for jumping with strafe keys only
turn = false;
}
}
- if(turn)
+ if(turn && (onground || !strafekeys)) // retain last state until strafe turning times out
{
- maxspeed = PHYS_MAXAIRSTRAFESPEED(strafeplayer); // no modifiers here because they don't affect air strafing
wishangle = turnangle;
+ movespeed = turnspeed;
+ }
+ else{
+ movespeed = vlen(vec2(movement));
+ if(movespeed == 0) movespeed = maxspeed;
+ else movespeed = min(movespeed, maxspeed);
}
- minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? maxspeed + antiflicker_speed : autocvar_hud_panel_strafehud_switch_minspeed;
+ minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? movespeed + antiflicker_speed : autocvar_hud_panel_strafehud_switch_minspeed;
// get current strafing angle ranging from -180° to +180°
if(!autocvar__hud_configure)
// calculate view angle relative to the players current velocity direction
angle = vel_angle - view_angle;
- // if the angle goes above 180° or below -180° wrap it to the opposite side
+ // if the angle goes above 180° or below -180° wrap it to the opposite side since we want the interior angle
if (angle > 180) angle -= 360;
else if(angle < -180) angle += 360;
- // shift the strafe angle by 180° for hud calculations
- if(angle < 0) angle += 180;
- else angle -= 180;
-
// determine whether the player is strafing forwards or backwards
// if the player isn't strafe turning use forwards/backwards keys to determine direction
- if(!strafekeys)
+ if(fabs(wishangle) != 90)
{
if(keys_fwd > 0)
{
}
state_fwd_prev = state_fwd;
- if((time - state_fwd_time) >= autocvar_hud_panel_strafehud_timeout_direction || speed < maxspeed || (strafekeys && mode == 0)) // timeout when changing between forwards and backwards movement
+ if((time - state_fwd_time) >= autocvar_hud_panel_strafehud_timeout_direction || speed < movespeed || ((fabs(wishangle) == 90) && mode == 0)) // timeout when changing between forwards and backwards movement
{
fwd = state_fwd;
}
}
// best angle to strafe at
- bestangle = (speed > maxspeed ? acos(maxspeed / speed) : 0) * RAD2DEG * (direction < 0 ? -1 : 1);
+ bestangle = (speed > movespeed ? acos(movespeed / speed) : 0) * RAD2DEG * (direction < 0 ? -1 : 1);
odd_bestangle = -bestangle - wishangle;
bestangle -= wishangle;
slickdetector_height = panel_size.y * bound(0, autocvar_hud_panel_strafehud_slickdetector_height, 0.5);
if(autocvar_hud_panel_strafehud_slickdetector_range > 0 && autocvar_hud_panel_strafehud_slickdetector_alpha > 0 && slickdetector_height > 0 && panel_size.x > 0) // dunno if slick detection works in spectate
{
- float slicksteps = 90 / pow(2, bound(0, autocvar_hud_panel_strafehud_slickdetector_granularity, 4));
bool slickdetected = false;
+ if(PHYS_FRICTION(strafeplayer) != 0)
+ {
+ float slicksteps = 90 / pow(2, bound(0, autocvar_hud_panel_strafehud_slickdetector_granularity, 4));
- slickdetected = IS_ONSLICK(strafeplayer); // don't need to traceline if already touching slick
+ slickdetected = IS_ONSLICK(strafeplayer); // don't need to traceline if already touching slick
- // traceline into every direction
- trace_dphitq3surfaceflags = 0;
- for(float i = 0; i < 360 && !slickdetected; i += slicksteps)
- {
- vector slickoffset;
- float slickrotate;
- slickoffset.z = -cos(i * DEG2RAD) * autocvar_hud_panel_strafehud_slickdetector_range;
- slickrotate = sin(i * DEG2RAD) * autocvar_hud_panel_strafehud_slickdetector_range;
- if(i != 0 && i != 180)
+ // traceline into every direction
+ trace_dphitq3surfaceflags = 0;
+ for(float i = 0; i < 360 && !slickdetected; i += slicksteps)
{
- for(float j = 0; j < 180 && !slickdetected; j += slicksteps)
+ vector slickoffset;
+ float slickrotate;
+ slickoffset.z = -cos(i * DEG2RAD) * autocvar_hud_panel_strafehud_slickdetector_range;
+ slickrotate = sin(i * DEG2RAD) * autocvar_hud_panel_strafehud_slickdetector_range;
+ if(i != 0 && i != 180)
{
- slickoffset.x = sin(j * DEG2RAD) * slickrotate;
- slickoffset.y = cos(j * DEG2RAD) * slickrotate;
-
+ for(float j = 0; j < 180 && !slickdetected; j += slicksteps)
+ {
+ slickoffset.x = sin(j * DEG2RAD) * slickrotate;
+ slickoffset.y = cos(j * DEG2RAD) * slickrotate;
+
+ traceline(strafeplayer.origin, strafeplayer.origin + slickoffset, MOVE_WORLDONLY, NULL);
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) slickdetected = true;
+ }
+ }
+ else
+ {
+ slickoffset.x = slickoffset.y = 0;
traceline(strafeplayer.origin, strafeplayer.origin + slickoffset, MOVE_WORLDONLY, NULL);
if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) slickdetected = true;
}
}
- else
- {
- slickoffset.x = slickoffset.y = 0;
- traceline(strafeplayer.origin, strafeplayer.origin + slickoffset, MOVE_WORLDONLY, NULL);
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) slickdetected = true;
- }
+ }
+ else
+ {
+ slickdetected = true;
}
// if a traceline hit a slick surface
if(autocvar_hud_panel_strafehud_startspeed_fade > 0)
{
float text_alpha = 0;
- if(race_checkpoint == 254) // checkpoint 254 is the start trigger
+ 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)
{
}
draw_endBoldFont();
- if(speed < (maxspeed + antiflicker_speed) && !immobile)
+ if(speed < (movespeed + antiflicker_speed) && !immobile)
{
bestangle_anywhere = true; // moving forward should suffice to gain speed
}
float mirror_offset, mirror_width;
vector size = panel_size;
vector mirror_size = panel_size;
+ int gradient_start;
+ float gradient_offset, gradient_mirror_offset;
+ float overflow_width = 0, overflow_mirror_width = 0;
float original_width = width;
- float hiddencolor_width;
if(alpha <= 0 && type != 2 || width <= 0) return;
mirror_width = min(offset + width - panel_size.x - hidden_width, width);
mirror_offset = max(offset - panel_size.x - hidden_width, 0);
}
+
+ if(width < 0) width = 0;
if((offset + width) > panel_size.x)
{
+ overflow_width = (offset + width) - panel_size.x;
width = panel_size.x - offset;
}
if(mirror_offset < 0)
mirror_width += mirror_offset;
mirror_offset = 0;
}
+
+ if(mirror_width < 0) mirror_width = 0;
if((mirror_offset + mirror_width) > panel_size.x)
{
+ overflow_mirror_width = (mirror_offset + mirror_width) - panel_size.x;
mirror_width = panel_size.x - mirror_offset;
}
- if(width < 0) width = 0;
- if(mirror_width < 0) mirror_width = 0;
- hiddencolor_width = original_width - width - mirror_width;
-
if(direction < 0) // swap mirror and non-mirror values if direction points left
{
offset += mirror_offset;
width += mirror_width;
mirror_width = width - mirror_width;
width -= mirror_width;
+
+ overflow_width += overflow_mirror_width;
+ overflow_mirror_width = overflow_width - overflow_mirror_width;
+ overflow_width -= overflow_mirror_width;
}
size.x = width;
break;
case 2: // gradient style (types: 1 = left, 2 = right, 3 = both)
- StrafeHUD_drawGradient(color, autocvar_hud_panel_strafehud_bar_neutral_color, mirror_size, original_width, mirror_offset, alpha, width + (mirror_offset == 0 ? hiddencolor_width : 0), gradientType);
- StrafeHUD_drawGradient(color, autocvar_hud_panel_strafehud_bar_neutral_color, size, original_width, offset, alpha, (offset == 0 ? hiddencolor_width : 0), gradientType);
+ // determine whether the gradient starts in the mirrored or the non-mirrored area
+ if(offset == 0 && mirror_offset == 0) gradient_start = width > mirror_width ? 2 : 1;
+ else if(offset == 0) gradient_start = 2;
+ else if(mirror_offset == 0) gradient_start = 1;
+ else gradient_start = 0;
+
+ switch(gradient_start){
+ default:
+ case 0: // no offset required
+ gradient_offset = gradient_mirror_offset = 0;
+ break;
+ case 1: // offset starts in non-mirrored area, mirrored area requires offset
+ gradient_offset = 0;
+ gradient_mirror_offset = original_width - (mirror_width + overflow_mirror_width);
+ break;
+ case 2: // offset starts in mirrored area, non-mirrored area requires offset
+ gradient_offset = original_width - (width + overflow_width);
+ gradient_mirror_offset = 0;
+ }
+
+ StrafeHUD_drawGradient(color, autocvar_hud_panel_strafehud_bar_neutral_color, mirror_size, original_width, mirror_offset, alpha, gradient_mirror_offset, gradientType);
+ StrafeHUD_drawGradient(color, autocvar_hud_panel_strafehud_bar_neutral_color, size, original_width, offset, alpha, gradient_offset, gradientType);
}
}