Decompiled source of SpongeSpeedRunners v1.0.1

plugins/SpongeMods.SpongeSpeedRunners.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SpongeMods.SpongeSpeedRunners.Features;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("FishNet.Runtime")]
[assembly: AssemblyCompany("Merged community mod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Godmode, lethal fists, rigged roulette, Baby Bitch Boy difficulty and the hidden dev island.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+57b046ca713d57979727d03a670b612f85778e17")]
[assembly: AssemblyProduct("SpongeSpeedRunners")]
[assembly: AssemblyTitle("SpongeMods.SpongeSpeedRunners")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 SpongeMods.SpongeSpeedRunners
{
	[BepInPlugin("spongemods.howtofish.spongespeedrunners", "SpongeMods - SpongeSpeedRunners", "1.0.0")]
	[BepInProcess("How to Fish.exe")]
	public sealed class OverridePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "spongemods.howtofish.spongespeedrunners";

		public const string PluginName = "SpongeMods - SpongeSpeedRunners";

		public const string PluginVersion = "1.0.0";

		internal static ConfigEntry<bool> GodMode;

		internal static ConfigEntry<bool> LethalFists;

		internal static ConfigEntry<int> FistDamage;

		internal static ConfigEntry<bool> AlwaysGreen;

		internal static ConfigEntry<bool> BabyModeEnabled;

		internal static ConfigEntry<bool> DevIslandEnabled;

		internal static ConfigEntry<bool> DevIslandPinkDot;

		internal static ConfigEntry<KeyCode> DevIslandKey;

		internal static ConfigEntry<bool> MultiJump;

		internal static ConfigEntry<int> MaxJumps;

		internal static ConfigEntry<bool> FastSwim;

		internal static ConfigEntry<float> SwimSpeedMultiple;

		private Harmony _harmony;

		internal static OverridePlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			GodMode = ((BaseUnityPlugin)this).Config.Bind<bool>("God Mode", "Enabled", true, "Make every player invincible. Applied on the host, so it protects everyone in the session.");
			LethalFists = ((BaseUnityPlugin)this).Config.Bind<bool>("Fists", "Enabled", true, "Make bare-handed punches deal massive damage.");
			FistDamage = ((BaseUnityPlugin)this).Config.Bind<int>("Fists", "Damage", 999999, new ConfigDescription("Damage a punch deals.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 999999), Array.Empty<object>()));
			AlwaysGreen = ((BaseUnityPlugin)this).Config.Bind<bool>("Roulette", "AlwaysGreen", true, "The casino ball always lands on green. Decided by the host, so payouts are consistent for everyone.");
			BabyModeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Baby Bitch Boy", "Enabled", true, "Add the 'Baby Bitch Boy' difficulty option. It sets the game's internal Hard difficulty, so runs stay legitimate for speedrunning, while this mod's other features make it easy.");
			DevIslandEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Dev Island", "Enabled", true, "Re-enable the hidden developer island that ships with the game but is unreachable in normal play.");
			DevIslandPinkDot = ((BaseUnityPlugin)this).Config.Bind<bool>("Dev Island", "PinkRadarDot", true, "Show the dev island on the boat radar as a bright pink dot. Skipped automatically if another island already uses a similar colour.");
			DevIslandKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Dev Island", "TravelKey", (KeyCode)288, "Host-only hotkey that sends the whole session to the dev island. The island is also reachable by sailing past the last normal island.");
			MultiJump = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "MultiJump", true, "Allow jumping repeatedly in mid-air.");
			MaxJumps = ((BaseUnityPlugin)this).Config.Bind<int>("Movement", "MaxJumps", 10, new ConfigDescription("Total jumps before touching the ground again, including the first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			FastSwim = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "FastSwim", true, "Move faster in water.");
			SwimSpeedMultiple = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "SwimSpeedMultiple", 5f, new ConfigDescription("Water movement speed as a multiple of normal walking speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			BabyDifficultyRuntime.Initialise(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("spongemods.howtofish.spongespeedrunners");
			try
			{
				_harmony.PatchAll(typeof(GodModePatches));
				_harmony.PatchAll(typeof(FistDamagePatch));
				_harmony.PatchAll(typeof(RouletteGreenPatch));
				_harmony.PatchAll(typeof(BabyDifficultyPatches));
				_harmony.PatchAll(typeof(DevIslandPatches));
				_harmony.PatchAll(typeof(RadarDotPatch));
				_harmony.PatchAll(typeof(MultiJumpPatch));
				_harmony.PatchAll(typeof(GroundedResetPatch));
				_harmony.PatchAll(typeof(FastSwimPatch));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to apply patches: " + ex));
			}
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
				((BaseUnityPlugin)this).Config.Save();
			}
			Log.LogInfo((object)("SpongeMods - SpongeSpeedRunners 1.0.0 loaded. " + $"GodMode={GodMode.Value} Fists={LethalFists.Value} Green={AlwaysGreen.Value} " + $"BabyMode={BabyModeEnabled.Value} DevIsland={DevIslandEnabled.Value} " + $"MultiJump={MaxJumps.Value} SwimSpeed={SwimSpeedMultiple.Value}x"));
		}

		private void Update()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			DevIslandAccess.Tick();
			BabyDifficultyRuntime.Tick();
			if (DevIslandEnabled.Value && Input.GetKeyDown(DevIslandKey.Value))
			{
				DevIslandAccess.TravelToDevIsland();
			}
		}

		private void OnDestroy()
		{
			BabyDifficultyRuntime.Shutdown();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Config.Save();
			Instance = null;
		}
	}
}
namespace SpongeMods.SpongeSpeedRunners.Features
{
	[HarmonyPatch]
	internal static class BabyDifficultyPatches
	{
		private static readonly FieldInfo SelectedDifficultyField = AccessTools.Field(typeof(ButtonManager), "_selectedDifficulty");

		private static readonly FieldInfo CreateHardTextField = AccessTools.Field(typeof(ButtonManager), "_createHardDifficultyText");

		private static readonly FieldInfo ServerHardTextField = AccessTools.Field(typeof(ButtonManager), "_serverHardDifficultyText");

		private static TextMeshProUGUI _createLabel;

		private static TextMeshProUGUI _serverLabel;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ButtonManager), "ChangeDifficultyButton")]
		public static void CreateCyclerPrefix(ButtonManager __instance, out Difficulty __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected I4, but got Unknown
			__state = (Difficulty)(int)ReadSelected(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ButtonManager), "ChangeDifficultyButton")]
		public static void CreateCyclerPostfix(ButtonManager __instance, bool toNext, Difficulty __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			HandleCycle(__instance, toNext, __state);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ButtonManager), "ChangeCurServerDifficulty")]
		public static void ServerCyclerPrefix(ButtonManager __instance, out Difficulty __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected I4, but got Unknown
			__state = (Difficulty)(int)ReadSelected(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ButtonManager), "ChangeCurServerDifficulty")]
		public static void ServerCyclerPostfix(ButtonManager __instance, bool toNext, Difficulty __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			HandleCycle(__instance, toNext, __state);
			if (SaveManager.CurServerSave != null)
			{
				BabyDifficultyRuntime.MarkWorld(SaveManager.CurServerSave.Name, BabyDifficultyRuntime.PendingSelection);
			}
		}

		private static Difficulty ReadSelected(ButtonManager instance)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (SelectedDifficultyField == null || (Object)(object)instance == (Object)null)
			{
				return (Difficulty)1;
			}
			try
			{
				return (Difficulty)SelectedDifficultyField.GetValue(instance);
			}
			catch
			{
				return (Difficulty)1;
			}
		}

		private static void HandleCycle(ButtonManager instance, bool toNext, Difficulty previous)
		{
			//IL_005f: 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)
			//IL_0039: Invalid comparison between Unknown and I4
			if (SelectedDifficultyField == null)
			{
				return;
			}
			if (!OverridePlugin.BabyModeEnabled.Value)
			{
				if (BabyDifficultyRuntime.PendingSelection)
				{
					BabyDifficultyRuntime.PendingSelection = false;
					RefreshLabels(instance);
				}
				return;
			}
			bool pendingSelection = BabyDifficultyRuntime.PendingSelection;
			if (toNext)
			{
				if ((int)previous == 2 && !pendingSelection)
				{
					Write(instance, (Difficulty)2);
					BabyDifficultyRuntime.PendingSelection = true;
				}
				else if (pendingSelection)
				{
					Write(instance, (Difficulty)0);
					BabyDifficultyRuntime.PendingSelection = false;
				}
			}
			else if ((int)previous == 0 && !pendingSelection)
			{
				Write(instance, (Difficulty)2);
				BabyDifficultyRuntime.PendingSelection = true;
			}
			else if (pendingSelection)
			{
				Write(instance, (Difficulty)2);
				BabyDifficultyRuntime.PendingSelection = false;
			}
			RefreshLabels(instance);
		}

		private static void Write(ButtonManager instance, Difficulty value)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				SelectedDifficultyField.SetValue(instance, value);
			}
			catch
			{
			}
		}

		private static void RefreshLabels(ButtonManager instance)
		{
			bool pendingSelection = BabyDifficultyRuntime.PendingSelection;
			object? obj = CreateHardTextField?.GetValue(instance);
			GameObject hardText = (GameObject)((obj is GameObject) ? obj : null);
			object? obj2 = ServerHardTextField?.GetValue(instance);
			object? hardText2 = ((obj2 is GameObject) ? obj2 : null);
			_createLabel = UpdateLabel(hardText, _createLabel, pendingSelection);
			_serverLabel = UpdateLabel((GameObject)hardText2, _serverLabel, pendingSelection);
		}

		private static TextMeshProUGUI UpdateLabel(GameObject hardText, TextMeshProUGUI existing, bool baby)
		{
			if ((Object)(object)hardText == (Object)null)
			{
				return existing;
			}
			if ((Object)(object)existing == (Object)null)
			{
				TextMeshProUGUI componentInChildren = hardText.GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					return null;
				}
				existing = Object.Instantiate<TextMeshProUGUI>(componentInChildren, ((TMP_Text)componentInChildren).transform.parent);
				((Object)((Component)existing).gameObject).name = "SpongeMods_BabyDifficultyLabel";
				((TMP_Text)existing).text = "BABY BITCH BOY";
				StripLocalisation(((Component)existing).gameObject);
				((TMP_Text)existing).enableAutoSizing = false;
				((TMP_Text)existing).fontSize = ((TMP_Text)componentInChildren).fontSize * 0.7f;
			}
			bool activeSelf = hardText.activeSelf;
			((Component)existing).gameObject.SetActive(activeSelf && baby);
			TextMeshProUGUI[] componentsInChildren = hardText.GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)(object)existing)
				{
					((Behaviour)val).enabled = !(activeSelf && baby);
				}
			}
			return existing;
		}

		private static void StripLocalisation(GameObject target)
		{
			MonoBehaviour[] componentsInChildren = target.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && ((object)val).GetType().Name.IndexOf("Localize", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SaveManager), "CreateServer")]
		public static void CreateServerPostfix(string serverName)
		{
			if (OverridePlugin.BabyModeEnabled.Value)
			{
				BabyDifficultyRuntime.MarkWorld(serverName, BabyDifficultyRuntime.PendingSelection);
				if (BabyDifficultyRuntime.PendingSelection)
				{
					OverridePlugin.Log.LogInfo((object)("Created '" + serverName + "' in BABY BITCH BOY mode " + $"(internally {(object)(Difficulty)2})."));
				}
			}
		}
	}
	internal struct BabyModeState : IBroadcast
	{
		public bool Active;
	}
	internal static class BabyDifficultyRuntime
	{
		internal const int BabyIndex = 3;

		internal const Difficulty InternalDifficulty = (Difficulty)2;

		internal const string DisplayName = "BABY BITCH BOY";

		private static ConfigEntry<string> _savedWorlds;

		private static readonly HashSet<string> BabyWorlds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static NetworkManager _registeredManager;

		private static bool _serializersReady;

		internal static bool IsBabyMode { get; private set; }

		internal static bool PendingSelection { get; set; }

		internal static void Initialise(ConfigFile config)
		{
			_savedWorlds = config.Bind<string>("Baby Bitch Boy", "BabyModeWorlds", string.Empty, "Save names that were created in Baby Bitch Boy mode. Managed by the mod.");
			BabyWorlds.Clear();
			string[] array = _savedWorlds.Value.Split(new char[1] { '\u001f' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string item in array)
			{
				BabyWorlds.Add(item);
			}
			AssignupSerializers();
		}

		private static void AssignupSerializers()
		{
			if (!_serializersReady)
			{
				GenericWriter<BabyModeState>.SetWrite((Action<Writer, BabyModeState>)delegate(Writer writer, BabyModeState value)
				{
					writer.WriteBoolean(value.Active);
				});
				GenericReader<BabyModeState>.SetRead((Func<Reader, BabyModeState>)((Reader reader) => new BabyModeState
				{
					Active = reader.ReadBoolean()
				}));
				_serializersReady = true;
			}
		}

		internal static void MarkWorld(string saveName, bool isBaby)
		{
			if (!string.IsNullOrEmpty(saveName))
			{
				if ((isBaby ? BabyWorlds.Add(saveName) : BabyWorlds.Remove(saveName)) && _savedWorlds != null)
				{
					_savedWorlds.Value = string.Join("\u001f", BabyWorlds.ToArray());
				}
				IsBabyMode = isBaby;
			}
		}

		internal static bool IsWorldBaby(string saveName)
		{
			if (!string.IsNullOrEmpty(saveName))
			{
				return BabyWorlds.Contains(saveName);
			}
			return false;
		}

		internal static void Tick()
		{
			if (!OverridePlugin.BabyModeEnabled.Value)
			{
				return;
			}
			RequireNetworkRegistration();
			if (SaveManager.CurServerSave != null && InstanceFinder.IsServerStarted)
			{
				bool flag = IsWorldBaby(SaveManager.CurServerSave.Name);
				if (flag != IsBabyMode)
				{
					IsBabyMode = flag;
					Broadcast();
				}
			}
		}

		private static void Broadcast()
		{
			if (!InstanceFinder.IsServerStarted || (Object)(object)InstanceFinder.ServerManager == (Object)null)
			{
				return;
			}
			try
			{
				InstanceFinder.ServerManager.Broadcast<BabyModeState>(new BabyModeState
				{
					Active = IsBabyMode
				}, true, (Channel)0);
			}
			catch (Exception ex)
			{
				OverridePlugin.Log.LogWarning((object)("Could not broadcast Baby Bitch Boy state: " + ex.Message));
			}
		}

		private static void RequireNetworkRegistration()
		{
			NetworkManager networkManager = InstanceFinder.NetworkManager;
			if ((Object)(object)networkManager == (Object)(object)_registeredManager)
			{
				return;
			}
			if ((Object)(object)_registeredManager != (Object)null)
			{
				try
				{
					_registeredManager.ClientManager.UnregisterBroadcast<BabyModeState>((Action<BabyModeState, Channel>)SpongeOnClientReceived);
					_registeredManager.ServerManager.UnregisterBroadcast<BabyModeState>((Action<NetworkConnection, BabyModeState, Channel>)SpongeOnServerReceived);
				}
				catch
				{
				}
			}
			_registeredManager = networkManager;
			if ((Object)(object)networkManager == (Object)null)
			{
				return;
			}
			try
			{
				networkManager.ClientManager.RegisterBroadcast<BabyModeState>((Action<BabyModeState, Channel>)SpongeOnClientReceived);
				networkManager.ServerManager.RegisterBroadcast<BabyModeState>((Action<NetworkConnection, BabyModeState, Channel>)SpongeOnServerReceived, true);
			}
			catch (Exception ex)
			{
				OverridePlugin.Log.LogWarning((object)("Could not register Baby Bitch Boy networking: " + ex.Message));
			}
		}

		private static void SpongeOnClientReceived(BabyModeState message, Channel channel)
		{
			if (!InstanceFinder.IsServerStarted)
			{
				IsBabyMode = message.Active;
			}
		}

		private static void SpongeOnServerReceived(NetworkConnection connection, BabyModeState message, Channel channel)
		{
			if (!InstanceFinder.IsServerStarted || (Object)(object)InstanceFinder.ServerManager == (Object)null)
			{
				return;
			}
			try
			{
				InstanceFinder.ServerManager.Broadcast<BabyModeState>(connection, new BabyModeState
				{
					Active = IsBabyMode
				}, true, (Channel)0);
			}
			catch (Exception ex)
			{
				OverridePlugin.Log.LogWarning((object)("Could not answer Baby Bitch Boy query: " + ex.Message));
			}
		}

		internal static void Shutdown()
		{
			if (!((Object)(object)_registeredManager == (Object)null))
			{
				try
				{
					_registeredManager.ClientManager.UnregisterBroadcast<BabyModeState>((Action<BabyModeState, Channel>)SpongeOnClientReceived);
					_registeredManager.ServerManager.UnregisterBroadcast<BabyModeState>((Action<NetworkConnection, BabyModeState, Channel>)SpongeOnServerReceived);
				}
				catch
				{
				}
				_registeredManager = null;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerVitals), "TakeDamage")]
	internal static class GodModePatches
	{
		private static bool Prefix()
		{
			return !OverridePlugin.GodMode.Value;
		}
	}
	[HarmonyPatch(typeof(PlayerPunching), "HitTarget")]
	internal static class FistDamagePatch
	{
		private static void Prefix(PlayerPunching __instance, out int __state)
		{
			__state = __instance._damage;
			if (OverridePlugin.LethalFists.Value)
			{
				__instance._damage = OverridePlugin.FistDamage.Value;
			}
		}

		private static Exception Finalizer(PlayerPunching __instance, int __state, Exception __exception)
		{
			__instance._damage = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(LocalCasino), "GetRouletteColorFromBall")]
	internal static class RouletteGreenPatch
	{
		private static void Postfix(ref BetColor __result)
		{
			if (OverridePlugin.AlwaysGreen.Value)
			{
				__result = (BetColor)2;
			}
		}
	}
	internal static class DevIslandAccess
	{
		internal const byte DevIslandIndex = 5;

		private static bool _loggedAvailability;

		private static readonly FieldInfo CurIslandField = AccessTools.Field(typeof(OnlineIslandManager), "_curIsland");

		internal static bool DevIslandExists => SceneManager.sceneCountInBuildSettings > 6;

		internal static bool IsOnDevIsland
		{
			get
			{
				try
				{
					return (Object)(object)OnlineIslandManager.Instance != (Object)null && CurrentIsland() == 5;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static byte CurrentIsland()
		{
			if (CurIslandField == null || (Object)(object)OnlineIslandManager.Instance == (Object)null)
			{
				return byte.MaxValue;
			}
			try
			{
				object value = CurIslandField.GetValue(OnlineIslandManager.Instance);
				PropertyInfo propertyInfo = value?.GetType().GetProperty("Value");
				return (propertyInfo == null) ? byte.MaxValue : Convert.ToByte(propertyInfo.GetValue(value));
			}
			catch
			{
				return byte.MaxValue;
			}
		}

		internal static void Tick()
		{
			if (OverridePlugin.DevIslandEnabled.Value && !_loggedAvailability && !((Object)(object)OnlineIslandManager.Instance == (Object)null))
			{
				_loggedAvailability = true;
				OverridePlugin.Log.LogInfo((object)(DevIslandExists ? ($"Dev island available at island index {(byte)5}. " + "Sail past the last island to reach it, or use the config hotkey.") : "This build does not contain a dev island scene; that feature is inactive."));
			}
		}

		internal static void TravelToDevIsland()
		{
			if (OverridePlugin.DevIslandEnabled.Value && DevIslandExists)
			{
				if (!InstanceFinder.IsServerStarted)
				{
					OverridePlugin.Log.LogInfo((object)"Only the host can move the world to the dev island.");
					return;
				}
				OnlineIslandManager.TpToSpecificIsland((byte)5);
				OverridePlugin.Log.LogInfo((object)"Travelling to the dev island; all players follow via the island SyncVar.");
			}
		}
	}
	[HarmonyPatch(typeof(OnlineIslandManager), "NextIslandInBuild")]
	internal static class DevIslandPatches
	{
		private static bool Prefix(OnlineIslandManager __instance, bool backwards)
		{
			if (!OverridePlugin.DevIslandEnabled.Value || !DevIslandAccess.DevIslandExists)
			{
				return true;
			}
			byte b = DevIslandAccess.CurrentIsland();
			if (b == byte.MaxValue)
			{
				return true;
			}
			int num = 5;
			int num2 = (backwards ? (b - 1) : (b + 1));
			if (num2 > num)
			{
				num2 = 0;
			}
			else if (num2 < 0)
			{
				num2 = num;
			}
			__instance.SpawnIsland((byte)num2);
			return false;
		}
	}
	internal static class MovementTuning
	{
		private static readonly FieldInfo IsSwimmingField = AccessTools.Field(typeof(PlayerMovement), "_isSwimming");

		private static readonly FieldInfo PlayerField = AccessTools.Field(typeof(PlayerMovement), "_player");

		private static int _airJumpsUsed;

		internal static bool HasLocal(PlayerMovement movement)
		{
			if ((Object)(object)Player.LocalPlayer == (Object)null || PlayerField == null)
			{
				return false;
			}
			object? value = PlayerField.GetValue(movement);
			return (Object)((value is Player) ? value : null) == (Object)(object)Player.LocalPlayer;
		}

		internal static bool IsSwimming(PlayerMovement movement)
		{
			if (IsSwimmingField == null)
			{
				return false;
			}
			try
			{
				return (bool)IsSwimmingField.GetValue(movement);
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryConsumeAirJump(PlayerMovement movement)
		{
			if (!OverridePlugin.MultiJump.Value || !HasLocal(movement))
			{
				return false;
			}
			if (movement.Grounded || IsSwimming(movement))
			{
				return false;
			}
			int num = Mathf.Max(0, OverridePlugin.MaxJumps.Value - 1);
			if (_airJumpsUsed >= num)
			{
				return false;
			}
			_airJumpsUsed++;
			return true;
		}

		internal static void NotifyGrounded()
		{
			_airJumpsUsed = 0;
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "JumpInput")]
	internal static class MultiJumpPatch
	{
		private static bool Prefix(PlayerMovement __instance)
		{
			if (!OverridePlugin.MultiJump.Value)
			{
				return true;
			}
			if (__instance.Grounded || MovementTuning.IsSwimming(__instance) || __instance.OnBoat)
			{
				return true;
			}
			if (!MovementTuning.TryConsumeAirJump(__instance))
			{
				return true;
			}
			__instance.Jump();
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "GroundCheck")]
	internal static class GroundedResetPatch
	{
		private static void Postfix(PlayerMovement __instance)
		{
			if (MovementTuning.HasLocal(__instance) && __instance.Grounded)
			{
				MovementTuning.NotifyGrounded();
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "LateUpdate")]
	internal static class FastSwimPatch
	{
		private static void Postfix(PlayerMovement __instance)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (OverridePlugin.FastSwim.Value && MovementTuning.HasLocal(__instance) && MovementTuning.IsSwimming(__instance) && !__instance.OnBoat)
			{
				float value = OverridePlugin.SwimSpeedMultiple.Value;
				SwimmingInterop.EnsureClampAllows(value);
				Rigidbody rig = __instance._rig;
				Vector3 linearVelocity = rig.linearVelocity;
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(linearVelocity.x, 0f, linearVelocity.z);
				float num = __instance._walkSpeed * value;
				if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f) && !(((Vector3)(ref val)).magnitude >= num))
				{
					val = ((Vector3)(ref val)).normalized * num;
					rig.linearVelocity = new Vector3(val.x, linearVelocity.y, val.z);
				}
			}
		}
	}
	[HarmonyPatch(typeof(RadarUI), "UpdateIslandDots")]
	internal static class RadarDotPatch
	{
		private static readonly Color DevIslandPink = new Color(1f, 0.16f, 0.72f, 1f);

		private const float HueTolerance = 0.06f;

		private static readonly FieldInfo IslandDotsField = AccessTools.Field(typeof(RadarUI), "_islandDots");

		private static readonly FieldInfo DotImageField = AccessTools.Field(typeof(MapDot), "_dot");

		private static Action<MapDot, Vector3> _updateDot;

		private static RadarUI _boundRadar;

		private static bool _evaluated;

		private static bool _pinkIsAvailable;

		private static MapDot _devDot;

		private static void Postfix(RadarUI __instance)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (OverridePlugin.DevIslandEnabled.Value && OverridePlugin.DevIslandPinkDot.Value && DevIslandAccess.DevIslandExists && IslandDotsField?.GetValue(__instance) is MapDot[] array && array.Length != 0)
			{
				if (!_evaluated)
				{
					EvaluatePinkAvailability(array);
				}
				if (_pinkIsAvailable && TryGetDevIslandPosition(out var position))
				{
					EnsureDevDot(__instance, array, position);
				}
			}
		}

		private static void EvaluatePinkAvailability(MapDot[] dots)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			_evaluated = true;
			_pinkIsAvailable = true;
			if (DotImageField == null)
			{
				_pinkIsAvailable = false;
				OverridePlugin.Log.LogWarning((object)"Could not read radar dot colours, so the pink dev island dot was skipped.");
				return;
			}
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(DevIslandPink, ref num, ref num2, ref num3);
			List<string> list = new List<string>();
			float num4 = default(float);
			float num5 = default(float);
			float num6 = default(float);
			for (int i = 0; i < dots.Length; i++)
			{
				object? value = DotImageField.GetValue(dots[i]);
				Image val = (Image)((value is Image) ? value : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Color color = ((Graphic)val).color;
				Color.RGBToHSV(color, ref num4, ref num5, ref num6);
				if (!(num5 < 0.25f) && !(num6 < 0.2f))
				{
					float num7 = Mathf.Abs(num4 - num);
					if (num7 > 0.5f)
					{
						num7 = 1f - num7;
					}
					if (num7 <= 0.06f && num5 >= num2 * 0.5f)
					{
						list.Add($"dot {i} (#{ColorUtility.ToHtmlStringRGB(color)})");
					}
				}
			}
			if (list.Count == 0)
			{
				OverridePlugin.Log.LogInfo((object)"Pink is unused by other islands; the dev island will use it.");
				return;
			}
			_pinkIsAvailable = false;
			OverridePlugin.Log.LogInfo((object)("Skipping the pink dev island dot because these radar dots already use a similar colour: " + string.Join(", ", list.ToArray()) + "."));
		}

		private static bool TryGetDevIslandPosition(out Vector3 position)
		{
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			try
			{
				IslandInfo islandInfo = IslandManager.GetIslandInfo(5);
				if (islandInfo == null)
				{
					return false;
				}
				position = islandInfo.IslandPosition;
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void EnsureDevDot(RadarUI radar, MapDot[] dots, Vector3 position)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if (_devDot != null)
			{
				object? value = DotImageField.GetValue(_devDot);
				Image val = (Image)((value is Image) ? value : null);
				if (val == null || !((Object)(object)val != (Object)null))
				{
					_devDot = null;
					_updateDot = null;
					_boundRadar = null;
				}
			}
			if (_devDot == null)
			{
				object? value2 = DotImageField.GetValue(dots[0]);
				Image val2 = (Image)((value2 is Image) ? value2 : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				Image val3 = Object.Instantiate<Image>(val2, ((Component)val2).transform.parent);
				((Object)((Component)val3).gameObject).name = "SpongeMods_DevIslandDot";
				((Graphic)val3).color = DevIslandPink;
				_devDot = new MapDot();
				DotImageField.SetValue(_devDot, val3);
				OverridePlugin.Log.LogInfo((object)"Added a bright pink radar dot for the dev island.");
			}
			if (_updateDot == null || (Object)(object)_boundRadar != (Object)(object)radar)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(RadarUI), "UpdateDot", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					return;
				}
				try
				{
					_updateDot = (Action<MapDot, Vector3>)Delegate.CreateDelegate(typeof(Action<MapDot, Vector3>), radar, methodInfo);
					_boundRadar = radar;
				}
				catch (Exception ex)
				{
					OverridePlugin.Log.LogWarning((object)("Could not bind the radar dot updater: " + ex.Message));
					return;
				}
			}
			_updateDot(_devDot, position);
		}
	}
	internal static class SwimmingInterop
	{
		private const string SwimmingModuleTypeName = "SpongeMods.SpongeTweaks.Gameplay.SwimmingModule";

		private const float MaximumHeadroom = 5f;

		private static bool _resolved;

		private static Type _moduleType;

		private static PropertyInfo _isEnabled;

		private static FieldInfo _swimSpeedMultiplierEntry;

		private static FieldInfo _waterSpeedHeadroomEntry;

		private static FieldInfo _waterSpeedClampEntry;

		private static float _lastRequested = float.MinValue;

		internal static bool CompendiumSwimmingActive
		{
			get
			{
				SpongeResolve();
				if (_moduleType == null || _isEnabled == null)
				{
					return false;
				}
				try
				{
					return (bool)_isEnabled.GetValue(null);
				}
				catch
				{
					return false;
				}
			}
		}

		private static void SpongeResolve()
		{
			if (!_resolved)
			{
				_resolved = true;
				_moduleType = AccessTools.TypeByName("SpongeMods.SpongeTweaks.Gameplay.SwimmingModule");
				if (_moduleType == null)
				{
					OverridePlugin.Log.LogInfo((object)"SpongeTweaks not detected; this mod owns water movement entirely.");
					return;
				}
				_isEnabled = AccessTools.Property(_moduleType, "IsEnabled");
				_swimSpeedMultiplierEntry = AccessTools.Field(_moduleType, "SwimSpeedMultiplier");
				_waterSpeedHeadroomEntry = AccessTools.Field(_moduleType, "WaterSpeedHeadroom");
				_waterSpeedClampEntry = AccessTools.Field(_moduleType, "WaterSpeedClamp");
				OverridePlugin.Log.LogInfo((object)"SpongeTweaks detected; coordinating with its water speed clamp.");
			}
		}

		private static bool TryGetValue<T>(FieldInfo field, out T value)
		{
			value = default(T);
			try
			{
				object obj = field?.GetValue(null);
				PropertyInfo propertyInfo = obj?.GetType().GetProperty("Value");
				if (propertyInfo == null)
				{
					return false;
				}
				value = (T)Convert.ChangeType(propertyInfo.GetValue(obj), typeof(T));
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool TrySetValue(FieldInfo field, object value)
		{
			try
			{
				object obj = field?.GetValue(null);
				PropertyInfo propertyInfo = obj?.GetType().GetProperty("Value");
				if (propertyInfo == null || !propertyInfo.CanWrite)
				{
					return false;
				}
				propertyInfo.SetValue(obj, value);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool TryDisableClamp()
		{
			try
			{
				PropertyInfo propertyInfo = (_waterSpeedClampEntry?.GetValue(null))?.GetType().GetProperty("Value");
				if (propertyInfo == null || !propertyInfo.PropertyType.IsEnum)
				{
					return false;
				}
				return TrySetValue(_waterSpeedClampEntry, Enum.ToObject(propertyInfo.PropertyType, 0));
			}
			catch
			{
				return false;
			}
		}

		internal static void EnsureClampAllows(float requestedMultiple)
		{
			if (!CompendiumSwimmingActive || Mathf.Approximately(_lastRequested, requestedMultiple))
			{
				return;
			}
			_lastRequested = requestedMultiple;
			if (!TryGetValue<float>(_swimSpeedMultiplierEntry, out var value) || value <= 0.01f)
			{
				value = 1f;
			}
			float num = requestedMultiple / value * 1.05f;
			if (TryGetValue<float>(_waterSpeedHeadroomEntry, out var value2) && value2 >= num)
			{
				return;
			}
			if (num <= 5f)
			{
				if (TrySetValue(_waterSpeedHeadroomEntry, num))
				{
					OverridePlugin.Log.LogInfo((object)($"Raised SpongeTweaks water speed headroom to {num:0.00} " + "so fast swimming is not clamped."));
				}
			}
			else if (TryDisableClamp())
			{
				OverridePlugin.Log.LogInfo((object)($"Fast swimming needs {num:0.00}x headroom, beyond the Compendium's maximum of " + $"{5f:0}. Turned its water speed clamp off; this mod now caps water speed."));
			}
			else
			{
				OverridePlugin.Log.LogWarning((object)"Could not adjust SpongeTweaks water speed clamp. Swimming may be slower than configured. Lower Swim Speed Multiplier here, or turn off 'Water Speed Clamp' in the Compendium's config.");
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}