Decompiled source of balrond shipyard v1.7.3

plugins/BalrondShipyard.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
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 Balrond.Shared;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BalrondShipyard")]
[assembly: AssemblyDescription("Ship building, ship upgrades and fishnet trap systems for Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Balrond")]
[assembly: AssemblyProduct("BalrondShipyard")]
[assembly: AssemblyCopyright("Copyright © Balrond")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d18d2a38-6578-40b5-a296-30f8a379d6ab")]
[assembly: AssemblyFileVersion("1.7.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.7.0.0")]
[module: UnverifiableCode]
public static class BalrondHashCompat
{
	private static readonly MethodInfo _getStableHashCodeStringBool;

	private static readonly MethodInfo _getStableHashCodeString;

	private static readonly bool _initialized;

	static BalrondHashCompat()
	{
		try
		{
			Type typeFromHandle = typeof(StringExtensionMethods);
			_getStableHashCodeStringBool = typeFromHandle.GetMethod("GetStableHashCode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(string),
				typeof(bool)
			}, null);
			_getStableHashCodeString = typeFromHandle.GetMethod("GetStableHashCode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
			_initialized = true;
		}
		catch
		{
			_initialized = false;
		}
	}

	public static int StableHash(string value)
	{
		if (value == null)
		{
			return 0;
		}
		try
		{
			if (_getStableHashCodeStringBool != null)
			{
				return (int)_getStableHashCodeStringBool.Invoke(null, new object[2] { value, false });
			}
			if (_getStableHashCodeString != null)
			{
				return (int)_getStableHashCodeString.Invoke(null, new object[1] { value });
			}
		}
		catch
		{
		}
		return FallbackStableHash(value);
	}

	private static int FallbackStableHash(string value)
	{
		int num = 5381;
		int num2 = num;
		for (int i = 0; i < value.Length; i += 2)
		{
			num = ((num << 5) + num) ^ value[i];
			if (i == value.Length - 1)
			{
				break;
			}
			num2 = ((num2 << 5) + num2) ^ value[i + 1];
		}
		return num + num2 * 1566083941;
	}
}
namespace Balrond.Shared
{
	public static class AudioRoutingService
	{
		public enum AudioRoute
		{
			SFX,
			Ambient,
			Music,
			Master
		}

		private enum TargetKind
		{
			Route,
			NamedRuntimeGroup
		}

		private sealed class AudioTarget
		{
			public TargetKind Kind;

			public AudioRoute Route;

			public string RuntimeGroupName;

			public string Reason;
		}

		private sealed class AudioBinding
		{
			public AudioSource Source;

			public AudioTarget Target;
		}

		private sealed class ProcessedNode
		{
			public GameObject Root;

			public readonly List<AudioBinding> AudioBindings = new List<AudioBinding>();

			public readonly List<GameObject> ReferencedRoots = new List<GameObject>();

			public readonly HashSet<GameObject> ReferencedRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);
		}

		private sealed class ExplicitOverride
		{
			public bool UsesNamedGroup;

			public AudioRoute Route;

			public string RuntimeGroupName;
		}

		private sealed class RuntimeMixerCatalog
		{
			private readonly Dictionary<string, AudioMixerGroup> _groupsByName = new Dictionary<string, AudioMixerGroup>(StringComparer.OrdinalIgnoreCase);

			private AudioMixer _mixer;

			private AudioMixerGroup _master;

			private AudioMixerGroup _ambient;

			private AudioMixerGroup _music;

			private AudioMixerGroup _sfx;

			public bool IsReady => (Object)(object)_mixer != (Object)null || (Object)(object)_master != (Object)null || (Object)(object)_ambient != (Object)null || (Object)(object)_music != (Object)null;

			public void Rebuild(AudioMan audioMan)
			{
				_groupsByName.Clear();
				_mixer = null;
				_master = null;
				_ambient = null;
				_music = null;
				_sfx = null;
				if ((Object)(object)audioMan == (Object)null)
				{
					return;
				}
				_master = ReadAudioManGroup(audioMan, "m_masterMixer");
				_ambient = ReadAudioManGroup(audioMan, "m_ambientMixer");
				_music = ReadAudioManGroup(audioMan, "m_musicMixer");
				_mixer = GetMixer(_master) ?? GetMixer(_ambient) ?? GetMixer(_music);
				AddGroup(_master);
				AddGroup(_ambient);
				AddGroup(_music);
				if ((Object)(object)_mixer != (Object)null)
				{
					try
					{
						AudioMixerGroup[] array = _mixer.FindMatchingGroups(string.Empty);
						if (array != null)
						{
							for (int i = 0; i < array.Length; i++)
							{
								AddGroup(array[i]);
							}
						}
					}
					catch (Exception ex)
					{
						Warn("Could not enumerate runtime AudioMixer groups: " + ex.Message);
					}
				}
				_sfx = FindNamedGroup("SFX");
				if ((Object)(object)_sfx == (Object)null)
				{
					_sfx = QueryExactGroup("SFX");
				}
				if ((Object)(object)_sfx == (Object)null)
				{
					AudioMixerGroup val = FindNamedGroup("Effects");
					if ((Object)(object)val == (Object)null)
					{
						val = QueryExactGroup("Effects");
					}
					_sfx = val;
				}
				if ((Object)(object)_sfx == (Object)null)
				{
					_sfx = _master;
					WarnOnce("sfx-group-fallback", "Runtime SFX mixer group was not found. Falling back to the AudioMan master output group.");
				}
			}

			public AudioMixerGroup Resolve(AudioTarget target)
			{
				if (target == null)
				{
					return ResolveRoute(AudioRoute.SFX);
				}
				if (target.Kind == TargetKind.NamedRuntimeGroup)
				{
					AudioMixerGroup val = FindNamedGroup(target.RuntimeGroupName);
					if ((Object)(object)val == (Object)null)
					{
						val = QueryExactGroup(target.RuntimeGroupName);
					}
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
					return ResolveRoute(AudioRoute.SFX);
				}
				return ResolveRoute(target.Route);
			}

			public bool HasNamedGroup(string groupName)
			{
				if (string.IsNullOrEmpty(groupName))
				{
					return false;
				}
				return (Object)(object)FindNamedGroup(groupName) != (Object)null || (Object)(object)QueryExactGroup(groupName) != (Object)null;
			}

			private AudioMixerGroup ResolveRoute(AudioRoute route)
			{
				return (AudioMixerGroup)(route switch
				{
					AudioRoute.Ambient => _ambient ?? _sfx ?? _master, 
					AudioRoute.Music => _music ?? _sfx ?? _master, 
					AudioRoute.Master => _master ?? _sfx ?? _ambient ?? _music, 
					_ => _sfx ?? _master ?? _ambient ?? _music, 
				});
			}

			private AudioMixerGroup FindNamedGroup(string groupName)
			{
				if (!string.IsNullOrEmpty(groupName) && _groupsByName.TryGetValue(groupName, out var value))
				{
					return value;
				}
				return null;
			}

			private AudioMixerGroup QueryExactGroup(string groupName)
			{
				if ((Object)(object)_mixer == (Object)null || string.IsNullOrEmpty(groupName))
				{
					return null;
				}
				try
				{
					AudioMixerGroup[] array = _mixer.FindMatchingGroups(groupName);
					if (array == null)
					{
						return null;
					}
					foreach (AudioMixerGroup val in array)
					{
						AddGroup(val);
						if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, groupName, StringComparison.OrdinalIgnoreCase))
						{
							return val;
						}
					}
				}
				catch (Exception ex)
				{
					WarnOnce("query-group-" + groupName, "Could not query runtime AudioMixer group '" + groupName + "': " + ex.Message);
				}
				return null;
			}

			private void AddGroup(AudioMixerGroup group)
			{
				if (!((Object)(object)group == (Object)null) && !string.IsNullOrEmpty(((Object)group).name) && !_groupsByName.ContainsKey(((Object)group).name))
				{
					_groupsByName.Add(((Object)group).name, group);
				}
			}

			private static AudioMixer GetMixer(AudioMixerGroup group)
			{
				try
				{
					return ((Object)(object)group != (Object)null) ? group.audioMixer : null;
				}
				catch
				{
					return null;
				}
			}

			private static AudioMixerGroup ReadAudioManGroup(AudioMan audioMan, string fieldName)
			{
				try
				{
					FieldInfo field = typeof(AudioMan).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null)
					{
						return null;
					}
					object value = field.GetValue(audioMan);
					AudioMixerGroup val = (AudioMixerGroup)((value is AudioMixerGroup) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
					AudioSource val2 = (AudioSource)((value is AudioSource) ? value : null);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2.outputAudioMixerGroup;
					}
				}
				catch (Exception ex)
				{
					WarnOnce("audioman-field-" + fieldName, "Could not read AudioMan." + fieldName + ": " + ex.Message);
				}
				return null;
			}
		}

		private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class
		{
			public static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>();

			public bool Equals(T x, T y)
			{
				return x == y;
			}

			public int GetHashCode(T obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		private const int MaxManagedDepth = 24;

		private const int MaxCollectionEntries = 4096;

		private static readonly RuntimeMixerCatalog MixerCatalog = new RuntimeMixerCatalog();

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

		private static readonly Dictionary<GameObject, ProcessedNode> ProcessedNodes = new Dictionary<GameObject, ProcessedNode>(ReferenceComparer<GameObject>.Instance);

		private static readonly Dictionary<AudioSource, AudioBinding> SourceBindings = new Dictionary<AudioSource, AudioBinding>(ReferenceComparer<AudioSource>.Instance);

		private static readonly Dictionary<Type, FieldInfo[]> TraversalFields = new Dictionary<Type, FieldInfo[]>();

		private static readonly HashSet<GameObject> RegisteredRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);

		private static readonly List<GameObject> RegisteredRoots = new List<GameObject>();

		private static readonly HashSet<GameObject> ActiveProcessing = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);

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

		private static Action<string> _infoLogger;

		private static Action<string> _warningLogger;

		private static Action<string> _errorLogger;

		public static bool DebugLogging { get; set; }

		public static void ConfigureLogging(Action<string> infoLogger, Action<string> warningLogger, Action<string> errorLogger)
		{
			_infoLogger = infoLogger;
			_warningLogger = warningLogger;
			_errorLogger = errorLogger;
		}

		public static void SetOverride(string gameObjectName, AudioRoute route)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName))
			{
				Overrides[gameObjectName] = new ExplicitOverride
				{
					UsesNamedGroup = false,
					Route = route,
					RuntimeGroupName = null
				};
				InvalidateClassificationCaches();
			}
		}

		public static void SetOverrides(AudioRoute route, params string[] gameObjectNames)
		{
			if (gameObjectNames == null)
			{
				return;
			}
			bool flag = false;
			foreach (string text in gameObjectNames)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					Overrides[text] = new ExplicitOverride
					{
						UsesNamedGroup = false,
						Route = route,
						RuntimeGroupName = null
					};
					flag = true;
				}
			}
			if (flag)
			{
				InvalidateClassificationCaches();
			}
		}

		public static void SetMixerGroupOverride(string gameObjectName, string runtimeMixerGroupName)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName) && !string.IsNullOrWhiteSpace(runtimeMixerGroupName))
			{
				Overrides[gameObjectName] = new ExplicitOverride
				{
					UsesNamedGroup = true,
					Route = AudioRoute.SFX,
					RuntimeGroupName = runtimeMixerGroupName
				};
				InvalidateClassificationCaches();
			}
		}

		public static void RemoveOverride(string gameObjectName)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName) && Overrides.Remove(gameObjectName))
			{
				InvalidateClassificationCaches();
			}
		}

		public static void Process(GameObject root)
		{
			if (IsUsable((Object)(object)root))
			{
				RegisterRoot(root);
				if (MixerCatalog.IsReady)
				{
					ProcessRoot(root);
				}
			}
		}

		public static void Process(IEnumerable<GameObject> roots)
		{
			if (roots == null)
			{
				return;
			}
			foreach (GameObject root in roots)
			{
				if (IsUsable((Object)(object)root))
				{
					RegisterRoot(root);
				}
			}
			if (!MixerCatalog.IsReady)
			{
				return;
			}
			for (int i = 0; i < RegisteredRoots.Count; i++)
			{
				GameObject val = RegisteredRoots[i];
				if (IsUsable((Object)(object)val))
				{
					ProcessRoot(val);
				}
			}
		}

		public static void OnAudioManAwake(AudioMan audioMan)
		{
			if ((Object)(object)audioMan == (Object)null)
			{
				return;
			}
			MixerCatalog.Rebuild(audioMan);
			if (!MixerCatalog.IsReady)
			{
				Error("AudioMan was available, but no usable runtime mixer group could be resolved.");
				return;
			}
			HashSet<GameObject> visited = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);
			for (int i = 0; i < RegisteredRoots.Count; i++)
			{
				GameObject val = RegisteredRoots[i];
				if (IsUsable((Object)(object)val))
				{
					if (ProcessedNodes.TryGetValue(val, out var value))
					{
						ValidateNodeGraph(value, visited);
					}
					else
					{
						ProcessRoot(val);
					}
				}
			}
		}

		private static void RegisterRoot(GameObject root)
		{
			if (RegisteredRootSet.Add(root))
			{
				RegisteredRoots.Add(root);
			}
		}

		private static void ProcessRoot(GameObject root)
		{
			if (!MixerCatalog.IsReady || !IsUsable((Object)(object)root))
			{
				return;
			}
			if (ProcessedNodes.TryGetValue(root, out var value))
			{
				ValidateNodeGraph(value, new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance));
			}
			else
			{
				if (!ActiveProcessing.Add(root))
				{
					return;
				}
				ProcessedNode processedNode = new ProcessedNode
				{
					Root = root
				};
				ProcessedNodes[root] = processedNode;
				try
				{
					ScanAudioSources(root, processedNode);
					ScanBehaviourReferences(root, processedNode);
				}
				catch (Exception ex)
				{
					Error("Failed to process audio graph for '" + SafeName((Object)(object)root) + "': " + ex);
				}
				finally
				{
					ActiveProcessing.Remove(root);
				}
			}
		}

		private static void ScanAudioSources(GameObject root, ProcessedNode node)
		{
			AudioSource[] componentsInChildren;
			try
			{
				componentsInChildren = root.GetComponentsInChildren<AudioSource>(true);
			}
			catch (Exception ex)
			{
				Warn("Could not enumerate AudioSources under '" + SafeName((Object)(object)root) + "': " + ex.Message);
				return;
			}
			if (componentsInChildren == null)
			{
				return;
			}
			foreach (AudioSource val in componentsInChildren)
			{
				if (IsUsable((Object)(object)val))
				{
					if (!SourceBindings.TryGetValue(val, out var value))
					{
						value = new AudioBinding
						{
							Source = val,
							Target = ClassifySource(root, val)
						};
						SourceBindings[val] = value;
					}
					if (!node.AudioBindings.Contains(value))
					{
						node.AudioBindings.Add(value);
					}
					ApplyBinding(value, root);
				}
			}
		}

		private static void ScanBehaviourReferences(GameObject root, ProcessedNode node)
		{
			MonoBehaviour[] componentsInChildren;
			try
			{
				componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true);
			}
			catch (Exception ex)
			{
				Warn("Could not enumerate MonoBehaviours under '" + SafeName((Object)(object)root) + "': " + ex.Message);
				return;
			}
			if (componentsInChildren == null)
			{
				return;
			}
			HashSet<object> managedVisited = new HashSet<object>(ReferenceComparer<object>.Instance);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (IsUsable((Object)(object)val) && !(val is ZSFX))
				{
					TraverseManagedObject(val, root, node, managedVisited, 0, isComponentRoot: true);
				}
			}
		}

		private static void TraverseManagedObject(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth, bool isComponentRoot)
		{
			if (value == null || depth > 24)
			{
				return;
			}
			Type type = value.GetType();
			if (!type.IsValueType && !managedVisited.Add(value))
			{
				return;
			}
			FieldInfo[] traversalFields = GetTraversalFields(type, isComponentRoot);
			foreach (FieldInfo fieldInfo in traversalFields)
			{
				object value2;
				try
				{
					value2 = fieldInfo.GetValue(value);
				}
				catch
				{
					continue;
				}
				TraverseValue(value2, ownerRoot, node, managedVisited, depth + 1);
			}
		}

		private static void TraverseValue(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth)
		{
			if (value == null || depth > 24)
			{
				return;
			}
			EffectList val = (EffectList)((value is EffectList) ? value : null);
			if (val != null)
			{
				TraverseEffectList(val, ownerRoot, node);
				return;
			}
			GameObject val2 = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val2 != (Object)null)
			{
				HandleReferencedGameObject(val2, ownerRoot, node);
				return;
			}
			Component val3 = (Component)((value is Component) ? value : null);
			if ((Object)(object)val3 != (Object)null)
			{
				if (IsUsable((Object)(object)val3))
				{
					HandleReferencedGameObject(val3.gameObject, ownerRoot, node);
				}
				return;
			}
			AudioClip val4 = (AudioClip)((value is AudioClip) ? value : null);
			if ((Object)(object)val4 != (Object)null)
			{
				return;
			}
			Object val5 = (Object)((value is Object) ? value : null);
			if (val5 != (Object)null)
			{
				ScriptableObject val6 = (ScriptableObject)((value is ScriptableObject) ? value : null);
				if ((Object)(object)val6 != (Object)null)
				{
					TraverseManagedObject(val6, ownerRoot, node, managedVisited, depth, isComponentRoot: false);
				}
				return;
			}
			if (value is IDictionary dictionary)
			{
				int num = 0;
				{
					foreach (DictionaryEntry item in dictionary)
					{
						TraverseValue(item.Value, ownerRoot, node, managedVisited, depth + 1);
						num++;
						if (num >= 4096)
						{
							break;
						}
					}
					return;
				}
			}
			if (value is IEnumerable enumerable && !(value is string))
			{
				int num2 = 0;
				{
					foreach (object item2 in enumerable)
					{
						TraverseValue(item2, ownerRoot, node, managedVisited, depth + 1);
						num2++;
						if (num2 >= 4096)
						{
							break;
						}
					}
					return;
				}
			}
			Type type = value.GetType();
			if (ShouldTraverseManagedType(type))
			{
				TraverseManagedObject(value, ownerRoot, node, managedVisited, depth, isComponentRoot: false);
			}
		}

		private static void TraverseEffectList(EffectList effectList, GameObject ownerRoot, ProcessedNode node)
		{
			EffectData[] effectPrefabs = effectList.m_effectPrefabs;
			if (effectPrefabs == null)
			{
				return;
			}
			for (int i = 0; i < effectPrefabs.Length; i++)
			{
				GameObject prefab = effectPrefabs[i].m_prefab;
				if (IsUsable((Object)(object)prefab))
				{
					HandleReferencedGameObject(prefab, ownerRoot, node);
				}
			}
		}

		private static void HandleReferencedGameObject(GameObject referenced, GameObject ownerRoot, ProcessedNode ownerNode)
		{
			if (IsUsable((Object)(object)referenced) && IsUsable((Object)(object)ownerRoot) && !IsInsideHierarchy(referenced, ownerRoot))
			{
				if (ownerNode.ReferencedRootSet.Add(referenced))
				{
					ownerNode.ReferencedRoots.Add(referenced);
				}
				ProcessRoot(referenced);
			}
		}

		private static bool IsInsideHierarchy(GameObject candidate, GameObject root)
		{
			if (candidate == root)
			{
				return true;
			}
			try
			{
				Transform transform = candidate.transform;
				Transform transform2 = root.transform;
				return (Object)(object)transform != (Object)null && (Object)(object)transform2 != (Object)null && transform.IsChildOf(transform2);
			}
			catch
			{
				return false;
			}
		}

		private static AudioTarget ClassifySource(GameObject root, AudioSource source)
		{
			AudioTarget audioTarget = FindExplicitOverride(((Component)source).gameObject, root);
			if (audioTarget != null)
			{
				return audioTarget;
			}
			if (HierarchyHasSfxPrefix(((Component)source).gameObject, root))
			{
				return RouteTarget(AudioRoute.SFX, "sfx_/vfx_/fx_ GameObject name");
			}
			if (HierarchyHasZSFX(((Component)source).gameObject, root))
			{
				return RouteTarget(AudioRoute.SFX, "ZSFX context");
			}
			AudioMixerGroup val = null;
			try
			{
				val = source.outputAudioMixerGroup;
			}
			catch
			{
				val = null;
			}
			AudioTarget audioTarget2 = ClassifyOldMixerGroup(val);
			if (audioTarget2 != null)
			{
				return audioTarget2;
			}
			return RouteTarget(AudioRoute.SFX, "default SFX fallback");
		}

		private static AudioTarget FindExplicitOverride(GameObject sourceObject, GameObject root)
		{
			Transform val = null;
			Transform val2 = null;
			try
			{
				val = (((Object)(object)sourceObject != (Object)null) ? sourceObject.transform : null);
				val2 = (((Object)(object)root != (Object)null) ? root.transform : null);
			}
			catch
			{
				return null;
			}
			while ((Object)(object)val != (Object)null)
			{
				string text = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : null);
				if (!string.IsNullOrEmpty(text) && Overrides.TryGetValue(text, out var value))
				{
					if (value.UsesNamedGroup)
					{
						return NamedTarget(value.RuntimeGroupName, "explicit mixer-group override on '" + text + "'");
					}
					return RouteTarget(value.Route, "explicit route override on '" + text + "'");
				}
				if (val == val2)
				{
					break;
				}
				val = val.parent;
			}
			return null;
		}

		private static bool HierarchyHasSfxPrefix(GameObject sourceObject, GameObject root)
		{
			Transform val = null;
			Transform val2 = null;
			try
			{
				val = (((Object)(object)sourceObject != (Object)null) ? sourceObject.transform : null);
				val2 = (((Object)(object)root != (Object)null) ? root.transform : null);
			}
			catch
			{
				return false;
			}
			while ((Object)(object)val != (Object)null)
			{
				string name = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : null);
				if (HasSfxPrefix(name))
				{
					return true;
				}
				if (val == val2)
				{
					break;
				}
				val = val.parent;
			}
			return false;
		}

		private static bool HierarchyHasZSFX(GameObject sourceObject, GameObject root)
		{
			Transform val = null;
			Transform val2 = null;
			try
			{
				val = (((Object)(object)sourceObject != (Object)null) ? sourceObject.transform : null);
				val2 = (((Object)(object)root != (Object)null) ? root.transform : null);
			}
			catch
			{
				return false;
			}
			while ((Object)(object)val != (Object)null)
			{
				try
				{
					if ((Object)(object)((Component)val).GetComponent<ZSFX>() != (Object)null)
					{
						return true;
					}
				}
				catch
				{
				}
				if (val == val2)
				{
					break;
				}
				val = val.parent;
			}
			return false;
		}

		private static AudioTarget ClassifyOldMixerGroup(AudioMixerGroup oldGroup)
		{
			if ((Object)(object)oldGroup == (Object)null || string.IsNullOrEmpty(((Object)oldGroup).name))
			{
				return null;
			}
			string name = ((Object)oldGroup).name;
			if (IsGenericMasterName(name))
			{
				return null;
			}
			if (ContainsInvariant(name, "ambient"))
			{
				return RouteTarget(AudioRoute.Ambient, "old mixer-group name: " + name);
			}
			if (ContainsInvariant(name, "music"))
			{
				return RouteTarget(AudioRoute.Music, "old mixer-group name: " + name);
			}
			if (ContainsInvariant(name, "sfx") || ContainsInvariant(name, "vfx") || ContainsInvariant(name, "effect"))
			{
				return RouteTarget(AudioRoute.SFX, "old mixer-group name: " + name);
			}
			if (MixerCatalog.HasNamedGroup(name))
			{
				return NamedTarget(name, "matching runtime mixer-group name");
			}
			return null;
		}

		private static bool HasSfxPrefix(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			return name.StartsWith("sfx_", StringComparison.OrdinalIgnoreCase) || name.StartsWith("vfx_", StringComparison.OrdinalIgnoreCase) || name.StartsWith("fx_", StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsGenericMasterName(string name)
		{
			return string.Equals(name, "Master", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "MasterAudio", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "Master Mixer", StringComparison.OrdinalIgnoreCase);
		}

		private static bool ContainsInvariant(string text, string fragment)
		{
			return text != null && fragment != null && text.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static AudioTarget RouteTarget(AudioRoute route, string reason)
		{
			return new AudioTarget
			{
				Kind = TargetKind.Route,
				Route = route,
				RuntimeGroupName = null,
				Reason = reason
			};
		}

		private static AudioTarget NamedTarget(string groupName, string reason)
		{
			return new AudioTarget
			{
				Kind = TargetKind.NamedRuntimeGroup,
				Route = AudioRoute.SFX,
				RuntimeGroupName = groupName,
				Reason = reason
			};
		}

		private static void ApplyBinding(AudioBinding binding, GameObject semanticRoot)
		{
			if (binding == null || !IsUsable((Object)(object)binding.Source))
			{
				return;
			}
			AudioMixerGroup val = MixerCatalog.Resolve(binding.Target);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			AudioMixerGroup val2 = null;
			try
			{
				val2 = binding.Source.outputAudioMixerGroup;
			}
			catch
			{
				val2 = null;
			}
			if (val2 != val)
			{
				try
				{
					binding.Source.outputAudioMixerGroup = val;
				}
				catch (Exception ex)
				{
					Warn("Could not route AudioSource '" + SafeName((Object)(object)((Component)binding.Source).gameObject) + "' under '" + SafeName((Object)(object)semanticRoot) + "': " + ex.Message);
					return;
				}
			}
			if (DebugLogging)
			{
				Info("AudioSource '" + SafeName((Object)(object)((Component)binding.Source).gameObject) + "' under '" + SafeName((Object)(object)semanticRoot) + "' -> '" + ((Object)val).name + "' (" + ((binding.Target != null) ? binding.Target.Reason : "unknown") + ").");
			}
		}

		private static void ValidateNodeGraph(ProcessedNode node, HashSet<GameObject> visited)
		{
			if (node == null || !IsUsable((Object)(object)node.Root) || !visited.Add(node.Root))
			{
				return;
			}
			for (int i = 0; i < node.AudioBindings.Count; i++)
			{
				ApplyBinding(node.AudioBindings[i], node.Root);
			}
			for (int j = 0; j < node.ReferencedRoots.Count; j++)
			{
				GameObject val = node.ReferencedRoots[j];
				if (IsUsable((Object)(object)val))
				{
					if (ProcessedNodes.TryGetValue(val, out var value))
					{
						ValidateNodeGraph(value, visited);
					}
					else
					{
						ProcessRoot(val);
					}
				}
			}
		}

		private static FieldInfo[] GetTraversalFields(Type type, bool isComponentRoot)
		{
			if (TraversalFields.TryGetValue(type, out var value))
			{
				return value;
			}
			List<FieldInfo> list = new List<FieldInfo>();
			Type type2 = type;
			while (type2 != null && type2 != typeof(object) && !(type2 == typeof(MonoBehaviour)) && !(type2 == typeof(Behaviour)) && !(type2 == typeof(Component)) && !(type2 == typeof(ScriptableObject)) && !(type2 == typeof(Object)))
			{
				FieldInfo[] array;
				try
				{
					array = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				catch
				{
					array = null;
				}
				if (array != null)
				{
					foreach (FieldInfo fieldInfo in array)
					{
						if (ShouldInspectField(fieldInfo))
						{
							list.Add(fieldInfo);
						}
					}
				}
				type2 = type2.BaseType;
			}
			value = list.ToArray();
			TraversalFields[type] = value;
			return value;
		}

		private static bool ShouldInspectField(FieldInfo field)
		{
			if (field == null || field.IsStatic || field.IsLiteral || field.IsNotSerialized)
			{
				return false;
			}
			Type fieldType = field.FieldType;
			if (fieldType == null)
			{
				return false;
			}
			if (typeof(EffectList).IsAssignableFrom(fieldType) || typeof(GameObject).IsAssignableFrom(fieldType) || typeof(Component).IsAssignableFrom(fieldType) || typeof(ScriptableObject).IsAssignableFrom(fieldType))
			{
				return true;
			}
			if (fieldType.IsArray || typeof(IEnumerable).IsAssignableFrom(fieldType))
			{
				return true;
			}
			if (fieldType == typeof(object) || fieldType.IsInterface || fieldType.IsAbstract)
			{
				return HasSerializeReferenceAttribute(field);
			}
			return ShouldTraverseManagedType(fieldType);
		}

		private static bool HasSerializeReferenceAttribute(FieldInfo field)
		{
			try
			{
				object[] customAttributes = field.GetCustomAttributes(inherit: false);
				if (customAttributes == null)
				{
					return false;
				}
				foreach (object obj in customAttributes)
				{
					if (obj != null)
					{
						string fullName = obj.GetType().FullName;
						if (string.Equals(fullName, "UnityEngine.SerializeReference", StringComparison.Ordinal))
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool ShouldTraverseManagedType(Type type)
		{
			if (type == null || type.IsPrimitive || type.IsEnum || type.IsPointer)
			{
				return false;
			}
			if (type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(TimeSpan) || type == typeof(Guid) || type == typeof(IntPtr) || type == typeof(UIntPtr) || type == typeof(Type))
			{
				return false;
			}
			if (typeof(Delegate).IsAssignableFrom(type))
			{
				return false;
			}
			if (typeof(Object).IsAssignableFrom(type))
			{
				return typeof(ScriptableObject).IsAssignableFrom(type);
			}
			string text = type.Namespace ?? string.Empty;
			if (text.StartsWith("System", StringComparison.Ordinal) || text.StartsWith("UnityEngine", StringComparison.Ordinal))
			{
				return false;
			}
			return type.IsClass || type.IsValueType;
		}

		private static void InvalidateClassificationCaches()
		{
			ProcessedNodes.Clear();
			SourceBindings.Clear();
			ActiveProcessing.Clear();
			if (!MixerCatalog.IsReady)
			{
				return;
			}
			for (int i = 0; i < RegisteredRoots.Count; i++)
			{
				GameObject val = RegisteredRoots[i];
				if (IsUsable((Object)(object)val))
				{
					ProcessRoot(val);
				}
			}
		}

		private static bool IsUsable(Object obj)
		{
			if (obj == null)
			{
				return false;
			}
			try
			{
				return obj != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		private static string SafeName(Object obj)
		{
			if (!IsUsable(obj))
			{
				return "<null>";
			}
			try
			{
				return obj.name ?? "<unnamed>";
			}
			catch
			{
				return "<unavailable>";
			}
		}

		private static void Info(string message)
		{
			if (_infoLogger != null)
			{
				_infoLogger("[AudioRoutingService] " + message);
			}
		}

		private static void Warn(string message)
		{
			if (_warningLogger != null)
			{
				_warningLogger("[AudioRoutingService] " + message);
			}
		}

		private static void Error(string message)
		{
			if (_errorLogger != null)
			{
				_errorLogger("[AudioRoutingService] " + message);
			}
		}

		private static void WarnOnce(string key, string message)
		{
			if (WarnedKeys.Add(key))
			{
				Warn(message);
			}
		}
	}
}
namespace BalrondShipyard
{
	internal static class MagicaClothCompatibilityService
	{
		private enum ClothCategory
		{
			Unknown,
			Cape,
			Banner,
			Sail
		}

		private sealed class ReferenceProfile
		{
			internal GameObject Root;

			internal Component Cloth;

			internal Renderer Renderer;

			internal ClothCategory Category;

			internal int VertexCount;

			internal float MeshDiagonal;
		}

		private static class ModLog
		{
			internal static void Warning(string message)
			{
				Debug.LogWarning((object)("[BalrondShipyard][MagicaClothCompat] " + message));
			}

			internal static void DiagnosticInfo(string category, string message)
			{
			}
		}

		private const string MagicaClothTypeName = "MagicaCloth2.MagicaCloth";

		private const float FixedVertexEpsilon = 0.0001f;

		private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static Type _magicaClothType;

		private static PropertyInfo _serializeDataProperty;

		private static MethodInfo _getSerializeData2Method;

		private static MethodInfo _serializeDataImportMethod;

		internal static void Prepare(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null)
			{
				return;
			}
			if (!ResolveMagicaApi())
			{
				ModLog.Warning("BalrondShipyard: MagicaCloth compatibility skipped - MagicaCloth2.MagicaCloth is not available at runtime.");
				return;
			}
			List<GameObject> roots = CollectReferenceRoots(scene);
			List<ReferenceProfile> list = CollectReferenceProfiles(roots);
			List<GameObject> list2 = CollectCustomTargetRoots();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			for (int i = 0; i < list2.Count; i++)
			{
				GameObject val = list2[i];
				if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<Ship>() != (Object)null)
				{
					continue;
				}
				Cloth[] componentsInChildren = val.GetComponentsInChildren<Cloth>(true);
				foreach (Cloth val2 in componentsInChildren)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					GameObject gameObject = ((Component)val2).gameObject;
					Component component = gameObject.GetComponent(_magicaClothType);
					if ((Object)(object)component != (Object)null)
					{
						val2.enabled = false;
						num2++;
						continue;
					}
					Renderer val3 = ResolveTargetRenderer(val2);
					if ((Object)(object)val3 == (Object)null)
					{
						num5++;
						ModLog.Warning("BalrondShipyard: MagicaCloth compatibility could not resolve renderer for " + Describe(val, gameObject) + ". Legacy Cloth was left enabled.");
						continue;
					}
					ClothCategory clothCategory = Classify(val, gameObject);
					if (clothCategory == ClothCategory.Unknown)
					{
						num3++;
						ModLog.Warning("BalrondShipyard: MagicaCloth compatibility skipped unclassified cloth target " + Describe(val, gameObject) + ".");
						continue;
					}
					int vertexCount = GetVertexCount(val3);
					ReferenceProfile referenceProfile = FindBestReference(list, clothCategory, vertexCount, val3);
					if (referenceProfile == null)
					{
						num4++;
						ModLog.Warning("BalrondShipyard: MagicaCloth compatibility found no working " + clothCategory.ToString() + " reference for " + Describe(val, gameObject) + ". Legacy Cloth was left enabled.");
						continue;
					}
					try
					{
						if (InstallReplacement(val, val2, val3, referenceProfile))
						{
							val2.enabled = false;
							num++;
							ModLog.DiagnosticInfo("MagicaClothCompat", ((Object)val).name + " -> " + clothCategory.ToString() + " template " + ((Object)referenceProfile.Root).name + ".");
						}
						else
						{
							num5++;
						}
					}
					catch (Exception ex)
					{
						num5++;
						ModLog.Warning("BalrondShipyard: MagicaCloth compatibility failed for " + Describe(val, gameObject) + "; legacy Cloth remains available for this object. " + ex);
					}
				}
			}
			ModLog.DiagnosticInfo("MagicaClothCompat", "compatibility completed. references=" + list.Count + ", converted=" + num + ", alreadyModern=" + num2 + ", skippedUnknown=" + num3 + ", skippedNoReference=" + num4 + ", failed=" + num5 + ".");
		}

		private static bool ResolveMagicaApi()
		{
			if (_magicaClothType != null)
			{
				return true;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = null;
				try
				{
					type = assemblies[i].GetType("MagicaCloth2.MagicaCloth", throwOnError: false);
				}
				catch
				{
				}
				if (!(type == null))
				{
					_magicaClothType = type;
					break;
				}
			}
			if (_magicaClothType == null)
			{
				return false;
			}
			_serializeDataProperty = _magicaClothType.GetProperty("SerializeData", InstanceFlags);
			_getSerializeData2Method = _magicaClothType.GetMethod("GetSerializeData2", InstanceFlags);
			if (_serializeDataProperty == null || _getSerializeData2Method == null)
			{
				_magicaClothType = null;
				return false;
			}
			Type propertyType = _serializeDataProperty.PropertyType;
			_serializeDataImportMethod = propertyType.GetMethod("Import", InstanceFlags, null, new Type[2]
			{
				propertyType,
				typeof(bool)
			}, null);
			if (_serializeDataImportMethod == null)
			{
				_magicaClothType = null;
				return false;
			}
			return true;
		}

		private static List<GameObject> CollectReferenceRoots(ZNetScene scene)
		{
			List<GameObject> list = new List<GameObject>();
			HashSet<int> seen = new HashSet<int>();
			AddRoots(scene.m_prefabs, list, seen);
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance != (Object)null)
			{
				AddRoots(instance.m_items, list, seen);
			}
			return list;
		}

		private static List<GameObject> CollectCustomTargetRoots()
		{
			List<GameObject> list = new List<GameObject>();
			HashSet<int> seen = new HashSet<int>();
			AddRoots(Launch.EnumerateOwnedPrefabs(), list, seen);
			return list;
		}

		private static void AddRoots(IEnumerable<GameObject> source, List<GameObject> target, HashSet<int> seen)
		{
			if (source == null)
			{
				return;
			}
			foreach (GameObject item in source)
			{
				if (!((Object)(object)item == (Object)null))
				{
					int instanceID = ((Object)item).GetInstanceID();
					if (seen.Add(instanceID))
					{
						target.Add(item);
					}
				}
			}
		}

		private static List<ReferenceProfile> CollectReferenceProfiles(List<GameObject> roots)
		{
			List<ReferenceProfile> list = new List<ReferenceProfile>();
			for (int i = 0; i < roots.Count; i++)
			{
				GameObject val = roots[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Component[] componentsInChildren;
				try
				{
					componentsInChildren = val.GetComponentsInChildren(_magicaClothType, true);
				}
				catch
				{
					continue;
				}
				foreach (Component val2 in componentsInChildren)
				{
					if ((Object)(object)val2 == (Object)null || !IsMeshCloth(val2))
					{
						continue;
					}
					Renderer val3 = ResolveMagicaRenderer(val2);
					if (!((Object)(object)val3 == (Object)null))
					{
						ClothCategory clothCategory = Classify(val, val2.gameObject);
						if (clothCategory != ClothCategory.Unknown)
						{
							list.Add(new ReferenceProfile
							{
								Root = val,
								Cloth = val2,
								Renderer = val3,
								Category = clothCategory,
								VertexCount = GetVertexCount(val3),
								MeshDiagonal = GetMeshDiagonal(val3)
							});
						}
					}
				}
			}
			return list;
		}

		private static bool IsMeshCloth(Component magicaCloth)
		{
			object value = _serializeDataProperty.GetValue(magicaCloth, null);
			if (value == null)
			{
				return false;
			}
			FieldInfo field = value.GetType().GetField("clothType", InstanceFlags);
			if (field == null)
			{
				return false;
			}
			object value2 = field.GetValue(value);
			return value2 != null && string.Equals(value2.ToString(), "MeshCloth", StringComparison.Ordinal);
		}

		private static Renderer ResolveMagicaRenderer(Component magicaCloth)
		{
			object value = _serializeDataProperty.GetValue(magicaCloth, null);
			if (value != null)
			{
				FieldInfo field = value.GetType().GetField("sourceRenderers", InstanceFlags);
				IList list = ((field != null) ? (field.GetValue(value) as IList) : null);
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						object? obj = list[i];
						Renderer val = (Renderer)((obj is Renderer) ? obj : null);
						if ((Object)(object)val != (Object)null)
						{
							return val;
						}
					}
				}
			}
			return magicaCloth.GetComponent<Renderer>();
		}

		private static Renderer ResolveTargetRenderer(Cloth legacyCloth)
		{
			if ((Object)(object)legacyCloth == (Object)null)
			{
				return null;
			}
			SkinnedMeshRenderer component = ((Component)legacyCloth).GetComponent<SkinnedMeshRenderer>();
			if ((Object)(object)component != (Object)null)
			{
				return (Renderer)(object)component;
			}
			return ((Component)legacyCloth).GetComponent<Renderer>();
		}

		private static ReferenceProfile FindBestReference(List<ReferenceProfile> references, ClothCategory category, int targetVertexCount, Renderer targetRenderer)
		{
			ReferenceProfile referenceProfile = null;
			long num = long.MaxValue;
			float num2 = float.MaxValue;
			float meshDiagonal = GetMeshDiagonal(targetRenderer);
			for (int i = 0; i < references.Count; i++)
			{
				ReferenceProfile referenceProfile2 = references[i];
				if (referenceProfile2 != null && referenceProfile2.Category == category)
				{
					long num3 = Math.Abs((long)referenceProfile2.VertexCount - (long)targetVertexCount);
					float num4 = Math.Abs(referenceProfile2.MeshDiagonal - meshDiagonal);
					if (referenceProfile == null || num3 < num || (num3 == num && num4 < num2))
					{
						referenceProfile = referenceProfile2;
						num = num3;
						num2 = num4;
					}
				}
			}
			return referenceProfile;
		}

		private static bool InstallReplacement(GameObject root, Cloth legacyCloth, Renderer renderer, ReferenceProfile source)
		{
			if ((Object)(object)root == (Object)null || (Object)(object)legacyCloth == (Object)null || (Object)(object)renderer == (Object)null || source == null || (Object)(object)source.Cloth == (Object)null)
			{
				return false;
			}
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			Mesh val2 = (((Object)(object)val != (Object)null) ? val.sharedMesh : null);
			if ((Object)(object)val2 != (Object)null && !val2.isReadable)
			{
				ModLog.Warning("BalrondShipyard: MagicaCloth requires a rebuilt readable mesh: " + Describe(root, ((Component)legacyCloth).gameObject) + " [" + ((Object)val2).name + "]. Enable Read/Write in Unity and rebuild the bundle; legacy Cloth retained.");
				return false;
			}
			Component val3 = ((Component)legacyCloth).gameObject.AddComponent(_magicaClothType);
			if ((Object)(object)val3 == (Object)null)
			{
				return false;
			}
			try
			{
				object value = _serializeDataProperty.GetValue(source.Cloth, null);
				object value2 = _serializeDataProperty.GetValue(val3, null);
				if (value == null || value2 == null)
				{
					throw new InvalidOperationException("MagicaCloth SerializeData is unavailable.");
				}
				_serializeDataImportMethod.Invoke(value2, new object[2] { value, false });
				ConfigureTargetSerializeData(value2, renderer);
				ConfigureTargetSelection(val3, legacyCloth, renderer, source);
				Behaviour val4 = (Behaviour)(object)((val3 is Behaviour) ? val3 : null);
				if ((Object)(object)val4 != (Object)null)
				{
					val4.enabled = true;
				}
				return true;
			}
			catch
			{
				Object.DestroyImmediate((Object)(object)val3);
				throw;
			}
		}

		private static void ConfigureTargetSerializeData(object targetData, Renderer renderer)
		{
			Type type = targetData.GetType();
			SetEnumField(type, targetData, "clothType", "MeshCloth");
			SetEnumField(type, targetData, "paintMode", "Manual");
			IList listField = GetListField(type, targetData, "sourceRenderers");
			if (listField == null)
			{
				throw new InvalidOperationException("MagicaCloth sourceRenderers field is unavailable.");
			}
			listField.Clear();
			listField.Add(renderer);
			GetListField(type, targetData, "paintMaps")?.Clear();
			GetListField(type, targetData, "rootBones")?.Clear();
			FieldInfo field = type.GetField("colliderCollisionConstraint", InstanceFlags);
			object obj = ((field != null) ? field.GetValue(targetData) : null);
			if (obj != null)
			{
				GetListField(obj.GetType(), obj, "colliderList")?.Clear();
			}
			FieldInfo field2 = type.GetField("selfCollisionConstraint", InstanceFlags);
			object obj2 = ((field2 != null) ? field2.GetValue(targetData) : null);
			if (obj2 != null)
			{
				FieldInfo field3 = obj2.GetType().GetField("syncPartner", InstanceFlags);
				if (field3 != null)
				{
					field3.SetValue(obj2, null);
				}
			}
		}

		private static void ConfigureTargetSelection(Component target, Cloth legacyCloth, Renderer renderer, ReferenceProfile source)
		{
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			Mesh val2 = (((Object)(object)val != (Object)null) ? val.sharedMesh : null);
			if ((Object)(object)val2 == (Object)null)
			{
				throw new InvalidOperationException("Legacy Unity Cloth has no SkinnedMeshRenderer/sharedMesh.");
			}
			ClothSkinningCoefficient[] coefficients = legacyCloth.coefficients;
			Vector3[] vertices = val2.vertices;
			if (coefficients != null && coefficients.Length != vertices.Length)
			{
				vertices = legacyCloth.vertices;
			}
			if (vertices == null || vertices.Length == 0)
			{
				throw new InvalidOperationException("Legacy Unity Cloth has no simulation vertices; author the cloth in Unity.");
			}
			if (coefficients == null || coefficients.Length != vertices.Length)
			{
				throw new InvalidOperationException("Legacy Unity Cloth coefficient count does not match simulation vertex count (" + ((coefficients != null) ? coefficients.Length : 0) + " != " + vertices.Length + ").");
			}
			object obj = _getSerializeData2Method.Invoke(target, null);
			if (obj == null)
			{
				throw new InvalidOperationException("MagicaCloth SerializeData2 is unavailable.");
			}
			FieldInfo field = obj.GetType().GetField("selectionData", InstanceFlags);
			if (field == null)
			{
				throw new InvalidOperationException("MagicaCloth selectionData field is unavailable.");
			}
			Type fieldType = field.FieldType;
			object obj2 = Activator.CreateInstance(fieldType);
			FieldInfo field2 = fieldType.GetField("positions", InstanceFlags);
			FieldInfo field3 = fieldType.GetField("attributes", InstanceFlags);
			FieldInfo field4 = fieldType.GetField("maxConnectionDistance", InstanceFlags);
			FieldInfo field5 = fieldType.GetField("userEdit", InstanceFlags);
			if (field2 == null || field3 == null)
			{
				throw new InvalidOperationException("MagicaCloth SelectionData layout is unsupported.");
			}
			Type elementType = field2.FieldType.GetElementType();
			Type elementType2 = field3.FieldType.GetElementType();
			if (elementType == null || elementType2 == null)
			{
				throw new InvalidOperationException("MagicaCloth SelectionData array types are unsupported.");
			}
			ConstructorInfo constructor = elementType.GetConstructor(new Type[3]
			{
				typeof(float),
				typeof(float),
				typeof(float)
			});
			FieldInfo field6 = elementType2.GetField("Fixed", StaticFlags);
			FieldInfo field7 = elementType2.GetField("Move", StaticFlags);
			if (constructor == null || field6 == null || field7 == null)
			{
				throw new InvalidOperationException("MagicaCloth vertex attribute/float3 API is unsupported.");
			}
			object value = field6.GetValue(null);
			object value2 = field7.GetValue(null);
			Array array = Array.CreateInstance(elementType, vertices.Length);
			Array array2 = Array.CreateInstance(elementType2, vertices.Length);
			for (int i = 0; i < vertices.Length; i++)
			{
				Vector3 val3 = vertices[i];
				array.SetValue(constructor.Invoke(new object[3] { val3.x, val3.y, val3.z }), i);
				float maxDistance = coefficients[i].maxDistance;
				bool flag = !float.IsPositiveInfinity(maxDistance) && maxDistance <= 0.0001f;
				array2.SetValue(flag ? value : value2, i);
			}
			field2.SetValue(obj2, array);
			field3.SetValue(obj2, array2);
			if (field4 != null)
			{
				field4.SetValue(obj2, EstimateConnectionDistance(val2, source));
			}
			if (field5 != null)
			{
				field5.SetValue(obj2, true);
			}
			field.SetValue(obj, obj2);
			FieldInfo field8 = obj.GetType().GetField("vertexAttributeList", InstanceFlags);
			((field8 != null) ? (field8.GetValue(obj) as IList) : null)?.Clear();
		}

		private static float EstimateConnectionDistance(Mesh targetMesh, ReferenceProfile source)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//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)
			float num;
			if (!((Object)(object)targetMesh != (Object)null))
			{
				num = 0f;
			}
			else
			{
				Bounds bounds = targetMesh.bounds;
				Vector3 size = ((Bounds)(ref bounds)).size;
				num = ((Vector3)(ref size)).magnitude;
			}
			float num2 = num;
			float referenceConnectionDistance = GetReferenceConnectionDistance(source);
			if (referenceConnectionDistance > 0f && source != null && source.MeshDiagonal > 0.0001f && num2 > 0f)
			{
				return referenceConnectionDistance * (num2 / source.MeshDiagonal);
			}
			return Mathf.Max(num2 * 0.05f, 0.001f);
		}

		private static float GetReferenceConnectionDistance(ReferenceProfile source)
		{
			if (source == null || (Object)(object)source.Cloth == (Object)null)
			{
				return 0f;
			}
			try
			{
				object obj = _getSerializeData2Method.Invoke(source.Cloth, null);
				FieldInfo fieldInfo = obj?.GetType().GetField("selectionData", InstanceFlags);
				object obj2 = ((fieldInfo != null) ? fieldInfo.GetValue(obj) : null);
				FieldInfo fieldInfo2 = obj2?.GetType().GetField("maxConnectionDistance", InstanceFlags);
				object obj3 = ((fieldInfo2 != null) ? fieldInfo2.GetValue(obj2) : null);
				return (obj3 is float) ? ((float)obj3) : 0f;
			}
			catch
			{
				return 0f;
			}
		}

		private static ClothCategory Classify(GameObject root, GameObject clothObject)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			if ((Object)(object)root == (Object)null || (Object)(object)clothObject == (Object)null)
			{
				return ClothCategory.Unknown;
			}
			ItemDrop component = root.GetComponent<ItemDrop>();
			if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null && (int)component.m_itemData.m_shared.m_itemType == 17)
			{
				return ClothCategory.Cape;
			}
			if ((Object)(object)root.GetComponent<Ship>() != (Object)null)
			{
				return ClothCategory.Sail;
			}
			string text = (((Object)root).name + "/" + BuildTransformPath(clothObject.transform, root.transform)).ToLowerInvariant();
			if (text.Contains("cape") || text.Contains("cloak"))
			{
				return ClothCategory.Cape;
			}
			if (text.Contains("sail") || text.Contains("mast"))
			{
				return ClothCategory.Sail;
			}
			if (text.Contains("banner") || text.Contains("flag"))
			{
				return ClothCategory.Banner;
			}
			return ClothCategory.Unknown;
		}

		private static string BuildTransformPath(Transform transform, Transform stopAt)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return string.Empty;
			}
			List<string> list = new List<string>();
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				list.Add(((Object)val).name);
				if ((Object)(object)val == (Object)(object)stopAt)
				{
					break;
				}
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list.ToArray());
		}

		private static int GetVertexCount(Renderer renderer)
		{
			Mesh rendererMesh = GetRendererMesh(renderer);
			return ((Object)(object)rendererMesh != (Object)null) ? rendererMesh.vertexCount : 0;
		}

		private static float GetMeshDiagonal(Renderer renderer)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			Mesh rendererMesh = GetRendererMesh(renderer);
			float result;
			if (!((Object)(object)rendererMesh != (Object)null))
			{
				result = 0f;
			}
			else
			{
				Bounds bounds = rendererMesh.bounds;
				Vector3 size = ((Bounds)(ref bounds)).size;
				result = ((Vector3)(ref size)).magnitude;
			}
			return result;
		}

		private static Mesh GetRendererMesh(Renderer renderer)
		{
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.sharedMesh;
			}
			MeshRenderer val2 = (MeshRenderer)(object)((renderer is MeshRenderer) ? renderer : null);
			if ((Object)(object)val2 != (Object)null)
			{
				MeshFilter component = ((Component)val2).GetComponent<MeshFilter>();
				return ((Object)(object)component != (Object)null) ? component.sharedMesh : null;
			}
			return null;
		}

		private static IList GetListField(Type type, object instance, string fieldName)
		{
			FieldInfo field = type.GetField(fieldName, InstanceFlags);
			return (field != null) ? (field.GetValue(instance) as IList) : null;
		}

		private static void SetEnumField(Type type, object instance, string fieldName, string enumName)
		{
			FieldInfo field = type.GetField(fieldName, InstanceFlags);
			if (!(field == null) && field.FieldType.IsEnum)
			{
				object value = Enum.Parse(field.FieldType, enumName, ignoreCase: false);
				field.SetValue(instance, value);
			}
		}

		private static string Describe(GameObject root, GameObject clothObject)
		{
			return (((Object)(object)root != (Object)null) ? ((Object)root).name : "<null-root>") + "/" + (((Object)(object)clothObject != (Object)null) ? BuildTransformPath(clothObject.transform, ((Object)(object)root != (Object)null) ? root.transform : null) : "<null-cloth>");
		}
	}
	internal static class ModConfig
	{
		private sealed class InventoryConfig
		{
			internal readonly ConfigEntry<int> Width;

			internal readonly ConfigEntry<int> Height;

			internal InventoryConfig(ConfigEntry<int> width, ConfigEntry<int> height)
			{
				Width = width;
				Height = height;
			}
		}

		private static readonly ConfigSync Sync = new ConfigSync("balrond.astafaraios.BalrondShipyard")
		{
			DisplayName = "BalrondShipyard",
			CurrentVersion = "1.7.3",
			MinimumRequiredVersion = "1.7.3",
			ModRequired = true
		};

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

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

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

		internal static ConfigEntry<string> ShipyardRecipe;

		internal static ConfigEntry<string> ScribeTableRecipe;

		internal static ConfigEntry<bool> AllowShipsToBeDestroyedUsingHammer;

		internal static bool Initialized { get; private set; }

		internal static void Initialize(ConfigFile config)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			if (Initialized || config == null)
			{
				return;
			}
			ConfigEntry<int> lockingConfig = config.Bind<int>("1 - General", "Lock Configuration", 1, new ConfigDescription("1 = server config is authoritative for synchronized gameplay settings; 0 = clients may edit synchronized settings.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			Sync.AddLockingConfigEntry<int>(lockingConfig);
			AllowShipsToBeDestroyedUsingHammer = config.Bind<bool>("1 - General", "Allow Ships To Be Destroyed Using Hammer", true, new ConfigDescription("LOCAL ONLY (not synchronized and not affected by Lock Configuration). true = the Hammer Remove action can dismantle supported ships; false = Remove is disabled for ships.", (AcceptableValueBase)null, Array.Empty<object>()));
			AllowShipsToBeDestroyedUsingHammer.SettingChanged += OnLocalHammerRemovalChanged;
			BindShipRecipe(config, "Raft", "default", "Use 'default' to keep the vanilla Raft recipe, or Prefab:Amount pairs separated by commas.");
			BindShipRecipe(config, "Karve", "FineWood:30,BronzeNails:90,LeatherScraps:16,DeerHide:10,CoalPaste:20");
			BindShipRecipe(config, "Knarr", "FineWood:35,BronzeNails:120,LeatherScraps:14,TrollHide:12,CoalPaste:25");
			BindShipRecipe(config, "Snekke", "FineWood:40,IronNails:100,CoalPaste:30,TrollHide:16,ElderBark:30");
			BindShipRecipe(config, "Drakkar", "FineWood:45,IronNails:200,CoalPaste:40,WolfPelt:12,JuteRed:4");
			BindShipRecipe(config, "Holk", "YggdrasilWood:50,IronNails:400,ElderBark:50,LoxPelt:12,Tar:20");
			BindShipRecipe(config, "AshlandsDrakkar", "FineWood:50,IronNails:350,Tar:25,YggdrasilWood:40,CeramicPlate:40");
			ShipyardRecipe = BindSynced(config, "3 - Station Recipes", "Shipyard Station Recipe", "FineWood:30,BronzeNails:200,Coal:25,DeerHide:10,Resin:25", "Build cost. Format: Prefab:Amount,Prefab:Amount. Invalid recipes fall back to built-in defaults.");
			ScribeTableRecipe = BindSynced(config, "3 - Station Recipes", "Scribe Table Recipe", "FineWood:15,Wood:15,BronzeNails:55,LeatherScraps:15,Feathers:10", "Build cost. Format: Prefab:Amount,Prefab:Amount. Invalid recipes fall back to built-in defaults.");
			foreach (KeyValuePair<string, string> value2 in SchematicRecipeDefaults.Values)
			{
				ConfigEntry<string> value = BindSynced(config, "4 - Schematic Recipes", value2.Key, value2.Value, "Scribe Table cost for " + value2.Key + ". Format: Prefab:Amount,Prefab:Amount.");
				SchematicRecipes[value2.Key] = value;
			}
			BindInventory(config, "Raft", -1, -1);
			BindInventory(config, "Karve", 3, 2);
			BindInventory(config, "Knarr", -1, -1);
			BindInventory(config, "Snekke", -1, -1);
			BindInventory(config, "Drakkar", 6, 4);
			BindInventory(config, "Holk", -1, -1);
			BindInventory(config, "AshlandsDrakkar", 8, 5);
			Initialized = true;
		}

		internal static string GetShipRecipe(string shipName)
		{
			ConfigEntry<string> value;
			return ShipRecipes.TryGetValue(shipName, out value) ? value.Value : null;
		}

		internal static string GetSchematicRecipe(string schematicName)
		{
			ConfigEntry<string> value;
			return SchematicRecipes.TryGetValue(schematicName, out value) ? value.Value : null;
		}

		internal static bool TryGetInventorySize(string shipName, int currentWidth, int currentHeight, out int width, out int height)
		{
			width = currentWidth;
			height = currentHeight;
			if (!InventorySizes.TryGetValue(shipName, out var value))
			{
				return false;
			}
			if (value.Width.Value > 0)
			{
				width = value.Width.Value;
			}
			if (value.Height.Value > 0)
			{
				height = value.Height.Value;
			}
			return width > 0 && height > 0;
		}

		private static void BindShipRecipe(ConfigFile config, string shipName, string defaultValue, string description = null)
		{
			ConfigEntry<string> value = BindSynced(config, "2 - Ship Recipes", shipName + " Recipe", defaultValue, description ?? ("Build cost for " + shipName + ". Format: Prefab:Amount,Prefab:Amount. Invalid recipes fall back to built-in defaults."));
			ShipRecipes[shipName] = value;
		}

		private static void BindInventory(ConfigFile config, string shipName, int defaultWidth, int defaultHeight)
		{
			string text = " Set to -1 or 0 to keep the prefab's authored value. Changes are synchronized; reload/re-enter the world after changing cargo dimensions so existing Container inventories are recreated safely.";
			ConfigEntry<int> width = BindSynced(config, "5 - Ship Inventory", shipName + " Inventory Width", defaultWidth, "Cargo inventory width (1-20; -1/0 = keep prefab)." + text, (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 20));
			ConfigEntry<int> height = BindSynced(config, "5 - Ship Inventory", shipName + " Inventory Height", defaultHeight, "Cargo inventory height (1-20; -1/0 = keep prefab)." + text, (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 20));
			InventorySizes[shipName] = new InventoryConfig(width, height);
		}

		private static ConfigEntry<T> BindSynced<T>(ConfigFile config, string section, string key, T value, string description, AcceptableValueBase acceptable = null)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ConfigDescription val = ((acceptable == null) ? new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()) : new ConfigDescription(description, acceptable, Array.Empty<object>()));
			ConfigEntry<T> val2 = config.Bind<T>(section, key, value, val);
			Sync.AddConfigEntry<T>(val2).SynchronizedConfig = true;
			val2.SettingChanged += OnGameplayConfigChanged;
			return val2;
		}

		private static void OnGameplayConfigChanged(object sender, EventArgs args)
		{
			if (Initialized)
			{
				Launch.QueueSynchronizedConfigurationRefresh();
			}
		}

		private static void OnLocalHammerRemovalChanged(object sender, EventArgs args)
		{
			Launch.ApplyLocalHammerRemovalSettingToLoadedShips();
		}

		internal static bool IsHammerShipRemovalAllowed()
		{
			return AllowShipsToBeDestroyedUsingHammer == null || AllowShipsToBeDestroyedUsingHammer.Value;
		}
	}
	internal struct RecipeIngredient
	{
		internal string Prefab;

		internal int Amount;
	}
	internal static class RecipeSpec
	{
		internal static bool TryParse(string value, out List<RecipeIngredient> ingredients, out string error)
		{
			ingredients = new List<RecipeIngredient>();
			error = null;
			if (string.IsNullOrWhiteSpace(value))
			{
				error = "recipe is empty";
				return false;
			}
			string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				error = "recipe contains no ingredients";
				return false;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				int num = text.LastIndexOf(':');
				if (num <= 0 || num >= text.Length - 1)
				{
					error = "invalid ingredient '" + text + "' (expected Prefab:Amount)";
					return false;
				}
				string text2 = text.Substring(0, num).Trim();
				string s = text.Substring(num + 1).Trim();
				if (string.IsNullOrEmpty(text2) || !int.TryParse(s, out var result) || result <= 0)
				{
					error = "invalid ingredient '" + text + "' (amount must be a positive integer)";
					return false;
				}
				if (dictionary.TryGetValue(text2, out var value2))
				{
					RecipeIngredient value3 = ingredients[value2];
					if (value3.Amount > int.MaxValue - result)
					{
						error = "ingredient amount overflow for '" + text2 + "'";
						return false;
					}
					value3.Amount += result;
					ingredients[value2] = value3;
				}
				else
				{
					dictionary[text2] = ingredients.Count;
					ingredients.Add(new RecipeIngredient
					{
						Prefab = text2,
						Amount = result
					});
				}
			}
			return ingredients.Count > 0;
		}

		internal static List<Requirement> BuildRequirements(string value, Func<string, int, Requirement> resolver, string context, List<Requirement> fallback)
		{
			if (!TryParse(value, out var ingredients, out var error))
			{
				Debug.LogWarning((object)("[BalrondShipyard] Invalid configured recipe for " + context + ": " + error + ". Keeping built-in recipe."));
				return fallback;
			}
			List<Requirement> list = new List<Requirement>();
			for (int i = 0; i < ingredients.Count; i++)
			{
				RecipeIngredient recipeIngredient = ingredients[i];
				Requirement val = resolver(recipeIngredient.Prefab, recipeIngredient.Amount);
				if (val == null)
				{
					Debug.LogWarning((object)("[BalrondShipyard] Configured recipe for " + context + " references missing prefab '" + recipeIngredient.Prefab + "'. Keeping built-in recipe."));
					return fallback;
				}
				list.Add(val);
			}
			return list;
		}
	}
	internal static class SchematicRecipeDefaults
	{
		internal static readonly Dictionary<string, string> Values = new Dictionary<string, string>
		{
			{ "SchematicDrakkarAnchor", "Iron:13,CoalPaste:20,FineWood:10,Chain:3" },
			{ "SchematicDrakkarArmor", "Carapace:20,BlackMetal:16,Guck:20,Chain:8" },
			{ "SchematicDrakkarBarrels", "IronNails:57,Resin:30,FineWood:10,Wood:20" },
			{ "SchematicDrakkarCrates", "Tar:6,BlackMetal:8,FineWood:25,IronNails:33" },
			{ "SchematicDrakkarLights", "FineWood:10,IronNails:37,Copper:5" },
			{ "SchematicDrakkarOar", "Wood:10,FineWood:5,RoundLog:10" },
			{ "SchematicDrakkarShields", "IronNails:88,RoundLog:12,Resin:20,DeerHide:15" },
			{ "SchematicDrakkarTent", "JuteRed:5,IronNails:23,FineWood:10,WolfPelt:3" },
			{ "SchematicDrakkarHeadCernyx", "FineWood:4,Tin:2" },
			{ "SchematicDrakkarHeadDragon", "FineWood:4,Tin:2" },
			{ "SchematicDrakkarHeadOsberg", "FineWood:4,Tin:2" },
			{ "SchematicDrakkarHeadSkull", "FineWood:4,Tin:2" },
			{ "SchematicDrakkarSailBlack", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailBlue", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailGreen", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailDefault", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailTransparent", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailWhite", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailHound", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailDruid", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailWolf", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicDrakkarSailRaven", "CoalPaste:5,FineWood:5,SailInk:4,LeatherScraps:8" },
			{ "SchematicKarveAnchor", "Bronze:6,CoalPaste:4,LeatherScraps:12,Wood:10" },
			{ "SchematicKarveArmor", "Carapace:12,BlackMetal:12,Guck:12,Chain:4" },
			{ "SchematicKarveBag", "LeatherScraps:10,Tin:3,BronzeNails:15,FineWood:10" },
			{ "SchematicKarveLights", "CoalPaste:8,BronzeNails:25,Tin:4,FineWood:8" },
			{ "SchematicKarveOars", "Wood:5,FineWood:2,RoundLog:4,CoalPaste:4" },
			{ "SchematicKarveShields", "LeatherScraps:14,BronzeNails:50,Copper:8,RoundLog:20" },
			{ "SchematicKarveSailBlack", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailBlue", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailDefault", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailGreen", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailTransparent", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailWhite", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailHound", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailDruid", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailWolf", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKarveSailRaven", "CoalPaste:2,Wood:5,SailInk:3,LeatherScraps:5" },
			{ "SchematicKnarrAnchor", "Bronze:8,CoalPaste:6,LeatherScraps:16,Wood:14" },
			{ "SchematicKnarrArmor", "Carapace:16,BlackMetal:14,Guck:16,Chain:4" },
			{ "SchematicKnarrBag", "LeatherScraps:12,Tin:4,BronzeNails:20,FineWood:14" },
			{ "SchematicKnarrLights", "CoalPaste:12,BronzeNails:35,Tin:6,FineWood:12" },
			{ "SchematicKnarrOars", "Wood:7,FineWood:4,RoundLog:6,CoalPaste:6" },
			{ "SchematicKnarrShields", "LeatherScraps:20,BronzeNails:65,Copper:12,RoundLog:24" },
			{ "SchematicKnarrTent", "TrollHide:10,BronzeNails:35,FineWood:10,Wood:6" },
			{ "SchematicKnarrSailBlack", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailBlue", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailDefault", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailDruid", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailGreen", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailHound", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailRaven", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailTransparent", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailWhite", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicKnarrSailWolf", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicHolkAnchor", "Iron:20,Tar:6,YggdrasilWood:10,Chain:5" },
			{ "SchematicHolkArmor", "Carapace:40,BlackMetal:24,Guck:30,Chain:10" },
			{ "SchematicHolkLights", "YggdrasilWood:10,IronNails:37,Wisp:4" },
			{ "SchematicHolkOars", "Wood:10,FineWood:10,RoundLog:10,YggdrasilWood:10" },
			{ "SchematicHolkShields", "Chain:10,RoundLog:40,BlackMetal:30,WolfPelt:16" },
			{ "SchematicHolkTent", "YggdrasilWood:14,IronNails:40,Tar:10,BlackMetal:3" },
			{ "SchematicHolkBag", "Tar:10,BlackMetal:12,YggdrasilWood:15,IronNails:77" },
			{ "SchematicHolkRopes", "Wood:10,YggdrasilWood:5,LeatherScraps:10,IronNails:16" },
			{ "SchematicHolkSailBlack", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailBlue", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailGreen", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailDefault", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailTransparent", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailWhite", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailHound", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailDruid", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailWolf", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicHolkSailRaven", "CoalPaste:7,YggdrasilWood:4,SailInk:6,LeatherScraps:10" },
			{ "SchematicAshlandsDrakkarAnchor", "FlametalNew:13,CoalPaste:20,Blackwood:10,Chain:4" },
			{ "SchematicAshlandsDrakkarCrates", "CharcoalResin:6,FlametalNew:8,Blackwood:25,IronNails:33" },
			{ "SchematicAshlandsDrakkarLights", "Blackwood:10,IronNails:37,FlametalNew:5,Wisp:4" },
			{ "SchematicAshlandsDrakkarOar", "Wood:10,Blackwood:5,RoundLog:10" },
			{ "SchematicAshlandsDrakkarShields", "FlametalNew:14,Blackwood:20,CharcoalResin:20,AskHide:10" },
			{ "SchematicAshlandsDrakkarTent", "LinenThread:25,FlametalNew:10,Blackwood:20,AskHide:10" },
			{ "SchematicAshlandsDrakkarRopes", "Wood:20,Blackwood:5,LeatherScraps:20,FlametalNew:5" },
			{ "SchematicAshlandsDrakkarSailBlack", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailBlue", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailGreen", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailDefault", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailTransparent", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailWhite", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailHound", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailDruid", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailWolf", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicAshlandsDrakkarSailRaven", "CoalPaste:4,Blackwood:6,SailInk:6,LeatherScraps:14" },
			{ "SchematicKnarrNamePlate", "Wood:2,Coal:2" },
			{ "SchematicKarveNamePlate", "Wood:2,Coal:2" },
			{ "SchematicHolkNamePlate", "Wood:2,Coal:2" },
			{ "SchematicAshlandsDrakkarNamePlate", "Wood:2,Coal:2" },
			{ "SchematicDrakkarNamePlate", "Wood:2,Coal:2" },
			{ "SchematicSnekkeAnchor", "Iron:8,CoalPaste:6,LeatherScraps:16,RoundLog:10" },
			{ "SchematicSnekkeArmor", "Carapace:18,BlackMetal:16,Guck:16,Chain:4" },
			{ "SchematicSnekkeLights", "CoalPaste:12,IronNails:55,Tin:8,FineWood:16" },
			{ "SchematicSnekkeOars", "Wood:8,FineWood:5,RoundLog:7,CoalPaste:7" },
			{ "SchematicSnekkeShields", "LeatherScraps:24,IronNails:55,Copper:14,RoundLog:28" },
			{ "SchematicSnekkeTent", "TrollHide:12,IronNails:55,FineWood:14,Wood:10" },
			{ "SchematicSnekkeBag", "LeatherScraps:14,Tin:6,IronNails:30,FineWood:16" },
			{ "SchematicSnekkeNamePlate", "Wood:2,Coal:2" },
			{ "SchematicSnekkeSailBlack", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailBlue", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailGreen", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailDefault", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailTransparent", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailWhite", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailHound", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailDruid", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailWolf", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" },
			{ "SchematicSnekkeSailRaven", "CoalPaste:3,Wood:4,SailInk:2,LeatherScraps:4" }
		};
	}
	[BepInPlugin("balrond.astafaraios.BalrondShipyard", "BalrondShipyard", "1.7.3")]
	public sealed class Launch : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(FejdStartup), "Start")]
		private static class FejdStartupShaderMaterialFixPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				RunEarlyShaderMaterialFixOnce();
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetSceneAwakePatch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static void Prefix(ZNetScene __instance)
			{
				ConfigureZNetScene(__instance);
			}

			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix(ZNetScene __instance)
			{
				FinalizeVisualCompatibility(__instance);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDBAwakePatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				ConfigureObjectDB(__instance);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class ObjectDBCopyOtherDBPatch
		{
			private static void Postfix(ObjectDB __instance)
			{
				ConfigureObjectDB(__instance);
			}
		}

		[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
		{
			typeof(int),
			typeof(bool),
			typeof(int),
			typeof(float),
			typeof(short)
		})]
		private static class SEManAddStatusEffectWaterproofPatch
		{
			private static readonly int WaterproofHash = BalrondHashCompat.StableHash("Waterproof");

			private static readonly int WetHash = BalrondHashCompat.StableHash("Wet");

			[HarmonyPrefix]
			private static bool Prefix(SEMan __instance, int nameHash)
			{
				if (__instance == null || nameHash != WetHash)
				{
					return true;
				}
				return !__instance.HaveStatusEffect(WaterproofHash);
			}
		}

		[HarmonyPatch(typeof(AudioMan), "Awake")]
		private static class AudioManAwakePatch
		{
			private static void Postfix(AudioMan __instance)
			{
				AudioRoutingService.OnAudioManAwake(__instance);
			}
		}

		[HarmonyPatch(typeof(Ship), "Awake")]
		private static class ShipAwakeHammerRemovalPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Ship __instance)
			{
				ApplyLocalHammerRemovalSetting(__instance);
			}
		}

		[HarmonyPatch(typeof(Ship), "OnTriggerEnter")]
		private static class ShipTriggerEnterPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Collider collider, Ship __instance, List<Player> ___m_players)
			{
				RefreshOarRudderSpeedFromPassengers(collider, __instance, ___m_players);
			}
		}

		[HarmonyPatch(typeof(Ship), "OnTriggerExit")]
		private static class ShipTriggerExitPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Collider collider, Ship __instance, List<Player> ___m_players)
			{
				RefreshOarRudderSpeedFromPassengers(collider, __instance, ___m_players);
			}
		}

		public const string PluginGUID = "balrond.astafaraios.BalrondShipyard";

		public const string PluginName = "BalrondShipyard";

		public const string PluginVersion = "1.7.3";

		private const string AssetBundleName = "shipyardnew";

		private const string AssetBasePath = "Assets/custom/balrondshipyard/";

		private const string WaterproofStatusName = "Waterproof";

		private readonly Harmony harmony = new Harmony("balrond.astafaraios.BalrondShipyard");

		private static Launch instance;

		private static bool earlyShaderMaterialFixDone;

		private static ZNetScene finalizedVisualScene;

		private static AssetBundle assetBundle;

		public static GameObject snekkeExtension;

		public static GameObject karveExtension;

		public static GameObject knarrExtension;

		public static GameObject raftExtension;

		public static GameObject drakkarExtensions;

		public static GameObject ashlandsDrakkarExtensions;

		public static GameObject holkExtensions;

		public static GameObject shipyard;

		public static readonly ShipExtensionFactory shipExtensionFactory = new ShipExtensionFactory();

		public static List<ShipExtension> list = new List<ShipExtension>();

		public static List<ShipExtension> listSnekke = new List<ShipExtension>();

		public static List<ShipExtension> listKarve = new List<ShipExtension>();

		public static List<ShipExtension> listKnarr = new List<ShipExtension>();

		public static List<ShipExtension> listDrakkar = new List<ShipExtension>();

		public static List<ShipExtension> listAshlandsDrakkar = new List<ShipExtension>();

		public static List<ShipExtension> listHolk = new List<ShipExtension>();

		public static List<Texture> texturesKarve = new List<Texture>();

		public static List<Texture> texturesDrakkar = new List<Texture>();

		public static List<Texture> texturesRaft = new List<Texture>();

		public static List<GameObject> scrolls = new List<GameObject>();

		public static GameObject scribeTable;

		public static List<Recipe> recipes = new List<Recipe>();

		public static readonly ShipProcessor shipProcessor = new ShipProcessor();

		public static readonly ObjectRecipes objectRecipes = new ObjectRecipes();

		public static List<FishnetTrap.ItemConversion> fishConversions = new List<FishnetTrap.ItemConversion>();

		public static readonly RecipeBuilder recipeBuilder = new RecipeBuilder();

		public static Sprite waterProofIco;

		public static List<GameObject> items = new List<GameObject>();

		public static string[] itemNames = new string[8] { "SailInk", "CoalPaste", "HelmetPirateBandana", "HelmetPirateHat", "SledgeAnchor", "CapeSailor", "ShieldSteering", "SwordSaber" };

		public static string[] scrollNames = new string[113]
		{
			"SchematicDrakkarAnchor", "SchematicDrakkarArmor", "SchematicDrakkarBarrels", "SchematicDrakkarCrates", "SchematicDrakkarLights", "SchematicDrakkarOar", "SchematicDrakkarShields", "SchematicDrakkarTent", "SchematicDrakkarHeadCernyx", "SchematicDrakkarHeadDragon",
			"SchematicDrakkarHeadOsberg", "SchematicDrakkarHeadSkull", "SchematicDrakkarSailBlack", "SchematicDrakkarSailBlue", "SchematicDrakkarSailGreen", "SchematicDrakkarSailDefault", "SchematicDrakkarSailTransparent", "SchematicDrakkarSailWhite", "SchematicDrakkarSailHound", "SchematicDrakkarSailDruid",
			"SchematicDrakkarSailWolf", "SchematicDrakkarSailRaven", "SchematicKarveAnchor", "SchematicKarveArmor", "SchematicKarveBag", "SchematicKarveLights", "SchematicKarveOars", "SchematicKarveShields", "SchematicKarveSailBlack", "SchematicKarveSailBlue",
			"SchematicKarveSailDefault", "SchematicKarveSailGreen", "SchematicKarveSailTransparent", "SchematicKarveSailWhite", "SchematicKarveSailHound", "SchematicKarveSailDruid", "SchematicKarveSailWolf", "SchematicKarveSailRaven", "SchematicKnarrAnchor", "SchematicKnarrArmor",
			"SchematicKnarrBag", "SchematicKnarrLights", "SchematicKnarrOars", "SchematicKnarrShields", "SchematicKnarrTent", "SchematicKnarrSailBlack", "SchematicKnarrSailBlue", "SchematicKnarrSailDefault", "SchematicKnarrSailDruid", "SchematicKnarrSailGreen",
			"SchematicKnarrSailHound", "SchematicKnarrSailRaven", "SchematicKnarrSailTransparent", "SchematicKnarrSailWhite", "SchematicKnarrSailWolf", "SchematicHolkAnchor", "SchematicHolkArmor", "SchematicHolkLights", "SchematicHolkOars", "SchematicHolkShields",
			"SchematicHolkTent", "SchematicHolkBag", "SchematicHolkRopes", "SchematicHolkSailBlack", "SchematicHolkSailBlue", "SchematicHolkSailGreen", "SchematicHolkSailDefault", "SchematicHolkSailTransparent", "SchematicHolkSailWhite", "SchematicHolkSailHound",
			"SchematicHolkSailDruid", "SchematicHolkSailWolf", "SchematicHolkSailRaven", "SchematicAshlandsDrakkarAnchor", "SchematicAshlandsDrakkarCrates", "SchematicAshlandsDrakkarLights", "SchematicAshlandsDrakkarOar", "SchematicAshlandsDrakkarShields", "SchematicAshlandsDrakkarTent", "SchematicAshlandsDrakkarRopes",
			"SchematicAshlandsDrakkarSailBlack", "SchematicAshlandsDrakkarSailBlue", "SchematicAshlandsDrakkarSailGreen", "SchematicAshlandsDrakkarSailDefault", "SchematicAshlandsDrakkarSailTransparent", "SchematicAshlandsDrakkarSailWhite", "SchematicAshlandsDrakkarSailHound", "SchematicAshlandsDrakkarSailDruid", "SchematicAshlandsDrakkarSailWolf", "SchematicAshlandsDrakkarSailRaven",
			"SchematicKnarrNamePlate", "SchematicKarveNamePlate", "SchematicHolkNamePlate", "SchematicAshlandsDrakkarNamePlate", "SchematicDrakkarNamePlate", "SchematicSnekkeAnchor", "SchematicSnekkeArmor", "SchematicSnekkeLights", "SchematicSnekkeOars", "SchematicSnekkeShields",
			"SchematicSnekkeTent", "SchematicSnekkeBag", "SchematicSnekkeNamePlate", "SchematicSnekkeSailBlack", "SchematicSnekkeSailBlue", "SchematicSnekkeSailGreen", "SchematicSnekkeSailDefault", "SchematicSnekkeSailTransparent", "SchematicSnekkeSailWhite", "SchematicSnekkeSailHound",
			"SchematicSnekkeSailDruid", "SchematicSnekkeSailWolf", "SchematicSnekkeSailRaven"
		};

		public static List<GameObject> pieces = new List<GameObject>();

		public static string[] piecesNames = new string[28]
		{
			"tar_wood_wall_log4", "tar_wood_wall_log2", "tar_wood_pole_log4", "tar_wood_pole_log2", "tar_wood_floor", "tar_ladder", "tar_wood_log_26", "tar_wood_log_45", "tar_wood_pole_log1", "tar_wood_ramp",
			"piece_chest_tarred", "piece_chest_cargo", "piece_iron_wall_torch", "decor_dragon", "bridge_rope", "bridge_rope_support", "bridge_rope_support_side", "cloth_roof", "cloth_roof_corner", "cloth_roof_corner2",
			"cloth_roof_triangle_sloope", "fishnet_wall", "piece_banner_sail1", "piece_banner_sail2", "piece_ropewall", "piece_ropewall_big", "piece_wisplampceiling", "FishnetTrap"
		};

		public static string[] sailNames = new string[10] { "default_drakkar", "blue_drakkar", "torn_drakkar", "green_drakkar", "transparent_drakkar", "white_drakkar", "sailCross", "yellow_drakkar", "sailDark", "sailVegvisir" };

		public static GameObject dvergercarve;

		public static GameObject karr;

		public static GameObject snekke;

		public static GameObject projectileAnchor;

		private static readonly HashSet<string> TrustedShipShaderNames = new HashSet<string>(StringComparer.Ordinal) { "Custom/Vegetation", "Custom/WaterMask", "Custom/ShadowBlob" };

		private void Awake()
		{
			instance = this;
			ModConfig.Initialize(((BaseUnityPlugin)this).Config);
			if (!LoadAllAssetBundles())
			{
				Debug.LogError((object)"[BalrondShipyard] Asset bundle initialization failed. Harmony patches will not be installed.");
				return;
			}
			shipProcessor.shipyard = shipyard;
			shipProcessor.texturesDrakkar = texturesDrakkar;
			AudioRoutingService.ConfigureLogging(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[Audio] " + message));
			}, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[Audio] " + message));
			}, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[Audio] " + message));
			});
			AudioRoutingService.DebugLogging = false;
			AudioRoutingService.Process(EnumerateOwnedPrefabs());
			harmony.PatchAll();
			Debug.Log((object)"[BalrondShipyard] Loaded 1.7.3.");
		}

		private void OnDestroy()
		{
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
			earlyShaderMaterialFixDone = false;
			finalizedVisualScene = null;
			harmony.UnpatchSelf();
		}

		public static bool LoadAllAssetBundles()
		{
			assetBundle = GetAssetBundleFromResources("shipyardnew");
			if ((Object)(object)assetBundle == (Object)null)
			{
				return false;
			}
			return LoadPrefabs();
		}

		public static AssetBundle GetAssetBundleFromResources(string filename)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(filename, StringComparison.OrdinalIgnoreCase));
			if (string.IsNullOrEmpty(text))
			{
				Debug.LogError((object)("[BalrondShipyard] Embedded asset bundle not found: " + filename));
				return null;
			}
			using Stream stream = executingAssembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				Debug.LogError((object)("[BalrondShipyard] Could not open embedded asset bundle stream: " + text));
				return null;
			}
			return AssetBundle.LoadFromStream(stream);
		}

		private static bool LoadPrefabs()
		{
			ClearLoadedCollections();
			waterProofIco = LoadAsset<Sprite>("wateproof_ico.png", warn: false);
			dvergercarve = LoadAsset<GameObject>("prefabs/Holk.prefab", warn: true);
			karr = LoadAsset<GameObject>("prefabs/KnarrBal.prefab", warn: true);
			snekke = LoadAsset<GameObject>("prefabs/SnekkeShip_bal.prefab", warn: true);
			raftExtension = LoadAsset<GameObject>("prefabs/RaftExtension/RaftExtension.prefab", warn: true);
			projectileAnchor = LoadAsset<GameObject>("prefabs/projectile_anchor.prefab", warn: false);
			karveExtension = LoadAsset<GameObject>("prefabs/KarveExtensions/karveExtension.prefab", warn: true);
			shipyard = LoadAsset<GameObject>("prefabs/pieces/ShipyardStation.prefab", warn: true);
			drakkarExtensions = LoadAsset<GameObject>("prefabs/DrakkarExtensions/DrakkarExtensions.prefab", warn: true);
			ashlandsDrakkarExtensions = LoadAsset<GameObject>("prefabs/AshlandsDrakkarExtensions.prefab", warn: true);
			scribeTable = LoadAsset<GameObject>("prefabs/pieces/piece_scribetable.prefab", warn: true);
			if ((Object)(object)karr != (Object)null)
			{
				((Object)karr).name = "Knarr";
			}
			if ((Object)(object)snekke != (Object)null)
			{
				((Object)snekke).name = "Snekke";
			}
			knarrExtension = FindChildObject(karr, "knarrExtension");
			snekkeExtension = FindChildObject(snekke, "snekkeExtension");
			holkExtensions = FindChildObject(dvergercarve, "DvergerExtension");
			ReplaceShaders(dvergercarve, karr, snekke, raftExtension, karveExtension, knarrExtension, snekkeExtension, holkExtensions, shipyard, drakkarExtensions, ashlandsDrakkarExtensions, scribeTable);
			string[] array = sailNames;
			foreach (string text in array)
			{
				Texture val = LoadAsset<Texture>("sails/" + text + ".png", warn: false);
				if ((Object)(object)val != (Object)null)
				{
					texturesDrakkar.Add(val);
				}
				else
				{
					Debug.LogWarning((object)("[BalrondShipyard] Sail texture not found: " + text));
				}
			}
			string[] array2 = piecesNames;
			foreach (string text2 in array2)
			{
				GameObject val2 = LoadAsset<GameObject>("prefabs/pieces/" + text2 + ".prefab", warn: false);
				if ((Object)(object)val2 == (Object)null)
				{
					Debug.LogWarning((object)("[BalrondShipyard] Build prefab not found: " + text2));
					continue;
				}
				if (text2 == "FishnetTrap")
				{
					Smelter component = val2.GetComponent<Smelter>();
					if ((Object)(object)component != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)component);
					}
				}
				ShaderReplacment.Replace(val2);
				pieces.Add(val2);
			}
			string[] array3 = scrollNames;
			foreach (string text3 in array3)
			{
				GameObject val3 = LoadAsset<GameObject>("prefabs/schematics/" + text3 + ".prefab", warn: false);
				if ((Object)(object)val3 == (Object)null)
				{
					Debug.LogWarning((object)("[BalrondShipyard] Schematic prefab not found: " + text3));
					continue;
				}
				ShaderReplacment.Replace(val3);
				scrolls.Add(val3);
			}
			string[] array4 = itemNames;
			foreach (string text4 in array4)
			{
				GameObject val4 = LoadAsset<GameObject>("prefabs/items/" + text4 + ".prefab", warn: false);
				if ((Object)(object)val4 == (Object)null)
				{
					Debug.LogWarning((object)("[BalrondShipyard] Item prefab not found: " + text4));
					continue;
				}
				ShaderReplacment.Replace(val4);
				items.Add(val4);
			}
			ashlandsUpdateFix();
			if (!((Object)(object)shipyard != (Object)null) || !((Object)(object)scribeTable != (Object)null) || !((Object)(object)dvergercarve != (Object)null) || !((Object)(object)karr != (Object)null) || !((Object)(object)snekke != (Object)null))
			{
				Debug.LogError((object)"[BalrondShipyard] One or more essential prefabs failed to load. The mod will keep running with unavailable features skipped.");
			}
			return true;
		}

		private static T LoadAsset<T>(string relativePath, bool warn = true) where T : Object
		{
			if ((Object)(object)assetBundle == (Object)null)
			{
				return default(T);
			}
			T val = assetBundle.LoadAsset<T>("Assets/custom/balrondshipyard/" + relativePath);
			if ((Object)(object)val == (Object)null && warn)
			{
				Debug.LogWarning((object)("[BalrondShipyard] Missing asset: " + relativePath));
			}
			return val;
		}

		private static void ClearLoadedCollections()
		{
			scrolls.Clear();
			items.Clear();
			pieces.Clear();
			texturesKarve.Clear();
			texturesDrakkar.Clear();
			texturesRaft.Clear();
			recipes.Clear();
			fishConversions.Clear();
		}

		private static GameObject FindChildObject(GameObject root, string path)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			Transform val = root.transform.Find(path);
			return ((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null;
		}

		private static void ReplaceShaders(params GameObject[] prefabs)
		{
			foreach (GameObject val in prefabs)
			{
				if ((Object)(object)val != (Object)null)
				{
					ShaderReplacment.Replace(val);
				}
			}
		}

		private static void RegisterTrustedVanillaShipMaterials(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null || scene.m_prefabs == null)
			{
				return;
			}
			string[] array = new string[4] { "Karve", "VikingShip", "VikingShip_Ashlands", "Raft" };
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (string prefabName in array)
			{
				GameObject val = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && string.Equals(((Object)x).name, prefabName, StringComparison.Ordinal));
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true);
				if (componentsInChildren == null)
				{
					continue;
				}
				foreach (Renderer val2 in componentsInChildren)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials = val2.sharedMaterials;
					if (sharedMaterials == null)
					{
						continue;
					}
					foreach (Material val3 in sharedMaterials)
					{
						Shader val4 = (((Object)(object)val3 != (Object)null) ? val3.shader : null);
						string text = (((Object)(object)val4 != (Object)null) ? ((Object)val4).name : null);
						if (!string.IsNullOrEmpty(text) && TrustedShipShaderNames.Contains(text) && !hashSet.Contains(text))
						{
							ShaderReplacment.RegisterTrustedMaterial(val3);
							hashSet.Add(text);
							Debug.Log((object)("[BalrondShipyard][ShaderFix] Trusted vanilla material anchor: " + text + " from " + prefabName + "/" + ((Object)val2).name + ", shaderInstanceID=" + ((Object)val4).GetInstanceID() + "."));
						}
					}
				}
			}
			foreach (string trustedShipShaderName in TrustedShipShaderNames)
			{
				if (!hashSet.Contains(trustedShipShaderName))
				{
					Debug.LogWarning((object)("[BalrondShipyard][ShaderFix] No vanilla ship material anchor found for '" + trustedShipShaderName + "'. Generic evidence resolution will be used as fallback."));
				}
			}
		}

		public static void setupRavenGuide(GameObject target, List<GameObject> gameObjects)
		{
			if (!((Object)(object)target == (Object)null) && gameObjects != null)
			{
				Transform val = target.transform.Find("GuidePoint");
				GameObject val2 = gameObjects.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "piece_workbench");
				Transform val3 = (((Object)(object)val2 != (Object)null) ? val2.transform.Find("GuidePoint") : null);
				GuidePoint val4 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<GuidePoint>() : null);
				GuidePoint val5 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<GuidePoint>() : null);
				if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null || (Object)(object)val5.m_ravenPrefab == (Object)null)
				{
					Debug.LogWarning((object)("[BalrondShipyard] Raven guide could not be configured for " + ((Object)target).name + "."));
				}
				else
				{
					val4.m_ravenPrefab = val5.m_ravenPrefab;
				}
			}
		}

		public static void ashlandsUpdateFix()
		{
			ConfigureShipForAshlands(dvergercarve);
			ConfigureShipForAshlands(karr);
		}

		private static void ConfigureShipForAshlands(GameObject prefab)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			ImpactEffect component = prefab.GetComponent<ImpactEffect>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_hitType = (HitType)17;
			}
			WearNTear component2 = prefab.GetComponent<WearNTear>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_burnable = true;
			}
			Rigidbody component3 = prefab.GetComponent<Rigidbody>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.automaticCenterOfMass = true;
				component3.automaticInertiaTensor = true;
				component3.useGravity = true;
			}
			Ship component4 = prefab.GetComponent<Ship>();
			if ((Object)(object)component4 != (Object)null)
			{
				component4.m_ashDamageMsgTime = 10f;
				Transform val = prefab.transform.Find("ashlandeffects");
				if ((Object)(object)val != (Object)null)
				{
					component4.m_ashdamageEffects = ((Component)val).gameObject;
				}
			}
		}

		public static SE_WetImmunity createWateproofStatusEffect()
		{
			SE_WetImmunity sE_WetImmunity = ScriptableObject.CreateInstance<SE_WetImmunity>();
			((Object)sE_WetImmunity).name = "Waterproof";
			((StatusEffect)sE_WetImmunity).m_name = "$tag_status_raincoat_bal";
			((StatusEffect)sE_WetImmunity).m_tooltip = "$tag_status_raincoat_tooltip_bal";
			((StatusEffect)sE_WetImmunity).m_icon = waterProofIco;
			return sE_WetImmunity;
		}

		private static bool IsObjectDBValid(ObjectDB db)
		{
			return (Object)(object)db != (Object)null && db.m_items != null && db.m_recipes != null && db.m_items.Count > 0;
		}

		private static void ConfigureZNetScene(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null || scene.m_prefabs == null)
			{
				return;
			}
			RegisterTrustedVanillaShipMaterials(scene);
			ShaderReplacment.ForceRefreshAndRunMaterialFix();
			AddPrefabs(scene.m_prefabs, scrolls);
			AddPrefabs(scene.m_prefabs, items);
			AddPrefabs(scene.m_prefabs, pieces);
			AddPrefab(scene.m_prefabs, projectileAnchor);
			AddPrefab(scene.m_prefabs, shipyard);
			AddPrefab(scene.m_prefabs, scribeTable);
			AddPrefab(scene.m_prefabs, dvergercarve);
			AddPrefab(scene.m_prefabs, karr);
			AddPrefab(scene.m_prefabs, snekke);
			objectRecipes.initPieces(scene.m_prefabs);
			shipExtensionFactory.init(scene, scrolls);
			list = shipExtensionFactory.list;
			listKarve = shipExtensionFactory.listKarve;
			listSnekke = shipExtensionFactory.listSnekke;
			listKnarr = shipExtensionFactory.listKnarr;
			listDrakkar = shipExtensionFactory.listDrakkar;
			listAshlandsDrakkar = shipExtensionFactory.listAshlandsDrakkar;
			listHolk = shipExtensionFactory.listHolk;
			shipProcessor.list = list;
			objectRecipes.createHolkRecipe(dvergercarve);
			objectRecipes.createKnarrRecipe(karr);
			objectRecipes.createSnekkeRecipe(snekke);
			objectRecipes.createShipyardRecipe(shipyard);
			objectRecipes.createScribeTableRecipe(scribeTable);
			objectRecipes.editBuildPieceRecipes(pieces);
			shipProcessor.setupShipyard(scene, shipyard);
			GameObject val = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "Hammer");
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			PieceTable val3 = (((Object)(object)val2 != (Object)null) ? val2.m_itemData.m_shared.m_buildPieces : null);
			if ((Object)(object)val3 != (Object)null)
			{
				AddPiece(val3, shipyard);
				AddPiece(val3, scribeTable);
				AddPiece(val3, dvergercarve);
				AddPiece(val3, karr);
				AddPiece(val3, snekke);
				foreach (GameObject piece in pieces)
				{
					AddPiece(val3, piece);
				}
			}
			else
			{
				Debug.LogWarning((object)"[BalrondShipyard] Hammer PieceTable was not available during ZNetScene initialization.");
			}
			setupRavenGuide(shipyard, scene.m_prefabs);
			setupRavenGuide(scribeTable, scene.m_prefabs);
			GameObject val4 = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "Raft");
			GameObject val5 = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "Karve");
			GameObject val6 = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "VikingShip");
			GameObject val7 = scene.m_prefabs.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "VikingShip_Ashlands");
			if ((Object)(object)val4 != (Object)null)
			{
				Piece component = val4.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_canBeRemoved = ModConfig.IsHammerShipRemovalAllowed();
				}
				objectRecipes.createRaftRecipe(val4);
				shipProcessor.addRaftExtensions(raftExtension, val4, scene);
				SetConfiguredContainerSize(val4, "Raft");
			}
			if ((Object)(object)val5 != (Object)null)
			{
				objectRecipes.createKarveRecipe(val5);
				shipProcessor.addKarveExtensions(karveExtension, val5, scene);
				SetConfiguredContainerSize(val5, "Karve");
			}
			if ((Object)(object)val6 != (Object)null)
			{
				objectRecipes.createDrakkarRecipe(val6);
				shipProcessor.addDrakkarExtensions(drakkarExtensions, val6, scene);
				SetConfiguredContainerSize(val6, "Drakkar");
			}
			if ((Object)(object)val7 != (Object)null)
			{
				objectRecipes.createAshlandsDrakkarRecipe(val7);
				shipProcessor.addAshlandsDrakkarExtensions(ashlandsDrakkarExtensions, val7, scene);
				SetConfiguredContainerSize(val7, "AshlandsDrakkar");
			}
			if ((Object)(object)karr != (Object)null)
			{
				shipProcessor.addKnarrExtensions(knarrExtension, karr, scene);
				SetConfiguredContainerSize(karr, "Knarr");
			}
			if ((Object)(object)snekke != (Object)null)
			{
				shipProcessor.addSnekkeExtensions(snekkeExtension, snekke, scene);
				SetConfiguredContainerSize(snekke, "Snekke");
			}
			if ((Object)