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 CraftMore v1.0.1
BepInEx/plugins/CraftMore/CraftMore.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Kriona.Shared; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Kriona")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 Kriona Hagen - MIT License")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("Craft More")] [assembly: AssemblyTitle("Craft More")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.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; } } } namespace CraftMore { internal enum CraftMode { Count, Stack, All } internal class CraftKey { public readonly KeyCode Key; public readonly CraftMode Mode; public readonly int Count; public CraftKey(KeyCode key, CraftMode mode, int count) { //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) Key = key; Mode = mode; Count = count; } } internal static class CraftConfig { private const string FileName = "kriona.CraftMore.cfg"; private const string SettingsSection = "1 - Crafting"; private const string KeysSection = "2 - Keys"; private const string Comment = "##"; private const string StackValue = "Stack"; private const string AllValue = "All"; private const string ShowHintsKey = "Show Hints"; private const string FullExperienceKey = "Full Experience"; private const string OneAtATimeKey = "One At A Time"; public const int MaxCount = 10000; private static readonly string[] defaultContents = new string[25] { "## Craft More", "## Changes take effect as soon as this file is saved; you don't need to restart Valheim or rejoin the world", "", "Show Hints = true ## Lists the keys and how many each crafts under the Craft button, true or false", "", "## The game raises a crafting skill by at most one level per batch, however many crafts are in it, and throws", "## away the rest of the experience - with Full Experience on, a batch gives the experience of crafting each one", "## separately, so a large batch can raise the skill several levels", "## It isn't needed when One At A Time is true, since each craft then gives its own experience", "Full Experience = true ## true or false", "", "## With One At A Time on, a batch crafts its items one after another, each going into your inventory as it's done,", "## instead of all of them at the end - the batch takes as long either way, drops what you can't carry on the", "## ground, and stops early when it's canceled or you run out of materials", "One At A Time = true ## true or false", "", "## Hold a key while pressing Craft to craft more than one at a time. Each line is: Key = Stack/All/number", "## Key is a Unity KeyCode name, e.g. LeftControl, LeftAlt or LeftShift - either side of Ctrl, Shift, Alt and Command counts", "## Stack crafts as many as fit in one inventory slot", "## All crafts as many as you have the materials for, dropping what you can't carry on the ground", "## A number crafts that many, up to " + 10000, "## When more than one key is held, the first line wins", "", "LeftControl = Stack", "LeftAlt = All" }; public static readonly List<CraftKey> Keys = new List<CraftKey>(); private static ConfigMirror mirror; private static ConfigEntry<bool> showHintsEntry; private static ConfigEntry<bool> fullExperienceEntry; private static ConfigEntry<bool> oneAtATimeEntry; private static readonly KeyValuePair<KeyCode, string>[] defaultKeys = new KeyValuePair<KeyCode, string>[2] { new KeyValuePair<KeyCode, string>((KeyCode)306, "Stack"), new KeyValuePair<KeyCode, string>((KeyCode)308, "All") }; private static readonly List<KeyValuePair<KeyCode, string>> keyLines = new List<KeyValuePair<KeyCode, string>>(); private static readonly List<ConfigEntry<KeyCode>> keyEntries = new List<ConfigEntry<KeyCode>>(); private static readonly List<ConfigEntry<string>> craftsEntries = new List<ConfigEntry<string>>(); public static bool ShowHints { get; private set; } = true; public static bool FullExperience { get; private set; } = true; public static bool OneAtATime { get; private set; } = true; public static string FilePath => Path.Combine(Paths.ConfigPath, "kriona.CraftMore.cfg"); public static void Init(ConfigFile file) { mirror = new ConfigMirror(file, OnEntryChanged); showHintsEntry = mirror.Bind("1 - Crafting", "Show Hints", defaultValue: true, "List the keys and how many each crafts under the Craft button", null, 3, null); fullExperienceEntry = mirror.Bind("1 - Crafting", "Full Experience", defaultValue: true, "A batch gives the experience of crafting each one separately, so it can raise the skill several levels", null, 2, null); oneAtATimeEntry = mirror.Bind("1 - Crafting", "One At A Time", defaultValue: true, "A batch crafts its items one after another, each going into your inventory as it's done, instead of all of them at the end", null, 1, null); } private static void SyncEntries() { if (mirror == null) { return; } mirror.Sync(delegate { //IL_005d: Unknown result type (might be due to invalid IL or missing references) showHintsEntry.Value = ShowHints; fullExperienceEntry.Value = FullExperience; oneAtATimeEntry.Value = OneAtATime; for (int i = 0; i < keyLines.Count; i++) { if (i == keyEntries.Count) { BindKeySlot(i); } keyEntries[i].Value = keyLines[i].Key; craftsEntries[i].Value = keyLines[i].Value; } while (keyEntries.Count > keyLines.Count) { int index = keyEntries.Count - 1; mirror.Remove((ConfigEntryBase)(object)keyEntries[index]); mirror.Remove((ConfigEntryBase)(object)craftsEntries[index]); keyEntries.RemoveAt(index); craftsEntries.RemoveAt(index); } }); } private static void BindKeySlot(int slot) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) KeyValuePair<KeyCode, string> keyValuePair = ((slot < defaultKeys.Length) ? defaultKeys[slot] : keyLines[slot]); string text = "Key " + (slot + 1); int num = 1000 - slot * 2; keyEntries.Add(mirror.Bind<KeyCode>("2 - Keys", text, keyValuePair.Key, "The key to hold while pressing Craft - either side of Ctrl, Shift, Alt and Command counts. When more than one key is held, the first slot wins", null, num, null)); craftsEntries.Add(mirror.Bind("2 - Keys", text + " Crafts", keyValuePair.Value, "Stack crafts as many as fit in one inventory slot, All crafts as many as you have the materials for, and a number crafts that many, up to " + 10000, null, num - 1, null)); } public static void Load() { string filePath = FilePath; string[] lines = defaultContents; try { if (File.Exists(filePath)) { lines = File.ReadAllLines(filePath); } else { File.WriteAllText(filePath, string.Join("\r\n", defaultContents) + "\r\n"); } } catch (IOException ex) { Debug.LogWarning((object)("[Craft More] Couldn't read " + filePath + ", using the default keys: " + ex.Message)); } Parse(lines); SyncEntries(); } private static void OnEntryChanged() { string filePath = FilePath; string[] array = defaultContents; try { if (File.Exists(filePath)) { array = File.ReadAllLines(filePath); } List<string> list = new List<string>(array); SetLine(list, "Show Hints", showHintsEntry.Value ? "true" : "false"); SetLine(list, "Full Experience", fullExperienceEntry.Value ? "true" : "false"); SetLine(list, "One At A Time", oneAtATimeEntry.Value ? "true" : "false"); SetKeyLines(list); array = list.ToArray(); File.WriteAllText(filePath, string.Join("\r\n", array) + "\r\n"); } catch (IOException ex) { Debug.LogWarning((object)("[Craft More] Couldn't write " + filePath + ": " + ex.Message)); } Parse(array); SyncEntries(); Plugin.Watcher?.Remember(); } private static void SetLine(List<string> lines, string key, string valueText) { bool flag = false; for (int i = 0; i < lines.Count; i++) { if (SplitLine(lines[i], out var keyText, out var _, out var comment) && string.Equals(keyText, key, StringComparison.OrdinalIgnoreCase)) { lines[i] = JoinLine(keyText, valueText, comment); flag = true; } } if (!flag) { lines.Add(key + " = " + valueText); } } private unsafe static void SetKeyLines(List<string> lines) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) int num = 0; for (int i = 0; i < lines.Count; i++) { if (num >= keyEntries.Count) { break; } if (SplitLine(lines[i], out var keyText, out var _, out var comment) && TryParseKey(keyText, out var _)) { KeyCode value = keyEntries[num].Value; lines[i] = JoinLine(((int)value == 0) ? keyText : ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString(), craftsEntries[num].Value.Trim(), comment); num++; } } } private static bool SplitLine(string line, out string keyText, out string valueText, out string comment) { string text = line; comment = ""; int num = text.IndexOf("##", StringComparison.Ordinal); if (num >= 0) { comment = text.Substring(num); text = text.Substring(0, num); } int num2 = text.IndexOf('='); keyText = ((num2 > 0) ? text.Substring(0, num2).Trim() : ""); valueText = ((num2 > 0) ? text.Substring(num2 + 1).Trim() : ""); return keyText.Length > 0; } private static string JoinLine(string keyText, string valueText, string comment) { return keyText + " = " + valueText + ((comment.Length > 0) ? (" " + comment) : ""); } private static bool TryParseKey(string keyText, out KeyCode key) { key = (KeyCode)0; if (keyText.Length > 0 && char.IsLetter(keyText[0]) && Enum.TryParse<KeyCode>(keyText, ignoreCase: true, out key)) { return (int)key != 0; } return false; } private static void Parse(string[] lines) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) Keys.Clear(); keyLines.Clear(); ShowHints = true; FullExperience = true; OneAtATime = true; foreach (string text in lines) { string text2 = text; int num = text2.IndexOf("##", StringComparison.Ordinal); if (num >= 0) { text2 = text2.Substring(0, num); } if (text2.Trim().Length == 0) { continue; } int num2 = text2.IndexOf('='); if (num2 <= 0) { Warn(text); continue; } string text3 = text2.Substring(0, num2).Trim(); string text4 = text2.Substring(num2 + 1).Trim(); if (string.Equals(text3, "Show Hints", StringComparison.OrdinalIgnoreCase)) { ShowHints = ParseBool(text, text4, ShowHints); continue; } if (string.Equals(text3, "Full Experience", StringComparison.OrdinalIgnoreCase)) { FullExperience = ParseBool(text, text4, FullExperience); continue; } if (string.Equals(text3, "One At A Time", StringComparison.OrdinalIgnoreCase)) { OneAtATime = ParseBool(text, text4, OneAtATime); continue; } if (!TryParseKey(text3, out var key)) { Warn(text); continue; } keyLines.Add(new KeyValuePair<KeyCode, string>(key, text4)); int result; if (string.Equals(text4, "Stack", StringComparison.OrdinalIgnoreCase)) { Keys.Add(new CraftKey(key, CraftMode.Stack, 0)); } else if (string.Equals(text4, "All", StringComparison.OrdinalIgnoreCase)) { Keys.Add(new CraftKey(key, CraftMode.All, 0)); } else if (int.TryParse(text4, out result) && result >= 1) { Keys.Add(new CraftKey(key, CraftMode.Count, Mathf.Min(result, 10000))); } else { Warn(text); } } } private static bool ParseBool(string line, string valueText, bool fallback) { if (bool.TryParse(valueText, out var result)) { return result; } Warn(line); return fallback; } private static void Warn(string line) { Debug.LogWarning((object)("[Craft More] Couldn't read \"" + line.Trim() + "\" in kriona.CraftMore.cfg, skipping it")); } } internal static class CraftHint { private const float FontScale = 0.55f; private const string Separator = " "; private const float Gap = 4f; private const string GameButton = "AltPlace"; private static TextMeshProUGUI label; private static readonly StringBuilder builder = new StringBuilder(); public static void Update(InventoryGui gui, Player player) { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe; if (!CraftConfig.ShowHints || !((Object)(object)recipe != (Object)null) || ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData != null || CraftConfig.Keys.Count <= 0 || ZInput.IsGamepadActive()) { if ((Object)(object)label != (Object)null) { ((Component)label).gameObject.SetActive(false); } return; } if ((Object)(object)label == (Object)null) { label = CreateLabel(gui); if ((Object)(object)label == (Object)null) { return; } } builder.Clear(); string gameKeyName = GetGameKeyName(); int num; if (gameKeyName != null) { num = ((Craft.DefaultAmount > 0) ? 1 : 0); if (num != 0) { int value = Craft.DefaultAmount * Mathf.Max(recipe.m_amount, 1); builder.Append(gameKeyName).Append(": x").Append(Craft.DefaultAmount) .Append(" (") .Append(value) .Append(')'); } } else { num = 0; } int num2 = ((num != 0) ? Craft.DefaultAmount : (-1)); int num3 = -1; foreach (CraftKey key in CraftConfig.Keys) { if (key.Mode == CraftMode.Stack) { num3 = Craft.GetStackAmount(recipe); break; } } foreach (CraftKey key2 in CraftConfig.Keys) { int craftCount = Craft.GetCraftCount(key2, recipe, player); if ((key2.Mode != CraftMode.Stack || (craftCount != num2 && craftCount > 1)) && (key2.Mode != CraftMode.All || (craftCount != num2 && craftCount != num3))) { if (builder.Length > 0) { builder.Append(" "); } int value2 = craftCount * Mathf.Max(recipe.m_amount, 1); builder.Append(GetKeyName(key2.Key)).Append(": ").Append(GetModeName(key2)) .Append(" (") .Append(value2) .Append(')'); } } string text = builder.ToString(); if (((TMP_Text)label).text != text) { ((TMP_Text)label).text = text; } ((Component)label).gameObject.SetActive(true); } private static TextMeshProUGUI CreateLabel(InventoryGui gui) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren == (Object)null) { return null; } GameObject val = new GameObject("CraftMoreHint", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)gui.m_craftButton).transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(0.5f, 1f); val2.anchoredPosition = new Vector2(0f, -4f); val2.sizeDelta = new Vector2(0f, 30f); TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).font = componentInChildren.font; ((TMP_Text)obj).fontSharedMaterial = componentInChildren.fontSharedMaterial; ((TMP_Text)obj).fontSize = (componentInChildren.enableAutoSizing ? componentInChildren.fontSizeMax : componentInChildren.fontSize) * 0.55f; ((Graphic)obj).color = Color.white; ((TMP_Text)obj).alignment = (TextAlignmentOptions)258; ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj).overflowMode = (TextOverflowModes)0; ((Graphic)obj).raycastTarget = false; return obj; } private static string GetGameKeyName() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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) ButtonDef val = ((ZInput.instance != null) ? ZInput.instance.GetButtonDef("AltPlace") : null); if (val == null || (int)val.Source == 180 || val.ButtonAction.bindings.Count == 0) { return null; } InputBinding val2 = val.ButtonAction.bindings[0]; string effectivePath = ((InputBinding)(ref val2)).effectivePath; if (string.IsNullOrEmpty(effectivePath)) { return null; } string text = NormalizeKey(effectivePath.Substring(effectivePath.LastIndexOf('/') + 1)); foreach (CraftKey key in CraftConfig.Keys) { if (NormalizeKey(((object)key.Key/*cast due to .constrained prefix*/).ToString()) == text) { return null; } } return text switch { "control" => "Ctrl", "shift" => "Shift", "alt" => "Alt", "command" => "Cmd", _ => Localization.instance.Localize(ZInput.instance.GetBoundKeyString("AltPlace", true)), }; } private static string NormalizeKey(string name) { string text = name.ToLowerInvariant().Replace("left", "").Replace("right", ""); if (!(text == "ctrl")) { if (text == "meta") { return "command"; } return text; } return "control"; } private static string GetModeName(CraftKey craftKey) { return craftKey.Mode switch { CraftMode.Stack => "Stack", CraftMode.All => "All", _ => "x" + craftKey.Count, }; } private unsafe static string GetKeyName(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown switch (key - 303) { case 2: case 3: return "Ctrl"; case 0: case 1: return "Shift"; case 4: case 5: return "Alt"; case 6: case 7: return "Cmd"; default: return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); } } } internal static class Craft { private static int defaultAmount = -1; private static float defaultDuration; private static bool dropExtra; private static int queuedCrafts; private static int batchCrafts; private static readonly FieldRef<GuiBar, float> barMaxValue = AccessTools.FieldRefAccess<GuiBar, float>("m_maxValue"); private static readonly FieldRef<GuiBar, float> barSmoothValue = AccessTools.FieldRefAccess<GuiBar, float>("m_smoothValue"); public static int DefaultAmount => defaultAmount; public static void QueueBatch(InventoryGui gui) { queuedCrafts = 0; batchCrafts = 0; if (CraftConfig.OneAtATime && gui.m_craftTimer == 0f && gui.m_multiCrafting && gui.m_multiCraftAmount > 1) { batchCrafts = gui.m_multiCraftAmount; queuedCrafts = batchCrafts - 1; gui.m_multiCraftAmount = 1; gui.m_multiCraftDuration = GetSingleDuration(); } } public static void ContinueBatch(InventoryGui gui, Player player) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if (queuedCrafts <= 0 || gui.m_craftTimer >= 0f) { return; } Recipe craftRecipe = gui.m_craftRecipe; if ((Object)(object)craftRecipe == (Object)null) { queuedCrafts = 0; return; } if (!player.NoCostCheat() && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)25) && !player.HaveRequirements(craftRecipe, false, 1, 1)) { queuedCrafts = 0; return; } queuedCrafts--; dropExtra = true; gui.m_multiCrafting = true; gui.m_multiCraftAmount = 1; gui.m_multiCraftDuration = GetSingleDuration(); gui.m_craftTimer = 0f; CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)craftRecipe.m_craftingStation != (Object)null) { if ((Object)(object)currentCraftingStation != (Object)null) { currentCraftingStation.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } else { gui.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } public static void StopBatch() { queuedCrafts = 0; batchCrafts = 0; } public static void ShowBatchProgress(InventoryGui gui) { if (gui.m_craftTimer < 0f) { batchCrafts = 0; } else if (batchCrafts > 1) { GuiBar craftProgressBar = gui.m_craftProgressBar; float num = barMaxValue.Invoke(craftProgressBar); int num2 = batchCrafts - queuedCrafts - 1; float num3 = num * (float)batchCrafts; float num4 = Mathf.Min(num * (float)num2 + gui.m_craftTimer, num3); barMaxValue.Invoke(craftProgressBar) = num3; craftProgressBar.SetValue(num4); barSmoothValue.Invoke(craftProgressBar) = Mathf.Clamp01(num4 / num3); } } private static float GetSingleDuration() { return defaultDuration / (float)Mathf.Max(defaultAmount, 1); } private static bool IsKeyHeld(KeyCode key) { //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_0006: 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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) KeyCode otherSide = GetOtherSide(key); if (!ZInput.GetKey(key, false)) { if ((int)otherSide != 0) { return ZInput.GetKey(otherSide, false); } return false; } return true; } private static KeyCode GetOtherSide(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown return (KeyCode)((key - 303) switch { 3 => 305, 2 => 306, 1 => 303, 0 => 304, 5 => 307, 4 => 308, 7 => 309, 6 => 310, _ => 0, }); } private static CraftKey GetHeldKey() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) foreach (CraftKey key in CraftConfig.Keys) { if (IsKeyHeld(key.Key)) { return key; } } return null; } public static bool GetButton(string name) { if (!ZInput.GetButton(name)) { if (name == "AltPlace") { return GetHeldKey() != null; } return false; } return true; } public static int GetStackAmount(Recipe recipe) { int num = Mathf.Max(recipe.m_amount, 1); return Mathf.Max(recipe.m_item.m_itemData.m_shared.m_maxStackSize / num, 1); } private static int GetAllAmount(Recipe recipe, Player player) { if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25) || IsFree(recipe)) { return GetStackAmount(recipe); } if (!player.HaveRequirements(recipe, false, 1, 1)) { return 0; } int num = 1; int num2 = 2; while (num2 <= 10000 && player.HaveRequirements(recipe, false, 1, num2)) { num = num2; num2 *= 2; } num2 = Mathf.Min(num2, 10001); while (num2 - num > 1) { int num3 = (num + num2) / 2; if (player.HaveRequirements(recipe, false, 1, num3)) { num = num3; } else { num2 = num3; } } return num; } private static bool IsFree(Recipe recipe) { bool result = false; bool result2 = true; Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val.m_resItem == (Object)null)) { if (val.GetAmount(1) <= 0) { result = true; } else { result2 = false; } } } if (!recipe.m_requireOnlyOneIngredient) { return result2; } return result; } public static int GetCraftCount(CraftKey craftKey, Recipe recipe, Player player) { return craftKey.Mode switch { CraftMode.Stack => GetStackAmount(recipe), CraftMode.All => GetAllAmount(recipe, player), _ => craftKey.Count, }; } public static CraftKey ApplyAmount(InventoryGui gui) { if (defaultAmount < 0) { defaultAmount = gui.m_multiCraftAmount; defaultDuration = gui.m_multiCraftDuration; } CraftKey heldKey = GetHeldKey(); Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe; Player localPlayer = Player.m_localPlayer; int num = defaultAmount; if (heldKey != null && (Object)(object)recipe != (Object)null && (Object)(object)localPlayer != (Object)null) { num = Mathf.Max(GetCraftCount(heldKey, recipe, localPlayer), 1); } gui.m_multiCraftAmount = num; gui.m_multiCraftDuration = defaultDuration * (float)num / (float)Mathf.Max(defaultAmount, 1); return heldKey; } public static void StartCraft(InventoryGui gui, CraftKey craftKey) { dropExtra = craftKey != null && craftKey.Mode == CraftMode.All && ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData == null; } public static bool CanAddItem(Inventory inventory, GameObject prefab, int stack) { if (!dropExtra) { return inventory.CanAddItem(prefab, stack); } return true; } public static ItemData AddItem(Inventory inventory, string name, int stack, int quality, int variant, long crafterID, string crafterName, Vector2i position, bool cheated, bool pickedUp, bool dropIfFullInv) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) GameObject val = (dropExtra ? ObjectDB.instance.GetItemPrefab(name) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 == (Object)null) { return inventory.AddItem(name, stack, quality, variant, crafterID, crafterName, position, cheated, pickedUp, dropIfFullInv); } int num = Mathf.Min(stack, GetFreeSpace(inventory, val2.m_itemData.m_shared, quality)); ItemData val3 = null; if (num > 0) { val3 = inventory.AddItem(name, num, quality, variant, crafterID, crafterName, position, cheated, pickedUp, dropIfFullInv); } if (stack > num) { ItemData val4 = Drop(val2, stack - num, quality, variant, crafterID, crafterName, cheated); if (val3 == null) { val3 = val4; } } return val3; } public static void RaiseSkill(Character character, SkillType skill, float value) { //IL_0010: 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_003d: Unknown result type (might be due to invalid IL or missing references) if (!CraftConfig.FullExperience || value <= 1f) { character.RaiseSkill(skill, value); return; } int num = Mathf.FloorToInt(value); for (int i = 0; i < num; i++) { character.RaiseSkill(skill, 1f); } if (value > (float)num) { character.RaiseSkill(skill, value - (float)num); } } private static int GetFreeSpace(Inventory inventory, SharedData shared, int quality) { int num = inventory.GetEmptySlots() * shared.m_maxStackSize; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_shared.m_name == shared.m_name && allItem.m_quality == quality && allItem.m_worldLevel == Game.m_worldLevel) { num += Mathf.Max(shared.m_maxStackSize - allItem.m_stack, 0); } } return num; } private static ItemData Drop(ItemDrop itemDrop, int amount, int quality, int variant, long crafterID, string crafterName, bool cheated) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)Player.m_localPlayer).transform; Vector3 val = transform.position + transform.forward + Vector3.up; int num = Mathf.Max(itemDrop.m_itemData.m_shared.m_maxStackSize, 1); ItemData val2 = null; while (amount > 0) { val2 = itemDrop.m_itemData.Clone(); val2.m_dropPrefab = ((Component)itemDrop).gameObject; val2.m_stack = Mathf.Min(amount, num); val2.m_quality = quality; val2.m_variant = variant; val2.m_durability = val2.GetMaxDurability(); val2.m_crafterID = crafterID; val2.m_crafterName = crafterName; val2.m_worldLevel = (byte)Game.m_worldLevel; val2.m_cheated = cheated; ItemDrop.DropItem(val2, val2.m_stack, val, Quaternion.identity); amount -= val2.m_stack; } return val2; } public static IEnumerable<CodeInstruction> ReplaceCalls(IEnumerable<CodeInstruction> instructions, MethodInfo original, MethodInfo replacement) { foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; } yield return instruction; } } public static IEnumerable<CodeInstruction> ReplaceButtonAndSpace(IEnumerable<CodeInstruction> instructions) { instructions = ReplaceCalls(instructions, AccessTools.Method(typeof(ZInput), "GetButton", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "GetButton", (Type[])null, (Type[])null)); return ReplaceCalls(instructions, AccessTools.Method(typeof(Inventory), "CanAddItem", new Type[2] { typeof(GameObject), typeof(int) }, (Type[])null), AccessTools.Method(typeof(Craft), "CanAddItem", (Type[])null, (Type[])null)); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] public static class UpdateRecipePatch { private static void Prefix(InventoryGui __instance) { if (__instance.m_craftTimer < 0f) { Craft.ApplyAmount(__instance); } } private static void Postfix(InventoryGui __instance, Player player) { Craft.ContinueBatch(__instance, player); Craft.ShowBatchProgress(__instance); CraftHint.Update(__instance, player); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Craft.ReplaceCalls(instructions, AccessTools.Method(typeof(ZInput), "GetButton", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "GetButton", (Type[])null, (Type[])null)); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] public static class OnCraftPressedPatch { private static void Prefix(InventoryGui __instance) { Craft.StartCraft(__instance, Craft.ApplyAmount(__instance)); } private static void Postfix(InventoryGui __instance) { Craft.QueueBatch(__instance); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Craft.ReplaceButtonAndSpace(instructions); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] public static class DoCraftingPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(Inventory), "AddItem", new Type[10] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool), typeof(bool), typeof(bool) }, (Type[])null); instructions = Craft.ReplaceButtonAndSpace(instructions); instructions = Craft.ReplaceCalls(instructions, original, AccessTools.Method(typeof(Craft), "AddItem", (Type[])null, (Type[])null)); return Craft.ReplaceCalls(instructions, AccessTools.Method(typeof(Character), "RaiseSkill", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "RaiseSkill", (Type[])null, (Type[])null)); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")] public static class OnCraftCancelPressedPatch { private static void Prefix() { Craft.StopBatch(); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] public static class HidePatch { private static void Prefix() { Craft.StopBatch(); } } [BepInPlugin("kriona.CraftMore", "Craft More", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "kriona.CraftMore"; public const string PluginName = "Craft More"; public const string PluginVersion = "1.0.1"; internal static ConfigWatcher Watcher; private Harmony harmony; private void Awake() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown CraftConfig.Init(((BaseUnityPlugin)this).Config); CraftConfig.Load(); Watcher = new ConfigWatcher(CraftConfig.FilePath, CraftConfig.Load, ((BaseUnityPlugin)this).Logger, "Craft More"); harmony = new Harmony("kriona.CraftMore"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Craft More 1.0.1 loaded"); } private void Update() { Watcher.Update(); } private void OnDestroy() { Watcher?.Dispose(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace Kriona.Shared { internal class ConfigMirror { private readonly ConfigFile config; private readonly Action changed; private bool syncing; public ConfigMirror(ConfigFile config, Action changed) { this.config = config; this.changed = changed; config.SaveOnConfigSet = false; config.SettingChanged += OnSettingChanged; } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, AcceptableValueBase acceptableValues, int? order, string displayName) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes(); configurationManagerAttributes.Order = order; configurationManagerAttributes.DispName = displayName; return config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, new object[1] { configurationManagerAttributes })); } public void Remove(ConfigEntryBase entry) { config.Remove(entry.Definition); } public void Sync(Action apply) { syncing = true; try { apply(); } finally { syncing = false; } } public static void SetDisplayName(ConfigEntryBase entry, string displayName) { object[] tags = entry.Description.Tags; for (int i = 0; i < tags.Length; i++) { if (tags[i] is ConfigurationManagerAttributes configurationManagerAttributes) { configurationManagerAttributes.DispName = displayName; } } } private void OnSettingChanged(object sender, SettingChangedEventArgs args) { if (!syncing) { changed(); } } } internal sealed class ConfigurationManagerAttributes { public int? Order; public string DispName; } internal class ConfigWatcher : IDisposable { private const float ReloadDelay = 0.5f; private readonly string path; private readonly Action reload; private readonly ManualLogSource log; private readonly string pluginName; private readonly FileSystemWatcher watcher; private string knownContents; private volatile bool fileChanged; private float reloadTime = -1f; public ConfigWatcher(string path, Action reload, ManualLogSource log, string pluginName) { this.path = path; this.reload = reload; this.log = log; this.pluginName = pluginName; Remember(); try { watcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path)); watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; watcher.Changed += delegate { fileChanged = true; }; watcher.Created += delegate { fileChanged = true; }; watcher.Renamed += delegate { fileChanged = true; }; watcher.EnableRaisingEvents = true; } catch (Exception ex) { watcher = null; log.LogWarning((object)("Couldn't watch " + path + " for changes, so changes made to it while Valheim is running aren't read: " + ex.Message)); } } public ConfigWatcher(ConfigFile config, ManualLogSource log, string pluginName) : this(config.ConfigFilePath, delegate { ReloadConfig(config); }, log, pluginName) { } private static void ReloadConfig(ConfigFile config) { bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { config.Reload(); } finally { config.SaveOnConfigSet = saveOnConfigSet; } if (saveOnConfigSet && !MatchesFile(config)) { config.Save(); } } private static bool MatchesFile(ConfigFile config) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>(); string text = string.Empty; string[] array = File.ReadAllLines(config.ConfigFilePath); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } string[] array2 = text2.Split(new char[1] { '=' }, 2); if (array2.Length == 2) { dictionary[new ConfigDefinition(text, array2[0].Trim())] = array2[1].Trim(); } } foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config) { if (!dictionary.TryGetValue(item.Key, out var value)) { return false; } try { if (!object.Equals(TomlTypeConverter.ConvertToValue(value, item.Value.SettingType), item.Value.BoxedValue)) { return false; } } catch (Exception) { return false; } } return true; } public void Update() { if (fileChanged) { fileChanged = false; reloadTime = Time.realtimeSinceStartup + 0.5f; } if (reloadTime < 0f || Time.realtimeSinceStartup < reloadTime) { return; } reloadTime = -1f; string text; try { text = File.ReadAllText(path); } catch (FileNotFoundException) { return; } catch (IOException) { reloadTime = Time.realtimeSinceStartup + 0.5f; return; } if (text == knownContents) { return; } reload(); Remember(); if (pluginName != null) { log.LogInfo((object)("Reloaded " + Path.GetFileName(path))); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)1, pluginName + ": config reloaded", 0, (Sprite)null, false, true); } } } public void Remember() { try { knownContents = File.ReadAllText(path); } catch (IOException) { knownContents = null; } } public void Dispose() { watcher?.Dispose(); } } internal class StateEntries { public const string Inherit = "inherit"; private static readonly AcceptableValueList<string> values = new AcceptableValueList<string>(new string[3] { "inherit", "true", "false" }); private readonly ConfigMirror mirror; private readonly string help; private readonly Dictionary<string, ConfigEntry<string>> entries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public StateEntries(ConfigMirror mirror, string help) { this.mirror = mirror; this.help = help; } public void Set(string section, string key, string displayName, bool? state) { string entryName = GetEntryName(key, displayName); if (entries.TryGetValue(key, out var value) && ((ConfigEntryBase)value).Definition.Section != section) { mirror.Remove((ConfigEntryBase)(object)value); entries.Remove(key); value = null; } if (value == null) { try { value = mirror.Bind(section, key, "inherit", "Prefab " + key + " - " + help, (AcceptableValueBase)(object)values, null, entryName); } catch (ArgumentException) { return; } entries[key] = value; } else { ConfigMirror.SetDisplayName((ConfigEntryBase)(object)value, entryName); } value.Value = Format(state); set.Add(key); } public void RemoveUnset() { List<string> list = new List<string>(); foreach (KeyValuePair<string, ConfigEntry<string>> entry in entries) { if (!set.Contains(entry.Key)) { mirror.Remove((ConfigEntryBase)(object)entry.Value); list.Add(entry.Key); } } foreach (string item in list) { entries.Remove(item); } set.Clear(); } public bool TryGetState(string key, out bool? state) { state = null; if (!entries.TryGetValue(key, out var value)) { return false; } if (bool.TryParse(value.Value, out var result)) { state = result; } return true; } public static string Format(bool? state) { if (!state.HasValue) { return "inherit"; } if (!state.Value) { return "false"; } return "true"; } private static string GetEntryName(string key, string displayName) { string text = displayName.Trim(); if (text.Length == 0 || text.Equals(key, StringComparison.OrdinalIgnoreCase)) { return key; } return text + " (" + key + ")"; } } }