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 _enabled; private ConfigEntry _showMovementSpeed; private ConfigEntry _showWorldSpeed; private ConfigEntry _includeVertical; private ConfigEntry _showLabel; private ConfigEntry _decimalPlaces; private ConfigEntry _smoothingSeconds; private ConfigEntry _scale; private ConfigEntry _opacity; private ConfigEntry _rightOffset; private ConfigEntry _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("HUD", "Enabled", true, "Show the local-player speedometer."); _showMovementSpeed = ((BaseUnityPlugin)this).Config.Bind("HUD", "ShowMovementSpeed", true, "Show MOVE speed relative to the ground or boat."); _showWorldSpeed = ((BaseUnityPlugin)this).Config.Bind("HUD", "ShowWorldSpeed", true, "Show WORLD speed measured from the player's physical world position."); _includeVertical = ((BaseUnityPlugin)this).Config.Bind("Speed", "IncludeVertical", false, "Include upward and downward movement in WORLD speed. MOVE speed remains horizontal."); _showLabel = ((BaseUnityPlugin)this).Config.Bind("HUD", "ShowLabel", true, "Show the MOVE and WORLD labels."); _decimalPlaces = ((BaseUnityPlugin)this).Config.Bind("HUD", "DecimalPlaces", 2, new ConfigDescription("Number of decimal places.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 3), new object[0])); _smoothingSeconds = ((BaseUnityPlugin)this).Config.Bind("Speed", "SmoothingSeconds", 0.08f, new ConfigDescription("Display smoothing time. Set to zero for the raw value.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.5f), new object[0])); _scale = ((BaseUnityPlugin)this).Config.Bind("HUD", "Scale", 1f, new ConfigDescription("Speedometer text scale.", (AcceptableValueBase)(object)new AcceptableValueRange(0.6f, 1.8f), new object[0])); _opacity = ((BaseUnityPlugin)this).Config.Bind("HUD", "Opacity", 1f, new ConfigDescription("Speedometer opacity.", (AcceptableValueBase)(object)new AcceptableValueRange(0.2f, 1f), new object[0])); _rightOffset = ((BaseUnityPlugin)this).Config.Bind("Position", "RightOffset", 36f, new ConfigDescription("Distance from the right edge of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 600f), new object[0])); _bottomOffset = ((BaseUnityPlugin)this).Config.Bind("Position", "BottomOffset", 70f, new ConfigDescription("Distance from the bottom edge of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange(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(); _group = val2.GetComponent(); 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(); 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[] 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[] 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[] 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(); } }