Decompiled source of VargheimSailing v1.0.5

VargheimSailing.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using VargheimProfessions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.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 VargheimSailing
{
	[BepInPlugin("digi.vargheim.sailing", "VargheimSailing", "1.0.5")]
	[BepInDependency("digi.vargheim.professions", "0.4.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch]
		internal static class Patches
		{
			private static string s_rowList;

			private static readonly HashSet<string> s_rows = new HashSet<string>(StringComparer.Ordinal);

			[HarmonyPatch(typeof(Piece), "SetCreator")]
			[HarmonyPostfix]
			private static void SetCreator(Piece __instance)
			{
				try
				{
					Ship component = ((Component)__instance).GetComponent<Ship>();
					Player localPlayer = Player.m_localPlayer;
					ZDO val = Zdo((Component)(object)__instance);
					if ((Object)(object)component == (Object)null || (Object)(object)localPlayer == (Object)null || val == null)
					{
						return;
					}
					val.Set("vp_builder", localPlayer.GetPlayerName());
					if (Api.HasPerk("Sailing", IceLevel.Value) && LargeSet.Contains(PrefabName(((Component)__instance).gameObject)))
					{
						val.Set("vp_icebreaker", true);
					}
					if (Api.HasPerk("Sailing", AdmiralLevel.Value))
					{
						val.Set("vp_admiral", true);
						WearNTear component2 = ((Component)__instance).GetComponent<WearNTear>();
						if ((Object)(object)component2 != (Object)null)
						{
							component2.m_health *= 1f + AdmiralHealth.Value;
						}
					}
				}
				catch
				{
				}
			}

			[HarmonyPatch(typeof(WearNTear), "Awake")]
			[HarmonyPostfix]
			private static void WearAwake(WearNTear __instance)
			{
				try
				{
					if (!((Object)(object)((Component)__instance).GetComponent<Ship>() == (Object)null))
					{
						ZDO val = Zdo((Component)(object)__instance);
						if (val != null && val.GetBool("vp_admiral", false))
						{
							__instance.m_health *= 1f + AdmiralHealth.Value;
						}
					}
				}
				catch
				{
				}
			}

			[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
			[HarmonyPrefix]
			private static bool ShipDamage(WearNTear __instance, HitData hit)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Invalid comparison between Unknown and I4
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Invalid comparison between Unknown and I4
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (hit == null || Seasons.FloeType == null || (Object)(object)hit.GetAttacker() != (Object)null)
					{
						return true;
					}
					HitType hitType = hit.m_hitType;
					if ((int)hitType != 0 && (int)hitType != 11 && (int)hitType != 17)
					{
						return true;
					}
					if (hit.m_dir == Vector3.up)
					{
						return true;
					}
					Ship component = ((Component)__instance).GetComponent<Ship>();
					if ((Object)(object)component == (Object)null || !IsIcebreaker(component))
					{
						return true;
					}
					Collider[] array = Physics.OverlapSphere(hit.m_point, 4f);
					foreach (Collider val in array)
					{
						if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponentInParent(Seasons.FloeType) != (Object)null)
						{
							return false;
						}
					}
				}
				catch
				{
				}
				return true;
			}

			internal static bool IsRowboatName(string name)
			{
				IsRowboat(null);
				if (name != null)
				{
					return s_rows.Contains(name);
				}
				return false;
			}

			private static bool IsRowboat(Ship s)
			{
				string text = Rowboats.Value ?? string.Empty;
				if ((object)text != s_rowList)
				{
					s_rowList = text;
					s_rows.Clear();
					string[] array = text.Split(',');
					foreach (string text2 in array)
					{
						if (text2.Trim().Length > 0)
						{
							s_rows.Add(text2.Trim());
						}
					}
				}
				if (s_rows.Count > 0 && (Object)(object)s != (Object)null)
				{
					return s_rows.Contains(Utils.GetPrefabName(((Component)s).gameObject));
				}
				return false;
			}

			private static bool IsRowTarget(Ship s)
			{
				return ShipSpeed.IsRow(s);
			}

			[HarmonyPatch(typeof(Ship), "GetSailForce")]
			[HarmonyPostfix]
			private static void SailForce(Ship __instance, float sailSize, ref Vector3 __result)
			{
				//IL_003c: 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)
				//IL_004e: 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_0143: Unknown result type (might be due to invalid IL or missing references)
				//IL_0148: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					ShipSpeed.Apply(__instance);
					if (IsRowboat(__instance) || IsRowTarget(__instance))
					{
						float num = ((sailSize >= 0.99f) ? RowboatFull.Value : RowboatHalf.Value);
						__result = ((Component)__instance).transform.forward * (__instance.m_backwardForce * num);
					}
					int num2 = SteerLevel(__instance);
					float num3 = 1f;
					if (num2 >= CrewLevel.Value)
					{
						List<Player> list = s_players.Invoke(__instance);
						int num4 = ((list != null) ? Math.Max(0, list.Count - 1) : 0);
						num3 += Mathf.Min(CrewMax.Value, (float)num4 * CrewPer.Value);
					}
					if (num2 >= StormLevel.Value && (Object)(object)EnvMan.instance != (Object)null)
					{
						EnvSetup currentEnvironment = EnvMan.instance.GetCurrentEnvironment();
						if ((currentEnvironment != null && currentEnvironment.m_name != null && currentEnvironment.m_name.IndexOf("storm", StringComparison.OrdinalIgnoreCase) >= 0) || EnvMan.instance.GetWindIntensity() >= StormWind.Value)
						{
							num3 += StormBonus.Value;
						}
					}
					if (IsIcebreaker(__instance) && Seasons.Frozen())
					{
						num3 *= IceSpeed.Value;
					}
					__result *= num3;
				}
				catch
				{
				}
			}

			[HarmonyPatch(typeof(ShipControlls), "Interact")]
			[HarmonyPrefix]
			private static bool Interact(ShipControlls __instance, Humanoid character, bool alt, ref bool __result)
			{
				try
				{
					if (!alt || (Object)(object)character != (Object)(object)Player.m_localPlayer || !Api.HasPerk("Sailing", NameLevel.Value))
					{
						return true;
					}
					ZDO val = Zdo((Component)(object)__instance.m_ship);
					if (val == null || val.GetString("vp_builder", "") != Player.m_localPlayer.GetPlayerName())
					{
						return true;
					}
					if (__instance.HaveValidUser())
					{
						Api.Message("You can only name the ship while nobody is steering.");
						__result = false;
						return false;
					}
					TextInput.instance.RequestText((TextReceiver)(object)new Namer(__instance.m_ship), "Name your ship", 32);
					__result = true;
					return false;
				}
				catch
				{
					return true;
				}
			}

			[HarmonyPatch(typeof(ShipControlls), "GetHoverText")]
			[HarmonyPostfix]
			private static void Hover(ShipControlls __instance, ref string __result)
			{
				try
				{
					ZDO val = Zdo((Component)(object)__instance.m_ship);
					if (val != null)
					{
						string text = val.GetString("vp_shipname", "");
						string text2 = val.GetString("vp_builder", "");
						string text3 = "";
						if (text.Length > 0)
						{
							text3 = text3 + "\n<color=#ffd27a>" + text + "</color>";
						}
						if (val.GetBool("vp_admiral", false) && text2.Length > 0)
						{
							text3 = text3 + "\nBuilt by " + text2;
						}
						if (val.GetBool("vp_icebreaker", false))
						{
							text3 += "\n<color=#9fd8ff>Icebreaker</color>";
						}
						if ((Object)(object)Player.m_localPlayer != (Object)null && text2 == Player.m_localPlayer.GetPlayerName() && Api.HasPerk("Sailing", NameLevel.Value))
						{
							text3 += "\n[<color=yellow><b>Shift + $KEY_Use</b></color>] Name ship";
						}
						if (text3.Length > 0)
						{
							__result += Localization.instance.Localize(text3);
						}
					}
				}
				catch
				{
				}
			}
		}

		internal sealed class Namer : TextReceiver
		{
			private readonly Ship _ship;

			public Namer(Ship ship)
			{
				_ship = ship;
			}

			public string GetText()
			{
				ZDO val = Zdo((Component)(object)_ship);
				if (val == null)
				{
					return "";
				}
				return val.GetString("vp_shipname", "");
			}

			public void SetText(string text)
			{
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				ZNetView val = (((Object)(object)_ship != (Object)null) ? ((Component)_ship).GetComponent<ZNetView>() : null);
				if (!((Object)(object)val == (Object)null) && val.IsValid())
				{
					text = (text ?? "").Replace("<", "").Replace(">", "").Trim();
					if (text.Length > 32)
					{
						text = text.Substring(0, 32);
					}
					val.ClaimOwnership();
					val.GetZDO().Set("vp_shipname", text);
					Pins.Remember(val.GetZDO().m_uid, text, ((Component)_ship).transform.position);
					Api.Message((text.Length > 0) ? ("This ship is now the " + text + ".") : "Name removed.");
				}
			}
		}

		internal static class Pins
		{
			private static readonly Dictionary<string, PinData> s_pins = new Dictionary<string, PinData>();

			private static Dictionary<string, string> Store(Player p)
			{
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				if (p.m_customData != null && p.m_customData.TryGetValue("vp_ships", out var value))
				{
					string[] array = value.Split(';');
					foreach (string text in array)
					{
						int num = text.IndexOf('=');
						if (num > 0)
						{
							dictionary[text.Substring(0, num)] = text.Substring(num + 1);
						}
					}
				}
				return dictionary;
			}

			private static void Save(Player p, Dictionary<string, string> d)
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, string> item in d)
				{
					list.Add(item.Key + "=" + item.Value);
				}
				p.m_customData["vp_ships"] = string.Join(";", list.ToArray());
			}

			private static string Key(ZDOID id)
			{
				return ((ZDOID)(ref id)).UserID.ToString(CultureInfo.InvariantCulture) + ":" + ((ZDOID)(ref id)).ID.ToString(CultureInfo.InvariantCulture);
			}

			internal static void Remember(ZDOID id, string name, Vector3 pos)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && localPlayer.m_customData != null)
				{
					Dictionary<string, string> dictionary = Store(localPlayer);
					if (name.Length == 0)
					{
						dictionary.Remove(Key(id));
					}
					else
					{
						dictionary[Key(id)] = name.Replace(";", "").Replace("=", "").Replace("|", "") + "|" + pos.x.ToString("0", CultureInfo.InvariantCulture) + "|" + pos.z.ToString("0", CultureInfo.InvariantCulture);
					}
					Save(localPlayer, dictionary);
				}
			}

			internal static void Update(Player p)
			{
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00da: Unknown result type (might be due to invalid IL or missing references)
				//IL_0213: Unknown result type (might be due to invalid IL or missing references)
				//IL_0223: 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)
				if ((Object)(object)Minimap.instance == (Object)null || p.m_customData == null)
				{
					return;
				}
				Dictionary<string, string> dictionary = Store(p);
				bool flag = false;
				foreach (IMonoUpdater item in new List<IMonoUpdater>(Ship.Instances))
				{
					Ship val = (Ship)(object)((item is Ship) ? item : null);
					ZDO val2 = (((Object)(object)val != (Object)null) ? Zdo((Component)(object)val) : null);
					if (val2 == null)
					{
						continue;
					}
					string key = Key(val2.m_uid);
					if (dictionary.TryGetValue(key, out var value))
					{
						string text = val2.GetString("vp_shipname", "");
						string text2 = text + "|" + ((Component)val).transform.position.x.ToString("0", CultureInfo.InvariantCulture) + "|" + ((Component)val).transform.position.z.ToString("0", CultureInfo.InvariantCulture);
						if (text.Length == 0)
						{
							dictionary.Remove(key);
							flag = true;
						}
						else if (text2 != value)
						{
							dictionary[key] = text2;
							flag = true;
						}
					}
				}
				if (flag)
				{
					Save(p, dictionary);
				}
				foreach (KeyValuePair<string, PinData> item2 in new Dictionary<string, PinData>(s_pins))
				{
					Minimap.instance.RemovePin(item2.Value);
				}
				s_pins.Clear();
				foreach (KeyValuePair<string, string> item3 in dictionary)
				{
					string[] array = item3.Value.Split('|');
					if (array.Length == 3 && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
					{
						s_pins[item3.Key] = Minimap.instance.AddPin(new Vector3(result, 0f, result2), (PinType)6, array[0], false, false, 0L, default(PlatformUserID));
					}
				}
			}
		}

		internal static class Seasons
		{
			internal static Type FloeType;

			private static MethodInfo s_frozen;

			internal static bool Frozen()
			{
				try
				{
					return s_frozen != null && (bool)s_frozen.Invoke(null, null);
				}
				catch
				{
					return false;
				}
			}

			internal static void Patch(Harmony h)
			{
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: Expected O, but got Unknown
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a5: Expected O, but got Unknown
				Type type = AccessTools.TypeByName("Seasons.ZoneSystemVariantController");
				FloeType = AccessTools.TypeByName("Seasons.IceFloe");
				if (!(type == null))
				{
					s_frozen = AccessTools.Method(type, "IsWaterSurfaceFrozen", (Type[])null, (Type[])null);
					MethodInfo methodInfo = AccessTools.Method(type, "PlaceShip", (Type[])null, (Type[])null);
					MethodInfo methodInfo2 = AccessTools.Method(type, "ShouldSkipFrozenShipPhysics", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Seasons), "PlacePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					if (methodInfo2 != null)
					{
						h.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Seasons), "SkipPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)("Seasons found: icebreaker hooks " + ((methodInfo != null) ? "on" : "missing") + "/" + ((methodInfo2 != null) ? "on" : "missing") + "."));
				}
			}

			private static bool PlacePrefix(Ship ship)
			{
				try
				{
					if ((Object)(object)ship != (Object)null && IsIcebreaker(ship))
					{
						Rigidbody component = ((Component)ship).GetComponent<Rigidbody>();
						if ((Object)(object)component != (Object)null && component.isKinematic)
						{
							component.isKinematic = false;
						}
						return false;
					}
				}
				catch
				{
				}
				return true;
			}

			private static void SkipPostfix(Ship ship, ref bool __result)
			{
				if (__result && (Object)(object)ship != (Object)null && IsIcebreaker(ship))
				{
					__result = false;
				}
			}
		}

		public const string Guid = "digi.vargheim.sailing";

		public const string Version = "1.0.5";

		private const string Prof = "Sailing";

		internal static ConfigEntry<int> CrewLevel;

		internal static ConfigEntry<int> NameLevel;

		internal static ConfigEntry<int> PatchLevel;

		internal static ConfigEntry<int> StormLevel;

		internal static ConfigEntry<int> IceLevel;

		internal static ConfigEntry<int> ChartLevel;

		internal static ConfigEntry<int> AdmiralLevel;

		internal static ConfigEntry<float> CrewPer;

		internal static ConfigEntry<float> CrewMax;

		internal static ConfigEntry<float> StormBonus;

		internal static ConfigEntry<float> StormWind;

		internal static ConfigEntry<float> IceSpeed;

		internal static ConfigEntry<float> ChartMult;

		internal static ConfigEntry<float> AdmiralHealth;

		internal static ConfigEntry<string> PatchCost;

		internal static ConfigEntry<string> LargeShips;

		internal static ConfigEntry<string> Rowboats;

		internal static ConfigEntry<float> RowboatHalf;

		internal static ConfigEntry<float> RowboatFull;

		internal static HashSet<string> LargeSet = new HashSet<string>(StringComparer.Ordinal);

		internal static Plugin Instance;

		private float _next;

		private float _nextPins;

		private static readonly FieldRef<Ship, List<Player>> s_players = AccessTools.FieldRefAccess<Ship, List<Player>>("m_players");

		private static MethodInfo s_explore;

		internal static void Log(string msg)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
			}
		}

		private void Awake()
		{
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Expected O, but got Unknown
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Expected O, but got Unknown
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Expected O, but got Unknown
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Expected O, but got Unknown
			//IL_0419: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Expected O, but got Unknown
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Expected O, but got Unknown
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0470: Expected O, but got Unknown
			//IL_04b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Expected O, but got Unknown
			Instance = this;
			CrewLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "CrewBonusLevel", 30, "Crew bonus level.");
			CrewPer = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "CrewBonusPerPlayer", 0.03f, "Crew bonus: extra sail speed per other player aboard while a sailor steers.");
			CrewMax = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "CrewBonusMax", 0.15f, "Crew bonus: most extra sail speed.");
			NameLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "ChristeningLevel", 35, "Christening: name ships you built (Shift + use on the rudder).");
			PatchLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "HullPatchLevel", 40, "Hull patch: repair the ship you're on, at sea (button in the Professions window).");
			PatchCost = ((BaseUnityPlugin)this).Config.Bind<string>("Perks", "HullPatchCost", "Wood:10,Resin:5", "Materials for one hull patch.");
			StormLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "StormRiderLevel", 50, "Storm rider level.");
			StormBonus = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "StormRiderBonus", 0.15f, "Storm rider: extra sail speed in storms and strong wind.");
			StormWind = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "StormRiderWind", 0.7f, "Storm rider: wind strength (0-1) that counts as rough weather.");
			IceLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "IcebreakerLevel", 60, "Icebreaker: larger ships a sailor of this level builds break Seasons ice.");
			Rowboats = ((BaseUnityPlugin)this).Config.Bind<string>("Rowboats", "Rowboats", "BBA_Canoe", "Ship prefabs that are rowed, not sailed: half and full speed ignore the wind and push straight ahead at a steady strength. Separated by ','.");
			RowboatHalf = ((BaseUnityPlugin)this).Config.Bind<float>("Rowboats", "HalfSpeed", 1.4f, "Rowboat push at half speed, as a multiple of its paddle (slow) push. Wind is ignored.");
			RowboatFull = ((BaseUnityPlugin)this).Config.Bind<float>("Rowboats", "FullSpeed", 1.8f, "Rowboat push at full speed, as a multiple of its paddle (slow) push. Wind is ignored.");
			LargeShips = ((BaseUnityPlugin)this).Config.Bind<string>("Perks", "IcebreakerShips", "VikingShip,VikingShip_Ashlands,KnarrBal,BigCargoShip,CargoShip,WarShip,MercantShip,HerculeShip,BBA_Knarr,BBA_LargeRaft", "Ship prefabs that count as larger ships.");
			IceSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "IcebreakerFrozenSpeed", 0.5f, "Icebreaker: sail speed while the ocean is frozen (0.5 = half).");
			ChartLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "SeaChartLevel", 75, "Sea chart level.");
			ChartMult = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "SeaChartRadiusMultiplier", 2f, "Sea chart: map reveal radius for everyone aboard, as a multiple of the normal reveal radius.");
			AdmiralLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "AdmiralLevel", 100, "Admiral level.");
			AdmiralHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Perks", "AdmiralHealthBonus", 0.25f, "Admiral: extra health for ships the admiral builds.");
			LargeSet = new HashSet<string>(LargeShips.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				LargeSet = new HashSet<string>(LargeShips.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal);
			};
			Api.RegisterLadder("Sailing", "VargheimSailing 1.0.5", "Smoothbrain Sailing: faster ships, less ship damage and a bigger map reveal at sea. Ships added by mods are built by sailors, by tier.", (Perk[])(object)new Perk[7]
			{
				new Perk(CrewLevel.Value, "Crew bonus", "When you steer, +" + P(CrewPer.Value) + " speed for each other player aboard, up to +" + P(CrewMax.Value) + "."),
				new Perk(NameLevel.Value, "Christening", "Name ships you built: Shift + use on the rudder. The name shows on hover and on your map."),
				new Perk(PatchLevel.Value, "Hull patch", "Repair the ship you're on, at sea, for " + PatchCost.Value.Replace(":", " ").Replace(",", ", ") + " (button below)."),
				new Perk(StormLevel.Value, "Storm rider", "+" + P(StormBonus.Value) + " sail speed in storms and strong wind while you steer."),
				new Perk(IceLevel.Value, "Icebreaker", "Larger ships you build smash winter ice floes without damage and can keep sailing through the frozen ocean at reduced speed."),
				new Perk(ChartLevel.Value, "Sea chart", "While you steer, everyone aboard reveals the map at your radius."),
				new Perk(AdmiralLevel.Value, "Admiral", "Ships you build have +" + P(AdmiralHealth.Value) + " health and show \"Built by <name>\".")
			});
			Content.AddAction("Sailing", PatchLevel.Value, "Patch the hull of this ship", (Func<string>)HullPatch);
			ShipSpeed.Bind(((BaseUnityPlugin)this).Config);
			Harmony val = new Harmony("digi.vargheim.sailing");
			val.PatchAll(typeof(Patches));
			val.PatchAll(typeof(ShipSpeed.Patches));
			try
			{
				Seasons.Patch(val);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Seasons icebreaker hooks skipped: " + ex.Message));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"VargheimSailing 1.0.5 loaded.");
		}

		internal static string P(float f)
		{
			return Mathf.RoundToInt(f * 100f) + "%";
		}

		internal static string PrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}

		internal static ZDO Zdo(Component c)
		{
			ZNetView val = (((Object)(object)c != (Object)null) ? c.GetComponent<ZNetView>() : null);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return null;
			}
			return val.GetZDO();
		}

		internal static Ship Steering()
		{
			Player localPlayer = Player.m_localPlayer;
			ShipControlls val = (ShipControlls)(((Object)(object)localPlayer != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.m_ship;
		}

		internal static Player Steerer(Ship s)
		{
			if ((Object)(object)s == (Object)null || (Object)(object)s.m_shipControlls == (Object)null)
			{
				return null;
			}
			long user = s.m_shipControlls.GetUser();
			if (user == 0L)
			{
				return null;
			}
			Player player = Player.GetPlayer(user);
			if (!((Object)(object)player != (Object)null) || !s.IsPlayerInBoat(player))
			{
				return null;
			}
			return player;
		}

		internal static int SteerLevel(Ship s)
		{
			Player val = Steerer(s);
			if (!((Object)(object)val != (Object)null))
			{
				return 0;
			}
			return Api.PerkLevelOf(val, "Sailing");
		}

		internal static Ship ShipUnder(Player p)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Ship localShip = Ship.GetLocalShip();
			if ((Object)(object)localShip != (Object)null && (Object)(object)p == (Object)(object)Player.m_localPlayer)
			{
				return localShip;
			}
			Ship result = null;
			float num = 20f;
			Ship[] array = Object.FindObjectsOfType<Ship>();
			foreach (Ship val in array)
			{
				if (val.IsPlayerInBoat(p))
				{
					return val;
				}
				float num2 = Vector3.Distance(((Component)val).transform.position, ((Component)p).transform.position);
				if (num2 < num)
				{
					result = val;
					num = num2;
				}
			}
			return result;
		}

		internal static bool IsIcebreaker(Ship s)
		{
			ZDO val = Zdo((Component)(object)s);
			if (val != null)
			{
				return val.GetBool("vp_icebreaker", false);
			}
			return false;
		}

		private static string HullPatch()
		{
			Player localPlayer = Player.m_localPlayer;
			Ship val = (((Object)(object)localPlayer != (Object)null) ? ShipUnder(localPlayer) : null);
			if ((Object)(object)val == (Object)null)
			{
				return "Stand on (or next to) the ship you want to patch.";
			}
			WearNTear component = ((Component)val).GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null)
			{
				return "This ship can't be patched.";
			}
			if (component.GetHealthPercentage() >= 0.999f)
			{
				return "The hull is already sound.";
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>();
			string[] array = PatchCost.Value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					list.Add(new KeyValuePair<string, int>(array2[0].Trim(), result));
				}
			}
			foreach (KeyValuePair<string, int> item in list)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Key);
				string text = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name : item.Key);
				if (inventory.CountItems(text, -1, true) < item.Value)
				{
					return "You need " + PatchCost.Value.Replace(":", " ").Replace(",", ", ") + " to patch the hull.";
				}
			}
			if (!component.Repair())
			{
				return "Couldn't patch the hull right now.";
			}
			foreach (KeyValuePair<string, int> item2 in list)
			{
				GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab(item2.Key);
				inventory.RemoveItem(itemPrefab2.GetComponent<ItemDrop>().m_itemData.m_shared.m_name, item2.Value, -1, true);
			}
			return "Hull patched.";
		}

		private void Update()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || Time.time < _next)
			{
				return;
			}
			_next = Time.time + 1f;
			try
			{
				Ship s = Steering();
				Ship localShip = Ship.GetLocalShip();
				if ((Object)(object)localShip != (Object)null && (Object)(object)Minimap.instance != (Object)null && SteerLevel(localShip) >= ChartLevel.Value)
				{
					float num = Minimap.instance.m_exploreRadius * ChartMult.Value;
					if (s_explore == null)
					{
						s_explore = AccessTools.Method(typeof(Minimap), "Explore", new Type[2]
						{
							typeof(Vector3),
							typeof(float)
						}, (Type[])null);
					}
					s_explore.Invoke(Minimap.instance, new object[2]
					{
						((Component)localPlayer).transform.position,
						num
					});
				}
				Icebreak(s);
				if (Time.time > _nextPins)
				{
					_nextPins = Time.time + 5f;
					Pins.Update(localPlayer);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)ex.Message);
			}
		}

		private static void Icebreak(Ship s)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_0091: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s == (Object)null || !IsIcebreaker(s) || Seasons.FloeType == null)
			{
				return;
			}
			Collider[] array = Physics.OverlapSphere(((Component)s).transform.position + ((Component)s).transform.forward * 4f, 9f);
			foreach (Collider val in array)
			{
				Component val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent(Seasons.FloeType) : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					IDestructible component = val2.GetComponent<IDestructible>();
					if (component != null)
					{
						HitData val3 = new HitData();
						val3.m_damage.m_blunt = 10000f;
						val3.m_damage.m_chop = 10000f;
						val3.m_damage.m_pickaxe = 10000f;
						val3.m_toolTier = 100;
						val3.m_point = val2.transform.position;
						val3.m_dir = ((Component)s).transform.forward;
						val3.SetAttacker((Character)(object)Player.m_localPlayer);
						component.Damage(val3);
					}
				}
			}
		}
	}
	internal static class ShipSpeed
	{
		private sealed class Ref
		{
			public float Length;

			public float Sail;

			public float Paddle;
		}

		internal sealed class Target
		{
			public float Sail;

			public float Paddle;

			public bool Row;
		}

		[HarmonyPatch]
		internal static class Patches
		{
			[HarmonyPatch(typeof(Ship), "Awake")]
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Awake(Ship __instance)
			{
				try
				{
					Apply(__instance);
				}
				catch
				{
				}
			}

			[HarmonyPatch(typeof(ZNetScene), "Awake")]
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Scene()
			{
				try
				{
					Reset();
					Log();
				}
				catch (Exception ex)
				{
					Plugin.Log("ship speed table: " + ex.Message);
				}
			}
		}

		internal static ConfigEntry<bool> Normalize;

		internal static ConfigEntry<bool> AutoRowboats;

		internal static ConfigEntry<float> Tune;

		internal static ConfigEntry<string> Exclude;

		internal static ConfigEntry<string> Vanilla;

		private static List<Ref> s_refs;

		private static readonly Dictionary<string, Target> s_targets = new Dictionary<string, Target>(StringComparer.Ordinal);

		private static bool s_logged;

		internal static void Bind(ConfigFile cfg)
		{
			Normalize = cfg.Bind<bool>("Ship Speed", "MatchVanilla", true, "Modded ships get the sail and paddle push of a vanilla ship of the same length (Raft, Karve, Longship, Drakkar). Multi-player bonuses (Oarsmen, Crew bonus) still apply on top.");
			Tune = cfg.Bind<float>("Ship Speed", "Tune", 1f, "Extra multiplier on the matched push of modded ships (1 = same as vanilla of that size).");
			Exclude = cfg.Bind<string>("Ship Speed", "Exclude", "Trailership", "Ship prefabs left exactly as their mod made them, separated by ','.");
			Vanilla = cfg.Bind<string>("Ship Speed", "VanillaShips", "Raft,Karve,VikingShip,VikingShip_Ashlands", "The vanilla ships used as the yardstick (left untouched).");
			cfg.SettingChanged += delegate
			{
				Reset();
			};
			cfg.ConfigReloaded += delegate
			{
				Reset();
			};
			AutoRowboats = cfg.Bind<bool>("Rowboats", "AutoDetect", true, "Ships without a sail count as rowboats: half and full speed ignore the wind.");
		}

		private static HashSet<string> List(string s)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string[] array = (s ?? string.Empty).Split(',');
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					hashSet.Add(text.Trim());
				}
			}
			return hashSet;
		}

		private static float Length(Ship s)
		{
			//IL_0014: 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)
			try
			{
				if ((Object)(object)s.m_floatCollider != (Object)null)
				{
					return Mathf.Abs(s.m_floatCollider.size.z * ((Component)s.m_floatCollider).transform.lossyScale.z);
				}
			}
			catch
			{
			}
			return 0f;
		}

		private static float Damp(Ship s)
		{
			return Mathf.Max(0.0001f, s.m_dampingForward);
		}

		private static void BuildRefs()
		{
			if (s_refs != null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			List<Ref> list = new List<Ref>();
			foreach (string item in List(Vanilla.Value))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(item);
				Ship val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Ship>() : null);
				float num = (((Object)(object)val != (Object)null) ? Length(val) : 0f);
				if ((Object)(object)val != (Object)null && num > 0f)
				{
					list.Add(new Ref
					{
						Length = num,
						Sail = val.m_sailForceFactor / Damp(val),
						Paddle = val.m_backwardForce / Damp(val)
					});
				}
			}
			list.Sort((Ref a, Ref b) => a.Length.CompareTo(b.Length));
			s_refs = list;
		}

		private static void Interp(float len, out float sail, out float paddle)
		{
			sail = (paddle = 0f);
			if (s_refs == null || s_refs.Count == 0)
			{
				return;
			}
			if (len <= s_refs[0].Length)
			{
				sail = s_refs[0].Sail;
				paddle = s_refs[0].Paddle;
				return;
			}
			for (int i = 1; i < s_refs.Count; i++)
			{
				if (len <= s_refs[i].Length)
				{
					Ref obj = s_refs[i - 1];
					Ref obj2 = s_refs[i];
					float num = (len - obj.Length) / Mathf.Max(0.001f, obj2.Length - obj.Length);
					sail = Mathf.Lerp(obj.Sail, obj2.Sail, num);
					paddle = Mathf.Lerp(obj.Paddle, obj2.Paddle, num);
					return;
				}
			}
			Ref obj3 = s_refs[s_refs.Count - 1];
			sail = obj3.Sail;
			paddle = obj3.Paddle;
		}

		internal static Target For(Ship s)
		{
			if ((Object)(object)s == (Object)null)
			{
				return null;
			}
			string prefabName = Utils.GetPrefabName(((Component)s).gameObject);
			if (s_targets.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			BuildRefs();
			value = null;
			bool flag = List(Vanilla.Value).Contains(prefabName);
			bool flag2 = List(Exclude.Value).Contains(prefabName);
			if (!flag2 && (Plugin.Patches.IsRowboatName(prefabName) || (AutoRowboats.Value && !s.m_hasSail && !flag)))
			{
				value = new Target
				{
					Sail = -1f,
					Paddle = -1f,
					Row = true
				};
			}
			else if (Normalize.Value && !flag && !flag2 && s_refs != null && s_refs.Count > 0)
			{
				Interp(Length(s), out var sail, out var paddle);
				value = new Target
				{
					Sail = sail * Damp(s) * Tune.Value,
					Paddle = paddle * Damp(s) * Tune.Value,
					Row = false
				};
			}
			if (s_refs != null)
			{
				s_targets[prefabName] = value;
			}
			return value;
		}

		internal static bool IsRow(Ship s)
		{
			return For(s)?.Row ?? false;
		}

		internal static void Apply(Ship s)
		{
			Target target = For(s);
			if (target != null)
			{
				if (target.Sail >= 0f && !Mathf.Approximately(s.m_sailForceFactor, target.Sail))
				{
					s.m_sailForceFactor = target.Sail;
				}
				if (target.Paddle >= 0f && !Mathf.Approximately(s.m_backwardForce, target.Paddle))
				{
					s.m_backwardForce = target.Paddle;
				}
			}
		}

		internal static void Reset()
		{
			s_targets.Clear();
			s_refs = null;
		}

		internal static void Log()
		{
			if (s_logged || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			s_logged = true;
			BuildRefs();
			StringBuilder stringBuilder = new StringBuilder("VargheimSailing ship speeds (prefab | length | sail x damping-ratio before -> after | paddle before -> after | rowboat):");
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				Ship val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Ship>() : null);
				if (!((Object)(object)val == (Object)null))
				{
					Target target = For(val);
					stringBuilder.Append("\n  ").Append(((Object)prefab).name).Append(" | ")
						.Append(Length(val).ToString("0.0", CultureInfo.InvariantCulture))
						.Append(" | ")
						.Append(val.m_sailForceFactor.ToString("0.###", CultureInfo.InvariantCulture))
						.Append(" -> ")
						.Append((target != null && target.Sail >= 0f) ? target.Sail.ToString("0.###", CultureInfo.InvariantCulture) : "same")
						.Append(" | ")
						.Append(val.m_backwardForce.ToString("0.###", CultureInfo.InvariantCulture))
						.Append(" -> ")
						.Append((target != null && target.Paddle >= 0f) ? target.Paddle.ToString("0.###", CultureInfo.InvariantCulture) : "same")
						.Append(" | damp ")
						.Append(val.m_dampingForward.ToString("0.###", CultureInfo.InvariantCulture))
						.Append(" | ")
						.Append((target != null && target.Row) ? "row" : (val.m_hasSail ? "sail" : "no sail"));
				}
			}
			Plugin.Log(stringBuilder.ToString());
		}
	}
}