using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CraftFromContainers; using HarmonyLib; using Microsoft.CodeAnalysis; 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.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("MultiCraft")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MultiCraft")] [assembly: AssemblyTitle("MultiCraft")] [assembly: AssemblyVersion("1.0.0.0")] [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 MultiCraft { [HarmonyPatch(typeof(InventoryGui), "Show")] public static class CraftGUI_Patch { private static void Postfix(Container container) { MultiCraft_UI.instance?.Start(); MultiCraft_Logic.instance?.Start(); } public static void Update() { if (MultiCraft_Logic.Started && InventoryGui.IsVisible()) { MultiCraft_UI.instance.RefreshCustomButtonStates(); MultiCraft_Logic.instance.Update(); } } } [HarmonyPatch(typeof(InventoryGui), "Hide")] public static class CraftGUI_Hide_Patch { private static void Postfix() { if (MultiCraft_Logic.Started) { MultiCraft_Logic.instance.ResetMultiCrafting(); } } } public class RequirementInformation { public string name; public int RequiredAmount; public RequirementInformation(int reqAmount, string itemName) { RequiredAmount = reqAmount; name = itemName; } public int GetMaxByCurrentRequirement() { if (RequiredAmount <= 0) { return int.MaxValue; } return Helper.GetAvailableItems(name) / RequiredAmount; } } public class CurrentRecipeInformation { private static CurrentRecipeInformation _instance; public static UnityEvent OnRecipeChanged = new UnityEvent(); private Dictionary requirements; private string _name = ""; public static CurrentRecipeInformation instance { get { if (_instance == null) { _instance = new CurrentRecipeInformation(); } return _instance; } } public static bool Started { get; private set; } public bool IsRecipeReady { get; private set; } public string name { get { return _name; } set { if (!IsRecipe(value)) { _name = value; UnityEvent onRecipeChanged = OnRecipeChanged; if (onRecipeChanged != null) { onRecipeChanged.Invoke(); } ClearRequirements(); } } } public bool HasRequirements => requirements.Count > 0; private CurrentRecipeInformation() { requirements = new Dictionary(); IsRecipeReady = false; } public void Start() { Started = true; } public bool IsRecipe(string otherName) { return name == otherName; } public void ClearRequirements() { IsRecipeReady = false; requirements.Clear(); } public void AddRequirement(string name, int requiredAmount) { if (requirements.ContainsKey(name)) { IsRecipeReady = true; return; } RequirementInformation value = new RequirementInformation(requiredAmount, name); requirements[name] = value; } public int GetCurrentMaxCraftable() { int num = 99; foreach (KeyValuePair requirement in requirements) { num = Math.Min(num, requirement.Value.GetMaxByCurrentRequirement()); } return num; } } internal class Helper { public static ManualLogSource Logger; public static bool CraftFromContainersRunning = false; private static string CraftFromContainersGUID = "aedenthorn.CraftFromContainers"; public static bool CraftFromContainersInstalledAndActive { get; private set; } public static void CheckAvailabilityForCraftFromContainersPlugin() { CraftFromContainersRunning = false; BaseUnityPlugin[] componentsInChildren = GameObject.Find("BepInEx_Manager").GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { if (componentsInChildren[i].Info.Metadata.GUID == CraftFromContainersGUID) { CraftFromContainersRunning = IsCraftFromContainersEnabled(); } } } public static bool IsCraftFromContainersEnabled() { GameObject val = GameObject.Find("BepInEx_Manager"); Debug.Log((object)("Found bepInExManager: " + (object)val)); BaseUnityPlugin[] componentsInChildren = val.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { if (componentsInChildren[i].Info.Metadata.GUID == "aedenthorn.CraftFromContainers") { return BepInExPlugin.modEnabled.Value; } } return false; } public static int GetAvailableItems(string itemName) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return 0; } int num = 0; if (CraftFromContainersRunning) { num = GetItemsInCloseContainers(itemName); } return ((Humanoid)localPlayer).GetInventory().CountItems(itemName, -1, true) + num; } public static int GetItemsInCloseContainers(string itemName) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (CraftFromContainersRunning) { int num = 0; { foreach (Container nearbyContainer in BepInExPlugin.GetNearbyContainers(((Component)Player.m_localPlayer).transform.position)) { num += nearbyContainer.GetInventory().CountItems(itemName, -1, true); } return num; } } return ((Humanoid)Player.m_localPlayer).GetInventory().CountItems(itemName, -1, true); } } [HarmonyPatch(typeof(Menu), "Start", new Type[] { })] public static class MainMenuShow_Patch { private static void Postfix() { Helper.CheckAvailabilityForCraftFromContainersPlugin(); } } [BepInPlugin("maximods.valheim.multicraft", "MultiCraft", "1.5.0")] [BepInProcess("valheim.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class MultiCraftPlugin : BaseUnityPlugin { public const string pluginGuid = "maximods.valheim.multicraft"; public const string pluginName = "MultiCraft"; public const string pluginVersion = "1.5.0"; public static ConfigEntry CapMaximumCrafts; public static ConfigEntry DisableVanillaMultiCraft; public static ConfigEntry GreyOutPlusAtMax; public static ConfigEntry EnableGamepadInput; public static ConfigEntry GamepadBulkStep; public static ConfigEntry ShowCraftAmountHints; private Harmony _harmony; public static bool CraftFromContainersInstalledAndActive; private static List cachedContainers; public void Awake() { CapMaximumCrafts = ((BaseUnityPlugin)this).Config.Bind("MultiCraft", "CapMaximumCrafts", false, "Caps the maximum amount to craft to the maximum possible to craft."); DisableVanillaMultiCraft = ((BaseUnityPlugin)this).Config.Bind("Compatibility", "DisableVanillaMultiCraft", true, "Disables Valheim's built-in \"Craft x5\" shortcut (Left Shift on keyboard, Left Stick click on controller) while MultiCraft is active, including the \" x 5\" suffix on the craft button."); GreyOutPlusAtMax = ((BaseUnityPlugin)this).Config.Bind("UI", "GreyOutPlusAtMax", true, "Greys out the + button once the craft amount reaches the maximum craftable with the materials available."); EnableGamepadInput = ((BaseUnityPlugin)this).Config.Bind("Gamepad", "EnableGamepadInput", true, "Enables controller input for the craft amount. D-Pad Left/Right steps the amount; hold Left Trigger for bulk steps, hold Right Trigger to jump to the maximum (or back to 1)."); GamepadBulkStep = ((BaseUnityPlugin)this).Config.Bind("Gamepad", "GamepadBulkStep", 10, "How much the amount changes when Left Trigger is held while pressing D-Pad Left/Right."); ShowCraftAmountHints = ((BaseUnityPlugin)this).Config.Bind("UI", "ShowCraftAmountHints", true, "Adds entries to the inventory hint bar describing the bulk and maximum craft-amount shortcuts."); Helper.Logger = ((BaseUnityPlugin)this).Logger; _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { CraftGUI_Patch.Update(); } private void LateUpdate() { if (CraftFromContainersInstalledAndActive && cachedContainers != null) { cachedContainers.Clear(); cachedContainers = null; } } } public static class MultiCraft_KeyHints { private const string BULK_ENTRY = "MultiCraft_Bulk"; private const string MAX_ENTRY = "MultiCraft_Max"; private const string KEYBOARD_GROUP = "Keyboard"; private const string KEYBOARD_TEMPLATE = "Split"; private const string GAMEPAD_TEMPLATE = "Text - Split"; private static readonly string[] HintRoots = new string[2] { "InventoryHints", "InventoryWithContainerHints" }; private static readonly string[] GamepadGroups = new string[2] { "Gamepad", "GamepadMouse" }; private static readonly string[] VanillaMultiCraftEntries = new string[3] { "Keyboard/Multicraft", "Gamepad/Text - Multicraft", "GamepadMouse/Text - Multicraft" }; public static void Refresh() { if ((Object)(object)KeyHints.instance == (Object)null) { return; } string[] hintRoots = HintRoots; foreach (string text in hintRoots) { Transform val = ((Component)KeyHints.instance).transform.Find(text); if (!((Object)(object)val == (Object)null)) { HideVanillaMultiCraftHint(val); RefreshCraftAmountHints(val); } } } private static void HideVanillaMultiCraftHint(Transform root) { bool active = !MultiCraftPlugin.DisableVanillaMultiCraft.Value; string[] vanillaMultiCraftEntries = VanillaMultiCraftEntries; foreach (string text in vanillaMultiCraftEntries) { Transform val = root.Find(text); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(active); } } } private static void RefreshCraftAmountHints(Transform root) { bool value = MultiCraftPlugin.ShowCraftAmountHints.Value; int num = Mathf.Max(1, MultiCraftPlugin.GamepadBulkStep.Value); Transform val = root.Find("Keyboard"); if ((Object)(object)val != (Object)null) { Transform orCreateEntry = GetOrCreateEntry(val, "Split", "MultiCraft_Bulk"); SetKeyboardEntry(orCreateEntry, "Craft +" + num, "L-Shift"); Transform orCreateEntry2 = GetOrCreateEntry(val, "Split", "MultiCraft_Max"); SetKeyboardEntry(orCreateEntry2, "Craft max", "L-Ctrl"); SetVisible(orCreateEntry, value); SetVisible(orCreateEntry2, value); } string[] gamepadGroups = GamepadGroups; foreach (string text in gamepadGroups) { Transform val2 = root.Find(text); if (!((Object)(object)val2 == (Object)null)) { Transform orCreateEntry3 = GetOrCreateEntry(val2, "Text - Split", "MultiCraft_Bulk"); SetGamepadEntry(orCreateEntry3, "Craft +" + num, "JoyLTrigger"); Transform orCreateEntry4 = GetOrCreateEntry(val2, "Text - Split", "MultiCraft_Max"); SetGamepadEntry(orCreateEntry4, "Craft max", "JoyRTrigger"); bool visible = value && MultiCraftPlugin.EnableGamepadInput.Value; SetVisible(orCreateEntry3, visible); SetVisible(orCreateEntry4, visible); } } } private static Transform GetOrCreateEntry(Transform group, string templateName, string entryName) { Transform val = group.Find(entryName); if ((Object)(object)val != (Object)null) { return val; } Transform val2 = group.Find(templateName); if ((Object)(object)val2 == (Object)null) { return null; } Transform obj = Object.Instantiate(val2, group); ((Object)obj).name = entryName; obj.SetAsLastSibling(); return obj; } private static void SetKeyboardEntry(Transform entry, string label, string modifierKey) { if (!((Object)(object)entry == (Object)null)) { SetText(entry.Find("Text"), label); SetText(entry.Find("key_bkg/Key"), modifierKey); SetText(entry.Find("key_bkg (2)/Key"), "Mouse-1"); } } private static void SetGamepadEntry(Transform entry, string label, string modifierZinputKey) { if (!((Object)(object)entry == (Object)null) && ZInput.instance != null) { string boundKeyString = ZInput.instance.GetBoundKeyString(modifierZinputKey, true); string boundKeyString2 = ZInput.instance.GetBoundKeyString("JoyDPadRight", true); if (!string.IsNullOrEmpty(boundKeyString) && !string.IsNullOrEmpty(boundKeyString2)) { SetText(entry, label + " " + boundKeyString + " + " + boundKeyString2 + ""); } } } private static void SetText(Transform target, string value) { if (!((Object)(object)target == (Object)null)) { TMP_Text component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null) { component.text = value; } } } private static void SetVisible(Transform entry, bool visible) { if ((Object)(object)entry != (Object)null) { ((Component)entry).gameObject.SetActive(visible); } } } public class MultiCraft_Logic { private static MultiCraft_Logic _instance; public static bool Started; private int _CurrentCraftAmount = 1; public int value = 5; private bool WasCrafting; private int WaitUpdates; public static MultiCraft_Logic instance { get { if (_instance == null) { _instance = new MultiCraft_Logic(); } return _instance; } } public int CurrentCraftAmount { get { return _CurrentCraftAmount; } set { _CurrentCraftAmount = value; MultiCraft_UI.instance.UpdateAmountLabel(_CurrentCraftAmount); } } public void Start() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown Started = true; CurrentCraftAmount = 1; CurrentRecipeInformation.OnRecipeChanged.AddListener(new UnityAction(OnRecipeChanged)); } public void OnRecipeChanged() { ResetMultiCrafting(); } public void ResetMultiCrafting() { CurrentCraftAmount = 1; CancelMultiCrafting(); } public void CancelMultiCrafting() { WasCrafting = false; if (Object.op_Implicit((Object)(object)InventoryGui.instance) && InventoryGui.instance.InCraftTab()) { MultiCraft_UI.instance.UnblockCustomButtons(); } } public void StartCraftingLogic() { WasCrafting = true; WaitUpdates = 5; } public void SetCraftAmount(int value) { CurrentCraftAmount = ClampCraftAmount(value); } public void AddToCraftAmount(int delta) { CurrentCraftAmount = ClampCraftAmount(CurrentCraftAmount + delta); } private int ClampCraftAmount(int value) { if (MultiCraftPlugin.CapMaximumCrafts.Value) { value = Math.Min(value, CurrentRecipeInformation.instance.GetCurrentMaxCraftable()); } if (value < 1) { value = 1; } return value; } public bool IsCrafting() { return ((Component)InventoryGui.instance.m_craftProgressBar).gameObject.activeInHierarchy; } public void Update() { if (WaitUpdates-- <= 0 && WasCrafting && !IsCrafting()) { if (CurrentCraftAmount <= 1) { CancelMultiCrafting(); } else { TryNextCraft(); } } } public void TryNextCraft() { WasCrafting = false; if (CurrentCraftAmount > 1) { int currentCraftAmount = CurrentCraftAmount - 1; CurrentCraftAmount = currentCraftAmount; if (!instance.CanCraft()) { CancelMultiCrafting(); } else { ((UnityEvent)((Component)InventoryGui.instance.m_craftButton).GetComponent