#include <client/draw.qh>
#include <client/hud/panel/racetimer.qh>
-#include <client/resources.qh>
#include <client/view.qh>
#include <common/animdecide.qh>
#include <common/ent_cs.qh>
#include <common/mapinfo.qh>
#include <common/physics/movetypes/movetypes.qh>
#include <common/physics/player.qh>
+#include <common/resources/cl_resources.qh>
#include <lib/csqcmodel/cl_player.qh>
// StrafeHUD (#25)
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 jumptime = 0;
-float jumpheight = -1;
-float jumpheight_persistent = -1;
-float jumpheight_prev = 0;
-float jumpspeed_prev = 0;
-bool jumprestart = true;
// provide basic panel cvars to old clients
// TODO remove them after a future release (0.8.2+)
// draw strafehud
if(csqcplayer && strafeplayer)
{
+ // 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;
float maxspeed_phys = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
float maxspeed = !autocvar__hud_configure ? maxspeed_phys * crouch_mod * water_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 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;
int mode = autocvar_hud_panel_strafehud_mode >= 0 && autocvar_hud_panel_strafehud_mode <= 1 ? autocvar_hud_panel_strafehud_mode : 0;
float speed_conversion_factor = GetSpeedUnitFactor(autocvar_hud_panel_strafehud_unit);
float length_conversion_factor = GetLengthUnitFactor(autocvar_hud_panel_strafehud_unit);
- string speed_unit = GetSpeedUnit(autocvar_hud_panel_strafehud_unit);
- string length_unit = GetLengthUnit(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);
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;
+
+ // 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
{
}
}
- 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);
+ minspeed = autocvar_hud_panel_strafehud_switch_minspeed < 0 ? bestspeed : autocvar_hud_panel_strafehud_switch_minspeed;
// get current strafing angle ranging from -180° to +180°
if(!autocvar__hud_configure)
}
// best angle to strafe at
- bestangle = (speed > (movespeed - maxaccel) ? acos((movespeed - maxaccel) / speed) : 0) * RAD2DEG * (direction < 0 ? -1 : 1);
- prebestangle = (speed > movespeed ? acos(movespeed / speed) : 0) * RAD2DEG * (direction < 0 ? -1 : 1);
+ bestangle = (speed > bestspeed ? acos(bestspeed / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
+ prebestangle = (speed > movespeed ? acos(movespeed / speed) * RAD2DEG * (direction < 0 ? -1 : 1) : 0);
odd_bestangle = -bestangle - wishangle;
bestangle -= wishangle;
prebestangle -= wishangle;
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 < ((movespeed - maxaccel) + antiflicker_speed) && !immobile)
+ if(speed <= bestspeed && !immobile)
{
bestangle_anywhere = true; // moving forward should suffice to gain speed
}
// 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)
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)
text_offset = max(text_offset, 0);
}
+ string speed_unit = GetSpeedUnit(autocvar_hud_panel_strafehud_unit);
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;
- }
- // experimental: show height achieved by a single jump (doesn't work in low gravity and may not be 100% accurate)
- if(autocvar_hud_panel_strafehud_jumpheight_fade > 0)
+ // 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)
{
- float text_alpha = 0;
- float jumpheight_min = max(autocvar_hud_panel_strafehud_jumpheight_min, 0);
- float jumpheight_current = strafeplayer.origin.z;
- float jumpspeed_current = strafeplayer.velocity.z;
- if(jumpspeed_prev <= jumpspeed_current || jumpheight_prev > jumpheight_current || onground || swimming || IS_DEAD(strafeplayer) || spectating)
+ 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
+ if((strafeplayer.velocity.z <= 0) || onground || swimming || IS_DEAD(strafeplayer) || spectating)
{
- // tries to catch kill and spectate but those are not reliable, should just hook to kill/spectate/teleport and reset jump height there
- jumprestart = true;
+ height_min = height_max = strafeplayer.origin.z;
}
- else
+ else if(strafeplayer.origin.z > height_max)
{
- if(jumpheight < 0 || jumprestart)
- {
- jumprestart = false;
- jumpheight = 0;
- }
- else
- {
- jumpheight += jumpheight_current - jumpheight_prev;
- }
- if((jumpheight * length_conversion_factor) > jumpheight_min && jumpheight > jumpheight_persistent)
+ height_max = strafeplayer.origin.z;
+ float jumpheight_new = (height_max - height_min) * length_conversion_factor;
+
+ if(jumpheight_new > max(autocvar_hud_panel_strafehud_jumpheight_min, 0))
{
+ jumpheight = jumpheight_new;
jumptime = time;
- jumpheight_persistent = jumpheight;
}
}
- jumpheight_prev = jumpheight_current;
- jumpspeed_prev = jumpspeed_current;
- if(jumpheight_persistent > 0)
- {
- text_alpha = cos(((time - jumptime) / autocvar_hud_panel_strafehud_jumpheight_fade) * 90 * DEG2RAD); // fade non-linear like the physics panel does
- if((time - jumptime) > autocvar_hud_panel_strafehud_jumpheight_fade)
- {
- jumpheight_persistent = -1;
- }
- }
- if(jumpheight_persistent > 0 && text_alpha > 0 && autocvar_hud_panel_strafehud_jumpheight_size > 0)
+
+ 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;
jumpheight_size.y = autocvar_hud_panel_strafehud_jumpheight_size;
if(!autocvar_hud_panel_strafehud_uncapped)
text_offset = max(text_offset, 0);
}
- drawstring_aspect(panel_pos - eY * (jumpheight_size.y + text_offset), strcat(ftos_decimals(jumpheight_persistent * length_conversion_factor, length_decimals), autocvar_hud_panel_strafehud_unit_show ? length_unit : ""), jumpheight_size, autocvar_hud_panel_strafehud_jumpheight_color, text_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ string length_unit = GetLengthUnit(autocvar_hud_panel_strafehud_unit);
+ drawstring_aspect(panel_pos - eY * (jumpheight_size.y + text_offset), strcat(ftos_decimals(jumpheight, length_decimals), autocvar_hud_panel_strafehud_unit_show ? length_unit : ""), jumpheight_size, autocvar_hud_panel_strafehud_jumpheight_color, text_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
}
}
- else
- {
- jumpheight_prev = jumpspeed_prev = 0;
- jumpheight = jumpheight_persistent = -1;
- }
draw_endBoldFont();
}
}