Decompiled source of MovementStats v0.1.8

plugins/MovementStats/MovementStats.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MovementStats")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.8.0")]
[assembly: AssemblyInformationalVersion("0.1.8")]
[assembly: AssemblyProduct("MovementStats")]
[assembly: AssemblyTitle("MovementStats")]
[assembly: AssemblyVersion("0.1.8.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MovementStats
{
	public static class MovementMath
	{
		public static float HorizontalSpeed(float x, float z)
		{
			return (float)Math.Sqrt(x * x + z * z);
		}

		public static float ConvertSpeed(float metersPerSecond, string unit)
		{
			if (!(unit == "km/h"))
			{
				if (unit == "mph")
				{
					return metersPerSecond / 0.44704f;
				}
				return metersPerSecond;
			}
			return metersPerSecond * 3.6f;
		}

		public static string FormatSpeed(float metersPerSecond, string unit)
		{
			string text = ((unit == "km/h" || unit == "mph") ? unit : "m/s");
			return ConvertSpeed(metersPerSecond, text).ToString("0.00", CultureInfo.InvariantCulture) + " " + text;
		}

		public static float? EffectPercent(float before, float after)
		{
			if (float.IsNaN(before) || float.IsInfinity(before) || float.IsNaN(after) || float.IsInfinity(after) || Math.Abs(before) < 0.001f)
			{
				return null;
			}
			return (after / before - 1f) * 100f;
		}

		public static float? SurfacePercent(float before, float after, float liquidFactor)
		{
			float? num = EffectPercent(before, after);
			if (!num.HasValue || float.IsNaN(liquidFactor) || float.IsInfinity(liquidFactor))
			{
				return null;
			}
			return ((1f + num.Value / 100f) * liquidFactor - 1f) * 100f;
		}

		public static float LavaFactor(float proximity, float height, float slowHeight, float slowMax)
		{
			if (!(proximity > 0.33f) || !(height > slowHeight))
			{
				return 1f;
			}
			return 1f - (proximity - 0.33f) * 1.4925374f * slowMax;
		}

		public static float SnowFactor(float thickness, float startHeight, float maxHeight, float slowMax, float groundY, float playerY)
		{
			if (slowMax == 0f || thickness <= startHeight || groundY + thickness <= playerY || maxHeight <= startHeight)
			{
				return 1f;
			}
			return 1f - (groundY + thickness - playerY - startHeight) / (maxHeight - startHeight) * slowMax;
		}

		public static float CornerAlpha(float x, float y, float size, float radius)
		{
			return RectangleCornerAlpha(x, y, size, size, radius);
		}

		public static float RectangleCornerAlpha(float x, float y, float width, float height, float radius)
		{
			float num = Math.Max(Math.Max(radius - x, x - (width - radius)), 0f);
			float num2 = Math.Max(Math.Max(radius - y, y - (height - radius)), 0f);
			return Math.Max(0f, Math.Min(1f, radius + 0.5f - (float)Math.Sqrt(num * num + num2 * num2)));
		}

		public static string EquipmentColor(float percent)
		{
			if (float.IsNaN(percent) || float.IsInfinity(percent))
			{
				return "#FFFFFF";
			}
			if (percent > 0.05f)
			{
				return "#89E8A1";
			}
			return (int)Math.Min(5.0, Math.Floor((Math.Max(0.0, 0.0 - (double)percent) + 1E-05) / 5.0)) switch
			{
				1 => "#FFCCCC", 
				2 => "#FF9999", 
				3 => "#FF6666", 
				4 => "#FF3333", 
				5 => "#FF0000", 
				_ => "#FFFFFF", 
			};
		}

		public static string Percent(float value)
		{
			return ((Math.Abs(value) < 0.05f) ? 0f : value).ToString("+0.0;-0.0;0.0", CultureInfo.InvariantCulture) + " %";
		}
	}
	[BepInPlugin("cz.misaweb.valheim.movementstats", "Movement Stats", "0.1.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		private Harmony harmony;

		private ConfigEntry<bool> modEnabled;

		private ConfigEntry<bool> showRunIcon;

		private ConfigEntry<bool> showEquipmentIcon;

		private ConfigEntry<bool> surfaceDiagnostics;

		private SpeedyPathsReader speedyReader;

		private Type speedyType;

		private string lastSurfaceDiagnostic;

		private bool speedyErrorLogged;

		private ConfigEntry<float> backgroundTransparency;

		private ConfigEntry<int> widthStep;

		private ConfigEntry<string> speedUnit;

		private Texture2D roundedBackground;

		private Sprite runIcon;

		private Sprite equipmentIcon;

		private bool equipmentIconFailed;

		private bool fallbackLogged;

		private bool iconFailed;

		private float playerFirstSeen;

		private ConfigEntry<float> left;

		private ConfigEntry<float> top;

		private Player trackedPlayer;

		private string equipmentText = "V: —";

		private string speedText = "— m/s";

		private string surfaceText = "P: —";

		private float nextUpdate;

		private float surfaceTime = -100f;

		private float liquidTime = -100f;

		private float tarTime = -100f;

		private float? terrainBefore;

		private float? terrainPercent;

		private float? liquidPercent;

		private float? tarPercent;

		private float liquidBefore;

		private float liquidAfter;

		private float tileSide = 80f;

		private Type guiType;

		private Type styleType;

		private Type contentType;

		private object style;

		private MethodInfo label;

		private MethodInfo drawTexture;

		private MethodInfo drawTextureCoords;

		private MethodInfo calcSize;

		private PropertyInfo guiColor;

		private bool guiFailed;

		private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Expected O, but got Unknown
			Instance = this;
			((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("1. Display", "Font size"));
			((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("1. Display", "Test effects row"));
			modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Display", "Enabled", true, "Zobrazit ukazatel pohybu. Pismo se automaticky vejde do velikosti slotu rychleho vyberu.");
			backgroundTransparency = ((BaseUnityPlugin)this).Config.Bind<float>("1. Display", "Background transparency", 75f, new ConfigDescription("Pruhlednost pozadi v procentech: 0 = nepruhledne, 100 = uplne pruhledne. Text a ikona zustavaji citelne.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			showRunIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Display", "Show running icon", true, "Zobrazit puvodni ikonu dovednosti Beh u rychlosti.");
			surfaceDiagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Diagnostics", "Surface diagnostics", false, "Zapisovat do LogOutput.log zmeny hodnoty a zdroje modifikatoru povrchu (SpeedyPaths nebo zakladni hra).");
			widthStep = ((BaseUnityPlugin)this).Config.Bind<int>("2. Position", "Width step", 1, new ConfigDescription("Sirka panelu ve trech krocich: 1 = puvodni ctverec (100 %), 2 = 150 %, 3 = 200 %. Vyska zustava stejna.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
			showEquipmentIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Display", "Show equipment icon", true, "Zobrazit misto V puvodni stavebni ikonu D.U.B. (piece_TrainingDummy).");
			speedUnit = ((BaseUnityPlugin)this).Config.Bind<string>("1. Display", "Speed unit", "m/s", new ConfigDescription("Jednotka zobrazovane rychlosti: m/s, km/h nebo mph.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "m/s", "km/h", "mph" }), Array.Empty<object>()));
			speedUnit.SettingChanged += delegate
			{
				nextUpdate = 0f;
			};
			left = ((BaseUnityPlugin)this).Config.Bind<float>("2. Position", "Left percent", 2f, new ConfigDescription("Vodorovna pozice v procentech obrazovky.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			top = ((BaseUnityPlugin)this).Config.Bind<float>("2. Position", "Top percent", 28f, new ConfigDescription("Svisla pozice v procentech obrazovky.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			harmony = new Harmony("cz.misaweb.valheim.movementstats");
			Type[] array = new Type[3]
			{
				typeof(GroundSpeedPatch),
				typeof(LiquidResistancePatch),
				typeof(TarSpeedPatch)
			};
			foreach (Type type in array)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Installed " + type.Name));
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)(type.Name + " unavailable: " + ex));
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Movement Stats 0.1.8 loaded. Refresh 1 s.");
		}

		private void Update()
		{
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)(object)trackedPlayer)
			{
				trackedPlayer = localPlayer;
				surfaceTime = (liquidTime = (tarTime = -100f));
				terrainPercent = (terrainBefore = (liquidPercent = (tarPercent = null)));
				runIcon = (equipmentIcon = null);
				equipmentIconFailed = false;
				fallbackLogged = (iconFailed = false);
				playerFirstSeen = Time.unscaledTime;
				nextUpdate = 0f;
			}
			if (!Object.op_Implicit((Object)(object)localPlayer) || !modEnabled.Value || Time.unscaledTime < nextUpdate)
			{
				return;
			}
			nextUpdate = Time.unscaledTime + 1f;
			LoadEquipmentIcon();
			float num = ((Character)localPlayer).GetEquipmentMovementModifier() * 100f;
			equipmentText = ((showEquipmentIcon.Value && Object.op_Implicit((Object)(object)equipmentIcon)) ? "" : "V: ") + "<color=" + MovementMath.EquipmentColor(num) + ">" + MovementMath.Percent(num) + "</color>";
			Vector3 velocity = ((Character)localPlayer).GetVelocity();
			speedText = MovementMath.FormatSpeed(MovementMath.HorizontalSpeed(velocity.x, velocity.z), speedUnit.Value);
			UpdateSurfaceText(localPlayer);
			if (showRunIcon.Value && !iconFailed && !Object.op_Implicit((Object)(object)runIcon))
			{
				try
				{
					Skills skills = ((Character)localPlayer).GetSkills();
					object? obj = AccessTools.Method(typeof(Skills), "GetSkillDef", (Type[])null, (Type[])null).Invoke(skills, new object[1] { (object)(SkillType)102 });
					SkillDef val = (SkillDef)((obj is SkillDef) ? obj : null);
					if (val != null)
					{
						runIcon = val.m_icon;
					}
				}
				catch (Exception ex)
				{
					iconFailed = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Running icon unavailable: " + ex.Message));
				}
			}
			MeasureHotbar();
		}

		private void LoadEquipmentIcon()
		{
			if (!showEquipmentIcon.Value || Object.op_Implicit((Object)(object)equipmentIcon) || equipmentIconFailed || !Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				return;
			}
			try
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("piece_TrainingDummy");
				if (Object.op_Implicit((Object)(object)prefab))
				{
					Piece component = prefab.GetComponent<Piece>();
					if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_icon))
					{
						equipmentIcon = component.m_icon;
					}
				}
			}
			catch (Exception ex)
			{
				equipmentIconFailed = true;
				((BaseUnityPlugin)this).Logger.LogWarning((object)("D.U.B. equipment icon unavailable: " + ex.Message));
			}
		}

		private void UpdateSurfaceText(Player player)
		{
			PluginInfo value;
			bool flag = Chainloader.PluginInfos.TryGetValue("nex.SpeedyPaths", out value) && Object.op_Implicit((Object)(object)value.Instance);
			bool flag2 = ((Character)player).InLiquid();
			SurfaceMode surfaceMode = SurfaceReadout.Select(((Character)player).IsSwimming(), flag2, flag);
			if (surfaceMode == SurfaceMode.Swimming)
			{
				surfaceText = "Plavání";
				LogSurface("Swimming; wading resistance not displayed as a walking modifier");
				return;
			}
			bool flag3 = false;
			foreach (StatusEffect statusEffect in ((Character)player).GetSEMan().GetStatusEffects())
			{
				if (Object.op_Implicit((Object)(object)statusEffect) && (((Object)statusEffect).name == "Tared" || ((Object)statusEffect).name == "Tared(Clone)"))
				{
					flag3 = true;
				}
			}
			float? num = 0f;
			string surface = "none";
			if (flag)
			{
				Type type = ((object)value.Instance).GetType();
				if (speedyReader == null || speedyType != type)
				{
					speedyType = type;
					speedyReader = new SpeedyPathsReader(type);
					speedyErrorLogged = false;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Surface source: native terrain plus live SpeedyPaths " + value.Metadata.Version));
				}
				if (speedyReader.TryRead(out var percent, out surface, out var error))
				{
					num = percent;
				}
				else
				{
					num = null;
					if (!speedyErrorLogged)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("SpeedyPaths integration unavailable: " + error));
						speedyErrorLogged = true;
					}
				}
			}
			float? num2 = SurfaceReadout.FreshPercent(terrainPercent, Time.unscaledTime - surfaceTime);
			bool flag4 = !num2.HasValue;
			if (flag4)
			{
				num2 = ReadNativeTerrain(player);
			}
			float? num3 = SurfaceReadout.ActivePercent(surfaceMode == SurfaceMode.Wading, liquidPercent, Time.unscaledTime - liquidTime);
			float? num4 = SurfaceReadout.ActivePercent(flag3, tarPercent, Time.unscaledTime - tarTime);
			float? value2 = SurfaceReadout.Combine(num, num2, num3, num4);
			string text = ReadGroundMaterial(player);
			bool lavaActive = false;
			try
			{
				Func<string, float> func = (string name) => Convert.ToSingle(AccessTools.Field(typeof(Character), name).GetValue(player));
				lavaActive = MovementMath.LavaFactor(func("m_lavaProximity"), func("m_lavaHeightFactor"), func("m_lavaSlowHeight"), func("m_lavaSlowMax")) != 1f;
				if (flag2 && func("m_tarLevel") > func("m_waterLevel"))
				{
					text = "Tar";
				}
			}
			catch
			{
			}
			string text2 = SurfaceReadout.Label(text, flag2, flag3, lavaActive);
			if (!flag2 && !((Character)player).IsOnGround())
			{
				surfaceText = "P: vzduch";
			}
			else
			{
				surfaceText = (value2.HasValue ? Colored(text2 + (flag4 ? "≈ " : ": "), value2.Value) : (text2 + ": ?"));
			}
			LogSurface("material=" + text + "; SpeedyPaths(" + surface + ")=" + Describe(num) + "; terrain=" + Describe(num2) + (flag4 ? " fallback" : " observed") + "; liquid=" + Describe(num3) + "; tar=" + Describe(num4) + "; total=" + Describe(value2) + "; liquid speed=" + liquidBefore.ToString("0.000", CultureInfo.InvariantCulture) + " -> " + liquidAfter.ToString("0.000", CultureInfo.InvariantCulture));
			if (flag4 && !fallbackLogged && Time.unscaledTime - playerFirstSeen > 3f)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Native terrain observer has no fresh sample; using the native lava/snow fallback when available.");
				fallbackLogged = true;
			}
		}

		private static string Describe(float? value)
		{
			if (!value.HasValue)
			{
				return "unavailable";
			}
			return MovementMath.Percent(value.Value);
		}

		private string ReadGroundMaterial(Player player)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FootStep componentInChildren = ((Component)player).GetComponentInChildren<FootStep>(true);
				if (!Object.op_Implicit((Object)(object)componentInChildren))
				{
					return "Unknown";
				}
				return AccessTools.Method(typeof(FootStep), "GetGroundMaterial", (Type[])null, (Type[])null).Invoke(componentInChildren, new object[2]
				{
					player,
					((Component)player).transform.position
				})?.ToString() ?? "Unknown";
			}
			catch
			{
				return "Unknown";
			}
		}

		private void LogSurface(string diagnostic)
		{
			if (!surfaceDiagnostics.Value)
			{
				lastSurfaceDiagnostic = null;
			}
			else if (!(lastSurfaceDiagnostic == diagnostic))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Surface: " + diagnostic));
				lastSurfaceDiagnostic = diagnostic;
			}
		}

		private float? ReadNativeTerrain(Player player)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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_012d: 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)
			if (((Character)player).IsSwimming())
			{
				return null;
			}
			try
			{
				Func<string, float> func = (string name) => Convert.ToSingle(AccessTools.Field(typeof(Character), name).GetValue(player));
				float num = MovementMath.LavaFactor(func("m_lavaProximity"), func("m_lavaHeightFactor"), func("m_lavaSlowHeight"), func("m_lavaSlowMax"));
				if (Convert.ToInt32(AccessTools.Field(typeof(Character), "m_lastBiome").GetValue(player)) == 64)
				{
					object? value = AccessTools.Field(typeof(Character), "m_lastHeightmap").GetValue(player);
					Heightmap val = (Heightmap)((value is Heightmap) ? value : null);
					if (Object.op_Implicit((Object)(object)val))
					{
						float num2 = func("m_deepSnowSlowMaxHeight");
						float thickness = val.GetCultivationMask(((Component)player).transform.position) * num2;
						Vector3 val2 = (Vector3)AccessTools.Field(typeof(Character), "m_lastGroundHeight").GetValue(player);
						num *= MovementMath.SnowFactor(thickness, func("m_deepSnowSlowStartHeight"), num2, func("m_deepSnowSlowMax"), val2.y, ((Component)player).transform.position.y);
					}
					num *= MovementMath.LavaFactor(func("m_lavaProximity"), func("m_lavaHeightFactor"), func("m_lavaSlowHeight"), func("m_lavaSlowMax"));
				}
				return (num - 1f) * 100f;
			}
			catch (Exception ex)
			{
				if (!fallbackLogged)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Native terrain fallback unavailable: " + ex.Message));
				}
				return null;
			}
		}

		private static string Colored(string prefix, float value)
		{
			string text = ((value < -0.05f) ? "#FF8585" : ((value > 0.05f) ? "#89E8A1" : "#FFFFFF"));
			return prefix + "<color=" + text + ">" + MovementMath.Percent(value) + "</color>";
		}

		private void MeasureHotbar()
		{
			HotkeyBar[] array = Object.FindObjectsByType<HotkeyBar>((FindObjectsSortMode)0);
			foreach (HotkeyBar val in array)
			{
				if (!((Component)val).gameObject.activeInHierarchy || !(AccessTools.Field(typeof(HotkeyBar), "m_elements")?.GetValue(val) is IEnumerable enumerable))
				{
					continue;
				}
				foreach (object item in enumerable)
				{
					object? obj = AccessTools.Field(item.GetType(), "m_go")?.GetValue(item);
					GameObject val2 = (GameObject)((obj is GameObject) ? obj : null);
					if (!Object.op_Implicit((Object)(object)val2) || !val2.activeInHierarchy)
					{
						continue;
					}
					Transform transform = val2.transform;
					RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if (Object.op_Implicit((Object)(object)val3))
					{
						val3.GetWorldCorners(corners);
						float num = Mathf.Abs(corners[2].x - corners[0].x);
						if (num >= 20f && num <= (float)Screen.width / 3f)
						{
							tileSide = num;
							return;
						}
					}
				}
			}
		}

		internal static void BeginSurface(Character character, float speed)
		{
			Plugin instance = Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)character == (Object)(object)Player.m_localPlayer)
			{
				instance.terrainBefore = speed;
			}
		}

		internal static void EndSurface(Character character, float speed)
		{
			Plugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && instance.terrainBefore.HasValue)
			{
				instance.terrainPercent = MovementMath.EffectPercent(instance.terrainBefore.Value, speed);
				instance.surfaceTime = Time.unscaledTime;
				instance.terrainBefore = null;
			}
		}

		internal static void CaptureLiquid(Character character, float before, float after)
		{
			Plugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer))
			{
				float? num = MovementMath.EffectPercent(before, after);
				instance.liquidBefore = before;
				instance.liquidAfter = after;
				instance.liquidPercent = num;
				instance.liquidTime = Time.unscaledTime;
			}
		}

		internal static void CaptureTar(SE_Stats effect, Character character, float before, float after)
		{
			Plugin instance = Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && (!(((Object)effect).name != "Tared") || !(((Object)effect).name != "Tared(Clone)")))
			{
				float? num = MovementMath.EffectPercent(before, after);
				instance.tarPercent = num;
				instance.tarTime = Time.unscaledTime;
			}
		}

		private void InitializeGui()
		{
			guiType = Type.GetType("UnityEngine.GUI, UnityEngine.IMGUIModule", throwOnError: true);
			styleType = Type.GetType("UnityEngine.GUIStyle, UnityEngine.IMGUIModule", throwOnError: true);
			contentType = Type.GetType("UnityEngine.GUIContent, UnityEngine.IMGUIModule", throwOnError: true);
			object value = guiType.GetProperty("skin").GetValue(null, null);
			object value2 = value.GetType().GetProperty("label").GetValue(value, null);
			style = Activator.CreateInstance(styleType, value2);
			styleType.GetProperty("richText").SetValue(style, true, null);
			styleType.GetProperty("wordWrap").SetValue(style, false, null);
			styleType.GetProperty("alignment").SetValue(style, Enum.ToObject(styleType.GetProperty("alignment").PropertyType, 4), null);
			label = guiType.GetMethod("Label", new Type[3]
			{
				typeof(Rect),
				typeof(string),
				styleType
			});
			drawTexture = guiType.GetMethod("DrawTexture", new Type[2]
			{
				typeof(Rect),
				typeof(Texture)
			});
			drawTextureCoords = guiType.GetMethod("DrawTextureWithTexCoords", new Type[3]
			{
				typeof(Rect),
				typeof(Texture),
				typeof(Rect)
			});
			calcSize = styleType.GetMethod("CalcSize", new Type[1] { contentType });
			guiColor = guiType.GetProperty("color");
			if (label == null || drawTexture == null || calcSize == null)
			{
				throw new MissingMethodException("Unity IMGUI methods not found");
			}
		}

		private void EnsureBackground(float width, float height)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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)
			int num = Mathf.Max(1, Mathf.RoundToInt(64f * width / height));
			if (Object.op_Implicit((Object)(object)roundedBackground) && ((Texture)roundedBackground).width == num)
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)roundedBackground))
			{
				Object.Destroy((Object)(object)roundedBackground);
			}
			roundedBackground = new Texture2D(num, 64, (TextureFormat)4, false);
			((Object)roundedBackground).name = "MovementStats rounded background";
			((Texture)roundedBackground).wrapMode = (TextureWrapMode)1;
			((Texture)roundedBackground).filterMode = (FilterMode)1;
			Color[] array = (Color[])(object)new Color[num * 64];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < num; j++)
				{
					array[i * num + j] = new Color(1f, 1f, 1f, MovementMath.RectangleCornerAlpha((float)j + 0.5f, (float)i + 0.5f, num, 64f, 9f));
				}
			}
			roundedBackground.SetPixels(array);
			roundedBackground.Apply(false, true);
		}

		private void OnGUI()
		{
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_043e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: 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_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!modEnabled.Value || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || guiFailed)
			{
				return;
			}
			Hud instance = Hud.instance;
			if (Object.op_Implicit((Object)(object)instance) && !((Component)instance).gameObject.activeInHierarchy)
			{
				return;
			}
			GameObject val = (GameObject)(Object.op_Implicit((Object)(object)instance) ? /*isinst with value type is only supported in some contexts*/: null);
			if (Object.op_Implicit((Object)(object)val) && !val.activeInHierarchy)
			{
				return;
			}
			try
			{
				if (style == null)
				{
					InitializeGui();
				}
				float num = Mathf.Min(tileSide, (float)Mathf.Min(Screen.width, Screen.height));
				float num2 = Mathf.Min((float)Screen.width, num * (1f + (float)(widthStep.Value - 1) * 0.5f));
				EnsureBackground(num2, num);
				float num3 = Mathf.Max(2f, num * 0.04f);
				float num4 = (num - 2f * num3) / 3f;
				string[] array = new string[3] { equipmentText, speedText, surfaceText };
				Sprite[] array2 = (Sprite[])(object)new Sprite[3]
				{
					(showEquipmentIcon.Value && Object.op_Implicit((Object)(object)equipmentIcon) && drawTextureCoords != null) ? equipmentIcon : null,
					(showRunIcon.Value && Object.op_Implicit((Object)(object)runIcon) && drawTextureCoords != null) ? runIcon : null,
					default(Sprite)
				};
				float num5 = num4 * 0.8f;
				for (int num6 = Mathf.Max(6, Mathf.FloorToInt(num4 * 0.85f)); num6 >= 6; num6--)
				{
					styleType.GetProperty("fontSize").SetValue(style, num6, null);
					bool flag = true;
					for (int i = 0; i < array.Length; i++)
					{
						object obj = Activator.CreateInstance(contentType, array[i]);
						Vector2 val2 = (Vector2)calcSize.Invoke(style, new object[1] { obj });
						if (val2.x > num2 - 2f * num3 - (Object.op_Implicit((Object)(object)array2[i]) ? (num5 + 1f) : 0f) || val2.y > num4)
						{
							flag = false;
						}
					}
					if (flag || num6 == 6)
					{
						break;
					}
				}
				float num7 = Mathf.Clamp((float)Screen.width * left.Value / 100f, 0f, Mathf.Max(0f, (float)Screen.width - num2));
				float num8 = Mathf.Clamp((float)Screen.height * top.Value / 100f, 0f, Mathf.Max(0f, (float)Screen.height - num));
				object value = guiColor.GetValue(null, null);
				try
				{
					guiColor.SetValue(null, (object)new Color(0f, 0f, 0f, 1f - backgroundTransparency.Value / 100f), null);
					drawTexture.Invoke(null, new object[2]
					{
						(object)new Rect(num7, num8, num2, num),
						roundedBackground
					});
					guiColor.SetValue(null, Color.white, null);
					for (int j = 0; j < array.Length; j++)
					{
						float num9 = (Object.op_Implicit((Object)(object)array2[j]) ? (num5 + 1f) : 0f);
						label.Invoke(null, new object[3]
						{
							(object)new Rect(num7 + num3 + num9, num8 + num3 + (float)j * num4, num2 - num3 * 2f - num9, num4),
							array[j],
							style
						});
					}
					for (int k = 0; k < array2.Length; k++)
					{
						Sprite val3 = array2[k];
						if (!Object.op_Implicit((Object)(object)val3))
						{
							continue;
						}
						try
						{
							Rect textureRect = val3.textureRect;
							((Rect)(ref textureRect))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)val3.texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)val3.texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)val3.texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)val3.texture).height);
							drawTextureCoords.Invoke(null, new object[3]
							{
								(object)new Rect(num7 + num3, num8 + num3 + (float)k * num4 + (num4 - num5) / 2f, num5, num5),
								val3.texture,
								textureRect
							});
						}
						catch (Exception ex)
						{
							if (k == 0)
							{
								equipmentIconFailed = true;
								equipmentIcon = null;
							}
							else
							{
								iconFailed = true;
								runIcon = null;
							}
							((BaseUnityPlugin)this).Logger.LogWarning((object)("HUD icon could not be drawn (row " + k + "): " + ex.Message));
						}
					}
				}
				finally
				{
					guiColor.SetValue(null, value, null);
				}
			}
			catch (Exception ex2)
			{
				guiFailed = true;
				((BaseUnityPlugin)this).Logger.LogError((object)("Movement Stats HUD failed: " + ex2));
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if (Object.op_Implicit((Object)(object)roundedBackground))
			{
				Object.Destroy((Object)(object)roundedBackground);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	[HarmonyPatch(typeof(SE_Stats), "ModifySpeed")]
	internal static class TarSpeedPatch
	{
		private static void Prefix(float __1, out float __state)
		{
			__state = __1;
		}

		private static void Postfix(SE_Stats __instance, Character __2, float __1, float __state)
		{
			Plugin.CaptureTar(__instance, __2, __state, __1);
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyLiquidResistance")]
	internal static class LiquidResistancePatch
	{
		private static void Prefix(float __0, out float __state)
		{
			__state = __0;
		}

		private static void Postfix(Character __instance, float __0, float __state)
		{
			Plugin.CaptureLiquid(__instance, __state, __0);
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateWalking")]
	internal static class GroundSpeedPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SurfaceInstrumentation.Inject(instructions, AccessTools.Method(typeof(SEMan), "ApplyStatusEffectSpeedMods", (Type[])null, (Type[])null), AccessTools.Method(typeof(Character), "CanMove", (Type[])null, (Type[])null), AccessTools.Field(typeof(Character), "m_lavaProximity"), AccessTools.Method(typeof(Plugin), "BeginSurface", (Type[])null, (Type[])null), AccessTools.Method(typeof(Plugin), "EndSurface", (Type[])null, (Type[])null));
		}
	}
	internal sealed class SpeedyPathsReader
	{
		private readonly FieldInfo modifier;

		private readonly FieldInfo ground;

		internal SpeedyPathsReader(Type pluginType)
		{
			modifier = pluginType.GetField("_activeSpeedModifier", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			ground = pluginType.GetField("m_cachedGroundType", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
		}

		internal bool TryRead(out float percent, out string surface, out string error)
		{
			percent = 0f;
			surface = "unknown";
			error = null;
			if (modifier == null || modifier.FieldType != typeof(float))
			{
				error = "SpeedyPaths._activeSpeedModifier (float) is unavailable";
				return false;
			}
			try
			{
				float num = (float)modifier.GetValue(null);
				if (float.IsNaN(num) || float.IsInfinity(num) || num < 0f)
				{
					error = "SpeedyPaths returned an invalid speed factor";
					return false;
				}
				percent = (num - 1f) * 100f;
				if (ground != null)
				{
					surface = ground.GetValue(null)?.ToString() ?? "unknown";
				}
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				return false;
			}
		}
	}
	internal static class SurfaceInstrumentation
	{
		internal static IEnumerable<CodeInstruction> Inject(IEnumerable<CodeInstruction> input, MethodInfo effects, MethodInfo canMove, FieldInfo terrainField, MethodInfo begin, MethodInfo end)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			List<CodeInstruction> list = input.ToList();
			int num = list.FindIndex((CodeInstruction i) => CodeInstructionExtensions.Calls(i, effects));
			int num2 = list.FindIndex(num + 1, (CodeInstruction i) => CodeInstructionExtensions.Calls(i, canMove));
			if (num < 2 || num2 <= num + 3 || (list[num - 2].opcode != OpCodes.Ldloca_S && list[num - 2].opcode != OpCodes.Ldloca) || list[num + 1].opcode != OpCodes.Ldarg_0 || !CodeInstructionExtensions.LoadsField(list[num + 2], terrainField, false) || list[num2 - 1].opcode != OpCodes.Ldarg_0)
			{
				throw new InvalidOperationException("Surface observer: unsupported Character.UpdateWalking layout; no surface value available.");
			}
			object operand = list[num - 2].operand;
			CodeInstruction[] collection = (CodeInstruction[])(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldloc, operand),
				new CodeInstruction(OpCodes.Call, (object)begin)
			};
			CodeInstruction[] array = (CodeInstruction[])(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldloc, operand),
				new CodeInstruction(OpCodes.Call, (object)end)
			};
			FieldInfo? field = typeof(CodeInstruction).GetField("labels");
			IList list2 = (IList)field.GetValue(list[num2 - 1]);
			IList list3 = (IList)field.GetValue(array[0]);
			foreach (object item in list2)
			{
				list3.Add(item);
			}
			list2.Clear();
			list.InsertRange(num2 - 1, array);
			list.InsertRange(num + 1, collection);
			return list;
		}
	}
	internal enum SurfaceMode
	{
		Swimming,
		Wading,
		SpeedyPaths,
		Native
	}
	internal static class SurfaceReadout
	{
		internal static SurfaceMode Select(bool swimming, bool inLiquid, bool hasSpeedyPaths)
		{
			if (!swimming)
			{
				if (!inLiquid)
				{
					if (!hasSpeedyPaths)
					{
						return SurfaceMode.Native;
					}
					return SurfaceMode.SpeedyPaths;
				}
				return SurfaceMode.Wading;
			}
			return SurfaceMode.Swimming;
		}

		internal static float? ActivePercent(bool applies, float? percent, float age)
		{
			if (!applies)
			{
				return 0f;
			}
			return FreshPercent(percent, age);
		}

		internal static float? Combine(params float?[] percentages)
		{
			float num = 1f;
			for (int i = 0; i < percentages.Length; i++)
			{
				float? num2 = percentages[i];
				if (!num2.HasValue || float.IsNaN(num2.Value) || float.IsInfinity(num2.Value))
				{
					return null;
				}
				num *= 1f + num2.Value / 100f;
			}
			if (!float.IsNaN(num) && !float.IsInfinity(num))
			{
				return (num - 1f) * 100f;
			}
			return null;
		}

		internal static string Label(string material, bool inLiquid, bool tared, bool lavaActive)
		{
			if (tared || material == "Tar")
			{
				return "Dehet";
			}
			if (inLiquid || material == "Water")
			{
				return "Voda";
			}
			if (lavaActive || material == "Lava")
			{
				return "Láva";
			}
			if (material != null && material.StartsWith("Snow", StringComparison.Ordinal))
			{
				return "Sníh";
			}
			if (!(material == "Ice"))
			{
				return "P";
			}
			return "Led";
		}

		internal static float? FreshPercent(float? percent, float age)
		{
			if (!percent.HasValue || age < 0f || age > 1.5f || float.IsNaN(age) || float.IsInfinity(age) || float.IsNaN(percent.Value) || float.IsInfinity(percent.Value))
			{
				return null;
			}
			return percent;
		}
	}
}