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 ValheimQol v0.0.9
ValheimQol/ValheimQoL.dll
Decompiled 5 days 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.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using Splatform; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: AssemblyFileVersion("0.0.9")] [assembly: Guid("E74EB49A-461D-48EA-85BC-F462D60C98C4")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyProduct("ValheimQoL")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("Configurable quality-of-life patches for Valheim.")] [assembly: AssemblyTitle("ValheimQoL")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Marlthon")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.9.0")] [module: <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRules(11)] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded] internal sealed class <e057d413-f32e-43db-88ae-e78a5554fda6>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute : Attribute { public readonly byte[] NullableFlags; public <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class <54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContextAttribute : Attribute { public readonly byte Flag; public <54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimQoL { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [BepInPlugin("marlthon.ValheimQoL", "ValheimQoL", "0.0.9")] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] public sealed class ValheimQoLPlugin : BaseUnityPlugin { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] private sealed class ConfigurationManagerAttributes { public bool? Browsable = false; } internal const string ModName = "ValheimQoL"; internal const string ModVersion = "0.0.9"; internal const string Author = "marlthon"; internal const string ModGUID = "marlthon.ValheimQoL"; private static readonly string ConfigFileName = "marlthon.ValheimQoL.cfg"; private static readonly string ConfigFileFullPath; private readonly Harmony _harmony = new Harmony("marlthon.ValheimQoL"); [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] private FileSystemWatcher _configWatcher; internal static readonly ManualLogSource Log; private static readonly ConfigSync ConfigSync; private static ConfigEntry<bool> _serverConfigLocked; internal static bool IsLocalPlayerAdmin() { return ConfigSync != null && ConfigSync.IsAdmin; } private void Awake() { _serverConfigLocked = config("General", "Force Server Config", value: true, "Locks synchronized gameplay settings to the server. Example: when true, clients use the server's DayNight and WearNTear values instead of their local values."); ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked); Patch_Base.InitConfig(this); Patch_BulkHarvest.InitConfig(this); MenuQuickJoin.InitConfig(this); Patch_Clock.InitConfig(this); Patch_Container.InitConfig(this); Patch_DayNight.InitConfig(this); Patch_DeleteItem.InitConfig(this); Patch_HoverTimers.InitConfig(this); Patch_InfiniteFuel.InitConfig(this); Patch_ItemBalance.InitConfig(this); Patch_MapShare.InitConfig(this); NearbyContainerManager.InitConfig(this); Patch_PlantGrid.InitConfig(this); Patch_PortalProtection.InitConfig(this); Patch_Server.InitConfig(this); Patch_Smelter.InitConfig(this); Patch_ServerQoLFeatures.InitConfig(this); Patch_SwimmingEquipment.InitConfig(this); Patch_WearNTear.InitConfig(this); Patch_Workbench.InitConfig(this); _harmony.PatchAll(); Patch_DayNight.LogHarmonyPatchStatus(); SetupConfigWatcher(); Log.LogInfo((object)"ValheimQoL 0.0.9 carregado."); } private void OnDestroy() { if (_configWatcher != null) { _configWatcher.Changed -= ReadConfigValues; _configWatcher.Created -= ReadConfigValues; _configWatcher.Renamed -= ReadConfigValues; _configWatcher.Dispose(); _configWatcher = null; } Patch_InfiniteFuel.Shutdown(); Patch_ServerQoLFeatures.Shutdown(); Patch_Server.Shutdown(); _harmony.UnpatchSelf(); } private void SetupConfigWatcher() { string directoryName = Path.GetDirectoryName(ConfigFileFullPath); string fileName = Path.GetFileName(ConfigFileFullPath); if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName)) { _configWatcher = new FileSystemWatcher(directoryName, fileName) { IncludeSubdirectories = false, SynchronizingObject = ThreadingHelper.SynchronizingObject, EnableRaisingEvents = true }; _configWatcher.Changed += ReadConfigValues; _configWatcher.Created += ReadConfigValues; _configWatcher.Renamed += ReadConfigValues; } } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { ((BaseUnityPlugin)this).Config.Reload(); } catch (Exception ex) { Log.LogError((object)("Falha ao recarregar a configuração: " + ex)); } } public ConfigEntry<T> config<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, new object[1] { new ConfigurationManagerAttributes() }); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val2; } public ConfigEntry<T> config<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } internal ConfigEntry<T> configWithSyncReference<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting, out SyncedConfigEntry<T> syncedConfigEntry) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes() }); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val2; } static ValheimQoLPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; Log = Logger.CreateLogSource("ValheimQoL"); ConfigSync = new ConfigSync("marlthon.ValheimQoL") { DisplayName = "ValheimQoL", CurrentVersion = "0.0.9", MinimumRequiredVersion = "0.0.9", ModRequired = true }; _serverConfigLocked = null; } } [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [HarmonyPatch] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] public static class Patch_Base { private static ConfigEntry<float> CarryWeightBase; private static ConfigEntry<float> MegingjordBonus; private static ConfigEntry<float> BuildDistance; private static ConfigEntry<float> AutoPickupRange; private static ConfigEntry<float> InteractDistance; private static bool _appliedRuntimeTuningOnce; private const string MegingjordPrefab = "BeltStrength"; private const string MegingjordToken = "$item_beltstrength"; private const float VanillaCarryBase = 300f; private const float VanillaMegiBonus = 150f; public static void InitConfig(ValheimQoLPlugin plugin) { CarryWeightBase = plugin.config("BaseTweaks", "CarryWeightBase", 300f, "Sets the player's base carry capacity. Example: 450 allows carrying 450 weight before applying the Megingjord bonus."); MegingjordBonus = plugin.config("BaseTweaks", "MegingjordBonus", 150f, "Sets the carry capacity added by Megingjord. Example: 300 combined with CarryWeightBase=300 provides 600 total capacity."); BuildDistance = plugin.config("BaseTweaks", "BuildDistance", 5f, "Sets the maximum building placement distance in meters. Example: 10 allows placing pieces up to 10 meters away."); AutoPickupRange = plugin.config("BaseTweaks", "AutoPickupRange", 2f, "Sets the automatic item pickup radius in meters. Example: 4 collects nearby drops within 4 meters."); InteractDistance = plugin.config("BaseTweaks", "InteractDistance", 5f, "Sets the maximum interaction distance in meters. Example: 8 allows opening containers and using objects from up to 8 meters away. Vanilla: 5."); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] private static void Player_GetMaxCarryWeight_Postfix(Player __instance, ref float __result) { float num = CarryWeightBase.Value - 300f; __result += num; if (HasMegingjordEquipped(__instance)) { float num2 = MegingjordBonus.Value - 150f; __result += num2; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "Awake")] private static void Player_Awake_Postfix(Player __instance) { __instance.m_maxPlaceDistance = BuildDistance.Value; __instance.m_autoPickupRange = AutoPickupRange.Value; __instance.m_maxInteractDistance = InteractDistance.Value; if (!_appliedRuntimeTuningOnce && (Object)(object)Player.m_localPlayer == (Object)(object)__instance) { _appliedRuntimeTuningOnce = true; ((Character)Player.m_localPlayer).Message((MessageType)1, $"CarryWeight base: {CarryWeightBase.Value:0.#} | Megingjord: {MegingjordBonus.Value:0.#}", 0, (Sprite)null, false); ((Character)Player.m_localPlayer).Message((MessageType)1, $"Build distance: {BuildDistance.Value:0.#} | Auto-pickup: {AutoPickupRange.Value:0.#} | Interact: {InteractDistance.Value:0.#}", 0, (Sprite)null, false); } } private static bool HasMegingjordEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List<ItemData> allItems = inventory.GetAllItems(); foreach (ItemData item in allItems) { if (item != null && item.m_equipped) { if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "BeltStrength") { return true; } string text = item.m_shared?.m_name; if (!string.IsNullOrEmpty(text) && string.Equals(text, "$item_beltstrength", StringComparison.OrdinalIgnoreCase)) { return true; } if (text != null && text.IndexOf("belt", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } return false; } } [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [HarmonyPatch] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal static class Patch_BulkHarvest { private const KeyCode HarvestModifierKey = (KeyCode)304; private const int ColliderBufferSize = 256; private static readonly Collider[] HarvestColliders = (Collider[])(object)new Collider[256]; private static readonly HashSet<int> ProcessedObjects = new HashSet<int>(); private static ConfigEntry<bool> Enabled = null; private static ConfigEntry<float> HarvestRadius = null; private static ConfigEntry<int> MaxTargets = null; private static ConfigEntry<bool> HarvestSameTypeOnly = null; private static ConfigEntry<bool> IncludeBeehives = null; private static ConfigEntry<bool> ShowHoverHint = null; internal static void InitConfig(ValheimQoLPlugin plugin) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown Enabled = plugin.config("BulkHarvest", "Enabled", value: true, "Enables server-controlled area harvesting with LeftShift + Use. Example: false restores normal single-target harvesting."); HarvestRadius = plugin.config("BulkHarvest", "HarvestRadius", 3f, new ConfigDescription("Sets the search radius for bulk harvesting in meters. Example: 3 collects eligible plants and beehives within three meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); MaxTargets = plugin.config("BulkHarvest", "MaxTargets", 50, new ConfigDescription("Limits how many objects one bulk-harvest action can interact with, including the original target. Example: 50 prevents very large farms from being processed in one frame.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>())); HarvestSameTypeOnly = plugin.config("BulkHarvest", "HarvestSameTypeOnly", value: true, "Controls which nearby objects are harvested. Example: true harvests only the same plant type, while false harvests every eligible plant and beehive in range."); IncludeBeehives = plugin.config("BulkHarvest", "IncludeBeehives", value: true, "Includes beehives with available honey in bulk harvesting. Example: LeftShift + Use on one beehive collects honey from nearby ready beehives."); ShowHoverHint = plugin.config("BulkHarvest", "ShowHoverHint", value: true, "Adds a LeftShift + Use area-harvest hint to eligible plants and beehives. Example: false hides the hint without disabling bulk harvesting."); } private static bool IsEnabled() { return Enabled != null && Enabled.Value; } private static bool IsHarvestModifierHeld() { return ZInput.GetKey((KeyCode)304, false); } private static string GetPrefabName(GameObject gameObject) { return ((Object)(object)gameObject == (Object)null) ? string.Empty : Utils.GetPrefabName(gameObject); } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] private static bool IsEligiblePickable(Pickable pickable) { return (Object)(object)pickable != (Object)null && !pickable.GetPicked() && pickable.GetEnabled != 0; } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] private static bool IsEligibleBeehive(Beehive beehive) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) return IncludeBeehives != null && IncludeBeehives.Value && (Object)(object)beehive != (Object)null && beehive.GetHoneyLevel() > 0 && PrivateArea.CheckAccess(((Component)beehive).transform.position, 0f, false, false); } [return: <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] private static Interactable GetInteractable(GameObject gameObject) { return ((Object)(object)gameObject == (Object)null) ? null : gameObject.GetComponentInParent<Interactable>(); } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] private static bool MatchesRootType(Pickable candidatePickable, Beehive candidateBeehive, Pickable rootPickable, Beehive rootBeehive, [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)] string rootPrefabName) { if (HarvestSameTypeOnly == null || !HarvestSameTypeOnly.Value) { return true; } if ((Object)(object)rootPickable != (Object)null) { return (Object)(object)candidatePickable != (Object)null && GetPrefabName(((Component)candidatePickable).gameObject) == rootPrefabName; } return (Object)(object)rootBeehive != (Object)null && (Object)(object)candidateBeehive != (Object)null; } private static string BuildHoverHint() { return Localization.instance.Localize("\n[<color=yellow><b>LeftShift + $KEY_Use</b></color>] Harvest nearby"); } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "Interact")] private static void Player_Interact_Prefix(Player __instance, GameObject go, bool hold, bool alt) { //IL_0135: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled() || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)go == (Object)null || hold || ((Character)__instance).InAttack() || ((Character)__instance).InDodge() || !IsHarvestModifierHeld()) { return; } Interactable interactable = GetInteractable(go); Pickable val = (Pickable)(object)((interactable is Pickable) ? interactable : null); Beehive val2 = (Beehive)(object)((interactable is Beehive) ? interactable : null); if (!IsEligiblePickable(val) && !IsEligibleBeehive(val2)) { return; } MonoBehaviour val3 = (MonoBehaviour)(object)((interactable is MonoBehaviour) ? interactable : null); if ((Object)(object)val3 == (Object)null) { return; } string rootPrefabName = (((Object)(object)val == (Object)null) ? string.Empty : GetPrefabName(((Component)val).gameObject)); float num = ((HarvestRadius == null) ? 3f : Mathf.Clamp(HarvestRadius.Value, 1f, 20f)); int num2 = ((MaxTargets == null) ? 50 : Mathf.Clamp(MaxTargets.Value, 1, 200)); int instanceID = ((Object)((Component)val3).gameObject).GetInstanceID(); ProcessedObjects.Clear(); ProcessedObjects.Add(instanceID); int num3 = Physics.OverlapSphereNonAlloc(((Component)val3).transform.position, num, HarvestColliders, -1, (QueryTriggerInteraction)2); int num4 = 1; for (int i = 0; i < num3 && num4 < num2; i++) { Collider val4 = HarvestColliders[i]; HarvestColliders[i] = null; if ((Object)(object)val4 == (Object)null) { continue; } Pickable componentInParent = ((Component)val4).GetComponentInParent<Pickable>(); Beehive val5 = (((Object)(object)componentInParent == (Object)null) ? ((Component)val4).GetComponentInParent<Beehive>() : null); MonoBehaviour val6 = (MonoBehaviour)(((Object)(object)componentInParent != (Object)null) ? ((object)componentInParent) : ((object)val5)); if ((Object)(object)val6 == (Object)null) { continue; } int instanceID2 = ((Object)((Component)val6).gameObject).GetInstanceID(); if (!ProcessedObjects.Add(instanceID2) || !MatchesRootType(componentInParent, val5, val, val2, rootPrefabName)) { continue; } Interactable val7; if (IsEligiblePickable(componentInParent)) { val7 = (Interactable)(object)componentInParent; } else { if (!IsEligibleBeehive(val5)) { continue; } val7 = (Interactable)(object)val5; } if (val7 != null) { val7.Interact((Humanoid)(object)__instance, false, alt); num4++; } } for (int j = 0; j < num3; j++) { HarvestColliders[j] = null; } ProcessedObjects.Clear(); } [HarmonyPostfix] [HarmonyPatch(typeof(Pickable), "GetHoverText")] private static void Pickable_GetHoverText_Postfix(Pickable __instance, ref string __result) { if (IsEnabled() && ShowHoverHint != null && ShowHoverHint.Value && !string.IsNullOrEmpty(__result) && IsEligiblePickable(__instance)) { __result += BuildHoverHint(); } } [HarmonyPostfix] [HarmonyPatch(typeof(Beehive), "GetHoverText")] private static void Beehive_GetHoverText_Postfix(Beehive __instance, ref string __result) { if (IsEnabled() && ShowHoverHint != null && ShowHoverHint.Value && !string.IsNullOrEmpty(__result) && IsEligibleBeehive(__instance)) { __result += BuildHoverHint(); } } } [HarmonyPatch] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal static class MenuQuickJoin { [CompilerGenerated] private static class <>O { [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] public static UnityAction <0>__Connect; } private static ConfigEntry<bool> Enabled; private static ConfigEntry<string> ButtonText; private static ConfigEntry<string> ServerAddress; private static ConfigEntry<int> ServerPort; private static ConfigEntry<string> ServerPassword; public static void InitConfig(ValheimQoLPlugin plugin) { Enabled = plugin.config("QuickConnect", "Enabled", value: false, "Adds a direct-connect button to the main menu. Example: true displays the button; false keeps the vanilla menu unchanged.", synchronizedSetting: false); ButtonText = plugin.config("QuickConnect", "ButtonText", "My Server", "Sets the text shown on the direct-connect button. Example: My Community Server.", synchronizedSetting: false); ServerAddress = plugin.config("QuickConnect", "ServerAddress", "127.0.0.1", "Sets the server IP address or domain name. Example: 192.168.1.50 or play.example.com.", synchronizedSetting: false); ServerPort = plugin.config("QuickConnect", "ServerPort", 2456, "Sets the main Valheim server port. Example: 2456 is the standard game port.", synchronizedSetting: false); ServerPassword = plugin.config("QuickConnect", "ServerPassword", "", "Sets the server password. Example: Viking123. Leave empty for no password. Warning: this value is stored as plain text in the cfg file.", synchronizedSetting: false); } [HarmonyPostfix] [HarmonyPatch(typeof(FejdStartup), "SetupGui")] private static void SetupGui_Postfix(FejdStartup __instance) { if (Enabled.Value) { TryCreateButton(__instance); } } private static void TryCreateButton(FejdStartup startup) { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown if ((Object)(object)startup == (Object)null || (Object)(object)startup.m_menuList == (Object)null) { return; } Button[] componentsInChildren = startup.m_menuList.GetComponentsInChildren<Button>(true); if (componentsInChildren.Any([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (Button button) => (Object)(object)button != (Object)null && string.Equals(((Object)button).name, "ValheimQoL_QuickJoin", StringComparison.Ordinal))) { return; } Button val = ((IEnumerable<Button>)componentsInChildren).FirstOrDefault((Func<Button, bool>)([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (Button button) => (Object)(object)button != (Object)null)); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null) { ValheimQoLPlugin.Log.LogWarning((object)"[QuickConnect] Nenhum botão vanilla foi encontrado para servir como modelo."); return; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent, false); ((Object)val2).name = "ValheimQoL_QuickJoin"; val2.transform.SetSiblingIndex(0); SetButtonText(val2, ButtonText.Value); Button component = val2.GetComponent<Button>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val2); return; } ((UnityEventBase)component.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = component.onClick; object obj = <>O.<0>__Connect; if (obj == null) { UnityAction val3 = Connect; <>O.<0>__Connect = val3; obj = (object)val3; } ((UnityEvent)onClick).AddListener((UnityAction)obj); Button[] array = startup.m_menuButtons ?? Array.Empty<Button>(); if (!array.Contains(component)) { startup.m_menuButtons = ((IEnumerable<Button>)(object)new Button[1] { component }).Concat(array).ToArray(); } } private static void Connect() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) FejdStartup instance = FejdStartup.m_instance; if (!((Object)(object)instance == (Object)null)) { string text = ((ServerAddress.Value == null) ? string.Empty : ServerAddress.Value.Trim()); int num = Mathf.Clamp(ServerPort.Value, 1, 65535); if (string.IsNullOrEmpty(text)) { ValheimQoLPlugin.Log.LogWarning((object)"[QuickConnect] ServerAddress está vazio no arquivo de configuração."); return; } FejdStartup.ServerPassword = ServerPassword.Value ?? string.Empty; instance.m_queuedJoinServer = new ServerJoinData(new ServerJoinDataDedicated(text + ":" + num)); instance.HideAll(); instance.ShowCharacterSelection(); } } private static void SetButtonText(GameObject buttonObject, string text) { string text2 = (string.IsNullOrWhiteSpace(text) ? "Meu Servidor" : text.Trim()); Text[] componentsInChildren = buttonObject.GetComponentsInChildren<Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { componentsInChildren[i].text = text2; } } TMP_Text[] componentsInChildren2 = buttonObject.GetComponentsInChildren<TMP_Text>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { if ((Object)(object)componentsInChildren2[j] != (Object)null) { componentsInChildren2[j].text = text2; } } } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [HarmonyPatch] public static class Patch_Clock { private static ConfigEntry<bool> ClockEnabled; private static ConfigEntry<float> ClockUpdateInterval; private static ConfigEntry<int> ClockFontSize; private static ConfigEntry<bool> ShowRealTime; private static ConfigEntry<bool> RealTimeUse24H; private static ConfigEntry<int> RealTimeFontSize; private static float _nextUpdateTime; private static GameObject _clockRoot; private static TextMeshProUGUI _timeText; private static TextMeshProUGUI _realText; private const float ROOT_OFFSET_Y = -60f; private const float ROOT_WIDTH = 600f; private const float ROOT_HEIGHT = 120f; public static void InitConfig(ValheimQoLPlugin plugin) { ClockEnabled = plugin.config("Clock", "ClockEnabled", value: true, "Shows the in-game day and time on the HUD. Example: false completely hides the custom clock."); ClockUpdateInterval = plugin.config("Clock", "ClockUpdateInterval", 1f, "Sets how often the clock refreshes, in seconds. Example: 0.5 updates twice per second; 2 updates every two seconds."); ClockFontSize = plugin.config("Clock", "ClockFontSize", 40, "Sets the font size of the in-game clock. Example: 32 produces smaller text; 48 produces larger text."); ShowRealTime = plugin.config("Clock", "ShowRealTime", value: true, "Shows the computer's local time below the in-game clock. Example: false displays only the Valheim time."); RealTimeUse24H = plugin.config("Clock", "RealTimeUse24H", value: true, "Selects the real-time format. Example: true displays 18:30; false displays 06:30 PM."); RealTimeFontSize = plugin.config("Clock", "RealTimeFontSize", 28, "Sets the font size of the real-world clock. Example: 20 makes the secondary clock less prominent."); } [HarmonyPatch(typeof(Hud), "Update")] [HarmonyPostfix] private static void Hud_Update_Postfix(Hud __instance) { if (!ClockEnabled.Value) { if ((Object)(object)_clockRoot != (Object)null) { Object.Destroy((Object)(object)_clockRoot); } return; } if ((Object)(object)_clockRoot == (Object)null) { CreateClock(__instance); } if (!(Time.unscaledTime < _nextUpdateTime)) { _nextUpdateTime = Time.unscaledTime + Mathf.Max(0.2f, ClockUpdateInterval.Value); UpdateClockTexts(); } } private static void CreateClock(Hud hud) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) _clockRoot = new GameObject("MarlthonClockRoot"); _clockRoot.transform.SetParent(hud.m_rootObject.transform, false); RectTransform val = _clockRoot.AddComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.anchoredPosition = new Vector2(0f, -60f); val.sizeDelta = new Vector2(600f, 120f); TMP_FontAsset font = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (TMP_FontAsset f) => ((Object)f).name.ToLower().Contains("norsebold"))) ?? TMP_Settings.defaultFontAsset; _timeText = CreateTMP("ClockTime", font, ClockFontSize.Value, new Color(1f, 0.85f, 0.2f)); ((TMP_Text)_timeText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_timeText).transform.SetParent(_clockRoot.transform, false); ((TMP_Text)_timeText).rectTransform.anchoredPosition = new Vector2(0f, 10f); _realText = CreateTMP("ClockReal", font, RealTimeFontSize.Value, new Color(0.8f, 0.8f, 0.8f)); ((TMP_Text)_realText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_realText).transform.SetParent(_clockRoot.transform, false); ((TMP_Text)_realText).rectTransform.anchoredPosition = new Vector2(0f, -30f); } private static TextMeshProUGUI CreateTMP(string name, TMP_FontAsset font, int size, Color color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.SetActive(false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).font = font ?? TMP_Settings.defaultFontAsset; ((TMP_Text)val2).fontSize = size; ((Graphic)val2).color = color; ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val2).richText = true; Material val3 = new Material(((TMP_Text)val2).fontSharedMaterial); val3.SetFloat("_OutlineWidth", 0.1f); val3.SetColor("_OutlineColor", new Color(0f, 0f, 0f, 0.6f)); ((TMP_Text)val2).fontSharedMaterial = val3; val.SetActive(true); return val2; } private static void UpdateClockTexts() { if (!((Object)(object)EnvMan.instance == (Object)null)) { float num = EnvMan.instance.GetDay(); float dayFraction = EnvMan.instance.GetDayFraction(); int num2 = Mathf.FloorToInt(dayFraction * 24f); int num3 = Mathf.FloorToInt((dayFraction * 24f - (float)num2) * 60f); ((TMP_Text)_timeText).text = $"Day {num} - {num2:00}:{num3:00}"; if (ShowRealTime.Value) { DateTime now = DateTime.Now; string text = (RealTimeUse24H.Value ? now.ToString("HH:mm") : now.ToString("hh:mm tt")); ((TMP_Text)_realText).text = "<color=#CCCCCC>Real Life: " + text + "</color>"; } else { ((TMP_Text)_realText).text = ""; } } } } [HarmonyPatch(typeof(SE_Rested), "GetNearbyComfortPieces")] public static class SE_Rested_GetNearbyComfortPieces_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 10f) { instruction.operand = 20f; } yield return instruction; } } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [HarmonyPatch(typeof(EffectArea), "Awake")] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] public static class EffectArea_Awake_Patch { private static void Postfix(EffectArea __instance) { //IL_0002: 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_000a: Invalid comparison between Unknown and I4 if ((__instance.m_type & 1) > 0) { UpdateFireEffectRadius(__instance, 20f); } } private static void UpdateFireEffectRadius(EffectArea area, float newRadius) { SphereCollider component = ((Component)area).GetComponent<SphereCollider>(); if ((Object)(object)component != (Object)null) { component.radius = newRadius; } } } [HarmonyPatch] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] public static class Patch_Container { private static ConfigEntry<bool> ChestControl; private static ConfigEntry<int> PrivateRows; private static ConfigEntry<int> PrivateCols; private static ConfigEntry<int> WoodRows; private static ConfigEntry<int> WoodCols; private static ConfigEntry<int> IronRows; private static ConfigEntry<int> IronCols; private static ConfigEntry<int> BlackmetalRows; private static ConfigEntry<int> BlackmetalCols; private static ConfigEntry<int> BarrelRows; private static ConfigEntry<int> BarrelCols; private static ConfigEntry<int> GraustenRows; private static ConfigEntry<int> GraustenCols; private static ConfigEntry<int> WardrobeRows; private static ConfigEntry<int> WardrobeCols; public static void InitConfig(ValheimQoLPlugin plugin) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Expected O, but got Unknown //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Expected O, but got Unknown //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Expected O, but got Unknown ChestControl = plugin.config("ContainerControl", "EnableChestControl", value: true, "Enables custom sizes for supported vanilla containers. Example: false keeps every chest at its vanilla size."); PrivateRows = plugin.config("Chests", "PrivateChestRows", 2, new ConfigDescription("Sets the row count of the personal chest. Example: 4 rows with PrivateChestCols=3 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>())); PrivateCols = plugin.config("Chests", "PrivateChestCols", 3, new ConfigDescription("Sets the column count of the personal chest. Example: 6 columns with PrivateChestRows=2 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>())); WoodRows = plugin.config("Chests", "WoodChestRows", 2, new ConfigDescription("Sets the row count of the wooden chest. Example: 4 rows with WoodChestCols=5 provides 20 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); WoodCols = plugin.config("Chests", "WoodChestCols", 5, new ConfigDescription("Sets the column count of the wooden chest. Example: 8 columns with WoodChestRows=2 provides 16 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 8), Array.Empty<object>())); IronRows = plugin.config("Chests", "IronChestRows", 4, new ConfigDescription("Sets the row count of the reinforced chest. Example: 6 rows with IronChestCols=6 provides 36 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>())); IronCols = plugin.config("Chests", "IronChestCols", 6, new ConfigDescription("Sets the column count of the reinforced chest. Example: 8 columns with IronChestRows=4 provides 32 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>())); BlackmetalRows = plugin.config("Chests", "BlackmetalChestRows", 4, new ConfigDescription("Sets the row count of the black metal chest. Example: 6 rows with BlackmetalChestCols=8 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 20), Array.Empty<object>())); BlackmetalCols = plugin.config("Chests", "BlackmetalChestCols", 8, new ConfigDescription("Sets the column count of the black metal chest. Example: 8 columns with BlackmetalChestRows=4 provides 32 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(6, 8), Array.Empty<object>())); BarrelRows = plugin.config("Chests", "BarrelChestRows", 3, new ConfigDescription("Sets the row count of the barrel container. Example: 4 rows with BarrelChestCols=3 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); BarrelCols = plugin.config("Chests", "BarrelChestCols", 3, new ConfigDescription("Sets the column count of the barrel container. Example: 6 columns with BarrelChestRows=3 provides 18 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>())); GraustenRows = plugin.config("Chests", "GraustenChestRows", 5, new ConfigDescription("Sets the row count of the Grausten Chest. Vanilla: 5. Example: 6 rows with GraustenChestCols=8 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>())); GraustenCols = plugin.config("Chests", "GraustenChestCols", 8, new ConfigDescription("Sets the column count of the Grausten Chest. Vanilla: 8. Example: 8 columns with GraustenChestRows=6 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>())); WardrobeRows = plugin.config("Chests", "WardrobeRows", 10, new ConfigDescription("Sets the row count of the Wardrobe. Vanilla: 10. Example: 12 rows with WardrobeCols=5 provides 60 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>())); WardrobeCols = plugin.config("Chests", "WardrobeCols", 5, new ConfigDescription("Sets the column count of the Wardrobe. Vanilla: 5. Example: 8 columns with WardrobeRows=10 provides 80 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>())); SubscribeChanges(); } private static void SubscribeChanges() { Watch<bool>(ChestControl); Watch<int>(PrivateRows); Watch<int>(PrivateCols); Watch<int>(WoodRows); Watch<int>(WoodCols); Watch<int>(IronRows); Watch<int>(IronCols); Watch<int>(BlackmetalRows); Watch<int>(BlackmetalCols); Watch<int>(BarrelRows); Watch<int>(BarrelCols); Watch<int>(GraustenRows); Watch<int>(GraustenCols); Watch<int>(WardrobeRows); Watch<int>(WardrobeCols); static void Watch<T>([<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(new byte[] { 1, 0 })] ConfigEntry<T> entry) { if (entry != null) { entry.SettingChanged += [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (object _, EventArgs _) => { UpdateAllChests(); }; } } } [HarmonyPatch(typeof(Container), "Awake")] [HarmonyPostfix] private static void Container_Awake_Postfix(Container __instance) { TryResize(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "Interact")] private static void Container_Interact_Prefix(Container __instance, Humanoid character, bool hold, bool alt) { TryResize(__instance); } private static void TryResize(Container container) { if (!ChestControl.Value || (Object)(object)container == (Object)null || ((Object)container).name.StartsWith("Treasure")) { return; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) == 0) { return; } Inventory inventory = container.GetInventory(); if (inventory != null) { switch (((Object)((Component)container).transform.root).name.Trim().Replace("(Clone)", "")) { case "piece_chest_private": inventory.m_height = PrivateRows.Value; inventory.m_width = PrivateCols.Value; break; case "piece_chest_wood": inventory.m_height = WoodRows.Value; inventory.m_width = WoodCols.Value; break; case "piece_chest": inventory.m_height = IronRows.Value; inventory.m_width = IronCols.Value; break; case "piece_chest_blackmetal": inventory.m_height = BlackmetalRows.Value; inventory.m_width = BlackmetalCols.Value; break; case "piece_chest_barrel": inventory.m_height = BarrelRows.Value; inventory.m_width = BarrelCols.Value; break; case "piece_chest_grausten": ApplyConfiguredSize(inventory, GraustenRows.Value, GraustenCols.Value); break; case "piece_chest_warderobe": ApplyConfiguredSize(inventory, WardrobeRows.Value, WardrobeCols.Value); break; } } } private static void ApplyConfiguredSize(Inventory inventory, int rows, int columns) { inventory.m_height = rows; inventory.m_width = columns; } private static void UpdateAllChests() { if (!ChestControl.Value) { return; } Container[] array = Resources.FindObjectsOfTypeAll<Container>(); foreach (Container val in array) { if (!((Object)(object)val == (Object)null) && !((Object)val).name.StartsWith("Treasure")) { ZNetView nview = val.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0) { TryResize(val); } } } ValheimQoLPlugin.Log.LogInfo((object)"[ValheimQoL/Container] Tamanhos dos baús atualizados."); } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [HarmonyPatch] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] public static class Patch_DayNight { private const float DefaultDayDurationMinutes = 21f; private const float DefaultNightDurationMinutes = 9f; private const long FallbackVanillaCycleLengthSeconds = 1800L; private const double TimeSkipDurationSeconds = 12.0; private const double RuntimeVerificationDurationSeconds = 10.0; private static ConfigEntry<bool> Enabled = null; private static ConfigEntry<float> DayDurationMinutes = null; private static ConfigEntry<float> NightDurationMinutes = null; private static long _vanillaCycleLengthSeconds = 1800L; private static bool? _lastLoggedDayState; private static bool _loggedFirstRescaleExecution; private static double _phaseStartedAtNetTime; private static bool _phaseStartedAtBoundary; private static bool _phaseMeasurementInvalidated; private static bool? _verificationPhase; private static double _verificationStartedAtNetTime; private static float _verificationStartedAtMappedFraction; private static bool _runtimeVerificationCompleted; public static void InitConfig(ValheimQoLPlugin plugin) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown RemoveLegacySettings(plugin); Enabled = plugin.config("DayNight", "Enabled", value: false, "Enables custom real-time day and night durations. Example: false restores Valheim's original cycle duration and time mapping."); DayDurationMinutes = plugin.config("DayNight", "DayDurationMinutes", 21f, new ConfigDescription("Sets how many real minutes daytime lasts. Valheim's vanilla daytime duration is 21 real minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>())); NightDurationMinutes = plugin.config("DayNight", "NightDurationMinutes", 9f, new ConfigDescription("Sets how many real minutes nighttime lasts. Valheim's vanilla nighttime duration is 9 real minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>())); Enabled.SettingChanged += OnConfigChanged; DayDurationMinutes.SettingChanged += OnConfigChanged; NightDurationMinutes.SettingChanged += OnConfigChanged; } internal static void LogHarmonyPatchStatus() { MethodInfo methodInfo = AccessTools.Method(typeof(EnvMan), "RescaleDayFraction", (Type[])null, (Type[])null); Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); bool flag = false; if (patchInfo != null) { for (int i = 0; i < patchInfo.Prefixes.Count; i++) { if (patchInfo.Prefixes[i].owner == "marlthon.ValheimQoL") { flag = true; break; } } } ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Harmony status: EnvMan.RescaleDayFraction prefix registered=" + flag + ".")); } private static void RemoveLegacySettings(ValheimQoLPlugin plugin) { //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown string configFilePath = ((BaseUnityPlugin)plugin).Config.ConfigFilePath; if (!File.Exists(configFilePath)) { return; } bool flag = false; bool flag2 = false; try { string[] array = File.ReadAllLines(configFilePath); bool flag3 = false; for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal)) { flag3 = text.Equals("[DayNight]", StringComparison.Ordinal); } else if (flag3 && !text.StartsWith("#", StringComparison.Ordinal)) { int num = text.IndexOf('='); if (num > 0) { string text2 = text.Substring(0, num).Trim(); flag |= text2.Equals("CycleLengthSec", StringComparison.Ordinal); flag2 |= text2.Equals("DayFractionOfCycle", StringComparison.Ordinal); } } } } catch (Exception ex) { ValheimQoLPlugin.Log.LogWarning((object)("[DayNight] Could not inspect legacy configuration entries: " + ex.Message)); return; } if (flag || flag2) { if (flag) { ConfigDefinition val = new ConfigDefinition("DayNight", "CycleLengthSec"); ((BaseUnityPlugin)plugin).Config.Bind<int>(val, 1800, new ConfigDescription("Obsolete DayNight setting.", (AcceptableValueBase)null, Array.Empty<object>())); ((BaseUnityPlugin)plugin).Config.Remove(val); } if (flag2) { ConfigDefinition val2 = new ConfigDefinition("DayNight", "DayFractionOfCycle"); ((BaseUnityPlugin)plugin).Config.Bind<float>(val2, 0.5f, new ConfigDescription("Obsolete DayNight setting.", (AcceptableValueBase)null, Array.Empty<object>())); ((BaseUnityPlugin)plugin).Config.Remove(val2); } ((BaseUnityPlugin)plugin).Config.Save(); ValheimQoLPlugin.Log.LogInfo((object)"[DayNight] Removed obsolete CycleLengthSec and DayFractionOfCycle settings. The new settings use explicit real-time minutes."); } } private static void OnConfigChanged(object sender, EventArgs eventArgs) { ApplySettings(EnvMan.instance); } [HarmonyPostfix] [HarmonyPatch(typeof(EnvMan), "Awake")] private static void EnvMan_Awake_Postfix(EnvMan __instance) { _vanillaCycleLengthSeconds = Math.Max(60L, __instance.m_dayLengthSec); ApplySettings(__instance); } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] private static void ApplySettings(EnvMan envMan) { if (!((Object)(object)envMan == (Object)null)) { _lastLoggedDayState = null; _loggedFirstRescaleExecution = false; _phaseStartedAtNetTime = 0.0; _phaseStartedAtBoundary = false; _phaseMeasurementInvalidated = false; _verificationPhase = null; _verificationStartedAtNetTime = 0.0; _verificationStartedAtMappedFraction = 0f; _runtimeVerificationCompleted = false; if (!Enabled.Value) { envMan.m_dayLengthSec = _vanillaCycleLengthSeconds; ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Disabled. Restored Valheim cycle length to " + _vanillaCycleLengthSeconds + " real seconds.")); return; } double configuredDaySeconds = GetConfiguredDaySeconds(); double configuredNightSeconds = GetConfiguredNightSeconds(); long num = (envMan.m_dayLengthSec = Math.Max(60L, (long)Math.Round(configuredDaySeconds + configuredNightSeconds, MidpointRounding.AwayFromZero))); ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Applied: day=" + FormatMinutes(configuredDaySeconds / 60.0) + " real minute(s), night=" + FormatMinutes(configuredNightSeconds / 60.0) + " real minute(s), cycle=" + FormatMinutes((double)num / 60.0) + " real minute(s), day fraction=" + FormatNumber(GetConfiguredDayFraction()) + ".")); } } [HarmonyPrefix] [HarmonyPatch(typeof(EnvMan), "RescaleDayFraction")] private static bool EnvMan_RescaleDayFraction_Prefix(float fraction, ref float __result) { if (!Enabled.Value) { return true; } float configuredDayFraction = GetConfiguredDayFraction(); float num = (1f - configuredDayFraction) * 0.5f; float num2 = num + configuredDayFraction; float num3 = Mathf.Repeat(fraction, 1f); if (num3 < num) { __result = Mathf.Lerp(0f, 0.25f, num3 / num); } else if (num3 < num2) { __result = Mathf.Lerp(0.25f, 0.75f, (num3 - num) / configuredDayFraction); } else { __result = Mathf.Lerp(0.75f, 1f, (num3 - num2) / num); } LogRuntimeState(num3, __result, num, num2); return false; } private static void LogRuntimeState(float rawFraction, float mappedFraction, float morningStart, float eveningStart) { bool flag = rawFraction >= morningStart && rawFraction < eveningStart; UpdateRuntimeVerification(flag, mappedFraction); if (!_loggedFirstRescaleExecution) { _loggedFirstRescaleExecution = true; ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Rescale prefix is executing. Raw fraction=" + FormatNumber(rawFraction) + ", mapped clock fraction=" + FormatNumber(mappedFraction) + ".")); } if (!_lastLoggedDayState.HasValue || _lastLoggedDayState.Value != flag) { bool hasValue = _lastLoggedDayState.HasValue; double num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0); if (hasValue && _phaseStartedAtBoundary && !_phaseMeasurementInvalidated && num >= _phaseStartedAtNetTime) { bool value = _lastLoggedDayState.Value; double num2 = num - _phaseStartedAtNetTime; double num3 = (value ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds()); ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Completed " + (value ? "DAY" : "NIGHT") + ": measured duration=" + FormatMinutes(num2 / 60.0) + " real minute(s), configured duration=" + FormatMinutes(num3 / 60.0) + " real minute(s).")); } _lastLoggedDayState = flag; _phaseStartedAtNetTime = num; _phaseStartedAtBoundary = hasValue; _phaseMeasurementInvalidated = false; ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Phase=" + (flag ? "DAY" : "NIGHT") + ", expected duration=" + FormatMinutes((flag ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds()) / 60.0) + " real minute(s), clock=" + FormatClock(mappedFraction) + ".")); } } private static void UpdateRuntimeVerification(bool isDay, float mappedFraction) { if (_runtimeVerificationCompleted || (Object)(object)ZNet.instance == (Object)null) { return; } double timeSeconds = ZNet.instance.GetTimeSeconds(); if (_phaseMeasurementInvalidated || !_verificationPhase.HasValue || _verificationPhase.Value != isDay) { _verificationPhase = isDay; _verificationStartedAtNetTime = timeSeconds; _verificationStartedAtMappedFraction = mappedFraction; return; } double num = timeSeconds - _verificationStartedAtNetTime; if (!(num < 10.0)) { double num2 = (isDay ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds()); double value = num * 720.0 / num2; double value2 = (double)Mathf.Repeat(mappedFraction - _verificationStartedAtMappedFraction, 1f) * 24.0 * 60.0; _runtimeVerificationCompleted = true; ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] 10-second runtime verification: phase=" + (isDay ? "DAY" : "NIGHT") + ", elapsed=" + FormatNumber(num) + " real second(s), clock advanced=" + FormatNumber(value2) + " minute(s), expected=" + FormatNumber(value) + " minute(s).")); } } [HarmonyPrefix] [HarmonyPatch(typeof(EnvMan), "GetMorningStartSec")] private static bool EnvMan_GetMorningStartSec_Prefix(EnvMan __instance, int day, ref double __result) { if (!Enabled.Value) { return true; } double morningFraction = GetMorningFraction(); __result = (double)day * (double)__instance.m_dayLengthSec + (double)__instance.m_dayLengthSec * morningFraction; return false; } [HarmonyPatch(typeof(EnvMan), "SkipToMorning")] [HarmonyPrefix] private static bool EnvMan_SkipToMorning_Prefix(EnvMan __instance) { if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null) { return true; } double timeSeconds = ZNet.instance.GetTimeSeconds(); double morningFraction = GetMorningFraction(); double num = __instance.m_dayLengthSec; int num2 = Mathf.FloorToInt((float)((timeSeconds - num * morningFraction) / num)); double num3 = (double)(num2 + 1) * num + num * morningFraction; __instance.m_skipTime = true; __instance.m_skipToTime = num3; __instance.m_timeSkipSpeed = (num3 - timeSeconds) / 12.0; _phaseMeasurementInvalidated = true; return false; } private static double GetConfiguredDaySeconds() { float num = ((DayDurationMinutes == null) ? 21f : DayDurationMinutes.Value); return Math.Max(30.0, (double)num * 60.0); } private static double GetConfiguredNightSeconds() { float num = ((NightDurationMinutes == null) ? 9f : NightDurationMinutes.Value); return Math.Max(30.0, (double)num * 60.0); } private static float GetConfiguredDayFraction() { double configuredDaySeconds = GetConfiguredDaySeconds(); double num = configuredDaySeconds + GetConfiguredNightSeconds(); return (float)(configuredDaySeconds / num); } private static double GetMorningFraction() { return (1.0 - (double)GetConfiguredDayFraction()) * 0.5; } private static string FormatClock(float fraction) { float num = Mathf.Repeat(fraction, 1f) * 24f * 60f; int num2 = Mathf.FloorToInt(num / 60f); int num3 = Mathf.FloorToInt(num) % 60; return num2.ToString("00", CultureInfo.InvariantCulture) + ":" + num3.ToString("00", CultureInfo.InvariantCulture); } private static string FormatMinutes(double value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatNumber(double value) { return value.ToString("0.0000", CultureInfo.InvariantCulture); } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [HarmonyPatch] public static class Patch_DeleteItem { private static ConfigEntry<KeyCode> DeleteKey; public static void InitConfig(ValheimQoLPlugin plugin) { DeleteKey = plugin.config<KeyCode>("DeleteItem", "DeleteKey", (KeyCode)127, "Sets the key that permanently deletes the item currently held by the inventory cursor. Example: Delete uses the keyboard Delete key. Equipped items cannot be deleted.", synchronizedSetting: false); } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void PlayerUpdate_Postfix(Player __instance) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)InventoryGui.instance == (Object)null) && Input.GetKeyDown(DeleteKey.Value)) { TryDeleteSelectedItem(); } } private static void TryDeleteSelectedItem() { InventoryGui instance = InventoryGui.instance; object? value = AccessTools.Field(typeof(InventoryGui), "m_dragItem").GetValue(instance); ItemData val = (ItemData)((value is ItemData) ? value : null); object? value2 = AccessTools.Field(typeof(InventoryGui), "m_dragInventory").GetValue(instance); Inventory val2 = (Inventory)((value2 is Inventory) ? value2 : null); object? value3 = AccessTools.Field(typeof(InventoryGui), "m_dragGo").GetValue(instance); GameObject val3 = (GameObject)((value3 is GameObject) ? value3 : null); if (val == null || val2 == null) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "No item selected.", 0, (Sprite)null, false); } return; } if (val.m_equipped) { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)1, "Cannot delete equipped item!", 0, (Sprite)null, false); } return; } val2.RemoveItem(val); if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } AccessTools.Field(typeof(InventoryGui), "m_dragGo").SetValue(instance, null); AccessTools.Field(typeof(InventoryGui), "m_dragItem").SetValue(instance, null); AccessTools.Field(typeof(InventoryGui), "m_dragInventory").SetValue(instance, null); Player localPlayer3 = Player.m_localPlayer; if (localPlayer3 != null) { ((Character)localPlayer3).Message((MessageType)1, "Deleted: " + val.m_shared.m_name, 0, (Sprite)null, false); } Player localPlayer4 = Player.m_localPlayer; if ((Object)(object)localPlayer4 != (Object)null) { instance.m_playerGrid.UpdateInventory(((Humanoid)localPlayer4).GetInventory(), localPlayer4, (ItemData)null); } } } [HarmonyPatch] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal static class Patch_HoverTimers { private const string RemainingLabel = "Remaining"; private const string PausedLabel = "Paused"; private static ConfigEntry<bool> ShowPlantTimer; private static ConfigEntry<bool> ShowFermenterTimer; private static ConfigEntry<bool> ShowCookingTimer; private static ConfigEntry<bool> ShowSmelterTimer; private static ConfigEntry<bool> ShowBeehiveTimer; private static ConfigEntry<bool> ShowPickableTimer; private static ConfigEntry<bool> ShowTamingTimer; private static ConfigEntry<bool> ShowGrowthTimer; internal static void InitConfig(ValheimQoLPlugin plugin) { ShowPlantTimer = plugin.config("HoverTimers", "ShowPlantTimer", value: true, "Shows the remaining growth time when looking at a healthy plant. Example: false hides only the plant timer.", synchronizedSetting: false); ShowFermenterTimer = plugin.config("HoverTimers", "ShowFermenterTimer", value: true, "Shows the remaining processing time when looking at an active fermenter. Example: false hides only the fermenter timer.", synchronizedSetting: false); ShowCookingTimer = plugin.config("HoverTimers", "ShowCookingTimer", value: true, "Shows the remaining cooking time for every uncooked food slot on Cooking Stations and Stone Ovens. Example: false hides only cooking timers.", synchronizedSetting: false); ShowSmelterTimer = plugin.config("HoverTimers", "ShowSmelterTimer", value: true, "Shows the remaining time for the item currently processing in furnaces, kilns, windmills, spinning wheels and Eitr refineries. Example: false hides only these timers.", synchronizedSetting: false); ShowBeehiveTimer = plugin.config("HoverTimers", "ShowBeehiveTimer", value: true, "Shows the remaining production time for the next honey when looking at a beehive. Example: false hides only the beehive timer.", synchronizedSetting: false); ShowPickableTimer = plugin.config("HoverTimers", "ShowPickableTimer", value: true, "Shows the remaining respawn time when looking at a harvested pickable resource. Example: false hides only the pickable timer.", synchronizedSetting: false); ShowTamingTimer = plugin.config("HoverTimers", "ShowTamingTimer", value: true, "Shows the remaining taming time when looking at an untamed creature. Example: false hides only the taming timer.", synchronizedSetting: false); ShowGrowthTimer = plugin.config("HoverTimers", "ShowGrowthTimer", value: true, "Shows the remaining growth time when looking at offspring or a growing egg. Example: false hides only offspring and egg growth timers.", synchronizedSetting: false); } private static bool TryGetZDO(Component component, out ZDO zdo) { zdo = null; ZNetView component2 = component.GetComponent<ZNetView>(); if ((Object)(object)component2 == (Object)null || !component2.IsValid()) { return false; } zdo = component2.GetZDO(); return zdo != null; } private static bool TryGetElapsedSeconds(long startTicks, out double elapsedSeconds) { elapsedSeconds = 0.0; if (startTicks <= 0 || (Object)(object)ZNet.instance == (Object)null) { return false; } try { DateTime dateTime = new DateTime(startTicks); elapsedSeconds = (ZNet.instance.GetTime() - dateTime).TotalSeconds; if (elapsedSeconds < 0.0) { elapsedSeconds = 0.0; } return true; } catch (ArgumentOutOfRangeException) { return false; } } private static string FormatDuration(double seconds) { TimeSpan timeSpan = TimeSpan.FromSeconds(Math.Ceiling(Math.Max(0.0, seconds))); if (timeSpan.TotalDays >= 1.0) { return (int)timeSpan.TotalDays + "d " + timeSpan.Hours + "h"; } if (timeSpan.TotalHours >= 1.0) { return (int)timeSpan.TotalHours + "h " + timeSpan.Minutes + "m"; } if (timeSpan.TotalMinutes >= 1.0) { return (int)timeSpan.TotalMinutes + "m " + timeSpan.Seconds + "s"; } return timeSpan.Seconds + "s"; } private static void AppendRemaining(ref string hoverText, double remainingSeconds) { if (!(remainingSeconds <= 0.0)) { string text = "Remaining: " + FormatDuration(remainingSeconds); hoverText = (string.IsNullOrEmpty(hoverText) ? text : (hoverText + "\n" + text)); } } private static void AppendPaused(ref string hoverText) { hoverText = (string.IsNullOrEmpty(hoverText) ? "Paused" : (hoverText + "\nPaused")); } private static bool ContainsRemainingTimer(string hoverText) { return !string.IsNullOrEmpty(hoverText) && hoverText.IndexOf("Remaining:", StringComparison.Ordinal) >= 0; } private static bool TryGetGrowthRemaining(Component component, out double remainingSeconds) { remainingSeconds = 0.0; if (ShowGrowthTimer == null || !ShowGrowthTimer.Value) { return false; } Growup component2 = component.GetComponent<Growup>(); BaseAI component3 = component.GetComponent<BaseAI>(); if ((Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null) { return false; } remainingSeconds = (double)component2.m_growTime - component3.GetTimeSinceSpawned().TotalSeconds; return remainingSeconds > 0.0; } private static void AppendCookingTimers(CookingStation cookingStation, ref string hoverText) { if (ShowCookingTimer == null || !ShowCookingTimer.Value || (Object)(object)cookingStation == (Object)null || cookingStation.m_slots == null || cookingStation.m_conversion == null || !TryGetZDO((Component)(object)cookingStation, out var zdo)) { return; } string text = ""; for (int i = 0; i < cookingStation.m_slots.Length; i++) { string text2 = "slot" + i; string text3 = zdo.GetString(text2, ""); if (string.IsNullOrEmpty(text3) || zdo.GetInt("slotstatus" + i, 0) != 0) { continue; } ItemConversion val = null; for (int j = 0; j < cookingStation.m_conversion.Count; j++) { ItemConversion val2 = cookingStation.m_conversion[j]; if (val2 != null && (Object)(object)val2.m_from != (Object)null && ((Object)((Component)val2.m_from).gameObject).name == text3) { val = val2; break; } } if (val == null) { continue; } double num = val.m_cookTime - zdo.GetFloat(text2, 0f); if (!(num <= 0.0)) { if (text.Length > 0) { text += " / "; } text += FormatDuration(num); } } if (text.Length != 0) { if (string.IsNullOrEmpty(hoverText)) { hoverText = Localization.instance.Localize(cookingStation.m_name); } hoverText = hoverText + "\nRemaining: " + text; } } private static void AppendSmelterTimer(Smelter smelter, ref string hoverText) { if (ShowSmelterTimer != null && ShowSmelterTimer.Value && !((Object)(object)smelter == (Object)null) && TryGetZDO((Component)(object)smelter, out var zdo) && zdo.GetInt(ZDOVars.s_queued, 0) > 0) { if (smelter.m_maxFuel > 0 && zdo.GetFloat(ZDOVars.s_fuel, 0f) <= 0f) { AppendPaused(ref hoverText); } else { AppendRemaining(ref hoverText, smelter.m_secPerProduct - zdo.GetFloat(ZDOVars.s_bakeTimer, 0f)); } } } [HarmonyPostfix] [HarmonyPatch(typeof(Plant), "GetHoverText")] private static void Plant_GetHoverText_Postfix(Plant __instance, ref string __result) { //IL_007c: 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) //IL_0062: 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 (ShowPlantTimer == null || !ShowPlantTimer.Value || (int)__instance.GetStatus() != 0 || !TryGetZDO((Component)(object)__instance, out var zdo)) { return; } long startTicks = zdo.GetLong(ZDOVars.s_plantTime, 0L); if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds)) { int num = zdo.GetInt(ZDOVars.s_seed, 0); State state = Random.state; float value; try { Random.InitState(num); value = Random.value; } finally { Random.state = state; } double num2 = Mathf.Lerp(__instance.m_growTime, __instance.m_growTimeMax, value); AppendRemaining(ref __result, num2 - elapsedSeconds); } } [HarmonyPatch(typeof(Fermenter), "GetHoverText")] [HarmonyPostfix] private static void Fermenter_GetHoverText_Postfix(Fermenter __instance, ref string __result) { if (ShowFermenterTimer != null && ShowFermenterTimer.Value && TryGetZDO((Component)(object)__instance, out var zdo) && zdo.GetInt(ZDOVars.s_content, 0) != 0) { long startTicks = zdo.GetLong(ZDOVars.s_startTime, 0L); if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds)) { AppendRemaining(ref __result, (double)__instance.m_fermentationDuration - elapsedSeconds); } } } [HarmonyPatch(typeof(CookingStation), "GetHoverText")] [HarmonyPostfix] private static void CookingStation_GetHoverText_Postfix(CookingStation __instance, ref string __result) { AppendCookingTimers(__instance, ref __result); } [HarmonyPatch(typeof(Switch), "GetHoverText")] [HarmonyPostfix] private static void CookingStationSwitch_GetHoverText_Postfix(Switch __instance, ref string __result) { CookingStation componentInParent = ((Component)__instance).GetComponentInParent<CookingStation>(); if (!((Object)(object)componentInParent == (Object)null) && !((Object)(object)componentInParent.m_addFoodSwitch != (Object)(object)__instance)) { AppendCookingTimers(componentInParent, ref __result); } } [HarmonyPatch(typeof(Switch), "GetHoverText")] [HarmonyPostfix] private static void SmelterSwitch_GetHoverText_Postfix(Switch __instance, ref string __result) { Smelter componentInParent = ((Component)__instance).GetComponentInParent<Smelter>(); if ((Object)(object)componentInParent != (Object)null && ((Object)(object)componentInParent.m_addOreSwitch == (Object)(object)__instance || (Object)(object)componentInParent.m_addWoodSwitch == (Object)(object)__instance)) { AppendSmelterTimer(componentInParent, ref __result); } } [HarmonyPatch(typeof(Beehive), "GetHoverText")] [HarmonyPostfix] private static void Beehive_GetHoverText_Postfix(Beehive __instance, ref string __result) { if (ShowBeehiveTimer == null || !ShowBeehiveTimer.Value || !TryGetZDO((Component)(object)__instance, out var zdo)) { return; } int num = zdo.GetInt(ZDOVars.s_level, 0); if (num >= __instance.m_maxHoney) { return; } if (!__instance.CheckBiome() || !__instance.HaveFreeSpace()) { AppendPaused(ref __result); return; } long startTicks = zdo.GetLong(ZDOVars.s_lastTime, 0L); if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds)) { double num2 = (double)zdo.GetFloat(ZDOVars.s_product, 0f) + elapsedSeconds; AppendRemaining(ref __result, (double)__instance.m_secPerUnit - num2); } } [HarmonyPatch(typeof(Pickable), "GetHoverText")] [HarmonyPostfix] private static void Pickable_GetHoverText_Postfix(Pickable __instance, ref string __result) { if (ShowPickableTimer == null || !ShowPickableTimer.Value || __instance.m_respawnTimeMinutes <= 0f || __instance.GetEnabled == 0 || !TryGetZDO((Component)(object)__instance, out var zdo) || !zdo.GetBool(ZDOVars.s_picked, __instance.m_defaultPicked)) { return; } long startTicks = zdo.GetLong(ZDOVars.s_pickedTime, 0L); if (!TryGetElapsedSeconds(startTicks, out var elapsedSeconds)) { return; } double num = (double)__instance.m_respawnTimeMinutes * 60.0 - elapsedSeconds; if (!(num <= 0.0)) { if (string.IsNullOrEmpty(__result)) { __result = Localization.instance.Localize(__instance.GetHoverName()); } AppendRemaining(ref __result, num); } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] [HarmonyPostfix] private static void Tameable_GetHoverText_Postfix(Tameable __instance, ref string __result) { if (ShowTamingTimer != null && ShowTamingTimer.Value && !__instance.IsTamed() && TryGetZDO((Component)(object)__instance, out var zdo)) { float num = zdo.GetFloat(ZDOVars.s_tameTimeLeft, __instance.m_tamingTime); AppendRemaining(ref __result, num); } if (TryGetGrowthRemaining((Component)(object)__instance, out var remainingSeconds)) { AppendRemaining(ref __result, remainingSeconds); } } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "GetHoverText")] private static void Character_GetHoverText_Postfix(Character __instance, ref string __result) { if (!ContainsRemainingTimer(__result) && TryGetGrowthRemaining((Component)(object)__instance, out var remainingSeconds)) { if (string.IsNullOrEmpty(__result)) { __result = Localization.instance.Localize(__instance.m_name); } AppendRemaining(ref __result, remainingSeconds); } } [HarmonyPatch(typeof(EggGrow), "GetHoverText")] [HarmonyPostfix] private static void EggGrow_GetHoverText_Postfix(EggGrow __instance, ref string __result) { if (ShowGrowthTimer != null && ShowGrowthTimer.Value && TryGetZDO((Component)(object)__instance, out var zdo) && !((Object)(object)ZNet.instance == (Object)null)) { float num = zdo.GetFloat(ZDOVars.s_growStart, 0f); if (num <= 0f) { AppendPaused(ref __result); return; } double num2 = ZNet.instance.GetTimeSeconds() - (double)num; AppendRemaining(ref __result, (double)__instance.m_growTime - num2); } } } [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal static class Patch_InfiniteFuel { private const string BathtubToggleRpc = "ValheimQoL_ToggleBathtub"; private static readonly List<FireplaceFuelState> RegisteredFireplaces = new List<FireplaceFuelState>(); private static readonly List<SmokeSpawnerState> RegisteredSmokeSpawners = new List<SmokeSpawnerState>(); private static readonly List<SmelterFuelState> RegisteredSmelters = new List<SmelterFuelState>(); private static readonly List<CookingStationFuelState> RegisteredCookingStations = new List<CookingStationFuelState>(); private static readonly int AutoExtinguishedByRainZdoKey = StringExtensionMethods.GetStableHashCode("ValheimQoL_AutoExtinguishedByRain"); private static readonly int BathtubEnabledZdoKey = StringExtensionMethods.GetStableHashCode("ValheimQoL_BathtubEnabled"); private static ConfigEntry<bool> NoFuelRequired = null; private static ConfigEntry<bool> OvenNoFuelRequired = null; private static ConfigEntry<bool> BathtubNoFuelRequired = null; private static ConfigEntry<bool> MakeToggleable = null; private static ConfigEntry<bool> DisableSmoke = null; internal static void InitConfig(ValheimQoLPlugin plugin) { NoFuelRequired = plugin.config("FireSources", "NoFuelRequired", value: true, "Makes every Fireplace-based fire source work without consuming fuel. This includes campfires, hearths, torches, sconces and braziers. Example: false restores normal Wood, Resin and Coal consumption."); OvenNoFuelRequired = plugin.config("FireSources", "OvenNoFuelRequired", value: true, "Makes the Stone Oven work without consuming Wood. Food is still required and cooking time is unchanged. Example: false restores the vanilla Wood requirement for the Stone Oven."); BathtubNoFuelRequired = plugin.config("FireSources", "BathtubNoFuelRequired", value: true, "Keeps the Hot Tub heated without consuming Wood and changes its Wood input into an On/Off switch. The selected state is saved in the world and synchronized in multiplayer. Example: false restores the vanilla Wood requirement and Wood input interaction for the Hot Tub."); MakeToggleable = plugin.config("FireSources", "MakeToggleable", value: true, "Allows every Fireplace-based fire source to be turned on and off with the Use key. Example: true lets players turn off campfires, torches, sconces and braziers."); DisableSmoke = plugin.config("FireSources", "DisableSmoke", value: false, "Stops Fireplace-based fire sources from creating smoke and prevents smoke blockage from extinguishing them. Example: true removes chimney smoke while keeping the fire and its heat active."); NoFuelRequired.SettingChanged += OnSettingChanged; OvenNoFuelRequired.SettingChanged += OnSettingChanged; BathtubNoFuelRequired.SettingChanged += OnSettingChanged; MakeToggleable.SettingChanged += OnSettingChanged; DisableSmoke.SettingChanged += OnSettingChanged; } internal static void Shutdown() { if (NoFuelRequired != null) { NoFuelRequired.SettingChanged -= OnSettingChanged; } if (OvenNoFuelRequired != null) { OvenNoFuelRequired.SettingChanged -= OnSettingChanged; } if (BathtubNoFuelRequired != null) { BathtubNoFuelRequired.SettingChanged -= OnSettingChanged; } if (MakeToggleable != null) { MakeToggleable.SettingChanged -= OnSettingChanged; } if (DisableSmoke != null) { DisableSmoke.SettingChanged -= OnSettingChanged; } for (int num = RegisteredFireplaces.Count - 1; num >= 0; num--) { FireplaceFuelState fireplaceFuelState = RegisteredFireplaces[num]; if ((Object)(object)fireplaceFuelState != (Object)null && (Object)(object)fireplaceFuelState.Fireplace != (Object)null) { Restore(fireplaceFuelState); } } RegisteredFireplaces.Clear(); RestoreSmokeSpawners(); RestoreSmelters(); RestoreCookingStations(); } private static bool IsInfiniteFuelEnabled() { return NoFuelRequired != null && NoFuelRequired.Value; } private static bool IsToggleEnabled() { return MakeToggleable != null && MakeToggleable.Value; } private static bool IsOvenInfiniteFuelEnabled() { return OvenNoFuelRequired != null && OvenNoFuelRequired.Value; } private static bool IsBathtubInfiniteFuelEnabled() { return BathtubNoFuelRequired != null && BathtubNoFuelRequired.Value; } private static bool IsSmokeDisabled() { return DisableSmoke != null && DisableSmoke.Value; } private static void OnSettingChanged(object sender, EventArgs eventArgs) { ApplyToRegisteredFireplaces(); ApplyToRegisteredSmokeSpawners(); ApplyToRegisteredSmelters(); ApplyToRegisteredCookingStations(); if (IsSmokeDisabled()) { FadeExistingSmoke(); } } private static void ApplyToRegisteredFireplaces() { for (int num = RegisteredFireplaces.Count - 1; num >= 0; num--) { FireplaceFuelState fireplaceFuelState = RegisteredFireplaces[num]; if ((Object)(object)fireplaceFuelState == (Object)null || (Object)(object)fireplaceFuelState.Fireplace == (Object)null) { RegisteredFireplaces.RemoveAt(num); } else { Apply(fireplaceFuelState); } } } private static void Apply(FireplaceFuelState state) { state.Fireplace.m_infiniteFuel = state.OriginalInfiniteFuel || IsInfiniteFuelEnabled(); state.Fireplace.m_canTurnOff = state.OriginalCanTurnOff || IsToggleEnabled(); if (IsSmokeDisabled()) { state.Fireplace.m_smokeSpawner = null; } else { state.Fireplace.m_smokeSpawner = state.OriginalSmokeSpawner; } } private static void Restore(FireplaceFuelState state) { state.Fireplace.m_infiniteFuel = state.OriginalInfiniteFuel; state.Fireplace.m_canTurnOff = state.OriginalCanTurnOff; state.Fireplace.m_smokeSpawner = state.OriginalSmokeSpawner; } private static void FadeExistingSmoke() { for (int num = Smoke.Instances.Count - 1; num >= 0; num--) { IMonoUpdater obj = Smoke.Instances[num]; Smoke val = (Smoke)(object)((obj is Smoke) ? obj : null); if ((Object)(object)val != (Object)null) { val.StartFadeOut(); } } } internal static void Register(Fireplace fireplace) { if (!((Object)(object)fireplace == (Object)null)) { FireplaceFuelState fireplaceFuelState = ((Component)fireplace).GetComponent<FireplaceFuelState>(); if ((Object)(object)fireplaceFuelState == (Object)null) { fireplaceFuelState = ((Component)fireplace).gameObject.AddComponent<FireplaceFuelState>(); fireplaceFuelState.Fireplace = fireplace; fireplaceFuelState.OriginalInfiniteFuel = fireplace.m_infiniteFuel; fireplaceFuelState.OriginalCanTurnOff = fireplace.m_canTurnOff; fireplaceFuelState.OriginalSmokeSpawner = fireplace.m_smokeSpawner; RegisteredFireplaces.Add(fireplaceFuelState); } else if (!RegisteredFireplaces.Contains(fireplaceFuelState)) { RegisteredFireplaces.Add(fireplaceFuelState); } SmokeSpawner[] componentsInChildren = ((Component)fireplace).GetComponentsInChildren<SmokeSpawner>(true); for (int i = 0; i < componentsInChildren.Length; i++) { RegisterSmokeSpawner(componentsInChildren[i]); } RegisterSmokeSpawner(fireplaceFuelState.OriginalSmokeSpawner); Apply(fireplaceFuelState); } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] internal static void RegisterSmokeSpawner(SmokeSpawner smokeSpawner) { if ((Object)(object)smokeSpawner == (Object)null || (Object)(object)((Component)smokeSpawner).GetComponentInParent<Smelter>() != (Object)null) { return; } for (int i = 0; i < RegisteredSmokeSpawners.Count; i++) { if ((Object)(object)RegisteredSmokeSpawners[i].SmokeSpawner == (Object)(object)smokeSpawner) { ApplySmokeSpawner(RegisteredSmokeSpawners[i]); return; } } SmokeSpawnerState smokeSpawnerState = new SmokeSpawnerState(smokeSpawner, ((Behaviour)smokeSpawner).enabled); RegisteredSmokeSpawners.Add(smokeSpawnerState); ApplySmokeSpawner(smokeSpawnerState); } private static void ApplyToRegisteredSmokeSpawners() { for (int num = RegisteredSmokeSpawners.Count - 1; num >= 0; num--) { SmokeSpawnerState smokeSpawnerState = RegisteredSmokeSpawners[num]; if ((Object)(object)smokeSpawnerState.SmokeSpawner == (Object)null) { RegisteredSmokeSpawners.RemoveAt(num); } else { ApplySmokeSpawner(smokeSpawnerState); } } } private static void ApplySmokeSpawner(SmokeSpawnerState state) { ((Behaviour)state.SmokeSpawner).enabled = state.OriginalEnabled && !IsSmokeDisabled(); } private static void RestoreSmokeSpawners() { for (int num = RegisteredSmokeSpawners.Count - 1; num >= 0; num--) { SmokeSpawnerState smokeSpawnerState = RegisteredSmokeSpawners[num]; if ((Object)(object)smokeSpawnerState.SmokeSpawner != (Object)null) { ((Behaviour)smokeSpawnerState.SmokeSpawner).enabled = smokeSpawnerState.OriginalEnabled; } } RegisteredSmokeSpawners.Clear(); } internal static void RegisterSmelter(Smelter smelter) { if ((Object)(object)smelter == (Object)null || !IsSupportedFuelFreeSmelter(smelter)) { return; } SmelterFuelState smelterFuelState = ((Component)smelter).GetComponent<SmelterFuelState>(); if ((Object)(object)smelterFuelState == (Object)null) { smelterFuelState = ((Component)smelter).gameObject.AddComponent<SmelterFuelState>(); smelterFuelState.Smelter = smelter; smelterFuelState.OriginalMaxFuel = smelter.m_maxFuel; smelterFuelState.FuelSwitch = smelter.m_addWoodSwitch; smelterFuelState.OriginalFuelSwitchActive = (Object)(object)smelterFuelState.FuelSwitch != (Object)null && ((Component)smelterFuelState.FuelSwitch).gameObject.activeSelf; if ((Object)(object)smelterFuelState.FuelSwitch != (Object)null) { smelterFuelState.OriginalFuelOnUse = smelterFuelState.FuelSwitch.m_onUse; smelterFuelState.OriginalFuelOnHover = smelterFuelState.FuelSwitch.m_onHover; } RegisteredSmelters.Add(smelterFuelState); } else if (!RegisteredSmelters.Contains(smelterFuelState)) { RegisteredSmelters.Add(smelterFuelState); } RegisterBathtubRpc(smelterFuelState); Apply(smelterFuelState); } private static void RegisterBathtubRpc(SmelterFuelState state) { if (!state.RpcRegistered) { ZNetView smelterNView = GetSmelterNView(state.Smelter); if (!((Object)(object)smelterNView == (Object)null) && smelterNView.IsValid()) { state.NView = smelterNView; smelterNView.Register("ValheimQoL_ToggleBathtub", (Action<long>)state.ReceiveToggleRpc); state.RpcRegistered = true; } } } private static bool IsSupportedFuelFreeSmelter(Smelter smelter) { return smelter.m_name == "$piece_bathtub"; } private static bool ShouldRemoveSmelterFuel(Smelter smelter) { if (smelter.m_name == "$piece_bathtub") { return IsBathtubInfiniteFuelEnabled(); } return false; } internal static bool ShouldHandleBathtubToggle(Smelter smelter) { return (Object)(object)smelter != (Object)null && IsSupportedFuelFreeSmelter(smelter) && IsBathtubInfiniteFuelEnabled(); } internal static bool RequestBathtubToggle(Smelter smelter) { if (!ShouldHandleBathtubToggle(smelter)) { return false; } ZNetView smelterNView = GetSmelterNView(smelter); if ((Object)(object)smelterNView == (Object)null || !smelterNView.IsValid()) { return false; } if (!smelterNView.HasOwner()) { smelterNView.ClaimOwnership(); } smelterNView.InvokeRPC("ValheimQoL_ToggleBathtub", Array.Empty<object>()); return true; } internal static void ReceiveBathtubToggle(Smelter smelter, ZNetView nview) { if (ShouldHandleBathtubToggle(smelter) && !((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { zDO.Set(BathtubEnabledZdoKey, !zDO.GetBool(BathtubEnabledZdoKey, true)); ApplyBathtubActiveState(smelter); } } } internal static bool IsBathtubTurnedOn(Smelter smelter) { ZNetView smelterNView = GetSmelterNView(smelter); if ((Object)(object)smelterNView == (Object)null || !smelterNView.IsValid()) { return true; } ZDO zDO = smelterNView.GetZDO(); return zDO == null || zDO.GetBool(BathtubEnabledZdoKey, true); } internal static void ApplyBathtubActiveState(Smelter smelter) { if (ShouldHandleBathtubToggle(smelter) && !((Object)(object)smelter.m_enabledObject == (Object)null)) { smelter.m_enabledObject.SetActive(IsBathtubTurnedOn(smelter)); } } internal static string GetBathtubToggleHoverText(Smelter smelter) { bool flag = IsBathtubTurnedOn(smelter); return Localization.instance.Localize(smelter.m_name + " (" + (flag ? "On" : "Off") + ")\n[<color=yellow><b>$KEY_Use</b></color>] " + (flag ? "Turn off" : "Turn on")); } private static ZNetView GetSmelterNView(Smelter smelter) { if ((Object)(object)smelter == (Object)null) { return null; } ZNetView component = ((Component)smelter).GetComponent<ZNetView>(); return ((Object)(object)component != (Object)null) ? component : ((Component)smelter).GetComponentInParent<ZNetView>(); } private static void ApplyToRegisteredSmelters() { for (int num = RegisteredSmelters.Count - 1; num >= 0; num--) { SmelterFuelState smelterFuelState = RegisteredSmelters[num]; if ((Object)(object)smelterFuelState == (Object)null || (Object)(object)smelterFuelState.Smelter == (Object)null) { RegisteredSmelters.RemoveAt(num); } else { Apply(smelterFuelState); } } } private static void Apply(SmelterFuelState state) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) bool flag = ShouldRemoveSmelterFuel(state.Smelter); state.Smelter.m_maxFuel = ((!flag) ? state.OriginalMaxFuel : 0); if ((Object)(object)state.FuelSwitch != (Object)null) { ((Component)state.FuelSwitch).gameObject.SetActive(state.OriginalFuelSwitchActive); state.FuelSwitch.m_onUse = (Callback)((!flag) ? ((object)state.OriginalFuelOnUse) : ((object)new Callback(state.ToggleBathtub))); state.FuelSwitch.m_onHover = (TooltipCallback)((!flag) ? ((object)state.OriginalFuelOnHover) : ((object)new TooltipCallback(state.GetBathtubToggleHoverText))); } } private static void RestoreSmelters() { for (int num = RegisteredSmelters.Count - 1; num >= 0; num--) { SmelterFuelState smelterFuelState = RegisteredSmelters[num]; if (!((Object)(object)smelterFuelState == (Object)null) && !((Object)(object)smelterFuelState.Smelter == (Object)null)) { smelterFuelState.Smelter.m_maxFuel = smelterFuelState.OriginalMaxFuel; if ((Object)(object)smelterFuelState.FuelSwitch != (Object)null) { ((Component)smelterFuelState.FuelSwitch).gameObject.SetActive(smelterFuelState.OriginalFuelSwitchActive); smelterFuelState.FuelSwitch.m_onUse = smelterFuelState.OriginalFuelOnUse; smelterFuelState.FuelSwitch.m_onHover = smelterFuelState.OriginalFuelOnHover; } } } RegisteredSmelters.Clear(); } internal static void RegisterCookingStation(CookingStation cookingStation) { if (!((Object)(object)cookingStation == (Object)null) && IsStoneOven(cookingStation)) { CookingStationFuelState cookingStationFuelState = ((Component)cookingStation).GetComponent<CookingStationFuelState>(); if ((Object)(object)cookingStationFuelState == (Object)null) { cookingStationFuelState = ((Component)cookingStation).gameObject.AddComponent<CookingStationFuelState>(); cookingStationFuelState.CookingStation = cookingStation; cookingStationFuelState.FuelSwitch = cookingStation.m_addFuelSwitch; cookingStationFuelState.OriginalFuelSwitchActive = (Object)(object)cookingStationFuelState.FuelSwitch != (Object)null && ((Component)cookingStationFuelState.FuelSwitch).gameObject.activeSelf; RegisteredCookingStations.Add(cookingStationFuelState); } else if (!RegisteredCookingStations.Contains(cookingStationFuelState)) { RegisteredCookingStations.Add(cookingStationFuelState); } Apply(cookingStationFuelState); } } private static bool IsStoneOven(CookingStation cookingStation) { return cookingStation.m_name == "$piece_oven" || ((Object)((Component)cookingStation).gameObject).name == "piece_oven" || ((Object)((Component)cookingStation).gameObject).name == "piece_oven(Clone)"; } internal static bool ShouldUseInfiniteOvenFuel(CookingStation cookingStation) { if ((Object)(object)cookingStation == (Object)null || !IsStoneOven(cookingStation) || !IsOvenInfiniteFuelEnabled()) { return false; } CookingStationFuelState component = ((Component)cookingStation).GetComponent<CookingStationFuelState>(); return (Object)(object)component == (Object)null || !component.SuppressInfiniteFuel; } private static void ApplyToRegisteredCookingStations() { for (int num = RegisteredCookingStations.Count - 1; num >= 0; num--) { CookingStationFuelState cookingStationFuelState = RegisteredCookingStations[num]; if ((Object)(object)cookingStationFuelState == (Object)null || (Object)(object)cookingStationFuelState.CookingStation == (Object)null) { RegisteredCookingStations.RemoveAt(num); } else { Apply(cookingStationFuelState); } } } private static void Apply(CookingStationFuelState state) { if (!((Object)(object)state.FuelSwitch == (Object)null)) { ((Component)state.FuelSwitch).gameObject.SetActive(state.OriginalFuelSwitchActive && !IsOvenInfiniteFuelEnabled()); } } private static void RestoreCookingStations() { for (int num = RegisteredCookingStations.Count - 1; num >= 0; num--) { CookingStationFuelState cookingStationFuelState = RegisteredCookingStations[num]; if (!((Object)(object)cookingStationFuelState == (Object)null) && !((Object)(object)cookingStationFuelState.CookingStation == (Object)null) && !((Object)(object)cookingStationFuelState.FuelSwitch == (Object)null)) { cookingStationFuelState.SuppressInfiniteFuel = false; ((Component)cookingStationFuelState.FuelSwitch).gameObject.SetActive(cookingStationFuelState.OriginalFuelSwitchActive); } } RegisteredCookingStations.Clear(); } internal static void SetCookingStationFuelSuppressed(CookingStation cookingStation, bool suppressed) { if (!((Object)(object)cookingStation == (Object)null)) { CookingStationFuelState component = ((Component)cookingStation).GetComponent<CookingStationFuelState>(); if ((Object)(object)component != (Object)null) { component.SuppressInfiniteFuel = suppressed; } } } internal static void Unregister(SmelterFuelState state) { if ((Object)(object)state != (Object)null) { RegisteredSmelters.Remove(state); } } internal static void Unregister(CookingStationFuelState state) { if ((Object)(object)state != (Object)null) { RegisteredCookingStations.Remove(state); } } internal static void UpdateRainAutoRelight(Fireplace fireplace, bool isWet, ZNetView nview) { if ((Object)(object)fireplace == (Object)null || (Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } if (!IsInfiniteFuelEnabled() || !IsToggleEnabled() || !fireplace.m_infiniteFuel || !fireplace.m_canTurnOff) { if (zDO.GetBool(AutoExtinguishedByRainZdoKey, false)) { zDO.Set(AutoExtinguishedByRainZdoKey, false); } return; } int num = zDO.GetInt(ZDOVars.s_state, 1); if (isWet) { if (num == 1) { zDO.Set(AutoExtinguishedByRainZdoKey, true); } } else if (zDO.GetBool(AutoExtinguishedByRainZdoKey, false)) { if (num != 1) { zDO.Set(ZDOVars.s_state, 1, false); } zDO.Set(AutoExtinguishedByRainZdoKey, false); } } internal static void Unregister(FireplaceFuelState state) { if ((Object)(object)state != (Object)null) { RegisteredFireplaces.Remove(state); } } internal static bool ShouldHandleInfiniteFuelToggle(Fireplace fireplace) { return (Object)(object)fireplace != (Object)null && fireplace.m_canTurnOff && fireplace.m_infiniteFuel; } } internal sealed class FireplaceFuelState : MonoBehaviour { [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)] internal Fireplace Fireplace = null; internal bool OriginalInfiniteFuel; internal bool OriginalCanTurnOff; [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] internal SmokeSpawner OriginalSmokeSpawner; private void OnDestroy() { Patch_InfiniteFuel.Unregister(this); } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] internal sealed class SmokeSpawnerState { internal SmokeSpawner SmokeSpawner { get; } internal bool OriginalEnabled { get; } internal SmokeSpawnerState(SmokeSpawner smokeSpawner, bool originalEnabled) { SmokeSpawner = smokeSpawner; OriginalEnabled = originalEnabled; } } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)] [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)] internal sealed class SmelterFuelState : MonoBehaviour { [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)] internal Smelter Smelter = null; internal ZNetView NView; internal Switch FuelSwitch; internal Callback OriginalFuelOnUse; internal TooltipCallback OriginalFuelOnHover; internal int OriginalMaxFuel; internal bool OriginalFuelSwitchActive; internal bool RpcRegistered; [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal bool ToggleBathtub(Switch caller, Humanoid user, ItemData item) { return Patch_InfiniteFuel.RequestBathtubToggle(Smelter); } [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] internal string GetBathtubToggleHoverText() { return Patch_InfiniteFuel.GetBathtubToggleHoverText(Smelter); } internal void ReceiveToggleRpc(long sender) { Patch_InfiniteFuel.ReceiveBathtubToggle(Smelter, NView); } private void OnDestroy() { Patch_InfiniteFuel.Unregister(this); } } internal sealed class CookingStationFuelState : MonoBehaviour { [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)] internal CookingStation CookingStation = null; [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] internal Switch FuelSwitch; internal bool OriginalFuelSwitchActive; internal bool SuppressInfiniteFuel; private void OnDestroy() { Patch_InfiniteFuel.Unregister(this); } } [HarmonyPatch(typeof(Fireplace), "Awake")] internal static class InfiniteFuel_Fireplace_Awake_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Postfix(Fireplace __instance) { Patch_InfiniteFuel.Register(__instance); } } [HarmonyPatch(typeof(Fireplace), "UpdateState")] internal static class FireSources_Fireplace_UpdateState_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Prefix(Fireplace __instance, bool ___m_wet, ZNetView ___m_nview) { Patch_InfiniteFuel.UpdateRainAutoRelight(__instance, ___m_wet, ___m_nview); } } [HarmonyPatch(typeof(Smelter), "Awake")] [HarmonyPriority(0)] internal static class InfiniteFuel_Smelter_Awake_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Postfix(Smelter __instance) { Patch_InfiniteFuel.RegisterSmelter(__instance); } } [HarmonyPatch(typeof(Smelter), "IsActive")] internal static class InfiniteFuel_Smelter_IsActive_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Postfix(Smelter __instance, ref bool __result) { if (Patch_InfiniteFuel.ShouldHandleBathtubToggle(__instance)) { __result = Patch_InfiniteFuel.IsBathtubTurnedOn(__instance); } } } [HarmonyPatch(typeof(Smelter), "UpdateState")] internal static class InfiniteFuel_Smelter_UpdateState_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Postfix(Smelter __instance) { Patch_InfiniteFuel.ApplyBathtubActiveState(__instance); } } [HarmonyPatch(typeof(CookingStation), "Awake")] [HarmonyPriority(0)] internal static class InfiniteFuel_CookingStation_Awake_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static void Postfix(CookingStation __instance) { Patch_InfiniteFuel.RegisterCookingStation(__instance); } } [HarmonyPatch(typeof(CookingStation), "GetFuel")] internal static class InfiniteFuel_CookingStation_GetFuel_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static bool Prefix(CookingStation __instance, ref float __result) { if (!Patch_InfiniteFuel.ShouldUseInfiniteOvenFuel(__instance)) { return true; } __result = Mathf.Max(1f, (float)__instance.m_maxFuel); return false; } } [HarmonyPatch(typeof(CookingStation), "UpdateFuel")] internal static class InfiniteFuel_CookingStation_UpdateFuel_Patch { [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)] private static bool Prefix(CookingStation __instance) { return !Patch_InfiniteFuel.ShouldUseInfiniteOvenFuel(__instance); } } [HarmonyPatch(typeof(CookingStation), "DropAllItems")] [<1