Decompiled source of EpicShips v0.1.5

BepInEx/plugins/EpicShips/EpicShips.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using EpicLoot;
using EpicLoot.Crafting;
using EpicLoot.CraftingV2;
using EpicLoot.LegendarySystem;
using EpicLoot.ShardStones;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PortableShips;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("EpicShips")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyInformationalVersion("0.1.5")]
[assembly: AssemblyProduct("EpicShips")]
[assembly: AssemblyTitle("EpicShips")]
[assembly: AssemblyVersion("0.1.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 FalseHarmony.EpicShips
{
	internal sealed class EffectSpec
	{
		internal readonly string Key;

		internal readonly string Name;

		internal readonly string Description;

		internal readonly string Gem;

		internal readonly string Requires;

		internal readonly float Low;

		internal readonly float High;

		internal readonly float Weight;

		internal readonly int MinimumRarity;

		internal readonly bool Defensive;

		internal string Id => "FHEpicShips" + Key;

		internal EffectSpec(string key, string name, string description, float low, float high, string gem = "", int min = 0, float weight = 100f, string requires = "", bool defensive = false)
		{
			Key = key;
			Name = name;
			Description = description;
			Low = low;
			High = high;
			Gem = gem;
			MinimumRarity = min;
			Weight = weight;
			Requires = requires;
			Defensive = defensive;
		}

		internal float GemValue(int rarity)
		{
			return Low + (High - Low) * (float)(rarity - MinimumRarity) / (float)(5 - MinimumRarity);
		}
	}
	internal static class Catalog
	{
		internal static readonly EffectSpec[] All = new EffectSpec[46]
		{
			new EffectSpec("Hull", "Stout Hull", "+{0}% vanilla hull health", 10f, 30f, "", 0, 100f, "", defensive: true),
			new EffectSpec("Physical", "Ironbound", "{0}% less physical hull damage", 5f, 15f, "", 0, 100f, "", defensive: true),
			new EffectSpec("Fire", "Emberward", "{0}% less fire hull damage", 10f, 30f, "", 0, 100f, "", defensive: true),
			new EffectSpec("Impact", "Wavebreaker", "{0}% less collision and wave damage", 15f, 40f, "", 0, 100f, "", defensive: true),
			new EffectSpec("PlayerWard", "Crew's Covenant", "Immune to player damage", 1f, 1f, "", 0, 100f, "", defensive: true),
			new EffectSpec("Sail", "Fair Winds", "+{0}% sail propulsion", 5f, 15f),
			new EffectSpec("Row", "Tireless Oars", "+{0}% rowing propulsion", 10f, 25f),
			new EffectSpec("Steer", "Nimble Keel", "+{0}% steering response", 10f, 25f),
			new EffectSpec("Drift", "Steady Keel", "{0}% less sideways drift", 10f, 25f),
			new EffectSpec("Mend", "Mender's Blessing", "+{0}% hull regen/s (stopped, out of combat)", 0.1f, 0.3f, "", 0, 100f, "", defensive: true),
			new EffectSpec("Swim", "Sheltering Voyage", "-{0}% swim stamina use (10m)", 5f, 15f),
			new EffectSpec("Mist", "Mistwatch", "+{0}% wisplight range", 15f, 40f, "", 0, 100f, "wisp"),
			new EffectSpec("Horn", "Njörðr's Favor", "{0}% less horn cooldown", 10f, 25f, "", 0, 100f, "horn"),
			new EffectSpec("Cast", "Quickening Runes", "+{0}% shrink/summon speed", 10f, 30f),
			new EffectSpec("Tailwind", "Njorn's Chosen", "Always tailwind", 1f, 1f, "", 3, 1f),
			new EffectSpec("Immortal", "Oden's Chosen", "Indestructible hull", 1f, 1f, "", 3, 0.1f),
			new EffectSpec("VoyagerEitr", "Voyager: Arcane Passage", "Voyager +{0}% Eitr regen", 15f, 40f),
			new EffectSpec("VoyagerHealth", "Voyager: Restful Passage", "Voyager +{0}% health regen", 20f, 50f),
			new EffectSpec("VoyagerStamina", "Voyager: Tireless Passage", "Voyager +{0}% stamina regen", 15f, 40f),
			new EffectSpec("GemRed", "Heartwood", "+{0}% vanilla hull health", 3f, 15f, "Red", 0, 100f, "", defensive: true),
			new EffectSpec("GemYellow", "Oarsman", "+{0}% rowing propulsion", 3f, 15f, "Yellow"),
			new EffectSpec("GemCyan", "Runic Conduit", "+{0}% shrink/summon speed", 3f, 15f, "Cyan"),
			new EffectSpec("GemOrange", "Emberguard", "{0}% less fire hull damage", 5f, 25f, "Orange", 0, 100f, "", defensive: true),
			new EffectSpec("GemPink", "Dancer's Keel", "+{0}% steering response", 4f, 20f, "Pink"),
			new EffectSpec("GemBlack", "Night Voyager", "+{0}% sail propulsion at night", 4f, 20f, "Black"),
			new EffectSpec("GemWhite", "Day Voyager", "+{0}% sail propulsion during daytime", 4f, 20f, "White"),
			new EffectSpec("GemGreen", "Sure Keel", "{0}% less sideways drift", 4f, 20f, "Green"),
			new EffectSpec("GemPurple", "Echoing Horn", "{0}% less horn cooldown", 3f, 15f, "Purple", 0, 100f, "horn"),
			new EffectSpec("GemGrey", "Angler's Haven", "-{0}% fishing stamina use", 5f, 25f, "Grey"),
			new EffectSpec("GemDarkRed", "Battlewake", "+{0}% speed after enemy hit (10s)", 5f, 25f, "DarkRed"),
			new EffectSpec("GemDarkGreen", "Venomward", "-{0}% passenger poison damage", 3f, 15f, "DarkGreen"),
			new EffectSpec("GemDarkBlue", "Winterhold", "-{0}% passenger frost damage", 3f, 15f, "DarkBlue"),
			new EffectSpec("GemDarkPurple", "Spiteful Hull", "{0}% enemy damage reflection", 5f, 20f, "DarkPurple", 0, 100f, "", defensive: true),
			new EffectSpec("GemGolden", "Fortunate Catch", "+{0}% extra fish chance", 2f, 10f, "Golden"),
			new EffectSpec("GemLightBlue", "Stormsail", "+{0}% sail propulsion during storms", 5f, 25f, "LightBlue"),
			new EffectSpec("GemLightGreen", "Healing Anchorage", "+{0}% hull regen/min (stopped, out of combat)", 0.5f, 2f, "LightGreen", 0, 100f, "", defensive: true),
			new EffectSpec("GemPeach", "Mariner's Endurance", "-{0}% swim stamina use (10m)", 5f, 20f, "Peach"),
			new EffectSpec("GemFirewalker", "Cinderwake", "-{0}% boiling-water hull damage", 15f, 50f, "Firewalker", 2, 100f, "", defensive: true),
			new EffectSpec("GemStormcaller", "Thunderous Horn", "+{0}% speed after horn (20s)", 10f, 25f, "Stormcaller", 2, 100f, "horn"),
			new EffectSpec("GemEikthyr", "Stag's Passage", "+{0}% passenger stamina regen", 5f, 15f, "Eikthyr", 1),
			new EffectSpec("GemElder", "Ancient Timber", "{0}% less collision and wave damage", 10f, 30f, "Elder", 1, 100f, "", defensive: true),
			new EffectSpec("GemBonemass", "Living Bulwark", "-{0}% enemy physical hull damage", 5f, 20f, "Bonemass", 2, 100f, "", defensive: true),
			new EffectSpec("GemModer", "Dragon's Breath", "{0}% less headwind penalty", 15f, 40f, "Moder", 2),
			new EffectSpec("GemYagluth", "King's Sanctuary", "-{0}% passenger fire/frost/lightning damage", 5f, 15f, "Yagluth", 3),
			new EffectSpec("GemQueen", "Queen's Insight", "+{0}% passenger Eitr regen", 10f, 30f, "Queen", 3),
			new EffectSpec("GemFader", "Last Ember", "{0}% hull shield below 25% HP (15m cooldown)", 5f, 15f, "Fader", 4, 100f, "", defensive: true)
		};

		internal static readonly Dictionary<string, EffectSpec> ById = All.ToDictionary<EffectSpec, string>((EffectSpec e) => e.Id, StringComparer.Ordinal);

		internal static bool IsShip(string? prefab)
		{
			if (!(prefab == "PortableShips_PackedKarve") && !(prefab == "PortableShips_PackedLongship"))
			{
				return prefab == "PortableShips_PackedDrakkar";
			}
			return true;
		}

		internal static float Reduce(params float[] reductions)
		{
			float num = 1f;
			foreach (float val in reductions)
			{
				num *= 1f - Math.Max(0f, Math.Min(75f, val)) / 100f;
			}
			return Math.Max(0.25f, num);
		}

		internal static float SumCap(float cap, params float[] values)
		{
			return Math.Min(cap, values.Sum());
		}
	}
	[HarmonyPatch(typeof(UniqueLegendaryHelper), "GetAvailableLegendaries")]
	internal static class ShipLegendaryPool
	{
		private static bool Prefix(ItemData baseItem, ref IList<LegendaryInfo> __result)
		{
			if (!EpicShipsPlugin.IsShip(baseItem))
			{
				return true;
			}
			__result = new List<LegendaryInfo> { UniqueLegendaryHelper.GenericLegendaryInfo };
			return false;
		}
	}
	[HarmonyPatch(typeof(UniqueLegendaryHelper), "GetAvailableMythics")]
	internal static class ShipMythicPool
	{
		private static bool Prefix(ItemData baseItem, ref IList<LegendaryInfo> __result)
		{
			if (!EpicShipsPlugin.IsShip(baseItem))
			{
				return true;
			}
			__result = new List<LegendaryInfo> { UniqueLegendaryHelper.GenericLegendaryInfo };
			return false;
		}
	}
	[HarmonyPatch(typeof(EnchantCostsHelper), "GetEnchantCost")]
	internal static class ShipEnchantCosts
	{
		private unsafe static void Postfix(ItemData item, ItemRarity rarity, ref List<ItemAmountConfig> __result)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			if (EpicShipsPlugin.IsShip(item))
			{
				__result = ((IEnumerable<EnchantCostConfig>)EnchantCostsHelper.Config.EnchantCosts).FirstOrDefault((Func<EnchantCostConfig, bool>)((EnchantCostConfig c) => c.Rarity == rarity && c.ItemTypes != null && c.ItemTypes.Contains("Tool")))?.Cost ?? new List<ItemAmountConfig>
				{
					new ItemAmountConfig
					{
						Item = "Runestone" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
						Amount = 1
					},
					new ItemAmountConfig
					{
						Item = "Dust" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
						Amount = 5
					},
					new ItemAmountConfig
					{
						Item = "Essence" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
						Amount = 5
					}
				};
			}
		}
	}
	[HarmonyPatch(typeof(EnchantCostsHelper), "GetAugmentCost")]
	internal static class ShipAugmentCosts
	{
		private unsafe static bool Prefix(ItemData item, ItemRarity rarity, int recipeEffectIndex, ref List<ItemAmountConfig> __result)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0085: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			if (!EpicShipsPlugin.IsShip(item))
			{
				return true;
			}
			__result = ((IEnumerable<AugmentCostConfig>)EnchantCostsHelper.Config.AugmentCosts).FirstOrDefault((Func<AugmentCostConfig, bool>)((AugmentCostConfig c) => c.Rarity == rarity && c.ItemTypes != null && c.ItemTypes.Contains("Tool")))?.Cost.ToList() ?? new List<ItemAmountConfig>
			{
				new ItemAmountConfig
				{
					Item = "Shard" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
					Amount = 1
				},
				new ItemAmountConfig
				{
					Item = "Dust" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
					Amount = 1
				},
				new ItemAmountConfig
				{
					Item = "Essence" + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(),
					Amount = 1
				}
			};
			if (ItemDataExtensions.GetMagicItem(item) != null && !ItemDataExtensions.GetMagicItem(item).IsEffectAugmented(recipeEffectIndex))
			{
				ItemAmountConfig reAugmentCost = EnchantCostsHelper.GetReAugmentCost(item, recipeEffectIndex);
				if (reAugmentCost != null)
				{
					__result.Add(reAugmentCost);
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(EnchantCostsHelper), "GetRuneCost")]
	internal static class ShipRuneCosts
	{
		private unsafe static bool Prefix(ItemData item, ItemRarity rarity, RuneActions operation, ref List<ItemAmountConfig> __result)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!EpicShipsPlugin.IsShip(item))
			{
				return true;
			}
			RuneCostConfig val = ((IEnumerable<RuneCostConfig>)(((int)operation == 0) ? EnchantCostsHelper.Config.RuneExtractCosts : EnchantCostsHelper.Config.RuneEtchCosts)).FirstOrDefault((Func<RuneCostConfig, bool>)((RuneCostConfig c) => c.Rarity == rarity && (c.ItemTypes == null || c.ItemTypes.Count == 0 || c.ItemTypes.Contains("Tool"))));
			if (val == null)
			{
				throw new InvalidOperationException("No configured rune recipe for ship rarity " + ((object)(*(ItemRarity*)(&rarity))/*cast due to .constrained prefix*/).ToString());
			}
			__result = val.Cost.ToList();
			return false;
		}
	}
	[HarmonyPatch(typeof(EpicLoot), "CanBeMagicItem")]
	internal static class ShipEnchantEligibility
	{
		private static bool Prefix(ItemData item, ref bool __result)
		{
			if (!EpicShipsPlugin.IsShip(item))
			{
				return true;
			}
			__result = ShipAddonAPI.PreparePackedItem(item);
			return false;
		}
	}
	[HarmonyPatch(typeof(MagicItemEffectDefinitions), "GetAvailableEffects")]
	internal static class ShipEffectPool
	{
		private static void Postfix(ItemData itemData, ref List<MagicItemEffectDefinition> __result)
		{
			if (!EpicShipsPlugin.IsShip(itemData))
			{
				return;
			}
			if (!ShipAddonAPI.PreparePackedItem(itemData))
			{
				__result.Clear();
				return;
			}
			__result.RemoveAll((MagicItemEffectDefinition d) => !Catalog.ById.TryGetValue(d.Type, out EffectSpec value) || value.Gem.Length > 0);
		}
	}
	[HarmonyPatch(typeof(Shards), "GetShardEffect")]
	internal static class ShipGemEffects
	{
		private unsafe static bool Prefix(ItemData item, ShardType color, ref ShardEffectDefinition __result)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			if (!EpicShipsPlugin.IsShip(item))
			{
				return true;
			}
			EffectSpec effectSpec = Catalog.All.FirstOrDefault((EffectSpec e) => e.Gem == ((object)(*(ShardType*)(&color))/*cast due to .constrained prefix*/).ToString());
			if (effectSpec == null)
			{
				__result = null;
				return false;
			}
			__result = new ShardEffectDefinition
			{
				EffectType = effectSpec.Id
			};
			for (int num = effectSpec.MinimumRarity; num < 6; num++)
			{
				__result.ValuesPerRarity[(ItemRarity)num] = effectSpec.GemValue(num);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ShardSocketManager), "CanSocket")]
	internal static class GuardShipSockets
	{
		private static bool Prefix(ItemData equipment, ItemData input, ref string reason, ref bool __result)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Invalid comparison between Unknown and I4
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			if (!EpicShipsPlugin.IsShip(equipment))
			{
				return true;
			}
			bool num = ShipAddonAPI.PreparePackedItem(equipment);
			MagicItem magicItem = ItemDataExtensions.GetMagicItem(equipment);
			MagicItemEffect val = default(MagicItemEffect);
			ShardType color = default(ShardType);
			ItemRarity val2 = default(ItemRarity);
			if (num && magicItem != null && ShardSocketManager.ResolveSocketedEffect(equipment, input, ref val, ref color, ref val2) && val != null && Catalog.ById.TryGetValue(val.EffectType, out EffectSpec value) && EpicShipsPlugin.Allowed(equipment, magicItem, value) && ((int)color != 97 || (value.Key != "Immortal" && value.Key != "Tailwind")) && ((int)color == 97 || magicItem.Sockets.All((SocketedEffect s) => s == null || s.ShardType != color)))
			{
				return true;
			}
			reason = "This ship cannot accept that effect, duplicate gem, or missing required upgrade.";
			__result = false;
			return false;
		}
	}
	[BepInPlugin("FalseHarmony.EpicShips", "EpicShips", "0.1.5")]
	[BepInDependency("portable.ships", "0.12.6")]
	[BepInDependency("randyknapp.mods.epicloot", "0.14.13")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class EpicShipsPlugin : BaseUnityPlugin
	{
		private sealed class Cached
		{
			internal string Raw = "";

			internal ShipMagicState State = new ShipMagicState();

			internal Dictionary<string, float> Values = new Dictionary<string, float>();
		}

		public const string Id = "FalseHarmony.EpicShips";

		internal static EpicShipsPlugin Instance = null;

		private static readonly ConditionalWeakTable<ZDO, Cached> Cache = new ConditionalWeakTable<ZDO, Cached>();

		private Harmony patches;

		private float nextError;

		internal void LoggerMessage(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}

		private void Awake()
		{
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected O, but got Unknown
			Instance = this;
			if (API.GetPluginVersion() != "0.14.13")
			{
				throw new InvalidOperationException("EpicShips 0.1.5 requires the inspected Epic Loot 0.14.13 API.");
			}
			RegisterEffects();
			API.RegisterSacrificeFilter("FalseHarmony.EpicShips", (Func<ItemData, bool>)((ItemData item) => !IsShip(item)));
			API.AddMagicItemChangedListener((Action<ItemData, string>)OnMagicChanged);
			ShipAddonAPI.Register("FalseHarmony.EpicShips", (Action<Ship, ItemData>)Capture, (Action<ItemData>)Validate, (Action<ZDO, ItemData>)Restore, (Action<Ship, ItemData>)Verify, (Func<ZDO, float, float>)((ZDO z, float hp) => hp + BaseHealth(z) * Catalog.SumCap(60f, Values(z).Get("Hull"), Values(z).Get("GemRed")) / 100f), (Func<ZDO, float, float>)((ZDO z, float seconds) => seconds * Catalog.Reduce(Values(z).Get("Cast"), Values(z).Get("GemCyan"))), (Func<ItemData, float, float>)((ItemData item, float seconds) => seconds * Catalog.Reduce(ItemState(item).Values().Get("Cast"), ItemState(item).Values().Get("GemCyan"))), (Func<ZDO, float, float>)((ZDO z, float seconds) => seconds * Catalog.Reduce(Values(z).Get("Horn"), Values(z).Get("GemPurple"))), (Action<ZDO>)delegate(ZDO z)
			{
				ShipMagicState shipMagicState = State(z);
				if (!string.IsNullOrEmpty(shipMagicState.Magic))
				{
					shipMagicState.HornBurstUntil = ((Values(z).Get("GemStormcaller") > 0f) ? DateTime.UtcNow.AddSeconds(20.0).Ticks : 0);
					z.Set("FalseHarmony.EpicShips.State", shipMagicState.Encode());
				}
			});
			VoyagerStatus voyagerStatus = ScriptableObject.CreateInstance<VoyagerStatus>();
			((Object)voyagerStatus).name = "EpicShips_Voyager";
			((StatusEffect)voyagerStatus).m_name = "Voyager";
			ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)voyagerStatus, false));
			patches = new Harmony("FalseHarmony.EpicShips");
			patches.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"EpicShips 0.1.5: 19 ship enchantments, 27 shardstone effects and Voyager registered. Live testing required.");
		}

		private void Update()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return;
			}
			try
			{
				PassengerBuffs.Tick(Player.m_localPlayer);
			}
			catch (Exception ex)
			{
				if (Time.unscaledTime >= nextError)
				{
					nextError = Time.unscaledTime + 10f;
					((BaseUnityPlugin)this).Logger.LogError((object)("EpicShips passenger effects: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
			API.RemoveMagicItemChangedListener((Action<ItemData, string>)OnMagicChanged);
			Harmony obj = patches;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		internal static bool IsShip(ItemData? item)
		{
			if (item != null && Object.op_Implicit((Object)(object)item.m_dropPrefab))
			{
				return Catalog.IsShip(((Object)item.m_dropPrefab).name);
			}
			return false;
		}

		internal static ShipMagicState ItemState(ItemData item)
		{
			string value;
			ShipMagicState shipMagicState = ShipMagicState.Decode(item.m_customData.TryGetValue("FalseHarmony.EpicShips.State", out value) ? value : "");
			string magicItemJson = API.GetMagicItemJson(item);
			if (!string.IsNullOrEmpty(shipMagicState.Magic) && string.IsNullOrEmpty(magicItemJson))
			{
				throw new InvalidOperationException("The packed ship's Epic Loot data is missing; keep the item for recovery.");
			}
			if (!string.IsNullOrEmpty(magicItemJson) && magicItemJson != "null")
			{
				shipMagicState.Magic = magicItemJson;
			}
			shipMagicState.Validate();
			return shipMagicState;
		}

		private static void OnMagicChanged(ItemData item, string reason)
		{
			if (!IsShip(item))
			{
				return;
			}
			ShipMagicState shipMagicState = ItemState(item);
			if (!string.IsNullOrEmpty(shipMagicState.Magic))
			{
				if (shipMagicState.Values().Get("GemFader") <= 0f)
				{
					shipMagicState.ShieldHealth = 0f;
				}
				item.m_customData["FalseHarmony.EpicShips.State"] = shipMagicState.Encode();
				ShipAddonAPI.Require(item, "FalseHarmony.EpicShips");
			}
		}

		private static Cached Read(ZDO zdo)
		{
			Cached value = Cache.GetValue(zdo, (ZDO _) => new Cached());
			string text = zdo.GetString("FalseHarmony.EpicShips.State", "");
			if (value.Raw != text)
			{
				value.State = ShipMagicState.Decode(text);
				value.Values = value.State.Values();
				value.Raw = text;
			}
			return value;
		}

		internal static ShipMagicState State(ZDO zdo)
		{
			return Read(zdo).State;
		}

		internal static Dictionary<string, float> Values(ZDO zdo)
		{
			return Read(zdo).Values;
		}

		internal static bool Valid(ZDO zdo)
		{
			try
			{
				return !string.IsNullOrEmpty(State(zdo).Magic);
			}
			catch
			{
				return false;
			}
		}

		internal static float BaseHealth(ZDO zdo)
		{
			return (zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("Karve")) ? 500 : ((zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("VikingShip")) ? 1000 : 3000);
		}

		private static void Capture(Ship ship, ItemData item)
		{
			ZDO zDO = ((Component)ship).GetComponent<ZNetView>().GetZDO();
			if (zDO.GetBool("WispUpgraded", false))
			{
				item.m_customData["FalseHarmony.EpicShips.HasWisp"] = "1";
			}
			string text = zDO.GetString("FalseHarmony.EpicShips.State", "");
			if (!string.IsNullOrEmpty(text))
			{
				ShipMagicState shipMagicState = ShipMagicState.Decode(text);
				if (!string.IsNullOrEmpty(shipMagicState.Magic) && item.m_customData.TryGetValue("PortableShips.ShipName", out var value) && !string.IsNullOrEmpty(value))
				{
					JObject val = ShipMagicState.ReadObject(shipMagicState.Magic);
					val["DisplayName"] = JToken.op_Implicit(value);
					shipMagicState.Magic = ((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>());
				}
				if (!string.IsNullOrEmpty(shipMagicState.Magic) && !API.ApplyMagicItemJson(item, shipMagicState.Magic))
				{
					throw new InvalidOperationException("Epic Loot could not restore the ship item's magic data.");
				}
				item.m_customData["FalseHarmony.EpicShips.State"] = shipMagicState.Encode();
				ShipAddonAPI.Require(item, "FalseHarmony.EpicShips");
			}
		}

		private static void Validate(ItemData item)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if (!IsShip(item))
			{
				return;
			}
			if (item.m_customData.TryGetValue("PortableShips.RequiredAddons", out var value) && value.Split(new char[1] { ';' }).Contains("FalseHarmony.EpicShips") && !item.m_customData.ContainsKey("FalseHarmony.EpicShips.State"))
			{
				throw new InvalidOperationException("The packed ship's addon snapshot is missing; keep the item for recovery.");
			}
			ShipMagicState shipMagicState = ItemState(item);
			if (string.IsNullOrEmpty(shipMagicState.Magic))
			{
				return;
			}
			foreach (JToken item2 in (IEnumerable<JToken>)(((object)ShipMagicState.ReadObject(shipMagicState.Magic)["Sockets"]) ?? ((object)new JArray())))
			{
				string text = ((string)((item2 != null) ? item2[(object)"SourcePrefab"] : null)) ?? "";
				if (text.Length > 0 && Object.op_Implicit((Object)(object)ObjectDB.instance) && !Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(text)))
				{
					throw new InvalidOperationException("Missing socketed ship gem: " + text);
				}
			}
			item.m_customData["FalseHarmony.EpicShips.State"] = shipMagicState.Encode();
			ShipAddonAPI.Require(item, "FalseHarmony.EpicShips");
		}

		private static void Restore(ZDO zdo, ItemData item)
		{
			if (item.m_customData.ContainsKey("FalseHarmony.EpicShips.State"))
			{
				ShipMagicState shipMagicState = ItemState(item);
				zdo.Set("FalseHarmony.EpicShips.State", shipMagicState.Encode());
			}
		}

		private static void Verify(Ship ship, ItemData item)
		{
			if (!item.m_customData.ContainsKey("FalseHarmony.EpicShips.State") || State(((Component)ship).GetComponent<ZNetView>().GetZDO()).SameRestoredMagic(ItemState(item)))
			{
				return;
			}
			throw new InvalidOperationException("Enchanted ship restoration failed; item retained for recovery.");
		}

		private static void RegisterEffects()
		{
			//IL_0044: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_0142: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			EffectSpec[] all = Catalog.All;
			foreach (EffectSpec effectSpec in all)
			{
				EffectSpec selected = effectSpec;
				string text = "FalseHarmony.EpicShips." + effectSpec.Key;
				API.RegisterMagicEffectRequirement(text, (Func<ItemData, object, string, bool, bool, bool, bool>)((ItemData item, object magic, string type, bool loot, bool augment, bool rune) => Allowed(item, (MagicItem?)((magic is MagicItem) ? magic : null), selected)));
				MagicItemEffectDefinition val = new MagicItemEffectDefinition
				{
					Type = effectSpec.Id,
					DisplayText = effectSpec.Description,
					Description = effectSpec.Description,
					SelectionWeight = effectSpec.Weight,
					CanBeAugmented = (effectSpec.Gem.Length == 0),
					CanBeRunified = (effectSpec.Gem.Length == 0 && effectSpec.Key != "Immortal" && effectSpec.Key != "Tailwind"),
					Requirements = new MagicItemEffectRequirements
					{
						NoRoll = (effectSpec.Gem.Length > 0),
						ExclusiveSelf = true,
						ExternalRequirements = new List<string> { text },
						AllowedRarities = (from num3 in Enumerable.Range(effectSpec.MinimumRarity, 6 - effectSpec.MinimumRarity)
							select (ItemRarity)num3).ToList()
					}
				};
				for (int num = effectSpec.MinimumRarity; num < 6; num++)
				{
					float num2 = (float)(num - effectSpec.MinimumRarity) / (float)Math.Max(1, 5 - effectSpec.MinimumRarity);
					ValueDef value = new ValueDef
					{
						MinValue = effectSpec.Low + (effectSpec.High - effectSpec.Low) * num2 * 0.65f,
						MaxValue = effectSpec.Low + (effectSpec.High - effectSpec.Low) * (0.35f + 0.65f * num2),
						Increment = ((effectSpec.High < 1f) ? 0.01f : 1f)
					};
					typeof(ValuesPerRarityDef).GetField(((object)(ItemRarity)num/*cast due to .constrained prefix*/).ToString()).SetValue(val.ValuesPerRarity, value);
				}
				if (string.IsNullOrEmpty(API.AddMagicEffect(JsonConvert.SerializeObject((object)val))))
				{
					throw new InvalidOperationException("Epic Loot rejected " + effectSpec.Id);
				}
			}
		}

		internal static bool Allowed(ItemData item, MagicItem? magic, EffectSpec spec)
		{
			if (!IsShip(item))
			{
				return false;
			}
			if (!item.m_customData.TryGetValue("PortableShips.State", out var value) || value != "ready")
			{
				return false;
			}
			if (spec.Requires == "horn" && (!item.m_customData.TryGetValue("PortableShips.WindHorn", out var value2) || value2 != "1"))
			{
				return false;
			}
			if (spec.Requires == "wisp")
			{
				if (!item.m_customData.TryGetValue("PortableShips.LongshipUpgrades", out var _))
				{
					return false;
				}
				if (!item.m_customData.TryGetValue("FalseHarmony.EpicShips.HasWisp", out var value4) || value4 != "1")
				{
					return false;
				}
			}
			string[] source = (from e in magic?.Effects.Concat(from s in magic.Sockets
					where s?.Effect != null
					select s.Effect)
				select e.EffectType).ToArray() ?? Array.Empty<string>();
			if (spec.Defensive && source.Contains("FHEpicShipsImmortal"))
			{
				return false;
			}
			if (spec.Key == "Immortal" && source.Any((string id) => Catalog.ById.TryGetValue(id, out EffectSpec value5) && value5.Defensive))
			{
				return false;
			}
			if ((spec.Key == "Tailwind" && source.Contains("FHEpicShipsGemModer")) || (spec.Key == "GemModer" && source.Contains("FHEpicShipsTailwind")))
			{
				return false;
			}
			return true;
		}
	}
	internal static class EffectValues
	{
		internal static float Get(this Dictionary<string, float> values, string key)
		{
			if (!values.TryGetValue("FHEpicShips" + key, out var value))
			{
				return 0f;
			}
			return value;
		}
	}
	internal static class PassengerBuffs
	{
		private const string Saved = "FalseHarmony.EpicShips.Voyager";

		internal static VoyagerLedger Ledger = new VoyagerLedger();

		private static Player? character;

		private static ShipRuntime? boarded;

		private static float boardedSince;

		private static float next;

		internal static Dictionary<string, float> AboardValues = new Dictionary<string, float>();

		internal static float SwimReduction;

		internal static bool Fishing;

		internal static void Tick(Player player)
		{
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			long ticks = DateTime.UtcNow.Ticks;
			if ((Object)(object)character != (Object)(object)player)
			{
				character = player;
				boarded = null;
				next = 0f;
				try
				{
					Ledger = (player.m_customData.TryGetValue("FalseHarmony.EpicShips.Voyager", out var value) ? VoyagerLedger.Decode(value, ticks) : new VoyagerLedger());
				}
				catch
				{
					Ledger = new VoyagerLedger();
				}
			}
			if (((Character)player).IsDead())
			{
				Ledger = new VoyagerLedger();
				player.m_customData.Remove("FalseHarmony.EpicShips.Voyager");
				AboardValues.Clear();
				SwimReduction = 0f;
				return;
			}
			ShipRuntime shipRuntime = ShipRuntime.Aboard(player);
			if ((Object)(object)shipRuntime != (Object)(object)boarded)
			{
				boarded = shipRuntime;
				boardedSince = Time.time;
			}
			AboardValues = ((Object.op_Implicit((Object)(object)shipRuntime) && shipRuntime.Active) ? shipRuntime.Values : new Dictionary<string, float>());
			if (Time.unscaledTime < next)
			{
				return;
			}
			next = Time.unscaledTime + 1f;
			if (Object.op_Implicit((Object)(object)shipRuntime) && shipRuntime.Active && Time.time - boardedSince >= 30f)
			{
				Ledger.Grant("Health", AboardValues.Get("VoyagerHealth"), ticks);
				Ledger.Grant("Stamina", AboardValues.Get("VoyagerStamina"), ticks);
				Ledger.Grant("Eitr", AboardValues.Get("VoyagerEitr"), ticks);
			}
			player.m_customData["FalseHarmony.EpicShips.Voyager"] = Ledger.Encode();
			SEMan sEMan = ((Character)player).GetSEMan();
			if (Ledger.Remaining(ticks) > 0.0)
			{
				VoyagerStatus voyagerStatus = sEMan.AddStatusEffect(StringExtensionMethods.GetStableHashCode("EpicShips_Voyager"), false, 0, 0f, (short)(-1)) as VoyagerStatus;
				if (Object.op_Implicit((Object)(object)voyagerStatus) && !Object.op_Implicit((Object)(object)((StatusEffect)voyagerStatus).m_icon) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Rested"))))
				{
					((StatusEffect)voyagerStatus).m_icon = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Rested")).m_icon;
				}
			}
			else
			{
				sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("EpicShips_Voyager"), true);
			}
			SwimReduction = 0f;
			foreach (ShipRuntime item in ShipRuntime.All)
			{
				if (Object.op_Implicit((Object)(object)item) && item.Active)
				{
					BoxCollider floatCollider = item.Ship.m_floatCollider;
					Vector3 val;
					if (!Object.op_Implicit((Object)(object)floatCollider))
					{
						val = ((Component)item).transform.position;
					}
					else
					{
						Bounds bounds = ((Collider)floatCollider).bounds;
						val = ((Bounds)(ref bounds)).ClosestPoint(((Component)player).transform.position);
					}
					Vector3 val2 = val;
					if (!(Vector3.Distance(((Component)player).transform.position, val2) > 10f))
					{
						Dictionary<string, float> values = item.Values;
						SwimReduction = Math.Max(SwimReduction, 100f * (1f - Catalog.Reduce(values.Get("Swim"), values.Get("GemPeach"))));
					}
				}
			}
		}

		internal static float Voyager(string stat)
		{
			return Ledger.Value(stat, DateTime.UtcNow.Ticks);
		}
	}
	public sealed class VoyagerStatus : StatusEffect
	{
		public override void UpdateStatusEffect(float dt)
		{
			((StatusEffect)this).UpdateStatusEffect(dt);
			double val = PassengerBuffs.Ledger.Remaining(DateTime.UtcNow.Ticks);
			base.m_ttl = base.m_time + (float)Math.Max(0.001, val);
		}

		public override bool IsDone()
		{
			if (!(PassengerBuffs.Ledger.Remaining(DateTime.UtcNow.Ticks) <= 0.0))
			{
				return ((StatusEffect)this).IsDone();
			}
			return true;
		}

		public override void ModifyHealthRegen(ref float regenMultiplier)
		{
			regenMultiplier += PassengerBuffs.Voyager("Health") / 100f;
		}

		public override void ModifyStaminaRegen(ref float staminaRegen)
		{
			staminaRegen += PassengerBuffs.Voyager("Stamina") / 100f;
		}

		public override void ModifyEitrRegen(ref float eitrRegen)
		{
			eitrRegen += PassengerBuffs.Voyager("Eitr") / 100f;
		}

		public override string GetTooltipString()
		{
			return "Voyager\nHealth regeneration +" + PassengerBuffs.Voyager("Health").ToString("0.##") + "%\nStamina regeneration +" + PassengerBuffs.Voyager("Stamina").ToString("0.##") + "%\nEitr regeneration +" + PassengerBuffs.Voyager("Eitr").ToString("0.##") + "%\nBonuses expire independently, up to 10 minutes after leaving.";
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyEitrRegen")]
	internal static class QueenRegeneration
	{
		private static void Postfix(SEMan __instance, ref float __0)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).GetSEMan() == __instance)
			{
				__0 += PassengerBuffs.AboardValues.Get("GemQueen") / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
	internal static class EikthyrRegeneration
	{
		private static void Postfix(SEMan __instance, ref float __0)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).GetSEMan() == __instance)
			{
				__0 += PassengerBuffs.AboardValues.Get("GemEikthyr") / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifySwimStaminaUsage")]
	internal static class SwimmingEndurance
	{
		private static void Postfix(SEMan __instance, ref float staminaUse)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).GetSEMan() == __instance)
			{
				staminaUse *= 1f - PassengerBuffs.SwimReduction / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class PassengerProtection
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Dictionary<string, float> aboardValues = PassengerBuffs.AboardValues;
				hit.m_damage.m_poison *= Catalog.Reduce(aboardValues.Get("GemDarkGreen"));
				hit.m_damage.m_frost *= Catalog.Reduce(aboardValues.Get("GemDarkBlue"), aboardValues.Get("GemYagluth"));
				hit.m_damage.m_fire *= Catalog.Reduce(aboardValues.Get("GemYagluth"));
				hit.m_damage.m_lightning *= Catalog.Reduce(aboardValues.Get("GemYagluth"));
			}
		}
	}
	[HarmonyPatch(typeof(FishingFloat), "FixedUpdate")]
	internal static class FishingContext
	{
		private static readonly Func<FishingFloat, Character> Owner = AccessTools.MethodDelegate<Func<FishingFloat, Character>>(AccessTools.Method(typeof(FishingFloat), "GetOwner", (Type[])null, (Type[])null), (object)null, true);

		private static void Prefix(FishingFloat __instance, out bool __state)
		{
			__state = PassengerBuffs.Fishing;
			PassengerBuffs.Fishing = (Object)(object)Owner(__instance) == (Object)(object)Player.m_localPlayer;
		}

		private static void Finalizer(bool __state)
		{
			PassengerBuffs.Fishing = __state;
		}
	}
	[HarmonyPatch(typeof(Player), "UseStamina")]
	internal static class FishingStamina
	{
		private static void Prefix(Player __instance, ref float v)
		{
			if (PassengerBuffs.Fishing && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				v *= Catalog.Reduce(PassengerBuffs.AboardValues.Get("GemGrey"));
			}
		}
	}
	[HarmonyPatch(typeof(FishingFloat), "Catch")]
	internal static class FortunateCatch
	{
		private sealed class CatchState
		{
			internal Player Player;

			internal ItemData Item;

			internal int Before;

			internal float Chance;
		}

		private static void Prefix(Fish fish, Character owner, out CatchState? __state)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			__state = null;
			if ((Object)(object)owner != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)fish))
			{
				return;
			}
			ItemDrop component = ((Component)fish).GetComponent<ItemDrop>();
			if (!Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)fish.m_pickupItem))
			{
				component = fish.m_pickupItem.GetComponent<ItemDrop>();
			}
			if (Object.op_Implicit((Object)(object)component))
			{
				ItemData val = component.m_itemData.Clone();
				val.m_dropPrefab = (Object.op_Implicit((Object)(object)component.m_itemData.m_dropPrefab) ? component.m_itemData.m_dropPrefab : ZNetScene.instance.GetPrefab(((Object)((Component)component).gameObject).name.Replace("(Clone)", "")));
				if (Object.op_Implicit((Object)(object)val.m_dropPrefab))
				{
					Player val2 = (Player)owner;
					__state = new CatchState
					{
						Player = val2,
						Item = val,
						Before = ((Humanoid)val2).GetInventory().CountItems(val.m_shared.m_name, -1, true),
						Chance = PassengerBuffs.AboardValues.Get("GemGolden")
					};
				}
			}
		}

		private static void Postfix(CatchState? __state)
		{
			CatchState capture;
			if (__state != null && !(__state.Chance <= 0f))
			{
				capture = __state;
				((MonoBehaviour)EpicShipsPlugin.Instance).StartCoroutine(WaitForPickup());
			}
			IEnumerator WaitForPickup()
			{
				float until = Time.unscaledTime + 3f;
				while (Object.op_Implicit((Object)(object)capture.Player) && (Object)(object)capture.Player == (Object)(object)Player.m_localPlayer && ((Humanoid)capture.Player).GetInventory().CountItems(capture.Item.m_shared.m_name, -1, true) <= capture.Before && Time.unscaledTime < until)
				{
					yield return null;
				}
				if (Object.op_Implicit((Object)(object)capture.Player) && !((Object)(object)capture.Player != (Object)(object)Player.m_localPlayer) && ((Humanoid)capture.Player).GetInventory().CountItems(capture.Item.m_shared.m_name, -1, true) > capture.Before && !(Random.value * 100f >= capture.Chance))
				{
					ItemData item = capture.Item;
					item.m_stack = 1;
					if (!((Humanoid)capture.Player).GetInventory().CanAddItem(item, -1) || !((Humanoid)capture.Player).GetInventory().AddItem(item))
					{
						ItemDrop.DropItem(item, 1, ((Component)capture.Player).transform.position + Vector3.up, Quaternion.identity);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
	internal static class ShipDamage
	{
		private sealed class Reflection
		{
			internal Character? Attacker;

			internal float Damage;
		}

		private static bool Prefix(WearNTear __instance, ref float damage, HitData hitData, ref bool __result, out Reflection? __state)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Invalid comparison between Unknown and I4
			__state = null;
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (!Object.op_Implicit((Object)(object)component) || !component.Active || !component.View.IsOwner() || damage <= 0f)
			{
				return true;
			}
			Dictionary<string, float> values = component.Values;
			if (values.Get("Immortal") > 0f)
			{
				__result = false;
				return false;
			}
			Character val = ((hitData != null) ? hitData.GetAttacker() : null);
			if (Object.op_Implicit((Object)(object)val) && val.IsPlayer() && values.Get("PlayerWard") > 0f)
			{
				__result = false;
				return false;
			}
			ShipMagicState shipMagicState = EpicShipsPlugin.State(component.View.GetZDO());
			long ticks = DateTime.UtcNow.Ticks;
			bool flag = Object.op_Implicit((Object)(object)val) && !val.IsPlayer() && !val.IsTamed();
			if (hitData != null)
			{
				if ((int)hitData.m_hitType == 21)
				{
					damage *= Catalog.Reduce(values.Get("GemFirewalker"));
				}
				else
				{
					float totalDamage = hitData.GetTotalDamage();
					if (totalDamage > 0f)
					{
						float totalPhysicalDamage = hitData.GetTotalPhysicalDamage();
						float fire = hitData.m_damage.m_fire;
						float num = Catalog.Reduce(values.Get("Physical"), flag ? values.Get("GemBonemass") : 0f);
						if (!Object.op_Implicit((Object)(object)val))
						{
							num *= Catalog.Reduce(values.Get("Impact"), values.Get("GemElder"));
						}
						float num2 = Catalog.Reduce(values.Get("Fire"), values.Get("GemOrange"));
						damage *= Math.Max(0.25f, (totalPhysicalDamage * num + fire * num2 + Math.Max(0f, totalDamage - totalPhysicalDamage - fire)) / totalDamage);
					}
				}
			}
			float num3 = component.View.GetZDO().GetFloat(ZDOVars.s_health, __instance.m_health);
			if (num3 <= 0f)
			{
				return true;
			}
			if (values.Get("GemFader") > 0f && num3 - damage < __instance.m_health * 0.25f && shipMagicState.FaderReady <= ticks && shipMagicState.ShieldHealth <= 0f)
			{
				shipMagicState.ShieldHealth = __instance.m_health * values.Get("GemFader") / 100f;
				shipMagicState.FaderReady = DateTime.UtcNow.AddMinutes(15.0).Ticks;
			}
			float num4 = Math.Min(shipMagicState.ShieldHealth, damage);
			shipMagicState.ShieldHealth -= num4;
			damage -= num4;
			shipMagicState.LastDamage = ticks;
			if (flag && damage > 0f)
			{
				shipMagicState.BattleUntil = DateTime.UtcNow.AddSeconds(10.0).Ticks;
			}
			component.View.GetZDO().Set("FalseHarmony.EpicShips.State", shipMagicState.Encode());
			if (flag && values.Get("GemDarkPurple") > 0f)
			{
				__state = new Reflection
				{
					Attacker = val,
					Damage = Math.Min(num3, damage) * values.Get("GemDarkPurple") / 100f
				};
			}
			return true;
		}

		private static void Postfix(bool __result, Reflection? __state)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//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)
			if (__result && __state != null && Object.op_Implicit((Object)(object)__state.Attacker) && !(__state.Damage <= 0f))
			{
				HitData val = new HitData();
				val.m_damage.m_blunt = __state.Damage;
				val.m_point = __state.Attacker.GetCenterPoint();
				__state.Attacker.Damage(val);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class IndestructibleShip
	{
		private static bool Prefix(WearNTear __instance)
		{
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (Object.op_Implicit((Object)(object)component) && component.Active)
			{
				return component.Values.Get("Immortal") <= 0f;
			}
			return true;
		}
	}
	internal sealed class ShipMagicState
	{
		internal const string Key = "FalseHarmony.EpicShips.State";

		public int Version = 1;

		public string Identity = Guid.NewGuid().ToString("N");

		public string Magic = "";

		public long FaderReady;

		public long LastDamage;

		public long BattleUntil;

		public long HornBurstUntil;

		public float ShieldHealth;

		internal bool SameRestoredMagic(ShipMagicState expected)
		{
			Validate();
			expected.Validate();
			if (Identity != expected.Identity || Version != expected.Version)
			{
				return false;
			}
			if (string.IsNullOrEmpty(Magic) || string.IsNullOrEmpty(expected.Magic))
			{
				if (string.IsNullOrEmpty(Magic))
				{
					return string.IsNullOrEmpty(expected.Magic);
				}
				return false;
			}
			return JToken.DeepEquals((JToken)(object)ReadObject(Magic), (JToken)(object)ReadObject(expected.Magic));
		}

		internal Dictionary<string, float> Values()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Invalid comparison between Unknown and I4
			Dictionary<string, float> result = new Dictionary<string, float>(StringComparer.Ordinal);
			if (string.IsNullOrEmpty(Magic))
			{
				return result;
			}
			JObject val = ReadObject(Magic);
			if ((bool?)val["IsUnidentified"] == true)
			{
				return result;
			}
			foreach (JToken item in (IEnumerable<JToken>)(((object)val["Effects"]) ?? ((object)new JArray())))
			{
				Add(item);
			}
			foreach (JToken item2 in (IEnumerable<JToken>)(((object)val["Sockets"]) ?? ((object)new JArray())))
			{
				if ((int)item2.Type != 10 && item2[(object)"Effect"] != null)
				{
					Add(item2[(object)"Effect"]);
				}
			}
			return result;
			void Add(JToken effect)
			{
				string key = ((string)effect[(object)"EffectType"]) ?? "";
				float valueOrDefault = ((float?)effect[(object)"EffectValue"]).GetValueOrDefault();
				result[key] = Math.Max(result.TryGetValue(key, out var value) ? value : 0f, valueOrDefault);
			}
		}

		internal void Validate()
		{
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Invalid comparison between Unknown and I4
			if (Version != 1 || !Guid.TryParseExact(Identity, "N", out var _) || ShieldHealth < 0f || ShieldHealth > 200000000f || float.IsNaN(ShieldHealth) || float.IsInfinity(ShieldHealth))
			{
				throw new InvalidDataException("Invalid enchanted ship state");
			}
			long[] array = new long[4] { FaderReady, LastDamage, BattleUntil, HornBurstUntil };
			int num = 0;
			while (num < array.Length)
			{
				long num2 = array[num];
				if (num2 >= 0)
				{
					DateTime maxValue = DateTime.MaxValue;
					if (num2 <= maxValue.Ticks)
					{
						num++;
						continue;
					}
				}
				throw new InvalidDataException("Invalid enchanted ship cooldown");
			}
			if (string.IsNullOrEmpty(Magic))
			{
				return;
			}
			JObject val = ReadObject(Magic);
			int num3 = ((int?)val["Rarity"]) ?? (-1);
			int valueOrDefault = ((int?)val["Version"]).GetValueOrDefault();
			if (num3 < 0 || num3 > 5 || valueOrDefault < 1 || valueOrDefault > 3 || ((int?)val["SocketCount"]).GetValueOrDefault() < 0 || ((int?)val["SocketCount"]).GetValueOrDefault() > 16)
			{
				throw new InvalidDataException("Unsupported Epic Loot ship item version, rarity or sockets");
			}
			JToken obj = val["Effects"];
			JToken obj2 = ((obj is JArray) ? obj : null) ?? throw new InvalidDataException("Missing ship effects");
			JToken obj3 = val["Sockets"];
			JArray val2 = (JArray)(((object)((obj3 is JArray) ? obj3 : null)) ?? ((object)new JArray()));
			if (((JContainer)obj2).Count > 16 || ((JContainer)val2).Count > 16 || ((JContainer)val2).Count > ((int?)val["SocketCount"]).GetValueOrDefault())
			{
				throw new InvalidDataException("Too many ship effects/sockets");
			}
			HashSet<string> seen = new HashSet<string>();
			foreach (JToken item in (JArray)obj2)
			{
				Check(item, socket: false, num3);
			}
			seen.Clear();
			foreach (JToken item2 in val2)
			{
				if ((int)item2.Type != 10)
				{
					JToken effect = item2[(object)"Effect"] ?? throw new InvalidDataException("Missing socket effect");
					int num4 = ((int?)item2[(object)"SourceRarity"]) ?? (-1);
					if (num4 < 0 || num4 > 5 || (((string)item2[(object)"SourcePrefab"]) ?? "").Length > 120)
					{
						throw new InvalidDataException("Invalid socket source");
					}
					Check(effect, socket: true, num4);
				}
			}
			void Check(JToken val3, bool socket, int r)
			{
				string text = ((string)val3[(object)"EffectType"]) ?? "";
				float num5 = ((float?)val3[(object)"EffectValue"]) ?? float.NaN;
				if (!Catalog.ById.TryGetValue(text, out EffectSpec value) || !seen.Add(text) || float.IsNaN(num5) || float.IsInfinity(num5) || num5 < value.Low - 0.01f || num5 > value.High + 0.01f || r < value.MinimumRarity || (!socket && value.Gem.Length > 0))
				{
					throw new InvalidDataException("Unknown, duplicate or out-of-range ship effect: " + text);
				}
			}
		}

		internal string Encode()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Validate();
			string text = JsonConvert.SerializeObject((object)this);
			string text2 = ((JToken)new JObject
			{
				["Payload"] = JToken.op_Implicit(text),
				["Sha256"] = JToken.op_Implicit(Hash(text))
			}).ToString((Formatting)0, Array.Empty<JsonConverter>());
			if (text2.Length > 65536 || Encoding.UTF8.GetByteCount(text2) > 65536)
			{
				throw new InvalidDataException("Enchanted ship data exceeds 64 KiB");
			}
			return text2;
		}

		internal static ShipMagicState Decode(string encoded)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			if (string.IsNullOrEmpty(encoded))
			{
				return new ShipMagicState();
			}
			JObject val = ReadObject(encoded);
			string obj = ((string)val["Payload"]) ?? "";
			if (Hash(obj) != (string)val["Sha256"])
			{
				throw new InvalidDataException("Enchanted ship checksum failed");
			}
			ShipMagicState obj2 = JsonConvert.DeserializeObject<ShipMagicState>(obj, new JsonSerializerSettings
			{
				MaxDepth = 24,
				TypeNameHandling = (TypeNameHandling)0
			}) ?? throw new InvalidDataException("Missing enchanted ship state");
			obj2.Validate();
			return obj2;
		}

		internal static JObject ReadObject(string value)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			if (value.Length > 65536 || Encoding.UTF8.GetByteCount(value) > 65536)
			{
				throw new InvalidDataException("Oversized enchanted ship JSON");
			}
			JsonTextReader val = new JsonTextReader((TextReader)new StringReader(value))
			{
				MaxDepth = 24,
				DateParseHandling = (DateParseHandling)0
			};
			try
			{
				return JObject.Load((JsonReader)(object)val);
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
		}

		private static string Hash(string value)
		{
			using SHA256 sHA = SHA256.Create();
			return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(value)));
		}
	}
	internal sealed class ShipRuntime : MonoBehaviour
	{
		internal static readonly HashSet<ShipRuntime> All = new HashSet<ShipRuntime>();

		internal Ship Ship;

		internal ZNetView View;

		internal WearNTear Wear;

		private float tick;

		private readonly Dictionary<Demister, float> mistBase = new Dictionary<Demister, float>();

		internal Dictionary<string, float> Values => EpicShipsPlugin.Values(View.GetZDO());

		internal bool Active
		{
			get
			{
				if (Object.op_Implicit((Object)(object)View) && View.IsValid())
				{
					return EpicShipsPlugin.Valid(View.GetZDO());
				}
				return false;
			}
		}

		private void Awake()
		{
			Ship = ((Component)this).GetComponent<Ship>();
			View = ((Component)this).GetComponent<ZNetView>();
			Wear = ((Component)this).GetComponent<WearNTear>();
			All.Add(this);
		}

		private void OnDestroy()
		{
			All.Remove(this);
		}

		private void Update()
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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)
			if (!Active || Time.time < tick)
			{
				return;
			}
			tick = Time.time + 1f;
			try
			{
				Dictionary<string, float> values = Values;
				float num = values.Get("Mist");
				Demister[] componentsInChildren = ((Component)this).GetComponentsInChildren<Demister>(true);
				foreach (Demister val in componentsInChildren)
				{
					if (Object.op_Implicit((Object)(object)val.m_forceField))
					{
						if (!mistBase.TryGetValue(val, out var value))
						{
							value = val.m_forceField.endRange;
							mistBase[val] = value;
						}
						val.m_forceField.endRange = value * (1f + num / 100f);
					}
				}
				if (!View.IsOwner() || !string.IsNullOrEmpty(View.GetZDO().GetString("PortableShips.Transaction", "")) || !string.IsNullOrEmpty(View.GetZDO().GetString("PortableShips.ShrinkLease", "")))
				{
					return;
				}
				ShipMagicState shipMagicState = EpicShipsPlugin.State(View.GetZDO());
				if (DateTime.UtcNow.Ticks - shipMagicState.LastDamage < TimeSpan.FromSeconds(30.0).Ticks || (int)Ship.GetSpeedSetting() != 0)
				{
					return;
				}
				Vector3 linearVelocity = ((Component)this).GetComponent<Rigidbody>().linearVelocity;
				if (!(((Vector3)(ref linearVelocity)).sqrMagnitude > 0.25f))
				{
					float num2 = Wear.m_health * (values.Get("Mend") / 100f + values.Get("GemLightGreen") / 6000f);
					float num3 = View.GetZDO().GetFloat(ZDOVars.s_health, Wear.m_health);
					if (num2 > 0f && num3 > 0f && num3 < Wear.m_health)
					{
						float num4 = Math.Min(Wear.m_health, num3 + num2);
						View.GetZDO().Set(ZDOVars.s_health, num4);
						View.InvokeRPC(ZNetView.Everybody, "RPC_HealthChanged", new object[1] { num4 });
					}
				}
			}
			catch (Exception ex)
			{
				EpicShipsPlugin.Instance.LoggerMessage("Ship effect update: " + ex.Message);
			}
		}

		internal static ShipRuntime? From(Component component)
		{
			return component.GetComponentInParent<ShipRuntime>();
		}

		internal static ShipRuntime? Aboard(Player player)
		{
			if (((Character)player).IsSwimming())
			{
				return null;
			}
			Transform attachPoint = player.GetAttachPoint();
			Ship val = (Object.op_Implicit((Object)(object)attachPoint) ? ((Component)attachPoint).GetComponentInParent<Ship>() : (((Character)player).IsOnGround() ? ((Character)player).GetStandingOnShip() : null));
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return ((Component)val).GetComponent<ShipRuntime>();
		}

		internal float Propulsion()
		{
			Dictionary<string, float> values = Values;
			ShipMagicState shipMagicState = EpicShipsPlugin.State(View.GetZDO());
			long ticks = DateTime.UtcNow.Ticks;
			return 1f + Catalog.SumCap(100f, values.Get("Sail"), EnvMan.IsNight() ? values.Get("GemBlack") : values.Get("GemWhite"), (Object.op_Implicit((Object)(object)EnvMan.instance) && EnvMan.instance.GetCurrentEnvironment().m_name.IndexOf("storm", StringComparison.OrdinalIgnoreCase) >= 0) ? values.Get("GemLightBlue") : 0f, (shipMagicState.BattleUntil > ticks) ? values.Get("GemDarkRed") : 0f, (shipMagicState.HornBurstUntil > ticks) ? values.Get("GemStormcaller") : 0f) / 100f;
		}
	}
	[HarmonyPatch(typeof(Ship), "Start")]
	internal static class RegisterRuntime
	{
		private static void Postfix(Ship __instance)
		{
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				int prefab = component.GetZDO().GetPrefab();
				if (new string[3] { "Karve", "VikingShip", "VikingShip_Ashlands" }.Any((string n) => StringExtensionMethods.GetStableHashCode(n) == prefab) && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<ShipRuntime>()))
				{
					((Component)__instance).gameObject.AddComponent<ShipRuntime>();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Ship), "GetSailForce")]
	internal static class SailPropulsion
	{
		private static void Postfix(Ship __instance, float sailSize, ref Vector3 __result)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (Object.op_Implicit((Object)(object)component) && component.Active)
			{
				Dictionary<string, float> values = component.Values;
				if (values.Get("Tailwind") > 0f)
				{
					__result = ((Component)__instance).transform.forward * __instance.m_sailForceFactor * sailSize;
				}
				else if (values.Get("GemModer") > 0f)
				{
					float num = (1f - __instance.GetWindAngleFactor()) * values.Get("GemModer") / 100f;
					__result += ((Component)__instance).transform.forward * __instance.m_sailForceFactor * sailSize * num;
				}
				__result *= component.Propulsion();
			}
		}
	}
	[HarmonyPatch(typeof(Ship), "GetWindAngleFactor")]
	internal static class TailwindIndicator
	{
		private static void Postfix(Ship __instance, ref float __result)
		{
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (Object.op_Implicit((Object)(object)component) && component.Active && component.Values.Get("Tailwind") > 0f)
			{
				__result = 1f;
			}
		}
	}
	[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
	internal static class RowAndDrift
	{
		private static void Prefix(Ship __instance, out float[]? __state)
		{
			__state = null;
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (Object.op_Implicit((Object)(object)component) && component.Active)
			{
				__state = new float[3] { __instance.m_backwardForce, __instance.m_dampingSideway, __instance.m_stearForce };
				Dictionary<string, float> values = component.Values;
				__instance.m_backwardForce *= 1f + Catalog.SumCap(75f, values.Get("Row"), values.Get("GemYellow")) / 100f;
				__instance.m_dampingSideway /= Catalog.Reduce(values.Get("Drift"), values.Get("GemGreen"));
				__instance.m_stearForce *= 1f + Catalog.SumCap(75f, values.Get("Steer"), values.Get("GemPink")) / 100f;
			}
		}

		private static void Finalizer(Ship __instance, float[]? __state)
		{
			if (__state != null)
			{
				__instance.m_backwardForce = __state[0];
				__instance.m_dampingSideway = __state[1];
				__instance.m_stearForce = __state[2];
			}
		}
	}
	[HarmonyPatch(typeof(Ship), "ApplyControlls")]
	internal static class Steering
	{
		private static void Prefix(Ship __instance, out float __state)
		{
			__state = __instance.m_rudderSpeed;
			ShipRuntime component = ((Component)__instance).GetComponent<ShipRuntime>();
			if (Object.op_Implicit((Object)(object)component) && component.Active)
			{
				Dictionary<string, float> values = component.Values;
				__instance.m_rudderSpeed *= 1f + Catalog.SumCap(75f, values.Get("Steer"), values.Get("GemPink")) / 100f;
			}
		}

		private static void Finalizer(Ship __instance, float __state)
		{
			__instance.m_rudderSpeed = __state;
		}
	}
	internal sealed class VoyagerLedger
	{
		public Dictionary<string, Dictionary<int, long>> Grants = new Dictionary<string, Dictionary<int, long>>();

		internal void Grant(string stat, float percent, long now)
		{
			if (stat != "Health" && stat != "Stamina" && stat != "Eitr")
			{
				throw new ArgumentException("Invalid Voyager stat");
			}
			if (float.IsNaN(percent) || float.IsInfinity(percent) || percent < 0f || percent > 50f)
			{
				throw new ArgumentException("Invalid Voyager strength");
			}
			if (percent != 0f)
			{
				Prune(now);
				if (!Grants.TryGetValue(stat, out Dictionary<int, long> value))
				{
					value = (Grants[stat] = new Dictionary<int, long>());
				}
				value[(int)Math.Round(percent * 100f)] = checked(now + TimeSpan.FromMinutes(10.0).Ticks);
			}
		}

		internal float Value(string stat, long now)
		{
			Prune(now);
			if (!Grants.TryGetValue(stat, out Dictionary<int, long> value) || value.Count <= 0)
			{
				return 0f;
			}
			return (float)value.Keys.Max() / 100f;
		}

		internal double Remaining(long now)
		{
			Prune(now);
			if (Grants.Count != 0)
			{
				return TimeSpan.FromTicks(Grants.Values.SelectMany((Dictionary<int, long> v) => v.Values).Max() - now).TotalSeconds;
			}
			return 0.0;
		}

		internal void Prune(long now)
		{
			string[] array = Grants.Keys.ToArray();
			foreach (string key in array)
			{
				int[] array2 = (from g in Grants[key]
					where g.Value <= now
					select g.Key).ToArray();
				foreach (int key2 in array2)
				{
					Grants[key].Remove(key2);
				}
				if (Grants[key].Count == 0)
				{
					Grants.Remove(key);
				}
			}
		}

		internal string Encode()
		{
			return JsonConvert.SerializeObject((object)this);
		}

		internal static VoyagerLedger Decode(string raw, long now)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if (raw.Length > 65536)
			{
				throw new ArgumentException("Oversized Voyager data");
			}
			VoyagerLedger voyagerLedger = JsonConvert.DeserializeObject<VoyagerLedger>(raw, new JsonSerializerSettings
			{
				MaxDepth = 8,
				TypeNameHandling = (TypeNameHandling)0
			}) ?? new VoyagerLedger();
			if (voyagerLedger.Grants == null || voyagerLedger.Grants.Count > 3)
			{
				throw new ArgumentException("Invalid Voyager data");
			}
			foreach (KeyValuePair<string, Dictionary<int, long>> grant in voyagerLedger.Grants)
			{
				if ((grant.Key != "Health" && grant.Key != "Stamina" && grant.Key != "Eitr") || grant.Value == null || grant.Value.Count > 5001)
				{
					throw new ArgumentException("Invalid Voyager data");
				}
				foreach (KeyValuePair<int, long> item in grant.Value)
				{
					if (item.Key > 0 && item.Key <= 5000 && item.Value >= 0)
					{
						long value = item.Value;
						DateTime maxValue = DateTime.MaxValue;
						if (value <= maxValue.Ticks && item.Value <= now + TimeSpan.FromMinutes(10.0).Ticks)
						{
							continue;
						}
					}
					throw new ArgumentException("Invalid Voyager expiry or value");
				}
			}
			voyagerLedger.Prune(now);
			return voyagerLedger;
		}
	}
}