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 Inventory and QoL v1.0.3
DigiQoL.dll
Decompiled an hour agousing 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 AchievementKeeper; using BackpackAtStations; using BackpackStoreNearby; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DigiCore; using HarmonyLib; using KeepRested; using Microsoft.CodeAnalysis; using SafeItemMove; using SkillXp; using StoreNearbyPlus; using TMPro; using UnityEngine; 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: AssemblyCompany("DigiQoL")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DigiQoL")] [assembly: AssemblyTitle("DigiQoL")] [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 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>(); 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) { 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; } 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 DigiQoL { [BepInPlugin("digi.valheim.qol", "Digi's Inventory and QoL", "1.0.3")] [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.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : DigiPlugin { public const string Guid = "digi.valheim.qol"; public const string Name = "Digi's Inventory and QoL"; public const string Version = "1.0.3"; protected override ModuleInfo[] Modules { get; } = new ModuleInfo[7] { new ModuleInfo(typeof(BackpackAtStations.Plugin), "Backpack At Stations", "digi.valheim.backpackatstations", "Open your Adventure Backpack at crafting stations.", new string[1] { "vapok.mods.adventurebackpacks" }), new ModuleInfo(typeof(BackpackStoreNearby.Plugin), "Backpack Store Nearby", "digi.valheim.backpackstorenearby", "Store Nearby also puts matching items from your backpack into nearby chests.", new string[2] { "LuminairPrime.valheim.localcraftfromstorageprime", "vapok.mods.adventurebackpacks" }), new ModuleInfo(typeof(SafeItemMove.Plugin), "Safe Item Move", "digi.valheim.safeitemmove", "Items you drag around your inventory can no longer vanish."), new ModuleInfo(typeof(AchievementKeeper.Plugin), "Achievement Keeper", "digi.valheim.achievementkeeper", "Steam achievements keep working with mods."), new ModuleInfo(typeof(KeepRested.Plugin), "Keep Rested", "digi.valheim.keeprested", "Your Rested buff stays when you log out and back in."), new ModuleInfo(typeof(StoreNearbyPlus.Plugin), "Store Nearby Plus", "digi.valheim.storenearbyplus", "Store Nearby also stores food and potions, never touches extra slots, and a checkbox per chest to skip it.", new string[1] { "LuminairPrime.valheim.localcraftfromstorageprime" }), new ModuleInfo(typeof(SkillXp.Plugin), "Skill XP", "digi.valheim.skillxp", "The Skills window shows each skill's XP, e.g. 24 12/63.") }; } } namespace StoreNearbyPlus { public class Plugin : DigiModule { private const string LcfspGuid = "LuminairPrime.valheim.localcraftfromstorageprime"; private static ConfigEntry<bool> _food; private static ConfigEntry<bool> _potions; private static ConfigEntry<bool> _extraSlots; private static ConfigEntry<bool> _checkbox; private static ConfigEntry<float> _offsetX; private static ConfigEntry<float> _offsetY; private static ConfigEntry<float> _size; private static readonly FieldRef<InventoryGui, Container> CurrentContainer = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); private static RectTransform _box; private static Toggle _toggle; private static bool _updating; private static Sprite _tick; private const string TooltipText = "<color=yellow>Ticked</color> (normal): Store Nearby may put your items into this chest.\n<color=yellow>Unticked</color>: Store Nearby skips this chest.\n\nThe auto stations, crafting and building keep using it as normal."; private void Awake() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown _food = base.Config.Bind("1 - Store Nearby", "Store food", defaultValue: true, "Store Nearby also moves cooked food (stews, pies, cooked meat, ...) into nearby chests that already hold that food. Normally it always keeps food on you. Food on your hotbar (first row) always stays with you."); _potions = base.Config.Bind("1 - Store Nearby", "Store potions", defaultValue: true, "Store Nearby also moves meads and potions into nearby chests that already hold them. Normally it always keeps them on you. Potions on your hotbar (first row) always stay with you."); _extraSlots = base.Config.Bind("1 - Store Nearby", "Leave extra slots alone", defaultValue: true, "Items outside the normal 8 x 4 inventory, in slots that other mods add (equipment slots, quick slots, ...), are never moved by Store Nearby. Turn off only if a mod gives you extra normal inventory rows that you do want emptied."); _checkbox = base.Config.Bind("2 - Chest checkbox", "Show checkbox", defaultValue: true, "Show the Store Nearby checkbox in chest windows, left of Place stacks. Untick it in a chest and your Store Nearby button skips that chest; the auto stations, crafting and building keep using it as normal."); _size = base.Config.Bind("2 - Chest checkbox", "Size", 26f, new ConfigDescription("Size of the checkbox in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 48f), Array.Empty<object>())); _offsetX = base.Config.Bind("2 - Chest checkbox", "Offset X", 0f, new ConfigDescription("Moves the checkbox sideways from its place next to Place stacks, in pixels (+ = right).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-600f, 600f), Array.Empty<object>())); _offsetY = base.Config.Bind("2 - Chest checkbox", "Offset Y", 0f, new ConfigDescription("Moves the checkbox up or down, in pixels (+ = up).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-400f, 400f), Array.Empty<object>())); _checkbox.SettingChanged += OnLayoutChanged; _size.SettingChanged += OnLayoutChanged; _offsetX.SettingChanged += OnLayoutChanged; _offsetY.SettingChanged += OnLayoutChanged; PatchModule(typeof(Plugin)); PatchStoreNearby(); } private void OnDestroy() { _checkbox.SettingChanged -= OnLayoutChanged; _size.SettingChanged -= OnLayoutChanged; _offsetX.SettingChanged -= OnLayoutChanged; _offsetY.SettingChanged -= OnLayoutChanged; if ((Object)(object)_box != (Object)null) { Object.Destroy((Object)(object)((Component)_box).gameObject); } _box = null; _toggle = null; } private static void OnLayoutChanged(object sender, EventArgs e) { if ((Object)(object)InventoryGui.instance != (Object)null) { Refresh(InventoryGui.instance); } } private void PatchStoreNearby() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown if (Chainloader.PluginInfos.TryGetValue("LuminairPrime.valheim.localcraftfromstorageprime", out var value) && !((Object)(object)value.Instance == (Object)null)) { Type type = ((object)value.Instance).GetType().Assembly.GetType("LocalCraftFromStoragePrime.DepositOperation"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Eligible", new Type[1] { typeof(ItemData) }, (Type[])null) : null); MethodInfo methodInfo2 = ((type != null) ? AccessTools.Method(type, "CanUse", new Type[1] { typeof(Container) }, (Type[])null) : null); if (methodInfo == null || methodInfo2 == null) { base.Logger.LogWarning((object)($"Local Craft From Storage Prime {value.Metadata.Version}: Store Nearby's checks were not found, " + "so food, potions and the chest checkbox do nothing there.")); return; } base.ModuleHarmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ItemEligible", (Type[])null) { priority = 600 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.ModuleHarmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ChestUsable", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void ItemEligible(ItemData __0, ref bool __result) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Invalid comparison between Unknown and I4 if (__0?.m_shared == null) { return; } bool flag = __0.m_gridPos.x < 0 || __0.m_gridPos.x >= 8 || __0.m_gridPos.y < 0 || __0.m_gridPos.y >= 4; if (__result) { if (flag && _extraSlots.Value) { __result = false; } } else { if (flag || __0.m_gridPos.y == 0 || __0.m_equipped || __0.m_shared.m_questItem || (int)__0.m_shared.m_itemType != 2 || ((Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).IsItemEquiped(__0))) { return; } bool num; if (!(__0.m_shared.m_food > 0f) && !(__0.m_shared.m_foodStamina > 0f) && !(__0.m_shared.m_foodEitr > 0f)) { if (!_potions.Value) { return; } num = (Object)(object)__0.m_shared.m_consumeStatusEffect != (Object)null; } else { num = _food.Value; } if (num) { __result = true; } } } private static void ChestUsable(Container __0, ref bool __result) { if (__result && ChestFlags.StoreBlocked(__0)) { __result = false; } } [HarmonyPatch(typeof(InventoryGui), "Show")] [HarmonyPostfix] private static void Shown(InventoryGui __instance) { Refresh(__instance); } private static void Refresh(InventoryGui gui) { Container val = CurrentContainer.Invoke(gui); if (!_checkbox.Value || (Object)(object)val == (Object)null || !IsBuiltChest(val)) { if ((Object)(object)_box != (Object)null) { ((Component)_box).gameObject.SetActive(false); } return; } if ((Object)(object)_box == (Object)null) { Create(gui); } if (!((Object)(object)_box == (Object)null)) { ((Component)_box).gameObject.SetActive(true); Layout(gui); _updating = true; _toggle.isOn = !ChestFlags.StoreBlocked(val); _updating = false; } } private static bool IsBuiltChest(Container chest) { if ((Object)(object)((Component)chest).GetComponent<Piece>() != (Object)null && (Object)(object)chest.m_wagon == (Object)null && (Object)(object)((Component)chest).GetComponentInParent<Ship>() == (Object)null) { return (Object)(object)((Component)chest).GetComponent<TombStone>() == (Object)null; } return false; } private static void Changed(bool allowed) { if (_updating || (Object)(object)InventoryGui.instance == (Object)null) { return; } Container val = CurrentContainer.Invoke(InventoryGui.instance); ZNetView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ZNetView>() : null); if (!((Object)(object)val2 == (Object)null) && val2.IsValid()) { if (!val2.IsOwner()) { val2.ClaimOwnership(); } val2.GetZDO().Set("digi_nostore", !allowed); } } private static void Create(InventoryGui gui) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)gui.m_stackAllButton == (Object)null)) { Transform parent = ((Component)gui.m_stackAllButton).transform.parent; GameObject val = new GameObject("DigiStoreNearbyCheckbox", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); _box = (RectTransform)val.transform; GameObject val2 = new GameObject("Background", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent((Transform)(object)_box, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; Vector2 offsetMin = (val3.offsetMax = Vector2.zero); val3.offsetMin = offsetMin; Image val4 = val2.AddComponent<Image>(); Sprite val5 = SlotSprite(gui); if ((Object)(object)val5 != (Object)null) { val4.sprite = val5; val4.type = (Type)1; } ((Graphic)val4).color = new Color(0f, 0f, 0f, 0.5f); GameObject val6 = new GameObject("Checkmark", new Type[1] { typeof(RectTransform) }); val6.transform.SetParent((Transform)(object)val3, false); RectTransform val7 = (RectTransform)val6.transform; val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(-4f, -4f); val7.offsetMax = new Vector2(4f, 4f); Image val8 = val6.AddComponent<Image>(); val8.sprite = TickSprite(); TMP_Text componentInChildren = ((Component)gui.m_stackAllButton).GetComponentInChildren<TMP_Text>(true); ((Graphic)val8).color = (Color)(((Object)(object)componentInChildren != (Object)null) ? ((Graphic)componentInChildren).color : new Color(1f, 0.63f, 0.24f, 1f)); ((Graphic)val8).raycastTarget = false; _toggle = val.AddComponent<Toggle>(); ((Selectable)_toggle).targetGraphic = (Graphic)(object)val4; _toggle.graphic = (Graphic)(object)val8; _toggle.isOn = true; ((UnityEvent<bool>)(object)_toggle.onValueChanged).AddListener((UnityAction<bool>)Changed); UITooltip obj = val.AddComponent<UITooltip>(); InventoryGrid playerGrid = gui.m_playerGrid; object tooltipPrefab; if (playerGrid == null) { tooltipPrefab = null; } else { GameObject elementPrefab = playerGrid.m_elementPrefab; tooltipPrefab = ((elementPrefab == null) ? null : elementPrefab.GetComponent<UITooltip>()?.m_tooltipPrefab); } obj.m_tooltipPrefab = (GameObject)tooltipPrefab; obj.Set("Store Nearby", "<color=yellow>Ticked</color> (normal): Store Nearby may put your items into this chest.\n<color=yellow>Unticked</color>: Store Nearby skips this chest.\n\nThe auto stations, crafting and building keep using it as normal.", (RectTransform)null, default(Vector2)); } } private static void Layout(InventoryGui gui) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0037: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_box == (Object)null) && !((Object)(object)gui.m_stackAllButton == (Object)null)) { RectTransform val = (RectTransform)((Component)gui.m_stackAllButton).transform; RectTransform val2 = (RectTransform)((Transform)_box).parent; Vector3[] array = (Vector3[])(object)new Vector3[4]; val.GetWorldCorners(array); Vector3 val3 = ((Transform)val2).InverseTransformPoint((array[0] + array[1]) * 0.5f); float value = _size.Value; RectTransform box = _box; RectTransform box2 = _box; RectTransform box3 = _box; Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(0.5f, 0.5f); box3.pivot = val4; Vector2 anchorMin = (box2.anchorMax = val4); box.anchorMin = anchorMin; _box.sizeDelta = new Vector2(value, value); ((Transform)_box).localPosition = new Vector3(val3.x - value * 0.5f - 12f + _offsetX.Value, val3.y + _offsetY.Value, 0f); } } private static Sprite SlotSprite(InventoryGui gui) { GameObject val = gui.m_playerGrid?.m_elementPrefab; if ((Object)(object)val == (Object)null) { return null; } Image[] componentsInChildren = val.GetComponentsInChildren<Image>(true); foreach (Image val2 in componentsInChildren) { if ((Object)(object)val2.sprite != (Object)null && ((Object)val2.sprite).name == "item_background") { return val2.sprite; } } return null; } private static Sprite TickSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tick != (Object)null) { return _tick; } Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { name = "DigiStoreTick", filterMode = (FilterMode)1 }; Vector2 a = default(Vector2); ((Vector2)(ref a))..ctor(6f, 16f); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(13f, 8.5f); Vector2 b = default(Vector2); ((Vector2)(ref b))..ctor(26.5f, 25f); Color32[] array = (Color32[])(object)new Color32[1024]; Vector2 p = default(Vector2); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { ((Vector2)(ref p))..ctor((float)j + 0.5f, (float)i + 0.5f); float num = Mathf.Min(SegmentDistance(p, a, val2), SegmentDistance(p, val2, b)); float num2 = Mathf.Clamp01(2.6f - num); array[i * 32 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num2 * 255f)); } } val.SetPixels32(array); val.Apply(); _tick = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f)); return _tick; } private static float SegmentDistance(Vector2 p, Vector2 a, Vector2 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector2 val = b - a; float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / ((Vector2)(ref val)).sqrMagnitude); return Vector2.Distance(p, a + val * num); } } } namespace SkillXp { public class Plugin : DigiModule { public enum Style { Amount, Percent, AmountAndPercent } private static ConfigEntry<Style> _style; private static ConfigEntry<bool> _tooltip; private static readonly FieldRef<SkillsDialog, List<GameObject>> Elements = AccessTools.FieldRefAccess<SkillsDialog, List<GameObject>>("m_elements"); private static readonly Func<Skill, float> NextLevelXp = AccessTools.MethodDelegate<Func<Skill, float>>(AccessTools.Method(typeof(Skill), "GetNextLevelRequirement", (Type[])null, (Type[])null), (object)null, true); private void Awake() { _style = base.Config.Bind("General", "Show", Style.Amount, "What appears after the level in the Skills window. Amount: XP you have / XP the next level needs, e.g. \"24 12/63\". Percent: how far you are to the next level, e.g. \"24 19%\". AmountAndPercent: both."); _tooltip = base.Config.Bind("General", "Details in tooltip", defaultValue: true, "Hovering a skill also shows its XP in full, e.g. \"XP to level 25: 12.3 / 63 (19%)\"."); PatchModule(typeof(Plugin)); } [HarmonyPatch(typeof(SkillsDialog), "Setup")] [HarmonyPostfix] private static void Setup(SkillsDialog __instance, Player player) { if ((Object)(object)player == (Object)null) { return; } List<Skill> skillList = ((Character)player).GetSkills().GetSkillList(); List<GameObject> list = Elements.Invoke(__instance); for (int i = 0; i < skillList.Count && i < list.Count; i++) { Skill val = skillList[i]; GameObject val2 = list[i]; int num = Mathf.FloorToInt(val.m_level); bool flag = val.m_level >= 100f; float accumulator = val.m_accumulator; float num2 = (flag ? 0f : NextLevelXp(val)); float num3 = ((num2 > 0f) ? (Mathf.Clamp01(accumulator / num2) * 100f) : 0f); Transform obj = Utils.FindChild(val2.transform, "leveltext", (IterativeSearchType)0); TMP_Text val3 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if ((Object)(object)val3 != (Object)null) { string arg = (flag ? "max" : ((_style.Value == Style.Percent) ? $"{num3:0}%" : ((_style.Value == Style.AmountAndPercent) ? $"{Amount(accumulator, num2)} ({num3:0}%)" : Amount(accumulator, num2)))); val3.text = $"{num} <size=70%><color=#FFFFFFB3>{arg}</color></size>"; } if (_tooltip.Value) { UITooltip componentInChildren = val2.GetComponentInChildren<UITooltip>(); if (!((Object)(object)componentInChildren == (Object)null)) { string text = (flag ? "Maximum level reached." : $"XP to level {num + 1}: {accumulator:0.#} / {num2:0.#} ({num3:0}%)"); componentInChildren.m_text = val.m_info.m_description + "\n\n<color=yellow>" + text + "</color>"; } } } } private static string Amount(float have, float need) { if (!(need < 10f)) { return $"{Mathf.FloorToInt(have)}/{Mathf.RoundToInt(need)}"; } return $"{have:0.#}/{need:0.#}"; } } } namespace SafeItemMove { public class Plugin : DigiModule { private sealed class Snapshot { public Inventory From; public Inventory To; public readonly Dictionary<string, int> Counts = new Dictionary<string, int>(); public readonly Dictionary<string, ItemData> Samples = new Dictionary<string, ItemData>(); public bool Container; public string Description; } public const string Guid = "digi.valheim.safeitemmove"; public const string Name = "Digi's Safe Item Move"; public const string Version = "1.0.2"; private static ManualLogSource _log; private static readonly FieldRef<InventoryGui, Container> CurrentContainer = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.safeitemmove".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(); _log = base.Logger; PatchModule(typeof(Plugin)); base.Logger.LogInfo((object)"Watching inventory drag and drop for vanished items."); } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] [HarmonyPrefix] [HarmonyPriority(800)] private static void Before(InventoryGrid __instance, Inventory fromInventory, ItemData item, int amount, Vector2i pos, out Snapshot __state) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) __state = null; Inventory val = ((__instance != null) ? __instance.GetInventory() : null); if (val == null || fromInventory == null || item?.m_shared == null) { return; } try { ItemData itemAt = val.GetItemAt(pos.x, pos.y); Snapshot snapshot = new Snapshot { From = fromInventory, To = val }; ItemData[] array = (ItemData[])(object)new ItemData[2] { item, itemAt }; foreach (ItemData val2 in array) { if (val2?.m_shared != null && !snapshot.Counts.ContainsKey(val2.m_shared.m_name)) { snapshot.Counts[val2.m_shared.m_name] = Count(snapshot, val2.m_shared.m_name); snapshot.Samples[val2.m_shared.m_name] = val2.Clone(); } } Container val3 = (((Object)(object)InventoryGui.instance != (Object)null) ? CurrentContainer.Invoke(InventoryGui.instance) : null); Inventory val4 = (((Object)(object)val3 != (Object)null) ? val3.GetInventory() : null); snapshot.Container = val4 != null && (fromInventory == val4 || val == val4); snapshot.Description = $"{item.m_shared.m_name} x{amount} from ({item.m_gridPos.x},{item.m_gridPos.y}) " + $"to ({pos.x},{pos.y})" + ((itemAt != null) ? $" holding {itemAt.m_shared.m_name} x{itemAt.m_stack}" : ""); __state = snapshot; } catch (Exception ex) { _log.LogWarning((object)("Could not snapshot a drop: " + ex.Message)); } } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception After(Exception __exception, Snapshot __state) { if (__state == null) { return __exception; } try { foreach (KeyValuePair<string, int> count in __state.Counts) { int num = count.Value - Count(__state, count.Key); if (num > 0) { string key = count.Key; if (__state.Container) { _log.LogWarning((object)$"{key} x{num} not back yet after moving {__state.Description} (container, left alone)"); continue; } _log.LogWarning((object)$"{key} x{num} vanished while moving {__state.Description}; giving it back."); GiveBack(__state, __state.Samples[count.Key], num); } } } catch (Exception ex) { _log.LogWarning((object)("Could not check a drop: " + ex.Message)); } return __exception; } private static int Count(Snapshot s, string name) { int num = Sum(s.From, name); if (s.To != s.From) { num += Sum(s.To, name); } return num; } private static int Sum(Inventory inventory, string name) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem?.m_shared != null && allItem.m_shared.m_name == name) { num += allItem.m_stack; } } return num; } private static void GiveBack(Snapshot s, ItemData sample, int missing) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) int val = Math.Max(1, sample.m_shared.m_maxStackSize); while (missing > 0) { ItemData val2 = sample.Clone(); val2.m_stack = Math.Min(val, missing); missing -= val2.m_stack; if (!s.From.AddItem(val2) && (s.To == s.From || !s.To.AddItem(val2))) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Vector3 val3 = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up; ItemDrop.DropItem(val2, val2.m_stack, val3, Quaternion.identity); _log.LogWarning((object)$"No room for {val2.m_shared.m_name} x{val2.m_stack}; dropped it at your feet."); } } } } } } namespace KeepRested { public class Plugin : DigiModule { private const string Key = "digi_keeprested"; private static readonly FieldRef<Game, bool> FirstSpawn = AccessTools.FieldRefAccess<Game, bool>("m_firstSpawn"); private static readonly FieldRef<Player, int> ComfortLevel = AccessTools.FieldRefAccess<Player, int>("m_comfortLevel"); private static readonly FieldRef<StatusEffect, float> EffectTime = AccessTools.FieldRefAccess<StatusEffect, float>("m_time"); private void Awake() { PatchModule(typeof(Plugin)); } [HarmonyPatch(typeof(Player), "Save")] [HarmonyPrefix] private static void Save(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { StatusEffect statusEffect = ((Character)__instance).GetSEMan().GetStatusEffect(SEMan.s_statusEffectRested); float num = (((Object)(object)statusEffect != (Object)null) ? statusEffect.GetRemaningTime() : 0f); if (num > 1f && !((Character)__instance).IsDead()) { __instance.m_customData["digi_keeprested"] = string.Join(";", num.ToString("0.#", CultureInfo.InvariantCulture), statusEffect.m_ttl.ToString("0.#", CultureInfo.InvariantCulture), __instance.GetComfortLevel().ToString(CultureInfo.InvariantCulture)); } else { __instance.m_customData.Remove("digi_keeprested"); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] [HarmonyPostfix] private static void Spawned(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)Game.instance == (Object)null || !__instance.m_customData.TryGetValue("digi_keeprested", out var value)) { return; } __instance.m_customData.Remove("digi_keeprested"); if (!FirstSpawn.Invoke(Game.instance)) { return; } string[] array = value.Split(new char[1] { ';' }); if (array.Length >= 3 && float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3) && !(result <= 0f)) { int num = ComfortLevel.Invoke(__instance); ComfortLevel.Invoke(__instance) = result3; StatusEffect val = ((Character)__instance).GetSEMan().AddStatusEffect(SEMan.s_statusEffectRested, true, 0, 0f, (short)(-1)); ComfortLevel.Invoke(__instance) = num; if (!((Object)(object)val == (Object)null)) { val.m_ttl = Math.Max(result2, result); EffectTime.Invoke(val) = val.m_ttl - result; } } } } } namespace BackpackStoreNearby { public class Plugin : DigiModule { public const string Guid = "digi.valheim.backpackstorenearby"; public const string Name = "Digi's Backpack Store Nearby"; public const string Version = "1.0.2"; private const string LcfspGuid = "LuminairPrime.valheim.localcraftfromstorageprime"; private const string AdventureBackpacksGuid = "vapok.mods.adventurebackpacks"; private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static ManualLogSource _log; private static Plugin _instance; private static FieldInfo _backpackIsOpen; private static FieldInfo _depositButton; private static MethodInfo _refreshAvailability; private static MethodInfo _refreshOperationState; private static PropertyInfo _busy; private static MethodInfo _start; private static Type _depositOperation; private static FieldInfo _source; private static FieldInfo _policy; private static FieldInfo _identities; private static MethodInfo _decide; private static PropertyInfo _settings; private static PropertyInfo _settingsEnabled; private static PropertyInfo _settingsDepositEnabled; private static readonly ConditionalWeakTable<object, object> BackpackOperations = new ConditionalWeakTable<object, object>(); private static readonly FieldRef<InventoryGui, Container> CurrentContainerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); private static bool Busy => (bool)_busy.GetValue(null); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.backpackstorenearby".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; _log = base.Logger; try { Resolve(); Patch(base.ModuleHarmony); _log.LogInfo((object)"Store Nearby button enabled for open backpacks."); } catch (Exception ex) { _log.LogError((object)("Disabled, could not hook into Local Craft From Storage Prime / Adventure Backpacks: " + ex)); } } private static void Resolve() { _backpackIsOpen = Require(AccessTools.Field(Require(AccessTools.TypeByName("AdventureBackpacks.Patches.InventoryGuiPatches")), "BackpackIsOpen")); Type type = Require(AccessTools.TypeByName("LocalCraftFromStoragePrime.DepositButton")); _depositButton = Require(AccessTools.Field(type, "_button")); _refreshAvailability = Require(AccessTools.Method(type, "RefreshAvailability", (Type[])null, (Type[])null)); _refreshOperationState = Require(AccessTools.Method(type, "RefreshOperationState", (Type[])null, (Type[])null)); Type type2 = Require(AccessTools.TypeByName("LocalCraftFromStoragePrime.ContainerAccessService")); _busy = Require(AccessTools.Property(type2, "Busy")); _start = Require(AccessTools.Method(type2, "Start", (Type[])null, (Type[])null)); _depositOperation = Require(AccessTools.TypeByName("LocalCraftFromStoragePrime.DepositOperation")); _source = Require(AccessTools.Field(_depositOperation, "_source")); _policy = Require(AccessTools.Field(_depositOperation, "_policy")); _identities = Require(AccessTools.Field(_depositOperation, "_identities")); _decide = Require(AccessTools.Method(AccessTools.TypeByName("LocalCraftFromStoragePrime.DepositPolicy"), "Decide", (Type[])null, (Type[])null)); _settings = Require(AccessTools.Property(Require(AccessTools.TypeByName("LocalCraftFromStoragePrime.Plugin")), "Settings")); Type type3 = Require(AccessTools.TypeByName("LocalCraftFromStoragePrime.RuntimeSettings")); _settingsEnabled = Require(AccessTools.Property(type3, "Enabled")); _settingsDepositEnabled = Require(AccessTools.Property(type3, "DepositEnabled")); } private static T Require<T>(T member) where T : class { if (member == null) { throw new MissingMemberException("A member of a dependency was not found (" + typeof(T).Name + ")."); } return member; } private static void Patch(Harmony harmony) { Type type = AccessTools.TypeByName("LocalCraftFromStoragePrime.DepositButton"); Type type2 = AccessTools.TypeByName("LocalCraftFromStoragePrime.DepositAction"); harmony.Patch((MethodBase)AccessTools.Method(type, "RefreshVisibility", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("RefreshVisibilityPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(type2, "Run", (Type[])null, (Type[])null), Hook("RunPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(_depositOperation, "InvalidReason", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("InvalidReasonPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(_depositOperation, "CanUse", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("CanUsePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(_depositOperation, "Eligible", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("EligiblePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } 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.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); } private static bool BackpackOpen() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null && (Object)(object)CurrentContainer(instance) != (Object)null) { return (bool)_backpackIsOpen.GetValue(null); } return false; } private static bool DepositAllowed() { object value = _settings.GetValue(null); if (value != null && (bool)_settingsEnabled.GetValue(value)) { return (bool)_settingsDepositEnabled.GetValue(value); } return false; } private static Container CurrentContainer(InventoryGui gui) { if (!((Object)(object)gui == (Object)null)) { return CurrentContainerRef.Invoke(gui); } return null; } private static void RefreshVisibilityPostfix(InventoryGui gui) { object? value = _depositButton.GetValue(null); Button val = (Button)((value is Button) ? value : null); gui = gui ?? InventoryGui.instance; if ((Object)(object)val == (Object)null || (Object)(object)gui == (Object)null) { return; } RectTransform component = ((Component)val).GetComponent<RectTransform>(); if (BackpackOpen() && DepositAllowed()) { if ((Object)(object)((Transform)component).parent != (Object)(object)gui.m_container) { ((Transform)component).SetParent((Transform)(object)gui.m_container, false); } ((Component)val).gameObject.SetActive(true); _refreshAvailability.Invoke(null, new object[1] { gui }); } else if ((Object)(object)((Transform)component).parent != (Object)(object)gui.m_player) { ((Transform)component).SetParent((Transform)(object)gui.m_player, false); } } private static bool RunPrefix() { if (!BackpackOpen()) { return true; } if (!Busy && DepositAllowed()) { ((MonoBehaviour)_instance).StartCoroutine(StoreBoth()); } return false; } private static IEnumerator StoreBoth() { Player player = Player.m_localPlayer; InventoryGui gui = InventoryGui.instance; if (!CanStart(player, gui)) { yield break; } Start(NewOperation(player, gui, null)); yield return null; while (Busy) { yield return null; } if (CanStart(player, gui) && BackpackOpen()) { Inventory inventory = CurrentContainer(gui).GetInventory(); if (inventory != null) { Start(NewOperation(player, gui, inventory)); } } } private static bool CanStart(Player player, InventoryGui gui) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)gui != (Object)null && InventoryGui.IsVisible() && !((Character)player).IsDead() && !((Character)player).IsTeleporting()) { return !Busy; } return false; } private static object NewOperation(Player player, InventoryGui gui, Inventory source) { object obj = Activator.CreateInstance(_depositOperation, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new object[2] { player, gui }, null); if (source != null) { _source.SetValue(obj, source); BackpackOperations.Add(obj, null); } return obj; } private static void Start(object operation) { _start.Invoke(null, new object[1] { operation }); _refreshOperationState.Invoke(null, null); } private static void InvalidReasonPostfix(ref string __result) { if (__result == "cancelled" && InventoryGui.IsVisible() && BackpackOpen()) { __result = null; } } private static void CanUsePostfix(Container chest, ref bool __result) { if (__result && (Object)(object)chest != (Object)null && BackpackOpen() && (Object)(object)chest == (Object)(object)CurrentContainer(InventoryGui.instance)) { __result = false; } } private static void EligiblePostfix(object __instance, ItemData item, ref bool __result) { if (!__result && item?.m_shared != null && item.m_gridPos.y == 0 && !item.m_equipped && !item.m_shared.m_questItem && BackpackOperations.TryGetValue(__instance, out var _)) { object value2 = _policy.GetValue(__instance); IDictionary dictionary = _identities.GetValue(__instance) as IDictionary; if (value2 != null && dictionary != null && dictionary.Contains(item)) { object obj = _decide.Invoke(value2, new object[3] { dictionary[item], false, null }); object value3 = obj.GetType().GetField("Item1").GetValue(obj); __result = Convert.ToInt32(value3) == 0; } } } } } namespace BackpackAtStations { public class Plugin : DigiModule { [HarmonyPatch(typeof(InventoryGui), "Show", new Type[] { typeof(Container), typeof(int) })] private static class ShowPatch { private static void Postfix(InventoryGui __instance, Container container, int activeGroup) { if (_opening || (Object)(object)container != (Object)null || activeGroup != 3) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.GetCurrentCraftingStation() == (Object)null || !_openWithInventory.Value || (bool)_backpackIsOpen.GetValue(null) || !(bool)_canOpenBackpack.Invoke(null, new object[1] { localPlayer })) { return; } _opening = true; try { _openBackpack.Invoke(null, new object[2] { localPlayer, __instance }); _setActiveGroup?.Invoke(__instance, new object[2] { activeGroup, false }); } catch (Exception ex) { Logger.CreateLogSource("Digi's Backpack At Stations").LogWarning((object)("Could not open backpack at station: " + ex.Message)); } finally { _opening = false; } } } public const string Guid = "digi.valheim.backpackatstations"; public const string Name = "Digi's Backpack At Stations"; public const string Version = "1.0.3"; private const string AdventureBackpacksGuid = "vapok.mods.adventurebackpacks"; private static FieldInfo _backpackIsOpen; private static MethodInfo _openBackpack; private static MethodInfo _canOpenBackpack; private static ConfigEntry<bool> _openWithInventory; private static MethodInfo _setActiveGroup; private static bool _opening; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.backpackatstations".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(); _backpackIsOpen = AccessTools.Field(AccessTools.TypeByName("AdventureBackpacks.Patches.InventoryGuiPatches"), "BackpackIsOpen"); Type type = AccessTools.TypeByName("AdventureBackpacks.Extensions.PlayerExtensions"); _openBackpack = AccessTools.Method(type, "OpenBackpack", new Type[2] { typeof(Player), typeof(InventoryGui) }, (Type[])null); _canOpenBackpack = AccessTools.Method(type, "CanOpenBackpack", new Type[1] { typeof(Player) }, (Type[])null); _openWithInventory = AccessTools.Field(AccessTools.TypeByName("AdventureBackpacks.Configuration.ConfigRegistry"), "OpenWithInventory")?.GetValue(null) as ConfigEntry<bool>; _setActiveGroup = AccessTools.Method(typeof(InventoryGui), "SetActiveGroup", new Type[2] { typeof(int), typeof(bool) }, (Type[])null); if (_backpackIsOpen == null || _openBackpack == null || _canOpenBackpack == null || _openWithInventory == null) { base.Logger.LogError((object)"Disabled, Adventure Backpacks' backpack functions were not found."); return; } base.ModuleHarmony.PatchAll(typeof(ShowPatch)); base.Logger.LogInfo((object)"Backpack now also opens at crafting stations."); } } } namespace AchievementKeeper { public class Plugin : DigiModule { public const string Guid = "digi.valheim.achievementkeeper"; public const string Name = "Digi's Achievement Keeper"; public const string Version = "1.0.1"; private static ConfigEntry<bool> _modded; private static ConfigEntry<bool> _cheats; private static ConfigEntry<bool> _items; private static ConfigEntry<bool> _hideStats; private static readonly List<string> ToRemove = new List<string>(); private static readonly FieldRef<Dictionary<string, ConsoleCommand>> Commands = AccessTools.StaticFieldRefAccess<Dictionary<string, ConsoleCommand>>(AccessTools.Field(typeof(Terminal), "commands")); private float _timer; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.achievementkeeper".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(); _modded = base.Config.Bind("General", "Allow in modded games", defaultValue: true, "Steam achievements can unlock while you play with mods (Valheim normally blocks them in modded games)."); _cheats = base.Config.Bind("General", "Ignore cheat flags", defaultValue: true, "Achievements and stats keep counting even after console cheats (devcommands), on worlds with cheat world modifiers, or with spawned items."); _items = base.Config.Bind("General", "Clear cheated mark on items", defaultValue: true, "Items made with cheats (spawned, or dropped by spawned creatures) lose their hidden \"cheated\" mark."); _hideStats = base.Config.Bind("General", "Hide cheat stats", defaultValue: false, "Your character's stats page shows 0 cheats and no cheat commands."); PatchModule(typeof(Plugin)); } [HarmonyPatch(typeof(Achievements), "IsCheatedAtAll")] [HarmonyPrefix] private static bool IsCheatedAtAll(ref bool __result) { if (_cheats.Value && _modded.Value) { __result = false; return false; } if (!_cheats.Value && !_modded.Value) { return true; } bool flag = !_cheats.Value && (((Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile().m_usedCheats) || Achievements.IsWorldCheated() || ((Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory().AnyCheatedItem())); __result = flag || (!_modded.Value && Game.isModded); return false; } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void BypassCheatChecks(ref bool __result) { if (_cheats.Value) { __result = true; } } [HarmonyPatch(typeof(PlayerProfile), "IncrementStat")] [HarmonyPrefix] private static bool IncrementStat(PlayerStatType stat) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (_hideStats.Value) { return (int)stat != 4; } return true; } private void Update() { _timer += Time.unscaledDeltaTime; if (_timer < 3f) { return; } _timer = 0f; PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val == null) { return; } if (_cheats.Value) { val.m_usedCheats = false; } if (_hideStats.Value) { ClearCheatStats(val); } if (!_items.Value || !((Object)(object)Player.m_localPlayer != (Object)null)) { return; } foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()) { if (allItem.m_cheated) { allItem.m_cheated = false; } } } private static void ClearCheatStats(PlayerProfile profile) { Dictionary<string, ConsoleCommand> dictionary = Commands.Invoke(); PlayerStats[] playerStats = profile.m_playerStats; foreach (PlayerStats val in playerStats) { if (val == null) { continue; } if (val.m_stats != null) { val.m_stats[(PlayerStatType)4] = 0f; } Dictionary<string, float> knownCommands = val.m_knownCommands; if (knownCommands == null || knownCommands.Count == 0) { continue; } ToRemove.Clear(); foreach (string key in knownCommands.Keys) { if (key == "devcommands" || (dictionary != null && dictionary.TryGetValue(key, out var value) && value != null && value.IsCheat)) { ToRemove.Add(key); } } foreach (string item in ToRemove) { knownCommands.Remove(item); } } } } }