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 WiresForgeOfGoodtential v0.1.15
WiresForgeOfGoodtential.dll
Decompiled 6 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; 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("WiresForgeOfGoodtential")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WiresForgeOfGoodtential")] [assembly: AssemblyTitle("WiresForgeOfGoodtential")] [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 WiresForgeOfGoodtential { internal static class ForgeContext { internal static bool Active; internal static Player Player; internal static ItemData OriginalItem; internal static ItemData UpgraderResource; internal static Recipe Recipe; internal static Requirement[] OriginalResources; internal static bool OriginalRequireOnlyOneIngredient; internal static Vector2i OriginalPosition; internal static string CraftedPrefabName; internal static string SharedItemName; internal static int NaturalMax; internal static int OriginalQuality; internal static int AttemptedQuality; internal static int TargetQuality; internal static int Delta; internal static ForgeOutcome Outcome; internal static ForgeOdds Odds; internal static float OldUpgradeChance; internal static float OldBreakChance; internal static CraftingStation Station; internal static EffectList OldFailEffects; internal static bool AttemptExecuted; internal static void Restore() { if (UpgraderResource != null && UpgraderResource.m_shared != null) { UpgraderResource.m_shared.m_upgradeChance = OldUpgradeChance; UpgraderResource.m_shared.m_breakChance = OldBreakChance; } if ((Object)(object)Recipe != (Object)null && OriginalResources != null) { Recipe.m_resources = OriginalResources; Recipe.m_requireOnlyOneIngredient = OriginalRequireOnlyOneIngredient; } if ((Object)(object)Station != (Object)null && OldFailEffects != null) { Station.m_craftItemDoneFailEffects = OldFailEffects; } ClearFields(); } internal static void ClearFields() { Active = false; Player = null; OriginalItem = null; UpgraderResource = null; Recipe = null; OriginalResources = null; OriginalRequireOnlyOneIngredient = false; CraftedPrefabName = null; SharedItemName = null; NaturalMax = 0; OriginalQuality = 0; AttemptedQuality = 0; TargetQuality = 0; Delta = 0; Outcome = ForgeOutcome.None; Odds = null; Station = null; OldFailEffects = null; AttemptExecuted = false; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class ForgeCraftPatch { [HarmonyPrefix] private static void Prefix(Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem) { //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Expected O, but got Unknown ForgeContext.Restore(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)___m_craftRecipe == (Object)null || ___m_craftUpgradeItem == null) { return; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { return; } ForgeSelectionSnapshot pending = ForgeSelection.GetPending(___m_craftRecipe, ___m_craftUpgradeItem); if (pending == null) { return; } ItemDrop idolDrop = ForgeData.GetIdolDrop(pending.Family, pending.IdolTier); if ((Object)(object)idolDrop == (Object)null || idolDrop.m_itemData == null || idolDrop.m_itemData.m_shared == null) { return; } ItemData trophy = ((pending.IdolTier < pending.RequiredTier) ? null : pending.Trophy); Requirement[] resources = ___m_craftRecipe.m_resources; Requirement[] array = BuildAttemptRequirements(resources, idolDrop, trophy); if (array == null) { return; } int num = Mathf.Max(1, ___m_craftRecipe.m_item.m_itemData.m_shared.m_maxQuality); int quality = ___m_craftUpgradeItem.m_quality; int potential = Mathf.Max(0, quality - num); ForgeOdds odds = ForgeRules.GetOdds(potential, pending.RequiredTier, pending.IdolTier, trophy, pending.ItemReferencePrestige); ForgeOutcome forgeOutcome = ForgeRules.RollOutcome(odds); int num2 = quality; int delta = 0; switch (forgeOutcome) { case ForgeOutcome.Success: { int num4 = ForgeRules.RollSuccessGain(odds.JackpotChance); num2 = quality + num4; delta = num4; if (num4 > 1) { forgeOutcome = ForgeOutcome.Jackpot; } break; } case ForgeOutcome.Ignore: num2 = quality; break; case ForgeOutcome.Loss: { int num3 = ForgeRules.RollLossLevels(potential, odds.CascadeChance); num2 = Mathf.Max(num, quality - num3); delta = num2 - quality; break; } case ForgeOutcome.CompleteLoss: num2 = num; delta = num2 - quality; break; } ForgeContext.Active = true; ForgeContext.Player = localPlayer; ForgeContext.OriginalItem = ___m_craftUpgradeItem; ForgeContext.UpgraderResource = idolDrop.m_itemData; ForgeContext.Recipe = ___m_craftRecipe; ForgeContext.OriginalResources = resources; ForgeContext.OriginalRequireOnlyOneIngredient = ___m_craftRecipe.m_requireOnlyOneIngredient; ForgeContext.OriginalPosition = ___m_craftUpgradeItem.m_gridPos; ForgeContext.CraftedPrefabName = ((Object)((Component)___m_craftRecipe.m_item).gameObject).name; ForgeContext.SharedItemName = ___m_craftUpgradeItem.m_shared.m_name; ForgeContext.NaturalMax = num; ForgeContext.OriginalQuality = quality; ForgeContext.AttemptedQuality = quality + 1; ForgeContext.TargetQuality = num2; ForgeContext.Delta = delta; ForgeContext.Outcome = forgeOutcome; ForgeContext.Odds = odds; ForgeContext.OldUpgradeChance = idolDrop.m_itemData.m_shared.m_upgradeChance; ForgeContext.OldBreakChance = idolDrop.m_itemData.m_shared.m_breakChance; ForgeContext.Station = currentCraftingStation; ___m_craftRecipe.m_resources = array; ___m_craftRecipe.m_requireOnlyOneIngredient = false; switch (forgeOutcome) { case ForgeOutcome.Success: case ForgeOutcome.Jackpot: idolDrop.m_itemData.m_shared.m_upgradeChance = 2f; idolDrop.m_itemData.m_shared.m_breakChance = -1f; break; case ForgeOutcome.Destroyed: idolDrop.m_itemData.m_shared.m_upgradeChance = -1f; idolDrop.m_itemData.m_shared.m_breakChance = 2f; break; default: ForgeContext.OldFailEffects = currentCraftingStation.m_craftItemDoneFailEffects; currentCraftingStation.m_craftItemDoneFailEffects = new EffectList(); idolDrop.m_itemData.m_shared.m_upgradeChance = -1f; idolDrop.m_itemData.m_shared.m_breakChance = -1f; break; } } [HarmonyPostfix] private static void Postfix(EffectList ___m_craftBonusEffect) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (!ForgeContext.Active) { return; } bool flag = false; try { Player player = ForgeContext.Player; if ((Object)(object)player == (Object)null || ForgeContext.OriginalItem == null) { return; } flag = (ForgeContext.AttemptExecuted = !((Humanoid)player).GetInventory().ContainsItem(ForgeContext.OriginalItem)); if (flag) { if (ForgeContext.Outcome == ForgeOutcome.Ignore || ForgeContext.Outcome == ForgeOutcome.Loss || ForgeContext.Outcome == ForgeOutcome.CompleteLoss) { PlaySafeFailureEffect(player); } if (ForgeContext.Outcome == ForgeOutcome.Jackpot && ___m_craftBonusEffect != null) { ___m_craftBonusEffect.Create(((Component)player).transform.position + Vector3.up, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } ShowResult(player); } } finally { ForgeContext.Restore(); if (flag) { ForgeSelection.AfterAttempt(); } else { ForgeSelection.CancelPending(); } } } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception) { if (__exception != null && ForgeContext.Active) { ForgeContext.Restore(); ForgeSelection.CancelPending(); } return __exception; } private static Requirement[] BuildAttemptRequirements(Requirement[] original, ItemDrop selectedIdol, ItemData trophy) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown if (original == null) { return null; } List<Requirement> list = new List<Requirement>(); bool flag = false; foreach (Requirement val in original) { if (val != null) { if (!flag && val.m_upgraderResource && (Object)(object)val.m_resItem != (Object)null && ForgeData.TryParseIdol(val.m_resItem, out var _, out var _)) { list.Add(CloneRequirement(val, selectedIdol)); flag = true; } else { list.Add(val); } } } if (!flag) { return null; } if (trophy != null) { ItemDrop val2 = ResolveDrop(trophy); if ((Object)(object)val2 == (Object)null) { return null; } Requirement val3 = new Requirement(); val3.m_resItem = val2; val3.m_amount = 1; val3.m_amountPerLevel = 0; val3.m_upgraderResource = true; val3.m_recover = false; list.Add(val3); } return list.ToArray(); } private static Requirement CloneRequirement(Requirement original, ItemDrop replacement) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown return new Requirement { m_resItem = replacement, m_amount = original.m_amount, m_extraAmountOnlyOneIngredient = original.m_extraAmountOnlyOneIngredient, m_amountPerLevel = original.m_amountPerLevel, m_upgraderResource = original.m_upgraderResource, m_recover = original.m_recover }; } private static ItemDrop ResolveDrop(ItemData item) { if (item == null) { return null; } if ((Object)(object)item.m_dropPrefab != (Object)null) { ItemDrop component = item.m_dropPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { return component; } } if ((Object)(object)ObjectDB.instance != (Object)null && item.m_shared != null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.m_shared); if ((Object)(object)itemPrefab != (Object)null) { return itemPrefab.GetComponent<ItemDrop>(); } } return null; } private static void PlaySafeFailureEffect(Player player) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || ForgeContext.OldFailEffects == null) { return; } GameObject[] array = ForgeContext.OldFailEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null) { continue; } MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val2 in componentsInChildren) { if (val2 is IProjectile) { ((Behaviour)val2).enabled = false; Object.Destroy((Object)(object)val2); } } } } private static void ShowResult(Player player) { string text = ((Localization.instance != null) ? Localization.instance.Localize(ForgeContext.SharedItemName) : ForgeContext.SharedItemName); switch (ForgeContext.Outcome) { case ForgeOutcome.Jackpot: ((Character)player).Message((MessageType)2, "Exceptional refinement! " + text + " reached quality " + ForgeContext.TargetQuality + ".", 0, (Sprite)null, true); break; case ForgeOutcome.Ignore: ((Character)player).Message((MessageType)2, "Refinement failed. " + text + " remains at quality " + ForgeContext.OriginalQuality + ".", 0, (Sprite)null, true); break; case ForgeOutcome.Loss: _ = ForgeContext.Delta; ((Character)player).Message((MessageType)2, "Refinement failed. " + text + " was reduced to quality " + ForgeContext.TargetQuality + ".", 0, (Sprite)null, true); break; case ForgeOutcome.CompleteLoss: ((Character)player).Message((MessageType)2, "Refinement failed. " + text + " was reduced back to its base form at quality " + ForgeContext.TargetQuality + ".", 0, (Sprite)null, true); break; } } } [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 ForgeReturnedItemQualityPatch { [HarmonyPrefix] private static void Prefix(string name, ref int quality, Vector2i position) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (ForgeContext.Active && !(name != ForgeContext.CraftedPrefabName) && position.x == ForgeContext.OriginalPosition.x && position.y == ForgeContext.OriginalPosition.y) { quality = ForgeContext.TargetQuality; } } } [HarmonyPatch(typeof(Player), "Message")] internal static class ForgeVanillaMessagePatch { [HarmonyPrefix] private static bool Prefix(MessageType type, string msg) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 if (!ForgeContext.Active || (int)type != 2 || Localization.instance == null) { return true; } string text = Localization.instance.Localize("$msg_upgrader_success", new string[2] { ForgeContext.SharedItemName, ForgeContext.AttemptedQuality.ToString() }); string text2 = Localization.instance.Localize("$msg_upgrader_failed", new string[2] { ForgeContext.SharedItemName, (ForgeContext.AttemptedQuality - 2).ToString() }); string text3 = Localization.instance.Localize("$msg_upgrader_broke", new string[2] { ForgeContext.SharedItemName, (ForgeContext.AttemptedQuality - 1).ToString() }); if (msg == text) { return ForgeContext.Outcome == ForgeOutcome.Success; } if (msg == text2) { return false; } if (msg == text3) { return ForgeContext.Outcome == ForgeOutcome.Destroyed; } return true; } } internal enum IdolFamily { Battle, Protection } internal enum ForgeOutcome { None, Success, Jackpot, Ignore, Loss, CompleteLoss, Destroyed } internal enum GearArchetype { Sword, Mace, Sledge, Spear, Polearm, Knife, Bow, Crossbow, Axe, HeavyAxe, Greatsword, Fists, Pickaxe, Magic, Armor, RoundShield, TowerShield, Buckler, GenericBattle, GenericProtection } internal sealed class ForgeOdds { internal float Success; internal float OrdinaryFailure; internal float CompleteLoss; internal float Destruction; internal float JackpotChance = 0.2f; internal bool FailureIsIgnore; internal float CascadeChance; internal float TotalBad => OrdinaryFailure + CompleteLoss + Destruction; } internal static class ForgeData { private sealed class TrophyRule { internal readonly string[] Patterns; internal readonly float Prestige; internal TrophyRule(float prestige, params string[] patterns) { Prestige = prestige; Patterns = patterns; } } internal const int MaxTier = 7; internal static readonly string[] TierNames = new string[8] { "Wooden", "Bronze", "Iron", "Silver", "Black Metal", "Black Marble", "Flametal", "Bloodgold" }; internal static readonly float[] TierPrestige = new float[8] { 6f, 20f, 30f, 40f, 48f, 58f, 68f, 82f }; private static readonly Dictionary<GearArchetype, float[]> Thresholds = new Dictionary<GearArchetype, float[]> { { GearArchetype.Sword, new float[8] { 0f, 35f, 55f, 75f, 95f, 115f, 135f, 170f } }, { GearArchetype.Mace, new float[8] { 0f, 35f, 55f, 75f, 95f, 115f, 135f, 170f } }, { GearArchetype.Spear, new float[8] { 0f, 35f, 55f, 75f, 95f, 115f, 135f, 170f } }, { GearArchetype.Axe, new float[8] { 0f, 40f, 60f, 80f, 100f, 120f, 140f, 176f } }, { GearArchetype.Polearm, new float[8] { 0f, 45f, 65f, 85f, 105f, 125f, 145f, 182f } }, { GearArchetype.Knife, new float[8] { 0f, 12f, 20f, 25f, 34f, 45f, 60f, 75f } }, { GearArchetype.Bow, new float[8] { 0f, 32f, 42f, 52f, 62f, 72f, 82f, 100f } }, { GearArchetype.Crossbow, new float[8] { 0f, 0f, 0f, 0f, 0f, 200f, 220f, 264f } }, { GearArchetype.HeavyAxe, new float[8] { 0f, 50f, 70f, 90f, 110f, 130f, 150f, 188f } }, { GearArchetype.Greatsword, new float[8] { 0f, 70f, 90f, 110f, 130f, 150f, 170f, 210f } }, { GearArchetype.Sledge, new float[8] { 0f, 20f, 55f, 85f, 115f, 145f, 175f, 225f } }, { GearArchetype.Fists, new float[8] { 0f, 25f, 42f, 60f, 80f, 95f, 105f, 114f } }, { GearArchetype.Pickaxe, new float[8] { 0f, 25f, 33f, 41f, 49f, 57f, 65f, 73f } }, { GearArchetype.Armor, new float[8] { 0f, 8f, 14f, 20f, 26f, 32f, 38f, 44f } }, { GearArchetype.RoundShield, new float[8] { 6f, 24f, 42f, 60f, 78f, 96f, 114f, 132f } }, { GearArchetype.TowerShield, new float[8] { 10f, 32f, 52f, 72f, 104f, 122f, 140f, 158f } }, { GearArchetype.Buckler, new float[8] { 0f, 16f, 28f, 40f, 62f, 78f, 83f, 88f } } }; private static readonly Dictionary<string, float> VanillaTrophyPrestige = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase) { { "TrophyAbomination", 42f }, { "TrophyAsksvin", 66f }, { "TrophyBarka", 100f }, { "TrophyBjorn", 24f }, { "TrophyBjornUndead", 58f }, { "TrophyBlob", 22f }, { "TrophyBlob_Frost", 34f }, { "TrophyBlob_Lava", 55f }, { "TrophyBlob_Morkhalla", 58f }, { "TrophyBoar", 6f }, { "TrophyBonemass", 48f }, { "TrophyBonemawSerpent", 68f }, { "TrophyCharredArcher", 52f }, { "TrophyCharredMage", 72f }, { "TrophyCharredMelee", 60f }, { "TrophyCultist", 38f }, { "TrophyCultist_Hildir", 50f }, { "TrophyDeathsquito", 35f }, { "TrophyDeer", 2f }, { "TrophyDeerWhite", 10f }, { "TrophyDragonQueen", 54f }, { "TrophyDraugr", 28f }, { "TrophyDraugrElite", 34f }, { "TrophyDraugrFem", 28f }, { "TrophyDvergr", 55f }, { "TrophyEikthyr", 18f }, { "TrophyElaking", 60f }, { "TrophyFader", 84f }, { "TrophyFallenValkyrie", 72f }, { "TrophyFenring", 40f }, { "TrophyForestTroll", 28f }, { "TrophyFrostTroll", 28f }, { "TrophyGhost", 14f }, { "TrophyGjall", 64f }, { "TrophyGoblin", 42f }, { "TrophyGoblinBrute", 48f }, { "TrophyGoblinBruteBrosBrute", 58f }, { "TrophyGoblinBruteBrosShaman", 58f }, { "TrophyGoblinKing", 66f }, { "TrophyGoblinShaman", 38f }, { "TrophyGreydwarf", 12f }, { "TrophyGreydwarfBrute", 20f }, { "TrophyGreydwarfShaman", 16f }, { "TrophyGrowth", 40f }, { "TrophyHare", 8f }, { "TrophyHatchling", 34f }, { "TrophyJotunWarrior", 78f }, { "TrophyJotunWitch", 70f }, { "TrophyKvastur", 44f }, { "TrophyLeech", 24f }, { "TrophyLox", 50f }, { "TrophyMole", 82f }, { "TrophyMoose", 65f }, { "TrophyMorgen", 76f }, { "TrophyNeck", 4f }, { "TrophySeal", 24f }, { "TrophySeeker", 52f }, { "TrophySeekerBrute", 60f }, { "TrophySeekerQueen", 72f }, { "TrophySerpent", 45f }, { "TrophySGolem", 44f }, { "TrophySkeleton", 13f }, { "TrophySkeletonHildir", 34f }, { "TrophySkeletonPoison", 18f }, { "TrophySurtling", 18.5f }, { "TrophyTheElder", 34f }, { "TrophyTick", 42f }, { "TrophyUlv", 36f }, { "TrophyVolture", 45f }, { "TrophyWolf", 32f }, { "TrophyWraith", 31f }, { "TrophyWrithan", 45f } }; private static readonly TrophyRule[] TrophyRules = new TrophyRule[63] { new TrophyRule(100f, "barka"), new TrophyRule(84f, "fader"), new TrophyRule(82f, "eyeless"), new TrophyRule(78f, "krigen"), new TrophyRule(76f, "morgen"), new TrophyRule(72f, "fallenvalkyrie", "valkyrie"), new TrophyRule(72f, "queen"), new TrophyRule(70f, "hexen"), new TrophyRule(68f, "bonemaw"), new TrophyRule(66f, "asksvin"), new TrophyRule(66f, "yagluth"), new TrophyRule(65f, "moose"), new TrophyRule(64f, "gjall"), new TrophyRule(72f, "trophycharredmage", "charredmage", "charredwarlock", "warlock"), new TrophyRule(60f, "seekersoldier", "soldier"), new TrophyRule(60f, "trophycharredmelee", "charredmelee", "charredwarrior"), new TrophyRule(60f, "elaking"), new TrophyRule(58f, "zil", "thungr"), new TrophyRule(56f, "dvergrmage"), new TrophyRule(55f, "dvergrrogue", "dvergr"), new TrophyRule(55f, "lavablob"), new TrophyRule(54f, "moder"), new TrophyRule(52f, "trophycharredarcher", "charredarcher", "charredmarksman", "marksman"), new TrophyRule(52f, "seeker"), new TrophyRule(50f, "lox"), new TrophyRule(50f, "geirrhafa"), new TrophyRule(48f, "fulingberserker", "berserker"), new TrophyRule(48f, "bonemass"), new TrophyRule(45f, "volture"), new TrophyRule(45f, "serpent"), new TrophyRule(44f, "stonegolem", "golem"), new TrophyRule(42f, "abomination"), new TrophyRule(42f, "tick"), new TrophyRule(42f, "fuling"), new TrophyRule(40f, "growth"), new TrophyRule(40f, "fenring"), new TrophyRule(38f, "cultist"), new TrophyRule(38f, "pulp"), new TrophyRule(38f, "fulingshaman"), new TrophyRule(35f, "deathsquito"), new TrophyRule(34f, "draugrelite"), new TrophyRule(34f, "drake"), new TrophyRule(34f, "elder"), new TrophyRule(32f, "wolf"), new TrophyRule(31f, "wraith"), new TrophyRule(28f, "draugr"), new TrophyRule(28f, "frosttroll", "troll"), new TrophyRule(24f, "seal"), new TrophyRule(24f, "leech"), new TrophyRule(24f, "bear"), new TrophyRule(22f, "blob"), new TrophyRule(20f, "greydwarfbrute", "brute"), new TrophyRule(18.5f, "surtling"), new TrophyRule(18f, "rancid"), new TrophyRule(18f, "eikthyr"), new TrophyRule(16f, "greydwarfshaman", "shaman"), new TrophyRule(13f, "skeleton"), new TrophyRule(12f, "greydwarf"), new TrophyRule(10f, "deerwhite", "whitedeer"), new TrophyRule(8f, "hare"), new TrophyRule(6f, "boar"), new TrophyRule(4f, "neck"), new TrophyRule(2f, "deer") }; internal static string IdolPrefabName(IdolFamily family, int tier) { return "Upgrader" + Mathf.Clamp(tier, 0, 7) + ((family == IdolFamily.Battle) ? "Weapon" : "Armor"); } internal static ItemDrop GetIdolDrop(IdolFamily family, int tier) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(IdolPrefabName(family, tier)); if (!((Object)(object)itemPrefab != (Object)null)) { return null; } return itemPrefab.GetComponent<ItemDrop>(); } internal static bool TryParseIdol(ItemDrop idol, out int tier, out IdolFamily family) { tier = -1; family = IdolFamily.Battle; if ((Object)(object)idol == (Object)null) { return false; } string a = (((Object)(object)((Component)idol).gameObject != (Object)null) ? ((Object)((Component)idol).gameObject).name : ""); for (int i = 0; i <= 7; i++) { if (string.Equals(a, IdolPrefabName(IdolFamily.Battle, i), StringComparison.OrdinalIgnoreCase)) { tier = i; family = IdolFamily.Battle; return true; } if (string.Equals(a, IdolPrefabName(IdolFamily.Protection, i), StringComparison.OrdinalIgnoreCase)) { tier = i; family = IdolFamily.Protection; return true; } } return false; } internal static bool TryGetNativeIdol(Recipe recipe, out int tier, out IdolFamily family, out Requirement requirement) { tier = -1; family = IdolFamily.Battle; requirement = null; if ((Object)(object)recipe == (Object)null || recipe.m_resources == null) { return false; } for (int i = 0; i < recipe.m_resources.Length; i++) { Requirement val = recipe.m_resources[i]; if (val != null && val.m_upgraderResource && !((Object)(object)val.m_resItem == (Object)null) && TryParseIdol(val.m_resItem, out var tier2, out var family2)) { tier = tier2; family = family2; requirement = val; return true; } } return false; } internal static GearArchetype GetArchetype(ItemData item, IdolFamily family) { //IL_0078: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected I4, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Invalid comparison between Unknown and I4 //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Invalid comparison between Unknown and I4 //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Invalid comparison between Unknown and I4 //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Invalid comparison between Unknown and I4 if (item == null || item.m_shared == null) { if (family != IdolFamily.Battle) { return GearArchetype.GenericProtection; } return GearArchetype.GenericBattle; } SharedData shared = item.m_shared; if (family == IdolFamily.Protection) { if ((int)shared.m_itemType == 5) { if (shared.m_timedBlockBonus <= 1.01f) { return GearArchetype.TowerShield; } if (shared.m_timedBlockBonus >= 2f) { return GearArchetype.Buckler; } return GearArchetype.RoundShield; } if ((int)shared.m_itemType == 6 || (int)shared.m_itemType == 7 || (int)shared.m_itemType == 11 || (int)shared.m_itemType == 17) { return GearArchetype.Armor; } return GearArchetype.GenericProtection; } SkillType skillType = shared.m_skillType; switch (skillType - 1) { case 0: if ((int)shared.m_animationState != 13) { return GearArchetype.Sword; } return GearArchetype.Greatsword; case 2: if ((int)shared.m_animationState != 2) { return GearArchetype.Mace; } return GearArchetype.Sledge; case 3: return GearArchetype.Polearm; case 4: return GearArchetype.Spear; case 1: return GearArchetype.Knife; case 7: return GearArchetype.Bow; case 13: return GearArchetype.Crossbow; case 6: if ((int)shared.m_animationState != 8) { return GearArchetype.Axe; } return GearArchetype.HeavyAxe; case 10: return GearArchetype.Fists; case 11: return GearArchetype.Pickaxe; case 8: case 9: return GearArchetype.Magic; default: if ((int)shared.m_itemType == 12) { return GearArchetype.Fists; } return GearArchetype.GenericBattle; } } internal static float GetPowerScore(ItemData item, int quality, GearArchetype archetype) { //IL_0072: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (item == null || item.m_shared == null) { return 0f; } SharedData shared = item.m_shared; int num = Mathf.Max(1, quality); switch (archetype) { case GearArchetype.Armor: case GearArchetype.GenericProtection: return shared.m_armor + (float)Mathf.Max(0, num - 1) * shared.m_armorPerLevel; case GearArchetype.RoundShield: case GearArchetype.TowerShield: case GearArchetype.Buckler: return shared.m_blockPower + (float)Mathf.Max(0, num - 1) * shared.m_blockPowerPerLevel; default: { DamageTypes damage = item.GetDamage(num, 0f); float num2 = damage.m_damage + damage.m_blunt + damage.m_slash + damage.m_pierce + damage.m_fire + damage.m_frost + damage.m_lightning + damage.m_poison * 0.5f; if (archetype == GearArchetype.Pickaxe) { num2 += damage.m_pickaxe; } return num2; } } } internal static int GetRequiredTier(ItemData item, Recipe recipe, int targetQuality, int nativeTier, IdolFamily family) { nativeTier = Mathf.Clamp(nativeTier, 0, 7); int num = (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && recipe.m_item.m_itemData != null && recipe.m_item.m_itemData.m_shared != null) ? Mathf.Max(1, recipe.m_item.m_itemData.m_shared.m_maxQuality) : Mathf.Max(1, item.m_shared.m_maxQuality)); int num2 = Mathf.Max(0, targetQuality - num); int num3 = ((num2 > 0) ? ((num2 - 1) / 3) : 0); return Mathf.Clamp(nativeTier + num3, nativeTier, 7); } internal static float GetContinuousTier(ItemData item, Recipe recipe, int quality, int nativeTier, IdolFamily family) { nativeTier = Mathf.Clamp(nativeTier, 0, 7); int num = (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && recipe.m_item.m_itemData != null && recipe.m_item.m_itemData.m_shared != null) ? Mathf.Max(1, recipe.m_item.m_itemData.m_shared.m_maxQuality) : Mathf.Max(1, item.m_shared.m_maxQuality)); quality = Mathf.Max(1, quality); float num2; if (quality <= num) { if (num <= 1) { num2 = nativeTier; } else { float num3 = Mathf.InverseLerp(1f, (float)num, (float)quality); num2 = (float)nativeTier - 0.5f + num3 * 0.5f; } } else { int num4 = quality - num; num2 = (float)nativeTier + (float)num4 / 3f; } return Mathf.Clamp(num2, Mathf.Max(0f, (float)nativeTier - 0.5f), 7f); } internal static float GetItemReferencePrestige(float continuousTier) { float num = Mathf.Clamp(continuousTier, 0f, 7f); int num2 = Mathf.FloorToInt(num); int num3 = Mathf.Min(7, num2 + 1); if (num2 == num3) { return TierPrestige[num2]; } return Mathf.Lerp(TierPrestige[num2], TierPrestige[num3], num - (float)num2); } internal static float GetTrophyPrestige(ItemData trophy) { if (trophy == null || trophy.m_shared == null) { return 0f; } if ((Object)(object)trophy.m_dropPrefab != (Object)null && VanillaTrophyPrestige.TryGetValue(((Object)trophy.m_dropPrefab).name, out var value)) { return value; } string text = ""; if ((Object)(object)trophy.m_dropPrefab != (Object)null) { text += ((Object)trophy.m_dropPrefab).name; } text = text + "|" + trophy.m_shared.m_name; string text2 = text.Replace("_", "").Replace("-", "").Replace(" ", "") .Replace("$", "") .ToLowerInvariant(); for (int i = 0; i < TrophyRules.Length; i++) { TrophyRule trophyRule = TrophyRules[i]; for (int j = 0; j < trophyRule.Patterns.Length; j++) { if (text2.Contains(trophyRule.Patterns[j])) { return trophyRule.Prestige; } } } return 40f; } internal static float GetTrophyBadOutcomeMultiplier(ItemData trophy, float itemReferencePrestige) { if (trophy == null) { return 1f; } float num = GetTrophyPrestige(trophy) - itemReferencePrestige; return Mathf.Clamp(Mathf.Exp(-0.11f * (num + 9f)), 0.03f, 0.98f); } internal static string GetFavorDescription(ItemData trophy, float itemReferencePrestige) { if (trophy == null) { return "No trophy is offered."; } float trophyBadOutcomeMultiplier = GetTrophyBadOutcomeMultiplier(trophy, itemReferencePrestige); if (trophyBadOutcomeMultiplier >= 0.9f) { return "Meagre offering."; } if (trophyBadOutcomeMultiplier >= 0.7f) { return "Modest offering."; } if (trophyBadOutcomeMultiplier >= 0.45f) { return "Worthy offering."; } if (trophyBadOutcomeMultiplier >= 0.22f) { return "Strong offering."; } if (trophyBadOutcomeMultiplier >= 0.08f) { return "Great offering."; } return "The gods are greatly pleased."; } internal static string LocalizeItemName(ItemData item) { if (item == null || item.m_shared == null) { return ""; } if (Localization.instance == null) { return item.m_shared.m_name; } return Localization.instance.Localize(item.m_shared.m_name); } internal static string LocalizeDropName(ItemDrop drop) { if (!((Object)(object)drop != (Object)null)) { return ""; } return LocalizeItemName(drop.m_itemData); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class ForgeInventorySpacePatch { private static readonly MethodInfo GetEmptySlotsMethod = AccessTools.Method(typeof(Inventory), "GetEmptySlots", (Type[])null, (Type[])null); private static readonly MethodInfo GetEmptySlotsForCraftMethod = AccessTools.Method(typeof(ForgeInventorySpacePatch), "GetEmptySlotsForCraft", (Type[])null, (Type[])null); [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, GetEmptySlotsMethod)) { yield return new CodeInstruction(OpCodes.Call, (object)GetEmptySlotsForCraftMethod); } else { yield return instruction; } } } private static int GetEmptySlotsForCraft(Inventory inventory) { Player localPlayer = Player.m_localPlayer; CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null); if ((Object)(object)val != (Object)null && val.m_upgrader && ForgeSelection.SelectedIdolTier >= ForgeSelection.RequiredTier) { return int.MaxValue; } if (inventory == null) { return 0; } return inventory.GetEmptySlots(); } } internal static class ForgeRules { internal static float GetBaseWinChance(int potential) { return Mathf.Max(0.65f, 0.9f - 0.05f * (float)Mathf.Max(0, potential)); } internal static bool LossRiskActive(int potential) { return potential >= 2; } internal static bool MultiLossRiskActive(int potential) { return potential >= 4; } internal static bool CompleteLossRiskActive(int potential) { return potential >= 5; } internal static ForgeOdds GetOdds(int potential, int requiredIdolTier, int selectedIdolTier, ItemData trophy, float itemReferencePrestige) { float baseWinChance = GetBaseWinChance(potential); float num = 1f - baseWinChance; int num2 = Mathf.Max(0, requiredIdolTier - selectedIdolTier); if (num2 > 0) { ForgeOdds forgeOdds = new ForgeOdds(); forgeOdds.Success = Mathf.Clamp01(baseWinChance - 0.025f * (float)num2); forgeOdds.Destruction = Mathf.Clamp01(1f - forgeOdds.Success); forgeOdds.OrdinaryFailure = 0f; forgeOdds.CompleteLoss = 0f; forgeOdds.FailureIsIgnore = false; forgeOdds.CascadeChance = 0f; forgeOdds.JackpotChance = Mathf.Clamp01(0.2f + 0.1f * (float)num2); return forgeOdds; } float num3 = (CompleteLossRiskActive(potential) ? Mathf.Min(0.1f, num) : 0f); float num4 = Mathf.Max(0f, num - num3); int num5 = Mathf.Max(0, selectedIdolTier - requiredIdolTier); float num6 = Mathf.Pow(0.72f, (float)num5); float num7 = Mathf.Pow(0.45f, (float)num5); float trophyBadOutcomeMultiplier = ForgeData.GetTrophyBadOutcomeMultiplier(trophy, itemReferencePrestige); float num8 = ((trophy == null) ? 1f : Mathf.Pow(trophyBadOutcomeMultiplier, 1.8f)); float num9 = num4 * num6 * trophyBadOutcomeMultiplier; float num10 = num3 * num7 * num8; num9 = Mathf.Clamp(num9, 0f, 0.98f); num10 = Mathf.Clamp(num10, 0f, 0.98f - num9); ForgeOdds obj = new ForgeOdds { OrdinaryFailure = num9, CompleteLoss = num10, Destruction = 0f, JackpotChance = 0.2f, Success = Mathf.Clamp01(1f - num9 - num10), FailureIsIgnore = !LossRiskActive(potential) }; float num11 = num6 * trophyBadOutcomeMultiplier; obj.CascadeChance = Mathf.Clamp01(num * Mathf.Sqrt(Mathf.Clamp01(num11))); return obj; } internal static ForgeOutcome RollOutcome(ForgeOdds odds) { float value = Random.value; if (value < odds.Success) { return ForgeOutcome.Success; } value -= odds.Success; if (value < odds.Destruction) { return ForgeOutcome.Destroyed; } value -= odds.Destruction; if (value < odds.OrdinaryFailure) { if (!odds.FailureIsIgnore) { return ForgeOutcome.Loss; } return ForgeOutcome.Ignore; } return ForgeOutcome.CompleteLoss; } internal static int RollLossLevels(int potential, float cascadeChance) { int num = Mathf.Max(1, potential); int i = 1; if (!MultiLossRiskActive(potential)) { return i; } for (; i < num; i++) { if (!(Random.value < cascadeChance)) { break; } } return i; } internal static int RollSuccessGain(float jackpotChance) { int result = 1; if (Random.value >= Mathf.Clamp01(jackpotChance)) { return result; } for (result = 2; result < 5; result++) { if (!(Random.value < 0.2f)) { break; } } return result; } internal static string GetRiskWarning(ForgeOdds odds, int refinementLevel) { if (odds == null) { return ""; } if (CompleteLossRiskActive(refinementLevel)) { return "Refinement may reduce your item back to its base form."; } if (MultiLossRiskActive(refinementLevel)) { return "Refinement may greatly lower your item's quality."; } if (LossRiskActive(refinementLevel)) { return "Refinement may lower your item's quality."; } return "Refinement may fail."; } } internal sealed class ForgeSelectionSnapshot { internal Recipe Recipe; internal ItemData Item; internal int NativeTier; internal int RequiredTier; internal IdolFamily Family; internal int IdolTier; internal ItemData Trophy; internal float ContinuousTier; internal float ItemReferencePrestige; } internal static class ForgeSelection { internal static Recipe CurrentRecipe; internal static ItemData CurrentItem; internal static int NativeTier; internal static int RequiredTier; internal static IdolFamily Family; internal static int SelectedIdolTier; internal static ItemData SelectedTrophy; internal static float ContinuousTier; internal static float ItemReferencePrestige; internal static ForgeSelectionSnapshot Pending; internal static bool Refresh(Recipe recipe, ItemData item, Player player) { if ((Object)(object)recipe == (Object)null || item == null || item.m_shared == null || (Object)(object)player == (Object)null) { ResetCurrent(); return false; } if (!ForgeData.TryGetNativeIdol(recipe, out var tier, out var family, out var _)) { ResetCurrent(); return false; } int targetQuality = Mathf.Max(1, item.m_quality + 1); int requiredTier = ForgeData.GetRequiredTier(item, recipe, targetQuality, tier, family); float continuousTier = ForgeData.GetContinuousTier(item, recipe, item.m_quality, tier, family); bool num = (Object)(object)CurrentRecipe != (Object)(object)recipe || CurrentItem != item || Family != family || NativeTier != tier || RequiredTier != requiredTier; CurrentRecipe = recipe; CurrentItem = item; Family = family; NativeTier = tier; RequiredTier = requiredTier; ContinuousTier = continuousTier; ItemReferencePrestige = ForgeData.GetItemReferencePrestige(continuousTier); if (num) { SelectedTrophy = null; SelectedIdolTier = FindLowestOwnedValidIdol(player, family, requiredTier); } else { ValidateIdol(player); ValidateTrophy(player); } return true; } internal static void ValidateIdol(Player player) { if (!((Object)(object)player == (Object)null) && (SelectedIdolTier < 0 || SelectedIdolTier > 7 || !PlayerHasIdol(player, Family, SelectedIdolTier))) { SelectedIdolTier = FindLowestOwnedValidIdol(player, Family, RequiredTier); } } internal static void ValidateTrophy(Player player) { if (SelectedIdolTier < RequiredTier) { SelectedTrophy = null; } else { if (SelectedTrophy == null || (Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { SelectedTrophy = null; return; } string text = ((SelectedTrophy.m_shared != null) ? SelectedTrophy.m_shared.m_name : null); if (string.IsNullOrEmpty(text) || inventory.CountItems(text, -1, true) <= 0) { SelectedTrophy = null; } } } internal static bool PlayerHasIdol(Player player, IdolFamily family, int tier) { if ((Object)(object)player == (Object)null) { return false; } ItemDrop idolDrop = ForgeData.GetIdolDrop(family, tier); if ((Object)(object)idolDrop == (Object)null || idolDrop.m_itemData == null || idolDrop.m_itemData.m_shared == null) { return false; } return ((Humanoid)player).GetInventory().CountItems(idolDrop.m_itemData.m_shared.m_name, -1, true) > 0; } internal static int FindLowestOwnedValidIdol(Player player, IdolFamily family, int requiredTier) { for (int i = Mathf.Clamp(requiredTier, 0, 7); i <= 7; i++) { if (PlayerHasIdol(player, family, i)) { return i; } } return Mathf.Clamp(requiredTier, 0, 7); } internal static List<int> GetOwnedValidIdolTiers(Player player) { List<int> list = new List<int>(); if ((Object)(object)player == (Object)null) { return list; } for (int i = 0; i <= 7; i++) { if (PlayerHasIdol(player, Family, i)) { list.Add(i); } } return list; } internal static void CycleIdol(Player player, int direction) { List<int> ownedValidIdolTiers = GetOwnedValidIdolTiers(player); if (ownedValidIdolTiers.Count == 0) { SelectedIdolTier = RequiredTier; return; } int num = ownedValidIdolTiers.IndexOf(SelectedIdolTier); num = ((num >= 0) ? WrapIndex(num + direction, ownedValidIdolTiers.Count) : 0); SelectedIdolTier = ownedValidIdolTiers[num]; if (SelectedIdolTier < RequiredTier) { SelectedTrophy = null; } } internal static List<ItemData> GetUniqueTrophies(Player player) { List<ItemData> list = new List<ItemData>(); if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null) { return list; } List<ItemData> allItemsOfType = ((Humanoid)player).GetInventory().GetAllItemsOfType((ItemType)13, true); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < allItemsOfType.Count; i++) { ItemData val = allItemsOfType[i]; if (val != null && val.m_shared != null && val.m_stack > 0) { string name = val.m_shared.m_name; if (hashSet.Add(name)) { list.Add(val); } } } list.Sort(delegate(ItemData a, ItemData b) { int num = ForgeData.GetTrophyPrestige(a).CompareTo(ForgeData.GetTrophyPrestige(b)); return (num != 0) ? num : string.Compare(ForgeData.LocalizeItemName(a), ForgeData.LocalizeItemName(b), StringComparison.CurrentCultureIgnoreCase); }); return list; } internal static void CycleTrophy(Player player, int direction) { if (SelectedIdolTier < RequiredTier) { SelectedTrophy = null; return; } List<ItemData> uniqueTrophies = GetUniqueTrophies(player); int count = uniqueTrophies.Count + 1; int num = 0; if (SelectedTrophy != null && SelectedTrophy.m_shared != null) { string name = SelectedTrophy.m_shared.m_name; for (int i = 0; i < uniqueTrophies.Count; i++) { if (uniqueTrophies[i].m_shared.m_name == name) { num = i + 1; break; } } } int num2 = WrapIndex(num + direction, count); SelectedTrophy = ((num2 == 0) ? null : uniqueTrophies[num2 - 1]); } internal static bool HasRequiredIdol(Player player) { return PlayerHasIdol(player, Family, SelectedIdolTier); } internal static bool HasSelectedTrophy(Player player) { if (SelectedTrophy == null) { return true; } if ((Object)(object)player == (Object)null || SelectedTrophy.m_shared == null) { return false; } return ((Humanoid)player).GetInventory().CountItems(SelectedTrophy.m_shared.m_name, -1, true) > 0; } internal static bool LockForCraft(Player player) { if ((Object)(object)CurrentRecipe == (Object)null || CurrentItem == null || (Object)(object)player == (Object)null) { Pending = null; return false; } ValidateIdol(player); ValidateTrophy(player); if (!player.NoCostCheat() && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) && (!HasRequiredIdol(player) || !HasSelectedTrophy(player))) { Pending = null; return false; } Pending = new ForgeSelectionSnapshot { Recipe = CurrentRecipe, Item = CurrentItem, NativeTier = NativeTier, RequiredTier = RequiredTier, Family = Family, IdolTier = SelectedIdolTier, Trophy = SelectedTrophy, ContinuousTier = ContinuousTier, ItemReferencePrestige = ItemReferencePrestige }; return true; } internal static ForgeSelectionSnapshot GetPending(Recipe recipe, ItemData item) { if (Pending != null && (Object)(object)Pending.Recipe == (Object)(object)recipe && Pending.Item == item) { return Pending; } if ((Object)(object)CurrentRecipe == (Object)(object)recipe && CurrentItem == item) { return new ForgeSelectionSnapshot { Recipe = CurrentRecipe, Item = CurrentItem, NativeTier = NativeTier, RequiredTier = RequiredTier, Family = Family, IdolTier = SelectedIdolTier, Trophy = SelectedTrophy, ContinuousTier = ContinuousTier, ItemReferencePrestige = ItemReferencePrestige }; } return null; } internal static void AfterAttempt() { SelectedTrophy = null; Pending = null; } internal static void CancelPending() { Pending = null; } internal static void ResetCurrent() { CurrentRecipe = null; CurrentItem = null; NativeTier = 0; RequiredTier = 0; Family = IdolFamily.Battle; SelectedIdolTier = 0; SelectedTrophy = null; ContinuousTier = 0f; ItemReferencePrestige = 0f; } internal static void ResetAll() { ResetCurrent(); Pending = null; } private static int WrapIndex(int value, int count) { if (count <= 0) { return 0; } value %= count; if (value < 0) { value += count; } return value; } } internal sealed class GoodtentialCycleButton : MonoBehaviour { internal int Kind; internal void Cycle() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) InventoryGui componentInParent = ((Component)this).GetComponentInParent<InventoryGui>(); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)componentInParent == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !ForgeUiPatch.IsRefining(componentInParent)) { int selectedIdolTier = ForgeSelection.SelectedIdolTier; ItemData selectedTrophy = ForgeSelection.SelectedTrophy; if (Kind == 0) { ForgeSelection.CycleIdol(localPlayer, 1); } else { ForgeSelection.CycleTrophy(localPlayer, 1); } if ((selectedIdolTier != ForgeSelection.SelectedIdolTier || selectedTrophy != ForgeSelection.SelectedTrophy) && componentInParent.m_moveItemEffects != null) { componentInParent.m_moveItemEffects.Create(((Component)componentInParent).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } ForgeUiPatch.RefreshAfterSelection(componentInParent); } } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class ForgeUiPatch { private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); private static readonly PropertyInfo SelectedRecipeProperty = ((SelectedRecipeField != null) ? SelectedRecipeField.FieldType.GetProperty("Recipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); private static readonly PropertyInfo SelectedItemProperty = ((SelectedRecipeField != null) ? SelectedRecipeField.FieldType.GetProperty("ItemData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); internal static bool TryGetSelected(InventoryGui gui, out Recipe recipe, out ItemData item) { recipe = null; item = null; if ((Object)(object)gui == (Object)null || SelectedRecipeField == null || SelectedRecipeProperty == null || SelectedItemProperty == null) { return false; } object value = SelectedRecipeField.GetValue(gui); if (value == null) { return false; } object? value2 = SelectedRecipeProperty.GetValue(value, null); recipe = (Recipe)((value2 is Recipe) ? value2 : null); object? value3 = SelectedItemProperty.GetValue(value, null); item = (ItemData)((value3 is ItemData) ? value3 : null); if ((Object)(object)recipe != (Object)null) { return item != null; } return false; } internal static bool IsRefining(InventoryGui gui) { if ((Object)(object)gui == (Object)null || CraftTimerField == null) { return false; } object value = CraftTimerField.GetValue(gui); if (value is float) { return (float)value >= 0f; } return false; } [HarmonyPrefix] private static void Prefix(InventoryGui __instance) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)__instance == (Object)null) && !((Object)(object)localPlayer == (Object)null)) { CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { RestoreRequirementSlots(__instance); } } } [HarmonyPostfix] private static void Postfix(InventoryGui __instance) { RefreshAfterSelection(__instance); } internal static void RefreshAfterSelection(InventoryGui gui) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)gui == (Object)null) && !((Object)(object)localPlayer == (Object)null)) { CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); Recipe recipe; ItemData item; if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { RestoreRequirementSlots(gui); ForgeSelection.ResetCurrent(); } else if (TryGetSelected(gui, out recipe, out item) && ForgeSelection.Refresh(recipe, item, localPlayer)) { gui.m_craftingStationName.text = "Forge of Goodtential"; EnsureRequirementSlots(gui); RenderIdolSlot(gui, localPlayer); RenderTrophySlot(gui, localPlayer); RenderRisk(gui, recipe, item); UpdateCraftButton(gui, localPlayer, currentCraftingStation, recipe, item); } } } private static void RestoreRequirementSlots(InventoryGui gui) { if ((Object)(object)gui == (Object)null || gui.m_recipeRequirementList == null) { return; } for (int i = 0; i < gui.m_recipeRequirementList.Length; i++) { GameObject val = gui.m_recipeRequirementList[i]; if (!((Object)(object)val == (Object)null)) { Transform val2 = val.transform.Find("Goodtential cycle button"); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val2).gameObject); } UITooltip component = val.GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; } } } } private static void EnsureRequirementSlots(InventoryGui gui) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (gui.m_recipeRequirementList == null || gui.m_recipeRequirementList.Length == 0) { return; } if (gui.m_recipeRequirementList.Length < 4) { GameObject val = gui.m_recipeRequirementList[0]; RectTransform component = val.GetComponent<RectTransform>(); List<GameObject> list = new List<GameObject>(gui.m_recipeRequirementList); while (list.Count < 4) { int count = list.Count; GameObject val2 = Object.Instantiate<GameObject>(val, val.transform.parent); ((Object)val2).name = "Goodtential requirement " + count; RectTransform component2 = val2.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null) { Vector2 anchoredPosition = component.anchoredPosition; Rect rect = component.rect; component2.anchoredPosition = anchoredPosition + new Vector2((((Rect)(ref rect)).width + 8f) * (float)count, 0f); } list.Add(val2); } gui.m_recipeRequirementList = list.ToArray(); } DisableItemTooltip(gui.m_recipeRequirementList[0]); DisableItemTooltip(gui.m_recipeRequirementList[2]); ConfigureCycleButton(gui, gui.m_recipeRequirementList[1], 0); ConfigureCycleButton(gui, gui.m_recipeRequirementList[3], 1); } private static void DisableItemTooltip(GameObject root) { if (!((Object)(object)root == (Object)null)) { UITooltip component = root.GetComponent<UITooltip>(); if (!((Object)(object)component == (Object)null)) { component.m_topic = ""; component.m_text = ""; ((Behaviour)component).enabled = false; } } } private static void ConfigureCycleButton(InventoryGui gui, GameObject root, int kind) { //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Expected O, but got Unknown //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gui == (Object)null || (Object)(object)root == (Object)null || (Object)(object)gui.m_takeAllButton == (Object)null) { return; } root.SetActive(true); string[] array = new string[3] { "res_icon", "res_name", "res_amount" }; for (int i = 0; i < array.Length; i++) { Transform val = root.transform.Find(array[i]); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } DisableItemTooltip(root); Transform val2 = root.transform.Find("Goodtential cycle button"); GameObject gameObject; if ((Object)(object)val2 == (Object)null) { UIGamePad component = ((Component)gui.m_takeAllButton).GetComponent<UIGamePad>(); bool enabled = (Object)(object)component != (Object)null && ((Behaviour)component).enabled; if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } Button obj = Object.Instantiate<Button>(gui.m_takeAllButton, root.transform); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = enabled; } gameObject = ((Component)obj).gameObject; ((Object)gameObject).name = "Goodtential cycle button"; obj.onClick = new ButtonClickedEvent(); UIGamePad component2 = gameObject.GetComponent<UIGamePad>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = false; } RectTransform component3 = gameObject.GetComponent<RectTransform>(); if ((Object)(object)component3 != (Object)null) { component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.pivot = new Vector2(0.5f, 0.5f); component3.anchoredPosition = Vector2.zero; component3.offsetMin = new Vector2(3f, 3f); component3.offsetMax = new Vector2(-3f, -3f); ((Transform)component3).localScale = Vector3.one; } UITooltip[] componentsInChildren = gameObject.GetComponentsInChildren<UITooltip>(true); for (int j = 0; j < componentsInChildren.Length; j++) { componentsInChildren[j].m_topic = ""; componentsInChildren[j].m_text = ""; ((Behaviour)componentsInChildren[j]).enabled = false; } } else { gameObject = ((Component)val2).gameObject; } gameObject.SetActive(true); Button component4 = gameObject.GetComponent<Button>(); if ((Object)(object)component4 == (Object)null) { return; } TextMeshProUGUI componentInChildren = gameObject.GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = ((kind == 0) ? "CI" : "CT"); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } GoodtentialCycleButton goodtentialCycleButton = gameObject.GetComponent<GoodtentialCycleButton>(); if ((Object)(object)goodtentialCycleButton == (Object)null) { goodtentialCycleButton = gameObject.AddComponent<GoodtentialCycleButton>(); component4.onClick = new ButtonClickedEvent(); ((UnityEvent)component4.onClick).AddListener(new UnityAction(goodtentialCycleButton.Cycle)); } goodtentialCycleButton.Kind = kind; Player localPlayer = Player.m_localPlayer; bool flag = false; if ((Object)(object)localPlayer != (Object)null) { if (kind == 0) { List<int> ownedValidIdolTiers = ForgeSelection.GetOwnedValidIdolTiers(localPlayer); flag = ownedValidIdolTiers.Count > 1 || (ownedValidIdolTiers.Count > 0 && !ownedValidIdolTiers.Contains(ForgeSelection.SelectedIdolTier)); } else { flag = ForgeSelection.SelectedIdolTier >= ForgeSelection.RequiredTier && ForgeSelection.GetUniqueTrophies(localPlayer).Count > 0; } } ((Selectable)component4).interactable = !IsRefining(gui) && flag; } private static void RenderIdolSlot(InventoryGui gui, Player player) { if (gui.m_recipeRequirementList != null && gui.m_recipeRequirementList.Length >= 1) { GameObject root = gui.m_recipeRequirementList[0]; ItemDrop idolDrop = ForgeData.GetIdolDrop(ForgeSelection.Family, ForgeSelection.SelectedIdolTier); int num = 0; if ((Object)(object)idolDrop != (Object)null && idolDrop.m_itemData != null && idolDrop.m_itemData.m_shared != null) { num = ((Humanoid)player).GetInventory().CountItems(idolDrop.m_itemData.m_shared.m_name, -1, true); } SetRequirement(name: ForgeData.TierNames[ForgeSelection.SelectedIdolTier], amount: (ForgeSelection.Family == IdolFamily.Battle) ? "Battle" : "Protection", root: root, sprite: ((Object)(object)idolDrop != (Object)null) ? idolDrop.m_itemData.GetIcon() : null, available: num > 0, showIcon: true); } } private static void RenderTrophySlot(InventoryGui gui, Player player) { if (gui.m_recipeRequirementList == null || gui.m_recipeRequirementList.Length < 3) { return; } GameObject root = gui.m_recipeRequirementList[2]; ItemData selectedTrophy = ForgeSelection.SelectedTrophy; if (selectedTrophy == null) { SetRequirement(root, null, "Trophy", "", available: true, showIcon: false); return; } int num = ((Humanoid)player).GetInventory().CountItems(selectedTrophy.m_shared.m_name, -1, true); string text = ForgeData.LocalizeItemName(selectedTrophy); if (text.EndsWith(" Trophy", StringComparison.CurrentCultureIgnoreCase)) { text = text.Substring(0, text.Length - " Trophy".Length); } SetRequirement(root, selectedTrophy.GetIcon(), text, "", num > 0, showIcon: true); } private static void SetRequirement(GameObject root, Sprite sprite, string name, string amount, bool available, bool showIcon) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return; } Transform val = root.transform.Find("res_icon"); Transform val2 = root.transform.Find("res_name"); Transform val3 = root.transform.Find("res_amount"); Image val4 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null); TMP_Text val5 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<TMP_Text>() : null); TMP_Text val6 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<TMP_Text>() : null); UITooltip component = root.GetComponent<UITooltip>(); if ((Object)(object)val4 != (Object)null) { ((Component)val4).gameObject.SetActive(showIcon); if ((Object)(object)sprite != (Object)null) { val4.sprite = sprite; } ((Graphic)val4).color = Color.white; } if ((Object)(object)val5 != (Object)null) { ((Component)val5).gameObject.SetActive(true); val5.text = name; ((Graphic)val5).color = Color.white; } if ((Object)(object)val6 != (Object)null) { bool flag = !string.IsNullOrEmpty(amount); ((Component)val6).gameObject.SetActive(flag); if (flag) { val6.text = amount; val6.enableAutoSizing = true; val6.fontSizeMin = 8f; ((Graphic)val6).color = (available ? Color.white : Color.red); } } if ((Object)(object)component != (Object)null) { component.m_topic = ""; component.m_text = ""; ((Behaviour)component).enabled = false; } } private static void RenderRisk(InventoryGui gui, Recipe recipe, ItemData item) { int num = Mathf.Max(1, recipe.m_item.m_itemData.m_shared.m_maxQuality); ForgeOdds odds = ForgeRules.GetOdds(Mathf.Max(0, item.m_quality - num), ForgeSelection.RequiredTier, ForgeSelection.SelectedIdolTier, ForgeSelection.SelectedTrophy, ForgeSelection.ItemReferencePrestige); ((Component)gui.m_itemCraftType).gameObject.SetActive(true); float num2 = (odds.FailureIsIgnore ? odds.OrdinaryFailure : 0f); float num3 = (odds.FailureIsIgnore ? 0f : odds.OrdinaryFailure); string text = FormatChance(odds.Success) + " success"; if (num2 > 0.0001f) { text = text + " " + FormatChance(num2) + " ignore"; } if (num3 > 0.0001f) { text = text + " " + FormatChance(num3) + " fail"; } if (odds.CompleteLoss > 0.0001f) { text = text + " " + FormatChance(odds.CompleteLoss) + " reset"; } if (odds.Destruction > 0.0001f) { text = text + " " + FormatChance(odds.Destruction) + " destruction"; } gui.m_itemCraftType.text = text; } private static string FormatChance(float chance) { float num = Mathf.Round(Mathf.Clamp01(chance) * 1000f) / 10f; float num2 = Mathf.Round(num); if (Mathf.Approximately(num, num2)) { return Mathf.RoundToInt(num) + "%"; } return num.ToString("0.0") + "%"; } private static void UpdateCraftButton(InventoryGui gui, Player player, CraftingStation station, Recipe recipe, ItemData item) { bool flag = player.NoCostCheat() || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)); bool flag2 = ForgeSelection.HasRequiredIdol(player); bool flag3 = ForgeSelection.HasSelectedTrophy(player); bool flag4 = station.CheckUsable(player, false); bool flag5 = ((Humanoid)player).GetInventory().ContainsItem(item); bool flag6 = flag2 && flag3; ((Selectable)gui.m_craftButton).interactable = flag5 && flag4 && (flag || flag6); UITooltip component = ((Component)gui.m_craftButton).GetComponent<UITooltip>(); if (!((Object)(object)component == (Object)null)) { if (!flag && !flag2) { ItemDrop idolDrop = ForgeData.GetIdolDrop(ForgeSelection.Family, ForgeSelection.RequiredTier); string text = (((Object)(object)idolDrop != (Object)null) ? ForgeData.LocalizeDropName(idolDrop) : (ForgeData.TierNames[ForgeSelection.RequiredTier] + " Idol")); component.m_text = "Requires " + text + " or a stronger idol."; } else if (!flag && !flag3) { component.m_text = "The selected trophy is no longer in your inventory."; } else { component.m_text = ""; } } } } [HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")] internal static class ForgeCraftCancelPatch { [HarmonyPostfix] private static void Postfix() { ForgeSelection.CancelPending(); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] internal static class ForgeInventoryHidePatch { [HarmonyPostfix] private static void Postfix() { ForgeSelection.ResetAll(); } } [BepInPlugin("wire.valheim.forgeofgoodtential", "Wire's Forge of Goodtential", "0.1.15")] [BepInProcess("valheim.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "wire.valheim.forgeofgoodtential"; public const string Name = "Wire's Forge of Goodtential"; public const string Version = "0.1.15"; 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.forgeofgoodtential"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Forge of Goodtential 0.1.15 loaded."); } private void OnDestroy() { ForgeContext.Restore(); ForgeSelection.ResetAll(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } }