using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using MixedCaseMapMarkers.Patches; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LexPy")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Displays Valheim map marker names using their stored lowercase and mixed-case text.")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("MixedCaseMapMarkers")] [assembly: AssemblyTitle("MixedCaseMapMarkers")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MixedCaseMapMarkers { [BepInPlugin("com.lexpy.mixedcasemapmarkers", "MixedCaseMapMarkers", "1.1.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.lexpy.mixedcasemapmarkers"; public const string PluginName = "MixedCaseMapMarkers"; public const string PluginVersion = "1.1.0"; private Harmony? _harmony; internal static ManualLogSource? Log { get; private set; } private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.lexpy.mixedcasemapmarkers"); _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"MixedCaseMapMarkers 1.1.0 loaded."); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } MapMarkerText.Dispose(); _harmony = null; Log = null; } } } namespace MixedCaseMapMarkers.Patches { internal static class MapMarkerText { private const float FontSizeMultiplier = 0.86f; private const float CharacterSpacingAdjustment = -3.25f; private const float WordSpacingAdjustment = -2f; private const float CharacterWidthAdjustment = 18f; private const float OutlineWidth = 0.12f; private const string UnderlayKeyword = "UNDERLAY_ON"; private static readonly Color OutlineColor = new Color(0f, 0f, 0f, 1f); private static readonly Color UnderlayColor = new Color(0f, 0f, 0f, 0.85f); private static readonly int OutlineColorId = Shader.PropertyToID("_OutlineColor"); private static readonly int OutlineWidthId = Shader.PropertyToID("_OutlineWidth"); private static readonly int UnderlayColorId = Shader.PropertyToID("_UnderlayColor"); private static readonly int UnderlayOffsetXId = Shader.PropertyToID("_UnderlayOffsetX"); private static readonly int UnderlayOffsetYId = Shader.PropertyToID("_UnderlayOffsetY"); private static readonly int UnderlayDilateId = Shader.PropertyToID("_UnderlayDilate"); private static readonly int UnderlaySoftnessId = Shader.PropertyToID("_UnderlaySoftness"); private static readonly FieldInfo? ChatInputField = AccessTools.Field(typeof(Terminal), "m_input"); private static readonly FieldInfo? NameInputField = AccessTools.Field(typeof(Minimap), "m_nameInput"); private static TMP_FontAsset? _chatFont; private static Material? _markerMaterial; private static FieldInfo? _pinSaveField; private static Type? _pinDataType; private static bool _fontLogged; private static bool _missingInputWarningLogged; private static bool _missingSaveFieldWarningLogged; public static void CaptureChatFont(Chat chat) { if (_chatFont != null || chat == null) { return; } _chatFont = FindFont(ChatInputField?.GetValue(chat)); if (_chatFont == null) { return; } _markerMaterial = CreateMarkerMaterial(_chatFont); if (!_fontLogged) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogInfo((object)("Using Valheim chat font '" + ((Object)_chatFont).name + "' for persistent map markers.")); } _fontLogged = true; } } public static void ConfigureNameInput(Minimap minimap) { if (minimap == null || _chatFont == null) { return; } object obj = NameInputField?.GetValue(minimap); if (obj == null) { LogMissingInputWarning(); return; } GameObject root = GetRoot(obj); if (root != null) { TMP_InputField[] componentsInChildren = root.GetComponentsInChildren(true); foreach (TMP_InputField obj2 in componentsInChildren) { obj2.contentType = (ContentType)0; obj2.characterValidation = (CharacterValidation)0; obj2.inputValidator = null; obj2.onValidateInput = null; } TMP_Text[] componentsInChildren2 = root.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ApplyText(componentsInChildren2[i]); } } } public static int CaptureChildCount(RectTransform? root) { if (root == null) { return -1; } return ((Transform)root).childCount; } public static void ApplyCreatedPersistentMarker(object? pinData, RectTransform? root, int previousChildCount) { if (_chatFont == null || root == null || previousChildCount < 0 || !IsPersistentPin(pinData)) { return; } for (int i = Mathf.Clamp(previousChildCount, 0, ((Transform)root).childCount); i < ((Transform)root).childCount; i++) { TMP_Text[] componentsInChildren = ((Component)((Transform)root).GetChild(i)).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { ApplyText(componentsInChildren[j]); } } } public static void Dispose() { if (_markerMaterial != null) { Object.Destroy((Object)(object)_markerMaterial); } _chatFont = null; _markerMaterial = null; _pinSaveField = null; _pinDataType = null; } private static bool IsPersistentPin(object? pinData) { if (pinData == null) { return false; } Type type = pinData.GetType(); if (_pinDataType != type) { _pinDataType = type; _pinSaveField = AccessTools.Field(type, "m_save"); } if ((object)_pinSaveField == null) { if (!_missingSaveFieldWarningLogged) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"Valheim's PinData.m_save field was not found. Persistent marker labels will not be modified."); } _missingSaveFieldWarningLogged = true; } return false; } object value = _pinSaveField.GetValue(pinData); if (value is bool) { return (bool)value; } return false; } private static void ApplyText(TMP_Text text) { if (!((Object)(object)text.font == (Object)(object)_chatFont) || (_markerMaterial != null && !((Object)(object)text.fontSharedMaterial == (Object)(object)_markerMaterial))) { RemoveCaseStyle(text); text.font = _chatFont; if (_markerMaterial != null) { text.fontSharedMaterial = _markerMaterial; } text.fontSize *= 0.86f; text.fontSizeMin *= 0.86f; text.fontSizeMax *= 0.86f; text.characterSpacing += -3.25f; text.wordSpacing += -2f; text.characterWidthAdjustment = Mathf.Max(text.characterWidthAdjustment, 18f); ((Graphic)text).SetVerticesDirty(); ((Graphic)text).SetLayoutDirty(); } } private static Material? CreateMarkerMaterial(TMP_FontAsset font) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0052: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00ae: Expected O, but got Unknown Material material = ((TMP_Asset)font).material; if (material == null) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"Valheim's chat font has no material. The custom outline will not be applied."); } return null; } Material val = new Material(material) { name = "MixedCaseMapMarkers-Material" }; SetColor(val, OutlineColorId, OutlineColor); SetFloat(val, OutlineWidthId, 0.12f); val.EnableKeyword("UNDERLAY_ON"); SetColor(val, UnderlayColorId, UnderlayColor); SetFloat(val, UnderlayOffsetXId, 0.1f); SetFloat(val, UnderlayOffsetYId, -0.1f); SetFloat(val, UnderlayDilateId, 0.15f); SetFloat(val, UnderlaySoftnessId, 0.05f); return val; } private static void RemoveCaseStyle(TMP_Text text) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) text.fontStyle = (FontStyles)(text.fontStyle & -57); TMP_Style textStyle = text.textStyle; if (textStyle != null && (ContainsCaseTag(textStyle.styleOpeningDefinition) || ContainsCaseTag(textStyle.styleClosingDefinition))) { text.textStyle = TMP_Style.NormalStyle; } } private static bool ContainsCaseTag(string? definition) { if (string.IsNullOrWhiteSpace(definition)) { return false; } if (definition.IndexOf("", StringComparison.OrdinalIgnoreCase) < 0 && definition.IndexOf("", StringComparison.OrdinalIgnoreCase) < 0 && definition.IndexOf("", StringComparison.OrdinalIgnoreCase) < 0) { return definition.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static TMP_FontAsset? FindFont(object? value) { TMP_InputField val = (TMP_InputField)((value is TMP_InputField) ? value : null); if (val != null) { TMP_Text textComponent = val.textComponent; object obj = ((textComponent != null) ? textComponent.font : null); if (obj == null) { Graphic placeholder = val.placeholder; Graphic obj2 = ((placeholder is TMP_Text) ? placeholder : null); if (obj2 == null) { return null; } obj = ((TMP_Text)obj2).font; } return (TMP_FontAsset?)obj; } TMP_Text val2 = (TMP_Text)((value is TMP_Text) ? value : null); if (val2 != null) { return val2.font; } GameObject root = GetRoot(value); if (root == null) { return null; } TMP_Text[] componentsInChildren = root.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { TMP_FontAsset font = componentsInChildren[i].font; if (font != null) { return font; } } return null; } private static GameObject? GetRoot(object? value) { GameObject val = (GameObject)((value is GameObject) ? value : null); if (val == null) { Component val2 = (Component)((value is Component) ? value : null); if (val2 != null) { return val2.gameObject; } return null; } return val; } private static void LogMissingInputWarning() { if (!_missingInputWarningLogged) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"Valheim's map-marker name input field was not found. A game update may have changed the map UI."); } _missingInputWarningLogged = true; } } private static void SetColor(Material material, int propertyId, Color value) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(propertyId)) { material.SetColor(propertyId, value); } } private static void SetFloat(Material material, int propertyId, float value) { if (material.HasProperty(propertyId)) { material.SetFloat(propertyId, value); } } } [HarmonyPatch(typeof(Chat), "Awake")] internal static class ChatAwakePatch { [HarmonyPostfix] private static void Postfix(Chat __instance) { MapMarkerText.CaptureChatFont(__instance); } } [HarmonyPatch(typeof(Minimap), "ShowPinNameInput")] internal static class MinimapShowPinNameInputPatch { [HarmonyPostfix] private static void Postfix(Minimap __instance) { MapMarkerText.ConfigureNameInput(__instance); } } [HarmonyPatch(typeof(Minimap), "CreateMapNamePin")] internal static class MinimapCreateMapNamePinPatch { [HarmonyPrefix] private static void Prefix(RectTransform __1, out int __state) { __state = MapMarkerText.CaptureChildCount(__1); } [HarmonyPostfix] private static void Postfix(object __0, RectTransform __1, int __state) { MapMarkerText.ApplyCreatedPersistentMarker(__0, __1, __state); } } }