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.1.0
ValheimQol/ValheimQoL.dll
Decompiled 2 weeks 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.Linq.Expressions; 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.1.0")] [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.1.0.0")] [module: <058f2ccc-a382-480c-a823-dd7cc0c147b6>RefSafetyRules(11)] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<d263e8e0-1127-4e9c-91f8-19016011d0ff>Embedded] internal sealed class <d263e8e0-1127-4e9c-91f8-19016011d0ff>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [<d263e8e0-1127-4e9c-91f8-19016011d0ff>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class <25c150a2-5fd6-4342-b618-1d99a90a7440>NullableAttribute : Attribute { public readonly byte[] NullableFlags; public <25c150a2-5fd6-4342-b618-1d99a90a7440>NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public <25c150a2-5fd6-4342-b618-1d99a90a7440>NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [<d263e8e0-1127-4e9c-91f8-19016011d0ff>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class <60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContextAttribute : Attribute { public readonly byte Flag; public <60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContextAttribute(byte P_0) { Flag = P_0; } } [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] [<d263e8e0-1127-4e9c-91f8-19016011d0ff>Embedded] [CompilerGenerated] internal sealed class <058f2ccc-a382-480c-a823-dd7cc0c147b6>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <058f2ccc-a382-480c-a823-dd7cc0c147b6>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimQoL { [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [BepInDependency(/*Could not decode attribute arguments.*/)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [BepInPlugin("marlthon.ValheimQoL", "ValheimQoL", "0.1.0")] public sealed class ValheimQoLPlugin : BaseUnityPlugin { [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(0)] private sealed class ConfigurationManagerAttributes { public bool? Browsable = false; } internal const string ModName = "ValheimQoL"; internal const string ModVersion = "0.1.0"; 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"); [<25c150a2-5fd6-4342-b618-1d99a90a7440>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); FarmHeimCompatibility.Initialize(_harmony); _harmony.PatchAll(); Patch_DayNight.LogHarmonyPatchStatus(); SetupConfigWatcher(); Log.LogInfo((object)"ValheimQoL 0.1.0 carregado."); } private void OnDestroy() { if (_configWatcher != null) { _configWatcher.Changed -= ReadConfigValues; _configWatcher.Created -= ReadConfigValues; _configWatcher.Renamed -= ReadConfigValues; _configWatcher.Dispose(); _configWatcher = null; } FarmHeimCompatibility.Shutdown(); 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<[<25c150a2-5fd6-4342-b618-1d99a90a7440>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<[<25c150a2-5fd6-4342-b618-1d99a90a7440>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<[<25c150a2-5fd6-4342-b618-1d99a90a7440>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.1.0", MinimumRequiredVersion = "0.1.0", ModRequired = true }; _serverConfigLocked = null; } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] internal static class FarmHeimCompatibility { [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] private sealed class ProcessorBinding { internal readonly Type Type; internal readonly Func<object, Switch> Input; internal readonly Func<object, float> Duration; internal readonly Func<object, ZNetView> View; internal readonly Func<object, bool> Ready; internal readonly int QueueKey; internal readonly int BakeKey; internal ProcessorBinding(Type type, string input, string duration, string prefix, Func<object, bool> ready) { Type = type; Input = Field<Switch>(type, input); Duration = Field<float>(type, duration); View = Field<ZNetView>(type, "m_nview"); Ready = ready; QueueKey = StringExtensionMethods.GetStableHashCode(prefix + "queued"); BakeKey = StringExtensionMethods.GetStableHashCode(prefix + "bakeTimer"); } } private const BindingFlags Members = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _plantType; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _beeType; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _eggType; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _cageType; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _animalType; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Type _milkType; private static readonly int MilkReadyKey = StringExtensionMethods.GetStableHashCode("FarmHeimMilkReady"); private static readonly int NextMilkTimeKey = StringExtensionMethods.GetStableHashCode("FarmHeimNextMilkTime"); [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _animalSleeping; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static FieldInfo _plantHoverField; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1, 1, 1 })] private static Func<Component, double, string, string> _previousPlantHover; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1, 1, 1 })] private static Func<Component, double, string, string> _plantHover; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _plantReady; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _beeBiome; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _beeSpace; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, float> _plantRadius; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, float> _beeDuration; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, float> _eggDuration; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, float> _cageDuration; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, int> _beeLevel; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, int> _beeMaximum; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _plantSoil; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _plantHeat; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, bool> _plantCold; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, Biome> _plantBiome; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Action<GameObject, bool> _plantPreview; [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static IEnumerable _activePlants; private static readonly List<ProcessorBinding> Processors = new List<ProcessorBinding>(); [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 2, 1 })] private static Func<object, double> _plantRemaining; internal static void Initialize(Harmony harmony) { Bind("plants", delegate { //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown _plantType = AccessTools.TypeByName("FarmHeim.FarmPlantSystem"); if (!(_plantType == null)) { Func<object, bool> plantReady = Method<bool>(_plantType, "EstaMadura"); _plantRadius = Field<float>(_plantType, "raioDeCrescimento"); _plantSoil = Field<bool>(_plantType, "precisaSoloCultivado"); _plantHeat = Field<bool>(_plantType, "toleraCalor"); _plantCold = Field<bool>(_plantType, "toleraFrio"); _plantBiome = Field<Biome>(_plantType, "biomasPermitidos"); _activePlants = (IEnumerable)(AccessTools.DeclaredField(_plantType, "PlantasAtivas") ?? throw new MissingFieldException(_plantType.FullName, "PlantasAtivas")).GetValue(null); MethodInfo methodInfo = AccessTools.DeclaredMethod(_plantType, "AtualizarDestaqueDePosicionamento", new Type[2] { typeof(GameObject), typeof(bool) }, (Type[])null); if (methodInfo != null) { _plantPreview = (Action<GameObject, bool>)Delegate.CreateDelegate(typeof(Action<GameObject, bool>), methodInfo); } _plantHoverField = _plantType.GetField("HoverTextCompatibility", BindingFlags.Static | BindingFlags.Public); if (_plantHoverField != null && _plantHoverField.FieldType == typeof(Func<Component, double, string, string>)) { _previousPlantHover = (Func<Component, double, string, string>)_plantHoverField.GetValue(null); _plantHover = (Component plant, double remaining, string text) => Patch_HoverTimers.AppendFarmPlantTimer(plant, remaining, (_previousPlantHover == null) ? text : _previousPlantHover(plant, remaining, text)); _plantHoverField.SetValue(null, _plantHover); } else { _plantRemaining = Method<double>(_plantType, "ObterTempoRestanteDeCrescimento"); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(_plantType, "GetHoverText", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(FarmHeimCompatibility), "PlantHoverPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _plantReady = plantReady; ValheimQoLPlugin.Log.LogInfo((object)"[FarmHeim] Plant timers, harvesting and grid integration enabled."); } }); Bind("beehives", delegate { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown _beeType = AccessTools.TypeByName("FarmHeim.FarmheimBeehive"); if (!(_beeType == null)) { Func<object, int> beeLevel = Method<int>(_beeType, "GetHoneyLevel"); _beeMaximum = Field<int>(_beeType, "m_maxHoney"); _beeDuration = Field<float>(_beeType, "m_secPerUnit"); _beeBiome = Method<bool>(_beeType, "CheckBiome"); _beeSpace = Method<bool>(_beeType, "HaveFreeSpace"); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(_beeType, "GetHoverText", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(FarmHeimCompatibility), "BeeHoverPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _beeLevel = beeLevel; } }); Bind("eggs", delegate { _eggType = AccessTools.TypeByName("FarmHeim.FarmHeimEggGrow"); if (_eggType != null) { _eggDuration = Field<float>(_eggType, "m_growTime"); } }); Bind("cages", delegate { _cageType = AccessTools.TypeByName("FarmHeim.FarmHeimCageGrow"); if (_cageType != null) { _cageDuration = Field<float>(_cageType, "m_spawnTime"); } }); Bind("animal growth", delegate { _animalType = AccessTools.TypeByName("FarmHeim.FarmHeimAI"); if (_animalType != null) { _animalSleeping = Method<bool>(_animalType, "IsInFarmSleepState"); } }); _milkType = AccessTools.TypeByName("FarmHeim.FarmHeimMilkProducer"); Bind("converters", delegate { Type type = AccessTools.TypeByName("FarmHeim.FarmHeimConversor"); if (type != null) { Func<object, object> ready = Method<object>(type, "FindReadyConversion"); Processors.Add(new ProcessorBinding(type, "m_addOreSwitch", "m_secPerProduct", "FarmHeimConversor_", (object instance) => ready(instance) != null)); } }); Bind("mills", delegate { Type type = AccessTools.TypeByName("FarmHeim.FarmHeimMoinho"); if (!(type == null)) { Func<object, bool> ready = Property<bool>(type, "UpperIsProcessing"); Func<object, bool> ready2 = Property<bool>(type, "LowerIsProcessing"); ProcessorBinding item = new ProcessorBinding(type, "m_upperAddOreSwitch", "m_upperSecPerProduct", "FarmHeimMoinho_upper_", ready); ProcessorBinding item2 = new ProcessorBinding(type, "m_lowerAddOreSwitch", "m_lowerSecPerProduct", "FarmHeimConversor_", ready2); Processors.Add(item); Processors.Add(item2); } }); } private static void Bind(string feature, Action bind) { try { bind(); } catch (Exception ex) { ValheimQoLPlugin.Log.LogWarning((object)("[FarmHeim] Could not bind " + feature + ": " + ex.Message)); } } internal static void Shutdown() { if (_plantHoverField != null && _plantHoverField.GetValue(null) == _plantHover) { _plantHoverField.SetValue(null, _previousPlantHover); } Processors.Clear(); } private static Func<object, T> Field<[<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] T>(Type type, string name) { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "instance"); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, name); return Expression.Lambda<Func<object, T>>(Expression.Convert(Expression.Field(Expression.Convert(parameterExpression, type), field), typeof(T)), new ParameterExpression[1] { parameterExpression }).Compile(); } private static Func<object, T> Property<[<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] T>(Type type, string name) { PropertyInfo propertyInfo = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException(type.FullName, name); return Method<T>(type, propertyInfo.GetGetMethod(nonPublic: true)); } private static Func<object, T> Method<[<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] T>(Type type, string name) { return Method<T>(type, type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) ?? throw new MissingMethodException(type.FullName, name)); } private static Func<object, T> Method<[<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] T>(Type type, MethodInfo method) { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "instance"); return Expression.Lambda<Func<object, T>>(Expression.Convert(Expression.Call(Expression.Convert(parameterExpression, type), method), typeof(T)), new ParameterExpression[1] { parameterExpression }).Compile(); } [return: <25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] internal static Component GetPlant(Component source) { return (_plantReady == null || (Object)(object)source == (Object)null) ? null : source.GetComponent(_plantType); } [return: <25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] internal static Component GetPlantInParent(Component source) { return (_plantReady == null || (Object)(object)source == (Object)null) ? null : source.GetComponentInParent(_plantType); } [return: <25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] internal static Component GetBeeInParent(Component source) { return (_beeLevel == null || (Object)(object)source == (Object)null) ? null : source.GetComponentInParent(_beeType); } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(2)] internal static bool IsPlant(Component component) { return (Object)(object)component != (Object)null && _plantReady != null && _plantType.IsInstanceOfType(component); } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(2)] internal static bool IsBee(Component component) { return (Object)(object)component != (Object)null && _beeLevel != null && _beeType.IsInstanceOfType(component); } internal static bool IsPlantReady(Component component) { return IsPlant(component) && _plantReady(component); } internal static int HoneyLevel(Component component) { return IsBee(component) ? _beeLevel(component) : 0; } internal static float PlantRadius(Component component) { return Math.Max(0f, _plantRadius(component)); } internal static bool PlantNeedsSoil(Component component) { return _plantSoil(component); } internal static bool PlantToleratesHeat(Component component) { return _plantHeat(component); } internal static bool PlantToleratesCold(Component component) { return _plantCold(component); } internal static Biome PlantBiome(Component component) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _plantBiome(component); } internal static void SetPlantPreview(GameObject preview, bool invalid) { _plantPreview?.Invoke(preview, invalid); } internal static bool HasPlantPlacementSpace(Component plant, Vector3 position) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) float num = PlantRadius(plant); if (num <= 0f) { return true; } foreach (Component activePlant in _activePlants) { Component val = activePlant; if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)plant) && Patch_HoverTimers.TryGetZDO(val, out var _)) { float num2 = num + PlantRadius(val); Vector3 val2 = position - val.transform.position; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < num2 * num2) { return false; } } } return true; } internal static bool IsGrowthPaused(Component source) { if (_animalSleeping == null || (Object)(object)source.GetComponent<Growup>() == (Object)null) { return false; } Component component = source.GetComponent(_animalType); return (Object)(object)component != (Object)null && _animalSleeping(component); } private static void PlantHoverPostfix(Component __instance, ref string __result) { __result = Patch_HoverTimers.AppendFarmPlantTimer(__instance, _plantRemaining(__instance), __result); } internal static void AppendMilkTimer(Tameable animal, ref string text) { if (!Patch_HoverTimers.MilkTimerEnabled || _milkType == null || (Object)(object)((Component)animal).GetComponent(_milkType) == (Object)null || !animal.IsTamed() || !Patch_HoverTimers.TryGetZDO((Component)(object)animal, out var zdo) || (Object)(object)ZNet.instance == (Object)null || zdo.GetBool(MilkReadyKey, false)) { return; } long num = zdo.GetLong(NextMilkTimeKey, 0L); if (num > 0) { double num2 = (double)num / 1000.0 - ZNet.instance.GetTimeSeconds(); if (num2 > 0.0) { text = text + "\nMilk: " + Patch_HoverTimers.FormatDuration(num2); } } } private static void BeeHoverPostfix(Component __instance, ref string __result) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Patch_HoverTimers.BeehiveTimerEnabled && Patch_HoverTimers.TryGetZDO(__instance, out var zdo) && PrivateArea.CheckAccess(__instance.transform.position, 0f, false, false) && _beeLevel(__instance) < _beeMaximum(__instance)) { double elapsedSeconds; if (!_beeBiome(__instance) || !_beeSpace(__instance)) { Patch_HoverTimers.AppendPaused(ref __result); } else if (Patch_HoverTimers.TryGetElapsedSeconds(zdo.GetLong(ZDOVars.s_lastTime, 0L), out elapsedSeconds)) { Patch_HoverTimers.AppendRemaining(ref __result, (double)(_beeDuration(__instance) - zdo.GetFloat(ZDOVars.s_product, 0f)) - elapsedSeconds); } } Patch_BulkHarvest.AppendFarmHeimHint(__instance, ref __result); } internal static void AppendItemGrowthTimer(ItemDrop item, ref string text) { if (!Patch_HoverTimers.GrowthTimerEnabled || Patch_HoverTimers.ContainsRemainingTimer(text)) { return; } Component val = ((_eggDuration == null) ? null : ((Component)item).GetComponent(_eggType)); Func<object, float> func = _eggDuration; if ((Object)(object)val == (Object)null && _cageDuration != null) { val = ((Component)item).GetComponent(_cageType); func = _cageDuration; } if (!((Object)(object)val == (Object)null) && Patch_HoverTimers.TryGetZDO((Component)(object)item, out var zdo) && !((Object)(object)ZNet.instance == (Object)null)) { float num = zdo.GetFloat(ZDOVars.s_growStart, 0f); if (num <= 0f || item.m_itemData.m_stack > 1) { Patch_HoverTimers.AppendPaused(ref text); } else { Patch_HoverTimers.AppendRemaining(ref text, (double)func(val) - (ZNet.instance.GetTimeSeconds() - (double)num)); } } } internal static void AppendProcessorTimer(Switch input, ref string text) { if (!Patch_HoverTimers.SmelterTimerEnabled || Patch_HoverTimers.ContainsRemainingTimer(text)) { return; } foreach (ProcessorBinding processor in Processors) { Component componentInParent = ((Component)input).GetComponentInParent(processor.Type); if ((Object)(object)componentInParent == (Object)null || (Object)(object)processor.Input(componentInParent) != (Object)(object)input) { continue; } ZNetView val = processor.View(componentInParent); if ((Object)(object)val == (Object)null || !val.IsValid()) { break; } ZDO zDO = val.GetZDO(); if (zDO.GetInt(processor.QueueKey, 0) > 0) { if (!processor.Ready(componentInParent)) { Patch_HoverTimers.AppendPaused(ref text); } else { Patch_HoverTimers.AppendRemaining(ref text, processor.Duration(componentInParent) - zDO.GetFloat(processor.BakeKey, 0f)); } } break; } } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [HarmonyPatch] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>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."); } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] [HarmonyPostfix] 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; } } [HarmonyPatch(typeof(Player), "Awake")] [HarmonyPostfix] 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; } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [HarmonyPatch] internal static class Patch_BulkHarvest { 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 either Shift key + 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: Shift + Use on one beehive collects honey from nearby ready beehives."); ShowHoverHint = plugin.config("BulkHarvest", "ShowHoverHint", value: true, "Adds a Shift + 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) || ZInput.GetKey((KeyCode)303, false); } private static string GetPrefabName(GameObject gameObject) { return ((Object)(object)gameObject == (Object)null) ? string.Empty : Utils.GetPrefabName(gameObject); } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(2)] private static bool IsEligiblePickable(Pickable pickable) { return (Object)(object)pickable != (Object)null && !pickable.GetPicked() && pickable.GetEnabled != 0; } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>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: <25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(2)] private static Interactable GetInteractable(GameObject gameObject) { return ((Object)(object)gameObject == (Object)null) ? null : gameObject.GetComponentInParent<Interactable>(); } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(2)] private static bool IsEligibleFarmHeim(Component component) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) return (Object)(object)component != (Object)null && ((FarmHeimCompatibility.IsPlant(component) && FarmHeimCompatibility.IsPlantReady(component)) || (IncludeBeehives.Value && FarmHeimCompatibility.IsBee(component) && FarmHeimCompatibility.HoneyLevel(component) > 0)) && PrivateArea.CheckAccess(component.transform.position, 0f, false, false); } internal static void AppendFarmHeimHint(Component component, ref string text) { if (IsEnabled() && ShowHoverHint.Value && IsEligibleFarmHeim(component) && !string.IsNullOrEmpty(text)) { text += BuildHoverHint(); } } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(2)] private static bool MatchesRootType(Pickable candidatePickable, Beehive candidateBeehive, Pickable rootPickable, Beehive rootBeehive, [<25c150a2-5fd6-4342-b618-1d99a90a7440>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>Shift + $KEY_Use</b></color>] Harvest nearby"); } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "Interact")] private static void Player_Interact_Prefix(Player __instance, GameObject go, bool hold, ref bool alt) { //IL_0144: 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); Component component = (Component)(object)((interactable is Component) ? interactable : null); if (!IsEligiblePickable(val) && !IsEligibleBeehive(val2) && !IsEligibleFarmHeim(component)) { return; } MonoBehaviour val3 = (MonoBehaviour)(object)((interactable is MonoBehaviour) ? interactable : null); if ((Object)(object)val3 == (Object)null) { return; } bool flag = IsEligibleFarmHeim(component); string prefabName = GetPrefabName(((Component)val3).gameObject); alt = false; 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); Component val6 = (((Object)(object)componentInParent == (Object)null && (Object)(object)val5 == (Object)null) ? (FarmHeimCompatibility.GetPlantInParent((Component)(object)val4) ?? FarmHeimCompatibility.GetBeeInParent((Component)(object)val4)) : null); MonoBehaviour val7 = (MonoBehaviour)(((Object)(object)componentInParent != (Object)null) ? componentInParent : (((Object)(object)val5 != (Object)null) ? ((object)val5) : ((object)((val6 is MonoBehaviour) ? val6 : null)))); if ((Object)(object)val7 == (Object)null) { continue; } int instanceID2 = ((Object)((Component)val7).gameObject).GetInstanceID(); if (!ProcessedObjects.Add(instanceID2)) { continue; } bool num5; if (!flag) { num5 = !MatchesRootType(componentInParent, val5, val, val2, prefabName); } else { if (!HarvestSameTypeOnly.Value) { goto IL_026f; } num5 = GetPrefabName(((Component)val7).gameObject) != prefabName; } if (num5) { continue; } goto IL_026f; IL_026f: Interactable val8; if (IsEligiblePickable(componentInParent)) { val8 = (Interactable)(object)componentInParent; } else if (IsEligibleBeehive(val5)) { val8 = (Interactable)(object)val5; } else { if (!IsEligibleFarmHeim(val6)) { continue; } val8 = (Interactable)(object)((val6 is Interactable) ? val6 : null); } if (val8 != null) { val8.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(); } } [HarmonyPatch(typeof(Beehive), "GetHoverText")] [HarmonyPostfix] 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] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] internal static class MenuQuickJoin { [CompilerGenerated] private static class <>O { [<25c150a2-5fd6-4342-b618-1d99a90a7440>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); } [HarmonyPatch(typeof(FejdStartup), "SetupGui")] [HarmonyPostfix] 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([<60a1380c-2b06-42c8-b36b-46c44f89aecc>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>)([<60a1380c-2b06-42c8-b36b-46c44f89aecc>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; } } } } [HarmonyPatch] [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] 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>)([<60a1380c-2b06-42c8-b36b-46c44f89aecc>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 { [<60a1380c-2b06-42c8-b36b-46c44f89aecc>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; } } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [HarmonyPatch(typeof(EffectArea), "Awake")] 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; } } } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [HarmonyPatch] 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>([<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(new byte[] { 1, 0 })] ConfigEntry<T> entry) { if (entry != null) { entry.SettingChanged += [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(0)] (object _, EventArgs _) => { UpdateAllChests(); }; } } } [HarmonyPostfix] [HarmonyPatch(typeof(Container), "Awake")] 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."); } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [HarmonyPatch] 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); } [HarmonyPatch(typeof(EnvMan), "Awake")] [HarmonyPostfix] private static void EnvMan_Awake_Postfix(EnvMan __instance) { _vanillaCycleLengthSeconds = Math.Max(60L, __instance.m_dayLengthSec); ApplySettings(__instance); } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>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()) + ".")); } } [HarmonyPatch(typeof(EnvMan), "RescaleDayFraction")] [HarmonyPrefix] 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); } } [HarmonyPatch] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] 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); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "Update")] 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); } } } [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] [HarmonyPatch] [<60a1380c-2b06-42c8-b36b-46c44f89aecc>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; private static ConfigEntry<bool> ShowMilkTimer; internal static bool PlantTimerEnabled => ShowPlantTimer != null && ShowPlantTimer.Value; internal static bool MilkTimerEnabled => ShowMilkTimer != null && ShowMilkTimer.Value; internal static bool BeehiveTimerEnabled => ShowBeehiveTimer != null && ShowBeehiveTimer.Value; internal static bool GrowthTimerEnabled => ShowGrowthTimer != null && ShowGrowthTimer.Value; internal static bool SmelterTimerEnabled => ShowSmelterTimer != null && ShowSmelterTimer.Value; internal static void InitConfig(ValheimQoLPlugin plugin) { ShowMilkTimer = plugin.config("HoverTimers", "ShowMilkTimer", value: true, "Shows the remaining milk production time when looking at a tamed FarmHeim cow or goat. Does not change production or milking requirements.", synchronizedSetting: false); 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); } internal 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; } internal 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; } } internal 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"; } internal 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)); } } internal static void AppendPaused(ref string hoverText) { hoverText = (string.IsNullOrEmpty(hoverText) ? "Paused" : (hoverText + "\nPaused")); } internal 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; } internal static string AppendFarmPlantTimer(Component plant, double remaining, string text) { if (PlantTimerEnabled && !ContainsRemainingTimer(text)) { AppendRemaining(ref text, remaining); } Patch_BulkHarvest.AppendFarmHeimHint(plant, ref text); return text; } [HarmonyPostfix] [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] private static void ItemDrop_GetHoverText_Postfix(ItemDrop __instance, ref string __result) { FarmHeimCompatibility.AppendItemGrowthTimer(__instance, ref __result); } 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)); } } } [HarmonyPatch(typeof(Plant), "GetHoverText")] [HarmonyPostfix] 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); } } [HarmonyPostfix] [HarmonyPatch(typeof(Fermenter), "GetHoverText")] 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); } } } [HarmonyPostfix] [HarmonyPatch(typeof(CookingStation), "GetHoverText")] 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); } } [HarmonyPostfix] [HarmonyPatch(typeof(Switch), "GetHoverText")] 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); } else { FarmHeimCompatibility.AppendProcessorTimer(__instance, ref __result); } } [HarmonyPostfix] [HarmonyPatch(typeof(Beehive), "GetHoverText")] 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); } } [HarmonyAfter(new string[] { "marlthon.FarmHeim" })] [HarmonyPatch(typeof(Tameable), "GetHoverText")] [HarmonyPostfix] private static void Tameable_GetHoverText_Postfix(Tameable __instance, ref string __result) { FarmHeimCompatibility.AppendMilkTimer(__instance, ref __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); } double remainingSeconds; if (GrowthTimerEnabled && FarmHeimCompatibility.IsGrowthPaused((Component)(object)__instance)) { AppendPaused(ref __result); } else if (TryGetGrowthRemaining((Component)(object)__instance, out remainingSeconds)) { AppendRemaining(ref __result, remainingSeconds); } } [HarmonyPatch(typeof(Character), "GetHoverText")] [HarmonyPostfix] private static void Character_GetHoverText_Postfix(Character __instance, ref string __result) { double remainingSeconds; if (GrowthTimerEnabled && FarmHeimCompatibility.IsGrowthPaused((Component)(object)__instance)) { if (__result.IndexOf("Paused", StringComparison.Ordinal) < 0) { AppendPaused(ref __result); } } else if (!ContainsRemainingTimer(__result) && TryGetGrowthRemaining((Component)(object)__instance, out remainingSeconds)) { if (string.IsNullOrEmpty(__result)) { __result = Localization.instance.Localize(__instance.m_name); } AppendRemaining(ref __result, remainingSeconds); } } [HarmonyPostfix] [HarmonyPatch(typeof(EggGrow), "GetHoverText")] 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); } } } [<60a1380c-2b06-42c8-b36b-46c44f89aecc>NullableContext(1)] [<25c150a2-5fd6-4342-b618-1d99a90a7440>Nullable(0)] 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.