using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using IL.RoR2; using Microsoft.CodeAnalysis; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.RuntimeDetour; using RiskySotS.Tweaks.Progression; using RoR2; [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("LessGuaranteedAccessNodes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a58ebc764e79e2bd931f6d2d12e2474f433dee3e")] [assembly: AssemblyProduct("LessGuaranteedAccessNodes")] [assembly: AssemblyTitle("LessGuaranteedAccessNodes")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 RoR2LessGuaranteedAccessNodes { internal static class Log { private static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } internal static class RiskySotsInterop { [CompilerGenerated] private static class <>O { public static Manipulator <0>__IL_ModifyRiskySotsBehavior; public static Func <1>__ShouldAccessNodesSpawn; } private static ILHook riskySotsHook; public static void Load() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0031: 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_003c: Expected O, but got Unknown MethodInfo method = typeof(HabitatAccessNodes).GetMethod("SceneDirector_onPrePopulateSceneServer", BindingFlags.Instance | BindingFlags.NonPublic); if (method != null) { object obj = <>O.<0>__IL_ModifyRiskySotsBehavior; if (obj == null) { Manipulator val = IL_ModifyRiskySotsBehavior; <>O.<0>__IL_ModifyRiskySotsBehavior = val; obj = (object)val; } riskySotsHook = new ILHook((MethodBase)method, (Manipulator)obj); } else { Log.Error("Could not find SceneDirector_onPrePopulateSceneServer in RiskySotS code"); } } public static void Unload() { ILHook obj = riskySotsHook; if (obj != null) { obj.Dispose(); } riskySotsHook = null; } private static void IL_ModifyRiskySotsBehavior(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (val.TryGotoNext((MoveType)2, new Func[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt(instr, typeof(HabitatAccessNodes), "IsHabitat") })) { val.EmitDelegate>((Func)RoR2LessGuaranteedAccessNodes.ShouldAccessNodesSpawn); val.Emit(OpCodes.And); } else { Log.Error("Failed to match IsHabitat check in RiskySotS code"); } } } [BepInPlugin("SSM24.LessGuaranteedAccessNodes", "LessGuaranteedAccessNodes", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class RoR2LessGuaranteedAccessNodes : BaseUnityPlugin { public const string PluginGUID = "SSM24.LessGuaranteedAccessNodes"; public const string PluginAuthor = "SSM24"; public const string PluginName = "LessGuaranteedAccessNodes"; public const string PluginVersion = "1.0.0"; private static ConfigEntry guaranteedOnSecondRoll; private static ConfigEntry guaranteedAfterLoop; private static readonly Dictionary defaultStageConfigs = new Dictionary { ["Scorched Acres"] = 0.2f, ["Rallypoint Delta"] = 0.5f, ["Sulfur Pools"] = 0.33f, ["Iron Alluvium/Aurora"] = 0.75f, ["Repurposed Crater"] = 0.6f, ["Treeborn Colony / Golden Dieback"] = 0.33f, ["Fogbound Lagoon"] = 0.33f, ["Remote Village"] = 0.33f }; private static readonly HashSet moddedStages = new HashSet { "Fogbound Lagoon", "Remote Village" }; private static readonly Dictionary stageConfigs = new Dictionary(); private bool riskySotsLoaded; private const string RejectedAccessNodesOnce = "ssm_RejectedAccessNodes"; private const string SeenAccessNodesOnce = "ssm_SeenAccessNodes"; public void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Log.Init(((BaseUnityPlugin)this).Logger); InitializeSettings(); AccessCodesMissionController.OnStartServer += new Manipulator(IL_ModifyAccessCodesMissionController); riskySotsLoaded = Chainloader.PluginInfos.ContainsKey("com.RiskyLives.RiskySotS"); if (riskySotsLoaded) { RiskySotsInterop.Load(); } } public void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown AccessCodesMissionController.OnStartServer -= new Manipulator(IL_ModifyAccessCodesMissionController); if (riskySotsLoaded) { RiskySotsInterop.Unload(); } } private void InitializeSettings() { guaranteedOnSecondRoll = ((BaseUnityPlugin)this).Config.Bind("Basic Settings", "Guaranteed by Second Roll", true, "If the first Access Node roll fails, guarantee the spawn on the second roll.\r\nWill not do anything if the first roll succeeds."); guaranteedAfterLoop = ((BaseUnityPlugin)this).Config.Bind("Basic Settings", "Guaranteed After Looping", false, "Guarantees Access Node spawn after looping at least once."); foreach (KeyValuePair defaultStageConfig in defaultStageConfigs) { string key = defaultStageConfig.Key; string text = (moddedStages.Contains(key) ? "Modded Stages" : "Default Stages"); string text2 = "Chance for spawn on " + key; if (key == "Treeborn Colony / Golden Dieback") { text2 += "\nDoes nothing unless a mod like RiskySotS adds Access Node spawns to this stage"; } ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind(text, key, defaultStageConfig.Value, text2); stageConfigs[key] = val.Value; } } private void IL_ModifyAccessCodesMissionController(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (val.TryGotoNext((MoveType)2, new Func[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, 0) })) { val.Emit(OpCodes.Ldloc_0); val.EmitDelegate>((Func)ShouldAccessNodesSpawn); val.Emit(OpCodes.And); val.Emit(OpCodes.Stloc_0); } } internal static bool ShouldAccessNodesSpawn() { if (guaranteedAfterLoop.Value && Run.instance.loopClearCount > 0) { Log.Info("Guaranteeing post-loop Access Node spawn..."); return true; } if (guaranteedOnSecondRoll.Value && Run.instance.GetEventFlag("ssm_RejectedAccessNodes") && !Run.instance.GetEventFlag("ssm_SeenAccessNodes")) { Log.Info("Guaranteeing second-attempt Access Node spawn..."); Run.instance.SetEventFlag("ssm_SeenAccessNodes"); return true; } string baseSceneName = SceneCatalog.GetSceneDefForCurrentScene().baseSceneName; float num = baseSceneName switch { "wispgraveyard" => stageConfigs["Scorched Acres"], "frozenwall" => stageConfigs["Rallypoint Delta"], "sulfurpools" => stageConfigs["Sulfur Pools"], "ironalluvium" => stageConfigs["Iron Alluvium/Aurora"], "ironalluvium2" => stageConfigs["Iron Alluvium/Aurora"], "FBLScene" => stageConfigs["Fogbound Lagoon"], "agatevillage" => stageConfigs["Remote Village"], "repurposedcrater" => stageConfigs["Repurposed Crater"], "habitat" => stageConfigs["Treeborn Colony / Golden Dieback"], "habitatfall" => stageConfigs["Treeborn Colony / Golden Dieback"], _ => 0.5f, }; float nextNormalizedFloat = Run.instance.stageRng.nextNormalizedFloat; Log.Debug($"{baseSceneName}: rolled {nextNormalizedFloat} (needed: <{num})"); if (nextNormalizedFloat < num) { Run.instance.SetEventFlag("ssm_SeenAccessNodes"); return true; } Run.instance.SetEventFlag("ssm_RejectedAccessNodes"); return false; } } }