Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EpicShips v0.1.5
BepInEx/plugins/EpicShips/EpicShips.dll
Decompiled a day agousing 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; } } }