using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyTitle("SuperDuperMappyZoomer")] [assembly: AssemblyDescription("Removes the default zoom limits on Valheim's large map, letting you zoom in much closer and/or out much further than the game normally allows.")] [assembly: AssemblyCompany("Jeaudoir AKA FoobyScroobs")] [assembly: AssemblyProduct("Valheim Mod")] [assembly: AssemblyCopyright("Copyright (C) Jeaudoir 2026 - Released under MIT License")] [assembly: ComVisible(false)] [assembly: Guid("C5F3E912-8A4D-4B7C-9E6F-1A8B2C3D4E5F")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 SuperDuperMappyZoomer { public class ConfigManager { private string _configFilePath; private bool _needsResave = false; public float ZoomInMultiplier { get; private set; } = 100f; public float ZoomOutMultiplier { get; private set; } = 1f; public bool ShowDebugInfo { get; private set; } = false; public bool ShowZoomInfo { get; private set; } = false; private static float ZoomInMin => 1f; private static float ZoomInMax => 10000f; private static float ZoomOutMin => 1f; private static float ZoomOutMax => 1000f; public void LoadConfig(string configFilePath) { _configFilePath = configFilePath; DebugLogger.Info("Loading config from: " + _configFilePath); if (!File.Exists(_configFilePath)) { DebugLogger.Info($"Config file not found -- creating with defaults (zoom in: {ZoomInMultiplier:F0}; zoom out: {ZoomOutMultiplier:F0}; debug: {ShowDebugInfo.ToString().ToLower()}; verbose info: {ShowZoomInfo.ToString().ToLower()})"); SaveConfig(); return; } try { ParseConfigFile(); if (_needsResave) { DebugLogger.Info($"Config had invalid or missing values -- resaving with corrections. Zoom out: {ZoomOutMultiplier:F0}x, zoom in: {ZoomInMultiplier:F0}x"); SaveConfig(); } else { DebugLogger.Info($"Config loaded; zoom in: {ZoomInMultiplier:F0}x, zoom out: {ZoomOutMultiplier:F0}x"); } } catch (Exception ex) { DebugLogger.Error("Error reading config -- falling back to defaults.", ex); SaveConfig(); } } private void ParseConfigFile() { string[] array = File.ReadAllLines(_configFilePath); string text = ""; HashSet hashSet = new HashSet(); string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); if (string.IsNullOrEmpty(text3) || text3.StartsWith("#")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2); } else { if (!text3.Contains("=")) { continue; } string[] array3 = text3.Split(new char[1] { '=' }, 2); if (array3.Length == 2) { string text4 = array3[0].Trim(); string text5 = array3[1].Trim(); int num = text5.IndexOf('#'); if (num >= 0) { text5 = text5.Substring(0, num).Trim(); } string item = text + "." + text4; hashSet.Add(item); ParseConfigLine(text, text4, text5); } } } if (!hashSet.Contains("Zoom Settings.ZoomInMultiplier") || !hashSet.Contains("Zoom Settings.ZoomOutMultiplier") || !hashSet.Contains("Debug.ShowDebugInfo") || !hashSet.Contains("Debug.ShowZoomInfo")) { _needsResave = true; } } private void ParseConfigLine(string section, string key, string value) { switch (section + "." + key) { case "Zoom Settings.ZoomInMultiplier": ZoomInMultiplier = ParseFloat(value, "ZoomInMultiplier", 100f, ZoomInMin, ZoomInMax); return; case "Zoom Settings.ZoomOutMultiplier": ZoomOutMultiplier = ParseFloat(value, "ZoomOutMultiplier", 1f, ZoomOutMin, ZoomOutMax); return; case "Debug.ShowDebugInfo": ShowDebugInfo = ParseBool(value); return; case "Debug.ShowZoomInfo": ShowZoomInfo = ParseBool(value); return; } DebugLogger.Warning("Unknown config key dropped: " + section + "." + key + " -- this may be a leftover from an older version."); _needsResave = true; } private void SaveConfig() { try { using (StreamWriter streamWriter = new StreamWriter(_configFilePath)) { streamWriter.WriteLine("[Zoom Settings]"); streamWriter.WriteLine("# How much closer you can zoom IN compared to default."); streamWriter.WriteLine($"# 1 = original Valheim setting, {100f:F0} = {100f:F0}x closer, etc. (max: {10000f:F0})"); streamWriter.WriteLine("ZoomInMultiplier = " + ZoomInMultiplier.ToString(CultureInfo.InvariantCulture)); streamWriter.WriteLine(); streamWriter.WriteLine("# How much further you can zoom OUT compared to default."); streamWriter.WriteLine($"# 1 = original Valheim setting, 2 = 2x further, etc. (max: {1000f:F0})"); streamWriter.WriteLine("ZoomOutMultiplier = " + ZoomOutMultiplier.ToString(CultureInfo.InvariantCulture)); streamWriter.WriteLine(); streamWriter.WriteLine("[Debug]"); streamWriter.WriteLine("# Show debug info in the console (plugin init, scene loads, minimap detection, etc.)"); streamWriter.WriteLine("ShowDebugInfo = " + ShowDebugInfo.ToString().ToLower()); streamWriter.WriteLine(); streamWriter.WriteLine("# Show zoom level in console when zooming."); streamWriter.WriteLine("# NOTE: Requires ShowDebugInfo set to 'true'"); streamWriter.WriteLine("# WARNING: verbose -- logs every 'tick' of the scroll wheel!"); streamWriter.WriteLine("ShowZoomInfo = " + ShowZoomInfo.ToString().ToLower()); } DebugLogger.Log("Config saved to: " + _configFilePath); } catch (Exception ex) { DebugLogger.Error("Error saving config.", ex); } } private bool ParseBool(string value) { return string.Equals(value, "true", StringComparison.OrdinalIgnoreCase); } private float ParseFloat(string value, string key, float defaultValue, float min, float max) { if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { DebugLogger.Warning($"'{key}' value ('{value}') is not a valid number -- resetting to default ({defaultValue})."); _needsResave = true; return defaultValue; } if (result < min || result > max) { float num = Mathf.Clamp(result, min, max); DebugLogger.Warning($"'{key}' value ({result}) is out of range [{min}, {max}] -- clamping to {num}."); _needsResave = true; return num; } return result; } } internal static class Constants { internal const string PluginGuid = "FoobyScroobs.SuperDuperMappyZoomer"; internal const string PluginName = "Super Duper Mappy Zoomer"; internal const string PluginVersion = "1.1.0"; internal const float DefaultMaxZoom = 1f; internal const float DefaultMinZoom = 0.01f; internal const float MinZoomFloor = 0.0001f; internal const float ZoomInMultiplierMin = 1f; internal const float ZoomInMultiplierMax = 10000f; internal const float ZoomOutMultiplierMin = 1f; internal const float ZoomOutMultiplierMax = 1000f; internal const float DefaultZoomInMultiplier = 100f; internal const float DefaultZoomOutMultiplier = 1f; internal const float ScrollInputThreshold = 0.01f; internal const float ZoomChangeThreshold = 1E-05f; internal const string ScrollDirectionIn = "IN"; internal const string ScrollDirectionOut = "OUT"; internal const string ScrollNoChange = " [NO CHANGE - Limit reached?]"; internal const string SectionZoomSettings = "Zoom Settings"; internal const string SectionDebug = "Debug"; internal const string KeyZoomInMultiplier = "ZoomInMultiplier"; internal const string KeyZoomOutMultiplier = "ZoomOutMultiplier"; internal const string KeyShowDebugInfo = "ShowDebugInfo"; internal const string KeyShowZoomInfo = "ShowZoomInfo"; } public static class DebugLogger { private static string Prefix => typeof(BepInExPlugin).Namespace + " "; public static void Log(string message) { if (BepInExPlugin.Settings != null && BepInExPlugin.Settings.ShowDebugInfo) { Debug.Log((object)(Prefix + message)); } } public static void Info(string message) { Debug.Log((object)(Prefix + message)); } public static void Warning(string message) { Debug.LogWarning((object)(Prefix + message)); } public static void Error(string message, Exception ex = null) { if (ex != null) { Debug.LogError((object)(Prefix + message + ": " + ex.Message + "\n" + ex.StackTrace)); } else { Debug.LogError((object)(Prefix + message)); } } } [BepInPlugin("FoobyScroobs.SuperDuperMappyZoomer", "Super Duper Mappy Zoomer", "1.1.0")] public class BepInExPlugin : BaseUnityPlugin { private float _maxZoomLimit = 1f; private float _minZoomLimit = 0.01f; private bool _zoomLimitsApplied = false; private float _lastLoggedZoom = 0f; private FieldInfo _largeZoomField = null; public static ConfigManager Settings { get; private set; } private void Awake() { Settings = new ConfigManager(); Settings.LoadConfig(((BaseUnityPlugin)this).Config.ConfigFilePath); CalculateZoomLimits(); _largeZoomField = typeof(Minimap).GetField("m_largeZoom", BindingFlags.Instance | BindingFlags.NonPublic); if (_largeZoomField == null) { DebugLogger.Warning("Could not find Minimap.m_largeZoom field -- scroll zoom logging will be unavailable. This may indicate a game update has changed the field name."); } SceneManager.sceneUnloaded += OnSceneUnloaded; DebugLogger.Info("=== Super Duper Mappy Zoomer v1.1.0 Loaded ==="); DebugLogger.Log($"Zoom out: {Settings.ZoomOutMultiplier}x, Zoom in: {Settings.ZoomInMultiplier}x"); DebugLogger.Log($"Technical limits -- Max: {_maxZoomLimit}, Min: {_minZoomLimit}"); } private void CalculateZoomLimits() { _maxZoomLimit = 1f * Settings.ZoomOutMultiplier; _minZoomLimit = 0.01f / Settings.ZoomInMultiplier; if (_minZoomLimit <= 0f) { _minZoomLimit = 0.0001f; } } private void OnSceneUnloaded(Scene scene) { if (_zoomLimitsApplied) { DebugLogger.Log("Scene unloaded: '" + ((Scene)(ref scene)).name + "' -- resetting minimap state."); } _zoomLimitsApplied = false; _lastLoggedZoom = 0f; } private void Update() { if (!_zoomLimitsApplied && (Object)(object)Minimap.instance != (Object)null) { _zoomLimitsApplied = true; DebugLogger.Info("Minimap instance detected. Applying zoom limits."); DebugLogger.Log($"Valheim defaults -- Min: {Minimap.instance.m_minZoom}, Max: {Minimap.instance.m_maxZoom}"); Minimap.instance.m_maxZoom = _maxZoomLimit; Minimap.instance.m_minZoom = _minZoomLimit; DebugLogger.Log($"New limits -- Min: {_minZoomLimit}, Max: {_maxZoomLimit}"); float num = 0.01f / _minZoomLimit; float num2 = _maxZoomLimit / 1f; string text = $"Zoom limits applied: you can now zoom IN {num:F0}x closer"; if (num2 > 1f) { text += $" and OUT {num2:F0}x further"; } text += "!"; DebugLogger.Info(text); } UpdateScrollLogging(); } private void UpdateScrollLogging() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Invalid comparison between Unknown and I4 if (!Settings.ShowDebugInfo || !Settings.ShowZoomInfo || _largeZoomField == null || (Object)(object)Minimap.instance == (Object)null || (int)Minimap.instance.m_mode != 2) { return; } float axis = Input.GetAxis("Mouse ScrollWheel"); if (!(Mathf.Abs(axis) <= 0.01f)) { float num = (float)_largeZoomField.GetValue(Minimap.instance); float num2 = 1f / num; string text = ((axis > 0f) ? "IN" : "OUT"); string text2 = " [NO CHANGE - Limit reached?]"; if (Mathf.Abs(num - _lastLoggedZoom) >= 1E-05f) { _lastLoggedZoom = num; text2 = string.Empty; } if (num <= 0.01f) { float num3 = 0.01f / num; DebugLogger.Log($"Scroll {text}: Zoom: {num:F4} (Mag: {num2:F1}x) - {num3:F1}x closer{text2}"); } else { float num4 = num / 0.01f; DebugLogger.Log($"Scroll {text}: Zoom: {num:F4} (Mag: {num2:F1}x) - {num4:F1}x further{text2}"); } } } private void OnDestroy() { DebugLogger.Log("Plugin unloading -- unsubscribing from scene events."); SceneManager.sceneUnloaded -= OnSceneUnloaded; } } }