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 Crafting and Automation v1.0.5
DigiCrafting.dll
Decompiled a day 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using AutoFermenter; using AutoOven; using AutoProcessing; using AutoRepair; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Cookbook; using CookingMastery; using CraftBatch; using CraftStorageSpeedup; using DigiCore; using FermenterTimer; using HarmonyLib; using Microsoft.CodeAnalysis; using RecycleTab; using RestyleItems; using SapTimer; using ShiftFillFromStorage; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("DigiCrafting")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DigiCrafting")] [assembly: AssemblyTitle("DigiCrafting")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class StoreRules { private static bool _resolved; private static MethodInfo _readMarker; private static PropertyInfo _settings; private static PropertyInfo _useMarkers; private static PropertyInfo _canDeposit; private static PropertyInfo _canWithdraw; private static MethodInfo _isProtected; private static void Resolve() { if (_resolved) { return; } _resolved = true; try { Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.ContainerPolicyApi"); Type type2 = AccessTools.TypeByName("LocalCraftFromStoragePrime.ContainerMarkerPolicy"); Type type3 = AccessTools.TypeByName("LocalCraftFromStoragePrime.Plugin"); if (!(type == null) && !(type2 == null) && !(type3 == null)) { _readMarker = AccessTools.Method(type, "ReadMarker", new Type[2] { typeof(Inventory), typeof(bool) }, (Type[])null); _settings = AccessTools.Property(type3, "Settings"); _useMarkers = ((_settings != null) ? AccessTools.Property(_settings.PropertyType, "UseContainerMarkers") : null); _canDeposit = AccessTools.Property(type2, "CanDeposit"); _canWithdraw = AccessTools.Property(type2, "CanWithdraw"); _isProtected = AccessTools.Method(type2, "IsProtectedPosition", new Type[2] { typeof(int), typeof(int) }, (Type[])null); } } catch (Exception) { _readMarker = null; } } private static object Marker(Inventory inv) { Resolve(); if (_readMarker == null || _canDeposit == null || _canWithdraw == null || _isProtected == null) { return null; } try { bool flag = true; if (_settings != null && _useMarkers != null) { flag = (bool)_useMarkers.GetValue(_settings.GetValue(null)); } return _readMarker.Invoke(null, new object[2] { inv, flag }); } catch (Exception) { return null; } } internal static bool CanDeposit(Inventory inv) { object obj = Marker(inv); if (obj != null) { return (bool)_canDeposit.GetValue(obj); } return true; } internal static bool CanWithdraw(Inventory inv) { object obj = Marker(inv); if (obj != null) { return (bool)_canWithdraw.GetValue(obj); } return true; } internal static bool IsProtected(Inventory inv, ItemData item) { object obj = Marker(inv); if (obj != null) { return (bool)_isProtected.Invoke(obj, new object[2] { item.m_gridPos.x, item.m_gridPos.y }); } return false; } internal static Container PickOutputChest(List<Container> nearestFirst, GameObject prefab, int amount) { string shared = prefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name; List<Container> source = nearestFirst.Where((Container c) => CanDeposit(c.GetInventory()) && c.GetInventory().CanAddItem(prefab, amount)).ToList(); return ((IEnumerable<Container>)source).FirstOrDefault((Func<Container, bool>)((Container c) => c.GetInventory().GetAllItems().Any((ItemData i) => i?.m_shared != null && i.m_shared.m_name == shared && !IsProtected(c.GetInventory(), i)))) ?? source.FirstOrDefault(); } internal static bool TakeOne(List<Container> nearestFirst, Func<ItemData, bool> match, out ItemData taken) { foreach (Container item in nearestFirst) { Inventory inv = item.GetInventory(); if (CanWithdraw(inv)) { ItemData val = ((IEnumerable<ItemData>)inv.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => match(i) && !IsProtected(inv, i))); if (val != null) { taken = val.Clone(); inv.RemoveOneItem(val); return true; } } } taken = null; return false; } } internal static class SettingOrder { internal sealed class ConfigurationManagerAttributes { public int? Order; } internal static void Apply(ConfigFile config) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown FieldInfo fieldInfo = AccessTools.Field(typeof(ConfigEntryBase), "<Description>k__BackingField"); if (fieldInfo == null) { return; } int num = 1000; foreach (ConfigDefinition item in config.Keys.ToList()) { ConfigEntryBase val = config[item]; ConfigDescription description = val.Description; object[] array = ((description != null) ? description.Tags : null) ?? new object[0]; object obj = array.FirstOrDefault((object t) => t != null && t.GetType().Name == "ConfigurationManagerAttributes"); if (obj != null) { FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "Order"); if (fieldInfo2 != null && fieldInfo2.GetValue(obj) == null) { fieldInfo2.SetValue(obj, num); } } else { fieldInfo.SetValue(val, (object?)new ConfigDescription(((description != null) ? description.Description : null) ?? "", (description != null) ? description.AcceptableValues : null, array.Concat(new object[1] { new ConfigurationManagerAttributes { Order = num } }).ToArray())); } num--; } } } namespace DigiCore { public static class ChestFlags { public const string NoStore = "digi_nostore"; public static bool StoreBlocked(Container chest) { if ((Object)(object)chest == (Object)null) { return false; } ZNetView component = ((Component)chest).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { return component.GetZDO().GetBool("digi_nostore", false); } return false; } } public sealed class ModuleInfo { public readonly Type Type; public readonly string Title; public readonly string OldGuid; public readonly string Description; public readonly string[] Requires; public readonly bool DefaultOn; public ModuleInfo(Type type, string title, string oldGuid, string description, string[] requires = null, bool defaultOn = true) { Type = type; Title = title; OldGuid = oldGuid; Description = description; Requires = requires ?? new string[0]; DefaultOn = defaultOn; } } public abstract class DigiPlugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<string, string> <>9__11_0; public static Func<string, bool> <>9__11_1; public static Func<string, bool> <>9__11_2; public static Func<string, string> <>9__11_3; public static Func<string, string[]> <>9__19_0; public static Func<string[], string> <>9__19_1; public static Func<string[], string> <>9__19_4; public static Func<IGrouping<string, string[]>, string> <>9__19_2; public static PopupButtonCallback <>9__19_3; public static Func<string, bool> <>9__23_0; internal string <Awake>b__11_0(string l) { return l.Trim(); } internal bool <Awake>b__11_1(string l) { return l.Length > 0; } internal bool <Awake>b__11_2(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } internal string <Awake>b__11_3(string r) { if (!RequirementNames.TryGetValue(r, out var value)) { return r; } return value; } internal string[] <ShowDuplicates>b__19_0(string d) { return d.Split(new char[1] { '|' }); } internal string <ShowDuplicates>b__19_1(string[] d) { return d[0]; } internal string <ShowDuplicates>b__19_2(IGrouping<string, string[]> g) { return "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); } internal string <ShowDuplicates>b__19_4(string[] d) { return d[1]; } internal void <ShowDuplicates>b__19_3() { UnifiedPopup.Pop(); } internal bool <Apply>b__23_0(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } } public const string ModulesSection = "0 - Sub-mods"; internal static DigiPlugin StartingHost; internal static ModuleInfo StartingModule; private static readonly Dictionary<string, string> RequirementNames = new Dictionary<string, string> { { "randyknapp.mods.epicloot", "Epic Loot" }, { "com.jotunn.jotunn", "Jotunn" }, { "ValheimLegends", "Valheim Legends" }, { "vapok.mods.adventurebackpacks", "Adventure Backpacks" }, { "MidnightsFX.StarLevelSystem", "Star Level System" }, { "sosal.nordicrugs", "Nordic Rugs" }, { "LuminairPrime.valheim.localcraftfromstorageprime", "Local Craft From Storage Prime" } }; private readonly Dictionary<ModuleInfo, DigiModule> _running = new Dictionary<ModuleInfo, DigiModule>(); private readonly Dictionary<ModuleInfo, ConfigEntry<bool>> _switches = new Dictionary<ModuleInfo, ConfigEntry<bool>>(); private HashSet<string> _migrated; private const string DuplicatesKey = "digi.valheim.duplicates"; private const string DuplicatesShownKey = "digi.valheim.duplicates.shown"; protected abstract ModuleInfo[] Modules { get; } private string MigratedFile => Path.Combine(Paths.ConfigPath, ((BaseUnityPlugin)this).Info.Metadata.GUID + ".migrated.txt"); private static List<string> Duplicates { get { List<string> list = AppDomain.CurrentDomain.GetData("digi.valheim.duplicates") as List<string>; if (list == null) { list = new List<string>(); AppDomain.CurrentDomain.SetData("digi.valheim.duplicates", list); } return list; } } protected virtual void Awake() { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.Save(); _migrated = (File.Exists(MigratedFile) ? new HashSet<string>(from l in File.ReadAllLines(MigratedFile) select l.Trim() into l where l.Length > 0 select l) : new HashSet<string>()); bool flag = !_migrated.Contains("#switches"); ModuleInfo[] modules = Modules; foreach (ModuleInfo moduleInfo in modules) { List<string> list = moduleInfo.Requires.Where((string r) => !Chainloader.PluginInfos.ContainsKey(r)).ToList(); string value; string text = ((moduleInfo.Requires.Length == 0) ? "" : (" Needs " + string.Join(" and ", moduleInfo.Requires.Select((string r) => (!RequirementNames.TryGetValue(r, out value)) ? r : value)) + ".")); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Sub-mods", moduleInfo.Title, moduleInfo.DefaultOn, moduleInfo.Description + text); if (flag && !moduleInfo.DefaultOn && File.Exists(OldConfigPath(moduleInfo))) { val.Value = true; } _switches[moduleInfo] = val; ModuleInfo captured = moduleInfo; val.SettingChanged += delegate { Apply(captured); }; if (list.Count > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(moduleInfo.Title + ": off, needs " + string.Join(", ", list) + ".")); } } if (flag) { Remember("#switches"); } modules = Modules; foreach (ModuleInfo module in modules) { Apply(module); } } private void Start() { foreach (KeyValuePair<ModuleInfo, DigiModule> item in _running.ToList()) { if (!((Object)(object)item.Value == (Object)null) && Chainloader.PluginInfos.ContainsKey(item.Key.OldGuid)) { item.Value.Teardown(); _running.Remove(item.Key); Forget(item.Key.OldGuid); ReportDuplicate(item.Key); } } SettingOrder.Apply(((BaseUnityPlugin)this).Config); if (!Application.isBatchMode) { ((MonoBehaviour)this).StartCoroutine(ShowDuplicates()); } } private void ReportDuplicate(ModuleInfo module) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": not started, because the separate mod (" + module.OldGuid + ") is installed too. Remove that one.")); string item = ((BaseUnityPlugin)this).Info.Metadata.Name.Replace("Digi's ", "") + "|" + module.Title; if (!Duplicates.Contains(item)) { Duplicates.Add(item); } } internal void ReportReplaced(string what) { string item = ((BaseUnityPlugin)this).Info.Metadata.Name.Replace("Digi's ", "") + "|" + what; if (!Duplicates.Contains(item)) { Duplicates.Add(item); } } private IEnumerator ShowDuplicates() { while ((Object)(object)FejdStartup.instance == (Object)null || !UnifiedPopup.IsAvailable()) { yield return (object)new WaitForSeconds(0.5f); } yield return (object)new WaitForSeconds(1f); if (Duplicates.Count == 0 || AppDomain.CurrentDomain.GetData("digi.valheim.duplicates.shown") != null) { yield break; } AppDomain.CurrentDomain.SetData("digi.valheim.duplicates.shown", true); IEnumerable<string> values = from d in Duplicates select d.Split(new char[1] { '|' }) into d group d by d[0] into g select "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); string text = "These mods are installed twice: on their own and inside one of our combined mods.\n\n" + string.Join("\n", values) + "\n\nThe copy in the combined mod stays off until you remove the separate ones in your mod manager (or make a new profile with Digi Modpack). Your settings are copied over."; object obj = <>c.<>9__19_3; if (obj == null) { PopupButtonCallback val = delegate { UnifiedPopup.Pop(); }; <>c.<>9__19_3 = val; obj = (object)val; } UnifiedPopup.Push((PopupBase)new WarningPopup("Mods installed twice", text, (PopupButtonCallback)obj, false)); } private static string OldConfigPath(ModuleInfo module) { return Path.Combine(Paths.ConfigPath, module.OldGuid + ".cfg"); } private void Remember(string key) { if (!_migrated.Add(key)) { return; } try { File.AppendAllText(MigratedFile, key + Environment.NewLine); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Forget(string key) { if (!_migrated.Remove(key)) { return; } try { File.WriteAllLines(MigratedFile, _migrated.ToArray()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Apply(ModuleInfo module) { bool value = _switches[module].Value; _running.TryGetValue(module, out var value2); if (value && (Object)(object)value2 == (Object)null) { if (!module.Requires.Any((string r) => !Chainloader.PluginInfos.ContainsKey(r))) { if (Chainloader.PluginInfos.ContainsKey(module.OldGuid)) { ReportDuplicate(module); } else { StartModule(module); } } } else if (!value && (Object)(object)value2 != (Object)null) { value2.Teardown(); _running.Remove(module); ((BaseUnityPlugin)this).Logger.LogInfo((object)(module.Title + ": switched off.")); } } private void StartModule(ModuleInfo module) { StartingHost = this; StartingModule = module; try { DigiModule digiModule = (DigiModule)(object)((Component)this).gameObject.AddComponent(module.Type); digiModule.Host = this; digiModule.Info2 = module; _running[module] = digiModule; MigrateOldSettings(module, digiModule); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"{module.Title} could not start: {arg}"); } finally { StartingHost = null; StartingModule = null; } SettingOrder.Apply(((BaseUnityPlugin)this).Config); } private void MigrateOldSettings(ModuleInfo module, DigiModule component) { if (_migrated.Contains(module.OldGuid)) { return; } string path = OldConfigPath(module); if (File.Exists(path) && component.ConfigOrNull != null) { Dictionary<string, string> dictionary = ReadCfg(path); int num = 0; foreach (ModuleConfig.Bound entry in component.ConfigOrNull.Entries) { if (dictionary.TryGetValue(entry.Section + "\n" + entry.Key, out var value)) { try { entry.Entry.SetSerializedValue(value); num++; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": could not copy '" + entry.Key + "': " + ex.Message)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"{module.Title}: copied {num} setting(s) from {Path.GetFileName(path)}."); } Remember(module.OldGuid); } private static Dictionary<string, string> ReadCfg(string path) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string text = ""; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf(" = ", StringComparison.Ordinal); if (num > 0) { dictionary[text + "\n" + text2.Substring(0, num).Trim()] = text2.Substring(num + 3); } } return dictionary; } protected virtual void OnDestroy() { foreach (DigiModule item in _running.Values.ToList()) { item?.Teardown(); } _running.Clear(); } public bool IsRunning(Type moduleType) { return _running.Any((KeyValuePair<ModuleInfo, DigiModule> p) => p.Key.Type == moduleType && (Object)(object)p.Value != (Object)null); } } public abstract class DigiModule : MonoBehaviour { internal DigiPlugin Host; internal ModuleInfo Info2; private ModuleConfig _config; private ManualLogSource _logger; private Harmony _harmony; internal ModuleConfig ConfigOrNull => _config; protected ModuleConfig Config => _config ?? (_config = new ModuleConfig(((BaseUnityPlugin)(Host ?? DigiPlugin.StartingHost)).Config, (Info2 ?? DigiPlugin.StartingModule).Title)); protected ManualLogSource Logger => _logger ?? (_logger = Logger.CreateLogSource("Digi's " + (Info2 ?? DigiPlugin.StartingModule)?.Title)); protected Harmony ModuleHarmony { get { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004a: Expected O, but got Unknown Harmony obj = _harmony; if (obj == null) { Harmony val = new Harmony(((object)this).GetType().Namespace?.ToLowerInvariant() + "." + Guid.NewGuid().ToString("N")); Harmony val2 = val; _harmony = val; obj = val2; } return obj; } } protected void ReportReplacedMod(string what) { (Host ?? DigiPlugin.StartingHost)?.ReportReplaced(what); } protected Harmony PatchModule(Type type) { ModuleHarmony.PatchAll(type); return ModuleHarmony; } protected Harmony PatchModuleNamespace() { string text = ((object)this).GetType().Namespace; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly); foreach (Type type in typesFromAssembly) { if (type.Namespace == text || (type.Namespace != null && text != null && type.Namespace.StartsWith(text + "."))) { ModuleHarmony.CreateClassProcessor(type).Patch(); } } return ModuleHarmony; } internal void Teardown() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { Logger.LogWarning((object)("Unpatch: " + ex.Message)); } Object.Destroy((Object)(object)this); } } public sealed class ModuleConfig { public sealed class Bound { public string Section; public string Key; public ConfigEntryBase Entry; } private readonly ConfigFile _file; private readonly string _prefix; internal readonly List<Bound> Entries = new List<Bound>(); private static readonly char[] Invalid = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' }; public ConfigFile File => _file; public string ConfigFilePath => _file.ConfigFilePath; internal ModuleConfig(ConfigFile file, string title) { _file = file; _prefix = title + " - "; } public void Reload() { } public void Save() { _file.Save(); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null) { section = Clean(section); key = Clean(key); ConfigEntry<T> val = _file.Bind<T>(_prefix + section, key, defaultValue, configDescription); if (!Entries.Any((Bound b) => b.Section == section && b.Key == key)) { Entries.Add(new Bound { Section = section, Key = key, Entry = (ConfigEntryBase)(object)val }); } return val; } private static string Clean(string name) { if (name.IndexOfAny(Invalid) < 0) { return name; } string[] value = name.Split(Invalid, StringSplitOptions.None); return string.Join("", value).Trim(); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } public ConfigEntry<T> Bind<T>(ConfigDefinition definition, T defaultValue, ConfigDescription configDescription = null) { return Bind(definition.Section, definition.Key, defaultValue, configDescription); } public bool TryGetEntry<T>(ConfigDefinition definition, out ConfigEntry<T> entry) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown return _file.TryGetEntry<T>(new ConfigDefinition(_prefix + definition.Section, definition.Key), ref entry); } } } namespace DigiCrafting { [BepInPlugin("digi.valheim.crafting", "Digi's Crafting and Automation", "1.0.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : DigiPlugin { public const string Guid = "digi.valheim.crafting"; public const string Name = "Digi's Crafting and Automation"; public const string Version = "1.0.5"; protected override ModuleInfo[] Modules { get; } = new ModuleInfo[13] { new ModuleInfo(typeof(AutoProcessing.Plugin), "Auto Processing", "digi.valheim.autoprocessing", "Smelters, kilns, spinning wheels, eitr refineries and windmills fill themselves from nearby chests and store their output; sap extractors and beehives too."), new ModuleInfo(typeof(AutoOven.Plugin), "Auto Oven", "digi.valheim.autooven", "Ovens fill, fuel and empty themselves using nearby chests."), new ModuleInfo(typeof(AutoFermenter.Plugin), "Auto Fermenter", "digi.valheim.autofermenter", "Fermenters refill with mead base and tap finished mead into a chest by themselves."), new ModuleInfo(typeof(FermenterTimer.Plugin), "Fermenter Timer", "digi.valheim.fermentertimer", "Shows how long a fermenter still needs and what it will give."), new ModuleInfo(typeof(SapTimer.Plugin), "Sap Timer", "digi.valheim.saptimer", "Shows how much sap an ancient root holds and when a sap extractor collects its next sap."), new ModuleInfo(typeof(CraftBatch.Plugin), "Craft Batch", "digi.valheim.craftbatch", "Ctrl+Shift+Craft makes 20 at once."), new ModuleInfo(typeof(ShiftFillFromStorage.Plugin), "Shift Fill From Storage", "digi.valheim.shiftfillfromstorage", "Shift-fill smelters and fermenters straight from nearby chests.", new string[1] { "LuminairPrime.valheim.localcraftfromstorageprime" }), new ModuleInfo(typeof(RecycleTab.Plugin), "Recycle Tab", "digi.valheim.recycletab", "A Recycle tab at crafting stations to get materials back from items you don't need."), new ModuleInfo(typeof(AutoRepair.Plugin), "Auto Repair", "digi.valheim.autorepair", "Opening a crafting station repairs all your gear at once."), new ModuleInfo(typeof(RestyleItems.Plugin), "Restyle Items", "digi.valheim.restyleitems", "Change the style of an item you already own at a station."), new ModuleInfo(typeof(CookingMastery.Plugin), "Cooking Mastery", "digi.valheim.cookingmastery", "Your Cooking skill makes all food stronger and longer lasting."), new ModuleInfo(typeof(CraftStorageSpeedup.Plugin), "Craft From Storage Speedup", "digi.valheim.craftstoragespeedup", "Crafting stations with Local Craft From Storage no longer reload every nearby chest for every count: much less lag.", new string[1] { "LuminairPrime.valheim.localcraftfromstorageprime" }), new ModuleInfo(typeof(Cookbook.Plugin), "Cookbook", "digi.valheim.cookbook", "Press K for a book of every food you found: stats, where it is made and from what, sortable.") }; } } namespace ShiftFillFromStorage { public class Plugin : DigiModule { public const string Guid = "digi.valheim.shiftfillfromstorage"; public const string Name = "Digi's Shift Fill From Storage"; public const string Version = "1.0.2"; private const string LcfspGuid = "LuminairPrime.valheim.localcraftfromstorageprime"; private const string BetterSmelterGuid = "com.alan.bettersmelter"; private const int MaxItemsPerFill = 200; private static Plugin _instance; private static bool _filling; private static FieldInfo _inSwitch; private static PropertyInfo _busy; private static PropertyInfo _settings; private static PropertyInfo _dispatchReturned; private static PropertyInfo _enabled; private static PropertyInfo _fuelFromStorage; private static PropertyInfo _oreFromStorage; private static PropertyInfo _refuelDistance; private static MethodInfo _start; private static MethodInfo _fuelName; private static MethodInfo _fuelRoom; private static MethodInfo _oreRoom; private static MethodInfo _carriesOre; private static MethodInfo _suppliesAny; private static ConstructorInfo _operation; private static ConstructorInfo _fuelSupply; private static ConstructorInfo _oreSupply; private static MethodInfo _betterFuelPrefix; private static MethodInfo _betterOrePrefix; private static ConfigEntry<bool> _fermenterFromStorage; private static readonly ConditionalWeakTable<object, Fermenter> FermenterTargets = new ConditionalWeakTable<object, Fermenter>(); private static MethodInfo _spare; private static MethodInfo _updateCover; private static FieldInfo _hasRoof; private static FieldInfo _exposed; private static bool Busy => (bool)_busy.GetValue(null); private static object Settings => _settings.GetValue(null); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.shiftfillfromstorage".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { MigrateConfig(); _instance = this; try { Resolve(); } catch (Exception ex) { base.Logger.LogError((object)("Disabled, Local Craft From Storage Prime internals not found: " + ex.Message)); return; } Harmony moduleHarmony = base.ModuleHarmony; MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "OnAddFuel", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null); if (Chainloader.PluginInfos.ContainsKey("com.alan.bettersmelter")) { _betterFuelPrefix = AccessTools.Method(AccessTools.TypeByName("Smelter_OnAddFuel_BulkPatch"), "Prefix", (Type[])null, (Type[])null); _betterOrePrefix = AccessTools.Method(AccessTools.TypeByName("Smelter_OnAddOre_BulkPatch"), "Prefix", (Type[])null, (Type[])null); } if (_betterFuelPrefix != null) { moduleHarmony.Unpatch((MethodBase)methodInfo, _betterFuelPrefix); moduleHarmony.Patch((MethodBase)methodInfo, Hook("BetterFuelWrapper"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (_betterOrePrefix != null) { moduleHarmony.Unpatch((MethodBase)methodInfo2, _betterOrePrefix); moduleHarmony.Patch((MethodBase)methodInfo2, Hook("BetterOreWrapper"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } moduleHarmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, Hook("AddFuelPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); moduleHarmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, Hook("AddOrePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _fermenterFromStorage = base.Config.Bind("Fermenter", "Fill from storage", defaultValue: true, "Using an empty fermenter while you carry no mead base takes one from a nearby chest."); try { PatchFermenter(moduleHarmony); } catch (Exception ex2) { base.Logger.LogError((object)("Fermenter from storage disabled: " + ex2.Message)); } base.Logger.LogInfo((object)"Shift-filling smelters now also pulls from nearby chests; fermenters fill from chests."); } private static HarmonyMethod Hook(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(typeof(Plugin).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)); } private static void Resolve() { Type type = L("SupplyAction"); _inSwitch = Need<FieldInfo>(AccessTools.Field(type, "InSwitch"), "InSwitch"); _fuelName = Need<MethodInfo>(AccessTools.Method(type, "FuelName", (Type[])null, (Type[])null), "FuelName"); _fuelRoom = Need<MethodInfo>(AccessTools.Method(type, "SmelterFuelRoom", (Type[])null, (Type[])null), "SmelterFuelRoom"); _oreRoom = Need<MethodInfo>(AccessTools.Method(type, "SmelterOreRoom", (Type[])null, (Type[])null), "SmelterOreRoom"); _carriesOre = Need<MethodInfo>(AccessTools.Method(type, "CarriesOre", (Type[])null, (Type[])null), "CarriesOre"); _suppliesAny = Need<MethodInfo>(AccessTools.Method(type, "SuppliesAny", (Type[])null, (Type[])null), "SuppliesAny"); Type type2 = L("ContainerAccessService"); _busy = Need<PropertyInfo>(AccessTools.Property(type2, "Busy"), "Busy"); _start = Need<MethodInfo>(AccessTools.Method(type2, "Start", (Type[])null, (Type[])null), "Start"); Type type3 = L("SupplyPaymentOperation"); _operation = Need<ConstructorInfo>(AccessTools.Constructor(type3, new Type[4] { typeof(Player), L("SupplyTarget"), typeof(Vector3), typeof(float) }, false), "SupplyPaymentOperation()"); _dispatchReturned = Need<PropertyInfo>(AccessTools.Property(type3, "DispatchReturned"), "DispatchReturned"); _fuelSupply = Need<ConstructorInfo>(AccessTools.Constructor(L("SmelterFuelSupply"), new Type[2] { typeof(Smelter), typeof(string) }, false), "SmelterFuelSupply()"); _oreSupply = Need<ConstructorInfo>(AccessTools.Constructor(L("SmelterOreSupply"), new Type[1] { typeof(Smelter) }, false), "SmelterOreSupply()"); _settings = Need<PropertyInfo>(AccessTools.Property(L("Plugin"), "Settings"), "Settings"); Type type4 = L("RuntimeSettings"); _enabled = Need<PropertyInfo>(AccessTools.Property(type4, "Enabled"), "Enabled"); _fuelFromStorage = Need<PropertyInfo>(AccessTools.Property(type4, "SmelterFuelFromStorage"), "SmelterFuelFromStorage"); _oreFromStorage = Need<PropertyInfo>(AccessTools.Property(type4, "SmelterOreFromStorage"), "SmelterOreFromStorage"); _refuelDistance = Need<PropertyInfo>(AccessTools.Property(type4, "RefuelDistance"), "RefuelDistance"); static Type L(string name) { return Need<Type>(AccessTools.TypeByName("LocalCraftFromStoragePrime." + name), name); } static T Need<T>(T member, string what) where T : class { return member ?? throw new MissingMemberException(what); } } private static bool ShiftHeld() { if (!ZInput.GetKey((KeyCode)304, true)) { return ZInput.GetKey((KeyCode)303, true); } return true; } private static bool Setting(PropertyInfo p) { object settings = Settings; if (settings != null) { return (bool)p.GetValue(settings); } return false; } private static string FuelName(Smelter s) { return _fuelName.Invoke(null, new object[1] { s.m_fuelItem }) as string; } private static bool FuelRoom(Smelter s) { return (bool)_fuelRoom.Invoke(null, new object[1] { s }); } private static bool OreRoom(Smelter s) { return (bool)_oreRoom.Invoke(null, new object[1] { s }); } private static bool CarriesFuel(Player p, Smelter s) { string text = FuelName(s); if (text != null) { return ((Humanoid)p).GetInventory().HaveItem(text, true); } return false; } private static bool CarriesOre(Player p, Smelter s) { return (bool)_carriesOre.Invoke(null, new object[2] { s, ((Humanoid)p).GetInventory() }); } private static int FuelSpace(Smelter s) { ZNetView component = ((Component)s).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return 0; } return Mathf.Max(0, s.m_maxFuel - Mathf.CeilToInt(component.GetZDO().GetFloat(ZDOVars.s_fuel, 0f))); } private static int OreSpace(Smelter s) { ZNetView component = ((Component)s).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return 0; } return Mathf.Max(0, s.m_maxOre - component.GetZDO().GetInt(ZDOVars.s_queued, 0)); } private static bool BetterFuelWrapper(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && item == null && !CarriesFuel(val, __instance)) { return true; } return CallBetter(_betterFuelPrefix, sw, user, item, __instance, ref __result); } private static bool BetterOreWrapper(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && item == null && !CarriesOre(val, __instance)) { return true; } return CallBetter(_betterOrePrefix, sw, user, item, __instance, ref __result); } private static bool CallBetter(MethodInfo prefix, Switch sw, Humanoid user, ItemData item, Smelter smelter, ref bool result) { object[] array = new object[5] { sw, user, item, smelter, result }; bool result2 = (bool)prefix.Invoke(null, array); result = (bool)array[4]; return result2; } private static void AddFuelPostfix(Humanoid user, ItemData item, Smelter __instance) { TryFill(user, item, __instance, fuel: true); } private static void AddOrePostfix(Humanoid user, ItemData item, Smelter __instance) { TryFill(user, item, __instance, fuel: false); } private static void TryFill(Humanoid user, ItemData item, Smelter smelter, bool fuel) { if (_filling || item != null || !ShiftHeld() || !(bool)_inSwitch.GetValue(null)) { return; } Player val = (Player)(object)((user is Player) ? user : null); if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !Setting(_enabled)) { return; } if (fuel) { if (!Setting(_fuelFromStorage) || FuelName(smelter) == null || CarriesFuel(val, smelter)) { return; } } else if (!Setting(_oreFromStorage) || CarriesOre(val, smelter) || !(bool)_suppliesAny.Invoke(null, new object[2] { smelter, Settings })) { return; } int num = (fuel ? FuelSpace(smelter) : OreSpace(smelter)); if (num > 1) { ((MonoBehaviour)_instance).StartCoroutine(Fill(val, smelter, fuel, Mathf.Min(num - 1, 200))); } } private static void PatchFermenter(Harmony harmony) { Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.SmelterOreSupply"); _spare = AccessTools.Method(AccessTools.TypeByName("LocalCraftFromStoragePrime.SupplyTarget"), "Spare", (Type[])null, (Type[])null) ?? throw new MissingMemberException("SupplyTarget.Spare"); _updateCover = AccessTools.Method(typeof(Fermenter), "UpdateCover", new Type[2] { typeof(float), typeof(bool) }, (Type[])null) ?? throw new MissingMemberException("Fermenter.UpdateCover"); _hasRoof = AccessTools.Field(typeof(Fermenter), "m_hasRoof") ?? throw new MissingMemberException("Fermenter.m_hasRoof"); _exposed = AccessTools.Field(typeof(Fermenter), "m_exposed") ?? throw new MissingMemberException("Fermenter.m_exposed"); Redirect(AccessTools.PropertyGetter(type, "Station"), "StationPrefix"); Redirect(AccessTools.PropertyGetter(type, "MissingMessage"), "MissingPrefix"); Redirect(AccessTools.Method(type, "CanReceive", (Type[])null, (Type[])null), "CanReceivePrefix"); Redirect(AccessTools.Method(type, "Carried", (Type[])null, (Type[])null), "CarriedPrefix"); Redirect(AccessTools.Method(type, "Pick", (Type[])null, (Type[])null), "PickPrefix"); Redirect(AccessTools.Method(type, "Send", (Type[])null, (Type[])null), "SendPrefix"); Redirect(AccessTools.Method(type, "ShowFeedback", (Type[])null, (Type[])null), "FeedbackPrefix"); harmony.Patch((MethodBase)AccessTools.Method(typeof(Fermenter), "Interact", (Type[])null, (Type[])null), Hook("FermenterInteractPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); void Redirect(MethodInfo method, string hook) { if (method == null) { throw new MissingMemberException("SmelterOreSupply member for " + hook); } harmony.Patch((MethodBase)method, Hook(hook), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static bool FermenterEmpty(Fermenter f) { ZNetView val = (((Object)(object)f == (Object)null) ? null : ((Component)f).GetComponent<ZNetView>()); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO().GetInt(ZDOVars.s_content, 0) == 0; } return false; } private static ItemData CarriedMeadBase(Fermenter f, Inventory inventory) { foreach (ItemConversion item2 in f.m_conversion) { ItemData item = inventory.GetItem(item2.m_from.m_itemData.m_shared.m_name, -1, false); if (item != null) { return item; } } return null; } private static bool FermenterInteractPrefix(Fermenter __instance, Humanoid user, bool hold, ref bool __result) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (!hold && _fermenterFromStorage.Value) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { if (!Setting(_enabled) || Busy || !FermenterEmpty(__instance)) { return true; } if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false)) { return true; } _updateCover.Invoke(__instance, new object[2] { 0f, true }); if (!(bool)_hasRoof.GetValue(__instance) || (bool)_exposed.GetValue(__instance)) { return true; } if (CarriedMeadBase(__instance, ((Humanoid)val).GetInventory()) != null) { return true; } object obj = _oreSupply.Invoke(new object[1]); FermenterTargets.Add(obj, __instance); float num = (float)_refuelDistance.GetValue(Settings); _start.Invoke(null, new object[1] { _operation.Invoke(new object[4] { val, obj, ((Component)__instance).transform.position, num }) }); __result = true; return false; } } return true; } private static bool StationPrefix(object __instance, ref Component __result) { if (!FermenterTargets.TryGetValue(__instance, out var value)) { return true; } __result = (Component)(object)value; return false; } private static bool MissingPrefix(object __instance, ref string __result) { if (!FermenterTargets.TryGetValue(__instance, out var _)) { return true; } __result = "$msg_noprocessableitems"; return false; } private static bool CanReceivePrefix(object __instance, ref bool __result) { if (!FermenterTargets.TryGetValue(__instance, out var value)) { return true; } __result = FermenterEmpty(value); return false; } private static bool CarriedPrefix(object __instance, Inventory inventory, ref bool __result) { if (!FermenterTargets.TryGetValue(__instance, out var value)) { return true; } __result = CarriedMeadBase(value, inventory) != null; return false; } private static bool PickPrefix(object __instance, object[] __args, ref ItemData __result) { if (!FermenterTargets.TryGetValue(__instance, out var value)) { return true; } __result = null; foreach (ItemConversion item in value.m_conversion) { object? obj = _spare.Invoke(null, new object[3] { __args[0], item.m_from.m_itemData.m_shared.m_name, __args[1] }); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if ((Object)(object)val?.m_dropPrefab != (Object)null) { __result = val; break; } } return false; } private static bool SendPrefix(object __instance, ZNetView view, string prefab, bool cheated) { if (!FermenterTargets.TryGetValue(__instance, out var _)) { return true; } view.InvokeRPC("RPC_AddItem", new object[2] { StringExtensionMethods.GetStableHashCode(prefab), cheated }); return false; } private static bool FeedbackPrefix(object __instance, Player player, string name) { if (!FermenterTargets.TryGetValue(__instance, out var _)) { return true; } ((Character)player).Message((MessageType)2, "$msg_added " + name, 0, (Sprite)null, false); return false; } private static IEnumerator Fill(Player player, Smelter smelter, bool fuel, int remaining) { _filling = true; try { while (Busy) { yield return null; } Vector3 origin = ((Component)smelter).transform.position; float radius = (float)_refuelDistance.GetValue(Settings); while (remaining > 0 && (Object)(object)smelter != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && (fuel ? FuelRoom(smelter) : OreRoom(smelter)) && !(fuel ? CarriesFuel(player, smelter) : CarriesOre(player, smelter))) { object obj = (fuel ? _fuelSupply.Invoke(new object[2] { smelter, FuelName(smelter) }) : _oreSupply.Invoke(new object[1] { smelter })); object operation = _operation.Invoke(new object[4] { player, obj, origin, radius }); _start.Invoke(null, new object[1] { operation }); yield return null; while (Busy) { yield return null; } if ((bool)_dispatchReturned.GetValue(operation)) { remaining--; continue; } break; } } finally { _filling = false; } } } } namespace SapTimer { public class Plugin : DigiModule { public const string Guid = "digi.valheim.saptimer"; public const string Name = "Digi's Sap Timer"; public const string Version = "1.0.1"; private static ConfigEntry<bool> _roots; private static ConfigEntry<bool> _extractors; private static readonly FieldRef<SapCollector, ZNetView> ExtractorView = AccessTools.FieldRefAccess<SapCollector, ZNetView>("m_nview"); private static readonly FieldRef<SapCollector, ResourceRoot> ExtractorRoot = AccessTools.FieldRefAccess<SapCollector, ResourceRoot>("m_root"); private static readonly List<SapCollector> Extractors = new List<SapCollector>(); private void Awake() { _roots = base.Config.Bind("General", "Show on ancient roots", defaultValue: true, "Show how much sap an ancient root holds and when it is full again."); _extractors = base.Config.Bind("General", "Show on sap extractors", defaultValue: true, "Show when a sap extractor collects its next sap and when it is full."); PatchModule(typeof(Plugin)); } [HarmonyPatch(typeof(SapCollector), "Awake")] [HarmonyPostfix] private static void ExtractorAwake(SapCollector __instance) { Extractors.RemoveAll((SapCollector e) => (Object)(object)e == (Object)null); Extractors.Add(__instance); } private static int Tapping(ResourceRoot root) { int num = 0; foreach (SapCollector extractor in Extractors) { if (!((Object)(object)extractor == (Object)null) && !((Object)(object)ExtractorRoot.Invoke(extractor) != (Object)(object)root)) { ZNetView val = ExtractorView.Invoke(extractor); if ((Object)(object)val != (Object)null && val.IsValid() && val.GetZDO().GetInt(ZDOVars.s_level, 0) < extractor.m_maxLevel) { num++; } } } return num; } [HarmonyPatch(typeof(ResourceRoot), "GetHoverText")] [HarmonyPostfix] private static void RootText(ResourceRoot __instance, ref string __result) { if (_roots.Value && !((Object)(object)ZNet.instance == (Object)null) && !(__instance.m_regenPerSec <= 0f)) { float level = __instance.GetLevel(); float maxLevel = __instance.m_maxLevel; string text = $"\nSap: <color=yellow>{Mathf.FloorToInt(level)} / {maxLevel:0}</color>, regrows 1 every {Format(1f / __instance.m_regenPerSec)}"; int num = Tapping(__instance); if (num > 0) { text += string.Format("\nTapped by {0} extractor{1}: {2} the sap as it regrows", num, (num == 1) ? "" : "s", (num == 1) ? "it collects" : "they share"); } else if (level < maxLevel) { text = text + "\nFull in <color=yellow>" + Format((maxLevel - level) / __instance.m_regenPerSec) + "</color>"; } __result = Insert(__result, text); } } [HarmonyPatch(typeof(SapCollector), "GetHoverText")] [HarmonyPostfix] private static void ExtractorText(SapCollector __instance, ref string __result) { if (!_extractors.Value || (Object)(object)ZNet.instance == (Object)null) { return; } ZNetView val = ExtractorView.Invoke(__instance); ResourceRoot val2 = ExtractorRoot.Invoke(__instance); if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)val2 == (Object)null) { return; } ZDO zDO = val.GetZDO(); int num = zDO.GetInt(ZDOVars.s_level, 0); int maxLevel = __instance.m_maxLevel; float level = val2.GetLevel(); string text; if (num >= maxLevel) { text = "\n<color=orange>Full: empty it to keep collecting</color>"; } else { float num2 = (float)(ZNet.instance.GetTime() - new DateTime(zDO.GetLong(ZDOVars.s_lastTime, ZNet.instance.GetTime().Ticks))).TotalSeconds; float secPerUnit = __instance.m_secPerUnit; float num3 = Mathf.Max(val2.m_regenPerSec, 1E-06f); int num4 = Mathf.Max(1, Tapping(val2)); float num5 = ((!val2.CanDrain(1f)) ? ((1.01f - level) / num3 + Mathf.Max(0f, secPerUnit - zDO.GetFloat(ZDOVars.s_product, 0f))) : Mathf.Max(0f, secPerUnit - (zDO.GetFloat(ZDOVars.s_product, 0f) + Mathf.Max(0f, num2)))); bool flag = !val2.CanDrain(1f); text = "\nNext sap in <color=yellow>" + (flag ? "~" : "") + Format(num5) + "</color>" + (flag ? " (the root is regrowing)" : ""); int num6 = maxLevel - num; int num7 = Mathf.Max(0, Mathf.FloorToInt(level) - 1) / num4; int num8 = Mathf.Min(num6, num7); float num9 = Mathf.Max(secPerUnit, (float)num4 / num3); float num10 = ((num8 > 0) ? (num5 + (float)(num8 - 1) * secPerUnit + (float)(num6 - num8) * num9) : (num5 + (float)(num6 - 1) * num9)); if (num6 > 1) { text = text + "\nFull in <color=yellow>~" + Format(num10) + "</color>"; } } text += $"\nRoot: {Mathf.FloorToInt(level)} / {val2.m_maxLevel:0} sap"; __result = Insert(__result, text); } private static string Insert(string hover, string text) { if (string.IsNullOrEmpty(hover)) { return text.TrimStart(new char[1] { '\n' }); } int num = hover.IndexOf('\n'); if (num >= 0) { return hover.Substring(0, num) + text + hover.Substring(num); } return hover + text; } private static string Format(double seconds) { TimeSpan timeSpan = TimeSpan.FromSeconds(Math.Ceiling(Math.Max(0.0, seconds))); if (!(timeSpan.TotalHours >= 1.0)) { return $"{timeSpan.Minutes}:{timeSpan.Seconds:00}"; } return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"; } } } namespace RestyleItems { public class Plugin : DigiModule { public const string Guid = "digi.valheim.restyleitems"; public const string Name = "Digi's Restyle Items"; public const string Version = "1.0.2"; private static ConfigEntry<KeyCode> _key; private static MethodInfo _hovered; private static readonly FieldRef<InventoryGui, InventoryGrid> ContainerGrid = AccessTools.FieldRefAccess<InventoryGui, InventoryGrid>("m_containerGrid"); private static ItemData _pending; private static Inventory _pendingInventory; private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.restyleitems".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) MigrateConfig(); _key = base.Config.Bind<KeyCode>("General", "Restyle key", (KeyCode)114, "At a station that can craft the item, hover over it in your inventory and press this key to pick another style."); _hovered = AccessTools.Method(typeof(InventoryGrid), "GetHoveredElement", (Type[])null, (Type[])null); if (_hovered == null) { base.Logger.LogError((object)"Disabled, InventoryGrid.GetHoveredElement was not found."); return; } base.ModuleHarmony.PatchAll(typeof(Plugin)); base.Logger.LogInfo((object)("Hover an item at its crafting station and press " + ((object)_key.Value/*cast due to .constrained prefix*/).ToString() + " to restyle it.")); } [HarmonyPatch(typeof(InventoryGui), "Update")] [HarmonyPostfix] private static void UpdatePostfix(InventoryGui __instance) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (_pending != null && !((Component)__instance.m_variantDialog).gameObject.activeSelf) { _pending = null; } if (!InventoryGui.IsVisible() || ((Component)__instance.m_variantDialog).gameObject.activeSelf || !ZInput.GetKeyDown(_key.Value, true) || Console.IsVisible() || TextInput.IsVisible()) { return; } Chat instance = Chat.instance; if (instance != null && instance.HasFocus()) { return; } Player localPlayer = Player.m_localPlayer; Inventory inventory = __instance.m_playerGrid.GetInventory(); ItemData val = Hovered(__instance.m_playerGrid); if (val == null && (Object)(object)ContainerGrid.Invoke(__instance) != (Object)null) { inventory = ContainerGrid.Invoke(__instance).GetInventory(); val = Hovered(ContainerGrid.Invoke(__instance)); } if ((Object)(object)localPlayer == (Object)null || val?.m_shared == null) { return; } if (val.m_shared.m_variants <= 1) { ((Character)localPlayer).Message((MessageType)2, "This item has no other styles", 0, (Sprite)null, false); return; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); Recipe val2 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetRecipe(val) : null); CraftingStation val3 = val2?.m_craftingStation; if ((Object)(object)currentCraftingStation == (Object)null || (Object)(object)val2 == (Object)null || ((Object)(object)val3 != (Object)null && val3.m_name != currentCraftingStation.m_name)) { ((Character)localPlayer).Message((MessageType)2, ((Object)(object)val3 != (Object)null) ? ("Restyle at a " + val3.m_name) : "Restyle at a crafting station", 0, (Sprite)null, false); return; } _pending = val; _pendingInventory = inventory; __instance.m_variantDialog.Setup(val); } private static ItemData Hovered(InventoryGrid grid) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)grid == (Object)null) { return null; } object? obj = _hovered.Invoke(grid, null); InventoryElement val = (InventoryElement)((obj is InventoryElement) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } Inventory inventory = grid.GetInventory(); if (inventory == null) { return null; } return inventory.GetItemAt(val.Position.x, val.Position.y); } [HarmonyPatch(typeof(InventoryGui), "OnVariantSelected")] [HarmonyPrefix] private static bool OnVariantSelectedPrefix(int index) { ItemData pending = _pending; Inventory pendingInventory = _pendingInventory; _pending = null; _pendingInventory = null; if (pending == null) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || index < 0 || index >= pending.m_shared.m_variants) { return false; } if (pending.m_variant == index) { return false; } bool num = ((Humanoid)localPlayer).IsItemEquiped(pending); if (num) { ((Humanoid)localPlayer).UnequipItem(pending, false); } pending.m_variant = index; if (num) { ((Humanoid)localPlayer).EquipItem(pending, false); } if (pendingInventory != null) { InventoryChanged?.Invoke(pendingInventory, null); } ((Character)localPlayer).Message((MessageType)2, "Style changed", 0, (Sprite)null, false); return false; } } } namespace RecycleTab { public class Plugin : DigiModule { [CompilerGenerated] private static class <>O { public static PopupButtonCallback <0>__Pop; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__21_0; public static UnityAction <>9__21_1; public static Func<KeyValuePair<Recipe, ItemData>, bool> <>9__28_0; internal void <EnsureTab>b__21_0() { _active = true; InventoryGui.instance.UpdateCraftingPanel(false); } internal void <EnsureTab>b__21_1() { _showEquipped.Value = !_showEquipped.Value; UpdateToggleText(); InventoryGui.instance.UpdateCraftingPanel(false); } internal bool <RecipeList>b__28_0(KeyValuePair<Recipe, ItemData> e) { if (!Equipped.Contains(e.Value)) { return Hotbar.Contains(e.Value); } return true; } } public const string Guid = "digi.valheim.recycletab"; public const string Name = "Digi's Recycle Tab"; public const string Version = "1.0.1"; private static ConfigEntry<int> _returnPercent; private static ConfigEntry<bool> _keepItem; private static ConfigEntry<bool> _showEquipped; private static ConfigEntry<bool> _confirm; private static ConfigEntry<string> _excludedStations; private static bool _active; private static bool _skipConfirm; private static GameObject _tabObject; private static GameObject _toggleObject; private static Button _tab; private static Button _toggle; private static readonly HashSet<ItemData> Equipped = new HashSet<ItemData>(); private static readonly HashSet<ItemData> Hotbar = new HashSet<ItemData>(); private static readonly List<ItemData> Temp = new List<ItemData>(); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.recycletab".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown MigrateConfig(); _returnPercent = base.Config.Bind("General", "Materials returned (%)", 100, new ConfigDescription("How much of the crafting materials you get back: 100 = all, 50 = half.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _keepItem = base.Config.Bind("General", "Keep item data on downgrade", defaultValue: true, "An item that drops one quality level keeps its enchantments and crafter name. Off = you get a plain new item."); _showEquipped = base.Config.Bind("General", "Show equipped and hotbar items", defaultValue: false, "Also list what you are wearing and what is on your hotbar."); _confirm = base.Config.Bind("General", "Confirm equipped and hotbar items", defaultValue: true, "Ask before recycling something you are wearing or have on your hotbar."); _excludedStations = base.Config.Bind("General", "Stations without the tab", "cauldron", "Stations that get no Recycle tab, as words matched against the station name, comma-separated (the cauldron by default)."); PatchModule(typeof(Plugin)); KeepLocalCraftOut(); } private void KeepLocalCraftOut() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.PaymentAction"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Craft", new Type[2] { typeof(InventoryGui), typeof(Player) }, (Type[])null) : null); if (!(methodInfo == null)) { base.ModuleHarmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "LocalCraftPayment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static bool LocalCraftPayment(ref bool __result) { if (!_active) { return true; } __result = true; return false; } private static void EnsureTab(InventoryGui gui) { //IL_005e: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown if ((Object)(object)_tabObject != (Object)null) { return; } Button tabUpgrade = gui.m_tabUpgrade; _tabObject = Object.Instantiate<GameObject>(((Component)tabUpgrade).gameObject, ((Component)tabUpgrade).transform.parent, false); ((Object)_tabObject).name = "RecycleTab"; _tabObject.GetComponentInChildren<TMP_Text>().text = "RECYCLE"; Rect rect = _tabObject.GetComponent<RectTransform>().rect; float width = ((Rect)(ref rect)).width; float num = ((Component)tabUpgrade).transform.localPosition.x; foreach (Transform item in ((Component)tabUpgrade).transform.parent) { Transform val = item; if ((Object)(object)((Component)val).gameObject != (Object)(object)_tabObject && ((Component)val).gameObject.activeSelf && (Object)(object)((Component)val).GetComponent<Button>() != (Object)null) { num = Mathf.Max(num, val.localPosition.x); } } _tabObject.transform.localPosition = new Vector3(num + width + 10f, ((Component)tabUpgrade).transform.localPosition.y, ((Component)tabUpgrade).transform.localPosition.z); _tab = _tabObject.GetComponent<Button>(); _tab.onClick = new ButtonClickedEvent(); ButtonClickedEvent onClick = _tab.onClick; object obj = <>c.<>9__21_0; if (obj == null) { UnityAction val2 = delegate { _active = true; InventoryGui.instance.UpdateCraftingPanel(false); }; <>c.<>9__21_0 = val2; obj = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj); _tabObject.SetActive(false); _toggleObject = Object.Instantiate<GameObject>(_tabObject, ((Component)gui.m_craftButton).transform.parent, false); ((Object)_toggleObject).name = "RecycleShowEquipped"; _toggle = _toggleObject.GetComponent<Button>(); _toggle.onClick = new ButtonClickedEvent(); ButtonClickedEvent onClick2 = _toggle.onClick; object obj2 = <>c.<>9__21_1; if (obj2 == null) { UnityAction val3 = delegate { _showEquipped.Value = !_showEquipped.Value; UpdateToggleText(); InventoryGui.instance.UpdateCraftingPanel(false); }; <>c.<>9__21_1 = val3; obj2 = (object)val3; } ((UnityEvent)onClick2).AddListener((UnityAction)obj2); _toggleObject.transform.localPosition = new Vector3(140f, -45f, 0f); _toggleObject.GetComponent<RectTransform>().sizeDelta = new Vector2(160f, 30f); UpdateToggleText(); _toggleObject.SetActive(false); } private static void UpdateToggleText() { TMP_Text val = (((Object)(object)_toggleObject != (Object)null) ? _toggleObject.GetComponentInChildren<TMP_Text>() : null); if (!((Object)(object)val == (Object)null)) { val.text = (_showEquipped.Value ? "[X] Include equipped" : "[ ] Include equipped"); val.fontSize = 16f; } } private static bool StationAllowed(CraftingStation station) { if ((Object)(object)station == (Object)null) { return false; } string text = ((Object)((Component)station).gameObject).name.ToLowerInvariant(); string[] array = (_excludedStations.Value ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim().ToLowerInvariant(); if (text2.Length > 0 && text.Contains(text2)) { return false; } } return true; } [HarmonyPatch(typeof(InventoryGui), "OnTabCraftPressed")] [HarmonyPatch(typeof(InventoryGui), "OnTabUpgradePressed")] [HarmonyPrefix] private static void OtherTab() { _active = false; if ((Object)(object)_tab != (Object)null) { ((Selectable)_tab).interactable = true; } } [HarmonyPatch(typeof(InventoryGui), "Hide")] [HarmonyPostfix] private static void Hide() { _active = false; } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] [HarmonyPrefix] private static void PanelPrefix(InventoryGui __instance) { EnsureTab(__instance); Player localPlayer = Player.m_localPlayer; if (_active && ((Object)(object)localPlayer == (Object)null || !StationAllowed(localPlayer.GetCurrentCraftingStation()))) { _active = false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] [HarmonyPostfix] private static void PanelPostfix(InventoryGui __instance) { Player localPlayer = Player.m_localPlayer; bool flag = (Object)(object)localPlayer != (Object)null && StationAllowed(localPlayer.GetCurrentCraftingStation()) && ((Component)__instance.m_tabUpgrade).gameObject.activeSelf; _tabObject.SetActive(flag); if (!flag) { _active = false; } _toggleObject.SetActive(_active); if (_active) { ((Selectable)__instance.m_tabCraft).interactable = true; ((Selectable)__instance.m_tabUpgrade).interactable = true; ((Selectable)_tab).interactable = false; } else { ((Selectable)_tab).interactable = true; } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] [HarmonyPostfix] private static void RecipeList(InventoryGui __instance, List<Recipe> recipes) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) if (!_active) { return; } Player localPlayer = Player.m_localPlayer; Inventory inventory = ((Humanoid)localPlayer).GetInventory(); foreach (RecipeDataPair availableRecipe in __instance.m_availableRecipes) { RecipeDataPair current = availableRecipe; Object.Destroy((Object)(object)((RecipeDataPair)(ref current)).InterfaceElement); } __instance.m_availableRecipes.Clear(); Equipped.Clear(); foreach (ItemData equippedItem in inventory.GetEquippedItems()) { Equipped.Add(equippedItem); } Hotbar.Clear(); Temp.Clear(); inventory.GetBoundItems(Temp); foreach (ItemData item in Temp) { Hotbar.Add(item); } List<KeyValuePair<Recipe, ItemData>> list = new List<KeyValuePair<Recipe, ItemData>>(); foreach (Recipe recipe in recipes) { SharedData val = recipe.m_item?.m_itemData.m_shared; if (val == null || val.m_maxQuality < 1) { continue; } foreach (ItemData allItem in inventory.GetAllItems()) { if (!(allItem.m_shared.m_name != val.m_name) && allItem.m_quality >= 1 && (val.m_maxStackSize <= 1 || allItem.m_stack >= recipe.m_amount) && (_showEquipped.Value || (!Equipped.Contains(allItem) && !Hotbar.Contains(allItem)))) { list.Add(new KeyValuePair<Recipe, ItemData>(recipe, allItem)); if (val.m_maxStackSize > 1) { break; } } } } list = list.OrderBy((KeyValuePair<Recipe, ItemData> e) => Equipped.Contains(e.Value) || Hotbar.Contains(e.Value)).ToList(); CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); foreach (KeyValuePair<Recipe, ItemData> item2 in list) { Recipe key = item2.Key; ItemData value = item2.Value; bool flag = (StationOk(key, value, currentCraftingStation, localPlayer) || localPlayer.NoCostCheat()) && HaveSpace(inventory, key, value); __instance.AddRecipeToList(localPlayer, key, value, flag); RecipeDataPair val2 = __instance.m_availableRecipes[__instance.m_availableRecipes.Count - 1]; GameObject interfaceElement = ((RecipeDataPair)(ref val2)).InterfaceElement; Transform obj = interfaceElement.transform.Find("icon"); Image val3 = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null); if ((Object)(object)val3 != (Object)null) { val3.sprite = value.GetIcon(); } if (Equipped.Contains(value) || Hotbar.Contains(value)) { Transform obj2 = interfaceElement.transform.Find("name"); TMP_Text val4 = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); if ((Object)(object)val4 != (Object)null) { val4.text += (Equipped.Contains(value) ? " <color=yellow>(Equipped)</color>" : " <color=yellow>(Hotbar)</color>"); } } } __instance.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(__instance.m_recipeListBaseSize, (float)__instance.m_availableRecipes.Count * __instance.m_recipeListSpace)); } private static bool StationOk(Recipe recipe, ItemData item, CraftingStation station, Player player) { int num = item?.m_quality ?? 1; if (!((Object)(object)recipe.GetRequiredStation(num) == (Object)null)) { if ((Object)(object)station != (Object)null && station.CheckUsable(player, false)) { return station.GetLevel(true) >= recipe.GetRequiredStationLevel(num); } return false; } return true; } private static int Returned(Requirement req, int quality) { return (int)Math.Round((double)_returnPercent.Value / 100.0 * (double)req.GetAmount(quality)); } private static IEnumerable<Requirement> Returns(Recipe recipe) { Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val.m_resItem == (Object)null) && !val.m_upgraderResource) { yield return val; if (recipe.m_requireOnlyOneIngredient) { break; } } } } private static bool HaveSpace(Inventory inv, Recipe recipe, ItemData item) { int num = 0; foreach (Requirement item2 in Returns(recipe)) { int num2 = Returned(item2, item.m_quality); if (num2 > 0) { SharedData shared = item2.m_resItem.m_itemData.m_shared; int num3 = Mathf.Max(0, num2 - inv.FindFreeStackSpace(shared.m_name, (float)item2.m_resItem.m_itemData.m_worldLevel)); if (num3 > 0) { num += Mathf.CeilToInt((float)num3 / (float)shared.m_maxStackSize); } } } int num4 = ((item.m_quality <= 1 && item.m_shared.m_maxStackSize == 1) ? 1 : 0); return inv.GetEmptySlots() + num4 >= num; } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] [HarmonyPostfix] private static void Recipe(InventoryGui __instance, Player player) { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = (((Object)(object)__instance.m_craftProgressPanel != (Object)null) ? ((Component)__instance.m_craftProgressPanel).GetComponentInChildren<TMP_Text>() : null); if (!_active || (Object)(object)((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe == (Object)null) { if ((Object)(object)val != (Object)null && val.text == "Recycling...") { val.text = Localization.instance.Localize("$inventory_crafting"); } return; } Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe; ItemData itemData = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).ItemData; if (itemData != null) { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); bool flag = StationOk(recipe, itemData, currentCraftingStation, player); bool flag2 = HaveSpace(((Humanoid)player).GetInventory(), recipe, itemData); CraftingStation requiredStation = recipe.GetRequiredStation(itemData.m_quality); ((Component)__instance.m_minStationLevelIcon).gameObject.SetActive((Object)(object)requiredStation != (Object)null); if ((Object)(object)requiredStation != (Object)null) { int requiredStationLevel = recipe.GetRequiredStationLevel(itemData.m_quality); __instance.m_minStationLevelText.text = requiredStationLevel.ToString(); ((Graphic)__instance.m_minStationLevelText).color = ((!((Object)(object)currentCraftingStation == (Object)null) && currentCraftingStation.GetLevel(true) >= requiredStationLevel) ? __instance.m_minStationLevelBasecolor : ((Mathf.Sin(Time.time * 10f) > 0f) ? Color.red : __instance.m_minStationLevelBasecolor)); } string text = Localization.instance.Localize(itemData.m_shared.m_name); __instance.m_recipeName.text = text + ((recipe.m_amount > 1) ? (" x" + recipe.m_amount) : ""); ((Behaviour)__instance.m_recipeDecription).enabled = false; ((Component)__instance.m_itemCraftType).gameObject.SetActive(true); __instance.m_itemCraftType.text = ((itemData.m_quality <= 1) ? "Item will be recycled" : $"Lowers {text} to quality {itemData.m_quality - 1}"); Requirement[] resources = recipe.m_resources; recipe.m_resources = Returns(recipe).ToArray(); try { __instance.SetupRequirementList(itemData.m_quality, player, true, 1); } finally { recipe.m_resources = resources; } ((Selectable)__instance.m_craftButton).interactable = (flag || player.NoCostCheat()) && flag2; ((Component)__instance.m_craftButton).GetComponentInChildren<TMP_Text>().text = "Recycle"; if ((Object)(object)val != (Object)null) { val.text = "Recycling..."; } UITooltip component = ((Component)__instance.m_craftButton).GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null) { component.m_text = ((!flag2) ? Localization.instance.Localize("$inventory_full") : (flag ? "" : (((Object)(object)currentCraftingStation != (Object)null) ? "Workstation level too low" : Localization.instance.Localize("$msg_missingstation")))); } } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] [HarmonyPostfix] private static void Requirement(Transform elementRoot, Requirement req, int quality) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (_active && req != null) { ((Component)elementRoot).gameObject.SetActive(true); Transform obj = elementRoot.Find("res_amount"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (!((Object)(object)val == (Object)null)) { val.text = Returned(req, quality).ToString(); ((Graphic)val).color = Color.green; } } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] [HarmonyPrefix] private static bool CraftPressed(InventoryGui __instance) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if (!_active || _skipConfirm || !_confirm.Value) { return true; } ItemData itemData = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).ItemData; Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (itemData == null || val == null) { return true; } Temp.Clear(); val.GetBoundItems(Temp); if (!itemData.m_equipped && !Temp.Contains(itemData)) { return true; } PopupButtonCallback val2 = delegate { UnifiedPopup.Pop(); _skipConfirm = true; try { ((UnityEvent)__instance.m_craftButton.onClick).Invoke(); } finally { _skipConfirm = false; } }; object obj = <>O.<0>__Pop; if (obj == null) { PopupButtonCallback val3 = UnifiedPopup.Pop; <>O.<0>__Pop = val3; obj = (object)val3; } UnifiedPopup.Push((PopupBase)new YesNoPopup("Recycle item", "This item is equipped or on your hotbar.\nRecycle it anyway?", val2, (PopupButtonCallback)obj, false, false)); return false; } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] [HarmonyPrefix] private static bool DoCrafting(InventoryGui __instance, Player player) { if (!_active) { return true; } Recycle(__instance, player); return false; } private static void Recycle(InventoryGui gui, Player player) { //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) Recipe craftRecipe = gui.m_craftRecipe; ItemData craftUpgradeItem = gui.m_craftUpgradeItem; Inventory inventory = ((Humanoid)player).GetInventory(); if ((Object)(object)craftRecipe == (Object)null || craftUpgradeItem == null || !inventory.ContainsItem(craftUpgradeItem)) { return; } if (!HaveSpace(inventory, craftRecipe, craftUpgradeItem)) { ((Character)player).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false); return; } int quality = craftUpgradeItem.m_quality; List<KeyValuePair<Requirement, int>> list = (from r in Returns(craftRecipe) select new KeyValuePair<Requirement, int>(r, Returned(r, quality))).ToList(); if (craftUpgradeItem.m_equipped) { ((Humanoid)player).UnequipItem(craftUpgradeItem, true); } if (quality > 1) { if (_keepItem.Value) { craftUpgradeItem.m_quality = quality - 1; craftUpgradeItem.m_durability = Mathf.Min(craftUpgradeItem.m_durability, craftUpgradeItem.GetMaxDurability()); } else { inventory.RemoveItem(craftUpgradeItem); inventory.AddItem(((Object)((Component)craftRecipe.m_item).gameObject).name, craftRecipe.m_amount, quality - 1, craftUpgradeItem.m_variant, player.GetPlayerID(), player.GetPlayerName(), false, false); } } else { inventory.RemoveItem(craftUpgradeItem, craftRecipe.m_amount); } foreach (KeyValuePair<Requirement, int> item in list) { if (item.Value > 0) { ItemDrop resItem = item.Key.m_resItem; inventory.AddItem(((Object)((Component)resItem).gameObject).name, item.Value, resItem.m_itemData.m_quality, resItem.m_itemData.m_variant, 0L, "", false, false); } } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); (((Object)(object)currentCraftingStation != (Object)null) ? currentCraftingStation.m_craftItemDoneEffects : gui.m_craftItemDoneEffects).Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); ((Character)player).Message((MessageType)1, "Recycled " + Localization.instance.Localize(craftUpgradeItem.m_shared.m_name), 0, (Sprite)null, false); gui.UpdateCraftingPanel(true); } } } namespace FermenterTimer { public class Plugin : DigiModule { public const string Guid = "digi.valheim.fermentertimer"; public const string Name = "Digi's Fermenter Timer"; public const string Version = "1.0.2"; private static ConfigEntry<bool> _showYield; private static readonly FieldRef<Fermenter, ZNetView> View = AccessTools.FieldRefAccess<Fermenter, ZNetView>("m_nview"); private static readonly FieldRef<Fermenter, bool> Exposed = AccessTools.FieldRefAccess<Fermenter, bool>("m_exposed"); private static readonly FieldRef<Fermenter, bool> HasRoof = AccessTools.FieldRefAccess<Fermenter, bool>("m_hasRoof"); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.fermentertimer".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { MigrateConfig(); _showYield = base.Config.Bind("General", "Show yield", defaultValue: true, "Also show what the barrel will give, e.g. \"6x Minor healing mead\"."); PatchModule(typeof(Plugin)); } [HarmonyPatch(typeof(Fermenter), "GetHoverText")] [HarmonyPostfix] private static void GetHoverText(Fermenter __instance, ref string __result) { ZNetView val = View.Invoke(__instance); if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)ZNet.instance == (Object)null) { return; } ZDO zDO = val.GetZDO(); int content = zDO.GetInt(ZDOVars.s_content, 0); if (content == 0) { return; } string text = ""; ItemConversion val2 = ((IEnumerable<ItemConversion>)__instance.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion c) => (Object)(object)c.m_from != (Object)null && StringExtensionMethods.GetStableHashCode(((Object)c.m_from).name) == content)); if (_showYield.Value && (Object)(object)val2?.m_to != (Object)null) { text = text + "\n" + Localization.instance.Localize($"{val2.m_producedItems}x {val2.m_to.m_itemData.m_shared.m_name}"); } long num = zDO.GetLong(ZDOVars.s_startTime, 0L); if (num != 0L) { double num2 = (double)__instance.m_fermentationDuration - (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds; if (num2 > 0.0) { text = ((HasRoof.Invoke(__instance) && !Exposed.Invoke(__instance)) ? (text + "\nReady in <color=yellow>" + Format(num2) + "</color>") : (text + "\n<color=orange>Not fermenting: needs a roof, timer restarts</color>")); } } if (!(text == "")) { int num3 = __result.IndexOf('\n'); __result = ((num3 < 0) ? (__result + text) : (__result.Substring(0, num3) + text + __result.Substring(num3))); } } private static string Format(double seconds) { TimeSpan timeSpan = TimeSpan.FromSeconds(Math.Ceiling(seconds)); if (!(timeSpan.TotalHours >= 1.0)) { return $"{timeSpan.Minutes}:{timeSpan.Seconds:00}"; } return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"; } } } namespace CraftStorageSpeedup { public class Plugin : DigiModule { private const int RequirementCheck = 1; private const int RequirementDisplay = 2; private static ConfigEntry<float> _reuseFor; private static MethodInfo _mode; private static MethodInfo _player; private static MethodInfo _station; private static MethodInfo _origin; private static object _chests; private static object _forPlayer; private static object _forStation; private static Vector3 _forOrigin; private static float _until; private void Awake() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_0188: Expected O, but got Unknown _reuseFor = base.Config.Bind("General", "Reuse chest list for (s)", 0.2f, new ConfigDescription("How long the loaded nearby chests are reused for the crafting window's counts. Longer = lighter, but a change in a chest made by another player shows up that much later in the window (your own crafting and taking items are always up to date). 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.LinkedMaterialCounts"); Type type2 = AccessTools.TypeByName("LocalCraftFromStoragePrime.MaterialLinkContext"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "LoadCountingInventories", (Type[])null, (Type[])null) : null); _mode = ((type2 != null) ? AccessTools.PropertyGetter(type2, "Mode") : null); _player = ((type2 != null) ? AccessTools.PropertyGetter(type2, "Player") : null); _station = ((type2 != null) ? AccessTools.PropertyGetter(type2, "Station") : null); _origin = ((type2 != null) ? AccessTools.PropertyGetter(type2, "Origin") : null); if (methodInfo == null || _mode == null || _player == null || _station == null || _origin == null || methodInfo.ReturnType.IsValueType || !typeof(IEnumerable).IsAssignableFrom(methodInfo.ReturnType)) { base.Logger.LogWarning((object)"Local Craft From Storage Prime's chest loading was not found (other version?); nothing is sped up."); return; } base.ModuleHarmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "Reuse", (Type[])null), new HarmonyMethod(typeof(Plugin), "Remember", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); PatchModule(typeof(Plugin)); } private void OnDestroy() { Forget(); } private static void Forget() { _chests = null; _forPlayer = (_forStation = null); } private static bool ForWindow(object context, out object player, out object station, out Vector3 origin) { //IL_0009: 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_0068: Unknown result type (might be due to invalid IL or missing references) player = (station = null); origin = default(Vector3); int num = (int)Convert.ChangeType(_mode.Invoke(context, null), typeof(int)); if (num != 1 && num != 2) { return false; } player = _player.Invoke(context, null); station = _station.Invoke(context, null); origin = (Vector3)_origin.Invoke(context, null); return player != null; } private static bool Reuse(object context, ref object __result) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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) if (_chests == null || _reuseFor.Value <= 0f || Time.unscaledTime > _until) { return true; } if (!ForWindow(context, out var player, out var station, out var origin)) { return true; } if (player == _forPlayer && station == _forStation) { Vector3 val = origin - _forOrigin; if (!(((Vector3)(ref val)).sqrMagnitude > 0.25f)) { __result = _chests; return false; } } return true; } private static void Remember(object context, object __result) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!(_reuseFor.Value <= 0f) && __result != null && __result != _chests && ForWindow(context, out var player, out var station, out var origin)) { _chests = __result; _forPlayer = player; _forStation = station; _forOrigin = origin; _until = Time.unscaledTime + _reuseFor.Value; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] [HarmonyPostfix] private static void Crafted() { Forget(); } [HarmonyPatch(typeof(InventoryGui), "Show")] [HarmonyPrefix] private static void Opened() { Forget(); } [HarmonyPatch(typeof(InventoryGui), "Hide")] [HarmonyPostfix] private static void Closed() { Forget(); } } } namespace CraftBatch { public class Plugin : DigiModule { public const string Guid = "digi.valheim.craftbatch"; public const string Name = "Digi's Craft Batch"; public const string Version = "1.0.3"; private static ConfigEntry<int> _shiftAmount; private static ConfigEntry<int> _bigAmount; private static ConfigEntry<KeyCode> _bigKey; private static readonly FieldRef<InventoryGui, float> CraftTimer = AccessTools.FieldRefAccess<InventoryGui, float>("m_craftTimer"); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.craftbatch".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown MigrateConfig(); _shiftAmount = base.Config.Bind("Crafting", "Shift amount", 5, new ConfigDescription("How many Shift+Craft makes at once (normal 5).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); _bigAmount = base.Config.Bind("Crafting", "Ctrl+Shift amount", 20, new ConfigDescription("How many Ctrl+Shift+Craft makes at once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); _bigKey = base.Config.Bind<KeyCode>("Crafting", "Big batch key", (KeyCode)306, "Hold this key together with Shift when you press Craft to make the big batch (Right Ctrl works too when this is Left Ctrl)."); PatchModule(typeof(Plugin)); } private static bool BigHeld() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 IInputSystem current = UnityInput.Current; if (!current.GetKey(_bigKey.Value)) { if ((int)_bigKey.Value == 306) { return current.GetKey((KeyCode)305); } return false; } return true; } private static void SetAmount(InventoryGui gui) { gui.m_multiCraftAmount = (BigHeld() ? _bigAmount.Value : _shiftAmount.Value); } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] [HarmonyPrefix] private static void UpdateRecipe(InventoryGui __instance) { if (CraftTimer.Invoke(__instance) < 0f) { SetAmount(__instance); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] [HarmonyPrefix] private static void OnCraftPressed(InventoryGui __instance) { SetAmount(__instance); } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] [HarmonyPostfix] private static void FitAmount(Transform elementRoot) { object obj; if (!((Object)(object)elementRoot != (Object)null)) { obj = null; } else { Transform obj2 = elementRoot.Find("res_amount"); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); } TMP_Text val = (TMP_Text)obj; if (!((Object)(object)val == (Object)null) && !val.enableAutoSizing) { val.fontSizeMax = val.fontSize; val.fontSizeMin = val.fontSize * 0.5f; val.textWrappingMode = (TextWrappingModes)0; val.enableAutoSizing = true; } } } } namespace CookingMastery { public class Plugin : DigiModule { private struct Factors { public float Health; public float Stamina; public float Regen; public float Eitr; public float Duration; public bool Any { get { if (Health == 1f && Stamina == 1f && Regen == 1f && Eitr == 1f) { return Duration != 1f; } return true; } } } private sealed class Original { public float Food; public float Stamina; public float Eitr; public float Regen; } [HarmonyPatch(typeof(Player), "EatFood")] private static class EatFoodPatch { private static void Prefix(Player __instance, ItemData item, out bool __state) { __state = false; if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null) { Factors f = Current(); if (f.Any) { __state = true; BoostOne(item.m_shared, f, Rent()); } } } private static void Postfix(Player __instance, ItemData item, bool __result, List<Food> ___m_foods) { if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || item?.m_shared == null) { return; } float duration = Current().Duration; if (duration == 1f) { return; } foreach (Food ___m_food in ___m_foods) { if (___m_food.m_item?.m_shared?.m_name == item.m_shared.m_name && ___m_food.m_time > 0f) { ___m_food.m_time *= duration; } } } private static Exception Finalizer(Exception __exception, bool __state) { RestoreAll(__state); return __exception; } } [HarmonyPatch(typeof(Player), "UpdateFood")] private static class UpdateFoodPatch { private static void Prefix(Player __instance, List<Food> ___m_foods, out bool __state) { __state = false; if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ___m_foods == null || ___m_foods.Count == 0) { return; } Factors f = Current(); if (!f.Any) { return; } __state = true; List<SharedData> owned = Rent(); foreach (Food ___m_food in ___m_foods) { BoostOne(___m_food?.m_item?.m_shared, f, owned); } } private static Exception Finalizer(Exception __exception, bool __state) { RestoreAll(__state); return __exception; } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static class TotalFoodPatch { private static void Postfix(Player __instance, List<Food> ___m_foods, ref float hp, ref float stamina, ref float eitr) { if (!_noDecay.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ___m_foods == null || ___m_foods.Count == 0) { return; } Factors factors = Current(); foreach (Food ___m_food in ___m_foods) { SharedData val = ___m_food?.m_item?.m_shared; if (val != null) { hp += Boost(OriginalFood(val), factors.Health) - ___m_food.m_health; stamina += Boost(OriginalStamina(val), factors.Stamina) - ___m_food.m_stamina; eitr += Boost(OriginalEitr(val), factors.Eitr) - ___m_food.m_eitr; } } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] private static class DoCraftingPatch { private static void Prefix(InventoryGui __instance, Player player, out int __state) { __state = -1; _craftingFood = (_cookingRaised = false); if (_craftTrainsCooking.Value) { ItemData val = CraftRecipe.Invoke(__instance)?.m_item?.m_itemData; if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && IsFood(val?.m_shared)) { _craftingFood = true; __state = ((Humanoid)player).GetInventory().CountItems(val.m_shared.m_name, -1, true); } } } private static void Postfix(InventoryGui __instance, Player player, int __state) { if (_craftingFood && !_cookingRaised && __state >= 0 && !((Object)(object)player == (Object)null)) { ItemData val = CraftRecipe.Invoke(__instance)?.m_item?.m_itemData; if (val != null && ((Humanoid)player).GetInventory().CountItems(val.m_shared.m_name, -1, true) > __state) { ((Character)player).RaiseSkill((SkillType)105, (float)((!MultiCrafting.Invoke(__instance)) ? 1 : __instance.m_multiCraftAmount)); } } } private static Exception Finalizer(Exception __exception) { _craftingFood = (_cookingRaised = false); return __exception; } } [HarmonyPatch(typeof(Player), "RaiseSkill")] private static class RaiseSkillPatch { private static void Prefix(Player __instance, ref SkillType skill, ref float value) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (_craftingFood) { skill = (SkillType)105; _cookingRaised = true; } if ((int)skill == 105) { value *= _xp.Value; if (_logXp.Value) { _log.LogInfo((object)$"[XP] Cooking +{value:0.##} (crafting food: {_craftingFood})"); } } } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] private static class TooltipPatch { private static void Postfix(ItemData item, bool crafting, bool appending, ref string __result) { if (!_tooltips.Value || appending || string.IsNullOrEmpty(__result) || (Object)(object)Player.m_localPlayer == (Object)null) { return; } SharedData val = item?.m_shared; if (!IsFood(val)) { return; } Factors factors = Current(); if (!factors.Any) { return; } __result = Line(__result, HealthLine, OriginalFood(val), factors.Health, crafting, ""); __result = Line(__result, StaminaLine, OriginalStamina(val), factors.Stamina, crafting, ""); __result = Line(__result, EitrLine, OriginalEitr(val), factors.Eitr, crafting, ""); __result = Line(__result, RegenLine, OriginalRegen(val), factors.Regen, crafting, " hp/tick"); if (val.m_foodBurnTime > 0f && factors.Duration != 1f) { float foodBurnTime = val.m_foodBurnTime; string text = (crafting ? (ItemData.GetDurationString(foodBurnTime) + "</color>" + Bonus(ItemData.GetDurationString(foodBurnTime * factors.Duration - foodBurnTime))) : (ItemData.GetDurationString(foodBurnTime * factors.Duration) + "</color>")); __result = DurationLine.Replace(__result, (Match m) => m.Groups[1].Value + text, 1); } } private static string Number(float v) { return v.ToString("0.#", CultureInfo.InvariantCulture); } private static string Bonus(string text) { return " <color=#80ff80ff>+" + text + "</color>"; } private static string Line(string tooltip, Regex line, float value, float factor, bool crafting, string unit) { if (value <= 0f || factor == 1f) { return tooltip; } float num = Boost(value, factor); string text = (crafting ? (Number(value) + unit + "</color>" + Bonus(Number(num - value))) : (Number(num) + unit + "</color>")); return line.Replace(tooltip, (Match m) => m.Groups[1].Value + text, 1); } } public const string Guid = "digi.valheim.cookingmastery"; public const string Name = "Digi's Cooking Mastery"; public const string Version = "1.0.1"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _noDecay; private static ConfigEntry<bool> _craftTrainsCooking; private static ConfigEntry<bool> _tooltips; private static ConfigEntry<bool> _logXp; private static ConfigEntry<float> _health; private static ConfigEntry<float> _stamina; private static ConfigEntry<float> _regen; private static ConfigEntry<float> _eitr; private static ConfigEntry<float> _duration; private static ConfigEntry<float> _xp; private static ManualLogSource _log; private static readonly Dictionary<SharedData, Original> Boosted = new Dictionary<SharedData, Original>(); private static readonly Stack<Original> Pool = new Stack<Original>(); private static readonly List<List<SharedData>> Lists = new List<List<SharedData>>(); private static int _depth; private static readonly FieldRef<InventoryGui, Recipe> CraftRecipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe"); private static readonly FieldRef<InventoryGui, bool> MultiCrafting = AccessTools.FieldRefAccess<InventoryGui, bool>("m_multiCrafting"); private static bool _craftingFood; private static bool _cookingRaised; private static readonly Regex HealthLine = new Regex("(\\$item_food_health: <color=#ff8080ff>)([^<]*)(</color>)", RegexOptions.Compiled); private static readonly Regex StaminaLine = new Regex("(\\$item_food_stamina: <color=#ffff80ff>)([^<]*)(</color>)", RegexOptions.Compiled); private static readonly Regex EitrLine = new Regex("(\\$item_food_eitr: <color=#9090ffff>)([^<]*)(</color>)", RegexOptions.Compiled); private static readonly Regex RegenLine = new Regex("(\\$item_food_regen: <color=orange>)([^<]*)( hp/tick</color>)", RegexOptions.Compiled); private static readonly Regex DurationLine = new Regex("(\\$item_food_duration: <color=orange>)([^<]*)(</color>)", RegexOptions.Compiled); private void MigrateConfig() { string text = Path.Combine(Path