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 StationRangePlus v1.0.1
plugins\StationRangePlus.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using StationRangePlus.Localization; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PONEIS")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Hold Alt + scroll to adjust crafting station build range per bench, with a global multiplier")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+d304014157d4464f965ee2a75b11b45fa3a2a2e4")] [assembly: AssemblyProduct("StationRangePlus")] [assembly: AssemblyTitle("StationRangePlus")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.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 StationRangePlus { internal static class ModConfig { internal static ConfigEntry<KeyCode> HoldKey; internal static ConfigEntry<bool> BlockCameraZoom; internal static ConfigEntry<float> ScrollStep; internal static ConfigEntry<float> MinRange; internal static ConfigEntry<float> MaxRange; internal static ConfigEntry<float> GlobalMultiplier; internal static ConfigEntry<bool> RequireLookingAtStation; internal static ConfigEntry<float> MaxAdjustDistance; internal static ConfigEntry<bool> ShowAreaMarker; internal static ConfigEntry<string> Language; internal static void Bind(ConfigFile config) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown HoldKey = config.Bind<KeyCode>("Controls", "HoldKey", (KeyCode)308, "Hold this key and scroll to change the nearest crafting station build range. Default LeftAlt (RightAlt also works when Alt is chosen)."); BlockCameraZoom = config.Bind<bool>("Controls", "BlockCameraZoom", true, "While HoldKey is held, block character camera zoom so scroll only changes station range."); ScrollStep = config.Bind<float>("Controls", "ScrollStep", 1f, new ConfigDescription("Meters added/removed per scroll tick.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), Array.Empty<object>())); MinRange = config.Bind<float>("Range", "MinRange", 2f, new ConfigDescription("Minimum base build range (meters).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); MaxRange = config.Bind<float>("Range", "MaxRange", 100f, new ConfigDescription("Maximum base build range (meters).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>())); GlobalMultiplier = config.Bind<float>("Range", "GlobalMultiplier", 1f, new ConfigDescription("Multiplies every station base range (custom or vanilla). 1 = no change.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), Array.Empty<object>())); RequireLookingAtStation = config.Bind<bool>("Controls", "RequireLookingAtStation", true, "If true, prefer the station under the crosshair; otherwise the nearest station in MaxAdjustDistance."); MaxAdjustDistance = config.Bind<float>("Controls", "MaxAdjustDistance", 8f, new ConfigDescription("Max distance to a station for Alt+scroll adjust.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>())); ShowAreaMarker = config.Bind<bool>("Controls", "ShowAreaMarker", true, "Show the station area circle while adjusting range."); Language = config.Bind<string>("Localization", "Language", "Auto", "Language for StationRangePlus messages. Auto follows the game. Values: Auto, English, Portuguese."); Language.SettingChanged += delegate { Loc.ApplyLanguagePreference(); }; GlobalMultiplier.SettingChanged += delegate { StationRangeService.ReapplyAll(); }; } internal static bool IsHoldKeyDown() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and I4 //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 //IL_0086: Unknown result type (might be due to invalid IL or missing references) KeyCode value = HoldKey.Value; if ((int)value == 308 || (int)value == 307) { if (!Input.GetKey((KeyCode)308)) { return Input.GetKey((KeyCode)307); } return true; } if ((int)value == 306 || (int)value == 305) { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } if ((int)value == 304 || (int)value == 303) { if (!Input.GetKey((KeyCode)304)) { return Input.GetKey((KeyCode)303); } return true; } return Input.GetKey(value); } } [BepInPlugin("poneis.valheim.StationRangePlus", "StationRangePlus", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "poneis.valheim.StationRangePlus"; public const string PluginName = "StationRangePlus"; public const string PluginVersion = "1.0.1"; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); Loc.Init(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "poneis.valheim.StationRangePlus"); ((Component)this).gameObject.AddComponent<RangeInputListener>(); Log.LogInfo((object)"StationRangePlus 1.0.1 loaded."); } } internal sealed class RangeInputListener : MonoBehaviour { private void Update() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) StationRangeService.TickMarkers(); if (!ModConfig.IsHoldKeyDown()) { return; } float y = Input.mouseScrollDelta.y; if (!(Mathf.Abs(y) < 0.01f)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !Console.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus())) { StationRangeService.TryAdjustFromScroll(localPlayer, y); } } } } internal static class StationRangeService { internal static readonly int RangeHash = StringExtensionMethods.GetStableHashCode("srp_range"); private static readonly Dictionary<CraftingStation, float> VanillaDefaults = new Dictionary<CraftingStation, float>(); private static float _nextMsg; private static CraftingStation _lastMarker; internal static void RememberVanillaDefault(CraftingStation station) { if (!((Object)(object)station == (Object)null) && !VanillaDefaults.ContainsKey(station)) { VanillaDefaults[station] = station.m_rangeBuild; } } internal static void Forget(CraftingStation station) { if ((Object)(object)station != (Object)null) { VanillaDefaults.Remove(station); } } internal static float GetVanillaDefault(CraftingStation station) { RememberVanillaDefault(station); if (!VanillaDefaults.TryGetValue(station, out var value)) { return station.m_rangeBuild; } return value; } internal static float GetStoredBase(CraftingStation station) { RememberVanillaDefault(station); ZDO val = (((Object)(object)station.m_nview != (Object)null) ? station.m_nview.GetZDO() : null); float num = default(float); if (val != null && val.IsValid() && val.GetFloat(RangeHash, ref num) && num > 0.01f) { return num; } return GetVanillaDefault(station); } internal static void SetStoredBase(CraftingStation station, float baseRange) { baseRange = Mathf.Clamp(baseRange, ModConfig.MinRange.Value, ModConfig.MaxRange.Value); ZNetView nview = station.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { if (!nview.IsOwner()) { nview.ClaimOwnership(); } ZDO zDO = nview.GetZDO(); if (zDO != null && zDO.IsValid()) { zDO.Set(RangeHash, baseRange); } } Apply(station, baseRange); } internal static void Apply(CraftingStation station, float? baseRange = null) { if (!((Object)(object)station == (Object)null)) { RememberVanillaDefault(station); float num = baseRange ?? GetStoredBase(station); float num2 = Mathf.Max(0.01f, ModConfig.GlobalMultiplier.Value); float rangeBuild = Mathf.Clamp(num * num2, ModConfig.MinRange.Value, ModConfig.MaxRange.Value * Mathf.Max(1f, num2)); station.m_rangeBuild = rangeBuild; station.m_updateExtensionTimer = 2f; RefreshVisuals(station); } } internal static void RefreshVisuals(CraftingStation station) { if ((Object)(object)station == (Object)null) { return; } float radius = ((station.m_buildRange > 0.01f) ? station.m_buildRange : station.m_rangeBuild); if ((Object)(object)station.m_areaMarkerCircle != (Object)null) { station.m_areaMarkerCircle.m_radius = radius; } Collider effectAreaCollider = station.m_effectAreaCollider; SphereCollider val = (SphereCollider)(object)((effectAreaCollider is SphereCollider) ? effectAreaCollider : null); if (val != null) { val.radius = radius; return; } CapsuleCollider val2 = (CapsuleCollider)(object)((effectAreaCollider is CapsuleCollider) ? effectAreaCollider : null); if (val2 != null) { val2.radius = radius; } } internal static void ReapplyAll() { foreach (CraftingStation allStation in CraftingStation.m_allStations) { if ((Object)(object)allStation != (Object)null) { Apply(allStation); } } } internal static CraftingStation FindAdjustTarget(Player player) { //IL_004f: 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_0057: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_00bc: 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_00c3: 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_00cc: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return null; } float value = ModConfig.MaxAdjustDistance.Value; CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation != (Object)null && Vector3.Distance(((Component)player).transform.position, ((Component)currentCraftingStation).transform.position) <= value) { return currentCraftingStation; } CraftingStation result = null; float num = float.MaxValue; Vector3 eyePoint = ((Character)player).GetEyePoint(); Vector3 lookDir = ((Character)player).GetLookDir(); foreach (CraftingStation allStation in CraftingStation.m_allStations) { if ((Object)(object)allStation == (Object)null) { continue; } float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)allStation).transform.position); if (num2 > value) { continue; } float num3 = num2; if (ModConfig.RequireLookingAtStation.Value) { Vector3 val = ((Component)allStation).transform.position - eyePoint; Vector3 normalized = ((Vector3)(ref val)).normalized; float num4 = Vector3.Dot(lookDir, normalized); if (num4 < 0.35f) { continue; } num3 = num2 * (1.5f - num4); } if (num3 < num) { num = num3; result = allStation; } } return result; } internal static void TryAdjustFromScroll(Player player, float scrollY) { if ((Object)(object)player == (Object)null || Mathf.Abs(scrollY) < 0.01f) { return; } CraftingStation val = FindAdjustTarget(player); if ((Object)(object)val == (Object)null) { if (Time.time >= _nextMsg) { _nextMsg = Time.time + 0.4f; ((Character)player).Message((MessageType)2, Loc.T("srp_msg_no_station"), 0, (Sprite)null, false); } return; } float value = ModConfig.ScrollStep.Value; float num = ((scrollY > 0f) ? value : (0f - value)); float baseRange = GetStoredBase(val) + num; SetStoredBase(val, baseRange); if (ModConfig.ShowAreaMarker.Value) { if ((Object)(object)_lastMarker != (Object)null && (Object)(object)_lastMarker != (Object)(object)val) { HideMarker(_lastMarker); } val.ShowAreaMarker(); _lastMarker = val; } float stationBuildRange = val.GetStationBuildRange(); ((Character)player).Message((MessageType)2, Loc.Tf("srp_msg_range", stationBuildRange, ModConfig.GlobalMultiplier.Value), 0, (Sprite)null, false); } internal static void HideMarker(CraftingStation station) { if ((Object)(object)station?.m_areaMarker != (Object)null) { station.m_areaMarker.SetActive(false); } } internal static void TickMarkers() { if (!ModConfig.IsHoldKeyDown() && (Object)(object)_lastMarker != (Object)null) { HideMarker(_lastMarker); _lastMarker = null; } } } } namespace StationRangePlus.Patches { [HarmonyPatch] internal static class CameraZoomBlockPatch { private static float _savedDistance; private static bool _restore; [HarmonyPrefix] [HarmonyPatch(typeof(GameCamera), "UpdateCamera")] private static void Prefix(GameCamera __instance) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) _restore = false; if (ModConfig.BlockCameraZoom.Value && ModConfig.IsHoldKeyDown() && Input.mouseScrollDelta.y != 0f && !((Object)(object)Player.m_localPlayer == (Object)null)) { _savedDistance = __instance.m_distance; _restore = true; } } [HarmonyPostfix] [HarmonyPatch(typeof(GameCamera), "UpdateCamera")] private static void Postfix(GameCamera __instance) { if (_restore) { __instance.m_distance = _savedDistance; } } } [HarmonyPatch] internal static class CraftingStationRangePatch { [HarmonyPostfix] [HarmonyPatch(typeof(CraftingStation), "Start")] private static void StartPostfix(CraftingStation __instance) { if (!((Object)(object)__instance == (Object)null)) { StationRangeService.RememberVanillaDefault(__instance); StationRangeService.Apply(__instance); } } [HarmonyPostfix] [HarmonyPatch(typeof(CraftingStation), "OnDestroy")] private static void OnDestroyPostfix(CraftingStation __instance) { StationRangeService.Forget(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(CraftingStation), "GetExtensions")] private static void GetExtensionsPostfix(CraftingStation __instance) { if (!((Object)(object)__instance == (Object)null)) { float num = Mathf.Clamp(StationRangeService.GetStoredBase(__instance) * Mathf.Max(0.01f, ModConfig.GlobalMultiplier.Value), ModConfig.MinRange.Value, ModConfig.MaxRange.Value * Mathf.Max(1f, ModConfig.GlobalMultiplier.Value)); if (Mathf.Abs(__instance.m_rangeBuild - num) > 0.05f) { __instance.m_rangeBuild = num; } __instance.m_buildRange = __instance.m_rangeBuild + (float)__instance.GetExtentionCount(false) * __instance.m_extraRangePerLevel; StationRangeService.RefreshVisuals(__instance); } } } } namespace StationRangePlus.Localization { internal static class Loc { private static readonly Dictionary<string, Dictionary<string, string>> Tables = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static string _activeCode = "en"; private static string LangDir => Path.Combine(Paths.ConfigPath, "StationRangePlus", "lang"); internal static void Init() { EnsureLangFolder(); LoadEmbedded("en"); LoadEmbedded("pt"); LoadDiskOverrides(); ApplyLanguagePreference(); Plugin.Log.LogInfo((object)("Localization ready (active=" + _activeCode + ", files in " + LangDir + ")")); } internal static string T(string key) { if (string.IsNullOrEmpty(key)) { return key; } if (Tables.TryGetValue(_activeCode, out var value) && value.TryGetValue(key, out var value2)) { return value2; } if (Tables.TryGetValue("en", out var value3) && value3.TryGetValue(key, out value2)) { return value2; } return key; } internal static string Tf(string key, params object[] args) { try { return string.Format(T(key), args); } catch (FormatException) { return T(key); } } private static void EnsureLangFolder() { Directory.CreateDirectory(LangDir); WriteEmbedded("en"); WriteEmbedded("pt"); } private static void WriteEmbedded(string code) { string path = Path.Combine(LangDir, code + ".json"); string text = ReadEmbedded(code); if (!string.IsNullOrEmpty(text)) { File.WriteAllText(path, text, Encoding.UTF8); } } private static void LoadEmbedded(string code) { string text = ReadEmbedded(code); if (!string.IsNullOrEmpty(text)) { Tables[code] = ParseFlatJson(text); } } private static void LoadDiskOverrides() { if (!Directory.Exists(LangDir)) { return; } string[] files = Directory.GetFiles(LangDir, "*.json"); foreach (string text in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); try { Dictionary<string, string> dictionary = ParseFlatJson(File.ReadAllText(text, Encoding.UTF8)); if (dictionary.Count == 0) { continue; } if (!Tables.TryGetValue(fileNameWithoutExtension, out var value)) { Tables[fileNameWithoutExtension] = dictionary; continue; } foreach (KeyValuePair<string, string> item in dictionary) { value[item.Key] = item.Value; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to load lang file " + text + ": " + ex.Message)); } } } private static string ReadEmbedded(string code) { string text = "StationRangePlus.Languages." + code + ".json"; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); if (stream == null) { Plugin.Log.LogWarning((object)("Missing embedded language resource: " + text)); return null; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); return streamReader.ReadToEnd(); } internal static void ApplyLanguagePreference() { string a = ModConfig.Language?.Value ?? "Auto"; if (string.Equals(a, "Portuguese", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "pt", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "pt-BR", StringComparison.OrdinalIgnoreCase)) { _activeCode = (Tables.ContainsKey("pt") ? "pt" : "en"); return; } if (string.Equals(a, "English", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "en", StringComparison.OrdinalIgnoreCase)) { _activeCode = "en"; return; } try { string text = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null); if (!string.IsNullOrEmpty(text) && (text.IndexOf("Portuguese", StringComparison.OrdinalIgnoreCase) >= 0 || text.StartsWith("pt", StringComparison.OrdinalIgnoreCase) || text.IndexOf("Brazil", StringComparison.OrdinalIgnoreCase) >= 0)) { _activeCode = (Tables.ContainsKey("pt") ? "pt" : "en"); return; } } catch { } _activeCode = "en"; } internal static Dictionary<string, string> ParseFlatJson(string json) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(json)) { return dictionary; } int i = 0; SkipWs(json, ref i); if (i >= json.Length || json[i] != '{') { return dictionary; } i++; while (i < json.Length) { SkipWs(json, ref i); if (i < json.Length && json[i] == '}') { break; } if (i < json.Length && json[i] == ',') { i++; continue; } if (!TryReadString(json, ref i, out var value)) { break; } SkipWs(json, ref i); if (i >= json.Length || json[i] != ':') { break; } i++; SkipWs(json, ref i); if (!TryReadString(json, ref i, out var value2)) { break; } dictionary[value] = value2; } return dictionary; } private static void SkipWs(string s, ref int i) { while (i < s.Length && char.IsWhiteSpace(s[i])) { i++; } } private static bool TryReadString(string s, ref int i, out string value) { value = null; SkipWs(s, ref i); if (i >= s.Length || s[i] != '"') { return false; } i++; StringBuilder stringBuilder = new StringBuilder(); while (i < s.Length) { char c = s[i++]; switch (c) { case '"': value = stringBuilder.ToString(); return true; case '\\': if (i < s.Length) { char c2 = s[i++]; StringBuilder stringBuilder2 = stringBuilder; stringBuilder2.Append(c2 switch { 'n' => '\n', 'r' => '\r', 't' => '\t', '"' => '"', '\\' => '\\', _ => c2, }); continue; } break; } stringBuilder.Append(c); } return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }