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 PotionMaster v1.0.0
PotionMaster.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PotionMaster.Patches; using PotionMaster.Sync; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PotionMaster")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+4aa3e4b9cec60eb3ac3a77a521c68480a0a8472a")] [assembly: AssemblyProduct("PotionMaster")] [assembly: AssemblyTitle("PotionMaster")] [assembly: AssemblyVersion("1.0.0.0")] [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 PotionMaster { [BepInPlugin("com.emergent.potionmaster", "PotionMaster", "1.0.0")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public class PotionMasterPlugin : BaseUnityPlugin { public const string PluginGuid = "com.emergent.potionmaster"; public const string PluginName = "PotionMaster"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; private float _lastConsumeTime; public static PotionMasterPlugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static PotionMasterConfig Cfg { get; private set; } private void Awake() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; string text = Path.Combine(Paths.ConfigPath, "PotionMaster.cfg"); Cfg = new PotionMasterConfig(new ConfigFile(text, true)); _harmony = new Harmony("com.emergent.potionmaster"); _harmony.PatchAll(typeof(PlayerPatch)); ConfigSync.Init(this); Log.LogInfo((object)("PotionMaster v1.0.0 loaded. Config: " + text)); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) if (!Cfg.ModEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).InCutscene() || Console.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || TextInput.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible() || ((Object)(object)TextViewer.instance != (Object)null && TextViewer.instance.IsVisible())) { return; } if (Input.GetKeyDown(Cfg.HealthKey.Value)) { TryConsume(localPlayer, PotionType.Health, manual: true); } if (Input.GetKeyDown(Cfg.StaminaKey.Value)) { TryConsume(localPlayer, PotionType.Stamina, manual: true); } if (Input.GetKeyDown(Cfg.EitrKey.Value)) { TryConsume(localPlayer, PotionType.Eitr, manual: true); } if (Cfg.AutoPotionEnabled.Value) { float maxHealth = ((Character)localPlayer).GetMaxHealth(); float health = ((Character)localPlayer).GetHealth(); if (maxHealth > 0f && health / maxHealth * 100f <= Cfg.AutoHealthThreshold.Value) { TryConsume(localPlayer, PotionType.Health, manual: false); } float maxStamina = ((Character)localPlayer).GetMaxStamina(); float stamina = localPlayer.GetStamina(); if (maxStamina > 0f && stamina / maxStamina * 100f <= Cfg.AutoStaminaThreshold.Value) { TryConsume(localPlayer, PotionType.Stamina, manual: false); } float maxEitr = ((Character)localPlayer).GetMaxEitr(); float eitr = localPlayer.GetEitr(); if (maxEitr > 0f && eitr / maxEitr * 100f <= Cfg.AutoEitrThreshold.Value) { TryConsume(localPlayer, PotionType.Eitr, manual: false); } } } private void TryConsume(Player player, PotionType type, bool manual) { if (!(Time.time - _lastConsumeTime < Cfg.ConsumeInterval.Value) && PotionConsumer.Consume(player, type, out var itemName)) { _lastConsumeTime = Time.time; if (Cfg.ShowMessages.Value && manual) { ((Character)player).Message((MessageType)1, "[PotionMaster] " + itemName, 0, (Sprite)null); } } } } public static class PotionConsumer { public static bool Consume(Player player, PotionType type, out string itemName) { itemName = null; if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List<string> priorityList = GetPriorityList(type); if (priorityList.Count == 0) { return false; } foreach (string item in priorityList) { ItemData val = FindItemByPrefabName(inventory, item); if (val != null && CanConsume(player, val) && DoConsume(player, val)) { itemName = val.m_shared.m_name; return true; } } return false; } private static List<string> GetPriorityList(PotionType type) { object obj = type switch { PotionType.Health => PotionMasterPlugin.Cfg.HealthPotionPriority.Value, PotionType.Stamina => PotionMasterPlugin.Cfg.StaminaPotionPriority.Value, PotionType.Eitr => PotionMasterPlugin.Cfg.EitrPotionPriority.Value, _ => "", }; List<string> list = new List<string>(); string[] array = ((string)obj).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text)) { list.Add(text); } } return list; } private static ItemData FindItemByPrefabName(Inventory inv, string prefabName) { foreach (ItemData allItem in inv.GetAllItems()) { if ((Object)(object)allItem?.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name.Equals(prefabName, StringComparison.OrdinalIgnoreCase)) { return allItem; } } return null; } private static bool CanConsume(Player player, ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)item.m_shared.m_itemType != 2) { return false; } StatusEffect consumeStatusEffect = item.m_shared.m_consumeStatusEffect; if ((Object)(object)consumeStatusEffect != (Object)null && ((Character)player).GetSEMan().HaveStatusEffect(consumeStatusEffect.NameHash())) { return false; } return player.CanEat(item, false); } private static bool DoConsume(Player player, ItemData item) { Inventory inventory = ((Humanoid)player).GetInventory(); return ((Humanoid)player).ConsumeItem(inventory, item, false); } } public enum PotionType { Health, Stamina, Eitr } public class PotionMasterConfig { public readonly ConfigEntry<bool> ModEnabled; public readonly ConfigEntry<KeyCode> HealthKey; public readonly ConfigEntry<KeyCode> StaminaKey; public readonly ConfigEntry<KeyCode> EitrKey; public readonly ConfigEntry<bool> AutoPotionEnabled; public readonly ConfigEntry<float> AutoHealthThreshold; public readonly ConfigEntry<float> AutoStaminaThreshold; public readonly ConfigEntry<float> AutoEitrThreshold; public readonly ConfigEntry<float> ConsumeInterval; public readonly ConfigEntry<bool> BypassCooldownAnimation; public readonly ConfigEntry<bool> ShowMessages; public readonly ConfigEntry<string> HealthPotionPriority; public readonly ConfigEntry<string> StaminaPotionPriority; public readonly ConfigEntry<string> EitrPotionPriority; public readonly ConfigEntry<bool> ServerSyncEnabled; public readonly ConfigEntry<bool> ServerForceConfig; public PotionMasterConfig(ConfigFile cfg) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown ModEnabled = cfg.Bind<bool>("1 - General", "ModEnabled", true, "Master switch for PotionMaster. Set to false to fully disable."); HealthKey = cfg.Bind<KeyCode>("2 - Hotkeys", "HealthKey", (KeyCode)49, "Key to quick-consume a Health mead."); StaminaKey = cfg.Bind<KeyCode>("2 - Hotkeys", "StaminaKey", (KeyCode)50, "Key to quick-consume a Stamina mead."); EitrKey = cfg.Bind<KeyCode>("2 - Hotkeys", "EitrKey", (KeyCode)51, "Key to quick-consume an Eitr mead."); AutoPotionEnabled = cfg.Bind<bool>("3 - AutoPotion", "AutoPotionEnabled", false, "If true, PotionMaster will auto-drink meads when thresholds are crossed."); AutoHealthThreshold = cfg.Bind<float>("3 - AutoPotion", "AutoHealthThresholdPercent", 50f, new ConfigDescription("Auto drink health mead when HP % is below this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 99f), Array.Empty<object>())); AutoStaminaThreshold = cfg.Bind<float>("3 - AutoPotion", "AutoStaminaThresholdPercent", 30f, new ConfigDescription("Auto drink stamina mead when stamina % is below this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 99f), Array.Empty<object>())); AutoEitrThreshold = cfg.Bind<float>("3 - AutoPotion", "AutoEitrThresholdPercent", 30f, new ConfigDescription("Auto drink eitr mead when eitr % is below this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 99f), Array.Empty<object>())); ConsumeInterval = cfg.Bind<float>("4 - Consumption", "ConsumeIntervalSeconds", 1f, new ConfigDescription("Minimum seconds between any two mead consumptions.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); BypassCooldownAnimation = cfg.Bind<bool>("4 - Consumption", "BypassDrinkAnimation", true, "Skip the slow drink animation for instant consumption. Vanilla cooldowns on mead effects are respected."); ShowMessages = cfg.Bind<bool>("4 - Consumption", "ShowConsumeMessages", true, "Show a top-left message when a mead is consumed via hotkey."); HealthPotionPriority = cfg.Bind<string>("5 - Priority", "HealthPotionPriority", "MeadHealthMajor,MeadHealthMedium,MeadHealthMinor", "Comma-separated item prefab names in priority order. First match found in inventory is consumed."); StaminaPotionPriority = cfg.Bind<string>("5 - Priority", "StaminaPotionPriority", "MeadStaminaLingering,MeadStaminaMedium,MeadStaminaMinor", "Comma-separated item prefab names in priority order."); EitrPotionPriority = cfg.Bind<string>("5 - Priority", "EitrPotionPriority", "MeadEitrLingering,MeadEitrMinor", "Comma-separated item prefab names in priority order."); ServerSyncEnabled = cfg.Bind<bool>("6 - Server", "ServerSyncEnabled", true, "Sync config from server to all clients (dedicated servers)."); ServerForceConfig = cfg.Bind<bool>("6 - Server", "ServerForceConfig", false, "If true, the server overwrites client values. If false, values are merged (server takes precedence only for locked keys)."); } } } namespace PotionMaster.Sync { public static class ConfigSync { private const string RpcName = "PotionMaster_ConfigSync"; private static PotionMasterPlugin _plugin; private static readonly HashSet<string> SyncedKeys = new HashSet<string> { "ModEnabled", "AutoPotionEnabled", "AutoHealthThresholdPercent", "AutoStaminaThresholdPercent", "AutoEitrThresholdPercent", "ConsumeIntervalSeconds", "BypassDrinkAnimation", "HealthPotionPriority", "StaminaPotionPriority", "EitrPotionPriority", "ServerForceConfig" }; public static void Init(PotionMasterPlugin plugin) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _plugin = plugin; new Harmony("com.emergent.potionmaster.sync").PatchAll(typeof(ConfigSync)); } [HarmonyPatch(typeof(Game), "Start")] [HarmonyPostfix] private static void RegisterRpc() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<ZPackage>("PotionMaster_ConfigSync", (Action<long, ZPackage>)OnConfigReceived); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPostfix] private static void OnPeerConnected(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer() || !PotionMasterPlugin.Cfg.ServerSyncEnabled.Value) { return; } ZNetPeer peer = __instance.GetPeer(rpc); if (peer == null) { return; } try { ZPackage val = BuildPackage(); ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "PotionMaster_ConfigSync", new object[1] { val }); PotionMasterPlugin.Log.LogInfo((object)$"Sent config sync to peer {peer.m_uid}"); } catch (Exception ex) { PotionMasterPlugin.Log.LogError((object)("ConfigSync send failed: " + ex.Message)); } } private static ZPackage BuildPackage() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); List<(string, string)> list = new List<(string, string)>(); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)_plugin).Config) { if (SyncedKeys.Contains(item.Key.Key)) { ConfigEntryBase entry = ((BaseUnityPlugin)_plugin).Config[item.Key]; list.Add((item.Key.Key, ValueToString(entry))); } } val.Write(list.Count); foreach (var (text, text2) in list) { val.Write(text); val.Write(text2); } return val; } private static void OnConfigReceived(long sender, ZPackage pkg) { if (!PotionMasterPlugin.Cfg.ServerSyncEnabled.Value || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())) { return; } try { int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { string key = pkg.ReadString(); string value = pkg.ReadString(); if (TryFindEntry(key, out var entry)) { SetEntryFromString(entry, value); } } PotionMasterPlugin.Log.LogInfo((object)$"Applied server config sync ({num} keys)"); } catch (Exception ex) { PotionMasterPlugin.Log.LogError((object)("ConfigSync receive failed: " + ex.Message)); } } private static bool TryFindEntry(string key, out ConfigEntryBase entry) { foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)_plugin).Config) { if (item.Key.Key == key) { entry = ((BaseUnityPlugin)_plugin).Config[item.Key]; return true; } } entry = null; return false; } private static string ValueToString(ConfigEntryBase entry) { return entry.BoxedValue?.ToString() ?? ""; } private static void SetEntryFromString(ConfigEntryBase entry, string value) { try { Type settingType = entry.SettingType; object boxedValue = ((settingType == typeof(bool)) ? ((object)bool.Parse(value)) : ((settingType == typeof(float)) ? ((object)float.Parse(value, CultureInfo.InvariantCulture)) : ((settingType == typeof(int)) ? ((object)int.Parse(value, CultureInfo.InvariantCulture)) : ((!(settingType == typeof(string))) ? TomlTypeConverter.ConvertToValue(value, settingType) : value)))); entry.BoxedValue = boxedValue; } catch (Exception ex) { PotionMasterPlugin.Log.LogWarning((object)("Failed to set " + entry.Definition.Key + "=" + value + ": " + ex.Message)); } } } } namespace PotionMaster.Patches { [HarmonyPatch] public static class PlayerPatch { } }