From 6dfd5dbc888d3bb18eb47984774816dcbe2fc5da Mon Sep 17 00:00:00 2001 From: Juhu <5894800-Juhu_@users.noreply.gitlab.com> Date: Sun, 11 Sep 2022 02:20:23 +0200 Subject: [PATCH] Add various patches from Juhu/strafehud-fixes branch --- _hud_common.cfg | 2 +- qcsrc/client/hud/panel/strafehud.qc | 186 +++++++++++++++++----------- qcsrc/client/hud/panel/strafehud.qh | 4 +- 3 files changed, 120 insertions(+), 72 deletions(-) diff --git a/_hud_common.cfg b/_hud_common.cfg index 2817cffa2..c1f6b96a9 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -200,7 +200,7 @@ seta hud_panel_strafehud_timeout_air "0.1" "time (in seconds) after take off bef seta hud_panel_strafehud_timeout_ground "0.03333333" "time (in seconds) after landing before changing to non-air strafe physics (visually more consistent hud while strafe turning when touching the floor after every hop)" seta hud_panel_strafehud_timeout_turn "0.1" "time (in seconds) after releasing the strafe keys before changing mode (visually more consistent hud while switching between left/right strafe turning)" seta hud_panel_strafehud_antiflicker_angle "0.01" "how many degrees from 0° to 180° the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)" -seta hud_panel_strafehud_antiflicker_speed "0.0001" "how many qu/s the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)" +seta hud_panel_strafehud_fps_update "0.5" "update interval (in seconds) of the frametime to calculate the optimal angle, smaller values may cause flickering" // hud panel aliases alias quickmenu "cl_cmd hud quickmenu ${* ?}" diff --git a/qcsrc/client/hud/panel/strafehud.qc b/qcsrc/client/hud/panel/strafehud.qc index d656d8a00..2962cbe6e 100644 --- a/qcsrc/client/hud/panel/strafehud.qc +++ b/qcsrc/client/hud/panel/strafehud.qc @@ -31,20 +31,8 @@ void HUD_StrafeHUD_Export(int fh) float hidden_width; int direction; -float demo_angle = -37; -float demo_direction = 1; -float demo_time = 0; -bool state_onground = false; -float state_onground_time = 0; -bool state_strafekeys = false; -float state_strafekeys_time = 0; -bool turn = false; -float turnangle; -float turnspeed; -float turnaccel; -bool fwd = true; -float starttime = 0; -float startspeed = -1; + +float GeomLerp(float a, float _lerp, float b); // declare GeomLerp here since there's no header file for it void HUD_StrafeHUD() { @@ -105,19 +93,57 @@ void HUD_StrafeHUD() // draw strafehud if(csqcplayer && strafeplayer) { + float strafe_waterlevel; + + // get the player waterlevel without affecting the player entity + { + // store old values + void old_contentstransition(int, int) = strafeplayer.contentstransition; + float old_watertype = strafeplayer.watertype; + float old_waterlevel = strafeplayer.waterlevel; + + strafeplayer.contentstransition = func_null; // unset the contentstransition function if present + _Movetype_CheckWater(strafeplayer); + strafe_waterlevel = strafeplayer.waterlevel; // store the player waterlevel + + // restore old values + strafeplayer.contentstransition = old_contentstransition; + strafeplayer.watertype = old_watertype; + strafeplayer.waterlevel = old_waterlevel; + } + + // presistent + static float demo_angle = -37; + static float demo_direction = 1; + static float demo_time = 0; + static bool state_onground = false; + static float state_onground_time = 0; + static bool state_strafekeys = false; + static float state_strafekeys_time = 0; + static bool turn = false; + static float turnangle; + static float turnspeed; + static float turnaccel; + static bool fwd = true; + static float strafe_dt_time = 0; + static int strafe_dt_count = 0; + static float strafe_dt_sum = 0; + static float strafe_dt_avg = 0; + // physics bool onground = islocal ? IS_ONGROUND(strafeplayer) : !(strafeplayer.anim_implicit_state & ANIMIMPLICITSTATE_INAIR); bool strafekeys; - bool swimming = strafeplayer.waterlevel >= WATERLEVEL_SWIMMING; + bool swimming = strafe_waterlevel >= WATERLEVEL_SWIMMING; // the hud will not work well while swimming bool spectating = entcs_GetSpecState(strafeplayer.sv_entnum) == ENTCS_SPEC_PURE; float speed = !autocvar__hud_configure ? vlen(vec2(csqcplayer.velocity)) : 1337; // use local csqcmodel entity for this even when spectating, flickers too much otherwise - float crouch_mod = IS_DUCKED(csqcplayer) && !swimming ? .5 : 1; - float water_mod = swimming ? .7 : 1; // very simplified water physics, the hud will not work well (and is not supposed to) while swimming + float maxspeed_mod = IS_DUCKED(csqcplayer) ? .5 : 1; float maxspeed_phys = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer); - float maxspeed = !autocvar__hud_configure ? maxspeed_phys * crouch_mod * water_mod : 320; + float maxspeed = !autocvar__hud_configure ? maxspeed_phys * maxspeed_mod : 320; float movespeed; + float bestspeed; float maxaccel_phys = onground ? PHYS_ACCELERATE(strafeplayer) : PHYS_AIRACCELERATE(strafeplayer); - float maxaccel = !autocvar__hud_configure ? maxaccel_phys * crouch_mod * water_mod : 1; + float maxaccel = !autocvar__hud_configure ? maxaccel_phys : 1; + float frametime_phys; 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; @@ -132,11 +158,10 @@ void HUD_StrafeHUD() float length_conversion_factor = GetLengthUnitFactor(autocvar_hud_panel_strafehud_unit); int length_decimals = autocvar_hud_panel_strafehud_unit >= 3 && autocvar_hud_panel_strafehud_unit <= 5 ? 6 : 2; // use more decimals when displaying km or miles float antiflicker_angle = bound(0, autocvar_hud_panel_strafehud_antiflicker_angle, 180); - float antiflicker_speed = max(0, autocvar_hud_panel_strafehud_antiflicker_speed); float minspeed; float shift_offset = 0; bool straight_overturn = false; - bool immobile = speed <= (swimming ? antiflicker_speed : 0); + bool immobile = speed <= 0; float hudangle; float neutral_offset; float neutral_width; @@ -170,6 +195,27 @@ void HUD_StrafeHUD() if(!autocvar_hud_panel_strafehud_uncapped) arrow_size = max(arrow_size, 1); + // determine frametime + if((csqcplayer_status == CSQCPLAYERSTATUS_PREDICTED) && (input_timelength > 0)) + frametime_phys = input_timelength; + else + frametime_phys = ticrate; + + if(frametime_phys > .05) // server splits frames longer than 50 ms into two moves + frametime_phys /= 2; // doesn't ensure frames are smaller than 50 ms, just splits large frames in half, matches server behaviour + + // calculate average frametime + strafe_dt_sum += frametime_phys; + ++strafe_dt_count; + + if(((time - strafe_dt_time) > autocvar_hud_panel_strafehud_fps_update) || (strafe_dt_time == 0)) + { + strafe_dt_avg = strafe_dt_sum / strafe_dt_count; + + strafe_dt_time = time; + strafe_dt_count = strafe_dt_sum = 0; + } + // determine whether the player is pressing forwards or backwards keys if(islocal) // if entity is local player { @@ -299,7 +345,7 @@ void HUD_StrafeHUD() } state_strafekeys = strafekeys; - if((!strafekeys && vlen(vec2(movement)) > 0) || swimming || autocvar__hud_configure) + if((!strafekeys && vlen(vec2(movement)) > 0) || autocvar__hud_configure) { turn = false; } @@ -320,27 +366,18 @@ void HUD_StrafeHUD() turnangle = wishangle; // calculate the maximum air strafe speed and acceleration - 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 + float strafity = 1 - (90 - fabs(wishangle)) / 45; + + if(PHYS_MAXAIRSTRAFESPEED(strafeplayer) != 0){ + maxspeed = GeomLerp(PHYS_MAXAIRSPEED(strafeplayer), strafity, PHYS_MAXAIRSTRAFESPEED(strafeplayer)); + maxspeed = min(maxspeed, PHYS_MAXAIRSPEED(strafeplayer) * maxspeed_mod); } turnspeed = vlen(vec2(movement)); if(turnspeed == 0) turnspeed = maxspeed; else turnspeed = min(turnspeed, maxspeed); - if(PHYS_AIRACCELERATE(strafeplayer) == 0){ - maxaccel = 0; - } - else if(PHYS_AIRSTRAFEACCELERATE(strafeplayer) == 0 || PHYS_AIRACCELERATE(strafeplayer) <= PHYS_AIRSTRAFEACCELERATE(strafeplayer)){ - maxaccel = PHYS_AIRACCELERATE(strafeplayer); - } - else{ - maxaccel = PHYS_AIRACCELERATE(strafeplayer) * pow(fabs(PHYS_AIRSTRAFEACCELERATE(strafeplayer) / PHYS_AIRACCELERATE(strafeplayer)), 1 - (90 - fabs(wishangle)) / 45); // no modifiers here because they don't affect air strafing + if(PHYS_AIRSTRAFEACCELERATE(strafeplayer) != 0) { + maxaccel = GeomLerp(PHYS_AIRACCELERATE(strafeplayer), strafity, PHYS_AIRSTRAFEACCELERATE(strafeplayer)); } turnaccel = maxaccel; } @@ -365,19 +402,34 @@ void HUD_StrafeHUD() { if((keys & KEY_JUMP) && ((time - state_onground_time) < autocvar_hud_panel_strafehud_timeout_ground)) // if ground timeout hasn't expired yet use air accelerate { - maxaccel = !autocvar__hud_configure ? PHYS_AIRACCELERATE(strafeplayer) * crouch_mod * water_mod : 1; + maxaccel = !autocvar__hud_configure ? PHYS_AIRACCELERATE(strafeplayer) : 1; } } else { if(!(keys & KEY_JUMP) && ((time - state_onground_time) < autocvar_hud_panel_strafehud_timeout_air)) // if air timeout hasn't expired yet use ground accelerate { - maxaccel = !autocvar__hud_configure ? PHYS_ACCELERATE(strafeplayer) * crouch_mod * water_mod : 1; + maxaccel = !autocvar__hud_configure ? PHYS_ACCELERATE(strafeplayer) : 1; } } } - minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? (movespeed - maxaccel) + antiflicker_speed : autocvar_hud_panel_strafehud_switch_minspeed; + maxaccel *= strafe_dt_avg * movespeed; + bestspeed = max(movespeed - maxaccel, 0); + + float strafespeed = speed; // speed minus friction + + if((strafespeed > 0) && onground){ + float strafefriction = IS_ONSLICK(strafeplayer) ? PHYS_FRICTION_SLICK(strafeplayer) : PHYS_FRICTION(strafeplayer); + float f = 1 - strafe_dt_avg * strafefriction * max(PHYS_STOPSPEED(strafeplayer) / strafespeed, 1); + + if(f <= 0) + strafespeed = 0; + else + strafespeed *= f; + } + + minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? bestspeed + (speed - strafespeed) : autocvar_hud_panel_strafehud_switch_minspeed; // get current strafing angle ranging from -180° to +180° if(!autocvar__hud_configure) @@ -479,8 +531,8 @@ void HUD_StrafeHUD() } // best angle to strafe at - bestangle = (speed > fabs(movespeed - maxaccel) ? acos(fabs(movespeed - maxaccel) / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0); - prebestangle = (speed > fabs(movespeed) ? acos(fabs(movespeed) / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0); + bestangle = (strafespeed > bestspeed ? acos(bestspeed / strafespeed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0); + prebestangle = (strafespeed > movespeed ? acos(movespeed / strafespeed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0); odd_bestangle = -bestangle - wishangle; bestangle -= wishangle; prebestangle -= wishangle; @@ -600,7 +652,7 @@ void HUD_StrafeHUD() HUD_Panel_DrawStrafeHUD(accelzone_right_offset, accelzone_width, autocvar_hud_panel_strafehud_bar_accel_color, autocvar_hud_panel_strafehud_bar_accel_alpha * panel_fg_alpha, autocvar_hud_panel_strafehud_style, 2); HUD_Panel_DrawStrafeHUD(preaccelzone_right_offset, preaccelzone_width, autocvar_hud_panel_strafehud_bar_preaccel_color, autocvar_hud_panel_strafehud_bar_preaccel_alpha * panel_fg_alpha, autocvar_hud_panel_strafehud_style, 0); - // draw overturn zone + // draw overturn zone (technically incorrect, acceleration decreases after 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, autocvar_hud_panel_strafehud_bar_overturn_color, autocvar_hud_panel_strafehud_bar_overturn_alpha * panel_fg_alpha, autocvar_hud_panel_strafehud_style, 3); // draw neutral zone @@ -645,12 +697,13 @@ void HUD_StrafeHUD() if(!autocvar_hud_panel_strafehud_uncapped) slicksteps = min(slicksteps, 4); - slicksteps = 90 / pow(2, slicksteps); + slicksteps = 90 / 2 ** slicksteps; - if(islocal) slickdetected = IS_ONSLICK(strafeplayer); // don't need to traceline if already touching slick + if(islocal) slickdetected = (IS_ONSLICK(strafeplayer) || (IS_ONGROUND(strafeplayer) && (PHYS_FRICTION(strafeplayer) == 0))); // don't need to traceline if already touching slick // traceline into every direction trace_dphitq3surfaceflags = 0; + vector traceorigin = strafeplayer.origin + '0 0 1' * strafeplayer.mins.z; for(float i = 0; i < 360 && !slickdetected; i += slicksteps) { vector slickoffset; @@ -664,14 +717,14 @@ void HUD_StrafeHUD() slickoffset.x = sin(j * DEG2RAD) * slickrotate; slickoffset.y = cos(j * DEG2RAD) * slickrotate; - traceline(strafeplayer.origin, strafeplayer.origin + slickoffset, MOVE_WORLDONLY, NULL); + traceline(traceorigin, traceorigin + slickoffset, MOVE_NOMONSTERS, strafeplayer); if((PHYS_FRICTION(strafeplayer) == 0 && trace_fraction < 1) || trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) slickdetected = true; } } else { slickoffset.x = slickoffset.y = 0; - traceline(strafeplayer.origin, strafeplayer.origin + slickoffset, MOVE_WORLDONLY, NULL); + traceline(traceorigin, traceorigin + slickoffset, MOVE_NOMONSTERS, strafeplayer); if((PHYS_FRICTION(strafeplayer) == 0 && trace_fraction < 1) || trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) slickdetected = true; } } @@ -706,7 +759,7 @@ void HUD_StrafeHUD() drawfill(panel_pos + eX * (indicator_direction ? 0 : panel_size.x - direction_size_horizontal.x) + eY * panel_size.y, direction_size_horizontal, autocvar_hud_panel_strafehud_direction_color, autocvar_hud_panel_strafehud_direction_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); } - if(speed <= (fabs(movespeed - maxaccel) + antiflicker_speed) && !immobile) + if(strafespeed <= bestspeed && !immobile) { bestangle_anywhere = true; // moving forward should suffice to gain speed } @@ -822,7 +875,8 @@ void HUD_StrafeHUD() // show speed when crossing the start trigger if(autocvar_hud_panel_strafehud_startspeed_fade > 0) { - float text_alpha = 0; + static float startspeed = 0, starttime = 0; // displayed value and timestamp for fade out + 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) @@ -831,16 +885,10 @@ void HUD_StrafeHUD() startspeed = speed; } } - if(startspeed >= 0) - { - text_alpha = cos(((time - starttime) / autocvar_hud_panel_strafehud_startspeed_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does - if((time - starttime) > autocvar_hud_panel_strafehud_startspeed_fade) - { - startspeed = -1; - } - } - if(startspeed >= 0 && text_alpha > 0 && autocvar_hud_panel_strafehud_startspeed_size > 0) + + if((starttime > 0) && ((time - starttime) <= autocvar_hud_panel_strafehud_startspeed_fade) && autocvar_hud_panel_strafehud_startspeed_size > 0) { + float text_alpha = cos(((time - starttime) / autocvar_hud_panel_strafehud_startspeed_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does vector startspeed_size = panel_size; startspeed_size.y = autocvar_hud_panel_strafehud_startspeed_size; if(!autocvar_hud_panel_strafehud_uncapped) @@ -863,33 +911,33 @@ void HUD_StrafeHUD() drawstring_aspect(panel_pos + eY * (panel_size.y + text_offset), strcat(ftos_decimals(startspeed * speed_conversion_factor, 2), autocvar_hud_panel_strafehud_unit_show ? speed_unit : ""), startspeed_size, autocvar_hud_panel_strafehud_startspeed_color, text_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); } } - else - { - starttime = 0; - startspeed = -1; - } // show height achieved by a single jump + // FIXME: checking z position differences is unreliable (warpzones, teleporter, kill, etc) but using velocity to calculate jump height would be + // inaccurate in hud code (possibly different tick rate, doesn't run when hud isn't drawn, rounding errors) if(autocvar_hud_panel_strafehud_jumpheight_fade > 0 && autocvar_hud_panel_strafehud_jumpheight_size > 0) { static float height_min = 0, height_max = 0; // ground and peak of jump z coordinates static float jumpheight = 0, jumptime = 0; // displayed value and timestamp for fade out - // tries to catch kill and spectate but those are not reliable, should just hook to kill/spectate/teleport and reset jump height there - if((strafeplayer.velocity.z <= 0 && height_max >= strafeplayer.origin.z) || onground || swimming || IS_DEAD(strafeplayer) || spectating) + // tries to catch kill and spectate but those are not reliable + if((strafeplayer.velocity.z <= 0) || onground || swimming || IS_DEAD(strafeplayer) || spectating) { height_min = height_max = strafeplayer.origin.z; } else if(strafeplayer.origin.z > height_max) { height_max = strafeplayer.origin.z; - jumpheight = (height_max - height_min) * length_conversion_factor; + float jumpheight_new = (height_max - height_min) * length_conversion_factor; - if(jumpheight > max(autocvar_hud_panel_strafehud_jumpheight_min, 0)) + if(jumpheight_new > max(autocvar_hud_panel_strafehud_jumpheight_min, 0)) + { + jumpheight = jumpheight_new; jumptime = time; + } } - if((time - jumptime) <= autocvar_hud_panel_strafehud_jumpheight_fade) + if((jumptime > 0) && (time - jumptime) <= autocvar_hud_panel_strafehud_jumpheight_fade) { float text_alpha = cos(((time - jumptime) / autocvar_hud_panel_strafehud_jumpheight_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does vector jumpheight_size = panel_size; diff --git a/qcsrc/client/hud/panel/strafehud.qh b/qcsrc/client/hud/panel/strafehud.qh index 63ebbed98..e5ce48a7c 100644 --- a/qcsrc/client/hud/panel/strafehud.qh +++ b/qcsrc/client/hud/panel/strafehud.qh @@ -2,8 +2,8 @@ #include "../panel.qh" AUTOCVAR_SAVE(hud_panel_strafehud, int, 3, "enable this panel, 1 = show if not observing, 2 = show always, 3 = show only in race/cts if not observing"); -AUTOCVAR_SAVE(hud_panel_strafehud_dynamichud, bool, true, "apply the dynamic hud effects to this panel"); AUTOCVAR_SAVE(_hud_panel_strafehud_demo, bool, false, "strafehud changes angle during configure"); +AUTOCVAR_SAVE(hud_panel_strafehud_dynamichud, bool, true, "apply the dynamic hud effects to this panel"); AUTOCVAR_SAVE(hud_panel_strafehud_mode, int, 0, "strafehud mode which controls whether the strafehud is centered at \"0\" = view angle, \"1\" = velocity angle"); AUTOCVAR_SAVE(hud_panel_strafehud_range, float, 0, "the angle range up to 360 degrees displayed on the strafehud, \"0\" = dynamic (chooses the minimum range required to still see the whole area needed for accelerating)"); AUTOCVAR_SAVE(hud_panel_strafehud_style, int, 2, "\"0\" = no styling, \"1\" = progress bar style for the strafe bar, \"2\" = gradient for the strafe bar"); @@ -55,7 +55,7 @@ AUTOCVAR_SAVE(hud_panel_strafehud_timeout_air, float, 0.1, "time (in seconds) af AUTOCVAR_SAVE(hud_panel_strafehud_timeout_ground, float, 0.03333333, "time (in seconds) after landing before changing to non-air strafe physics (visually more consistent hud while strafe turning when touching the floor after every hop)"); AUTOCVAR_SAVE(hud_panel_strafehud_timeout_turn, float, 0.1, "time (in seconds) after releasing the strafe keys before changing mode (visually more consistent hud while switching between left/right strafe turning)"); AUTOCVAR_SAVE(hud_panel_strafehud_antiflicker_angle, float, 0.01, "how many degrees from 0° to 180° the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)"); -AUTOCVAR_SAVE(hud_panel_strafehud_antiflicker_speed, float, 0.0001, "how many qu/s the hud ignores if it could cause visual disturbances otherwise (and to counter rounding errors)"); +AUTOCVAR_SAVE(hud_panel_strafehud_fps_update, float, 0.5, "update interval (in seconds) of the frametime to calculate the optimal angle, smaller values may cause flickering"); void HUD_Panel_DrawStrafeHUD(float, float, vector, float, int, int); vector StrafeHUD_mixColors(vector, vector, float); -- 2.39.2