using System;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: AssemblyDescription("Shows separate player-relative and world-space speeds in Unity units per second.")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyCompany("evansvl")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HowToFish.Speedometer;
[BepInPlugin("community.howtofish.speedometer", "Speedometer", "0.1.2")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "community.howtofish.speedometer";
public const string PluginName = "Speedometer";
public const string PluginVersion = "0.1.2";
private static readonly FieldInfo MainCanvasField = typeof(PlayerUI).GetField("_mainCanvas", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo BossNameField = typeof(BossUI).GetField("_bossNameText", BindingFlags.Instance | BindingFlags.NonPublic);
private ConfigEntry<bool> _enabled;
private ConfigEntry<bool> _showMovementSpeed;
private ConfigEntry<bool> _showWorldSpeed;
private ConfigEntry<bool> _includeVertical;
private ConfigEntry<bool> _showLabel;
private ConfigEntry<int> _decimalPlaces;
private ConfigEntry<float> _smoothingSeconds;
private ConfigEntry<float> _scale;
private ConfigEntry<float> _opacity;
private ConfigEntry<float> _rightOffset;
private ConfigEntry<float> _bottomOffset;
private RectTransform _root;
private CanvasGroup _group;
private TextMeshProUGUI _movementText;
private TextMeshProUGUI _worldText;
private Player _trackedPlayer;
private Vector3 _lastWorldPosition;
private bool _hasWorldPosition;
private float _smoothedMovementSpeed;
private float _smoothedWorldSpeed;
private bool _hasMovementSample;
private bool _hasWorldSpeedSample;
private void Awake()
{
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Expected O, but got Unknown
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Expected O, but got Unknown
//IL_0147: Unknown result type (might be due to invalid IL or missing references)
//IL_0151: Expected O, but got Unknown
//IL_0186: Unknown result type (might be due to invalid IL or missing references)
//IL_0190: Expected O, but got Unknown
//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Expected O, but got Unknown
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_020e: Expected O, but got Unknown
_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, "Show the local-player speedometer.");
_showMovementSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowMovementSpeed", true, "Show MOVE speed relative to the ground or boat.");
_showWorldSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowWorldSpeed", true, "Show WORLD speed measured from the player's physical world position.");
_includeVertical = ((BaseUnityPlugin)this).Config.Bind<bool>("Speed", "IncludeVertical", false, "Include upward and downward movement in WORLD speed. MOVE speed remains horizontal.");
_showLabel = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowLabel", true, "Show the MOVE and WORLD labels.");
_decimalPlaces = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "DecimalPlaces", 2, new ConfigDescription("Number of decimal places.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), new object[0]));
_smoothingSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "SmoothingSeconds", 0.08f, new ConfigDescription("Display smoothing time. Set to zero for the raw value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), new object[0]));
_scale = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Scale", 1f, new ConfigDescription("Speedometer text scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1.8f), new object[0]));
_opacity = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Opacity", 1f, new ConfigDescription("Speedometer opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1f), new object[0]));
_rightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "RightOffset", 36f, new ConfigDescription("Distance from the right edge of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), new object[0]));
_bottomOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "BottomOffset", 70f, new ConfigDescription("Distance from the bottom edge of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[0]));
((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded. Speedometer shows movement-relative and physical world-space speed.");
}
private void Update()
{
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
Player localPlayer = Player.LocalPlayer;
if (!_enabled.Value || !IsGameplayHudVisible(localPlayer))
{
ResetSample();
SetVisible(visible: false);
return;
}
if (!EnsureHud())
{
SetVisible(visible: false);
return;
}
if ((Object)(object)_trackedPlayer != (Object)(object)localPlayer)
{
ResetSample();
_trackedPlayer = localPlayer;
}
PlayerMovement movement = localPlayer.Movement;
Rigidbody rigidbody = localPlayer.Rigidbody;
if ((Object)(object)movement == (Object)null || (Object)(object)rigidbody == (Object)null)
{
ResetSample();
SetVisible(visible: false);
return;
}
Vector3 velocity = movement.Velocity;
Vector2 val = new Vector2(velocity.x, velocity.z);
float num = ((Vector2)(ref val)).magnitude;
if (float.IsNaN(num) || float.IsInfinity(num))
{
num = 0f;
}
Vector3 position = rigidbody.position;
float unscaledDeltaTime = Time.unscaledDeltaTime;
Vector3 val2 = Vector3.zero;
if (_hasWorldPosition && unscaledDeltaTime > 0f && unscaledDeltaTime <= 0.25f && IsFinite(position))
{
val2 = (position - _lastWorldPosition) / unscaledDeltaTime;
}
_lastWorldPosition = position;
_hasWorldPosition = IsFinite(position);
float magnitude;
if (!_includeVertical.Value)
{
Vector2 val3 = new Vector2(val2.x, val2.z);
magnitude = ((Vector2)(ref val3)).magnitude;
}
else
{
magnitude = ((Vector3)(ref val2)).magnitude;
}
float num2 = magnitude;
if (float.IsNaN(num2) || float.IsInfinity(num2))
{
num2 = 0f;
}
float value = _smoothingSeconds.Value;
_smoothedMovementSpeed = Smooth(_smoothedMovementSpeed, num, value, ref _hasMovementSample);
_smoothedWorldSpeed = Smooth(_smoothedWorldSpeed, num2, value, ref _hasWorldSpeedSample);
int num3 = Mathf.Clamp(_decimalPlaces.Value, 0, 3);
string text = "F" + num3;
string text2 = _smoothedMovementSpeed.ToString(text, CultureInfo.InvariantCulture) + " u/s";
string text3 = _smoothedWorldSpeed.ToString(text, CultureInfo.InvariantCulture) + " u/s";
((TMP_Text)_movementText).text = (_showLabel.Value ? ("MOVE " + text2) : text2);
((TMP_Text)_worldText).text = (_showLabel.Value ? ("WORLD " + text3) : text3);
ApplyLiveSettings();
SetVisible(_showMovementSpeed.Value || _showWorldSpeed.Value);
}
private static float Smooth(float displayed, float raw, float smoothing, ref bool hasSample)
{
if (!hasSample || smoothing <= 0f)
{
hasSample = true;
return raw;
}
float num = Mathf.Max(0f, Time.unscaledDeltaTime);
float num2 = ((num <= 0f) ? 0f : (1f - Mathf.Exp((0f - num) / smoothing)));
return Mathf.Lerp(displayed, raw, num2);
}
private static bool IsGameplayHudVisible(Player player)
{
if ((Object)(object)player == (Object)null || !Player.LocalPlayerEnabled || MainMenuManager.IsInMenu || PauseManager.IsPaused)
{
return false;
}
try
{
return !PlayerUI.UIDisabled;
}
catch
{
return true;
}
}
private static bool IsFinite(Vector3 value)
{
if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z))
{
return !float.IsInfinity(value.z);
}
return false;
}
private bool EnsureHud()
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Expected O, but got Unknown
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_root != (Object)null && (Object)(object)_group != (Object)null && (Object)(object)_movementText != (Object)null && (Object)(object)_worldText != (Object)null)
{
return true;
}
DestroyHud();
CanvasGroup val = FindMainCanvas();
if ((Object)(object)val == (Object)null)
{
return false;
}
GameObject val2 = new GameObject("SpeedometerHUD", new Type[2]
{
typeof(RectTransform),
typeof(CanvasGroup)
});
val2.transform.SetParent(((Component)val).transform, false);
val2.transform.SetAsLastSibling();
_root = val2.GetComponent<RectTransform>();
_group = val2.GetComponent<CanvasGroup>();
RectTransform root = _root;
Vector2 anchorMin = (_root.anchorMax = new Vector2(1f, 0f));
root.anchorMin = anchorMin;
_root.pivot = new Vector2(1f, 0f);
_root.sizeDelta = new Vector2(620f, 112f);
TextMeshProUGUI template = FindTextTemplate();
_movementText = CreateSpeedText("MovementSpeedText", 56f, template);
_worldText = CreateSpeedText("WorldSpeedText", 4f, template);
ApplyLiveSettings();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Created native-style MOVE and WORLD speed HUD.");
return true;
}
private TextMeshProUGUI CreateSpeedText(string name, float bottom, TextMeshProUGUI template)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject(name, new Type[3]
{
typeof(RectTransform),
typeof(CanvasRenderer),
typeof(TextMeshProUGUI)
});
val.transform.SetParent((Transform)(object)_root, false);
TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
RectTransform rectTransform = ((TMP_Text)component).rectTransform;
Vector2 anchorMin = (rectTransform.anchorMax = new Vector2(1f, 0f));
rectTransform.anchorMin = anchorMin;
rectTransform.pivot = new Vector2(1f, 0f);
rectTransform.anchoredPosition = new Vector2(0f, bottom);
rectTransform.sizeDelta = new Vector2(620f, 52f);
if ((Object)(object)template != (Object)null && (Object)(object)((TMP_Text)template).font != (Object)null)
{
((TMP_Text)component).font = ((TMP_Text)template).font;
if ((Object)(object)((TMP_Text)template).fontSharedMaterial != (Object)null)
{
((TMP_Text)component).fontSharedMaterial = ((TMP_Text)template).fontSharedMaterial;
}
((TMP_Text)component).characterSpacing = ((TMP_Text)template).characterSpacing;
}
((TMP_Text)component).fontSize = 34f;
((TMP_Text)component).alignment = (TextAlignmentOptions)4100;
((TMP_Text)component).overflowMode = (TextOverflowModes)0;
((Graphic)component).raycastTarget = false;
((TMP_Text)component).outlineColor = new Color32((byte)20, (byte)18, (byte)18, byte.MaxValue);
((TMP_Text)component).outlineWidth = 0.18f;
((TMP_Text)component).text = string.Empty;
return component;
}
private static CanvasGroup FindMainCanvas()
{
if (MainCanvasField == null)
{
return null;
}
PlayerUI[] array = Resources.FindObjectsOfTypeAll<PlayerUI>();
PlayerUI[] array2 = array;
foreach (PlayerUI val in array2)
{
if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
{
object? value = MainCanvasField.GetValue(val);
CanvasGroup val2 = (CanvasGroup)((value is CanvasGroup) ? value : null);
if ((Object)(object)val2 != (Object)null)
{
return val2;
}
}
}
return null;
}
private static TextMeshProUGUI FindTextTemplate()
{
if (BossNameField != null)
{
BossUI[] array = Resources.FindObjectsOfTypeAll<BossUI>();
BossUI[] array2 = array;
foreach (BossUI val in array2)
{
if (!((Object)(object)val == (Object)null))
{
object? value = BossNameField.GetValue(val);
TextMeshProUGUI val2 = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
if ((Object)(object)val2 != (Object)null && (Object)(object)((TMP_Text)val2).font != (Object)null)
{
return val2;
}
}
}
}
TextMeshProUGUI[] array3 = Resources.FindObjectsOfTypeAll<TextMeshProUGUI>();
TextMeshProUGUI[] array4 = array3;
foreach (TextMeshProUGUI val3 in array4)
{
if ((Object)(object)val3 != (Object)null && (Object)(object)((TMP_Text)val3).font != (Object)null && ((TMP_Text)val3).text != null && ((TMP_Text)val3).text.IndexOf("Version", StringComparison.OrdinalIgnoreCase) >= 0)
{
return val3;
}
}
return null;
}
private void ApplyLiveSettings()
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_root == (Object)null) && !((Object)(object)_group == (Object)null) && !((Object)(object)_movementText == (Object)null) && !((Object)(object)_worldText == (Object)null))
{
_root.anchoredPosition = new Vector2(0f - _rightOffset.Value, _bottomOffset.Value);
((Transform)_root).localScale = Vector3.one * _scale.Value;
_group.alpha = _opacity.Value;
((Graphic)_movementText).color = new Color(1f, 1f, 1f, 1f);
((Graphic)_worldText).color = new Color(1f, 1f, 1f, 1f);
((Behaviour)_movementText).enabled = _showMovementSpeed.Value;
((Behaviour)_worldText).enabled = _showWorldSpeed.Value;
}
}
private void SetVisible(bool visible)
{
if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeSelf != visible)
{
((Component)_root).gameObject.SetActive(visible);
}
}
private void ResetSample()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
_trackedPlayer = null;
_hasWorldPosition = false;
_lastWorldPosition = Vector3.zero;
_hasMovementSample = false;
_hasWorldSpeedSample = false;
_smoothedMovementSpeed = 0f;
_smoothedWorldSpeed = 0f;
}
private void DestroyHud()
{
if ((Object)(object)_root != (Object)null)
{
Object.Destroy((Object)(object)((Component)_root).gameObject);
}
_root = null;
_group = null;
_movementText = null;
_worldText = null;
}
private void OnDestroy()
{
DestroyHud();
}
}