Decompiled source of RaftSpeedometer v1.0.0

RaftSpeedometer.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("RaftSpeedometer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("RaftSpeedometer")]
[assembly: AssemblyTitle("RaftSpeedometer")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RaftSpeedometer;

internal static class GameApi
{
	private enum State
	{
		Pending,
		Ready,
		Failed
	}

	private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private static State _state = State.Pending;

	private static float _nextAttempt;

	private static FieldInfo _localPlayer;

	private static FieldInfo _lastGroundCollider;

	private static FieldInfo _lastGroundTouch;

	private static readonly List<MethodInfo> UiBlockers = new List<MethodInfo>();

	private static MethodInfo _zinputGetKeyDown;

	private static object[] _zinputArgs;

	private static bool _legacyInputFailed;

	public static bool TryResolve(ManualLogSource log)
	{
		if (_state == State.Ready)
		{
			return true;
		}
		if (_state == State.Failed)
		{
			return false;
		}
		if (Time.time < _nextAttempt)
		{
			return false;
		}
		_nextAttempt = Time.time + 2f;
		Assembly assembly = null;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly2 in assemblies)
		{
			if (assembly2.GetName().Name == "assembly_valheim")
			{
				assembly = assembly2;
				break;
			}
		}
		if (assembly == null)
		{
			return false;
		}
		Type type = assembly.GetType("Player", throwOnError: false);
		_localPlayer = type?.GetField("m_localPlayer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (_localPlayer == null)
		{
			log.LogError((object)"Could not find Player.m_localPlayer in this Valheim version; the speedometer is disabled.");
			_state = State.Failed;
			return false;
		}
		_lastGroundCollider = FindInstanceField(type, "m_lastGroundCollider");
		_lastGroundTouch = FindInstanceField(type, "m_lastGroundTouch");
		if (_lastGroundCollider == null || _lastGroundTouch == null || _lastGroundTouch.FieldType != typeof(float))
		{
			_lastGroundCollider = null;
			log.LogInfo((object)"Ground-contact fields not found; vehicle detection will use a downward raycast only.");
		}
		AddUiBlocker(assembly, "Minimap", "IsOpen");
		AddUiBlocker(assembly, "InventoryGui", "IsVisible");
		AddUiBlocker(assembly, "Menu", "IsVisible");
		AddUiBlocker(assembly, "StoreGui", "IsVisible");
		AddUiBlocker(assembly, "TextInput", "IsVisible");
		AddUiBlocker(assembly, "Hud", "IsUserHidden");
		ResolveZInput(assembly);
		_state = State.Ready;
		return true;
	}

	public static Component GetLocalPlayer()
	{
		object? obj = _localPlayer?.GetValue(null);
		return (Component)((obj is Component) ? obj : null);
	}

	public static Collider GetGroundCollider(Component player)
	{
		if (_lastGroundCollider == null || (Object)(object)player == (Object)null)
		{
			return null;
		}
		try
		{
			if ((float)_lastGroundTouch.GetValue(player) > 0.3f)
			{
				return null;
			}
			object? value = _lastGroundCollider.GetValue(player);
			return (Collider)((value is Collider) ? value : null);
		}
		catch (Exception)
		{
			_lastGroundCollider = null;
			return null;
		}
	}

	public static bool IsUiBlocking()
	{
		for (int i = 0; i < UiBlockers.Count; i++)
		{
			try
			{
				if ((bool)UiBlockers[i].Invoke(null, null))
				{
					return true;
				}
			}
			catch (Exception)
			{
				UiBlockers.RemoveAt(i);
				i--;
			}
		}
		return false;
	}

	public static bool KeyDown(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		if ((int)key == 0)
		{
			return false;
		}
		if (_zinputGetKeyDown != null)
		{
			try
			{
				_zinputArgs[0] = key;
				return (bool)_zinputGetKeyDown.Invoke(null, _zinputArgs);
			}
			catch (Exception)
			{
				_zinputGetKeyDown = null;
			}
		}
		if (!_legacyInputFailed)
		{
			try
			{
				return Input.GetKeyDown(key);
			}
			catch (Exception)
			{
				_legacyInputFailed = true;
			}
		}
		return false;
	}

	private static FieldInfo FindInstanceField(Type type, string name)
	{
		Type type2 = type;
		while (type2 != null)
		{
			FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field;
			}
			type2 = type2.BaseType;
		}
		return null;
	}

	private static void AddUiBlocker(Assembly game, string typeName, string methodName)
	{
		MethodInfo methodInfo = game.GetType(typeName, throwOnError: false)?.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
		if (methodInfo != null && methodInfo.ReturnType == typeof(bool))
		{
			UiBlockers.Add(methodInfo);
		}
	}

	private static void ResolveZInput(Assembly game)
	{
		Type type = game.GetType("ZInput", throwOnError: false);
		if (type == null)
		{
			return;
		}
		MethodInfo methodInfo = null;
		MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo2 in methods)
		{
			if (methodInfo2.Name != "GetKeyDown" || methodInfo2.ReturnType != typeof(bool))
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo2.GetParameters();
			if (parameters.Length == 0 || parameters[0].ParameterType != typeof(KeyCode))
			{
				continue;
			}
			bool flag = true;
			for (int j = 1; j < parameters.Length; j++)
			{
				if (!parameters[j].IsOptional && parameters[j].ParameterType != typeof(bool))
				{
					flag = false;
					break;
				}
			}
			if (flag && (methodInfo == null || parameters.Length < methodInfo.GetParameters().Length))
			{
				methodInfo = methodInfo2;
			}
		}
		if (methodInfo == null)
		{
			return;
		}
		ParameterInfo[] parameters2 = methodInfo.GetParameters();
		object[] array = new object[parameters2.Length];
		for (int k = 1; k < parameters2.Length; k++)
		{
			ParameterInfo parameterInfo = parameters2[k];
			if (parameterInfo.ParameterType == typeof(bool))
			{
				array[k] = ((parameterInfo.Name != null && parameterInfo.Name.IndexOf("log", StringComparison.OrdinalIgnoreCase) >= 0) ? ((object)false) : (parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : ((object)false)));
			}
			else
			{
				array[k] = (parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : null);
			}
		}
		_zinputGetKeyDown = methodInfo;
		_zinputArgs = array;
	}
}
[BepInPlugin("burksheim.raftspeedometer", "Raft Speedometer", "1.0.0")]
public sealed class RaftSpeedometerPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "burksheim.raftspeedometer";

	public const string PluginName = "Raft Speedometer";

	public const string PluginVersion = "1.0.0";

	private const float ScanInterval = 0.1f;

	private const float StoppedThreshold = 0.15f;

	private ConfigEntry<bool> _enabled;

	private ConfigEntry<KeyCode> _toggleKey;

	private ConfigEntry<KeyCode> _resetTopKey;

	private ConfigEntry<SpeedUnit> _primaryUnit;

	private ConfigEntry<SecondaryUnit> _secondaryUnit;

	private ConfigEntry<bool> _showTop;

	private ConfigEntry<ScreenAnchor> _anchor;

	private ConfigEntry<int> _offsetX;

	private ConfigEntry<int> _offsetY;

	private ConfigEntry<float> _scale;

	private ConfigEntry<float> _backgroundOpacity;

	private ConfigEntry<float> _averagingSeconds;

	private ConfigEntry<float> _leaveDelay;

	private ConfigEntry<float> _maxPlausibleSpeed;

	private ConfigEntry<string> _extraVehicleNames;

	private readonly VehicleDetector _detector = new VehicleDetector();

	private readonly SpeedTracker _tracker = new SpeedTracker();

	private readonly SpeedHud _hud = new SpeedHud();

	private Transform _vehicle;

	private VehicleKind _kind;

	private Transform _lastVehicle;

	private float _lastOnVehicleTime = float.MinValue;

	private float _nextScanTime;

	private bool _hasSpeed;

	private float _speed;

	private float _topSpeed;

	private bool _hudVisible = true;

	private bool _uiBlocked;

	private void Awake()
	{
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Expected O, but got Unknown
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Expected O, but got Unknown
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Expected O, but got Unknown
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Expected O, but got Unknown
		//IL_024d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Expected O, but got Unknown
		//IL_028b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Expected O, but got Unknown
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Turn the speedometer on or off.");
		_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("1 - General", "Toggle Key", (KeyCode)288, "Shows or hides the speedometer while you are on a vehicle.");
		_resetTopKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("1 - General", "Reset Top Speed Key", (KeyCode)287, "Clears the top speed reading, e.g. after adding a sail and before a new test run.");
		_primaryUnit = ((BaseUnityPlugin)this).Config.Bind<SpeedUnit>("2 - Display", "Primary Unit", SpeedUnit.Knots, "Unit for the big number and the top speed.");
		_secondaryUnit = ((BaseUnityPlugin)this).Config.Bind<SecondaryUnit>("2 - Display", "Secondary Unit", SecondaryUnit.MetersPerSecond, "Unit shown underneath (None hides it). ValheimRAFT's config and most published Valheim ship speeds use m/s.");
		_showTop = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Display", "Show Top Speed", true, "Show the highest speed reached since you boarded this vehicle.");
		_anchor = ((BaseUnityPlugin)this).Config.Bind<ScreenAnchor>("2 - Display", "Screen Position", ScreenAnchor.TopCenter, "Which edge or corner of the screen the panel sits against.");
		_offsetX = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Display", "Offset X", 0, new ConfigDescription("Sideways shift in pixels (measured at 1080p; scales with resolution).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-2000, 2000), Array.Empty<object>()));
		_offsetY = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Display", "Offset Y", 12, new ConfigDescription("Distance from the top or bottom edge in pixels (measured at 1080p; scales with resolution).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-2000, 2000), Array.Empty<object>()));
		_scale = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Display", "Scale", 1f, new ConfigDescription("Size of the panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
		_backgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Display", "Background Opacity", 0.55f, new ConfigDescription("0 = no background, 1 = solid.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		_averagingSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Measurement", "Averaging Seconds", 1f, new ConfigDescription("Speed is averaged over this many seconds. Longer = steadier number, slower to react.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f), Array.Empty<object>()));
		_leaveDelay = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Measurement", "Leave Delay Seconds", 1.5f, new ConfigDescription("How long the panel stays up after you step off a vehicle (so jumping on deck doesn't hide it).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
		_maxPlausibleSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Measurement", "Max Plausible Speed", 60f, new ConfigDescription("In m/s. A sudden jump faster than this (a teleport, or ValheimRAFT re-centering a raft after you build on it) restarts the measurement instead of showing a spike. Raise it only if you have tuned vehicles to go faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>()));
		_extraVehicleNames = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Measurement", "Extra Vehicle Names", "", "Comma-separated component or object names from other boat mods to treat as vehicles. ValheimRAFT vehicles and vanilla ships are detected without this.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Raft Speedometer 1.0.0 loaded.");
	}

	private void Update()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		if (!_enabled.Value)
		{
			ClearVehicle();
		}
		else if (GameApi.TryResolve(((BaseUnityPlugin)this).Logger))
		{
			if (GameApi.KeyDown(_toggleKey.Value))
			{
				_hudVisible = !_hudVisible;
			}
			if (GameApi.KeyDown(_resetTopKey.Value))
			{
				_topSpeed = 0f;
			}
			Tick();
			_uiBlocked = (Object)(object)_vehicle != (Object)null && GameApi.IsUiBlocking();
		}
	}

	private void Tick()
	{
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		Component localPlayer = GameApi.GetLocalPlayer();
		if ((Object)(object)localPlayer == (Object)null)
		{
			ClearVehicle();
			_lastVehicle = null;
			return;
		}
		float time = Time.time;
		if (time >= _nextScanTime)
		{
			_nextScanTime = time + 0.1f;
			_detector.SetExtraHints(_extraVehicleNames.Value);
			VehicleMatch match = _detector.Detect(localPlayer);
			if (match.Found)
			{
				_lastOnVehicleTime = time;
				if ((Object)(object)match.Root != (Object)(object)_vehicle)
				{
					BoardVehicle(match);
				}
			}
		}
		if ((Object)(object)_vehicle == (Object)null)
		{
			if (_kind != VehicleKind.None)
			{
				ClearVehicle();
			}
			return;
		}
		if (time - _lastOnVehicleTime > _leaveDelay.Value)
		{
			ClearVehicle();
			return;
		}
		_tracker.Window = _averagingSeconds.Value;
		_tracker.MaxPlausibleSpeed = _maxPlausibleSpeed.Value;
		_tracker.AddSample(time, _vehicle.position);
		_hasSpeed = _tracker.TryGetSpeed(out _speed, out var span);
		if (_hasSpeed && _speed < 0.15f)
		{
			_speed = 0f;
		}
		if (_hasSpeed && span >= _tracker.Window * 0.75f && _speed > _topSpeed)
		{
			_topSpeed = _speed;
		}
	}

	private void BoardVehicle(VehicleMatch match)
	{
		if ((Object)(object)match.Root != (Object)(object)_lastVehicle)
		{
			_topSpeed = 0f;
		}
		_vehicle = match.Root;
		_lastVehicle = match.Root;
		_kind = match.Kind;
		_tracker.Clear();
		_hasSpeed = false;
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Tracking " + ((_kind == VehicleKind.Raft) ? "ValheimRAFT vehicle" : "ship") + " '" + ((Object)_vehicle).name + "'."));
	}

	private void ClearVehicle()
	{
		_vehicle = null;
		_kind = VehicleKind.None;
		_tracker.Clear();
		_hasSpeed = false;
		_uiBlocked = false;
	}

	private void OnGUI()
	{
		if (_hudVisible && !_uiBlocked && !((Object)(object)_vehicle == (Object)null) && _kind != VehicleKind.None)
		{
			_hud.Draw(_hasSpeed, _speed, _topSpeed, _kind, new HudSettings
			{
				Primary = _primaryUnit.Value,
				Secondary = _secondaryUnit.Value,
				ShowTop = _showTop.Value,
				Anchor = _anchor.Value,
				OffsetX = _offsetX.Value,
				OffsetY = _offsetY.Value,
				Scale = _scale.Value,
				BackgroundOpacity = _backgroundOpacity.Value
			});
		}
	}

	private void OnDestroy()
	{
		_hud.Dispose();
	}
}
public enum SpeedUnit
{
	Knots,
	MetersPerSecond,
	KilometersPerHour,
	MilesPerHour
}
public enum SecondaryUnit
{
	None,
	Knots,
	MetersPerSecond,
	KilometersPerHour,
	MilesPerHour
}
public enum ScreenAnchor
{
	TopLeft,
	TopCenter,
	TopRight,
	BottomLeft,
	BottomCenter,
	BottomRight
}
internal struct HudSettings
{
	public SpeedUnit Primary;

	public SecondaryUnit Secondary;

	public bool ShowTop;

	public ScreenAnchor Anchor;

	public float OffsetX;

	public float OffsetY;

	public float Scale;

	public float BackgroundOpacity;
}
internal sealed class SpeedHud
{
	private static readonly Color TitleColor = new Color(0.86f, 0.7f, 0.42f, 1f);

	private static readonly Color ValueColor = new Color(1f, 0.95f, 0.86f, 1f);

	private static readonly Color DetailColor = new Color(0.8f, 0.76f, 0.68f, 1f);

	private static readonly Color AccentColor = new Color(0.86f, 0.62f, 0.27f, 0.9f);

	private static readonly Color ShadowColor = new Color(0f, 0f, 0f, 0.85f);

	private static readonly Color White = new Color(1f, 1f, 1f, 1f);

	private Texture2D _pixel;

	private GUIStyle _title;

	private GUIStyle _value;

	private GUIStyle _detail;

	private GUIStyle _shadow;

	public void Draw(bool hasSpeed, float metersPerSecond, float topMetersPerSecond, VehicleKind kind, HudSettings s)
	{
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Expected O, but got Unknown
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Expected O, but got Unknown
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Expected O, but got Unknown
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_0313: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Unknown result type (might be due to invalid IL or missing references)
		//IL_0335: Unknown result type (might be due to invalid IL or missing references)
		//IL_036b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0376: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_039b: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03db: Unknown result type (might be due to invalid IL or missing references)
		EnsureResources();
		float num = (float)Screen.height / 1080f;
		float num2 = Math.Max(0.35f, s.Scale * num);
		_title.fontSize = Math.Max(8, (int)Math.Round(13f * num2));
		_value.fontSize = Math.Max(10, (int)Math.Round(30f * num2));
		_detail.fontSize = Math.Max(8, (int)Math.Round(14f * num2));
		string text = ((kind == VehicleKind.Raft) ? "RAFT SPEED" : "SHIP SPEED");
		string text2 = (hasSpeed ? Format(metersPerSecond, s.Primary) : "--");
		string text3 = null;
		if (s.Secondary != SecondaryUnit.None && hasSpeed)
		{
			text3 = Format(metersPerSecond, (SpeedUnit)(s.Secondary - 1));
		}
		if (s.ShowTop)
		{
			string text4 = "top " + Format(topMetersPerSecond, s.Primary);
			text3 = ((text3 == null) ? text4 : (text3 + "     " + text4));
		}
		Vector2 val = _title.CalcSize(new GUIContent(text));
		Vector2 val2 = _value.CalcSize(new GUIContent(text2));
		Vector2 val3 = (Vector2)((text3 != null) ? _detail.CalcSize(new GUIContent(text3)) : default(Vector2));
		float num3 = 14f * num2;
		float num4 = 8f * num2;
		float num5 = Math.Max(1f, 2f * num2);
		float num6 = 1f * num2;
		float num7 = Math.Max(Math.Max(val.x, Math.Max(val2.x, val3.x)), 150f * num2) + num3 * 2f;
		float num8 = num5 + num4 + val.y + num6 + val2.y + ((text3 != null) ? (num6 + val3.y) : 0f) + num4;
		float num9 = s.OffsetX * num;
		float num10 = s.OffsetY * num;
		float num11;
		float num12;
		switch (s.Anchor)
		{
		case ScreenAnchor.TopLeft:
			num11 = num9;
			num12 = num10;
			break;
		case ScreenAnchor.TopRight:
			num11 = (float)Screen.width - num7 - num9;
			num12 = num10;
			break;
		case ScreenAnchor.BottomLeft:
			num11 = num9;
			num12 = (float)Screen.height - num8 - num10;
			break;
		case ScreenAnchor.BottomCenter:
			num11 = ((float)Screen.width - num7) / 2f + num9;
			num12 = (float)Screen.height - num8 - num10;
			break;
		case ScreenAnchor.BottomRight:
			num11 = (float)Screen.width - num7 - num9;
			num12 = (float)Screen.height - num8 - num10;
			break;
		default:
			num11 = ((float)Screen.width - num7) / 2f + num9;
			num12 = num10;
			break;
		}
		num11 = (float)Math.Round(num11);
		num12 = (float)Math.Round(num12);
		Color color = GUI.color;
		GUI.color = new Color(1f, 1f, 1f, Clamp01(s.BackgroundOpacity));
		GUI.DrawTexture(new Rect(num11, num12, num7, num8), (Texture)(object)_pixel);
		GUI.color = AccentColor;
		GUI.DrawTexture(new Rect(num11, num12, num7, num5), (Texture)(object)_pixel);
		GUI.color = White;
		float shadow = Math.Max(1f, (float)Math.Round(1.5f * num2));
		float num13 = num12 + num5 + num4;
		DrawCentered(text, _title, TitleColor, num11, num13, num7, val.y, shadow);
		num13 += val.y + num6;
		DrawCentered(text2, _value, ValueColor, num11, num13, num7, val2.y, shadow);
		num13 += val2.y + num6;
		if (text3 != null)
		{
			DrawCentered(text3, _detail, DetailColor, num11, num13, num7, val3.y, shadow);
		}
		GUI.color = color;
	}

	public void Dispose()
	{
		if ((Object)(object)_pixel != (Object)null)
		{
			Object.Destroy((Object)(object)_pixel);
		}
		_pixel = null;
	}

	private void DrawCentered(string text, GUIStyle style, Color color, float x, float y, float width, float height, float shadow)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		_shadow.fontSize = style.fontSize;
		_shadow.fontStyle = style.fontStyle;
		_shadow.normal.textColor = ShadowColor;
		GUI.Label(new Rect(x + shadow, y + shadow, width, height), text, _shadow);
		style.normal.textColor = color;
		GUI.Label(new Rect(x, y, width, height), text, style);
	}

	private void EnsureResources()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_pixel == (Object)null)
		{
			_pixel = new Texture2D(1, 1);
			_pixel.SetPixel(0, 0, new Color(0f, 0f, 0f, 1f));
			_pixel.Apply(false, false);
			((Object)_pixel).hideFlags = (HideFlags)61;
		}
		if (_title == null)
		{
			_title = MakeStyle((FontStyle)1);
			_value = MakeStyle((FontStyle)1);
			_detail = MakeStyle((FontStyle)0);
			_shadow = MakeStyle((FontStyle)0);
		}
	}

	private static GUIStyle MakeStyle(FontStyle fontStyle)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		return new GUIStyle
		{
			fontStyle = fontStyle,
			alignment = (TextAnchor)4,
			richText = false
		};
	}

	internal static string Format(float metersPerSecond, SpeedUnit unit)
	{
		return unit switch
		{
			SpeedUnit.MetersPerSecond => metersPerSecond.ToString("0.0", CultureInfo.InvariantCulture) + " m/s", 
			SpeedUnit.KilometersPerHour => (metersPerSecond * 3.6f).ToString("0.0", CultureInfo.InvariantCulture) + " km/h", 
			SpeedUnit.MilesPerHour => (metersPerSecond * 2.2369363f).ToString("0.0", CultureInfo.InvariantCulture) + " mph", 
			_ => (metersPerSecond * 1.9438446f).ToString("0.0", CultureInfo.InvariantCulture) + " kn", 
		};
	}

	private static float Clamp01(float v)
	{
		if (!(v < 0f))
		{
			if (!(v > 1f))
			{
				return v;
			}
			return 1f;
		}
		return 0f;
	}
}
internal sealed class SpeedTracker
{
	private struct Sample
	{
		public float Time;

		public float X;

		public float Z;
	}

	private const float SampleInterval = 0.05f;

	private const float MinReportSpan = 0.25f;

	private readonly Sample[] _buffer = new Sample[256];

	private int _start;

	private int _count;

	public float Window = 1f;

	public float MaxPlausibleSpeed = 60f;

	public void Clear()
	{
		_start = 0;
		_count = 0;
	}

	public void AddSample(float time, Vector3 position)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if (_count > 0)
		{
			Sample sample = Get(_count - 1);
			float num = time - sample.Time;
			if (num < 0.05f)
			{
				return;
			}
			float num2 = position.x - sample.X;
			float num3 = position.z - sample.Z;
			if ((float)Math.Sqrt(num2 * num2 + num3 * num3) / num > MaxPlausibleSpeed || num > 2f)
			{
				Clear();
			}
		}
		Push(new Sample
		{
			Time = time,
			X = position.x,
			Z = position.z
		});
		while (_count > 2 && time - Get(1).Time >= Window)
		{
			DropOldest();
		}
	}

	public bool TryGetSpeed(out float metersPerSecond, out float span)
	{
		metersPerSecond = 0f;
		span = 0f;
		if (_count < 2)
		{
			return false;
		}
		Sample sample = Get(0);
		Sample sample2 = Get(_count - 1);
		span = sample2.Time - sample.Time;
		if (span < Math.Min(0.25f, Window))
		{
			return false;
		}
		float num = sample2.X - sample.X;
		float num2 = sample2.Z - sample.Z;
		metersPerSecond = (float)Math.Sqrt(num * num + num2 * num2) / span;
		return true;
	}

	private Sample Get(int index)
	{
		return _buffer[(_start + index) % _buffer.Length];
	}

	private void Push(Sample s)
	{
		if (_count == _buffer.Length)
		{
			DropOldest();
		}
		_buffer[(_start + _count) % _buffer.Length] = s;
		_count++;
	}

	private void DropOldest()
	{
		_start = (_start + 1) % _buffer.Length;
		_count--;
	}
}
internal enum VehicleKind
{
	None,
	Raft,
	Ship
}
internal struct VehicleMatch
{
	public Transform Root;

	public VehicleKind Kind;

	public bool Found => (Object)(object)Root != (Object)null;
}
internal sealed class VehicleDetector
{
	private static readonly string[] RaftTypeNames = new string[3] { "VehiclePiecesController", "MoveableBaseRootComponent", "MoveableBaseShipComponent" };

	private readonly RaycastHit[] _hits = (RaycastHit[])(object)new RaycastHit[16];

	private string _extraHintsSource;

	private string[] _extraHints = new string[0];

	public void SetExtraHints(string csv)
	{
		if ((object)csv != _extraHintsSource)
		{
			_extraHintsSource = csv;
			_extraHints = (csv ?? string.Empty).Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < _extraHints.Length; i++)
			{
				_extraHints[i] = _extraHints[i].Trim();
			}
			_extraHints = Array.FindAll(_extraHints, (string h) => h.Length > 0);
		}
	}

	public VehicleMatch Detect(Component player)
	{
		Transform transform = player.transform;
		Transform parent = transform.parent;
		if ((Object)(object)parent != (Object)null)
		{
			VehicleMatch result = Classify(parent);
			if (result.Found)
			{
				return result;
			}
		}
		Collider groundCollider = GameApi.GetGroundCollider(player);
		if ((Object)(object)groundCollider != (Object)null && !IsUnder(((Component)groundCollider).transform, transform))
		{
			VehicleMatch result2 = Classify(((Component)groundCollider).transform);
			if (result2.Found)
			{
				return result2;
			}
		}
		Transform val = RaycastBelow(transform);
		if ((Object)(object)val != (Object)null)
		{
			VehicleMatch result3 = Classify(val);
			if (result3.Found)
			{
				return result3;
			}
		}
		return default(VehicleMatch);
	}

	private VehicleMatch Classify(Transform start)
	{
		VehicleMatch result = default(VehicleMatch);
		int num = 0;
		Transform val = start;
		while ((Object)(object)val != (Object)null && num < 48)
		{
			VehicleKind vehicleKind = MarkerKind(val);
			if (vehicleKind != VehicleKind.None)
			{
				result.Root = val;
				result.Kind = vehicleKind;
			}
			val = val.parent;
			num++;
		}
		return result;
	}

	private VehicleKind MarkerKind(Transform t)
	{
		VehicleKind vehicleKind = VehicleKind.None;
		Component[] components = ((Component)t).GetComponents<Component>();
		foreach (Component val in components)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Type type = ((object)val).GetType();
			string name = type.Name;
			for (int j = 0; j < RaftTypeNames.Length; j++)
			{
				if (name == RaftTypeNames[j])
				{
					return VehicleKind.Raft;
				}
			}
			Type type2 = type;
			while (type2 != null)
			{
				if (type2.Name == "Ship")
				{
					vehicleKind = VehicleKind.Ship;
					break;
				}
				type2 = type2.BaseType;
			}
			if (vehicleKind == VehicleKind.None && MatchesExtraHint(name))
			{
				vehicleKind = VehicleKind.Ship;
			}
		}
		if (vehicleKind == VehicleKind.None && _extraHints.Length != 0 && MatchesExtraHint(((Object)((Component)t).gameObject).name))
		{
			vehicleKind = VehicleKind.Ship;
		}
		return vehicleKind;
	}

	private bool MatchesExtraHint(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return false;
		}
		for (int i = 0; i < _extraHints.Length; i++)
		{
			if (name.IndexOf(_extraHints[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private Transform RaycastBelow(Transform playerTransform)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = playerTransform.position;
		int num = Physics.RaycastNonAlloc(new Vector3(position.x, position.y + 0.6f, position.z), new Vector3(0f, -1f, 0f), _hits, 2.6f, -1, (QueryTriggerInteraction)1);
		Transform result = null;
		float num2 = float.MaxValue;
		for (int i = 0; i < num; i++)
		{
			Collider collider = ((RaycastHit)(ref _hits[i])).collider;
			if ((Object)(object)collider == (Object)null)
			{
				continue;
			}
			Transform transform = ((Component)collider).transform;
			if (!IsUnder(transform, playerTransform))
			{
				float distance = ((RaycastHit)(ref _hits[i])).distance;
				if (distance < num2)
				{
					num2 = distance;
					result = transform;
				}
			}
		}
		return result;
	}

	private static bool IsUnder(Transform t, Transform ancestor)
	{
		int num = 0;
		Transform val = t;
		while ((Object)(object)val != (Object)null && num < 48)
		{
			if ((Object)(object)val == (Object)(object)ancestor)
			{
				return true;
			}
			val = val.parent;
			num++;
		}
		return false;
	}
}