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 BlessedIdols v1.2.0
BlessedIdols.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; 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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.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 BlessedIdols { internal sealed class ConfigurationManagerAttributes { public string Category; public string DispName; public int? Order; public bool? Browsable; } internal static class Grade { internal enum Unit { Biomes, Percent, Flat } internal struct Bonus { public Unit Unit; public float Value; } internal const string Key = "BlessedIdols.Grade"; internal static ItemData PreviewItem; internal static int PreviewGrade; internal static Bonus Damage => new Bonus { Unit = Plugin.DamageUnit.Value, Value = Plugin.DamagePerGrade.Value }; internal static Bonus Armor => new Bonus { Unit = Plugin.ArmorUnit.Value, Value = Plugin.ArmorPerGrade.Value }; internal static Bonus Block => new Bonus { Unit = Plugin.BlockUnit.Value, Value = Plugin.BlockPerGrade.Value }; internal static float BiomeLevels(SharedData item, int grade) { float value = Plugin.GradesPerBiome.Value; if (!(value > 0f)) { return 0f; } return (float)(grade * Math.Max(0, item.m_maxQuality - 1)) / value; } internal static float Biomes(int grade) { float value = Plugin.GradesPerBiome.Value; if (!(value > 0f)) { return 0f; } return (float)grade / value; } internal static void ApplyDamage(SharedData item, int grade, ref DamageTypes damage) { //IL_002b: 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) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Bonus damage2 = Damage; switch (damage2.Unit) { case Unit.Biomes: { float num = BiomeLevels(item, grade); if (!(num <= 0f)) { DamageTypes damagesPerLevel = item.m_damagesPerLevel; ((DamageTypes)(ref damagesPerLevel)).Modify(num); ((DamageTypes)(ref damage)).Add(damagesPerLevel, 1); } break; } case Unit.Percent: ((DamageTypes)(ref damage)).Modify(Math.Max(0f, 1f + (float)grade * damage2.Value / 100f)); break; default: { float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); if (totalDamage > 0f) { ((DamageTypes)(ref damage)).Modify(Math.Max(0f, (totalDamage + (float)grade * damage2.Value) / totalDamage)); } break; } } } internal static float ApplyArmor(SharedData item, int grade, float armor) { return Apply(Armor, armor, grade, item.m_armorPerLevel * BiomeLevels(item, grade)); } internal static float ApplyBlock(SharedData item, int grade, float block) { return Apply(Block, block, grade, item.m_blockPowerPerLevel * BiomeLevels(item, grade)); } private static float Apply(Bonus bonus, float stat, int grade, float biomeGain) { return Math.Max(0f, (bonus.Unit == Unit.Biomes) ? (stat + biomeGain) : ((bonus.Unit == Unit.Percent) ? (stat * (1f + (float)grade * bonus.Value / 100f)) : (stat + (float)grade * bonus.Value))); } internal static int Of(ItemData item) { if (item == null) { return 0; } if (item == PreviewItem) { return PreviewGrade; } if (item.m_customData == null || !item.m_customData.TryGetValue("BlessedIdols.Grade", out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { return 0; } return result; } internal static void Set(ItemData item, int grade) { if (item.m_customData != null) { if (grade > 0) { item.m_customData["BlessedIdols.Grade"] = grade.ToString(CultureInfo.InvariantCulture); } else { item.m_customData.Remove("BlessedIdols.Grade"); } } } internal static int OldLevels(ItemData item) { return Math.Max(0, item.m_quality - item.m_shared.m_maxQuality); } internal static bool IsArmor(SharedData item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if ((int)item.m_itemType != 6 && (int)item.m_itemType != 7 && (int)item.m_itemType != 11) { return (int)item.m_itemType == 17; } return true; } internal static bool IsShield(SharedData item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 return (int)item.m_itemType == 5; } private static string Number(float value) { return value.ToString("0.##", CultureInfo.InvariantCulture); } internal static string Describe(SharedData item, int grade) { string text; Bonus bonus; float num; if (IsArmor(item)) { text = "armour"; bonus = Armor; num = item.m_armorPerLevel; } else if (IsShield(item)) { text = "block power"; bonus = Block; num = item.m_blockPowerPerLevel; } else { text = "damage"; bonus = Damage; num = ((DamageTypes)(ref item.m_damagesPerLevel)).GetTotalDamage(); } if (bonus.Unit == Unit.Biomes) { float num2 = Biomes(grade); float num3 = num * BiomeLevels(item, grade); if (!(num3 <= 0f)) { return string.Format("+{0} ({1} biome{2}, +{3} {4})", grade, Number(num2), (num2 == 1f) ? "" : "s", Number(num3), text); } return $"+{grade}"; } float num4 = bonus.Value * (float)grade; if (num4 != 0f) { return string.Format("+{0} (+{1}{2} {3})", grade, Number(num4), (bonus.Unit == Unit.Percent) ? "%" : "", text); } return $"+{grade}"; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class DoCraftingPatch { private static bool Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem) { Refinement.Current = null; Refinement.Upgrading = null; if ((Object)(object)___m_craftRecipe == (Object)null || ___m_craftUpgradeItem == null) { return true; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { Refinement.Upgrading = ___m_craftUpgradeItem; Refinement.UpgradingPrefab = ((Object)((Component)___m_craftRecipe.m_item).gameObject).name; return true; } if (Refinement.AtLimit(___m_craftUpgradeItem)) { ((Character)player).Message((MessageType)2, $"Maximum grade reached: +{Refinement.Limit}", 0, (Sprite)null, false); return false; } Requirement val = Refinement.IdolOf(___m_craftRecipe); if (val != null) { Refinement.Begin(___m_craftRecipe, ___m_craftUpgradeItem, val); } return true; } private static void Finalizer(Player player) { Refinement.End(player); } } [HarmonyPatch(typeof(Player), "GetItemUpgradeChance")] internal static class UpgradeRollPatch { private static readonly Random Fresh = new Random(); private static void Postfix(ref float __result) { Attempt current = Refinement.Current; if (current != null && !current.Rolled && current.Faces > 0) { __result = Refinement.RollDie(current, Fresh); } Refinement.Rolled(__result); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool), typeof(bool), typeof(bool) })] internal static class ReaddPatch { private static void Prefix(string name, ref int quality, ref long crafterID, ref string crafterName) { Refinement.Readding(name, ref quality, ref crafterID, ref crafterName); } private static void Postfix(string name, ItemData __result) { Refinement.Readded(name, __result); } } [HarmonyPatch(typeof(Player), "Message")] internal static class ResultMessagePatch { private static bool Prefix(MessageType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)type == 2 && Refinement.Current != null) { return !Refinement.Current.Rolled; } return true; } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class RefineTimerPatch { [HarmonyPriority(200)] private static void Prefix(Player player, ref float dt, float ___m_craftTimer, ItemData ___m_craftUpgradeItem) { float value = Plugin.RefineSpeed.Value; if (value != 1f && !(___m_craftTimer < 0f) && ___m_craftUpgradeItem != null && !((Object)(object)player == (Object)null)) { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if (!((Object)(object)currentCraftingStation == (Object)null) && currentCraftingStation.m_upgrader) { dt *= value; } } } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class ForgePanelPatch { private static void Postfix(InventoryGui __instance, Player player) { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { return; } Refinement.Selected(__instance, out var recipe, out var item); if (!((Object)(object)recipe == (Object)null) && item != null) { int num = Refinement.GradeOf(item); bool flag = Refinement.AtLimit(item); if (num > 0) { __instance.m_recipeName.text = $"+{num} {__instance.m_recipeName.text}"; } Grade.PreviewItem = item; Grade.PreviewGrade = (flag ? num : (num + 1)); try { string tooltip = ItemData.GetTooltip(item, Refinement.BaseQuality(item), true, (float)Game.m_worldLevel, -1, false); __instance.m_recipeDecription.text = Localization.instance.Localize(tooltip); } finally { Grade.PreviewItem = null; } string text = Refinement.Describe(recipe, item); if (text != null) { __instance.m_itemCraftType.text = text; } if (flag) { ((Selectable)__instance.m_craftButton).interactable = false; } } } } [HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")] internal static class RecipeListPatch { private static void Postfix(InventoryGui __instance, Player player, ItemData item) { if (item == null) { return; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { return; } RectTransform recipeListRoot = __instance.m_recipeListRoot; if (((Transform)recipeListRoot).childCount != 0) { Transform obj = ((Transform)recipeListRoot).GetChild(((Transform)recipeListRoot).childCount - 1).Find("QualityLevel"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if ((Object)(object)val != (Object)null) { val.text = "+" + Refinement.GradeOf(item); } } } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] internal static class TooltipPatch { private static void Postfix(ItemData item, int qualityLevel, bool crafting, ref string __result) { if (item == null || __result == null) { return; } int num = Grade.Of(item); int num2 = ((!crafting && qualityLevel == item.m_quality) ? Grade.OldLevels(item) : 0); if (num > 0 || num2 > 0) { string text = ""; if (num2 > 0) { text += $" <color=yellow>(+{num2} from earlier refining, becomes grade +{num2 + num} at the forge)</color>"; } if (num > 0) { text = text + "\nGrade: <color=orange>" + Grade.Describe(item.m_shared, num) + "</color>"; } string text2 = $"\n$item_quality: <color=orange>{qualityLevel}</color>"; int num3 = __result.IndexOf(text2, StringComparison.Ordinal); __result = ((num3 >= 0) ? __result.Insert(num3 + text2.Length, text) : (__result + text)); } } } [HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")] internal static class TooltipTitlePatch { private static bool Prefix(ItemData item, UITooltip tooltip, RectTransform ___m_tooltipAnchor) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) int num = Grade.Of(item); if (num <= 0) { return true; } tooltip.Set($"+{num} {item.m_shared.m_name}", item.GetTooltip(-1), ___m_tooltipAnchor, default(Vector2)); return false; } } [HarmonyPatch(typeof(ItemData), "GetDamage", new Type[] { typeof(int), typeof(float) })] internal static class GradeDamagePatch { private static void Postfix(ItemData __instance, ref DamageTypes __result) { int num = Grade.Of(__instance); if (num > 0 && !Grade.IsArmor(__instance.m_shared) && !Grade.IsShield(__instance.m_shared)) { Grade.ApplyDamage(__instance.m_shared, num, ref __result); } } } [HarmonyPatch(typeof(ItemData), "GetArmor", new Type[] { typeof(int), typeof(float) })] internal static class GradeArmorPatch { private static void Postfix(ItemData __instance, ref float __result) { int num = Grade.Of(__instance); if (num > 0 && Grade.IsArmor(__instance.m_shared)) { __result = Grade.ApplyArmor(__instance.m_shared, num, __result); } } } [HarmonyPatch(typeof(ItemData), "GetBaseBlockPower", new Type[] { typeof(int) })] internal static class GradeBlockPatch { private static void Postfix(ItemData __instance, ref float __result) { int num = Grade.Of(__instance); if (num > 0 && Grade.IsShield(__instance.m_shared)) { __result = Grade.ApplyBlock(__instance.m_shared, num, __result); } } } [BepInPlugin("vh.blessedidols", "BlessedIdols", "1.2.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "vh.blessedidols"; public const string Name = "BlessedIdols"; public const string Version = "1.2.0"; private const string Rules = "1. Refinement"; private const string Bonuses = "2. Bonus per grade"; private const string Debug = "3. Debug"; internal const int MaxStages = 10; private static readonly int[] DefaultChances = new int[10] { 80, 70, 60, 50, 40, 30, 20, 10, 10, 10 }; internal static ManualLogSource Log; internal static ConfigEntry<int> Stages; internal static readonly ConfigEntry<int>[] StageGrades = new ConfigEntry<int>[10]; internal static readonly ConfigEntry<int>[] StageChances = new ConfigEntry<int>[10]; internal static ConfigEntry<bool> Checkpoints; internal static ConfigEntry<bool> TrueRandom; internal static ConfigEntry<float> RefineSpeed; internal static ConfigEntry<float> GradesPerBiome; internal static ConfigEntry<Grade.Unit> DamageUnit; internal static ConfigEntry<Grade.Unit> ArmorUnit; internal static ConfigEntry<Grade.Unit> BlockUnit; internal static ConfigEntry<float> DamagePerGrade; internal static ConfigEntry<float> ArmorPerGrade; internal static ConfigEntry<float> BlockPerGrade; internal static ConfigEntry<bool> DebugLog; private static readonly List<Action> Visibility = new List<Action>(); private Harmony _harmony; private FileSystemWatcher _watcher; private DateTime _lastReload; private static ConfigurationManagerAttributes Display(string category, string name, int order) { return new ConfigurationManagerAttributes { Category = category, DispName = name, Order = order }; } private static ConfigurationManagerAttributes Display(string category, string name, int order, Func<bool> shown) { ConfigurationManagerAttributes display = Display(category, name, order); Visibility.Add(delegate { display.Browsable = shown(); }); return display; } private void Awake() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Expected O, but got Unknown //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Expected O, but got Unknown //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Expected O, but got Unknown //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Expected O, but got Unknown //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Expected O, but got Unknown //IL_0450: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Expected O, but got Unknown //IL_04c0: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Expected O, but got Unknown //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0509: Expected O, but got Unknown //IL_0549: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Stages = ((BaseUnityPlugin)this).Config.Bind<int>("1. Refinement", "Stages", 3, new ConfigDescription("How many stages the grades are split into. Refining at the Forge of Potential gives an item a grade (+1, +2, ...) on top of its quality, which stays as it is. Each stage has its own number of grades and success chance, and each stage you add gets its own sliders below. The stages follow one another from +1, and the highest grade is the end of the last one: 3 stages of 3 grades are +1 to +3, +4 to +6 and +7 to +9.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[1] { Display("1. Refinement", "Stages", 100) })); for (int i = 0; i < 10; i++) { int stage = i + 1; StageGrades[i] = ((BaseUnityPlugin)this).Config.Bind<int>("1. Refinement", $"Stage{stage}Grades", 3, new ConfigDescription($"How many grades stage {stage} has. With Checkpoints on, an item keeps the stage's last grade " + "once it has reached it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[1] { Display("1. Refinement", $"Stage {stage}: grades", 90 - 2 * i, () => stage <= Stages.Value) })); StageChances[i] = ((BaseUnityPlugin)this).Config.Bind<int>("1. Refinement", $"Stage{stage}Chance", DefaultChances[i], new ConfigDescription($"Success chance in percent of each refinement to a grade in stage {stage}. In vanilla every " + "Idol has 65% and every failure destroys the item.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { Display("1. Refinement", $"Stage {stage}: chance (%)", 89 - 2 * i, () => stage <= Stages.Value) })); } Checkpoints = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Refinement", "StageCheckpoints", true, new ConfigDescription("On: a failed refinement never takes an item below a stage it has completed. Its grade drops back to that stage's last grade (with 3 stages of 3: +3 or +6), or to +0 in the first stage, and a failure on a stage's last grade keeps it. Off: every failure resets to +0. Either way the item never breaks; in vanilla every failed refinement destroys it.", (AcceptableValueBase)null, new object[1] { Display("1. Refinement", "Checkpoints", 2) })); TrueRandom = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Refinement", "TrueRandom", true, new ConfigDescription("Roll every attempt fresh, as a whole number: 1 to 10 when the chance is a multiple of 10%, else 1 to 100, succeeding at the chance or below (at 70%, 1 to 7). Each roll is independent of everything before it, and a failed attempt can be retried by quitting before the game saves. Off is vanilla's roll: each character has a fixed sequence of rolls per item type and target quality, seeded from the character's name and saved with the character, so results are decided in advance and quitting without saving to retry gives the same roll again.", (AcceptableValueBase)null, new object[1] { Display("1. Refinement", "True random rolls", 1) })); RefineSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("1. Refinement", "Speed", 2f, new ConfigDescription("How fast a refinement at the Forge of Potential runs, as a multiple of vanilla. 2 takes half the time, 1 is vanilla. Vanilla takes 8 seconds plus 1 per quality level the item aims for, less up to 60% from your crafting skill. Only refining is sped up, not the forge's other crafts or other stations. A change applies at once, even to a refinement under way.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { Display("1. Refinement", "Refinement speed (x)", 0) })); GradesPerBiome = ((BaseUnityPlugin)this).Config.Bind<float>("2. Bonus per grade", "GradesPerBiome", 6f, new ConfigDescription("How many grades make one biome of progress, for the bonuses set to Biomes below. One biome is what the item's normal upgrades add from quality 1 to its maximum (in Valheim an item at its maximum is about as strong as the next biome's item at quality 1): +18 slash for a Black Metal sword, +6 armour for a Padded Cuirass, +12 block power for a Black Metal Shield. Each grade adds an equal share of it, to every damage type as quality would. 6: +6 is one biome, +9 one and a half.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Display("2. Bonus per grade", "Grades per biome", 10) })); DamageUnit = ((BaseUnityPlugin)this).Config.Bind<Grade.Unit>("2. Bonus per grade", "DamageBonus", Grade.Unit.Biomes, new ConfigDescription("How grades add to a weapon's or tool's damage. Biomes: by Grades per biome. Percent: Damage per grade percent of its damage at its quality, every damage type alike. Flat: Damage per grade points of total damage, spread over its damage types in proportion.", (AcceptableValueBase)null, new object[1] { Display("2. Bonus per grade", "Damage bonus", 9) })); DamagePerGrade = ((BaseUnityPlugin)this).Config.Bind<float>("2. Bonus per grade", "DamagePerGrade", 2f, new ConfigDescription("What each grade adds to damage, in percent with Percent (2: +18% at +9) or in points with Flat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Display("2. Bonus per grade", "Damage per grade", 8, () => DamageUnit.Value != Grade.Unit.Biomes) })); ArmorUnit = ((BaseUnityPlugin)this).Config.Bind<Grade.Unit>("2. Bonus per grade", "ArmorBonus", Grade.Unit.Biomes, new ConfigDescription("How grades add to a helmet's, chest piece's, leg piece's or cape's armour. Biomes: by Grades per biome. Percent: Armour per grade percent of it. Flat: Armour per grade points.", (AcceptableValueBase)null, new object[1] { Display("2. Bonus per grade", "Armour bonus", 7) })); ArmorPerGrade = ((BaseUnityPlugin)this).Config.Bind<float>("2. Bonus per grade", "ArmorPerGrade", 2f, new ConfigDescription("What each grade adds to armour, in percent with Percent or in points with Flat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Display("2. Bonus per grade", "Armour per grade", 6, () => ArmorUnit.Value != Grade.Unit.Biomes) })); BlockUnit = ((BaseUnityPlugin)this).Config.Bind<Grade.Unit>("2. Bonus per grade", "BlockBonus", Grade.Unit.Biomes, new ConfigDescription("How grades add to a shield's block power. Biomes: by Grades per biome. Percent: Block power per grade percent of it. Flat: Block power per grade points. Weapons' own block power isn't changed.", (AcceptableValueBase)null, new object[1] { Display("2. Bonus per grade", "Shield block power bonus", 5) })); BlockPerGrade = ((BaseUnityPlugin)this).Config.Bind<float>("2. Bonus per grade", "BlockPerGrade", 2f, new ConfigDescription("What each grade adds to a shield's block power, in percent with Percent or in points with Flat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Display("2. Bonus per grade", "Block power per grade", 4, () => BlockUnit.Value != Grade.Unit.Biomes) })); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Debug", "DebugLog", false, new ConfigDescription("Write every refinement to BepInEx\\LogOutput.log, with the item, its grade, the roll, the chance and the result. For troubleshooting; leave it off otherwise.", (AcceptableValueBase)null, new object[1] { Display("3. Debug", "Debug log", 1) })); DropOldSettings(); UpdateVisibility(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { UpdateVisibility(); }; _harmony = new Harmony("vh.blessedidols"); _harmony.PatchAll(); SetupWatcher(); Log.LogInfo((object)"Loaded."); } private static void UpdateVisibility() { foreach (Action item in Visibility) { item(); } } private void DropOldSettings() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown if (AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config, null) is Dictionary<ConfigDefinition, string> dictionary) { bool flag = false; string[] array = new string[3] { "Checkpoints", "SuccessChances", "MaxGrade" }; foreach (string text in array) { flag |= dictionary.Remove(new ConfigDefinition("1. Refinement", text)); } array = new string[3] { "Damage", "Armor", "Block" }; foreach (string text2 in array) { flag |= dictionary.Remove(new ConfigDefinition("2. Bonus per grade", text2)); } if (flag) { ((BaseUnityPlugin)this).Config.Save(); } } } private void OnDestroy() { _watcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _watcher.Changed += ReloadConfig; _watcher.Created += ReloadConfig; _watcher.Renamed += ReloadConfig; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReloadConfig(object sender, FileSystemEventArgs e) { if ((DateTime.Now - _lastReload).TotalSeconds < 1.0 || !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } _lastReload = DateTime.Now; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Configuration reloaded."); } catch (Exception ex) { Log.LogError((object)("Error reloading configuration: " + ex.Message)); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; } } } internal sealed class Attempt { public string Prefab; public ItemData Original; public int Quality; public int From; public int Fallback; public int Chance; public int Faces; public int Face; public SharedData Idol; public float IdolChance; public float IdolBreak; public bool Rolled; public bool Succeeded; } internal static class Refinement { internal static Attempt Current; internal static ItemData Upgrading; internal static string UpgradingPrefab; private static readonly FieldInfo SelectedRecipe = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly PropertyInfo SelectedRecipeRecipe = AccessTools.Property(SelectedRecipe.FieldType, "Recipe"); private static readonly PropertyInfo SelectedRecipeItem = AccessTools.Property(SelectedRecipe.FieldType, "ItemData"); internal static int Limit { get { int num = 0; for (int i = 0; i < Plugin.Stages.Value; i++) { num += Plugin.StageGrades[i].Value; } return num; } } internal static int GradeOf(ItemData item) { return Grade.Of(item) + Grade.OldLevels(item); } internal static int BaseQuality(ItemData item) { return Math.Min(item.m_quality, Math.Max(1, item.m_shared.m_maxQuality)); } private static int StageOf(int grade) { int num = 0; for (int i = 0; i < Plugin.Stages.Value; i++) { if (grade <= (num += Plugin.StageGrades[i].Value)) { return i; } } return Plugin.Stages.Value - 1; } internal static int Fallback(int grade) { if (!Plugin.Checkpoints.Value) { return 0; } int result = 0; int i = 0; int num = 0; for (; i < Plugin.Stages.Value; i++) { if ((num += Plugin.StageGrades[i].Value) > grade) { break; } result = num; } return result; } internal static int Chance(int grade) { return Plugin.StageChances[StageOf(grade + 1)].Value; } internal static bool AtLimit(ItemData item) { return GradeOf(item) >= Limit; } internal static Requirement IdolOf(Recipe recipe) { return ((IEnumerable<Requirement>)recipe.m_resources).FirstOrDefault((Func<Requirement, bool>)((Requirement r) => r.m_upgraderResource && (Object)(object)r.m_resItem != (Object)null)); } internal static void Selected(InventoryGui gui, out Recipe recipe, out ItemData item) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown object value = SelectedRecipe.GetValue(gui); recipe = (Recipe)SelectedRecipeRecipe.GetValue(value); item = (ItemData)SelectedRecipeItem.GetValue(value); } internal static string Describe(Recipe recipe, ItemData item) { int num = GradeOf(item); if (AtLimit(item)) { return $"Maximum grade reached: <color=orange>+{Limit}</color>"; } if (IdolOf(recipe) == null) { return null; } int num2 = Fallback(num); string text = ((num2 == num) ? $"stays at +{num}" : $"back to +{num2}"); return $"+{num} to +{num + 1}: <color=orange>{Chance(num)}%</color>. Failure: <color=orange>{text}</color>. " + $"Max <color=orange>+{Limit}</color>"; } internal static void Begin(Recipe recipe, ItemData item, Requirement idol) { SharedData shared = idol.m_resItem.m_itemData.m_shared; int num = GradeOf(item); int num2 = Chance(num); int num3 = (Plugin.TrueRandom.Value ? ((num2 % 10 == 0) ? 10 : 100) : 0); Current = new Attempt { Prefab = ((Object)((Component)recipe.m_item).gameObject).name, Original = item, Quality = BaseQuality(item), From = num, Fallback = Fallback(num), Chance = num2, Faces = num3, Idol = shared, IdolChance = shared.m_upgradeChance, IdolBreak = shared.m_breakChance }; shared.m_upgradeChance = ((num3 > 0) ? (((float)(num2 * num3 / 100) + 0.5f) / (float)num3) : ((num2 > 0) ? ((float)num2 / 100f) : (-1f))); shared.m_breakChance = -1f; } internal static float RollDie(Attempt attempt, Random random) { attempt.Face = random.Next(1, attempt.Faces + 1); return (float)attempt.Face / (float)attempt.Faces; } internal static void Rolled(float roll) { Attempt current = Current; if (current != null && !current.Rolled) { current.Rolled = true; current.Succeeded = current.Idol.m_upgradeChance >= roll; if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)($"Refining {current.Prefab} +{current.From}: " + ((current.Faces > 0) ? $"rolled {current.Face} of {current.Faces}, needs {current.Chance * current.Faces / 100} or less ({current.Chance}%), " : $"vanilla roll {roll:0.000}, needs {(float)current.Chance / 100f:0.00} or less, ") + (current.Succeeded ? $"success, now +{current.From + 1}." : $"failure, back to +{current.Fallback}."))); } } } internal static void Readding(string name, ref int quality, ref long crafterID, ref string crafterName) { Attempt current = Current; if (current != null && current.Rolled && !(name != current.Prefab)) { quality = current.Quality; crafterID = current.Original.m_crafterID; crafterName = current.Original.m_crafterName; } } internal static void Readded(string name, ItemData item) { if (item == null) { return; } if (Upgrading != null && name == UpgradingPrefab) { CopyCustomData(Upgrading, item); Upgrading = null; return; } Attempt current = Current; if (current != null && current.Rolled && !(name != current.Prefab)) { CopyCustomData(current.Original, item); Grade.Set(item, current.Succeeded ? (current.From + 1) : current.Fallback); } } private static void CopyCustomData(ItemData from, ItemData to) { if (from.m_customData == null || to.m_customData == null) { return; } foreach (KeyValuePair<string, string> customDatum in from.m_customData) { to.m_customData[customDatum.Key] = customDatum.Value; } } internal static void End(Player player) { Upgrading = null; Attempt current = Current; Current = null; if (current != null) { current.Idol.m_upgradeChance = current.IdolChance; current.Idol.m_breakChance = current.IdolBreak; if (current.Rolled) { string arg = Localization.instance.Localize(current.Original.m_shared.m_name); string text = (current.Succeeded ? $"{arg} is now +{current.From + 1}" : ((current.Fallback == current.From) ? $"Refinement failed: {arg} stays at +{current.Fallback}" : $"Refinement failed: {arg} is back to +{current.Fallback}")); ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, true); } } } } }