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 ShipwrightsTouch v1.4.0
plugins/ShipwrightsTouch.dll
Decompiled 2 weeks 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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using MagicaCloth2; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using malafein.Valheim.Shared; using malafein.Valheim.SharedUI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")] [assembly: AssemblyCompany("ShipwrightsTouch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Extra features for Valheim boats: naming and sail coloring.")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0+6d79c464f6b846169a657a492a3d1c52fa47843d")] [assembly: AssemblyProduct("ShipwrightsTouch")] [assembly: AssemblyTitle("ShipwrightsTouch")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace malafein.Valheim.SharedUI { internal static class ModalPanels { [HarmonyPatch] private static class Patches { [HarmonyPatch(typeof(InventoryGui), "IsVisible")] [HarmonyPostfix] private static void IsVisiblePostfix(ref bool __result) { if (!__result && AnyOpen) { __result = true; } } [HarmonyPatch(typeof(InventoryGui), "Update")] [HarmonyPrefix] private static void UpdatePrefix() { if (AnyOpen) { ZInput.ResetButtonStatus("Inventory"); ZInput.ResetButtonStatus("JoyButtonY"); } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] [HarmonyPostfix] private static void TakeInputPostfix(ref bool __result) { if (__result && AnyOpen && TextFieldFocused) { __result = false; } } } private static readonly List<Func<bool>> s_panels = new List<Func<bool>>(); public static bool AnyOpen { get { foreach (Func<bool> s_panel in s_panels) { if (s_panel()) { return true; } } return false; } } public static bool TextFieldFocused { get { GameObject val = (((Object)(object)EventSystem.current != (Object)null) ? EventSystem.current.currentSelectedGameObject : null); if ((Object)(object)val == (Object)null) { return false; } TMP_InputField component = val.GetComponent<TMP_InputField>(); if ((Object)(object)component != (Object)null) { return component.isFocused; } return false; } } public static void Register(Func<bool> isOpen) { if (isOpen != null && !s_panels.Contains(isOpen)) { s_panels.Add(isOpen); } } public static void Unregister(Func<bool> isOpen) { s_panels.Remove(isOpen); } } internal static class UIBuilder { public const float DefaultScrollSensitivity = 300f; public static RectTransform MakeChildRect(Transform parent, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); return (RectTransform)val.transform; } public static TextMeshProUGUI AddText(RectTransform rt, string text, TMP_FontAsset font, float fontSize, TextAlignmentOptions alignment) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = ((Component)rt).gameObject; bool activeSelf = gameObject.activeSelf; if (activeSelf) { gameObject.SetActive(false); } TextMeshProUGUI val = gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val).text = text; ((TMP_Text)val).alignment = alignment; ((Graphic)val).color = UIPalette.BodyTextColor; ((TMP_Text)val).fontSize = fontSize; if ((Object)(object)font != (Object)null) { ((TMP_Text)val).font = font; } if ((Object)(object)font != (Object)null && (Object)(object)font == (Object)(object)VanillaUI.BodyFont && (Object)(object)VanillaUI.BodyMaterial != (Object)null) { ((TMP_Text)val).fontSharedMaterial = VanillaUI.BodyMaterial; } if (activeSelf) { gameObject.SetActive(true); } return val; } public static Button AddButton(Transform parent, string name, string label, UnityAction onClick, float fontSize = 18f) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); val.transform.SetParent(parent, false); Button component = val.GetComponent<Button>(); VanillaUI.StyleButton(component, val.GetComponent<Image>()); if (onClick != null) { ((UnityEvent)component.onClick).AddListener(onClick); } RectTransform rt = MakeChildRect(val.transform, "Label"); Stretch(rt); VanillaUI.ApplyButtonLabelStyle((TMP_Text)(object)AddText(rt, label, VanillaUI.BodyFont, fontSize, (TextAlignmentOptions)514)); return component; } public static void Stretch(RectTransform rt, float inset = 0f) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = new Vector2(inset, inset); rt.offsetMax = new Vector2(0f - inset, 0f - inset); } public static RectTransform BuildScrollableList(Transform parent, string name, out GameObject scrollRoot, float scrollSensitivity = 300f) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown RectTransform val = MakeChildRect(parent, name); val.anchorMin = Vector2.zero; val.anchorMax = Vector2.one; val.offsetMin = Vector2.zero; val.offsetMax = Vector2.zero; scrollRoot = ((Component)val).gameObject; ScrollRect val2 = scrollRoot.AddComponent<ScrollRect>(); val2.horizontal = false; val2.vertical = true; val2.movementType = (MovementType)2; val2.scrollSensitivity = scrollSensitivity; RectTransform val3 = MakeChildRect((Transform)(object)val, "Viewport"); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = Vector2.zero; val3.offsetMax = new Vector2(-16f, 0f); ((Graphic)((Component)val3).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0f); ((Component)val3).gameObject.AddComponent<RectMask2D>(); RectTransform val4 = MakeChildRect((Transform)(object)val3, "Content"); val4.anchorMin = new Vector2(0f, 1f); val4.anchorMax = new Vector2(1f, 1f); val4.pivot = new Vector2(0.5f, 1f); val4.sizeDelta = Vector2.zero; VerticalLayoutGroup obj = ((Component)val4).gameObject.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 4f; ((LayoutGroup)obj).padding = new RectOffset(5, 5, 5, 5); ((LayoutGroup)obj).childAlignment = (TextAnchor)1; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((Component)val4).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; val2.viewport = val3; val2.content = val4; Scrollbar val5 = VanillaUI.CloneScrollbar(val, 12f); if ((Object)(object)val5 != (Object)null) { val2.verticalScrollbar = val5; val2.verticalScrollbarVisibility = (ScrollbarVisibility)1; } return val4; } } internal static class UIPalette { public static readonly Color BodyTextColor = new Color(0.9f, 0.9f, 0.9f, 1f); public const string HeaderColorTag = "orange"; public const string ValueColorTag = "orange"; public const string LabelColorTag = "yellow"; public static readonly Color AccentGold = new Color(1f, 0.718f, 0.36f, 1f); public static string Colored(string colorTag, string s) { return "<color=" + colorTag + ">" + s + "</color>"; } public static string Header(string s) { return "<b>" + Colored("orange", s) + "</b>"; } public static string Label(string s) { return Colored("yellow", s); } public static string HighlightValues(string s) { return Regex.Replace(s, "\\d+%", (Match m) => Colored("orange", m.Value)); } } internal static class VanillaUI { private static bool _resolved; private static TMP_FontAsset _bodyFont; private static Material _bodyMaterial; private static bool _hasPanel; private static Sprite _panelSprite; private static Color _panelColor; private static Material _panelMaterial; private static bool _hasButton; private static Sprite _buttonSprite; private static Transition _btnTransition; private static ColorBlock _btnColors; private static SpriteState _btnSpriteState; private static bool _hasButtonLabel; private static TMP_FontAsset _buttonLabelFont; private static Material _buttonLabelMaterial; private static Color _buttonLabelColor; private static FontStyles _buttonLabelStyle; private static TMP_FontAsset _titleFont; private static Material _titleMaterial; private static Color _titleColor; private static bool _hasTitleColor; private static FontStyles _titleStyle; private static Scrollbar _scrollbarTemplate; private static GuiInputField _inputFieldTemplate; public static TMP_FontAsset BodyFont { get { Resolve(); return _bodyFont; } } public static Material BodyMaterial { get { Resolve(); return _bodyMaterial; } } public static bool ApplyPanelBackground(Image dst) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) Resolve(); if ((Object)(object)dst == (Object)null || !_hasPanel) { return false; } dst.sprite = _panelSprite; dst.type = (Type)1; ((Graphic)dst).color = _panelColor; if ((Object)(object)_panelMaterial != (Object)null) { ((Graphic)dst).material = _panelMaterial; } return true; } public static void StyleButton(Button btn, Image img) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Resolve(); if ((Object)(object)img == (Object)null) { return; } if (_hasButton) { img.sprite = _buttonSprite; img.type = (Type)1; ((Graphic)img).color = ((ColorBlock)(ref _btnColors)).normalColor; if ((Object)(object)btn != (Object)null) { ((Selectable)btn).targetGraphic = (Graphic)(object)img; ((Selectable)btn).transition = _btnTransition; ((Selectable)btn).colors = _btnColors; ((Selectable)btn).spriteState = _btnSpriteState; } } else { ((Graphic)img).color = new Color(0.18f, 0.18f, 0.18f, 1f); if ((Object)(object)btn != (Object)null) { ((Selectable)btn).targetGraphic = (Graphic)(object)img; ((Selectable)btn).transition = (Transition)0; } } } public static void SetButtonActive(Button btn, bool active) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) Resolve(); if ((Object)(object)btn == (Object)null) { return; } Graphic targetGraphic = ((Selectable)btn).targetGraphic; Image val = (Image)(object)((targetGraphic is Image) ? targetGraphic : null); Transition val2 = (Transition)(_hasButton ? ((int)_btnTransition) : 0); if ((int)val2 != 1) { if ((int)val2 == 2) { if ((Object)(object)val != (Object)null) { val.sprite = ((active && (Object)(object)((SpriteState)(ref _btnSpriteState)).highlightedSprite != (Object)null) ? ((SpriteState)(ref _btnSpriteState)).highlightedSprite : _buttonSprite); } } else if ((Object)(object)val != (Object)null) { ((Graphic)val).color = (Color)(active ? Color.white : new Color(0.6f, 0.6f, 0.6f, 1f)); } } else { ColorBlock colors = ((Selectable)btn).colors; ((ColorBlock)(ref colors)).normalColor = (active ? ((ColorBlock)(ref _btnColors)).highlightedColor : ((ColorBlock)(ref _btnColors)).normalColor); ((Selectable)btn).colors = colors; if ((Object)(object)val != (Object)null) { ((Graphic)val).color = ((ColorBlock)(ref colors)).normalColor; } } } public static void ApplyButtonLabelStyle(TMP_Text t) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) Resolve(); if (!((Object)(object)t == (Object)null) && _hasButtonLabel) { if ((Object)(object)_buttonLabelFont != (Object)null) { t.font = _buttonLabelFont; } if ((Object)(object)_buttonLabelMaterial != (Object)null) { t.fontSharedMaterial = _buttonLabelMaterial; } ((Graphic)t).color = _buttonLabelColor; t.fontStyle = _buttonLabelStyle; } } public static void ApplyTitleStyle(TMP_Text t) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Resolve(); if (!((Object)(object)t == (Object)null)) { if ((Object)(object)_titleFont != (Object)null) { t.font = _titleFont; } if ((Object)(object)_titleMaterial != (Object)null) { t.fontSharedMaterial = _titleMaterial; } if (_hasTitleColor) { ((Graphic)t).color = _titleColor; } t.fontStyle = _titleStyle; } } public static Scrollbar CloneScrollbar(RectTransform parent, float width) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown Resolve(); if ((Object)(object)_scrollbarTemplate == (Object)null) { ResolveScrollbar(); } if ((Object)(object)_scrollbarTemplate == (Object)null) { return null; } GameObject obj = Object.Instantiate<GameObject>(((Component)_scrollbarTemplate).gameObject, (Transform)(object)parent); ((Object)obj).name = "Scrollbar"; obj.SetActive(true); Scrollbar component = obj.GetComponent<Scrollbar>(); if ((Object)(object)component != (Object)null) { component.onValueChanged = new ScrollEvent(); component.direction = (Direction)2; } RectTransform val = (RectTransform)obj.transform; val.anchorMin = new Vector2(1f, 0f); val.anchorMax = new Vector2(1f, 1f); val.pivot = new Vector2(1f, 0.5f); val.sizeDelta = new Vector2(width, 0f); val.anchoredPosition = Vector2.zero; return component; } public static TMP_InputField CloneInputField(RectTransform parent) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown Resolve(); if ((Object)(object)_inputFieldTemplate == (Object)null) { ResolveInputField(); } if ((Object)(object)_inputFieldTemplate == (Object)null) { return null; } GameObject obj = Object.Instantiate<GameObject>(((Component)_inputFieldTemplate).gameObject, (Transform)(object)parent); ((Object)obj).name = "InputField"; obj.SetActive(true); GuiInputField component = obj.GetComponent<GuiInputField>(); ((TMP_InputField)component).onValueChanged = new OnChangeEvent(); ((TMP_InputField)component).onEndEdit = new SubmitEvent(); ((TMP_InputField)component).onSubmit = new SubmitEvent(); ((TMP_InputField)component).onSelect = new SelectionEvent(); ((TMP_InputField)component).onDeselect = new SelectionEvent(); component.OnInputSubmit = new OnInputSubmitEvent(); ((TMP_InputField)component).lineType = (LineType)0; ((TMP_InputField)component).characterLimit = 0; ((TMP_InputField)component).text = ""; return (TMP_InputField)(object)component; } private static void Resolve() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!_resolved) { _resolved = true; ResolveFonts(); ResolvePanelBackground(); ResolveButton(); ResolveScrollbar(); ResolveInputField(); Log.Debug("VanillaUI resolved: title=" + Name(_titleFont) + " body=" + Name(_bodyFont) + " panel=" + (_hasPanel ? $"{((Object)_panelSprite).name} colour={_panelColor}" : "<flat>") + " button=" + (_hasButton ? $"{((Object)_buttonSprite).name} transition={_btnTransition}" : "<flat>") + " inputField=" + (((Object)(object)_inputFieldTemplate != (Object)null) ? "vanilla" : "<none>")); } } private static void ResolveFonts() { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) InventoryGui val = ResolveInventoryGui(); if ((Object)(object)val != (Object)null) { CaptureTitle(val.m_craftingStationName); } if ((Object)(object)_titleFont == (Object)null && (Object)(object)val != (Object)null) { CaptureTitle(val.m_recipeName); } TextsDialog[] array = Resources.FindObjectsOfTypeAll<TextsDialog>(); if (array.Length != 0) { TextsDialog val2 = array[0]; if ((Object)(object)val2.m_textArea != (Object)null) { _bodyFont = val2.m_textArea.font; _bodyMaterial = val2.m_textArea.fontSharedMaterial; } if ((Object)(object)_titleFont == (Object)null) { CaptureTitle(val2.m_textAreaTopic); } } if ((Object)(object)_bodyFont == (Object)null) { _bodyFont = FallbackBodyFont(); } if ((Object)(object)_titleFont == (Object)null) { _titleFont = _bodyFont; _titleMaterial = (((Object)(object)_bodyFont != (Object)null) ? ((TMP_Asset)_bodyFont).material : null); _titleStyle = (FontStyles)1; } } private static void CaptureTitle(TMP_Text donor) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)donor == (Object)null) && !((Object)(object)donor.font == (Object)null)) { _titleFont = donor.font; _titleMaterial = donor.fontSharedMaterial; _titleColor = ((Graphic)donor).color; _hasTitleColor = true; _titleStyle = donor.fontStyle; } } private static TMP_FontAsset FallbackBodyFont() { if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_healthText != (Object)null && (Object)(object)Hud.instance.m_healthText.font != (Object)null) { return Hud.instance.m_healthText.font; } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); TMP_FontAsset[] array2 = array; foreach (TMP_FontAsset val in array2) { if ((Object)(object)val != (Object)null && ((Object)val).name.Contains("Averia")) { return val; } } if (array.Length == 0) { return null; } return array[0]; } private static void ResolvePanelBackground() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Image[] array = Resources.FindObjectsOfTypeAll<Image>(); foreach (Image val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sprite == (Object)null) && ((Object)val.sprite).name.ToLowerInvariant().Contains("woodpanel_trophys")) { _panelSprite = val.sprite; _panelColor = ((Graphic)val).color; _panelMaterial = ((Graphic)val).material; _hasPanel = true; return; } } Sprite[] array2 = Resources.FindObjectsOfTypeAll<Sprite>(); foreach (Sprite val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrEmpty(((Object)val2).name) && ((Object)val2).name.ToLowerInvariant().Contains("woodpanel")) { _panelSprite = val2; _panelColor = Color.white; _hasPanel = true; break; } } } private static void ResolveButton() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) InventoryGui val = ResolveInventoryGui(); if ((Object)(object)val == (Object)null) { return; } Button val2 = (((Object)(object)val.m_craftButton != (Object)null) ? val.m_craftButton : val.m_tabUpgrade); if (!((Object)(object)val2 == (Object)null)) { Image component = ((Component)val2).GetComponent<Image>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) { _buttonSprite = component.sprite; _btnTransition = ((Selectable)val2).transition; _btnColors = ((Selectable)val2).colors; _btnSpriteState = ((Selectable)val2).spriteState; _hasButton = true; } TMP_Text componentInChildren = ((Component)val2).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.font != (Object)null) { _buttonLabelFont = componentInChildren.font; _buttonLabelMaterial = componentInChildren.fontSharedMaterial; _buttonLabelColor = ((Graphic)componentInChildren).color; _buttonLabelStyle = componentInChildren.fontStyle; _hasButtonLabel = true; } } } private static void ResolveScrollbar() { TextsDialog[] array = Resources.FindObjectsOfTypeAll<TextsDialog>(); if (array.Length != 0) { TextsDialog val = array[0]; _scrollbarTemplate = (((Object)(object)val.m_rightScrollbar != (Object)null) ? val.m_rightScrollbar : val.m_leftScrollbar); } } private static void ResolveInputField() { TextInput val = TextInput.instance; if ((Object)(object)val == (Object)null) { TextInput[] array = Resources.FindObjectsOfTypeAll<TextInput>(); if (array.Length != 0) { val = array[0]; } } if ((Object)(object)val != (Object)null) { _inputFieldTemplate = val.m_inputField; } } private static InventoryGui ResolveInventoryGui() { if ((Object)(object)InventoryGui.instance != (Object)null) { return InventoryGui.instance; } InventoryGui[] array = Resources.FindObjectsOfTypeAll<InventoryGui>(); if (array.Length == 0) { return null; } return array[0]; } private static string Name(TMP_FontAsset f) { if (!((Object)(object)f != (Object)null)) { return "<none>"; } return ((Object)f).name; } } } namespace malafein.Valheim.Shared { internal enum ModifierMatch { Exact, AtLeast } internal static class Keybinds { private class Binding { public string Name; public Func<KeyboardShortcut> Shortcut; public string Context; } [HarmonyPatch] private static class Patches { [HarmonyPatch(typeof(ZInput), "Load")] [HarmonyPostfix] private static void Postfix_ZInputLoad(ZInput __instance) { CheckConflicts(__instance); } [HarmonyPatch(typeof(ZInput), "Save")] [HarmonyPostfix] private static void Postfix_ZInputSave(ZInput __instance) { CheckConflicts(__instance); } [HarmonyPatch(typeof(ZInput), "ResetToDefault")] [HarmonyPostfix] private static void Postfix_ZInputResetToDefault(ZInput __instance) { CheckConflicts(__instance); } } private const string DefaultContext = ""; private static readonly MethodInfo TakeInputMethod = AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null); private static readonly FieldInfo ButtonsField = AccessTools.Field(typeof(ZInput), "m_buttons"); private static readonly MethodInfo KeyCodeToPathMethod = AccessTools.Method(typeof(ZInput), "KeyCodeToPath", (Type[])null, (Type[])null); private static readonly KeyCode[] ModifierKeys; private static readonly Dictionary<KeyCode, string> ModifierLabels; private static readonly string[] RawMouseButtons; private static readonly List<Binding> s_bindings; private static readonly HashSet<string> s_ignoredVanillaButtons; private static List<string> s_lastConflicts; internal static void Init(ConfigFile config, params string[] ignoredVanillaButtons) { s_ignoredVanillaButtons.UnionWith(ignoredVanillaButtons); config.SettingChanged += delegate { CheckConflicts(); }; } internal static void Add(ConfigEntry<KeyboardShortcut> entry, string context = null) { Add(((ConfigEntryBase)entry).Definition.Key, () => entry.Value, context); } internal static void Add(string name, Func<KeyboardShortcut> shortcut, string context = null) { s_bindings.Add(new Binding { Name = name, Shortcut = shortcut, Context = (context ?? "") }); CheckConflicts(); } internal static bool IsDown(KeyboardShortcut shortcut, ModifierMatch match = ModifierMatch.Exact) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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) KeyCode mainKey = ((KeyboardShortcut)(ref shortcut)).MainKey; if ((int)mainKey == 0 || !ZInput.IsKeyCodeValid(mainKey)) { return false; } if (!ZInput.GetKeyDown(mainKey, false)) { return false; } if (!((KeyboardShortcut)(ref shortcut)).Modifiers.All((KeyCode key) => ZInput.GetKey(key, false))) { return false; } if (match == ModifierMatch.AtLeast) { return true; } return ModifierKeys.All((KeyCode key) => ((KeyboardShortcut)(ref shortcut)).Modifiers.Contains(key) || !ZInput.GetKey(key, false)); } internal static KeyCode[] HeldModifiers() { return ModifierKeys.Where((KeyCode key) => ZInput.GetKey(key, false)).ToArray(); } internal static bool CanTakeInput(Player player) { return (bool)TakeInputMethod.Invoke(player, null); } internal unsafe static string Format(KeyboardShortcut shortcut) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return "Not set"; } List<string> list = new List<string>(); list.AddRange(from key in ModifierKeys where ((KeyboardShortcut)(ref shortcut)).Modifiers.Contains(key) select ModifierLabels[key]); list.AddRange(from key in ((KeyboardShortcut)(ref shortcut)).Modifiers where !ModifierLabels.ContainsKey(key) select ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString()); list.Add(((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString()); return string.Join(" + ", list); } private static string GetBoundPath(ButtonDef button) { try { return button.GetActionPath(true); } catch (ArgumentOutOfRangeException) { return null; } } internal static void CheckConflicts(ZInput input = null) { try { FindAndLogConflicts(input ?? ZInput.instance); } catch (Exception ex) { Log.Warn("Keybinding conflict check failed: " + ex.Message); } } private static void FindAndLogConflicts(ZInput input) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) if (input == null || ButtonsField == null || KeyCodeToPathMethod == null || !(ButtonsField.GetValue(input) is Dictionary<string, ButtonDef> dictionary)) { return; } List<string> list = new List<string>(); List<(Binding, KeyboardShortcut)> list2 = (from b in s_bindings select (Binding: b, Shortcut: b.Shortcut()) into b where (int)((KeyboardShortcut)(ref b.Shortcut)).MainKey > 0 select b).ToList(); foreach (var item3 in list2) { Binding item = item3.Item1; KeyboardShortcut item2 = item3.Item2; KeyCode mainKey = ((KeyboardShortcut)(ref item2)).MainKey; if (!ZInput.IsKeyCodeValid(mainKey)) { list.Add($"{item.Name} ({Format(item2)}) uses {mainKey}, which Valheim cannot read. The shortcut will never fire."); continue; } string path = (string)KeyCodeToPathMethod.Invoke(null, new object[2] { mainKey, false }); foreach (string item4 in (from b in dictionary.Values where !s_ignoredVanillaButtons.Contains(b.Name) where string.Equals(GetBoundPath(b), path, StringComparison.OrdinalIgnoreCase) select b.Name).Distinct()) { list.Add($"{item.Name} ({Format(item2)}) shares {mainKey} with the game's \"{item4}\" binding. Both will trigger."); } } foreach (IGrouping<(string, string), (Binding, KeyboardShortcut)> item5 in from b in list2 group b by (Context: b.Binding.Context, Keys: Format(b.Shortcut)) into g where g.Count() > 1 select g) { string text = string.Join(" and ", item5.Select<(Binding, KeyboardShortcut), string>(((Binding Binding, KeyboardShortcut Shortcut) b) => b.Binding.Name)); list.Add(text + " are bound to the same keys (" + item5.Key.Item2 + "). They will trigger at once."); } if (list.SequenceEqual(s_lastConflicts)) { return; } foreach (string item6 in list) { Log.Warn("Keybinding conflict: " + item6); } if (list.Count == 0 && s_lastConflicts.Count > 0) { Log.Info("Keybinding conflicts resolved."); } s_lastConflicts = list; } static Keybinds() { KeyCode[] array = new KeyCode[6]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); ModifierKeys = (KeyCode[])(object)array; ModifierLabels = new Dictionary<KeyCode, string> { { (KeyCode)306, "Left Ctrl" }, { (KeyCode)305, "Right Ctrl" }, { (KeyCode)304, "Left Shift" }, { (KeyCode)303, "Right Shift" }, { (KeyCode)308, "Left Alt" }, { (KeyCode)307, "Right Alt" } }; RawMouseButtons = new string[5] { "MouseLeft", "MouseRight", "MouseMiddle", "MouseForward", "MouseBack" }; s_bindings = new List<Binding>(); s_ignoredVanillaButtons = new HashSet<string>(RawMouseButtons); s_lastConflicts = new List<string>(); } } internal static class Log { private static ManualLogSource s_logger; private static Func<bool> s_debugEnabled; private static ManualLogSource Logger { get { if (s_logger == null) { s_logger = Logger.CreateLogSource(typeof(Log).Assembly.GetName().Name); } return s_logger; } } internal static void Init(ManualLogSource logger, Func<bool> debugEnabled = null) { s_logger = logger; s_debugEnabled = debugEnabled; } internal static void Info(string message) { Logger.LogInfo((object)message); } internal static void Warn(string message) { Logger.LogWarning((object)message); } internal static void Error(string message) { Logger.LogError((object)message); } internal static void Debug(string message) { if (s_debugEnabled == null || s_debugEnabled()) { Logger.LogDebug((object)message); } } } } namespace malafein.Valheim.ShipwrightsTouch { [HarmonyPatch] public static class CapsizePatches { private const float HealthFloor = 0.1f; private const float HoldSeconds = 2f; private const float CapsizedUpY = 0.25f; private static Ship s_holdShip; private static float s_holdTime; [HarmonyPatch(typeof(Ship), "UpdateUpsideDmg")] [HarmonyPrefix] private static bool Prefix_UpdateUpsideDmg(Ship __instance, float dt, ref float ___m_upsideDownDmgTimer, IDestructible ___m_destructible) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (!SailNetwork.RescueCapsizedShips) { return true; } if (((Component)__instance).transform.up.y >= 0f) { return false; } ___m_upsideDownDmgTimer += dt; if (___m_upsideDownDmgTimer <= __instance.m_upsideDownDmgInterval) { return false; } ___m_upsideDownDmgTimer = 0f; HitData val = new HitData(); val.m_damage.m_blunt = __instance.m_upsideDownDmg; val.m_point = ((Component)__instance).transform.position; val.m_dir = Vector3.up; if (!CapToFloor(__instance, val)) { return false; } ___m_destructible.Damage(val); return false; } private static bool CapToFloor(Ship ship, HitData hit) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) WearNTear component = ((Component)ship).GetComponent<WearNTear>(); ZNetView component2 = ((Component)ship).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid()) { return true; } float num = component2.GetZDO().GetFloat(ZDOVars.s_health, component.m_health) - component.m_health * 0.1f; if (num < 1f) { return false; } HitData obj = hit.Clone(); DamageModifier val = default(DamageModifier); obj.ApplyResistance(component.m_damages, ref val); float totalDamage = obj.GetTotalDamage(); if (totalDamage > num) { hit.m_damage.m_blunt *= num / totalDamage; Log.Debug($"Capsize damage stopped at {0.1f:P0} of the ship's health ({component.m_health * 0.1f:F0} of {component.m_health:F0})."); } return true; } public static bool IsCapsized(Ship ship) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ((Component)ship).transform.up.y < 0.25f; } private static Ship HoveredCapsizedShip(Player player) { if (!SailNetwork.RescueCapsizedShips) { return null; } GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null || hoverObject.GetComponent<Interactable>() != null) { return null; } Ship component = hoverObject.GetComponent<Ship>(); if ((Object)(object)component == (Object)null || !IsCapsized(component) || (Object)(object)((Component)component).GetComponent<ShipRighting>() != (Object)null) { return null; } ZNetView component2 = ((Component)component).GetComponent<ZNetView>(); if (!((Object)(object)component2 != (Object)null) || !component2.IsValid()) { return null; } return component; } public static void AppendRescuePrompt(Component hull, ref string result) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Ship val = HoveredCapsizedShip(localPlayer); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject != (Object)(object)hull.gameObject)) { string text = (((Object)(object)s_holdShip == (Object)(object)val && s_holdTime > 0f) ? $"Righting the ship... {Mathf.FloorToInt(s_holdTime / 2f * 100f)}%" : Localization.instance.Localize("[<color=yellow><b>$KEY_Use</b></color>] Hold to right the ship")); result = (string.IsNullOrEmpty(result) ? text : (result + "\n" + text)); } } } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void Postfix_PlayerUpdate(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } Ship val = HoveredCapsizedShip(__instance); bool flag = (Object)(object)val != (Object)null && (ZInput.GetButton("Use") || ZInput.GetButton("JoyUse")) && !Hud.InRadial() && Keybinds.CanTakeInput(__instance); if (!flag || (Object)(object)val != (Object)(object)s_holdShip) { s_holdShip = (flag ? val : null); s_holdTime = 0f; if (!flag) { return; } } s_holdTime += Time.deltaTime; if (!(s_holdTime < 2f)) { s_holdShip = null; s_holdTime = 0f; Log.Info("Righting a capsized ship."); ShipRighting.Begin(val); } } } public class ShipRighting : MonoBehaviour { private const float Duration = 1.5f; private const float Lift = 1f; private ZNetView _nview; private Rigidbody _body; private Quaternion _from; private Quaternion _to; private Vector3 _start; private float _time; public static void Begin(Ship ship) { ZNetView component = ((Component)ship).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { component.ClaimOwnership(); ((Component)ship).gameObject.AddComponent<ShipRighting>(); } } private void Awake() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) _nview = ((Component)this).GetComponent<ZNetView>(); _body = ((Component)this).GetComponent<Rigidbody>(); _from = ((Component)this).transform.rotation; _start = ((Component)this).transform.position; Vector3 val = Vector3.ProjectOnPlane(((Component)this).transform.forward, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.ProjectOnPlane(-((Component)this).transform.up, Vector3.up); } if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.forward; } _to = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } private void FixedUpdate() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner() || (Object)(object)_body == (Object)null) { Object.Destroy((Object)(object)this); return; } _time += Time.fixedDeltaTime; float num = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(_time / 1.5f)); _body.MoveRotation(Quaternion.Slerp(_from, _to, num)); _body.MovePosition(new Vector3(_body.position.x, Mathf.Max(_body.position.y, _start.y + 1f * num), _body.position.z)); _body.angularVelocity = Vector3.zero; if (_time >= 1.5f) { Object.Destroy((Object)(object)this); } } } [HarmonyPatch] public static class ColoringPatches { private static int m_selectedStyle; private static bool m_isPlacing; [HarmonyPatch(typeof(Player), "UpdatePlacement")] [HarmonyPrefix] private static void Prefix_UpdatePlacement(Player __instance, bool takeInput, float dt, GameObject ___m_placementGhost) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)___m_placementGhost == (Object)null) { return; } Ship component = ___m_placementGhost.GetComponent<Ship>(); if (!((Object)(object)component == (Object)null)) { ApplyStyle(component, m_selectedStyle); if (takeInput && Keybinds.IsDown(Plugin.SailColorPlacingKey.Value)) { m_selectedStyle = (m_selectedStyle + 1) % SailStyle.Presets.Length; MessageHud.instance.ShowMessage((MessageType)2, "Sail Color: <color=yellow>" + SailStyle.PresetNames[m_selectedStyle] + "</color>", 0, (Sprite)null, false, true); } } } [HarmonyPatch(typeof(Player), "PlacePiece")] [HarmonyPrefix] private static void Prefix_PlacePiece(Player __instance, Piece piece) { if ((Object)(object)piece != (Object)null && (Object)(object)((Component)piece).GetComponent<Ship>() != (Object)null) { m_isPlacing = true; } } [HarmonyPatch(typeof(Player), "PlacePiece")] [HarmonyPostfix] private static void Postfix_PlacePiece() { if (m_isPlacing) { m_isPlacing = false; } } [HarmonyPatch(typeof(Ship), "Awake")] [HarmonyPostfix] private static void Postfix_ShipAwake(Ship __instance) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } if (m_isPlacing && component.IsOwner()) { Log.Debug($"Setting initial sail style {m_selectedStyle} for new ship: {((Object)((Component)__instance).gameObject).name}"); component.GetZDO().Set("custom_sail_style", m_selectedStyle); if (Plugin.AssignBuilderIdentity.Value && (Object)(object)Player.m_localPlayer != (Object)null) { component.GetZDO().Set("shipwrightstouch.builder_id", Player.m_localPlayer.GetPlayerID()); component.GetZDO().Set("shipwrightstouch.builder_name", Player.m_localPlayer.GetPlayerName()); } } UpdateSailAppearance(__instance); } [HarmonyPatch(typeof(Ship), "Start")] [HarmonyPostfix] private static void Postfix_ShipStart(Ship __instance) { UpdateSailAppearance(__instance); } internal static void UpdateSailAppearance(Ship ship) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (CustomizePanel.TryGetPreview(ship, out var color, out var texture)) { SailAppearance.Apply(ship, color, texture); return; } ZNetView component = ((Component)ship).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { ZDO zDO = component.GetZDO(); if (SailStyle.TryGetColor(zDO, out var color2)) { Texture2D texture2 = (SailNetwork.Policy.AllowCustomTextures ? SailTextures.Get(zDO.GetString("shipwrightstouch.sail_texture", "")) : null); SailAppearance.Apply(ship, color2, texture2); } } } private static void ApplyStyle(Ship ship, int style) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (style >= 0 && style < SailStyle.Presets.Length) { SailAppearance.Apply(ship, SailStyle.Presets[style], null); } } [HarmonyPatch(typeof(Ship), "UpdateSail")] [HarmonyPostfix] private static void Postfix_UpdateSail(Ship __instance) { UpdateSailAppearance(__instance); } [HarmonyPatch(typeof(Ship), "UpdateSailSize")] [HarmonyPostfix] private static void Postfix_UpdateSailSize(Ship __instance) { UpdateSailAppearance(__instance); } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void Postfix_PlayerUpdate(Player __instance) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || TextInput.IsVisible() || !Keybinds.IsDown(Plugin.SailColorShipKey.Value) || !Keybinds.CanTakeInput(__instance)) { return; } GameObject hoverObject = ((Humanoid)__instance).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null || (Object)(object)hoverObject.GetComponentInParent<Container>() != (Object)null) { return; } Ship parentShip = NamingPatches.GetParentShip(hoverObject.GetComponent<Component>()); if ((Object)(object)parentShip == (Object)null) { return; } if (Plugin.CanModifyShip(parentShip, out var ownerName)) { ZNetView component = ((Component)parentShip).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { int num = (component.GetZDO().GetInt("custom_sail_style", 0) + 1) % SailStyle.Presets.Length; SailStyle.SetPreset(component.GetZDO(), num); UpdateSailAppearance(parentShip); MessageHud.instance.ShowMessage((MessageType)2, "Sail Color: <color=yellow>" + SailStyle.PresetNames[num] + "</color>", 0, (Sprite)null, false, true); } } else { MessageHud.instance.ShowMessage((MessageType)2, "Only " + ownerName + " can change this ship's sail color.", 0, (Sprite)null, false, true); } } } [HarmonyPatch] public static class DeconstructionPatches { [HarmonyPatch(typeof(Player), "CheckCanRemovePiece")] [HarmonyPrefix] private static void Prefix_CheckCanRemovePiece(ref Piece piece) { if ((Object)(object)piece == (Object)null || !Plugin.AllowShipDeconstruction.Value) { return; } Ship componentInParent = ((Component)piece).GetComponentInParent<Ship>(); string ownerName; if ((Object)(object)componentInParent == (Object)null) { if (piece.m_name.Contains("karve") || piece.m_name.Contains("longship") || piece.m_name.Contains("raft")) { Log.Warn("Found ship piece " + piece.m_name + " but no Ship component in parent."); } } else if (Plugin.CanModifyShip(componentInParent, out ownerName) && !piece.m_canBeRemoved) { piece.m_canBeRemoved = true; Log.Debug("Force-allowing deconstruction of ship piece: " + piece.m_name); } } [HarmonyPatch(typeof(Player), "RemovePiece")] [HarmonyPrefix] private static bool Prefix_RemovePiece(Player __instance) { if (!Plugin.AllowShipDeconstruction.Value) { return true; } GameObject hoverObject = ((Humanoid)__instance).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return true; } Ship componentInParent = hoverObject.GetComponentInParent<Ship>(); if ((Object)(object)componentInParent == (Object)null) { return true; } ItemData currentWeapon = ((Humanoid)__instance).GetCurrentWeapon(); if (currentWeapon == null || (Object)(object)currentWeapon.m_shared.m_buildPieces == (Object)null) { return true; } Piece component = ((Component)componentInParent).GetComponent<Piece>(); if ((Object)(object)component == (Object)null) { return true; } if (!Plugin.CanModifyShip(componentInParent, out var ownerName)) { MessageHud.instance.ShowMessage((MessageType)2, "Only " + ownerName + " can deconstruct this ship.", 0, (Sprite)null, false, true); return false; } if (!component.m_canBeRemoved) { component.m_canBeRemoved = true; } return true; } } [HarmonyPatch] public static class EnemyTargetPatches { [HarmonyPatch(typeof(Piece), "IsPriorityTarget")] [HarmonyPostfix] private static void Postfix_IsPriorityTarget(Piece __instance, ref bool __result) { if (__result && IsUnoccupiedShip(__instance)) { __result = false; } } [HarmonyPatch(typeof(Piece), "IsRandomTarget")] [HarmonyPostfix] private static void Postfix_IsRandomTarget(Piece __instance, ref bool __result) { if (__result && IsUnoccupiedShip(__instance)) { __result = false; } } private static bool IsUnoccupiedShip(Piece piece) { if (!SailNetwork.EnemiesIgnoreUnoccupiedShips) { return false; } Ship component = ((Component)piece).GetComponent<Ship>(); if ((Object)(object)component != (Object)null) { return component.CanBeRemoved(); } return false; } } [HarmonyPatch] public static class HoverTextPatches { [HarmonyPatch(typeof(HoverText), "GetHoverText")] [HarmonyPostfix] [HarmonyPriority(100)] private static void Postfix_HoverText(HoverText __instance, ref string __result) { ApplyNamingHover((Component)(object)__instance, ref __result); } [HarmonyPatch(typeof(ShipControlls), "GetHoverText")] [HarmonyPostfix] [HarmonyPriority(100)] private static void Postfix_RudderHoverText(ShipControlls __instance, ref string __result) { ApplyNamingHover((Component)(object)__instance, ref __result); } [HarmonyPatch(typeof(Container), "GetHoverText")] [HarmonyPostfix] [HarmonyPriority(100)] private static void Postfix_ContainerHoverText(Container __instance, ref string __result) { ApplyNamingHover((Component)(object)__instance, ref __result); } [HarmonyPatch(typeof(Chair), "GetHoverText")] [HarmonyPostfix] [HarmonyPriority(100)] private static void Postfix_ChairHoverText(Chair __instance, ref string __result) { ApplyNamingHover((Component)(object)__instance, ref __result); } internal static void ApplyNamingHover(Component instance, ref string result) { Ship parentShip = NamingPatches.GetParentShip(instance); if ((Object)(object)parentShip == (Object)null) { return; } ZNetView component = ((Component)parentShip).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } string text = Plugin.PlainText(component.GetZDO().GetString("custom_ship_name", "")); string text2 = Plugin.PlainText(component.GetZDO().GetString("shipwrightstouch.builder_name", "")); if (!string.IsNullOrEmpty(text)) { string text3 = "<color=yellow>" + text + "</color>"; if (result == null || !result.StartsWith(text3)) { result = (string.IsNullOrEmpty(result) ? text3 : (text3 + "\n" + result)); } } if (instance is Container) { return; } if (Plugin.CanModifyShip(parentShip, out var _)) { AppendPrompt(ref result, Plugin.CustomizeShipKey, "Customize Ship"); AppendPrompt(ref result, Plugin.SailColorShipKey, "Change Sail Color"); } if (!string.IsNullOrEmpty(text2)) { string text4 = "<size=80%><color=#C0C0C0>Built by: " + text2 + "</color></size>"; if (result != null && !result.Contains("Built by:")) { result = result + "\n" + text4; } } } private static void AppendPrompt(ref string result, ConfigEntry<KeyboardShortcut> entry, string label) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = entry.Value; if (result != null && (int)((KeyboardShortcut)(ref value)).MainKey != 0) { string text = "[<color=yellow><b>" + Keybinds.Format(value) + "</b></color>] " + label; if (!result.Contains(text)) { result = result + "\n" + text; } } } } [HarmonyPatch] public static class NamingPatches { public static Ship GetParentShip(Component component) { if ((Object)(object)component == (Object)null) { return null; } Ship val = (Ship)(object)((component is Ship) ? component : null); if (val != null) { return val; } Ship componentInParent = component.GetComponentInParent<Ship>(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } Transform root = component.transform.root; if ((Object)(object)root != (Object)null) { return ((Component)root).GetComponentInChildren<Ship>(); } return null; } [HarmonyPatch(typeof(Player), "Interact")] [HarmonyPrefix] private static bool Prefix_PlayerInteract(Player __instance, GameObject go, bool hold, bool alt) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (hold || (Object)(object)go == (Object)null) { return true; } if (!Keybinds.IsDown(Plugin.CustomizeShipKey.Value) && !Keybinds.IsDown(Plugin.SailColorShipKey.Value)) { return true; } if ((Object)(object)go.GetComponentInParent<Container>() != (Object)null) { return true; } return (Object)(object)GetParentShip(go.GetComponent<Component>()) == (Object)null; } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void Postfix_PlayerUpdate(Player __instance) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || TextInput.IsVisible() || !Keybinds.IsDown(Plugin.CustomizeShipKey.Value) || !Keybinds.CanTakeInput(__instance)) { return; } GameObject hoverObject = ((Humanoid)__instance).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null || (Object)(object)hoverObject.GetComponentInParent<Container>() != (Object)null) { return; } Ship parentShip = GetParentShip(hoverObject.GetComponent<Component>()); if (!((Object)(object)parentShip == (Object)null)) { if (Plugin.CanModifyShip(parentShip, out var ownerName)) { CustomizePanel.Open(parentShip); } else if (SailModeration.CanModerate && IsPlayerTexture(parentShip)) { ModerationPanel.Open(((Component)parentShip).GetComponent<ZNetView>().GetZDO().GetString("shipwrightstouch.sail_texture", "")); } else { MessageHud.instance.ShowMessage((MessageType)2, "Only " + ownerName + " can customize this ship.", 0, (Sprite)null, false, true); } } } private static bool IsPlayerTexture(Ship ship) { ZNetView component = ((Component)ship).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } string text = component.GetZDO().GetString("shipwrightstouch.sail_texture", ""); if (text != "") { CatalogEntry catalogEntry = SailDownloads.Find(text); if (catalogEntry == null) { return true; } return catalogEntry.Source != TextureSource.Server; } return false; } } public class ShipHover : MonoBehaviour, Hoverable { [HarmonyPatch] private static class Patches { [HarmonyPatch(typeof(Ship), "Awake")] [HarmonyPostfix] private static void Postfix_ShipAwake(Ship __instance) { if (((Component)__instance).GetComponent<Hoverable>() == null) { ((Component)__instance).gameObject.AddComponent<ShipHover>(); } } } public string GetHoverText() { string result = ""; HoverTextPatches.ApplyNamingHover((Component)(object)this, ref result); CapsizePatches.AppendRescuePrompt((Component)(object)this, ref result); return result; } public string GetHoverName() { ZNetView component = ((Component)this).GetComponent<ZNetView>(); if (!((Object)(object)component != (Object)null) || !component.IsValid()) { return ""; } return Plugin.PlainText(component.GetZDO().GetString("custom_ship_name", "")); } public float GetHoverOffset() { return 0f; } } [BepInPlugin("com.malafein.shipwrightstouch", "Shipwright's Touch", "1.4.0")] public class Plugin : BaseUnityPlugin { public const string ModGUID = "com.malafein.shipwrightstouch"; public const string ModName = "Shipwright's Touch"; public const string ModVersion = "1.4.0"; public const string ZdoOwnerIdKey = "shipwrightstouch.builder_id"; public const string ZdoOwnerNameKey = "shipwrightstouch.builder_name"; public const string ZdoNameKey = "custom_ship_name"; public const string ZdoStyleKey = "custom_sail_style"; public const string ZdoTextureKey = "shipwrightstouch.sail_texture"; public const string ZdoColorKey = "shipwrightstouch.sail_color"; private const string ControlsSection = "Controls"; public static ConfigEntry<bool> AllowShipDeconstruction; public static ConfigEntry<bool> AssignBuilderIdentity; public static ConfigEntry<KeyboardShortcut> CustomizeShipKey; public static ConfigEntry<KeyboardShortcut> SailColorShipKey; public static ConfigEntry<KeyboardShortcut> SailColorPlacingKey; public static ConfigEntry<KeyboardShortcut> ModerationKey; public static ConfigEntry<float> ScrollSensitivity; public static ConfigEntry<bool> CompressTextures; public static ConfigEntry<bool> ShareTextures; public static ConfigEntry<bool> ShowOtherPlayersTextures; public static ConfigEntry<int> MaxTransferKBPerSecond; private readonly Harmony harmony = new Harmony("com.malafein.shipwrightstouch"); private void Awake() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) Log.Init(((BaseUnityPlugin)this).Logger); AllowShipDeconstruction = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowShipDeconstruction", false, "Allow deconstructing ships with the hammer (middle-mouse button)."); AssignBuilderIdentity = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AssignBuilderIdentity", true, "Automatically assign your character as the owner when constructing a ship, restricting modifications (renaming, recoloring, deconstruction) to yourself."); ScrollSensitivity = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScrollSensitivity", 300f, "Mouse-wheel scroll speed in the customization and moderation panels' lists. Higher scrolls faster. Applied each time a panel opens."); CompressTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CompressTextures", true, "Compress custom sail textures in video memory, like the game's own textures: about a quarter of the memory each, with slight artifacts on hard edges and smooth gradients. Turn off for exact images if you have memory to spare. Applies to textures loaded after the change (rejoin the world to reload them)."); ShareTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("Sharing", "ShareMyTextures", true, "On a server with this mod, share a texture from your sails folder with the server the first time you put it on a ship, so other players can see it (if the server allows it, possibly after a moderator's approval). When off, your textures show only for you."); ShowOtherPlayersTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("Sharing", "ShowOtherPlayersTextures", true, "Show and download the sail textures other players have shared on the server. When off, their ships show the vanilla sail to you. The server's own textures still show."); MaxTransferKBPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("Sharing", "MaxTransferKBPerSecond", 0, new ConfigDescription("Speed limit for sending sail textures, in KB per second: on a player's game for sharing a texture, on the server for sending textures to each player. 0 means no limit, as fast as the connection allows. The game already limits each connection to 150 KB/s, shared with world updates, so this can only slow transfers down, leaving more room for the world while a texture is sent. Choices: 0, 8, 16, 32, 64, 128; any other value counts as 0.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[6] { 0, 8, 16, 32, 64, 128 }), Array.Empty<object>())); MigrateRenameKey(); CustomizeShipKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "CustomizeShip", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Open the customization panel (name, sail color, sail texture) for the ship you are looking at (rudder, seats, mast, or hull)."); SailColorShipKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ChangeSailColor", KeyboardShortcut.Empty, "Quickly cycle the sail color of the ship you are looking at through the presets. Unbound by default; the customization panel covers it."); SailColorPlacingKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ChangeSailColorWhilePlacing", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), "Cycle the sail color of a ship while placing it with the hammer."); ModerationKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "OpenModeration", KeyboardShortcut.Empty, "Open the sail texture moderation panel (server moderators and admins only). Unbound by default; the customization panel has a button for it."); Keybinds.Init(((BaseUnityPlugin)this).Config, "Use", "TabLeft", "TabRight"); Keybinds.Add(CustomizeShipKey); Keybinds.Add(SailColorShipKey); Keybinds.Add(ModerationKey); Keybinds.Add(SailColorPlacingKey, "Placing"); SailPolicy.Bind(((BaseUnityPlugin)this).Config); SailNetwork.Init(((BaseUnityPlugin)this).Config); SailTextures.Refresh(); Log.Info("Shipwright's Touch 1.4.0 is loading..."); try { harmony.PatchAll(); } catch (Exception arg) { Log.Error($"Failed to apply some patches: {arg}"); } Log.Info("Shipwright's Touch loaded!"); } private void MigrateRenameKey() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown try { Dictionary<ConfigDefinition, string> dictionary = (Dictionary<ConfigDefinition, string>)AccessTools.Property(typeof(ConfigFile), "OrphanedEntries").GetValue(((BaseUnityPlugin)this).Config, null); ConfigDefinition key = new ConfigDefinition("Controls", "RenameShip"); ConfigDefinition key2 = new ConfigDefinition("Controls", "CustomizeShip"); if (dictionary.TryGetValue(key, out var value)) { dictionary.Remove(key); if (!dictionary.ContainsKey(key2)) { dictionary[key2] = value; } } } catch (Exception ex) { Log.Warn("Could not carry over the rename shortcut saved by an older version: " + ex.Message); } } public static string PlainText(string text) { if (string.IsNullOrEmpty(text)) { return ""; } return text.Replace("<", "").Replace(">", ""); } public static bool CanModifyShip(Ship ship, out string ownerName) { ownerName = string.Empty; if ((Object)(object)ship == (Object)null) { return true; } ZNetView component = ((Component)ship).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return true; } long num = component.GetZDO().GetLong("shipwrightstouch.builder_id", 0L); ownerName = PlainText(component.GetZDO().GetString("shipwrightstouch.builder_name", "")); if (num != 0L) { Player localPlayer = Player.m_localPlayer; return num == ((localPlayer != null) ? new long?(localPlayer.GetPlayerID()) : ((long?)null)); } return true; } } public static class SailAppearance { private class SailPart { public Renderer Renderer; public Texture VanillaTexture; public Vector4 VanillaScaleOffset; public Vector4 CustomScaleOffset; } private static readonly int ColorId = Shader.PropertyToID("_Color"); private static readonly int MainTexId = Shader.PropertyToID("_MainTex"); private static readonly int MainTexScaleOffsetId = Shader.PropertyToID("_MainTex_ST"); private static readonly ConditionalWeakTable<Ship, SailPart[]> s_parts = new ConditionalWeakTable<Ship, SailPart[]>(); private static readonly MaterialPropertyBlock s_block = new MaterialPropertyBlock(); public static void Apply(Ship ship, Color color, Texture2D texture) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) SailPart[] value = s_parts.GetValue(ship, FindParts); foreach (SailPart sailPart in value) { if (!((Object)(object)sailPart.Renderer == (Object)null)) { sailPart.Renderer.GetPropertyBlock(s_block); s_block.SetColor(ColorId, color); if ((Object)(object)texture != (Object)null) { s_block.SetTexture(MainTexId, (Texture)(object)texture); s_block.SetVector(MainTexScaleOffsetId, sailPart.CustomScaleOffset); } else if ((Object)(object)sailPart.VanillaTexture != (Object)null) { s_block.SetTexture(MainTexId, sailPart.VanillaTexture); s_block.SetVector(MainTexScaleOffsetId, sailPart.VanillaScaleOffset); } sailPart.Renderer.SetPropertyBlock(s_block); } } } public static Texture VanillaTextureOf(Ship ship) { return (from p in s_parts.GetValue(ship, FindParts) select p.VanillaTexture).FirstOrDefault((Func<Texture, bool>)((Texture t) => (Object)(object)t != (Object)null)); } private static SailPart[] FindParts(Ship ship) { MagicaCloth sailCloth = ship.m_sailCloth; IEnumerable<Renderer> source = ((!((Object)(object)sailCloth != (Object)null) || !sailCloth.SerializeData.sourceRenderers.Any((Renderer r) => (Object)(object)r != (Object)null)) ? ((Component)ship).GetComponentsInChildren<Renderer>(true).Where(delegate(Renderer r) { string text = ((Object)((Component)r).gameObject).name.ToLower(); return text.Contains("sail") || text.Contains("cloth") || text.Contains("flag"); }) : sailCloth.SerializeData.sourceRenderers.Where((Renderer r) => (Object)(object)r != (Object)null)); return source.Select(CreatePart).ToArray(); } private static SailPart CreatePart(Renderer renderer) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) SailPart sailPart = new SailPart { Renderer = renderer, CustomScaleOffset = FitToUvBounds(MeshOf(renderer)) }; Material sharedMaterial = renderer.sharedMaterial; if ((Object)(object)sharedMaterial != (Object)null && sharedMaterial.HasProperty(MainTexId)) { sailPart.VanillaTexture = sharedMaterial.GetTexture(MainTexId); Vector2 textureScale = sharedMaterial.GetTextureScale(MainTexId); Vector2 textureOffset = sharedMaterial.GetTextureOffset(MainTexId); sailPart.VanillaScaleOffset = new Vector4(textureScale.x, textureScale.y, textureOffset.x, textureOffset.y); } return sailPart; } private static Vector4 FitToUvBounds(Mesh mesh) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mesh == (Object)null || !mesh.isReadable) { return new Vector4(1f, 1f, 0f, 0f); } Vector2[] uv = mesh.uv; if (uv.Length == 0) { return new Vector4(1f, 1f, 0f, 0f); } float num = uv.Min((Vector2 val) => val.x); float num2 = uv.Max((Vector2 val) => val.x); float num3 = uv.Min((Vector2 val) => val.y); float num4 = uv.Max((Vector2 val) => val.y); if (num2 - num < 0.001f || num4 - num3 < 0.001f) { return new Vector4(1f, 1f, 0f, 0f); } float num5 = 1f / (num2 - num); float num6 = 1f / (num4 - num3); return new Vector4(num5, num6, (0f - num) * num5, (0f - num3) * num6); } private static Mesh MeshOf(Renderer renderer) { SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null); if (val != null) { return val.sharedMesh; } MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>(); if (!((Object)(object)component != (Object)null)) { return null; } return component.sharedMesh; } } public enum TextureSource : byte { Server, Player } public enum TextureStatus : byte { Approved, Pending, Denied } public enum UploadAnswer : byte { Send, Shared, Refused } [Flags] public enum UploadFlags : byte { None = 0, ConvertedDifferently = 1, UnexpectedContent = 2 } public class CatalogEntry { public const int MaxNameLength = 48; public const string AutoDeciderId = "auto"; public string Hash; public List<string> Aliases = new List<string>(); public string Name; public TextureSource Source; public TextureStatus Status; public string UploaderName = ""; public int Width; public int Height; public int Bytes; public bool Mine; public string UploaderId = ""; public string FileName = ""; public string Date = ""; public string DecidedById = ""; public string DecidedByName = ""; public string DecidedDate = ""; public UploadFlags Flags; public int UploadedBytes; public bool AutoDenied; public bool CountsTowardLimit => Source == TextureSource.Player; public void Write(ZPackage package, string viewerId) { package.Write(Hash); package.Write(Name); package.Write((byte)Source); package.Write((byte)Status); package.Write(UploaderName); package.Write(Width); package.Write(Height); package.Write(Bytes); bool flag = UploaderId != "" && UploaderId == viewerId; package.Write(flag); package.Write(flag && AutoDenied); package.Write(Aliases.Count); foreach (string alias in Aliases) { package.Write(alias); } } public static CatalogEntry Read(ZPackage package) { return new CatalogEntry { Hash = package.ReadString(), Name = package.ReadString(), Source = (TextureSource)package.ReadByte(), Status = (TextureStatus)package.ReadByte(), UploaderName = Plugin.PlainText(package.ReadString()), Width = package.ReadInt(), Height = package.ReadInt(), Bytes = package.ReadInt(), Mine = package.ReadBool(), AutoDenied = package.ReadBool(), Aliases = ReadAliases(package) }; } public static List<string> ReadAliases(ZPackage package) { int num = package.ReadInt(); List<string> list = new List<string>(); for (int i = 0; i < num; i++) { string text = package.ReadString(); if (SailDownloads.IsHash(text) && i < 64) { list.Add(text); } } return list; } public static void WriteList(ZPackage package, IReadOnlyCollection<CatalogEntry> entries, string viewerId) { package.Write(entries.Count); foreach (CatalogEntry entry in entries) { entry.Write(package, viewerId); } } public static List<CatalogEntry> ReadList(ZPackage package) { int num = package.ReadInt(); List<CatalogEntry> list = new List<CatalogEntry>(num); for (int i = 0; i < num; i++) { list.Add(Read(package)); } return list; } public static string CleanName(string name) { StringBuilder stringBuilder = new StringBuilder(); string text = name ?? ""; foreach (char c in text) { if (char.IsLetterOrDigit(c) || c == ' ' || c == '-' || c == '_' || c == '(' || c == ')') { stringBuilder.Append(c); } } string text2 = stringBuilder.ToString().Trim(); if (text2.Length > 48) { text2 = text2.Substring(0, 48).TrimEnd(); } if (text2.Length != 0) { return text2; } return "texture"; } } public static class SailImages { private static readonly HashSet<string> s_plainChunks = new HashSet<string> { "IHDR", "PLTE", "IDAT", "IEND", "tRNS", "sRGB", "gAMA", "cHRM", "pHYs" }; private static readonly byte[] s_pngSignature = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }; public static byte[] ToSharedPng(byte[] bytes, out int width, out int height, out string error) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown width = 0; height = 0; error = null; if (!SailThumbnails.TryReadSize(bytes, out var width2, out var height2)) { error = "not a valid PNG or JPG"; return null; } if (width2 > 2048 || height2 > 2048) { error = $"{width2}x{height2} is over the {2048}x{2048} limit"; return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); try { if (!ImageConversion.LoadImage(val, bytes, false)) { error = "not a valid PNG or JPG"; return null; } width = ((Texture)val).width; height = ((Texture)val).height; return Encode(val); } catch (Exception ex) { error = ex.Message; return null; } finally { Object.Destroy((Object)(object)val); } } private static byte[] Encode(Texture2D texture) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown if ((int)texture.format != 4 && (int)texture.format != 5) { return ImageConversion.EncodeToPNG(texture); } Color32[] pixels = texture.GetPixels32(); Color32[] array = pixels; for (int i = 0; i < array.Length; i++) { if (array[i].a != byte.MaxValue) { return ImageConversion.EncodeToPNG(texture); } } Texture2D val = new Texture2D(((Texture)texture).width, ((Texture)texture).height, (TextureFormat)3, false); try { val.SetPixels32(pixels); return ImageConversion.EncodeToPNG(val); } finally { Object.Destroy((Object)(object)val); } } public static bool IsPng(byte[] bytes) { if (bytes == null || bytes.Length < s_pngSignature.Length) { return false; } for (int i = 0; i < s_pngSignature.Length; i++) { if (bytes[i] != s_pngSignature[i]) { return false; } } return true; } public static string UnexpectedContent(byte[] png) { if (!IsPng(png)) { return "not a PNG"; } int num = s_pngSignature.Length; string text; do { if (num + 12 > png.Length) { return "a cut-off chunk"; } long num2 = (long)(((ulong)png[num] << 24) | ((ulong)png[num + 1] << 16) | ((ulong)png[num + 2] << 8) | png[num + 3]); text = ChunkType(png, num + 4); if (num2 > png.Length - num - 12) { return "a cut-off " + text + " chunk"; } if (!s_plainChunks.Contains(text)) { return "a " + text + " chunk"; } num += 12 + (int)num2; } while (!(text == "IEND")); if (num != png.Length) { return $"{png.Length - num} bytes after the image's end"; } return null; } private static string ChunkType(byte[] png, int offset) { for (int i = offset; i < offset + 4; i++) { byte b = png[i]; if ((b < 65 || b > 90) && (b < 97 || b > 122)) { return BitConverter.ToString(png, offset, 4); } } return Encoding.ASCII.GetString(png, offset, 4); } } public class SailPolicy { private const string Section = "Server"; private const string SectionNote = " Used when this game is the server (a dedicated server, or the player hosting). While connected to a server with this mod, these show the server's values, and its admins can change them here."; public static ConfigEntry<bool> AllowCustomTexturesConfig; public static ConfigEntry<bool> AllowPlayerTexturesConfig; public static ConfigEntry<bool> RequireApprovalConfig; public static ConfigEntry<int> MaxFileKilobytesConfig; public static ConfigEntry<int> MaxDimensionConfig; public static ConfigEntry<int> MaxTexturesPerPlayerConfig; public static ConfigEntry<bool> AutoDenyUnexpectedContentConfig; public static ConfigEntry<bool> RequireMatchingModConfig; public static ConfigEntry<bool> EnemiesIgnoreUnoccupiedShipsConfig; public static ConfigEntry<bool> RescueCapsizedShipsConfig; public bool AllowCustomTextures; public bool AllowPlayerTextures; public bool RequireApproval; public int MaxFileKilobytes; public int MaxDimension; public int MaxTexturesPerPlayer; public bool AutoDenyUnexpectedContent; public bool RequireMatchingMod; public bool EnemiesIgnoreUnoccupiedShips; public bool RescueCapsizedShips; public static readonly SailPolicy Unrestricted = new SailPolicy { AllowCustomTextures = true, AllowPlayerTextures = true, RequireApproval = false, MaxFileKilobytes = 4096, MaxDimension = 2048, MaxTexturesPerPlayer = int.MaxValue, AutoDenyUnexpectedContent = false }; private static readonly List<ConfigurationManagerAttributes> s_attributes = new List<ConfigurationManagerAttributes>(); public bool NeedsMatchingMod { get { if (!EnemiesIgnoreUnoccupiedShips) { return RescueCapsizedShips; } return true; } } public static ConfigEntryBase[] Entries { get; private set; } public static void Bind(ConfigFile config) { AllowCustomTexturesConfig = config.Bind<bool>("Server", "AllowCustomSailTextures", true, Describe("Allow custom sail textures on this server at all. When off, every ship shows the vanilla sail to everyone, including a player's own textures on their own screen.")); AllowPlayerTexturesConfig = config.Bind<bool>("Server", "AllowPlayerSailTextures", true, Describe("Allow players to share their own sail textures with everyone. When off, nothing new is shared: players still see their own textures on their own ships, everyone else sees the server's textures, textures approved earlier, or the vanilla sail.")); RequireApprovalConfig = config.Bind<bool>("Server", "RequireApproval", true, Describe("New player textures stay visible only to the player who shared them until a moderator or admin approves them. When off, they're shown to everyone right away; moderators can still deny one later.")); MaxFileKilobytesConfig = config.Bind<int>("Server", "MaxTextureFileKB", 2048, Describe("Largest texture a player may share, in KB, measured after it's converted to PNG for sharing (a JPG or a carefully compressed PNG can grow). This sets download time and the server's disk use; video memory depends only on MaxTextureSize.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 4096))); MaxDimensionConfig = config.Bind<int>("Server", "MaxTextureSize", 1024, Describe("Largest width or height, in pixels, of a texture a player may share.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 2048))); MaxTexturesPerPlayerConfig = config.Bind<int>("Server", "MaxTexturesPerPlayer", 10, Describe("How many textures each player may share (at least 1).")); AutoDenyUnexpectedContentConfig = config.Bind<bool>("Server", "AutoDenyUnexpectedContent", false, Describe("Automatically deny an upload that held more than image data (moderators see it flagged as \"unexpected content\" either way). The game never sends such files itself, so it means a modified game or a hand-made file. Players are only ever sent the server's own clean copy, so this is about keeping such uploads off ships, not about safety. The uploader sees it as auto-denied, and a moderator can still approve it.")); RequireMatchingModConfig = config.Bind<bool>("Server", "RequireMatchingMod", false, Describe("Only let players join who have a version of Shipwright's Touch this server can talk to. Others are turned away with the game's \"incompatible version\" message; players already connected without it get a 10 second warning, then are disconnected (their character is saved). Turned on automatically by EnemiesIgnoreUnoccupiedShips and RescueCapsizedShips, and stays on if those are turned off again.")); EnemiesIgnoreUnoccupiedShipsConfig = config.Bind<bool>("Server", "EnemiesIgnoreUnoccupiedShips", false, Describe("Safe Harbor: monsters don't go after a ship with nobody aboard. One already attacking keeps at it until you come back and deal with it, and a ship next to a fight can still be hit. Players' games run the monsters, so this turns RequireMatchingMod on.")); RescueCapsizedShipsConfig = config.Bind<bool>("Server", "RescueCapsizedShips", false, Describe("Safe Harbor: a capsized ship still takes damage, but never below 10% of its health, and anyone can turn it upright by holding the interact key on it. Players' games run the ships, so this turns RequireMatchingMod on.")); Entries = (ConfigEntryBase[])(object)new ConfigEntryBase[10] { (ConfigEntryBase)AllowCustomTexturesConfig, (ConfigEntryBase)AllowPlayerTexturesConfig, (ConfigEntryBase)RequireApprovalConfig, (ConfigEntryBase)MaxFileKilobytesConfig, (ConfigEntryBase)MaxDimensionConfig, (ConfigEntryBase)MaxTexturesPerPlayerConfig, (ConfigEntryBase)AutoDenyUnexpectedContentConfig, (ConfigEntryBase)RequireMatchingModConfig, (ConfigEntryBase)EnemiesIgnoreUnoccupiedShipsConfig, (ConfigEntryBase)RescueCapsizedShipsConfig }; } private static ConfigDescription Describe(string text, AcceptableValueBase range = null) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = -s_attributes.Count }; s_attributes.Add(configurationManagerAttributes); return new ConfigDescription(text + " Used when this game is the server (a dedicated server, or the player hosting). While connected to a server with this mod, these show the server's values, and its admins can change them here.", range, new object[1] { configurationManagerAttributes }); } public static void SetReadOnly(bool readOnly) { foreach (ConfigurationManagerAttributes s_attribute in s_attributes) { s_attribute.ReadOnly = readOnly; } } public void ApplyToConfig() { AllowCustomTexturesConfig.Value = AllowCustomTextures; AllowPlayerTexturesConfig.Value = AllowPlayerTextures; RequireApprovalConfig.Value = RequireApproval; MaxFileKilobytesConfig.Value = MaxFileKilobytes; MaxDimensionConfig.Value = MaxDimension; MaxTexturesPerPlayerConfig.Value = MaxTexturesPerPlayer; AutoDenyUnexpectedContentConfig.Value = AutoDenyUnexpectedContent; RequireMatchingModConfig.Value = RequireMatchingMod; EnemiesIgnoreUnoccupiedShipsConfig.Value = EnemiesIgnoreUnoccupiedShips; RescueCapsizedShipsConfig.Value = RescueCapsizedShips; } public static SailPolicy FromConfig() { return new SailPolicy { AllowCustomTextures = AllowCustomTexturesConfig.Value, AllowPlayerTextures = AllowPlayerTexturesConfig.Value, RequireApproval = RequireApprovalConfig.Value, MaxFileKilobytes = MaxFileKilobytesConfig.Value, MaxDimension = MaxDimensionConfig.Value, MaxTexturesPerPlayer = Math.Max(1, MaxTexturesPerPlayerConfig.Value), AutoDenyUnexpectedContent = AutoDenyUnexpectedContentConfig.Value, RequireMatchingMod = RequireMatchingModConfig.Value, EnemiesIgnoreUnoccupiedShips = EnemiesIgnoreUnoccupiedShipsConfig.Value, RescueCapsizedShips = RescueCapsizedShipsConfig.Value }; } public void Write(ZPackage package) { package.Write(AllowCustomTextures); package.Write(AllowPlayerTextures); package.Write(RequireApproval); package.Write(MaxFileKilobytes); package.Write(MaxDimension); package.Write(MaxTexturesPerPlayer); package.Write(AutoDenyUnexpectedContent); package.Write(RequireMatchingMod); package.Write(EnemiesIgnoreUnoccupiedShips); package.Write(RescueCapsizedShips); } public static SailPolicy Read(ZPackage package) { return new SailPolicy { AllowCustomTextures = package.ReadBool(), AllowPlayerTextures = package.ReadBool(), RequireApproval = package.ReadBool(), MaxFileKilobytes = package.ReadInt(), MaxDimension = package.ReadInt(), MaxTexturesPerPlayer = package.ReadInt(), AutoDenyUnexpectedContent = package.ReadBool(), RequireMatchingMod = package.ReadBool(), EnemiesIgnoreUnoccupiedShips = package.ReadBool(), RescueCapsizedShips = package.ReadBool() }; } public override string ToString() { return $"custom={AllowCustomTextures} player={AllowPlayerTextures} approval={RequireApproval} " + $"maxKB={MaxFileKilobytes} maxSize={MaxDimension} perPlayer={MaxTexturesPerPlayer} autoDeny={AutoDenyUnexpectedContent} " + $"requireMod={RequireMatchingMod} enemiesIgnore={EnemiesIgnoreUnoccupiedShips} rescueCapsized={RescueCapsizedShips}"; } } internal class ConfigurationManagerAttributes { public bool? ReadOnly; public int? Order; } public static class SailStyle { public static readonly Color[] Presets = (Color[])(object)new Color[6] { Color.white, Color.red, Color.blue, Color.green, Color.yellow, new Color(0.2f, 0.2f, 0.2f) }; public static readonly string[] PresetNames = new string[6] { "White", "Red", "Blue", "Green", "Yellow", "Black" }; private const int NoCustomColor = -1; public const int MaxRecentColors = 8; private static List<Color> s_recent; private static string RecentColorsPath => Path.Combine(Paths.ConfigPath, "ShipwrightsTouch", "recent-colors.txt"); public static IReadOnlyList<Color> RecentColors { get { LoadRecent(); return s_recent; } } public static bool TryGetColor(ZDO zdo, out Color color) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) int num = zdo.GetInt("shipwrightstouch.sail_color", -1); if (num != -1) { color = FromRgb(num); return true; } int num2 = zdo.GetInt("custom_sail_style", 0); color = ((num2 >= 0 && num2 < Presets.Length) ? Presets[num2] : Color.white); if (num2 >= 0) { return num2 < Presets.Length; } return false; } public static void SetPreset(ZDO zdo, int style) { zdo.Set("custom_sail_style", style); zdo.Set("shipwrightstouch.sail_color", -1); } public static void SetColor(ZDO zdo, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) int num = PresetIndexOf(color); if (num >= 0) { SetPreset(zdo, num); return; } zdo.Set("custom_sail_style", NearestPreset(color)); zdo.Set("shipwrightstouch.sail_color", ToRgb(color)); } public static int PresetIndexOf(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) int num = ToRgb(color); for (int i = 0; i < Presets.Length; i++) { if (ToRgb(Presets[i]) == num) { return i; } } return -1; } public static string ToHex(Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return "#" + ColorUtility.ToHtmlStringRGB(color); } public static bool TryParseHex(string text, out Color color) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) text = text?.Trim() ?? ""; if (!text.StartsWith("#")) { text = "#" + text; } if (text.Length == 7 && ColorUtility.TryParseHtmlString(text, ref color)) { color.a = 1f; return true; } color = Color.white; return false; } public static void AddRecent(Color color) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (PresetIndexOf(color) >= 0) { return; } LoadRecent(); int rgb = ToRgb(color); s_recent.RemoveAll((Color c) => ToRgb(c) == rgb); s_recent.Insert(0, color); if (s_recent.Count > 8) { s_recent.RemoveRange(8, s_recent.Count - 8); } try { Directory.CreateDirectory(Path.GetDirectoryName(RecentColorsPath)); File.WriteAllLines(RecentColorsPath, s_recent.Select(ToHex)); } catch (IOException ex) { Log.Warn("Could not save recent sail colors: " + ex.Message); } } private static void LoadRecent() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (s_recent != null) { return; } s_recent = new List<Color>(); if (!File.Exists(RecentColorsPath)) { return; } try { string[] array = File.ReadAllLines(RecentColorsPath); for (int i = 0; i < array.Length; i++) { if (TryParseHex(array[i], out var color)) { s_recent.Add(color); } if (s_recent.Count == 8) { break; } } } catch (IOException ex) { Log.Warn("Could not read recent sail colors: " + ex.Message); } } private static int NearestPreset(Color color) { //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_0018: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) int result = 0; float num = float.MaxValue; for (int i = 0; i < Presets.Length; i++) { Color val = Presets[i]; float num2 = (val.r - color.r) * (val.r - color.r) + (val.g - color.g) * (val.g - color.g) + (val.b - color.b) * (val.b - color.b); if (num2 < num) { num = num2; result = i; } } return result; } private static int ToRgb(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Color32 val = Color32.op_Implicit(color); return (val.r << 16) | (val.g << 8) | val.b; } private static Color FromRgb(int rgb) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) return Color32.op_Implicit(new Color32((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)rgb, byte.MaxValue)); } } public static cla