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 ForgeRework v1.0.0
ForgeRework.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ForgeRework")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ForgeRework")] [assembly: AssemblyTitle("ForgeRework")] [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 ForgeRework { [BepInPlugin("MerlinofMines.ForgeRework", "Forge Rework", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "MerlinofMines.ForgeRework"; internal static ProbabilityCurve Curve = new ProbabilityCurve(); private ConfigEntry<int> startingLevel; private ConfigEntry<int> maxLevel; private ConfigEntry<double> startingProbability; private ConfigEntry<double> minimumProbability; private ConfigEntry<double> dropoff; private Harmony harmony; private void Awake() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Expected O, but got Unknown HashSet<string> savedKeys = ReadSavedKeys(((BaseUnityPlugin)this).Config.ConfigFilePath); startingLevel = BindSetting(savedKeys, "Levels", "Min Level", "StartingLevel", 0, new ConfigDescription("Item quality where probability starts decreasing. Whole number, 0 or higher.", (AcceptableValueBase)(object)new NonNegativeInteger(), Array.Empty<object>())); maxLevel = BindSetting(savedKeys, "Levels", "Max Level", "MaxLevel", 10, new ConfigDescription("Highest reachable item quality. Whole number; 0 disables the cap.", (AcceptableValueBase)(object)new NonNegativeInteger(), Array.Empty<object>())); startingProbability = BindSetting(savedKeys, "Probabilities", "Max Probability", "StartingProbability", 100.0, new ConfigDescription("Upgrade chance at or below Min Level (%).", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 100.0), new object[1] { new ConfigurationManagerAttributes() })); minimumProbability = BindSetting(savedKeys, "Probabilities", "Min Probability", "MinimumProbability", 0.0, new ConfigDescription("Lowest upgrade chance (%). Must not exceed Max Probability.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 100.0), new object[1] { new ConfigurationManagerAttributes() })); dropoff = BindSetting(savedKeys, "Probabilities", "Probability Dropoff Per Level", "DropoffPerLevel", 10.0, new ConfigDescription("The reduction in success probability at each successive level above Min Level, in percentage points.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 100.0), new object[1] { new ConfigurationManagerAttributes() })); ((BaseUnityPlugin)this).Config.Save(); RefreshCurve(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { RefreshCurve(); }; harmony = new Harmony("MerlinofMines.ForgeRework"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Forge Rework loaded: safe failures and configurable success odds."); } private static HashSet<string> ReadSavedKeys(string path) { HashSet<string> hashSet = new HashSet<string>(); if (!File.Exists(path)) { return hashSet; } string text = ""; string[] array = File.ReadAllLines(path); 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; } int num = text2.IndexOf('='); if (num >= 0) { hashSet.Add(text + "/" + text2.Substring(0, num).Trim()); } } return hashSet; } private ConfigEntry<T> BindSetting<T>(HashSet<string> savedKeys, string section, string key, string oldKey, T defaultValue, ConfigDescription description) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description); if (savedKeys.Contains("Probability/" + oldKey)) { ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>("Probability", oldKey, defaultValue, description); if (!savedKeys.Contains(section + "/" + key)) { val.Value = val2.Value; } ((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("Probability", oldKey)); } if (section == "Levels" && key == "Min Level" && savedKeys.Contains("Levels/Starting Level")) { ConfigEntry<T> val3 = ((BaseUnityPlugin)this).Config.Bind<T>("Levels", "Starting Level", defaultValue, description); if (!savedKeys.Contains("Levels/Min Level")) { val.Value = val3.Value; } ((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("Levels", "Starting Level")); } return val; } private void RefreshCurve() { try { Curve = new ProbabilityCurve(startingLevel.Value, startingProbability.Value, minimumProbability.Value, dropoff.Value, maxLevel.Value); } catch (ArgumentException ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Invalid probability settings; retaining last valid curve. " + ex.Message)); } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } internal sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent = true; } internal sealed class NonNegativeInteger : AcceptableValueBase { public NonNegativeInteger() : base(typeof(int)) { } public override object Clamp(object value) { return Math.Max(0, (int)value); } public override bool IsValid(object value) { if (value is int num) { return num >= 0; } return false; } public override string ToDescriptionString() { return "# Acceptable values: non-negative integers"; } } [HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")] internal static class RecipeAvailabilityPatch { private static readonly FieldInfo Rows = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes"); private static readonly PropertyInfo Element = AccessTools.Property(Rows.FieldType.GetGenericArguments()[0], "InterfaceElement"); internal static bool IsMax(Player player, ItemData item) { if (Forge.IsForge(player) && item != null) { return !Plugin.Curve.CanAttempt(item.m_quality); } return false; } private static void Prefix(Player player, ItemData item, ref bool canCraft) { if (IsMax(player, item)) { canCraft = false; } } private static void Postfix(InventoryGui __instance, Player player, ItemData item) { //IL_003e: 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) if (!IsMax(player, item)) { return; } IList list = (IList)Rows.GetValue(__instance); if (list.Count != 0) { object obj = list[list.Count - 1]; Transform obj2 = ((GameObject)Element.GetValue(obj)).transform.Find("icon"); Image val = ((obj2 != null) ? ((Component)obj2).GetComponent<Image>() : null); if ((Object)(object)val != (Object)null) { ((Graphic)val).color = new Color(0.45f, 0.45f, 0.45f, 1f); } } } } internal static class Forge { private static readonly FieldInfo Selected = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly PropertyInfo SelectedItem = AccessTools.Property(Selected.FieldType, "ItemData"); private static readonly PropertyInfo SelectedRecipe = AccessTools.Property(Selected.FieldType, "Recipe"); private static readonly MethodInfo Refresh = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null); private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null); internal static ItemData Item(InventoryGui gui) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown return (ItemData)SelectedItem.GetValue(Selected.GetValue(gui)); } internal static Recipe Recipe(InventoryGui gui) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown return (Recipe)SelectedRecipe.GetValue(Selected.GetValue(gui)); } internal static void RefreshPanel(InventoryGui gui) { Refresh.Invoke(gui, new object[1] { false }); } internal static bool IsForge(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)player.GetCurrentCraftingStation() != (Object)null) { return player.GetCurrentCraftingStation().m_upgrader; } return false; } internal static bool Eligible(Player player, Recipe recipe, ItemData item) { if ((Object)(object)recipe == (Object)null || item == null || !((Humanoid)player).GetInventory().ContainsItem(item)) { return false; } if (!Plugin.Curve.CanAttempt(item.m_quality)) { return false; } if (!player.GetCurrentCraftingStation().CheckUsable(player, false)) { return false; } if (!recipe.m_resources.Any((Requirement r) => r.m_upgraderResource && (Object)(object)r.m_resItem != (Object)null)) { return false; } if (!string.IsNullOrEmpty(item.m_shared.m_dlc) && !DLCMan.instance.IsDLCInstalled(item.m_shared.m_dlc)) { return false; } if (!Free(player)) { return player.HaveRequirements(recipe, false, item.m_quality + 1, 1); } return true; } private static bool Free(Player player) { if (!player.NoCostCheat()) { return ZoneSystem.instance.GetGlobalKey((GlobalKeys)25); } return true; } internal static void Attempt(InventoryGui gui, Player player, Recipe recipe, ItemData item) { //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: 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_01cc: Unknown result type (might be due to invalid IL or missing references) if (!Eligible(player, recipe, item)) { return; } int targetQuality = item.m_quality + 1; int need = default(int); ItemData singleResource = default(ItemData); recipe.GetAmount(targetQuality, ref need, ref singleResource, 1); if (recipe.m_requireOnlyOneIngredient && singleResource == null) { return; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); bool cheatedResources = ((Humanoid)player).GetInventory().ItemCheated(recipe.m_resources, -1, true) || player.NoCostCheat(); ZNetView component = ((Component)currentCraftingStation).GetComponent<ZNetView>(); bool? obj; if (component == null) { obj = null; } else { ZDO zDO = component.GetZDO(); obj = ((zDO != null) ? new bool?(zDO.GetBool(ZDOVars.s_cheated, false)) : ((bool?)null)); } bool? flag = obj; bool cheatedStation = flag == true; switch (Refinement.Attempt(Plugin.Curve, item.m_quality, () => Random.value, delegate { if (!Free(player)) { if (singleResource != null) { ((Humanoid)player).GetInventory().RemoveItem(singleResource.m_shared.m_name, need, singleResource.m_quality, true); } else { player.ConsumeResources(recipe.m_resources, targetQuality, -1, 1); } } }, delegate(int quality) { ((Humanoid)player).UnequipItem(item, true); item.m_quality = quality; item.m_durability = item.GetMaxDurability(); ItemData obj2 = item; obj2.m_cheated |= (cheatedResources || cheatedStation) && !PlayerProfile.s_bypassCheatChecks; InventoryChanged.Invoke(((Humanoid)player).GetInventory(), new object[2] { false, false }); })) { case AttemptResult.Blocked: return; case AttemptResult.Succeeded: ((Character)player).Message((MessageType)2, Localization.instance.Localize("$msg_upgrader_success", new string[2] { item.m_shared.m_name, item.m_quality.ToString() }), 0, (Sprite)null, false); currentCraftingStation.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); break; default: ((Character)player).Message((MessageType)2, "Refinement failed. Your item is unchanged.", 0, (Sprite)null, false); break; } PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); playerProfile.IncrementStat((PlayerStatType)1, 1f, cheatedResources); playerProfile.IncrementStat((PlayerStatType)14, 1f, cheatedResources); Refresh.Invoke(gui, new object[1] { false }); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CraftPatch { private static bool Prefix(InventoryGui __instance, Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem) { if (!Forge.IsForge(player)) { return true; } Forge.Attempt(__instance, player, ___m_craftRecipe, ___m_craftUpgradeItem); return false; } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class StartPatch { private static bool Prefix(InventoryGui __instance, ref Recipe ___m_craftRecipe, ref ItemData ___m_craftUpgradeItem, ref float ___m_craftTimer, ref bool ___m_multiCrafting, ref bool ___m_touchMultiCrafting) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0072: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Forge.IsForge(localPlayer)) { return true; } ItemData val = Forge.Item(__instance); Recipe val2 = Forge.Recipe(__instance); if (___m_craftTimer >= 0f || !Forge.Eligible(localPlayer, val2, val)) { return false; } ___m_craftRecipe = val2; ___m_craftUpgradeItem = val; ___m_multiCrafting = false; ___m_touchMultiCrafting = false; ___m_craftTimer = 0f; localPlayer.GetCurrentCraftingStation().m_craftItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class DisplayPatch { private static InventoryGui lastGui; private static ProbabilityCurve lastCurve; private static void Postfix(InventoryGui __instance, Player player) { if (!Forge.IsForge(player)) { return; } if (lastGui != __instance || lastCurve != Plugin.Curve) { lastGui = __instance; lastCurve = Plugin.Curve; Forge.RefreshPanel(__instance); } ItemData val = Forge.Item(__instance); if (val == null) { return; } if (!Plugin.Curve.CanAttempt(val.m_quality)) { __instance.m_itemCraftType.text = "MAX"; ((Selectable)__instance.m_craftButton).interactable = false; TMP_Text componentInChildren = ((Component)__instance.m_craftButton).GetComponentInChildren<TMP_Text>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "MAX"; } } else { double num = Plugin.Curve.Chance(val.m_quality); __instance.m_itemCraftType.text = $"Chance To Upgrade: {num:0.##}%"; } } } public sealed class ProbabilityCurve { public int StartingLevel { get; } public double StartingProbability { get; } public double MinimumProbability { get; } public double Dropoff { get; } public int MaxLevel { get; } public ProbabilityCurve(int startingLevel = 0, double startingProbability = 100.0, double minimumProbability = 0.0, double dropoff = 10.0, int maxLevel = 10) { if (startingLevel < 0) { throw new ArgumentOutOfRangeException("startingLevel"); } if (maxLevel < 0) { throw new ArgumentOutOfRangeException("maxLevel"); } ValidatePercent(startingProbability, "startingProbability"); ValidatePercent(minimumProbability, "minimumProbability"); ValidatePercent(dropoff, "dropoff"); if (minimumProbability > startingProbability) { throw new ArgumentException("Minimum probability cannot exceed starting probability."); } StartingLevel = startingLevel; StartingProbability = startingProbability; MinimumProbability = minimumProbability; Dropoff = dropoff; MaxLevel = maxLevel; } private static void ValidatePercent(double value, string name) { if (double.IsNaN(value) || double.IsInfinity(value) || value < 0.0 || value > 100.0) { throw new ArgumentOutOfRangeException(name, "Must be a finite percentage from 0 to 100."); } } public double Chance(int currentLevel) { if (currentLevel < 0) { throw new ArgumentOutOfRangeException("currentLevel"); } return Math.Max(MinimumProbability, StartingProbability - Dropoff * (double)Math.Max(0L, (long)currentLevel - (long)StartingLevel)); } public bool CanAttempt(int currentLevel) { if (Chance(currentLevel) > 0.0 && currentLevel < int.MaxValue) { if (MaxLevel != 0) { return currentLevel < MaxLevel; } return true; } return false; } public bool Succeeds(int currentLevel, double roll) { if (double.IsNaN(roll) || double.IsInfinity(roll) || roll < 0.0 || roll > 1.0) { throw new ArgumentOutOfRangeException("roll"); } double num = Chance(currentLevel); if (CanAttempt(currentLevel)) { if (!(num >= 100.0)) { return roll < num / 100.0; } return true; } return false; } } public enum AttemptResult { Blocked, Failed, Succeeded } public static class Refinement { public static AttemptResult Attempt(ProbabilityCurve curve, int currentLevel, Func<double> roll, Action consumeResources, Action<int> upgrade) { if (!curve.CanAttempt(currentLevel)) { return AttemptResult.Blocked; } bool num = curve.Succeeds(currentLevel, roll()); consumeResources(); if (!num) { return AttemptResult.Failed; } upgrade(currentLevel + 1); return AttemptResult.Succeeded; } } }