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 BottleShips v1.1.9
BottleShips.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BottleShips; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BottleShips")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("BottleShips")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.1.9")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.9.0")] [module: UnverifiableCode] 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; } } } namespace BottleShips { internal static class BottleAssetManager { private static readonly Dictionary<string, AssetBundle> Bundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Sprite> IconSprites = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase); private static readonly List<GameObject> BottleItemPrefabs = new List<GameObject>(); private const int EmbeddedIconSize = 64; private const int EmbeddedIconBytesPerPixel = 4; internal static void RegisterBottleItem(string assetBundleName, string prefabName) { GameObject prefab = LoadBundle(assetBundleName).LoadAsset<GameObject>(prefabName); if ((Object)(object)prefab == (Object)null) { throw new InvalidOperationException("Bottle prefab '" + prefabName + "' was not found in asset bundle '" + assetBundleName + "'."); } ItemDrop val = default(ItemDrop); if (!prefab.TryGetComponent<ItemDrop>(ref val)) { throw new InvalidOperationException("Bottle prefab '" + prefabName + "' has no ItemDrop component."); } val.m_itemData.m_dropPrefab = prefab; ApplyEmbeddedIcon(val, prefabName); if (!BottleItemPrefabs.Any((GameObject existing) => (Object)(object)existing != (Object)null && ((Object)existing).name == ((Object)prefab).name)) { BottleItemPrefabs.Add(prefab); } } internal static void RegisterWithObjectDB(ObjectDB objectDB) { ItemDrop val = default(ItemDrop); foreach (GameObject bottleItemPrefab in BottleItemPrefabs) { if (!((Object)(object)bottleItemPrefab == (Object)null) && bottleItemPrefab.TryGetComponent<ItemDrop>(ref val)) { RegisterPrefabOrThrow(objectDB.m_items, bottleItemPrefab, "ObjectDB"); RegisterStatusEffect(objectDB, val.m_itemData.m_shared.m_attackStatusEffect); RegisterStatusEffect(objectDB, val.m_itemData.m_shared.m_consumeStatusEffect); RegisterStatusEffect(objectDB, val.m_itemData.m_shared.m_equipStatusEffect); RegisterStatusEffect(objectDB, val.m_itemData.m_shared.m_setStatusEffect); } } } internal static void RegisterWithZNetScene(ZNetScene scene) { foreach (GameObject bottleItemPrefab in BottleItemPrefabs) { if (!((Object)(object)bottleItemPrefab == (Object)null)) { RegisterPrefabOrThrow(scene.m_prefabs, bottleItemPrefab, "ZNetScene"); } } } private static void RegisterPrefabOrThrow(List<GameObject> registeredPrefabs, GameObject prefab, string registryName) { foreach (GameObject registeredPrefab in registeredPrefabs) { if ((Object)(object)registeredPrefab == (Object)(object)prefab) { return; } if ((Object)(object)registeredPrefab != (Object)null && string.Equals(((Object)registeredPrefab).name, ((Object)prefab).name, StringComparison.Ordinal)) { throw new InvalidOperationException("Cannot register BottleShips prefab '" + ((Object)prefab).name + "' with " + registryName + ": a different prefab with the same name is already registered."); } } registeredPrefabs.Add(prefab); } private static AssetBundle LoadBundle(string assetBundleName) { if (Bundles.TryGetValue(assetBundleName, out AssetBundle value) && (Object)(object)value != (Object)null) { return value; } string text = Assembly.GetExecutingAssembly().GetName().Name + ".assets." + assetBundleName; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Embedded asset bundle '" + text + "' was not found."); } AssetBundle val = AssetBundle.LoadFromStream(stream); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("Embedded asset bundle '" + text + "' could not be loaded."); } Bundles[assetBundleName] = val; return val; } private static void ApplyEmbeddedIcon(ItemDrop item, string prefabName) { item.m_itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { LoadEmbeddedIcon(prefabName) }; } private static Sprite LoadEmbeddedIcon(string prefabName) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) if (IconSprites.TryGetValue(prefabName, out Sprite value) && (Object)(object)value != (Object)null) { return value; } string text = Assembly.GetExecutingAssembly().GetName().Name + ".assets.icons." + prefabName + ".rgba"; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Embedded bottle icon '" + text + "' was not found."); } int num = 16384; byte[] array = new byte[num]; int i; int num2; for (i = 0; i < array.Length; i += num2) { num2 = stream.Read(array, i, array.Length - i); if (num2 == 0) { break; } } if (i != num || stream.ReadByte() != -1) { throw new InvalidOperationException($"Embedded bottle icon '{text}' has an invalid size. Expected exactly {num} bytes."); } Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false) { name = prefabName + "_icon", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; val.LoadRawTextureData(array); val.Apply(false, false); Rect val2 = new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height); Sprite val3 = Sprite.Create(val, val2, new Vector2(0.5f, 0.5f)); ((Object)val3).name = prefabName + "_icon"; IconSprites[prefabName] = val3; return val3; } private static void RegisterStatusEffect(ObjectDB objectDB, StatusEffect? statusEffect) { if ((Object)(object)statusEffect != (Object)null && !Object.op_Implicit((Object)(object)objectDB.GetStatusEffect(StringExtensionMethods.GetStableHashCode(((Object)statusEffect).name)))) { objectDB.m_StatusEffects.Add(statusEffect); } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class BottleAssetObjectDBAwakePatch { [HarmonyPriority(0)] private static void Prefix(ObjectDB __instance) { BottleAssetManager.RegisterWithObjectDB(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class BottleAssetObjectDBCopyOtherDBPatch { [HarmonyPriority(0)] [HarmonyBefore(new string[] { "sighsorry.DataForge" })] private static void Postfix(ObjectDB __instance) { BottleAssetManager.RegisterWithObjectDB(__instance); __instance.UpdateRegisters(); BottleShipsManager.ApplyDefaultsOrRetry(); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class BottleAssetZNetSceneAwakePatch { [HarmonyPriority(0)] private static void Prefix(ZNetScene __instance) { BottleAssetManager.RegisterWithZNetScene(__instance); } } internal static class BottleShipsManager { [Flags] private enum ApplyScope { None = 0, BottleWeight = 1, BottleStack = 2, BottleTeleportable = 4, BottleRecipeEnabled = 8, BottleRecipeStation = 0x10, BottleRecipeResources = 0x20, PieceResources = 0x40, PieceStation = 0x80, PieceCanBeRemoved = 0x100, BatteringRamSize = 0x200, BallistaAmmoCapacity = 0x400, BottleItem = 7, BottleRecipe = 0x38, PieceConfig = 0x1C0, LivePiece = 0x5C0, Piece = 0x7C0, All = 0x7FF } private sealed class BottleTarget { internal readonly string DisplayName; internal readonly string PiecePrefab; internal readonly string BottlePrefab; internal readonly string DefaultBuildStation; internal readonly bool DefaultCanBeRemoved; internal readonly float DefaultBottleWeight; internal readonly int DefaultBottleStack; internal readonly bool DefaultBottleTeleportable; internal readonly string DefaultRecipeStation; internal readonly int DefaultRecipeStationLevel; internal readonly string DefaultRecipeResources; internal BottleConfig? Config; internal BottleTarget(string displayName, string piecePrefab, string bottlePrefab, string defaultBuildStation, bool defaultCanBeRemoved, float defaultBottleWeight, int defaultBottleStack, bool defaultBottleTeleportable, string defaultRecipeStation, int defaultRecipeStationLevel, string defaultRecipeResources) { DisplayName = displayName; PiecePrefab = piecePrefab; BottlePrefab = bottlePrefab; DefaultBuildStation = defaultBuildStation; DefaultCanBeRemoved = defaultCanBeRemoved; DefaultBottleWeight = defaultBottleWeight; DefaultBottleStack = defaultBottleStack; DefaultBottleTeleportable = defaultBottleTeleportable; DefaultRecipeStation = defaultRecipeStation; DefaultRecipeStationLevel = defaultRecipeStationLevel; DefaultRecipeResources = defaultRecipeResources; } } private sealed class BottleConfig { internal ConfigEntry<BottleShipsPlugin.Toggle> UseOriginalBuildRecipe; internal ConfigEntry<string> BuildStation; internal ConfigEntry<BottleShipsPlugin.Toggle> CanBeRemoved; internal ConfigEntry<string> BuildResources; internal ConfigEntry<float> BottleWeight; internal ConfigEntry<int> BottleStack; internal ConfigEntry<BottleShipsPlugin.Toggle> BottleTeleportable; internal ConfigEntry<BottleShipsPlugin.Toggle> BottleRecipeEnabled; internal ConfigEntry<string> BottleRecipeStation; internal ConfigEntry<string> BottleRecipeResources; internal ConfigEntry<float>? BatteringRamSize; internal ConfigEntry<int>? BallistaAmmoCapacity; internal static BottleConfig Bind(BottleShipsPlugin plugin, BottleTarget target, int sectionNumber) { //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Expected O, but got Unknown string text = $"{sectionNumber:00} - {target.DisplayName}"; BottleConfig bottleConfig = new BottleConfig { UseOriginalBuildRecipe = plugin.config(text, "Use Original Build Recipe", BottleShipsPlugin.Toggle.Off, "If on, this piece uses the original upstream build resources captured at startup. If off, Build Resources is used.", synchronizedSetting: true, 1000), BuildStation = plugin.config(text, "Build Station", target.DefaultBuildStation, "Crafting station required to build the piece. Use None for no station or Original for the captured upstream station.", synchronizedSetting: true, 980), CanBeRemoved = plugin.config(text, "Can Be Removed", target.DefaultCanBeRemoved ? BottleShipsPlugin.Toggle.On : BottleShipsPlugin.Toggle.Off, "If on, this piece can be removed by the hammer.", synchronizedSetting: true, 970), BuildResources = plugin.config(text, "Build Resources", target.BottlePrefab + ":1", "Resources required to build the piece when Use Original Build Recipe is off. Format: Item:Amount, OtherItem:Amount.", synchronizedSetting: true, 990), BottleWeight = plugin.config(text, "Weight", target.DefaultBottleWeight, "Bottle item weight.", synchronizedSetting: true, 920), BottleStack = plugin.config(text, "Stack", target.DefaultBottleStack, "Bottle item maximum stack size.", synchronizedSetting: true, 910), BottleTeleportable = plugin.config(text, "Teleportable", target.DefaultBottleTeleportable ? BottleShipsPlugin.Toggle.On : BottleShipsPlugin.Toggle.Off, "If on, this bottle item can be teleported.", synchronizedSetting: true, 900), BottleRecipeEnabled = plugin.config(text, "Recipe Enabled", BottleShipsPlugin.Toggle.On, "If on, BottleShips creates or updates the bottle item recipe.", synchronizedSetting: true, 960), BottleRecipeStation = plugin.config(text, "Recipe Station", $"{target.DefaultRecipeStation}, {target.DefaultRecipeStationLevel}", "Crafting station and minimum level for the bottle item recipe. Format: StationPrefab, Level. Use None, 1 for hand crafting.", synchronizedSetting: true, 940), BottleRecipeResources = plugin.config(text, "Recipe Resources", target.DefaultRecipeResources, "Resources required to craft the bottle item. Format: Item:Amount, OtherItem:Amount.", synchronizedSetting: true, 950) }; if (string.Equals(target.PiecePrefab, "BatteringRam", StringComparison.OrdinalIgnoreCase)) { bottleConfig.BatteringRamSize = plugin.config("01 - General", "Battering Ram Size", 0.75f, new ConfigDescription("Overall size multiplier for newly placed battering rams. Existing placed battering rams are not changed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()), synchronizedSetting: true, 970); } if (string.Equals(target.PiecePrefab, "piece_turret", StringComparison.OrdinalIgnoreCase)) { bottleConfig.BallistaAmmoCapacity = plugin.config("01 - General", "Ballista Ammo Capacity", 0, new ConfigDescription("Maximum ammunition stored by vanilla ballistas. 0 or a value at or below the captured original capacity preserves the original capacity. Existing excess ammo is not deleted when this value is lowered.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()), synchronizedSetting: true, 950); } return bottleConfig; } internal void AddChangedHandlers(Action<ApplyScope> handler) { UseOriginalBuildRecipe.SettingChanged += delegate { handler(ApplyScope.PieceResources); }; BuildStation.SettingChanged += delegate { handler(ApplyScope.PieceStation); }; CanBeRemoved.SettingChanged += delegate { handler(ApplyScope.PieceCanBeRemoved); }; BuildResources.SettingChanged += delegate { if (UseOriginalBuildRecipe.Value == BottleShipsPlugin.Toggle.Off) { handler(ApplyScope.PieceResources); } }; BottleWeight.SettingChanged += delegate { handler(ApplyScope.BottleWeight); }; BottleStack.SettingChanged += delegate { handler(ApplyScope.BottleStack); }; BottleTeleportable.SettingChanged += delegate { handler(ApplyScope.BottleTeleportable); }; BottleRecipeEnabled.SettingChanged += delegate { handler(ApplyScope.BottleRecipeEnabled); }; BottleRecipeStation.SettingChanged += delegate { handler(ApplyScope.BottleRecipeStation); }; BottleRecipeResources.SettingChanged += delegate { handler(ApplyScope.BottleRecipeResources); }; if (BatteringRamSize != null) { BatteringRamSize.SettingChanged += delegate { handler(ApplyScope.BatteringRamSize); }; } if (BallistaAmmoCapacity != null) { BallistaAmmoCapacity.SettingChanged += delegate { handler(ApplyScope.BallistaAmmoCapacity); }; } } } private sealed class PieceBaseline { internal Requirement[] Resources = Array.Empty<Requirement>(); internal CraftingStation? CraftingStation; internal static PieceBaseline From(Piece piece) { return new PieceBaseline { Resources = CloneRequirements(piece.m_resources ?? Array.Empty<Requirement>()), CraftingStation = piece.m_craftingStation }; } } private sealed class BatteringRamSizeBaseline { private readonly Dictionary<string, ChildTransformSnapshot> _children = new Dictionary<string, ChildTransformSnapshot>(StringComparer.Ordinal); private readonly List<JointSnapshot> _joints = new List<JointSnapshot>(); private readonly Vector3 _rootScale; private readonly VagonSnapshot? _vagon; private readonly SiegeMachineSnapshot? _siegeMachine; private BatteringRamSizeBaseline(GameObject gameObject) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown _rootScale = gameObject.transform.localScale; foreach (Transform item in gameObject.transform) { Transform transform = item; _children[GetTransformPath(gameObject.transform, transform)] = new ChildTransformSnapshot(transform); } ConfigurableJoint[] componentsInChildren = gameObject.GetComponentsInChildren<ConfigurableJoint>(true); foreach (ConfigurableJoint joint in componentsInChildren) { _joints.Add(new JointSnapshot(gameObject.transform, joint)); } Vagon component = gameObject.GetComponent<Vagon>(); if ((Object)(object)component != (Object)null) { _vagon = new VagonSnapshot(component); } SiegeMachine component2 = gameObject.GetComponent<SiegeMachine>(); if ((Object)(object)component2 != (Object)null) { _siegeMachine = new SiegeMachineSnapshot(component2); } } internal static BatteringRamSizeBaseline From(GameObject gameObject) { return new BatteringRamSizeBaseline(gameObject); } internal void Apply(GameObject gameObject, float size) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown gameObject.transform.localScale = _rootScale; foreach (Transform item in gameObject.transform) { Transform transform = item; string transformPath = GetTransformPath(gameObject.transform, transform); if (_children.TryGetValue(transformPath, out ChildTransformSnapshot value)) { value.Apply(transform, size); } } foreach (JointSnapshot joint in _joints) { joint.Apply(gameObject.transform, size); } _vagon?.Apply(gameObject.GetComponent<Vagon>(), size); _siegeMachine?.Apply(gameObject.GetComponent<SiegeMachine>(), size); } } private sealed class ChildTransformSnapshot { private readonly Vector3 _localPosition; private readonly Vector3 _localScale; internal ChildTransformSnapshot(Transform transform) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) _localPosition = transform.localPosition; _localScale = transform.localScale; } internal void Apply(Transform transform, float size) { //IL_0002: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) transform.localPosition = _localPosition * size; transform.localScale = _localScale * size; } } private sealed class JointSnapshot { private readonly string _path; private readonly Vector3 _anchor; private readonly Vector3 _connectedAnchor; internal JointSnapshot(Transform root, ConfigurableJoint joint) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) _path = GetTransformPath(root, ((Component)joint).transform); _anchor = ((Joint)joint).anchor; _connectedAnchor = ((Joint)joint).connectedAnchor; } internal void Apply(Transform root, float size) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Transform val = root.Find(_path); ConfigurableJoint val2 = default(ConfigurableJoint); if (!((Object)(object)val == (Object)null) && ((Component)val).TryGetComponent<ConfigurableJoint>(ref val2)) { ((Joint)val2).anchor = _anchor * size; ((Joint)val2).connectedAnchor = _connectedAnchor * size; } } } private sealed class VagonSnapshot { private readonly float _detachDistance; private readonly Vector3 _attachOffset; private readonly Vector3 _lineAttachOffset; internal VagonSnapshot(Vagon vagon) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) _detachDistance = vagon.m_detachDistance; _attachOffset = vagon.m_attachOffset; _lineAttachOffset = vagon.m_lineAttachOffset; } internal void Apply(Vagon? vagon, float size) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) if (!((Object)(object)vagon == (Object)null)) { vagon.m_detachDistance = _detachDistance * size; vagon.m_attachOffset = _attachOffset * size; vagon.m_lineAttachOffset = _lineAttachOffset * size; } } } private sealed class SiegeMachineSnapshot { private readonly float _chargeOffsetDistance; internal SiegeMachineSnapshot(SiegeMachine siegeMachine) { _chargeOffsetDistance = siegeMachine.m_chargeOffsetDistance; } internal void Apply(SiegeMachine? siegeMachine, float size) { if (!((Object)(object)siegeMachine == (Object)null)) { siegeMachine.m_chargeOffsetDistance = _chargeOffsetDistance * size; } } } private const string OriginalStationValue = "Original"; private const string BatteringRamPrefab = "BatteringRam"; private const string BallistaPrefab = "piece_turret"; private static readonly BottleTarget[] Targets = new BottleTarget[11] { new BottleTarget("Raft", "Raft", "Raft_bottle", "piece_workbench", defaultCanBeRemoved: false, 45f, 2, defaultBottleTeleportable: true, "piece_workbench", 1, "Wood:20, LeatherScraps:6, Resin:6"), new BottleTarget("Karve", "Karve", "Karve_bottle", "piece_workbench", defaultCanBeRemoved: false, 116f, 1, defaultBottleTeleportable: true, "forge", 1, "FineWood:30, DeerHide:10, Resin:20, BronzeNails:80"), new BottleTarget("Longship", "VikingShip", "VikingShip_bottle", "piece_workbench", defaultCanBeRemoved: false, 220f, 1, defaultBottleTeleportable: true, "forge", 1, "IronNails:100, DeerHide:10, FineWood:40, ElderBark:40"), new BottleTarget("Drakkar", "VikingShip_Ashlands", "VikingShip_Ashlands_bottle", "piece_workbench", defaultCanBeRemoved: false, 260f, 1, defaultBottleTeleportable: true, "piece_artisanstation", 2, "IronNails:100, CeramicPlate:30, FineWood:50, YggdrasilWood:25"), new BottleTarget("Cart", "Cart", "Cart_bottle", "None", defaultCanBeRemoved: false, 45f, 1, defaultBottleTeleportable: true, "forge", 1, "Wood:20, BronzeNails:10"), new BottleTarget("Battering Ram", "BatteringRam", "BatteringRam_bottle", "None", defaultCanBeRemoved: false, 170f, 1, defaultBottleTeleportable: false, "piece_artisanstation", 2, "Wood:10, Blackwood:20, FlametalNew:10, SurtlingCore:2"), new BottleTarget("Catapult", "Catapult", "Catapult_bottle", "None", defaultCanBeRemoved: false, 165f, 1, defaultBottleTeleportable: false, "piece_artisanstation", 2, "Blackwood:20, FlametalNew:10, CharredCogwheel:1"), new BottleTarget("Wood Portal", "portal_wood", "portal_wood_bottle", "None", defaultCanBeRemoved: true, 52f, 2, defaultBottleTeleportable: true, "piece_workbench", 1, "GreydwarfEye:10, FineWood:20, SurtlingCore:2"), new BottleTarget("Stone Portal", "portal_stone", "portal_stone_bottle", "None", defaultCanBeRemoved: true, 119f, 1, defaultBottleTeleportable: false, "piece_artisanstation", 2, "GreydwarfEye:10, Grausten:30, MoltenCore:2, Iron:2"), new BottleTarget("Troll Trap", "piece_trap_troll", "piece_trap_troll_bottle", "None", defaultCanBeRemoved: true, 70f, 6, defaultBottleTeleportable: false, "piece_artisanstation", 1, "BlackMetal:5, BronzeNails:10, MechanicalSpring:1"), new BottleTarget("Ballista", "piece_turret", "piece_turret_bottle", "None", defaultCanBeRemoved: true, 155f, 3, defaultBottleTeleportable: false, "piece_artisanstation", 1, "BlackMetal:10, YggdrasilWood:10, MechanicalSpring:3") }; private static readonly Dictionary<string, PieceBaseline> PieceBaselines = new Dictionary<string, PieceBaseline>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Recipe> OwnedRecipes = new Dictionary<string, Recipe>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> WarnedMessages = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<BottleTarget, ApplyScope> PendingScopedApplies = new Dictionary<BottleTarget, ApplyScope>(); private static BatteringRamSizeBaseline? BatteringRamBaseline; private static int? BallistaOriginalAmmoCapacity; private static bool BallistaAmmoCapacityWasApplied; private static bool ConfigBound; private static ZNetScene? BaselineScene; private static bool FullApplyPending; private static bool ApplyWorkerRunning; private static bool ApplyFailureLogged; private static int ApplyRetryUntilFrame; private static bool HasPendingApply { get { if (!FullApplyPending) { return PendingScopedApplies.Count > 0; } return true; } } internal static IEnumerable<string> BottleItemNames => Targets.Select((BottleTarget target) => target.BottlePrefab); internal static void BindConfig(BottleShipsPlugin plugin) { if (ConfigBound) { return; } for (int i = 0; i < Targets.Length; i++) { BottleTarget target = Targets[i]; target.Config = BottleConfig.Bind(plugin, target, i + 2); target.Config.AddChangedHandlers(delegate(ApplyScope scope) { ApplyChanged(target, scope); }); } ConfigBound = true; } private static IEnumerator ApplyWhenReady() { try { while (HasPendingApply && Time.frameCount <= ApplyRetryUntilFrame) { yield return null; if (FullApplyPending) { if (TryApplyIfReady(Targets, ApplyScope.All)) { FullApplyPending = false; PendingScopedApplies.Clear(); yield break; } continue; } KeyValuePair<BottleTarget, ApplyScope>[] array = PendingScopedApplies.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<BottleTarget, ApplyScope> keyValuePair = array[i]; if (TryApplyIfReady(new BottleTarget[1] { keyValuePair.Key }, keyValuePair.Value)) { PendingScopedApplies.Remove(keyValuePair.Key); } } if (!HasPendingApply) { yield break; } } if (HasPendingApply) { FullApplyPending = false; PendingScopedApplies.Clear(); BottleShipsPlugin.BottleShipsLogger.LogDebug((object)"BottleShips stopped retrying configuration because required game data did not become ready in time."); } } finally { ApplyWorkerRunning = false; if (HasPendingApply) { TryStartApplyWorker(); } } } internal static void ApplyDefaultsOrRetry() { ApplyFailureLogged = false; FullApplyPending = true; PendingScopedApplies.Clear(); ApplyRetryUntilFrame = Time.frameCount + 3600; if (TryApplyIfReady(Targets, ApplyScope.All)) { FullApplyPending = false; } else { TryStartApplyWorker(); } } private static void ApplyChanged(BottleTarget target, ApplyScope scope) { if (FullApplyPending) { return; } if (!HasPendingApply) { ApplyFailureLogged = false; } if (TryApplyIfReady(new BottleTarget[1] { target }, scope)) { if (PendingScopedApplies.TryGetValue(target, out var value)) { value &= ~scope; if (value == ApplyScope.None) { PendingScopedApplies.Remove(target); } else { PendingScopedApplies[target] = value; } } } else { PendingScopedApplies.TryGetValue(target, out var value2); PendingScopedApplies[target] = value2 | scope; ApplyRetryUntilFrame = Time.frameCount + 3600; TryStartApplyWorker(); } } private static bool TryApplyIfReady(IReadOnlyList<BottleTarget> targets, ApplyScope scope) { try { if (!ConfigBound || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } bool flag = (scope & (ApplyScope.BottleItem | ApplyScope.BottleRecipe | ApplyScope.PieceResources)) != 0; foreach (BottleTarget target in targets) { if (flag && (Object)(object)ResolveItemDrop(target.BottlePrefab) == (Object)null) { return false; } } PrepareBaselineScene(); Piece[] livePieces = (((scope & ApplyScope.LivePiece) != ApplyScope.None) ? Piece.s_allPieces.ToArray() : Array.Empty<Piece>()); foreach (BottleTarget target2 in targets) { ApplyBottleItem(target2, scope); ApplyBottleRecipe(target2, scope); ApplyPiece(target2, livePieces, scope); } ApplyFailureLogged = false; return true; } catch (Exception ex) { if (!ApplyFailureLogged) { ApplyFailureLogged = true; BottleShipsPlugin.BottleShipsLogger.LogError((object)"BottleShips could not apply its configuration. A later config or game-database change will retry."); BottleShipsPlugin.BottleShipsLogger.LogError((object)ex); } return false; } } private static void TryStartApplyWorker() { if (ApplyWorkerRunning || (Object)(object)BottleShipsPlugin.Instance == (Object)null) { return; } ApplyWorkerRunning = true; try { ((MonoBehaviour)BottleShipsPlugin.Instance).StartCoroutine(ApplyWhenReady()); } catch (Exception ex) { ApplyWorkerRunning = false; FullApplyPending = false; PendingScopedApplies.Clear(); BottleShipsPlugin.BottleShipsLogger.LogError((object)"BottleShips could not start its configuration apply worker."); BottleShipsPlugin.BottleShipsLogger.LogError((object)ex); } } private static void ApplyBottleItem(BottleTarget target, ApplyScope scope) { ApplyScope applyScope = scope & ApplyScope.BottleItem; if (applyScope == ApplyScope.None || target.Config == null) { return; } ItemDrop val = ResolveItemDrop(target.BottlePrefab); if (val?.m_itemData?.m_shared != null) { SharedData shared = val.m_itemData.m_shared; if ((applyScope & ApplyScope.BottleWeight) != ApplyScope.None) { shared.m_weight = Math.Max(0f, target.Config.BottleWeight.Value); } if ((applyScope & ApplyScope.BottleStack) != ApplyScope.None) { shared.m_maxStackSize = Math.Max(1, target.Config.BottleStack.Value); } if ((applyScope & ApplyScope.BottleTeleportable) != ApplyScope.None) { shared.m_teleportable = target.Config.BottleTeleportable.Value == BottleShipsPlugin.Toggle.On; } } } private static void ApplyBottleRecipe(BottleTarget target, ApplyScope scope) { ApplyScope applyScope = scope & ApplyScope.BottleRecipe; BottleConfig config = target.Config; if (applyScope == ApplyScope.None || config == null) { return; } string text = "Recipe_" + target.BottlePrefab; Recipe value; bool flag = OwnedRecipes.TryGetValue(text, out value) && (Object)(object)value != (Object)null; if (!flag) { OwnedRecipes.Remove(text); } bool flag2 = config.BottleRecipeEnabled.Value == BottleShipsPlugin.Toggle.On; if (!flag && !flag2) { return; } bool flag3 = applyScope == ApplyScope.BottleRecipe; bool num = !flag; bool flag4 = num || flag3; bool flag5 = num || flag3 || (applyScope & ApplyScope.BottleRecipeEnabled) != 0; bool flag6 = num || flag3 || (applyScope & ApplyScope.BottleRecipeStation) != 0; bool flag7 = num || flag3 || (applyScope & ApplyScope.BottleRecipeResources) != 0; ItemDrop val = null; if (flag4) { val = ResolveItemDrop(target.BottlePrefab); if ((Object)(object)val == (Object)null) { WarnOnce("Could not apply recipe '" + text + "': bottle item '" + target.BottlePrefab + "' was not found."); return; } } CraftingStation station = null; string station2 = "None"; int level = 1; bool flag8 = true; if (flag6 && !TryParseCraftingStationWithLevel(config.BottleRecipeStation.Value, out station2, out level)) { flag8 = false; WarnOnce("Could not apply recipe '" + text + "': invalid recipe station '" + config.BottleRecipeStation.Value + "'."); } else if (flag6 && !TryResolveCraftingStation(station2, out station)) { flag8 = false; WarnOnce("Could not apply recipe '" + text + "': crafting station '" + station2 + "' was not found."); } Requirement[] requirements = Array.Empty<Requirement>(); bool flag9 = true; if (flag7 && !TryParseRequirements(config.BottleRecipeResources.Value, out requirements)) { flag9 = false; WarnOnce("Could not apply recipe '" + text + "': invalid resources '" + config.BottleRecipeResources.Value + "'."); } if (flag4 && (!flag8 || !flag9)) { if (flag5 && flag && (Object)(object)value != (Object)null && !flag2) { value.m_enabled = false; } return; } Recipe orCreateRecipe = GetOrCreateRecipe(text); if (!((Object)(object)orCreateRecipe == (Object)null)) { if (flag4) { orCreateRecipe.m_item = val; orCreateRecipe.m_amount = 1; orCreateRecipe.m_requireOnlyOneIngredient = false; orCreateRecipe.m_qualityResultAmountMultiplier = 1f; } if (flag5) { orCreateRecipe.m_enabled = flag2; } if (flag6 && flag8) { orCreateRecipe.m_craftingStation = station; orCreateRecipe.m_minStationLevel = level; } if (flag7 && flag9) { orCreateRecipe.m_resources = requirements; } } } private static void ApplyPiece(BottleTarget target, IReadOnlyList<Piece> livePieces, ApplyScope scope) { ApplyScope applyScope = scope & ApplyScope.Piece; if (applyScope == ApplyScope.None || target.Config == null) { return; } Piece val = ResolvePiece(target.PiecePrefab); if ((Object)(object)val == (Object)null) { WarnOnce("Could not apply piece '" + target.PiecePrefab + "': prefab was not found."); return; } PieceBaseline orCapturePieceBaseline = GetOrCapturePieceBaseline(target, val); ApplyScope applyScope2 = applyScope & ApplyScope.PieceConfig; if (applyScope2 != ApplyScope.None) { ApplyPieceConfig(target, val, orCapturePieceBaseline, applyScope2); } if ((applyScope & ApplyScope.BatteringRamSize) != ApplyScope.None) { ApplyBatteringRamPrefabSize(target, val); } bool flag = (applyScope & ApplyScope.BallistaAmmoCapacity) != 0; if (flag) { ApplyBallistaAmmoCapacity(target, val); } if (applyScope2 == ApplyScope.None && !flag) { return; } foreach (Piece livePiece in livePieces) { if ((Object)(object)livePiece != (Object)null && PrefabNameEquals(((Component)livePiece).gameObject, target.PiecePrefab)) { if (applyScope2 != ApplyScope.None) { ApplyPieceConfig(target, livePiece, orCapturePieceBaseline, applyScope2); } if (flag) { ApplyBallistaAmmoCapacity(target, livePiece); } } } } private static void ApplyPieceConfig(BottleTarget target, Piece piece, PieceBaseline baseline, ApplyScope scope) { if (target.Config == null) { return; } if ((scope & ApplyScope.PieceCanBeRemoved) != ApplyScope.None) { piece.m_canBeRemoved = target.Config.CanBeRemoved.Value == BottleShipsPlugin.Toggle.On; } if ((scope & ApplyScope.PieceStation) != ApplyScope.None && !ApplyBuildStation(piece, target.Config.BuildStation.Value, baseline)) { WarnOnce("Could not apply piece '" + target.PiecePrefab + "': build station '" + target.Config.BuildStation.Value + "' was not found."); } if ((scope & ApplyScope.PieceResources) != ApplyScope.None) { Requirement[] requirements; if (target.Config.UseOriginalBuildRecipe.Value == BottleShipsPlugin.Toggle.On) { piece.m_resources = CloneRequirements(baseline.Resources); } else if (!TryParseRequirements(target.Config.BuildResources.Value, out requirements)) { WarnOnce("Could not apply piece '" + target.PiecePrefab + "': invalid build resources '" + target.Config.BuildResources.Value + "'."); } else { piece.m_resources = requirements; } } } private static void ApplyBatteringRamPrefabSize(BottleTarget target, Piece prefabPiece) { if (string.Equals(target.PiecePrefab, "BatteringRam", StringComparison.OrdinalIgnoreCase) && target.Config?.BatteringRamSize != null) { if (BatteringRamBaseline == null) { BatteringRamBaseline = BatteringRamSizeBaseline.From(((Component)prefabPiece).gameObject); } float size = Mathf.Clamp(target.Config.BatteringRamSize.Value, 0.5f, 1f); BatteringRamBaseline.Apply(((Component)prefabPiece).gameObject, size); } } private static void ApplyBallistaAmmoCapacity(BottleTarget target, Piece piece) { if (!string.Equals(target.PiecePrefab, "piece_turret", StringComparison.OrdinalIgnoreCase) || target.Config?.BallistaAmmoCapacity == null) { return; } Turret component = ((Component)piece).GetComponent<Turret>(); if ((Object)(object)component == (Object)null) { WarnOnce("Could not apply Ballista Ammo Capacity: 'piece_turret' has no Turret component."); return; } if (!BallistaOriginalAmmoCapacity.HasValue) { if (component.m_maxAmmo <= 0) { WarnOnce("Could not apply Ballista Ammo Capacity: the captured original capacity is not positive."); return; } BallistaOriginalAmmoCapacity = component.m_maxAmmo; } int value = BallistaOriginalAmmoCapacity.Value; int num = Mathf.Clamp(target.Config.BallistaAmmoCapacity.Value, 0, 1000); if (num > value) { component.m_maxAmmo = num; BallistaAmmoCapacityWasApplied = true; } else if (BallistaAmmoCapacityWasApplied) { component.m_maxAmmo = value; } } internal static bool CanReceiveConfiguredBallistaAmmo(Turret turret) { if (!IsConfiguredBallista(turret) || !IsBallistaAmmoCapacityExpanded() || (Object)(object)turret.m_nview == (Object)null || !turret.m_nview.IsValid() || !turret.m_nview.IsOwner()) { return true; } return turret.GetAmmo() < turret.m_maxAmmo; } internal static bool TryDropStackedBallistaAmmo(Turret turret) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (!IsConfiguredBallista(turret) || !BallistaOriginalAmmoCapacity.HasValue || (Object)(object)turret.m_nview == (Object)null || !turret.m_nview.IsValid() || !turret.m_nview.IsOwner() || !turret.m_returnAmmoOnDestroy) { return false; } int ammo = turret.GetAmmo(); if (ammo <= BallistaOriginalAmmoCapacity.Value) { return false; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(turret.GetAmmoType()) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 == (Object)null) { return false; } int num = Mathf.Max(1, val2.m_itemData.m_shared.m_maxStackSize); if (num <= 1) { return false; } int num2 = ammo; while (num2 > 0) { Vector3 val3 = ((Component)turret).transform.position + Vector3.up + Random.insideUnitSphere * 0.3f; Quaternion val4 = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); ItemDrop component = Object.Instantiate<GameObject>(val, val3, val4).GetComponent<ItemDrop>(); int num3 = Mathf.Min(num2, num); component.SetStack(num3); ItemDrop.OnCreateNew(component); num2 -= num3; } return true; } private static bool IsBallistaAmmoCapacityExpanded() { int valueOrDefault = (Targets.FirstOrDefault((BottleTarget target) => string.Equals(target.PiecePrefab, "piece_turret", StringComparison.OrdinalIgnoreCase))?.Config?.BallistaAmmoCapacity?.Value).GetValueOrDefault(); if (BallistaOriginalAmmoCapacity.HasValue) { return valueOrDefault > BallistaOriginalAmmoCapacity.Value; } return false; } private static bool IsConfiguredBallista(Turret turret) { if ((Object)(object)turret != (Object)null) { return PrefabNameEquals(((Component)turret).gameObject, "piece_turret"); } return false; } private static void PrepareBaselineScene() { ZNetScene instance = ZNetScene.instance; if (!((Object)(object)BaselineScene == (Object)(object)instance)) { PieceBaselines.Clear(); WarnedMessages.Clear(); BaselineScene = instance; } } private static PieceBaseline GetOrCapturePieceBaseline(BottleTarget target, Piece prefabPiece) { if (PieceBaselines.TryGetValue(target.PiecePrefab, out PieceBaseline value)) { return value; } value = PieceBaseline.From(prefabPiece); PieceBaselines[target.PiecePrefab] = value; return value; } private static bool ApplyBuildStation(Piece piece, string value, PieceBaseline baseline) { string text = NormalizePrefabName(value); if (string.Equals(text, "Original", StringComparison.OrdinalIgnoreCase)) { piece.m_craftingStation = baseline.CraftingStation; return true; } if (IsNone(text)) { piece.m_craftingStation = null; return true; } if (!TryResolveCraftingStation(text, out CraftingStation station)) { return false; } piece.m_craftingStation = station; return true; } private static Recipe? GetOrCreateRecipe(string recipeName) { OwnedRecipes.TryGetValue(recipeName, out Recipe ownedRecipe); if ((Object)(object)ownedRecipe == (Object)null) { OwnedRecipes.Remove(recipeName); } if ((Object)(object)((IEnumerable<Recipe>)ObjectDB.instance.m_recipes).FirstOrDefault((Func<Recipe, bool>)((Recipe recipe) => (Object)(object)recipe != (Object)null && (Object)(object)recipe != (Object)(object)ownedRecipe && string.Equals(((Object)recipe).name, recipeName, StringComparison.OrdinalIgnoreCase))) != (Object)null) { WarnOnce("Could not create recipe '" + recipeName + "': another recipe with the same name is already registered."); return null; } if ((Object)(object)ownedRecipe != (Object)null) { if (!ObjectDB.instance.m_recipes.Contains(ownedRecipe)) { ObjectDB.instance.m_recipes.Add(ownedRecipe); } return ownedRecipe; } Recipe val = ScriptableObject.CreateInstance<Recipe>(); ((Object)val).name = recipeName; ObjectDB.instance.m_recipes.Add(val); OwnedRecipes[recipeName] = val; return val; } private static bool TryParseRequirements(string definition, out Requirement[] requirements) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown List<Requirement> list = new List<Requirement>(); requirements = Array.Empty<Requirement>(); string[] array = definition.Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { string[] array2 = text.Split(new char[1] { ':' }); int num = array2.Length; if ((num < 2 || num > 4) ? true : false) { return false; } string text2 = NormalizePrefabName(array2[0]); if (!int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0) { return false; } int result2 = 1; if (array2.Length >= 3 && array2[2].Trim().Length > 0 && !int.TryParse(array2[2].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)) { return false; } bool result3 = true; if (array2.Length >= 4 && array2[3].Trim().Length > 0 && !bool.TryParse(array2[3].Trim(), out result3)) { return false; } ItemDrop val = ResolveItemDrop(text2); if ((Object)(object)val == (Object)null) { WarnOnce("Could not resolve resource item '" + text2 + "'."); return false; } list.Add(new Requirement { m_resItem = val, m_amount = result, m_amountPerLevel = result2, m_recover = result3 }); } } requirements = list.ToArray(); return true; } private static bool TryResolveCraftingStation(string value, out CraftingStation? station) { station = null; string text = NormalizePrefabName(value); if (IsNone(text)) { return true; } GameObject val = ResolvePrefab(text); CraftingStation val2 = default(CraftingStation); if ((Object)(object)val == (Object)null || !val.TryGetComponent<CraftingStation>(ref val2)) { return false; } station = val2; return true; } private static bool TryParseCraftingStationWithLevel(string value, out string station, out int level) { station = "None"; level = 1; string[] array = (value ?? "").Split(new char[1] { ',' }); if (array.Length == 0 || array.Length > 2) { return false; } station = NormalizePrefabName(array[0]); if (station.Length == 0) { station = "None"; } if (array.Length == 2 && (!int.TryParse(array[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out level) || level < 1)) { return false; } return true; } private static ItemDrop? ResolveItemDrop(string itemName) { string text = NormalizePrefabName(itemName); ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(text) : null); if ((Object)(object)val == (Object)null) { val = ResolvePrefab(text); } ItemDrop result = default(ItemDrop); if (!((Object)(object)val != (Object)null) || !val.TryGetComponent<ItemDrop>(ref result)) { return null; } return result; } private static Piece? ResolvePiece(string prefabName) { GameObject val = ResolvePrefab(prefabName); Piece result = default(Piece); if (!((Object)(object)val != (Object)null) || !val.TryGetComponent<Piece>(ref result)) { return null; } return result; } private static GameObject? ResolvePrefab(string prefabName) { string text = NormalizePrefabName(prefabName); if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { return prefab; } } ObjectDB instance = ObjectDB.instance; if (instance == null) { return null; } return instance.GetItemPrefab(text); } private static Requirement[] CloneRequirements(Requirement[] requirements) { return requirements.Where((Requirement requirement) => requirement != null).Select((Func<Requirement, Requirement>)((Requirement requirement) => new Requirement { m_resItem = requirement.m_resItem, m_amount = requirement.m_amount, m_extraAmountOnlyOneIngredient = requirement.m_extraAmountOnlyOneIngredient, m_amountPerLevel = requirement.m_amountPerLevel, m_recover = requirement.m_recover })).ToArray(); } private static bool PrefabNameEquals(GameObject gameObject, string prefabName) { return string.Equals(Utils.GetPrefabName(gameObject), prefabName, StringComparison.OrdinalIgnoreCase); } private static string NormalizePrefabName(string value) { string text = (value ?? "").Trim(); if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } if (text.Equals("piece_forge", StringComparison.OrdinalIgnoreCase)) { return "forge"; } if (text.Equals("piece_artisantable", StringComparison.OrdinalIgnoreCase)) { return "piece_artisanstation"; } return text; } private static bool IsNone(string value) { if (!string.IsNullOrWhiteSpace(value) && !value.Equals("None", StringComparison.OrdinalIgnoreCase)) { return value.Equals("null", StringComparison.OrdinalIgnoreCase); } return true; } private static void WarnOnce(string message) { if (WarnedMessages.Add(message)) { BottleShipsPlugin.BottleShipsLogger.LogWarning((object)message); } } private static string GetTransformPath(Transform root, Transform transform) { List<string> list = new List<string>(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class BottleShipsObjectDbAwakePatch { [HarmonyPriority(0)] [HarmonyBefore(new string[] { "sighsorry.DataForge" })] private static void Postfix() { BottleShipsManager.ApplyDefaultsOrRetry(); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class BottleShipsZNetSceneAwakePatch { [HarmonyPriority(0)] [HarmonyBefore(new string[] { "sighsorry.DataForge" })] private static void Postfix() { BottleShipsManager.ApplyDefaultsOrRetry(); } } internal static class ShipTweaksManager { private sealed class PowerPaddlingRequest { internal long PlayerId; internal float LastHeartbeat; } private sealed class PowerPaddlingPhysicsContext { internal readonly float Bonus; internal readonly float BackwardForce; internal readonly float SteeringForce; internal bool Applied; internal PowerPaddlingPhysicsContext(float bonus, float backwardForce, float steeringForce) { Bonus = bonus; BackwardForce = backwardForce; SteeringForce = steeringForce; } } internal struct ExploreRadiusState { internal bool Active; internal readonly float ExploreRadius; internal ExploreRadiusState(float exploreRadius) { Active = true; ExploreRadius = exploreRadius; } } internal struct ShipHandlingState { internal bool Active; internal readonly float SailForceFactor; internal readonly float BackwardForce; internal readonly float SteeringForce; internal ShipHandlingState(float sailForceFactor, float backwardForce, float steeringForce) { Active = true; SailForceFactor = sailForceFactor; BackwardForce = backwardForce; SteeringForce = steeringForce; } } private const string ConfigGroup = "20 - Ship Tweaks"; private const string RockTheBoatGuid = "shudnal.RockTheBoat"; private const string PowerPaddlingRpc = "sighsorry.BottleShips.SetPowerPaddling"; private const float PowerPaddlingStaminaPerSecond = 10f; private const float PowerPaddlingFovIncrease = 10f; private const float PowerPaddlingHeartbeatInterval = 0.2f; private const float PowerPaddlingHeartbeatTimeout = 0.75f; private const float PowerPaddlingFovRiseSeconds = 0.4f; private const float PowerPaddlingFovFallSeconds = 0.25f; private static readonly Dictionary<Ship, Dictionary<long, PowerPaddlingRequest>> PowerPaddlingRequests = new Dictionary<Ship, Dictionary<long, PowerPaddlingRequest>>(); private static readonly Dictionary<Ship, PowerPaddlingPhysicsContext> PowerPaddlingPhysicsContexts = new Dictionary<Ship, PowerPaddlingPhysicsContext>(); private static readonly List<long> InvalidPowerPaddlingSenders = new List<long>(); private static ConfigEntry<float> _exploreRadiusMultiplier = null; private static ConfigEntry<float> _shipPowerMultiplier = null; private static ConfigEntry<float> _powerPaddlingBonusPerPlayer = null; private static bool _rockTheBoatChecked; private static bool _rockTheBoatInstalled; private static bool _rockTheBoatWarningLogged; private static Ship? _localPowerPaddlingShip; private static bool _localPowerPaddlingRequested; private static float _nextPowerPaddlingHeartbeat; private static float _powerPaddlingFovOffset; private static bool PowerPaddlingEnabled { get { if (_powerPaddlingBonusPerPlayer != null) { return GetFiniteValue(_powerPaddlingBonusPerPlayer, 0.5f) > 0f; } return false; } } internal static void BindConfig(BottleShipsPlugin plugin) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown _exploreRadiusMultiplier = plugin.config("20 - Ship Tweaks", "Explore Radius Multiplier", 1f, new ConfigDescription("Multiplier for the minimap exploration radius while controlling, standing on, or inside a ship.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()), synchronizedSetting: true, 1000); _shipPowerMultiplier = plugin.config("20 - Ship Tweaks", "Ship Power Multiplier", 1f, new ConfigDescription("Multiplier for wind-driven force at Half or Full and manual propulsion at Slow or Back. It also scales paddle steering at Slow or Back, but not speed-based steering or rudder response. This mainly changes acceleration rather than setting top speed directly. 1 is vanilla; 0 removes these propulsion forces and paddle steering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()), synchronizedSetting: true, 990); _powerPaddlingBonusPerPlayer = plugin.config("20 - Ship Tweaks", "Power Paddling Bonus Per Player", 0.5f, new ConfigDescription("Additional paddling-force ratio contributed by each helmsman or seated passenger who holds the Run input and spends a base 10 stamina per second. 0.5 adds 50% of the ship's globally scaled paddling force per active player at Back or Slow; merely sitting aboard adds nothing. Power Paddling is unavailable at Half or Full while the sails are deployed. 0 disables Power Paddling. Each active player's own camera field of view smoothly increases by up to 10 degrees; other paddlers do not stack additional FOV on that player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()), synchronizedSetting: true, 980); _powerPaddlingBonusPerPlayer.SettingChanged += delegate { HandlePowerPaddlingConfigChanged(); }; } internal static bool TryApplyExploreRadius(Minimap minimap, Player player, out ExploreRadiusState state) { state = default(ExploreRadiusState); float finiteValue = GetFiniteValue(_exploreRadiusMultiplier, 1f); if (Mathf.Approximately(finiteValue, 1f) || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !TryGetAffectedLocalShip(player, out Ship _)) { return false; } state = new ExploreRadiusState(minimap.m_exploreRadius); minimap.m_exploreRadius *= finiteValue; return true; } internal static void RestoreExploreRadius(Minimap minimap, ref ExploreRadiusState state) { if (state.Active) { minimap.m_exploreRadius = state.ExploreRadius; state.Active = false; } } internal static bool TryApplyHandling(Ship ship, out ShipHandlingState state) { state = default(ShipHandlingState); PowerPaddlingPhysicsContexts.Remove(ship); if (!ShouldAffect(ship) || (Object)(object)ship.m_nview == (Object)null || !ship.m_nview.IsValid()) { return false; } if (!ship.m_nview.IsOwner()) { PowerPaddlingRequests.Remove(ship); return false; } float finiteValue = GetFiniteValue(_shipPowerMultiplier, 1f); float num = (float)CountActivePowerPaddlers(ship) * GetFiniteValue(_powerPaddlingBonusPerPlayer, 0.5f); if (Mathf.Approximately(finiteValue, 1f) && num <= 0f) { return false; } state = new ShipHandlingState(ship.m_sailForceFactor, ship.m_backwardForce, ship.m_stearForce); ship.m_sailForceFactor *= finiteValue; ship.m_backwardForce *= finiteValue; ship.m_stearForce *= finiteValue; if (num > 0f) { PowerPaddlingPhysicsContexts[ship] = new PowerPaddlingPhysicsContext(num, ship.m_backwardForce, ship.m_stearForce); } return true; } internal static void RestoreHandling(Ship ship, ref ShipHandlingState state) { PowerPaddlingPhysicsContexts.Remove(ship); if (state.Active) { ship.m_sailForceFactor = state.SailForceFactor; ship.m_backwardForce = state.BackwardForce; ship.m_stearForce = state.SteeringForce; state.Active = false; } } internal static void ApplyPowerPaddlingForce(Ship ship, float fixedDeltaTime) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Invalid comparison between Unknown and I4 //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (PowerPaddlingPhysicsContexts.TryGetValue(ship, out PowerPaddlingPhysicsContext value) && !value.Applied && !(value.Bonus <= 0f) && !(fixedDeltaTime <= 0f) && IsPowerPaddlingGear(ship) && !((Object)(object)ship.m_nview == (Object)null) && ship.m_nview.IsValid() && ship.m_nview.IsOwner() && !((Object)(object)ship.m_body == (Object)null)) { value.Applied = true; Transform transform = ((Component)ship).transform; float num = (((int)ship.m_speed == 1) ? (-1f) : 1f); Vector3 val = transform.forward * (value.BackwardForce * (1f - Mathf.Abs(ship.m_rudderValue))); val += transform.right * (value.SteeringForce * (0f - ship.m_rudderValue)); val *= num * value.Bonus; Vector3 val2 = transform.position + transform.forward * ship.m_stearForceOffset; ship.m_body.AddForceAtPosition(val * (ship.m_body.mass * fixedDeltaTime), val2, (ForceMode)1); } } internal static void RegisterPowerPaddlingRpc(Ship ship) { if (!((Object)(object)ship == (Object)null) && !((Object)(object)ship.m_nview == (Object)null)) { PowerPaddlingRequests.Remove(ship); ship.m_nview.Register<bool>("sighsorry.BottleShips.SetPowerPaddling", (Action<long, bool>)delegate(long sender, bool requested) { HandlePowerPaddlingRequest(ship, sender, requested); }); } } internal static void UpdatePowerPaddlingInput(PlayerController input) { if (!((Object)(object)input == (Object)null) && !((Object)(object)input.m_character != (Object)(object)Player.m_localPlayer)) { Player character = input.m_character; Ship ship = null; bool flag = PowerPaddlingEnabled && TryGetLocalPowerPaddlingShip(character, out ship) && (Object)(object)ship != (Object)null && IsPowerPaddlingGear(ship) && !PlayerController.HasInputDelay && input.TakeInput(false) && input.m_runPressedWhileStamina && (ZInput.GetButton("Run") || ZInput.GetButton("JoyRun")) && ((Character)character).HaveStamina(0f); if (flag) { ((Character)character).UseStamina(10f * Time.fixedDeltaTime * Game.m_moveStaminaRate); } SetLocalPowerPaddlingRequest(ship, flag); } } internal static void StopLocalPowerPaddling(Player player) { if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { SetLocalPowerPaddlingRequest(null, requested: false); } } internal static void RemovePowerPaddlingState(Ship ship) { PowerPaddlingRequests.Remove(ship); PowerPaddlingPhysicsContexts.Remove(ship); if ((Object)(object)_localPowerPaddlingShip == (Object)(object)ship) { SetLocalPowerPaddlingRequest(null, requested: false); _powerPaddlingFovOffset = 0f; } } internal static void ApplyPowerPaddlingFov(GameCamera camera, float deltaTime) { bool num = IsLocalPowerPaddlingActive(); if (!num && _localPowerPaddlingRequested) { SetLocalPowerPaddlingRequest(null, requested: false); } float num2 = (num ? 10f : 0f); float num3 = (num ? 0.4f : 0.25f); float num4 = 10f * Mathf.Max(0f, deltaTime) / num3; _powerPaddlingFovOffset = Mathf.MoveTowards(_powerPaddlingFovOffset, num2, num4); if (!((Object)(object)camera == (Object)null) && !camera.m_freeFly && !(_powerPaddlingFovOffset <= 0f)) { if ((Object)(object)camera.m_camera != (Object)null) { camera.m_camera.fieldOfView = Mathf.Clamp(camera.m_fov + _powerPaddlingFovOffset, 0.5f, 165f); } if ((Object)(object)camera.m_skyCamera != (Object)null) { camera.m_skyCamera.fieldOfView = Mathf.Clamp(camera.m_fov + _powerPaddlingFovOffset, 0.5f, 165f); } } } internal static void Shutdown() { SetLocalPowerPaddlingRequest(null, requested: false); PowerPaddlingRequests.Clear(); PowerPaddlingPhysicsContexts.Clear(); InvalidPowerPaddlingSenders.Clear(); _powerPaddlingFovOffset = 0f; } private static void HandlePowerPaddlingConfigChanged() { if (!PowerPaddlingEnabled) { ClearPowerPaddlingState(); } } private static void ClearPowerPaddlingState() { SetLocalPowerPaddlingRequest(null, requested: false); PowerPaddlingRequests.Clear(); PowerPaddlingPhysicsContexts.Clear(); } private static void HandlePowerPaddlingRequest(Ship ship, long sender, bool requested) { if ((Object)(object)ship == (Object)null || (Object)(object)ship.m_nview == (Object)null || !ship.m_nview.IsValid() || !ship.m_nview.IsOwner()) { return; } if (!requested) { RemovePowerPaddlingRequest(ship, sender); return; } if (!PowerPaddlingEnabled || !ShouldAffect(ship)) { PowerPaddlingRequests.Remove(ship); return; } if (sender == 0L || !IsPowerPaddlingGear(ship) || !ship.HaveControllingPlayer() || !TryFindUniqueAboardPlayer(ship, sender, out Player player) || (Object)(object)player == (Object)null) { RemovePowerPaddlingRequest(ship, sender); return; } if (!PowerPaddlingRequests.TryGetValue(ship, out Dictionary<long, PowerPaddlingRequest> value)) { value = new Dictionary<long, PowerPaddlingRequest>(); PowerPaddlingRequests.Add(ship, value); } if (!value.TryGetValue(sender, out var value2)) { value2 = new PowerPaddlingRequest(); value.Add(sender, value2); } value2.PlayerId = player.GetPlayerID(); value2.LastHeartbeat = Time.unscaledTime; } private static int CountActivePowerPaddlers(Ship ship) { if (!PowerPaddlingEnabled || !IsPowerPaddlingGear(ship) || !ship.HaveControllingPlayer()) { PowerPaddlingRequests.Remove(ship); return 0; } if (!PowerPaddlingRequests.TryGetValue(ship, out Dictionary<long, PowerPaddlingRequest> value)) { return 0; } InvalidPowerPaddlingSenders.Clear(); int num = 0; float unscaledTime = Time.unscaledTime; foreach (KeyValuePair<long, PowerPaddlingRequest> item in value) { if (unscaledTime - item.Value.LastHeartbeat <= 0.75f && TryFindUniqueAboardPlayer(ship, item.Key, out Player player) && (Object)(object)player != (Object)null && player.GetPlayerID() == item.Value.PlayerId) { num++; } else { InvalidPowerPaddlingSenders.Add(item.Key); } } for (int i = 0; i < InvalidPowerPaddlingSenders.Count; i++) { value.Remove(InvalidPowerPaddlingSenders[i]); } InvalidPowerPaddlingSenders.Clear(); if (value.Count == 0) { PowerPaddlingRequests.Remove(ship); } return num; } private static bool IsLocalPowerPaddlingActive() { Player localPlayer = Player.m_localPlayer; if (_localPowerPaddlingRequested && (Object)(object)_localPowerPaddlingShip != (Object)null && (Object)(object)localPlayer != (Object)null && PowerPaddlingEnabled && TryGetLocalPowerPaddlingShip(localPlayer, out Ship ship) && (Object)(object)ship == (Object)(object)_localPowerPaddlingShip) { return IsPowerPaddlingGear(_localPowerPaddlingShip); } return false; } private static bool IsPowerPaddlingGear(Ship ship) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)ship.m_speed != 1) { return (int)ship.m_speed == 2; } return true; } private static void SetLocalPowerPaddlingRequest(Ship? ship, bool requested) { if ((Object)(object)_localPowerPaddlingShip != (Object)null && ((Object)(object)_localPowerPaddlingShip != (Object)(object)ship || !requested) && _localPowerPaddlingRequested) { SendPowerPaddlingRequest(_localPowerPaddlingShip, requested: false); } if (!requested || (Object)(object)ship == (Object)null) { _localPowerPaddlingShip = null; _localPowerPaddlingRequested = false; _nextPowerPaddlingHeartbeat = 0f; return; } bool num = (Object)(object)_localPowerPaddlingShip != (Object)(object)ship || !_localPowerPaddlingRequested || Time.unscaledTime >= _nextPowerPaddlingHeartbeat; _localPowerPaddlingShip = ship; _localPowerPaddlingRequested = true; if (num) { SendPowerPaddlingRequest(ship, requested: true); _nextPowerPaddlingHeartbeat = Time.unscaledTime + 0.2f; } } private static void SendPowerPaddlingRequest(Ship ship, bool requested) { if ((Object)(object)ship != (Object)null && (Object)(object)ship.m_nview != (Object)null && ship.m_nview.IsValid() && ZRoutedRpc.instance != null) { ship.m_nview.InvokeRPC("sighsorry.BottleShips.SetPowerPaddling", new object[1] { requested }); } } private static void RemovePowerPaddlingRequest(Ship ship, long sender) { if (PowerPaddlingRequests.TryGetValue(ship, out Dictionary<long, PowerPaddlingRequest> value)) { value.Remove(sender); if (value.Count == 0) { PowerPaddlingRequests.Remove(ship); } } } private static bool TryFindUniqueAboardPlayer(Ship ship, long owner, out Player? player) { player = null; for (int i = 0; i < ship.m_players.Count; i++) { Player val = ship.m_players[i]; if (!((Object)(object)val == (Object)null) && ((Character)val).GetOwner() == owner) { if ((Object)(object)player != (Object)null && (Object)(object)player != (Object)(object)val && player.GetPlayerID() != val.GetPlayerID()) { player = null; return false; } player = val; } } return (Object)(object)player != (Object)null; } private static bool TryGetLocalPowerPaddlingShip(Player player, out Ship? ship) { ship = player.GetControlledShip(); if ((Object)(object)ship == (Object)null) { if (!((Character)player).IsAttachedToShip()) { return false; } Transform attachPoint = player.GetAttachPoint(); if ((Object)(object)attachPoint != (Object)null) { ship = ((Component)attachPoint).GetComponentInParent<Ship>(); } if ((Object)(object)ship == (Object)null && !TryFindSingleLocalShip(player, out ship)) { return false; } } if ((Object)(object)ship != (Object)null && ship.IsPlayerInBoat(player) && ship.HaveControllingPlayer()) { return ShouldAffect(ship); } return false; } private static bool TryFindSingleLocalShip(Player player, out Ship? ship) { ship = null; for (int i = 0; i < Ship.s_currentShips.Count; i++) { Ship val = Ship.s_currentShips[i]; if (!((Object)(object)val == (Object)null) && val.IsPlayerInBoat(player)) { if ((Object)(object)ship != (Object)null && (Object)(object)ship != (Object)(object)val) { ship = null; return false; } ship = val; } } return (Object)(object)ship != (Object)null; } private static bool TryGetAffectedLocalShip(Player? player, out Ship? ship) { ship = null; if ((Object)(object)player == (Object)null) { return false; } ship = player.GetControlledShip(); if ((Object)(object)ship == (Object)null) { ship = ((Character)player).GetStandingOnShip(); } if ((Object)(object)ship == (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { ship = Ship.GetLocalShip(); } if ((Object)(object)ship != (Object)null) { return ShouldAffect(ship); } return false; } private static bool ShouldAffect(Ship? ship) { if ((Object)(object)ship != (Object)null) { return !CheckForRockTheBoat(); } return false; } private static bool CheckForRockTheBoat() { if (_rockTheBoatChecked) { return _rockTheBoatInstalled; } _rockTheBoatChecked = true; _rockTheBoatInstalled = Chainloader.PluginInfos.ContainsKey("shudnal.RockTheBoat"); if (_rockTheBoatInstalled && !_rockTheBoatWarningLogged) { _rockTheBoatWarningLogged = true; BottleShipsPlugin.BottleShipsLogger.LogWarning((object)"RockTheBoat is installed. BottleShips minimap, handling, and Power Paddling tweaks are disabled to prevent duplicate ship patches."); } return _rockTheBoatInstalled; } private static float GetFiniteValue(ConfigEntry<float> entry, float fallback) { float value = entry.Value; if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value; } return fallback; } } [HarmonyPatch(typeof(GameCamera), "UpdateCamera", new Type[] { typeof(float) })] internal static class BottleShipsGameCameraUpdateCameraPowerPaddlingPatch { [HarmonyPriority(0)] private static void Postfix(GameCamera __instance, float dt) { ShipTweaksManager.ApplyPowerPaddlingFov(__instance, dt); } } [HarmonyPatch(typeof(Minimap), "UpdateExplore", new Type[] { typeof(float), typeof(Player) })] internal static class BottleShipsMinimapUpdateExplorePatch { [HarmonyPriority(0)] private static void Prefix(Minimap __instance, Player player, out ShipTweaksManager.ExploreRadiusState __state) { ShipTweaksManager.TryApplyExploreRadius(__instance, player, out __state); } [HarmonyPriority(800)] private static void Postfix(Minimap __instance, ref ShipTweaksManager.ExploreRadiusState __state) { ShipTweaksManager.RestoreExploreRadius(__instance, ref __state); } [HarmonyPriority(800)] private static Exception? Finalizer(Minimap __instance, Exception? __exception, ref ShipTweaksManager.ExploreRadiusState __state) { ShipTweaksManager.RestoreExploreRadius(__instance, ref __state); return __exception; } } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate", new Type[] { typeof(float) })] internal static class BottleShipsShipCustomFixedUpdatePatch { [HarmonyPriority(0)] private static void Prefix(Ship __instance, out ShipTweaksManager.ShipHandlingState __state) { ShipTweaksManager.TryApplyHandling(__instance, out __state); } [HarmonyPriority(800)] private static void Postfix(Ship __instance, ref ShipTweaksManager.ShipHandlingState __state) { ShipTweaksManager.RestoreHandling(__instance, ref __state); } [HarmonyPriority(800)] private static Exception? Finalizer(Ship __instance, Exception? __exception, ref ShipTweaksManager.ShipHandlingState __state) { ShipTweaksManager.RestoreHandling(__instance, ref __state); return __exception; } } [HarmonyPatch(typeof(Ship), "ApplyEdgeForce", new Type[] { typeof(float) })] internal static class BottleShipsShipApplyEdgeForcePowerPaddlingPatch { [HarmonyPriority(0)] private static void Prefix(Ship __instance, float dt) { ShipTweaksManager.ApplyPowerPaddlingForce(__instance, dt); } } [HarmonyPatch(typeof(Ship), "Start")] internal static class BottleShipsShipStartPowerPaddlingPatch { private static void Postfix(Ship __instance) { ShipTweaksManager.RegisterPowerPaddlingRpc(__instance); } } [HarmonyPatch(typeof(Ship), "OnDisable")] internal static class BottleShipsShipOnDisablePowerPaddlingPatch { private static void Postfix(Ship __instance) { ShipTweaksManager.RemovePowerPaddlingState(__instance); } } [HarmonyPatch(typeof(PlayerController), "FixedUpdate")] internal static class BottleShipsPlayerControllerFixedUpdatePowerPaddlingPatch { [HarmonyPriority(0)] private static void Postfix(PlayerController __instance) { ShipTweaksManager.UpdatePowerPaddlingInput(__instance); } } [HarmonyPatch(typeof(Player), "StopDoodadControl")] internal static class BottleShipsPlayerStopDoodadControlPowerPaddlingPatch { private static void Prefix(Player __instance) { ShipTweaksManager.StopLocalPowerPaddling(__instance); } } [HarmonyPatch(typeof(Player), "AttachStop")] internal static class BottleShipsPlayerAttachStopPowerPaddlingPatch { private static void Prefix(Player __instance) { ShipTweaksManager.StopLocalPowerPaddling(__instance); } } internal static class ShipRepairManager { internal sealed class RepairAttempt { internal readonly Player Player; internal readonly Piece Piece; internal readonly WearNTear WearNTear; internal readonly Inventory Inventory; internal readonly string WoodName; internal readonly int WoodCost; internal readonly float LastRepairBefore; internal readonly RepairAttempt? Previous; internal bool RepairAccepted; internal RepairAttempt(Player player, Piece piece, WearNTear wearNTear, Inventory inventory, string woodName, int woodCost, float lastRepairBefore, RepairAttempt? previous) { Player = player; Piece = piece; WearNTear = wearNTear; Inventory = inventory; WoodName = woodName; WoodCost = woodCost; LastRepairBefore = lastRepairBefore; Previous = previous; } } private readonly struct RepairQuote { internal readonly Piece Piece; internal readonly WearNTear WearNTear; internal readonly ItemDrop? Wood; internal readonly int WoodCost; internal RepairQuote(Piece piece, WearNTear wearNTear, ItemDrop? wood, int woodCost) { Piece = piece; WearNTear = wearNTear; Wood = wood; WoodCost = woodCost; } } private const string ConfigGroup = "20 - Ship Tweaks"; private const string WoodPrefabName = "Wood"; private const string WorkbenchPrefabName = "piece_workbench"; private const string RequirementWidgetName = "BottleShips_ShipRepairCost"; private const int DefaultHealthPerWood = 200; private const float CostBoundaryEpsilon = 1E-06f; private static readonly Vector3 RequirementOffset = new Vector3(0f, -38f, 0f); private static readonly Dictionary<WearNTear, float> ObservedHealth = new Dictionary<WearNTear, float>(); private static ConfigEntry<int> _healthPerWood = null; [ThreadStatic] private static RepairAttempt? _activeRepair; private static ObjectDB? _woodObjectDb; private static ItemDrop? _wood; private static Requirement? _woodRequirement; private static ZNetScene? _workbenchScene; private static CraftingStation? _workbench; private static Hud? _repairHud; private static Hud? _widgetUnavailableHud; private static GameObject? _requirementWidget; private static bool _woodWarningLogged; private static bool _workbenchWarningLogged; private static bool _widgetWarningLogged; private static bool FieldRepairEnabled { get { if (_healthPerWood != null) { return _healthPerWood.Value > 0; } return false; } } internal static void BindConfig(BottleShipsPlugin plugin) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown _healthPerWood = plugin.config("20 - Ship Tweaks", "Ship Repair Durability Per Wood", 200, new ConfigDescription("0 disables field ship repair and preserves vanilla repair behavior. Otherwise, this is the missing ship health repaired per Wood when the repairing player is outside the ship's required build-station range. Range is checked from the player's position. Ships with Build Station set to None use a Workbench as their repair-station fallback. Repairs within range are free, and the ship is still repaired to full health in one action.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()), synchronizedSetting: true, 970); _healthPerWood.SettingChanged += delegate { HandleConfigChanged(); }; } internal static bool BeginRepair(Player player, out RepairAttempt? state) { state = null; if (!TryGetFieldRepairQuote(player, player.GetHoveringPiece(), out var quote)) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); string text = ""; if (quote.WoodCost > 0) { if ((Object)(object)quote.Wood == (Object)null) { ((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null); return false; } text = quote.Wood.m_itemData.m_shared.m_name; if (inventory.CountItems(text, -1, true) < quote.WoodCost) { ((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null); return false; } } _activeRepair = (state = new RepairAttempt(player, quote.Piece, quote.WearNTear, inventory, text, quote.WoodCost, quote.WearNTear.m_lastRepair, _activeRepair)); return true; } internal static bool TryBypassStationCheck(Player player, Piece piece, ref bool result) { RepairAttempt activeRepair = _activeRepair; if (activeRepair == null || activeRepair.Player != player || activeRepair.Piece != piece) { return false; } result = true; return true; } internal static void RecordRepairResult(WearNTear wearNTear, bool repaired) { RepairAttempt activeRepair = _activeRepair; if (activeRepair != null && !activeRepair.RepairAccepted && activeRepair.WearNTear == wearNTear && repaired && wearNTear.m_lastRepair != activeRepair.LastRepairBefore) { activeRepair.RepairAccepted = true; } } internal static void RecordHealthChanged(WearNTear wearNTear, float health) { if (FieldRepairEnabled && (Object)(object)wearNTear != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && !float.IsNaN(health) && !float.IsInfinity(health)) { ObservedHealth[wearNTear] = health; } } internal static void RemoveWearNTear(WearNTear wearNTear) { ObservedHealth.Remove(wearNTear); } internal static void FinishRepair(ref RepairAttempt? state) { RepairAttempt repairAttempt = state; if (repairAttempt == null) { return; } state = null; if (_activeRepair == repairAttempt) { _activeRepair = repairAttempt.Previous; } if (!repairAttempt.RepairAccepted || repairAttempt.WoodCost <= 0) { return; } try { repairAttempt.Inventory.RemoveItem(repairAttempt.WoodName, repairAttempt.WoodCost, -1, true); } catch (Exception arg) { BottleShipsPlugin.BottleShipsLogger.LogError((object)$"Failed to consume field ship-repair Wood: {arg}"); } } internal static void InitializeHud(Hud hud) { if (!((Object)(object)hud == (Object)null) && _repairHud != hud) { if ((Object)(object)_requirementWidget != (Object)null) { Object.Destroy((Object)(object)_requirementWidget); } _repairHud = hud; _widgetUnavailableHud = null; _requirementWidget = null; } } internal static void UpdateRequirementWidget(Hud hud, Player player) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown InitializeHud(hud); HideRequirementWidget(); if ((Object)(object)hud == (Object)null || (Object)(object)player == (Object)null || !FieldRepairEnabled) { return; } Piece selectedPiece = player.GetSelectedPiece(); if (selectedPiece == null || !selectedPiece.m_repairPiece || !TryGetFieldRepairQuote(player, player.GetHoveringPiece(), out var quote) || quote.WoodCost <= 0 || (Object)(object)quote.Wood == (Object)null || !EnsureRequirementWidget(hud)) { return; } if (_woodRequirement == null) { _woodRequirement = new Requirement(); } _woodRequirement.m_resItem = quote.Wood; _woodRequirement.m_amount = quote.WoodCost; _woodRequirement.m_amountPerLevel = 0; _woodRequirement.m_recover = false; bool flag; try { flag = InventoryGui.SetupRequirement(_requirementWidget.transform, _woodRequirement, player, false, 1, 1); } catch (Exception exception) { DisableRequirementWidget(hud, exception); return; } if (flag) { Transform val = _requirementWidget.transform.Find("res_name"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } SynchronizeRequirementWidgetTransform(hud); _requirementWidget.SetActive(true); } } internal static void ClearHud(Hud hud) { if (_repairHud == hud) { _repairHud = null; _widgetUnavailableHud = null; _requirementWidget = null; } } internal static void Shutdown() { _activeRepair = null; if ((Object)(object)_requirementWidget != (Object)null) { Object.Destroy((Object)(object)_requirementWidget); } _repairHud = null; _widgetUnavailableHud = null; _requirementWidget = null; _woodObjectDb = null; _wood = null; _woodRequirement = null; _workbenchScene = null; _workbench = null; ObservedHealth.Clear(); } private static void HandleConfigChanged() { HideRequirementWidget(); if (!FieldRepairEnabled) { ObservedHealth.Clear(); } } private static bool TryGetFieldRepairQuote(Player? player, Piece? piece, out RepairQuote quote) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) quote = default(RepairQuote); WearNTear val = default(WearNTear); if (!FieldRepairEnabled || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)((Component)piece).GetComponentInChildren<Ship>() == (Object)null || !((Component)piece).TryGetComponent<WearNTear>(ref val) || (Object)(object)val.m_nview == (Object)null || !val.m_nview.IsValid() || val.m_nview.GetZDO() == null || (Object)(object)ZoneSystem.instance == (Object)null || player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)22) || HasRepairStation(player, piece)) { return false; } float health = val.m_health; float value; if (val.m_nview.IsOwner()) { value = val.m_nview.GetZDO().GetFloat(ZDOVars.s_health, health); ObservedHealth[val] = value; } else if (!ObservedHealth.TryGetValue(val, out value)) { value = Mathf.Clamp01(val.GetHealthPercentage()) * health; } if (float.IsNaN(health) || float.IsInfinity(health) || float.IsNaN(value) || float.IsInfinity(value)) { return false; } float missingHealth = Mathf.Max(0f, health - value); int num = ((!ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) ? CalculateWoodCost(missingHealth, _healthPerWood.Value) : 0); ItemDrop wood = null; if (num > 0) { TryGetWood(out wood); } quote = new RepairQuote(piece, val, wood, num); return true; } private static int CalculateWoodCost(float missingHealth, int healthPerWood) { if (missingHealth <= 0f || healthPerWood <= 0) { return 0; } float num = missingHealth / (float)healthPerWood; return Mathf.Max(1, Mathf.CeilToInt(num - 1E-06f)); } private static bool HasRepairStation(Player player, Piece piece) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) CraftingStation workbench = piece.m_craftingStation; if ((Object)(object)workbench == (Object)null && !TryGetWorkbench(out workbench)) { return false; } if ((Object)(object)workbench != (Object)null) { return (Object)(object)CraftingStation.HaveBuildStationInRange(workbench.m_name, ((Component)player).transform.position) != (Object)null; } return false; } private static bool TryGetWorkbench(out CraftingStation? workbench) { ZNetScene instance = ZNetScene.instance; if (_workbenchScene != instance) { _workbenchScene = instance; _workbench = null; } if ((Object)(object)_workbench != (Object)null) { workbench = _workbench; return true; } GameObject val = ((instance != null) ? instance.GetPrefab("piece_workbench") : null); CraftingStation val2 = default(CraftingStation); if ((Object)(object)val != (Object)null && val.TryGetComponent<CraftingStation>(ref val2)) { _workbench = val2; workbench = val2; return true; } if (!_workbenchWarningLogged) { _workbenchWarningLogged = true; BottleShipsPlugin.BottleShipsLogger.LogWarning((object)"Could not resolve piece_workbench for the Build Station=None ship-repair fallback. Treating the repair as outside station range."); } workbench = null; return false; } private static bool TryGetWood(out ItemDrop? wood) { ObjectDB instance = ObjectDB.instance; if (_woodObjectDb != instance) { _woodObjectDb = instance; _wood = null; _woodRequirement = null; } if ((Object)(object)_wood != (Object)null) { wood = _wood; return true; } GameObject val = ((instance != null) ? instance.GetItemPrefab("Wood") : null); ItemDrop val2 = default(ItemDrop); if ((Object)(object)val != (Object)null && val.TryGetComponent<ItemDrop>(ref val2)) { _wood = val2; wood = val2; return true; } if (!_woodWarningLogged) { _woodWarningLogged = true; BottleShipsPlugin.BottleShipsLogger.LogWarning((object)"Could not resolve the Wood item for field ship repair. Repair requests that require Wood will be blocked."); } wood = null; return false; } private static bool EnsureRequirementWidget(Hud hud) { InitializeHud(hud); if (_widgetUnavailableHud == hud) { return false; } if ((Object)(object)_requirementWidget != (Object)null) { SynchronizeRequirementWidgetTransform(hud); return true; } Transform transform = ((Component)hud.m_pieceHealthRoot).transform; Transform parent = transform.parent; Transform val = parent.Find("BottleShips_ShipRepairCost"); if ((Object)(object)val == (Object)null) { val = transform.Find("BottleShips_ShipRepairCost"); } if ((Object)(object)val != (Object)null) { _requirementWidget = ((Component)val).gameObject; if (IsRequirementWidgetValid(_requirementWidget)) { ConfigureRequirementWidget(hud, _requirementWidget); return true; } Object.Destroy((Object)(object)_requirementWidget); _requirementWidget = null; DisableRequirementWidget(hud); return false; } if (hud.m_requirementItems == null || hud.m_requirementItems.Length == 0 || (Object)(object)hud.m_requirementItems[0] == (Object)null) { DisableRequirementWidget(hud); return false; } GameObject val2 = Object.Instantiate<GameObject>(hud.m_requirementItems[0], parent, false); ((Object)val2).name = "BottleShips_ShipRepairCost"; if (!IsRequirementWidgetValid(val2)) { Object.Destroy((Object)(object)val2); DisableRequirementWidget(hud); return false; } if (!(val2.transform is RectTransform)) { Object.Destroy((Object)(object)val2); DisableRequirementWidget(hud); return false; } ConfigureRequirementWidget(hud, val2); val2.SetActive(false); _requirementWidget = val2; return true; } private static void ConfigureRequirementWidget(Hud hud, GameObject widget) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)widget.transform; val.sizeDelta = new Vector2(42f, 42f); ((Transform)val).localRotation = Quaternion.identity; ((Transform)val).localScale = Vector3.one; Transform obj = widget.transform.Find("res_icon"); RectTransform val2 = (RectTransform)(object)((obj is RectTransform) ? obj : null); if (val2 != null) { val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.anchoredPosition = Vector2.zero; val2.sizeDelta = new Vector2(38f, 38f); ((Transform)val2).localRotation = Quaternion.identity; ((Transform)val2).localScale = Vector3.one; Image val3 = default(Image); if (((Component)val2).TryGetComponent<Image>(ref val3)) { val3.preserveAspect = true; ((Graphic)val3).raycastTarget = false; } } Transform obj2 = widget.transform.Find("res_amount"); RectTransform val4 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if (val4 != null) { val4.anchorMin = new Vector2(0.5f, 0.5f); val4.anchorMax = new Vector2(0.5f, 0.5f); val4.pivot = new Vector2(0.5f, 0.5f); val4.anchoredPosition = Vector2.zero; val4.sizeDelta = new Vector2(40f, 40f); ((Transform)val4).localRotation = Quaternion.identity; ((Transform)val4).localScale = Vector3.one; ((Transform)val4).SetAsLastSibling(); TMP_Text val5 = default(TMP_Text); if (((Component)val4).TryGetComponent<TMP_Text>(ref val5)) { val5.alignment = (TextAlignmentOptions)514; TMP_Text obj3 = val5; obj3.fontStyle = (FontStyles)(obj3.fontStyle | 1); val5.enableAutoSizing = true; val5.fontSizeMin = 12f; val5.fontSizeMax = 24f; ((Graphic)val5).raycastTarget = false; } } Transform val6 = widget.transform.Find("res_name"); if ((Object)(object)val6 != (Object)null) { ((Component)val6).gameObject.SetActive(false); } SynchronizeRequirementWidgetTransform(hud, widget); } private static void SynchronizeRequirementWidgetTransform(Hud hud, GameObject? widget = null) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (widget == null) { widget = _requirementWidget; } if ((Object)(object)widget == (Object)null) { return; } Transform transform = widget.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val != null) { Transform transform2 = ((Component)hud.m_pieceHealthRoot).transform; Transform parent = transform2.parent; if ((Object)(object)((Transform)val).parent != (Object)(object)parent) { ((Transform)val).SetParent(parent, false); } RectTransform val2 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null); if (val2 != null) { val.anchorMin = val2.anchorMin; val.anchorMax = val2.anchorMax; } val.pivot = new Vector2(0.5f, 0.5f); ((Transform)val).localPosition = transform2.localPosition + RequirementOffset; ((Transform)val).localRotation = Quaternion.identity; ((Transform)val).localScale = Vector3.one; } } private static bool IsRequirementWidgetValid(GameObject widget) { if ((Object)(object)widget.transform.Find("res_icon") != (Object)null && (Object)(object)widget.transform.Find("res_name") != (Object)null && (Object)(object)widget.transform.Find("res_amount") != (Object)null) { return (Object)(object)widget.GetComponent<UITooltip>() != (Object)null; } return false; } private static void DisableRequirementWidget(Hud hud, Exception? exception = null) { _widgetUnavailableHud = hud; if ((Object)(object)_requirementWidget != (Object)null) { _requirementWidget.SetActive(false); Object.Destroy((Object)(object)_requirementWidget); _requirementWidget = null; } if (!_widgetWarningLogged) { _widgetWarningLogged = true; BottleShipsPlugin.BottleShipsLogger.LogWarning((object)((exception == null) ? "Could not create the field ship-repair requirement widget because the vanilla requirement UI is unavailable." : $"Disabled the field ship-repair requirement widget after the vanilla requirement UI failed: {exception}")); } } private static void HideRequirementWidget() { i