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 NkuhanaTrialSkipper v1.0.0
plugins/NkuhanaTrialSkipper/NkuhanaTrialSkipper.dll
Decompiled 19 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using RoR2.Quests; using RoR2.Quests.TrialQuests; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("NkuhanaTrialSkipper")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("NkuhanaTrialSkipper")] [assembly: AssemblyTitle("NkuhanaTrialSkipper")] [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 NkuhanaTrialSkipper { [BepInPlugin("com.zcode.NkuhanaTrialSkipper", "NkuhanaTrialSkipper", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class NkuhanaTrialSkipperPlugin : BaseUnityPlugin { public const string PluginGuid = "com.zcode.NkuhanaTrialSkipper"; public const string PluginName = "NkuhanaTrialSkipper"; public const string PluginVersion = "1.0.0"; internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<float> TimeCostSeconds { get; private set; } internal static ConfigEntry<bool> MaximizeBronzeChoices { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Enabled", true, "总开关。开启后每次进入 N'kuhana 试炼(Hallowed Concepts DLC)时立即按成功结算。"); TimeCostSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Settings", "TimeCostSeconds", 60f, "每次跳过结算后,Reckoning 剩余时间直接设置到的档位差(每局总预算 210 秒):210 → 150 → 90 → 30。剩余时间不足该值时不触发跳过,试炼正常进行。"); MaximizeBronzeChoices = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "MaximizeBronzeChoices", true, "跳过结算时,把铜手(Greater Trial Blessing)的自选列表最大化:注入该试炼可能刷出的全部虚灵物品种类(原版规则是只列出本次试炼实际捡到的)。"); Harmony.CreateAndPatchAll(typeof(TrialSkipperPatches), (string)null); RegisterRiskOfOptions(); Log.LogInfo((object)"NkuhanaTrialSkipper v1.0.0 loaded."); } private void RegisterRiskOfOptions() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions")) { ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled)); ModSettingsManager.AddOption((BaseOption)new SliderOption(TimeCostSeconds, new SliderConfig { min = 0f, max = 210f })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(MaximizeBronzeChoices)); } } } internal static class RewardPools { internal static IReadOnlyList<ItemDef> GetPool(BaseTrialQuest quest) { if (!(quest is TrialQuestSadismOpenChests)) { if (!(quest is TrialQuestSadismBreakTargets)) { if (!(quest is TrialQuestSadismMoney)) { if (quest is TrialSpeedrunA || quest is TrialSpeedrunB || quest is TrialSpeedrunC) { return (IReadOnlyList<ItemDef>)(object)new ItemDef[5] { Items.Hoof, Items.SprintBonus, Items.Feather, Items.JumpBoost, Items.SprintOutOfCombat }; } if (!(quest is TrialQuestSurvivorProcCity)) { if (!(quest is TrialQuestSurvivorUmbra)) { if (quest is TrialQuestSurvivorMonstersOffMap) { return (IReadOnlyList<ItemDef>)(object)new ItemDef[6] { Items.Hoof, Items.SpeedOnPickup, Items.SprintOutOfCombat, Items.SpeedBoostPickup, Items.JumpBoost, Items.MoveSpeedOnKill }; } return null; } return (IReadOnlyList<ItemDef>)(object)new ItemDef[14] { Items.Bear, Items.BarrierOnKill, Items.IgniteOnKill, Items.Hoof, Items.AttackSpeedPerNearbyAllyOrEnemy, Items.Tooth, Items.SlowOnHit, Items.JumpBoost, Items.ChainLightning, Items.Missile, Items.PrimarySkillShuriken, Items.KillEliteFrenzy, Items.GhostOnKill, Items.SprintArmor }; } return (IReadOnlyList<ItemDef>)(object)new ItemDef[10] { Items.IgniteOnKill, Items.StickyBomb, Items.Dagger, Items.SecondarySkillMagazine, Items.Missile, Items.Thorns, Items.ChainLightning, Items.FireRing, Items.IceRing, Items.GhostOnKill }; } return (IReadOnlyList<ItemDef>)(object)new ItemDef[10] { Items.StickyBomb, Items.NearbyDamageBonus, Items.Syringe, Items.IgniteOnKill, Items.Firework, Items.Hoof, Items.GoldOnHit, Items.SpeedOnPickup, Items.ExecuteLowHealthElite, Items.ChainLightning }; } return (IReadOnlyList<ItemDef>)(object)new ItemDef[2] { Items.Hoof, Items.SprintBonus }; } return (IReadOnlyList<ItemDef>)(object)new ItemDef[3] { Items.Hoof, Items.StunChanceOnHit, Items.Firework }; } } [HarmonyPatch] internal static class TrialSkipperPatches { private static readonly HashSet<BaseTrialQuest> ArmedQuests = new HashSet<BaseTrialQuest>(); private static readonly HashSet<BaseTrialQuest> SkippedQuests = new HashSet<BaseTrialQuest>(); private static readonly MethodInfo EndQuestMethod = AccessTools.Method(typeof(BaseQuest), "EndQuest", (Type[])null, (Type[])null); private static readonly FieldInfo PickedUpItemsField = AccessTools.Field(typeof(CreateItemPotentialFromPickedUpItems), "_pickedUpItems"); private static DLC4TrialSelector _trackedSelector; private static int _skipsThisRun; [HarmonyPatch(typeof(BaseTrialQuest), "OnEnter")] [HarmonyPostfix] private static void ArmQuestOnEnter(BaseTrialQuest __instance) { if (NetworkServer.active) { ArmedQuests.Add(__instance); } } [HarmonyPatch(typeof(QuestRunner), "Update")] [HarmonyPostfix] private static void TriggerSkipOnQuestActive(QuestRunner __instance) { if (NetworkServer.active && ArmedQuests.Count != 0) { BaseQuest quest = __instance.quest; BaseTrialQuest val = (BaseTrialQuest)(object)((quest is BaseTrialQuest) ? quest : null); if (val != null && ArmedQuests.Remove(val)) { TrySkipQuest(val); } } } [HarmonyPatch(typeof(BaseTrialQuest), "OnExit")] [HarmonyPostfix] private static void ApplyTimeCostOnExit(BaseTrialQuest __instance) { if (!NetworkServer.active) { return; } ArmedQuests.Remove(__instance); if (SkippedQuests.Remove(__instance)) { DLC4TrialSelector trialSelector = GetTrialSelector(); if (!Object.op_Implicit((Object)(object)trialSelector)) { NkuhanaTrialSkipperPlugin.Log.LogWarning((object)"Time cost not applied: DLC4TrialSelector not found."); return; } EnsureRunTracking(trialSelector); _skipsThisRun++; float value = NkuhanaTrialSkipperPlugin.TimeCostSeconds.Value; float num = Mathf.Max(DLC4TrialSelector.totalAllotedSecondsForTrials - value * (float)_skipsThisRun, 0f); trialSelector.SetRemainingTrialTimeServer(num); NkuhanaTrialSkipperPlugin.Log.LogInfo((object)$"Trial skip settled (#{_skipsThisRun} this run), Reckoning set to {num:F0}s."); } } private static void TrySkipQuest(BaseTrialQuest quest) { if (!NkuhanaTrialSkipperPlugin.Enabled.Value) { return; } DLC4TrialSelector trialSelector = GetTrialSelector(); if (!Object.op_Implicit((Object)(object)trialSelector)) { NkuhanaTrialSkipperPlugin.Log.LogWarning((object)"Skip not triggered: DLC4TrialSelector not found."); return; } float remainingTrialTimeServer = trialSelector.GetRemainingTrialTimeServer(); float value = NkuhanaTrialSkipperPlugin.TimeCostSeconds.Value; if (remainingTrialTimeServer < value) { NkuhanaTrialSkipperPlugin.Log.LogInfo((object)$"Skip not triggered: Reckoning remaining {remainingTrialTimeServer:F1}s is below the required {value:F0}s, playing the trial normally."); return; } EnsureRunTracking(trialSelector); float num = Mathf.Max(DLC4TrialSelector.totalAllotedSecondsForTrials - value * (float)(_skipsThisRun + 1), 0f); SkippedQuests.Add(quest); InjectRewardPool(quest); EndQuestMethod.Invoke(quest, new object[1] { true }); NkuhanaTrialSkipperPlugin.Log.LogInfo((object)$"Trial skip triggered: instant success settlement, Reckoning will be set to {num:F0}s on settle (currently {remainingTrialTimeServer:F1}s)."); } private static void InjectRewardPool(BaseTrialQuest quest) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if (!NkuhanaTrialSkipperPlugin.MaximizeBronzeChoices.Value) { return; } IReadOnlyList<ItemDef> pool = RewardPools.GetPool(quest); if (pool == null || pool.Count == 0) { NkuhanaTrialSkipperPlugin.Log.LogInfo((object)("No reward pool mapped for " + ((object)quest).GetType().Name + ", bronze hand stays vanilla.")); return; } CreateItemPotentialFromPickedUpItems[] array = Object.FindObjectsOfType<CreateItemPotentialFromPickedUpItems>(true); if (array.Length == 0) { NkuhanaTrialSkipperPlugin.Log.LogWarning((object)"Bronze hand not maximized: no CreateItemPotentialFromPickedUpItems found in scene."); return; } CreateItemPotentialFromPickedUpItems[] array2 = array; foreach (CreateItemPotentialFromPickedUpItems obj in array2) { if (!(PickedUpItemsField.GetValue(obj) is HashSet<PickupIndex> hashSet)) { continue; } int num = 0; foreach (ItemDef item2 in pool) { if (Object.op_Implicit((Object)(object)item2)) { PickupIndex item = PickupCatalog.FindPickupIndex(item2.itemIndex); if (((PickupIndex)(ref item)).isValid && hashSet.Add(item)) { num++; } } } NkuhanaTrialSkipperPlugin.Log.LogInfo((object)$"Bronze hand choice list maximized: injected {num}/{pool.Count} ethereal item types."); } } private static DLC4TrialSelector GetTrialSelector() { DLC4ProgressionController instance = DLC4ProgressionController.instance; if (!Object.op_Implicit((Object)(object)instance)) { return null; } return instance.trialSelector; } private static void EnsureRunTracking(DLC4TrialSelector trialSelector) { if (_trackedSelector != trialSelector) { _trackedSelector = trialSelector; _skipsThisRun = 0; } } } }