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 WiresRecycler v1.0.1
WiresRecycler.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; 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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("WiresRecycler")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WiresRecycler")] [assembly: AssemblyTitle("WiresRecycler")] [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 WiresRecycler { [BepInPlugin("wire.valheim.recycler", "Wire's Recycler", "1.0.1")] [BepInProcess("valheim.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "wire.valheim.recycler"; public const string Name = "Wire's Recycler"; public const string Version = "1.0.1"; private Harmony _harmony; private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _harmony = new Harmony("wire.valheim.recycler"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Recycler 1.0.1 loaded."); } private void OnDestroy() { RecyclerUi.Shutdown(); CraftXpDebt.Reset(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal sealed class RecycleOutput { internal ItemDrop Item; internal int BaseMin; internal int BaseMax; internal float BaseHighChance; internal int UpgradeMin; internal int UpgradeMax; internal float UpgradeHighChance; internal int MinAmount => BaseMin + UpgradeMin; internal int MaxAmount => BaseMax + UpgradeMax; internal int Roll() { int num = BaseMin; if (BaseMax > BaseMin && Random.value < BaseHighChance) { num = BaseMax; } num += UpgradeMin; if (UpgradeMax > UpgradeMin && Random.value < UpgradeHighChance) { num += UpgradeMax - UpgradeMin; } return num; } } internal sealed class RecyclePlan { internal Recipe Recipe; internal ItemData Item; internal int ConsumeAmount; internal int RequiredLevel; internal bool SpecialReverse; internal readonly List<RecycleOutput> Outputs = new List<RecycleOutput>(); } internal static class GuiAccess { internal static readonly FieldInfo AvailableRecipes = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes"); internal static readonly FieldInfo SelectedRecipe = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); internal static readonly FieldInfo RecipeListBaseSize = AccessTools.Field(typeof(InventoryGui), "m_recipeListBaseSize"); internal static readonly FieldInfo MinStationBaseColor = AccessTools.Field(typeof(InventoryGui), "m_minStationLevelBasecolor"); internal static readonly FieldInfo CraftTimer = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); internal static readonly FieldInfo MultiCrafting = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); internal static readonly MethodInfo AddRecipeToList = AccessTools.Method(typeof(InventoryGui), "AddRecipeToList", new Type[4] { typeof(Player), typeof(Recipe), typeof(ItemData), typeof(bool) }, (Type[])null); internal static readonly MethodInfo UpdateCraftingPanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", new Type[1] { typeof(bool) }, (Type[])null); private static readonly Type PairType = AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair"); private static readonly PropertyInfo PairRecipe = ((PairType != null) ? PairType.GetProperty("Recipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); private static readonly PropertyInfo PairItem = ((PairType != null) ? PairType.GetProperty("ItemData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); private static readonly PropertyInfo PairElement = ((PairType != null) ? PairType.GetProperty("InterfaceElement", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); internal static IList GetAvailable(InventoryGui gui) { if (!(AvailableRecipes != null)) { return null; } return AvailableRecipes.GetValue(gui) as IList; } internal static void GetSelected(InventoryGui gui, out Recipe recipe, out ItemData item) { recipe = null; item = null; if (!(SelectedRecipe == null) && !(PairRecipe == null) && !(PairItem == null)) { object value = SelectedRecipe.GetValue(gui); if (value != null) { object? value2 = PairRecipe.GetValue(value, null); recipe = (Recipe)((value2 is Recipe) ? value2 : null); object? value3 = PairItem.GetValue(value, null); item = (ItemData)((value3 is ItemData) ? value3 : null); } } } internal static GameObject GetPairElement(object pair) { if (pair == null || !(PairElement != null)) { return null; } object? value = PairElement.GetValue(pair, null); return (GameObject)((value is GameObject) ? value : null); } internal static void Refresh(InventoryGui gui, bool focus) { if (UpdateCraftingPanel != null) { UpdateCraftingPanel.Invoke(gui, new object[1] { focus }); } } } internal static class RecyclerUi { [CompilerGenerated] private static class <>O { public static UnityAction <0>__SelectRecycleTab; } private static readonly HashSet<string> GaldrReversePrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Bronze", "CeramicPlate" }; private static readonly List<RecyclePlan> Plans = new List<RecyclePlan>(); private static readonly List<ItemData> TempItems = new List<ItemData>(); private static InventoryGui _gui; private static GameObject _tabObject; private static Button _tabButton; private static ItemData _armedItem; private static Recipe _armedRecipe; internal static bool Active { get; private set; } internal static void Initialize(InventoryGui gui) { //IL_00ae: 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_00b9: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01da: 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) Shutdown(); _gui = gui; if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_tabUpgrade == (Object)null) { return; } _tabObject = Object.Instantiate<GameObject>(((Component)gui.m_tabUpgrade).gameObject, ((Component)gui.m_tabUpgrade).transform.parent, false); ((Object)_tabObject).name = "WireRecycleTab"; _tabButton = _tabObject.GetComponent<Button>(); if ((Object)(object)_tabButton == (Object)null) { Object.Destroy((Object)(object)_tabObject); _tabObject = null; return; } ((UnityEventBase)_tabButton.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = _tabButton.onClick; object obj = <>O.<0>__SelectRecycleTab; if (obj == null) { UnityAction val = SelectRecycleTab; <>O.<0>__SelectRecycleTab = val; obj = (object)val; } ((UnityEvent)onClick).AddListener((UnityAction)obj); TMP_Text componentInChildren = _tabObject.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "RECYCLE"; } Transform transform = _tabObject.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); Transform transform2 = ((Component)gui.m_tabUpgrade).transform; RectTransform val3 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null) { Rect rect = val2.rect; float width = ((Rect)(ref rect)).width; float num = ((Transform)val3).localPosition.x; foreach (Transform item in ((Component)gui.m_tabUpgrade).transform.parent) { Transform val4 = item; if (!((Object)(object)val4 == (Object)(object)_tabObject.transform) && ((Component)val4).gameObject.activeSelf && !((Object)(object)((Component)val4).GetComponent<Button>() == (Object)null)) { num = Mathf.Max(num, val4.localPosition.x); } } Vector3 localPosition = ((Transform)val3).localPosition; localPosition.x = num + width + 10f; ((Transform)val2).localPosition = localPosition; } ((Selectable)_tabButton).interactable = true; _tabObject.SetActive(false); } internal static void Shutdown() { Active = false; ClearArmed(); Plans.Clear(); if ((Object)(object)_tabObject != (Object)null) { Object.Destroy((Object)(object)_tabObject); } _tabObject = null; _tabButton = null; _gui = null; } internal static void LeaveRecycleTab() { if (Active) { Active = false; ClearArmed(); if ((Object)(object)_tabButton != (Object)null) { ((Selectable)_tabButton).interactable = true; } } } internal static void SyncTabVisibility(InventoryGui gui) { if (!((Object)(object)_tabObject == (Object)null) && !((Object)(object)gui == (Object)null)) { Player localPlayer = Player.m_localPlayer; CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null); bool flag = (Object)(object)val != (Object)null && !val.m_upgrader && ((Component)gui.m_tabUpgrade).gameObject.activeSelf && StationSupportsRecycler(val); _tabObject.SetActive(flag); if (!flag && Active) { LeaveRecycleTab(); } } } private static void SelectRecycleTab() { if ((Object)(object)_gui == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } CraftingStation currentCraftingStation = Player.m_localPlayer.GetCurrentCraftingStation(); if (!((Object)(object)currentCraftingStation == (Object)null) && !currentCraftingStation.m_upgrader && StationSupportsRecycler(currentCraftingStation)) { Active = true; ClearArmed(); ((Selectable)_gui.m_tabCraft).interactable = true; ((Selectable)_gui.m_tabUpgrade).interactable = true; if ((Object)(object)_tabButton != (Object)null) { ((Selectable)_tabButton).interactable = false; } GuiAccess.Refresh(_gui, focus: false); } } internal static void RebuildRecipeList(InventoryGui gui, List<Recipe> vanillaRecipes) { if (!Active || (Object)(object)gui == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } IList available = GuiAccess.GetAvailable(gui); if (available == null || GuiAccess.AddRecipeToList == null) { return; } for (int i = 0; i < available.Count; i++) { GameObject pairElement = GuiAccess.GetPairElement(available[i]); if ((Object)(object)pairElement != (Object)null) { Object.Destroy((Object)(object)pairElement); } } available.Clear(); Plans.Clear(); Player localPlayer = Player.m_localPlayer; CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null) { return; } List<Recipe> list = new List<Recipe>(); if (vanillaRecipes != null) { for (int j = 0; j < vanillaRecipes.Count; j++) { Recipe val = vanillaRecipes[j]; if ((Object)(object)val != (Object)null && !list.Contains(val)) { list.Add(val); } } } AddSpecialReverseRecipes(localPlayer, currentCraftingStation, list); Inventory inventory = ((Humanoid)localPlayer).GetInventory(); for (int k = 0; k < list.Count; k++) { Recipe val2 = list[k]; bool flag = IsSpecialReverse(currentCraftingStation, val2); if (!RecipeCanAppear(val2, currentCraftingStation, flag)) { continue; } TempItems.Clear(); inventory.GetAllItems(val2.m_item.m_itemData.m_shared.m_name, TempItems); for (int l = 0; l < TempItems.Count; l++) { ItemData val3 = TempItems[l]; int consumeAmount = GetConsumeAmount(val3, val2); if (consumeAmount <= 0) { continue; } RecyclePlan recyclePlan = new RecyclePlan { Recipe = val2, Item = val3, ConsumeAmount = consumeAmount, RequiredLevel = (flag ? 1 : Mathf.Max(1, val2.GetRequiredStationLevel(1))), SpecialReverse = flag }; BuildOutputs(gui, recyclePlan); if (recyclePlan.Outputs.Count != 0) { bool flag2 = IsStationUsableForPlan(localPlayer, currentCraftingStation, recyclePlan); GuiAccess.AddRecipeToList.Invoke(gui, new object[4] { localPlayer, val2, val3, flag2 }); GameObject pairElement2 = GuiAccess.GetPairElement((available.Count > 0) ? available[available.Count - 1] : null); if ((Object)(object)pairElement2 != (Object)null) { PolishListElement(pairElement2, recyclePlan); } Plans.Add(recyclePlan); } } } float num = 0f; if (GuiAccess.RecipeListBaseSize != null) { object value = GuiAccess.RecipeListBaseSize.GetValue(gui); if (value is float) { num = (float)value; } } float num2 = Mathf.Max(num, (float)available.Count * gui.m_recipeListSpace); gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num2); } private static void PolishListElement(GameObject element, RecyclePlan plan) { if ((Object)(object)element == (Object)null || plan == null || plan.Item == null) { return; } Transform val = element.transform.Find("icon"); if ((Object)(object)val != (Object)null) { Image component = ((Component)val).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { component.sprite = plan.Item.GetIcon(); } } Transform val2 = element.transform.Find("name"); if ((Object)(object)val2 != (Object)null) { TMP_Text component2 = ((Component)val2).GetComponent<TMP_Text>(); if ((Object)(object)component2 != (Object)null) { string text = Localization.instance.Localize(plan.Item.m_shared.m_name); if (plan.Item.m_quality > 1) { text = text + " Q" + plan.Item.m_quality; } if (plan.ConsumeAmount > 1) { text = text + " x" + plan.ConsumeAmount; } component2.text = text; } } Transform val3 = element.transform.Find("QualityLevel"); if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(plan.Item.m_quality > 1); } } internal static void OverrideRecipeDetails(InventoryGui gui, Player player) { if (!Active || (Object)(object)gui == (Object)null || (Object)(object)player == (Object)null) { return; } GuiAccess.GetSelected(gui, out var recipe, out var item); RecyclePlan recyclePlan = FindPlan(recipe, item); if (recyclePlan == null) { ClearArmed(); return; } if (_armedItem != null && (_armedItem != item || (Object)(object)_armedRecipe != (Object)(object)recipe)) { ClearArmed(); } ((Behaviour)gui.m_recipeIcon).enabled = true; ((Behaviour)gui.m_recipeName).enabled = true; ((Behaviour)gui.m_recipeDecription).enabled = true; ((Component)gui.m_qualityPanel).gameObject.SetActive(false); ((Component)gui.m_variantButton).gameObject.SetActive(false); gui.m_recipeIcon.sprite = item.GetIcon(); string text = Localization.instance.Localize(item.m_shared.m_name); gui.m_recipeName.text = ((item.m_quality > 1) ? (text + " Q" + item.m_quality) : text); gui.m_recipeDecription.text = BuildRecycleSummary(gui, recyclePlan); ((Component)gui.m_itemCraftType).gameObject.SetActive(true); gui.m_itemCraftType.text = "Recycle " + recyclePlan.ConsumeAmount + ((recyclePlan.ConsumeAmount == 1) ? " item" : " items"); ShowStationLevel(gui, player, recyclePlan); ShowOutputs(gui, recyclePlan); bool flag = IsPlanStillValid(player, recyclePlan) && IsStationUsableForPlan(player, player.GetCurrentCraftingStation(), recyclePlan); ((Selectable)gui.m_craftButton).interactable = flag; TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = ((_armedItem == item && (Object)(object)_armedRecipe == (Object)(object)recipe) ? "Confirm Recycle" : "Recycle"); } UITooltip component = ((Component)gui.m_craftButton).GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null) { component.m_text = (flag ? "" : "Recycling requirements are not met."); } if (GuiAccess.CraftTimer != null && (float)GuiAccess.CraftTimer.GetValue(gui) >= 0f && (Object)(object)gui.m_craftProgressPanel != (Object)null) { TMP_Text componentInChildren2 = ((Component)gui.m_craftProgressPanel).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.text = "Recycling..."; } } } internal static bool HandleCraftPressed(InventoryGui gui) { if (!Active) { return true; } GuiAccess.GetSelected(gui, out var recipe, out var item); RecyclePlan recyclePlan = FindPlan(recipe, item); if (recyclePlan == null || !IsPlanStillValid(Player.m_localPlayer, recyclePlan)) { return false; } if (_armedItem != item || (Object)(object)_armedRecipe != (Object)(object)recipe) { _armedItem = item; _armedRecipe = recipe; TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "Confirm Recycle"; } return false; } ClearArmed(); return true; } internal static void HandleCraftCancelled() { if (Active) { ClearArmed(); } } internal static bool CompleteRecycle(InventoryGui gui, Player player) { //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: 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) if (!Active || (Object)(object)gui == (Object)null || (Object)(object)player == (Object)null) { return false; } GuiAccess.GetSelected(gui, out var recipe, out var item); RecyclePlan recyclePlan = FindPlan(recipe, item); if (recyclePlan == null || !IsPlanStillValid(player, recyclePlan) || !IsStationUsableForPlan(player, player.GetCurrentCraftingStation(), recyclePlan)) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (recyclePlan.Item.m_equipped) { ((Humanoid)player).UnequipItem(recyclePlan.Item, true); } bool cheated = recyclePlan.Item.m_cheated; inventory.RemoveItem(recyclePlan.Item, recyclePlan.ConsumeAmount); long playerID = player.GetPlayerID(); string playerName = player.GetPlayerName(); for (int i = 0; i < recyclePlan.Outputs.Count; i++) { RecycleOutput recycleOutput = recyclePlan.Outputs[i]; int num = recycleOutput.Roll(); if (num > 0 && !((Object)(object)recycleOutput.Item == (Object)null)) { inventory.AddItem(((Object)((Component)recycleOutput.Item).gameObject).name, num, recycleOutput.Item.m_itemData.m_quality, recycleOutput.Item.m_itemData.m_variant, playerID, playerName, cheated, false); } } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation != (Object)null) { currentCraftingStation.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } else { gui.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } SkillType skill = (SkillType)(((Object)(object)recyclePlan.Recipe != (Object)null && (Object)(object)recyclePlan.Recipe.m_craftingStation != (Object)null) ? ((int)recyclePlan.Recipe.m_craftingStation.m_craftingSkill) : 0); CraftXpDebt.Add(player, skill, GetBatches(recyclePlan)); ClearArmed(); GuiAccess.Refresh(gui, focus: false); return true; } internal static bool ForceFiveCraftDuration(InventoryGui gui, out bool previous) { previous = false; if (!Active || GuiAccess.MultiCrafting == null) { return false; } previous = (bool)GuiAccess.MultiCrafting.GetValue(gui); GuiAccess.MultiCrafting.SetValue(gui, true); return true; } internal static void RestoreMultiCrafting(InventoryGui gui, bool changed, bool previous) { if (changed && GuiAccess.MultiCrafting != null) { GuiAccess.MultiCrafting.SetValue(gui, previous); } } private static void ClearArmed() { _armedItem = null; _armedRecipe = null; } private static RecyclePlan FindPlan(Recipe recipe, ItemData item) { if ((Object)(object)recipe == (Object)null || item == null) { return null; } for (int i = 0; i < Plans.Count; i++) { RecyclePlan recyclePlan = Plans[i]; if ((Object)(object)recyclePlan.Recipe == (Object)(object)recipe && recyclePlan.Item == item) { return recyclePlan; } } return null; } private static bool IsPlanStillValid(Player player, RecyclePlan plan) { if ((Object)(object)player != (Object)null && plan != null && plan.Item != null && ((Humanoid)player).GetInventory().ContainsItem(plan.Item)) { return plan.Item.m_stack >= plan.ConsumeAmount; } return false; } private static bool StationSupportsRecycler(CraftingStation station) { if ((Object)(object)station == (Object)null || station.m_upgrader || (Object)(object)ObjectDB.instance == (Object)null) { return false; } if (IsFoodStation(station)) { return false; } if (IsGaldrTable(station)) { return true; } List<Recipe> recipes = ObjectDB.instance.m_recipes; for (int i = 0; i < recipes.Count; i++) { Recipe val = recipes[i]; if (BasicRecipeAllowed(val) && !((Object)(object)val.m_craftingStation == (Object)null) && string.Equals(val.m_craftingStation.m_name, station.m_name, StringComparison.Ordinal)) { return true; } } return false; } private static bool RecipeCanAppear(Recipe recipe, CraftingStation station, bool special) { if (!BasicRecipeAllowed(recipe)) { return false; } if (special) { return true; } if ((Object)(object)recipe.m_craftingStation != (Object)null && (Object)(object)station != (Object)null) { return string.Equals(recipe.m_craftingStation.m_name, station.m_name, StringComparison.Ordinal); } return false; } private static bool BasicRecipeAllowed(Recipe recipe) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Invalid comparison between Unknown and I4 if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || (Object)(object)recipe.m_item == (Object)null || recipe.m_item.m_itemData == null || recipe.m_noCraftOnlyUpgrade || recipe.m_requireOnlyOneIngredient) { return false; } SharedData shared = recipe.m_item.m_itemData.m_shared; if (shared != null && !shared.m_questItem) { return (int)shared.m_itemType != 2; } return false; } private static bool IsFoodStation(CraftingStation station) { if ((Object)(object)station == (Object)null) { return false; } string name = station.m_name; if (!string.Equals(name, "$piece_cauldron", StringComparison.Ordinal) && !string.Equals(name, "$piece_preptable", StringComparison.Ordinal)) { return string.Equals(name, "$piece_meadcauldron", StringComparison.Ordinal); } return true; } private static bool IsGaldrTable(CraftingStation station) { if ((Object)(object)station != (Object)null) { return string.Equals(station.m_name, "$piece_magetable", StringComparison.Ordinal); } return false; } private static bool IsSpecialReverse(CraftingStation station, Recipe recipe) { if (IsGaldrTable(station) && (Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) { return GaldrReversePrefabs.Contains(((Object)((Component)recipe.m_item).gameObject).name); } return false; } private static void AddSpecialReverseRecipes(Player player, CraftingStation station, List<Recipe> source) { if (!IsGaldrTable(station) || (Object)(object)ObjectDB.instance == (Object)null) { return; } List<Recipe> recipes = ObjectDB.instance.m_recipes; for (int i = 0; i < recipes.Count; i++) { Recipe val = recipes[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_item == (Object)null) && val.m_item.m_itemData != null && GaldrReversePrefabs.Contains(((Object)((Component)val.m_item).gameObject).name)) { string name = val.m_item.m_itemData.m_shared.m_name; if (player.IsRecipeKnown(name) && AllMaterialsKnown(player, val) && !source.Contains(val)) { source.Add(val); } } } } private static bool AllMaterialsKnown(Player player, Recipe recipe) { Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && !player.IsMaterialKnown(val.m_resItem.m_itemData.m_shared.m_name)) { return false; } } return true; } private static bool IsStationUsableForPlan(Player player, CraftingStation station, RecyclePlan plan) { if ((Object)(object)player == (Object)null || (Object)(object)station == (Object)null || plan == null || !station.CheckUsable(player, false)) { return false; } if (plan.SpecialReverse) { if (IsGaldrTable(station)) { return station.GetLevel(true) >= plan.RequiredLevel; } return false; } if ((Object)(object)plan.Recipe != (Object)null && (Object)(object)plan.Recipe.m_craftingStation != (Object)null && string.Equals(plan.Recipe.m_craftingStation.m_name, station.m_name, StringComparison.Ordinal)) { return station.GetLevel(true) >= plan.RequiredLevel; } return false; } private static int GetConsumeAmount(ItemData item, Recipe recipe) { if (item == null || (Object)(object)recipe == (Object)null || recipe.m_amount <= 0) { return 0; } if (recipe.m_amount == 1) { return item.m_stack; } if (item.m_stack < recipe.m_amount) { return 0; } int num = Mathf.CeilToInt((float)recipe.m_amount * 1.5f); return Mathf.Min(item.m_stack, num); } private static int GetBatches(RecyclePlan plan) { if (plan == null || (Object)(object)plan.Recipe == (Object)null || plan.Recipe.m_amount <= 0) { return 0; } return Mathf.Max(1, plan.ConsumeAmount / plan.Recipe.m_amount); } private static float GetRecoveryRate(ItemData item) { int nonPotentialQuality = GetNonPotentialQuality(item); if (nonPotentialQuality <= 1) { return 0.8f; } return nonPotentialQuality switch { 2 => 0.85f, 3 => 0.9f, _ => 1f, }; } private static int GetNonPotentialQuality(ItemData item) { return Mathf.Clamp(item.m_quality, 1, Mathf.Max(1, item.m_shared.m_maxQuality)); } private static float GetCraftBonus(InventoryGui gui) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)gui == (Object)null) { return 0f; } return Mathf.Clamp01(((Character)localPlayer).GetSkillFactor((SkillType)107) * gui.m_craftBonusChance); } private static void BuildOutputs(InventoryGui gui, RecyclePlan plan) { int batches = GetBatches(plan); float recoveryRate = GetRecoveryRate(plan.Item); float craftBonus = GetCraftBonus(gui); int nonPotentialQuality = GetNonPotentialQuality(plan.Item); Requirement[] resources = plan.Recipe.m_resources; foreach (Requirement val in resources) { if (val == null || (Object)(object)val.m_resItem == (Object)null || val.m_upgraderResource) { continue; } int num = val.GetAmount(1) * batches; if (num > 0) { RecycleOutput recycleOutput = new RecycleOutput { Item = val.m_resItem }; if (val.m_resItem.m_itemData.IsEquipable()) { recycleOutput.BaseMin = num; recycleOutput.BaseMax = num; } else { ConfigureBaseRecovery(recycleOutput, (float)num * recoveryRate, craftBonus); } int num2 = 0; for (int j = 2; j <= nonPotentialQuality; j++) { num2 += val.GetAmount(j) * batches; } ConfigureUpgradeRecovery(recycleOutput, (float)num2 * craftBonus); MergeOutput(plan.Outputs, recycleOutput); } } } private static void ConfigureBaseRecovery(RecycleOutput output, float exact, float craftBonus) { if (exact <= 0f) { return; } int num = Mathf.FloorToInt(exact); int num2 = Mathf.CeilToInt(exact); if (num == 0 && num2 == 1) { output.BaseMin = 1; output.BaseMax = 1; return; } output.BaseMin = num; output.BaseMax = num2; if (num2 > num) { float num3 = exact - (float)num; output.BaseHighChance = Mathf.Clamp01(num3 + (1f - num3) * craftBonus); } } private static void ConfigureUpgradeRecovery(RecycleOutput output, float exact) { if (!(exact <= 0f)) { int num = Mathf.FloorToInt(exact); int num2 = Mathf.CeilToInt(exact); output.UpgradeMin = num; output.UpgradeMax = num2; if (num2 > num) { output.UpgradeHighChance = Mathf.Clamp01(exact - (float)num); } } } private static void MergeOutput(List<RecycleOutput> outputs, RecycleOutput incoming) { if (incoming == null || (Object)(object)incoming.Item == (Object)null) { return; } string name = incoming.Item.m_itemData.m_shared.m_name; for (int i = 0; i < outputs.Count; i++) { RecycleOutput recycleOutput = outputs[i]; if (!(recycleOutput.Item.m_itemData.m_shared.m_name != name)) { recycleOutput.BaseMin += incoming.BaseMin; recycleOutput.BaseMax += incoming.BaseMax; recycleOutput.BaseHighChance = Mathf.Max(recycleOutput.BaseHighChance, incoming.BaseHighChance); recycleOutput.UpgradeMin += incoming.UpgradeMin; recycleOutput.UpgradeMax += incoming.UpgradeMax; recycleOutput.UpgradeHighChance = Mathf.Max(recycleOutput.UpgradeHighChance, incoming.UpgradeHighChance); return; } } outputs.Add(incoming); } private static void ShowStationLevel(InventoryGui gui, Player player, RecyclePlan plan) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_007c: 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) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) bool flag = plan != null && plan.RequiredLevel > 0; ((Component)gui.m_minStationLevelIcon).gameObject.SetActive(flag); if (!flag) { return; } ((Component)gui.m_minStationLevelText).gameObject.SetActive(true); gui.m_minStationLevelText.text = plan.RequiredLevel.ToString(); Color val = Color.white; if (GuiAccess.MinStationBaseColor != null) { object value = GuiAccess.MinStationBaseColor.GetValue(gui); if (value is Color) { val = (Color)value; } } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); bool flag2 = IsStationUsableForPlan(player, currentCraftingStation, plan); ((Graphic)gui.m_minStationLevelText).color = (flag2 ? val : ((Mathf.Sin(Time.time * 10f) > 0f) ? Color.red : val)); } private static void ShowOutputs(InventoryGui gui, RecyclePlan plan) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) int num = gui.m_recipeRequirementList.Length; int num2 = Mathf.Min(num, plan.Outputs.Count); for (int i = 0; i < num2; i++) { RecycleOutput recycleOutput = plan.Outputs[i]; Transform transform = gui.m_recipeRequirementList[i].transform; Image component = ((Component)transform.Find("res_icon")).GetComponent<Image>(); TMP_Text component2 = ((Component)transform.Find("res_name")).GetComponent<TMP_Text>(); TMP_Text component3 = ((Component)transform.Find("res_amount")).GetComponent<TMP_Text>(); ((Component)component).gameObject.SetActive(true); ((Component)component2).gameObject.SetActive(true); ((Component)component3).gameObject.SetActive(true); component.sprite = recycleOutput.Item.m_itemData.GetIcon(); ((Graphic)component).color = Color.white; string text = (component2.text = Localization.instance.Localize(recycleOutput.Item.m_itemData.m_shared.m_name)); int minAmount = recycleOutput.MinAmount; int maxAmount = recycleOutput.MaxAmount; component3.text = ((minAmount == maxAmount) ? minAmount.ToString() : (minAmount + "-" + maxAmount)); ((Graphic)component3).color = Color.white; UITooltip component4 = ((Component)transform).GetComponent<UITooltip>(); if ((Object)(object)component4 != (Object)null) { component4.m_text = text + "\n" + FormatOutputOdds(recycleOutput); } } for (int j = num2; j < num; j++) { InventoryGui.HideRequirement(gui.m_recipeRequirementList[j].transform); } } private static string BuildRecycleSummary(InventoryGui gui, RecyclePlan plan) { StringBuilder stringBuilder = new StringBuilder(); int num = Mathf.Max(1, plan.Item.m_shared.m_maxQuality); int nonPotentialQuality = GetNonPotentialQuality(plan.Item); int num2 = Mathf.Max(0, plan.Item.m_quality - num); float recoveryRate = GetRecoveryRate(plan.Item); float craftBonus = GetCraftBonus(gui); stringBuilder.Append("<color=orange><b>RECYCLING</b></color>"); stringBuilder.Append("\nAmount: <color=yellow>" + plan.ConsumeAmount + "</color>"); if (plan.Recipe.m_amount > 1) { stringBuilder.Append("\nRecipe batch: " + plan.Recipe.m_amount + " | Max per recycle: " + Mathf.CeilToInt((float)plan.Recipe.m_amount * 1.5f)); } stringBuilder.Append("\nBase recovery: <color=yellow>" + Mathf.RoundToInt(recoveryRate * 100f) + "%</color>"); stringBuilder.Append("\nCrafting rounding bonus: <color=yellow>" + (craftBonus * 100f).ToString("0.#") + "%</color>"); if (nonPotentialQuality > 1) { stringBuilder.Append("\nVanilla quality counted: Q" + nonPotentialQuality); } if (num2 > 0) { stringBuilder.Append("\nPotential +" + num2 + ": <color=#AAAAAA>no extra recycle value</color>"); } stringBuilder.Append("\n\n<color=orange><b>POSSIBLE RETURNS</b></color>"); for (int i = 0; i < plan.Outputs.Count; i++) { RecycleOutput recycleOutput = plan.Outputs[i]; string text = Localization.instance.Localize(recycleOutput.Item.m_itemData.m_shared.m_name); stringBuilder.Append("\n- " + text + ": " + FormatOutputOdds(recycleOutput)); } return stringBuilder.ToString(); } private static string FormatOutputOdds(RecycleOutput output) { SortedDictionary<int, float> sortedDictionary = new SortedDictionary<int, float>(); AddCombinedOdds(sortedDictionary, output.BaseMin, output.BaseMax, output.BaseHighChance, output.UpgradeMin, output.UpgradeMax, output.UpgradeHighChance); if (sortedDictionary.Count == 1) { using SortedDictionary<int, float>.Enumerator enumerator = sortedDictionary.GetEnumerator(); if (enumerator.MoveNext()) { return enumerator.Current.Key.ToString(); } } StringBuilder stringBuilder = new StringBuilder(); bool flag = true; foreach (KeyValuePair<int, float> item in sortedDictionary) { if (!(item.Value < 0.0005f)) { if (!flag) { stringBuilder.Append(" | "); } flag = false; stringBuilder.Append(item.Key + " (" + (item.Value * 100f).ToString("0.#") + "%)"); } } return stringBuilder.ToString(); } private static void AddCombinedOdds(SortedDictionary<int, float> odds, int baseMin, int baseMax, float baseHighChance, int upgradeMin, int upgradeMax, float upgradeHighChance) { float num = ((baseMax > baseMin) ? (1f - baseHighChance) : 1f); float num2 = ((baseMax > baseMin) ? baseHighChance : 0f); float num3 = ((upgradeMax > upgradeMin) ? (1f - upgradeHighChance) : 1f); float num4 = ((upgradeMax > upgradeMin) ? upgradeHighChance : 0f); AddOdds(odds, baseMin + upgradeMin, num * num3); if (upgradeMax > upgradeMin) { AddOdds(odds, baseMin + upgradeMax, num * num4); } if (baseMax > baseMin) { AddOdds(odds, baseMax + upgradeMin, num2 * num3); if (upgradeMax > upgradeMin) { AddOdds(odds, baseMax + upgradeMax, num2 * num4); } } } private static void AddOdds(SortedDictionary<int, float> odds, int amount, float chance) { if (!(chance <= 0f)) { odds.TryGetValue(amount, out var value); odds[amount] = value + chance; } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGuiAwakePatch { [HarmonyPostfix] private static void Postfix(InventoryGui __instance) { RecyclerUi.Initialize(__instance); } } [HarmonyPatch(typeof(InventoryGui), "OnDestroy")] internal static class InventoryGuiDestroyPatch { [HarmonyPrefix] private static void Prefix() { RecyclerUi.Shutdown(); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] internal static class InventoryGuiHidePatch { [HarmonyPrefix] private static void Prefix() { RecyclerUi.LeaveRecycleTab(); } } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] internal static class InventoryGuiCraftingPanelPatch { [HarmonyPostfix] private static void Postfix(InventoryGui __instance) { if (RecyclerUi.Active && (!((Selectable)__instance.m_tabCraft).interactable || !((Selectable)__instance.m_tabUpgrade).interactable)) { RecyclerUi.LeaveRecycleTab(); } RecyclerUi.SyncTabVisibility(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] internal static class InventoryGuiRecipeListPatch { [HarmonyPostfix] private static void Postfix(InventoryGui __instance, List<Recipe> recipes) { RecyclerUi.RebuildRecipeList(__instance, recipes); } } internal static class CraftXpDebt { private static Player _player; private static readonly Dictionary<SkillType, int> Debt = new Dictionary<SkillType, int>(); internal static void Reset() { _player = null; Debt.Clear(); } private static void EnsurePlayer(Player player) { if (!((Object)(object)_player == (Object)(object)player)) { _player = player; Debt.Clear(); } } internal static void Add(Player player, SkillType skill, int amount) { //IL_0009: 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_0030: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && (int)skill != 0 && amount > 0) { EnsurePlayer(player); Debt.TryGetValue(skill, out var value); long num = (long)value + (long)amount; Debt[skill] = (int)((num > int.MaxValue) ? int.MaxValue : num); } } internal static bool Apply(Player player, SkillType skill, ref float value) { //IL_0016: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)_player != (Object)(object)player || (int)skill == 0 || value <= 0f) { return false; } if (!Debt.TryGetValue(skill, out var value2) || value2 <= 0) { return false; } int num = Mathf.Max(0, Mathf.FloorToInt(value + 0.0001f)); if (num <= 0) { return false; } int num2 = Mathf.Min(value2, num); value2 -= num2; value -= num2; if (value2 > 0) { Debt[skill] = value2; } else { Debt.Remove(skill); } return value <= 0f; } } internal struct UpdateRecipeState { internal bool Changed; internal bool Previous; } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class InventoryGuiUpdateRecipePatch { [HarmonyPrefix] private static void Prefix(InventoryGui __instance, out UpdateRecipeState __state) { __state = new UpdateRecipeState { Changed = RecyclerUi.ForceFiveCraftDuration(__instance, out var previous), Previous = previous }; } [HarmonyPostfix] private static void Postfix(InventoryGui __instance, Player player, UpdateRecipeState __state) { RecyclerUi.RestoreMultiCrafting(__instance, __state.Changed, __state.Previous); RecyclerUi.OverrideRecipeDetails(__instance, player); } [HarmonyFinalizer] private static Exception Finalizer(InventoryGui __instance, Exception __exception, UpdateRecipeState __state) { if (__exception != null) { RecyclerUi.RestoreMultiCrafting(__instance, __state.Changed, __state.Previous); } return __exception; } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class InventoryGuiCraftPressedPatch { [HarmonyPrefix] private static bool Prefix(InventoryGui __instance) { return RecyclerUi.HandleCraftPressed(__instance); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")] internal static class InventoryGuiCraftCancelPatch { [HarmonyPostfix] private static void Postfix() { RecyclerUi.HandleCraftCancelled(); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class InventoryGuiDoCraftingPatch { internal static bool NormalCraftActive { get; private set; } [HarmonyPrefix] private static bool Prefix(InventoryGui __instance, Player player) { if (!RecyclerUi.Active) { NormalCraftActive = true; return true; } NormalCraftActive = false; RecyclerUi.CompleteRecycle(__instance, player); return false; } [HarmonyPostfix] private static void Postfix() { NormalCraftActive = false; } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception) { NormalCraftActive = false; return __exception; } } [HarmonyPatch(typeof(Player), "RaiseSkill", new Type[] { typeof(SkillType), typeof(float) })] internal static class PlayerRaiseCraftSkillPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(Player __instance, SkillType skill, ref float value) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!InventoryGuiDoCraftingPatch.NormalCraftActive) { return true; } return !CraftXpDebt.Apply(__instance, skill, ref value); } } }