Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of balrond shipyard v1.7.3
plugins/BalrondShipyard.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.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)