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 T4ylorTweaks v1.0.3
plugins/T4ylorTweaks/T4ylorTweaks.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using EntityStates; using IL.RoR2; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.RuntimeDetour; using On.RoR2; using On.RoR2.UI; using R2API.Networking; using R2API.Networking.Interfaces; using RoR2; using RoR2.CharacterAI; using RoR2.ExpansionManagement; using RoR2.Projectile; using RoR2.UI; using TMPro; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("T4ylorTweaks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("T4ylorTweaks gameplay tweaks, Simulacrum fog, Moon escape and selected Eclipse changes.")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: AssemblyProduct("T4ylorTweaks")] [assembly: AssemblyTitle("T4ylorTweaks")] [assembly: AssemblyVersion("1.0.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 T4ylorTweaks { public struct BazaarRandom { public ulong State; public BazaarRandom(ulong state) { State = state; } public static BazaarRandom ForVisit(ulong runSeed, uint visit, bool heresy) { return new BazaarRandom(runSeed ^ (ulong)(visit * -3372029247567499371L) ^ (ulong)(heresy ? 6067572782258079285L : 6067572782258079284L)); } public ulong Next() { ulong num = (State += 11400714819323198485uL); long num2 = (long)(num ^ (num >> 30)) * -4658895280553007687L; long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L; return (ulong)(num3 ^ (num3 >>> 31)); } public double Fraction() { return (double)(Next() >> 11) * 1.1102230246251565E-16; } public int Index(int count) { if (count <= 0) { throw new ArgumentOutOfRangeException("count"); } ulong num = (ulong)count; ulong num2 = (0 - num) % num; ulong num3; do { num3 = Next(); } while (num3 < num2); return (int)(num3 % num); } } public static class HeresyRules { public static T Choose<T>(IReadOnlyList<T> heresy, IReadOnlyList<T> other, float probability, ref BazaarRandom rng) { if (heresy.Count == 0 && other.Count == 0) { throw new InvalidOperationException("No eligible lunar merchandise."); } if (float.IsNaN(probability) || float.IsInfinity(probability)) { probability = 0.85f; } probability = Math.Max(0f, Math.Min(1f, probability)); bool flag = rng.Fraction() < (double)probability; IReadOnlyList<T> readOnlyList = (flag ? heresy : other); if (readOnlyList.Count == 0) { readOnlyList = (flag ? other : heresy); } return readOnlyList[rng.Index(readOnlyList.Count)]; } } public sealed class BazaarVisitGate<TPlayer> where TPlayer : notnull { private readonly HashSet<TPlayer> used = new HashSet<TPlayer>(); private TPlayer current; public bool Busy { get; private set; } public bool HasUsed(TPlayer player) { return used.Contains(player); } public bool HasRemainingUse(IEnumerable<TPlayer> players) { foreach (TPlayer player in players) { if (!HasUsed(player)) { return true; } } return false; } public bool TryBegin(TPlayer player) { if (Busy || HasUsed(player)) { return false; } current = player; Busy = true; return true; } public void Finish(bool success) { if (!Busy) { throw new InvalidOperationException("No active refresh."); } if (success) { used.Add(current); } current = default(TPlayer); Busy = false; } public void Reset() { Busy = false; current = default(TPlayer); used.Clear(); } } public static class BazaarVisitGate { public const int Cost = 3; public const int SlotCount = 5; } public enum ReplacementRule { Vanilla, Replacement } public enum AdjustmentRule { Vanilla, Adjusted } public enum IncomeStagePolicy { VerifiedOrdinaryStages, AllAdventureCombatStages } public sealed class Configuration { private readonly Dictionary<string, ConfigEntry<bool>> features = new Dictionary<string, ConfigEntry<bool>>(); public static IReadOnlyList<string> FeatureIds { get; } = new ReadOnlyCollection<string>(new string[9] { "F01", "F02", "F03", "F04", "F05", "F06", "F07", "F08", "F09" }); public ConfigEntry<IncomeStagePolicy> IncomeStages { get; } public ConfigEntry<float> DepartureWindowSeconds { get; } public ConfigEntry<float> HeresyProbability { get; } public ConfigEntry<float> AmbryCreditMultiplier { get; } public ConfigEntry<float> BoardingDebounceSeconds { get; } public ConfigEntry<float> FogInitialMultiplier { get; } public ConfigEntry<float> FogBlockGrowth { get; } public ConfigEntry<float> FogMaximumMultiplier { get; } public ConfigEntry<ReplacementRule> Eclipse1 { get; } public ConfigEntry<ReplacementRule> Eclipse2 { get; } public ConfigEntry<bool> Eclipse2Enabled { get; } public ConfigEntry<ReplacementRule> Eclipse5 { get; } public ConfigEntry<ReplacementRule> Eclipse6 { get; } public ConfigEntry<float> HealingRate { get; } public ConfigEntry<float> EnemyGoldExponent { get; } public ConfigEntry<AdjustmentRule> Eclipse8 { get; } public Configuration(ConfigFile file) { //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Expected O, but got Unknown //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Expected O, but got Unknown string[] array = new string[9] { "Enemy-free income", "Departure scanner", "Stage-wide teleporter", "Bazaar refresh", "Heresy bud", "Ambry density", "Ship boarding", "Simulacrum fog", "Automatic Deep Void portal" }; for (int i = 0; i < FeatureIds.Count; i++) { features.Add(FeatureIds[i], file.Bind<bool>(FeatureIds[i] + " " + array[i], "Enabled", true, "Enable this feature. Server settings are frozen at run start; edits apply next run.")); } IncomeStages = file.Bind<IncomeStagePolicy>("F01 Enemy-free income", "StagePolicy", IncomeStagePolicy.VerifiedOrdinaryStages, "VerifiedOrdinaryStages uses the installed normal-stage allowlist plus Gold Coast. AllAdventureCombatStages opts into all SceneType.Stage/UntimedStage scenes, including Moon/void combat scenes. Both exclude non-adventure runs."); DepartureWindowSeconds = Float(file, "F02 Departure scanner", "RevealWindowSeconds", 3f, 0f, 10f, "Minimum scanner window in seconds before this departure controller progresses. Money conversion and gameplay continue. Provisional default 3; zero adds no hold."); HeresyProbability = Float(file, "F05 Heresy bud", "HeresyProbability", 0.85f, 0f, 1f, "Overall probability, 0 to 1, for the asset-mapped customer-left lunar bud on initial stock and refresh. Fallback excludes Heresy. Live screenshot confirmation remains pending."); AmbryCreditMultiplier = Float(file, "F06 Ambry density", "CreditIncomeMultiplier", 1.5f, 1f, 10f, "Provisional active-trial enemy director income multiplier; not a guarantee of proportional enemy counts."); BoardingDebounceSeconds = Float(file, "F07 Ship boarding", "DebounceSeconds", 0.5f, 0f, 5f, "Seconds all eligible living participants must remain simultaneously inside the verified ship bay."); FogInitialMultiplier = Float(file, "F08 Simulacrum fog", "InitialMultiplier", 0.67f, 0f, 10f, "Multiplier on the vanilla player-team environmental fog hit at waves 1 through 5."); FogBlockGrowth = Float(file, "F08 Simulacrum fog", "FiveWaveGrowthMultiplier", 1.2f, 1f, 5f, "Multiplicative growth starting on waves 6, 11, 16, etc.; global run wave number."); FogMaximumMultiplier = Float(file, "F08 Simulacrum fog", "MaximumMultiplier", 0f, 0f, 1000f, "Zero means uncapped. Positive values cap the final multiplier relative to vanilla."); Eclipse1 = file.Bind<ReplacementRule>("Eclipse", "E1", ReplacementRule.Replacement, "Replacement: normal starting health and one ordinary-stage champion, with 20% gold. Vanilla retains half starting health. Applies next run."); Eclipse2 = file.Bind<ReplacementRule>("Eclipse", "E2", ReplacementRule.Replacement, "Replacement: normal teleporter radius and 1.20x event credits. Vanilla: original radius penalty. Both suppress F03 at Eclipse 2+. Applies next run."); Eclipse2Enabled = file.Bind<bool>("Eclipse", "E2Enabled", true, "False removes only E2's radius/credit rule and permits F03 at Eclipse 2+. Other Eclipse levels and progression are unchanged. Applies next run."); Eclipse5 = file.Bind<ReplacementRule>("Eclipse", "E5", ReplacementRule.Replacement, "Replacement admits 75% of eligible healing once and delivers over time. Includes regeneration; owned Rack/Corpsebloom bonuses apply once, with Corpsebloom imposing its slower cap. No overheal banking; resets on death/body/stage changes. Vanilla retains original E5. Applies next run."); Eclipse6 = file.Bind<ReplacementRule>("Eclipse", "E6", ReplacementRule.Replacement, "Replacement compresses verified enemy reward growth using GrowthExponent, preserves the player-count baseline and replaces native 20% reduction. F01, item gold and prices stay native. Unknown reward setup falls back to vanilla E6. Applies next run."); HealingRate = file.Bind<float>("Eclipse E5", "MaximumHealthPerSecond", 0.2f, new ConfigDescription("Selected default: 20% of current maximum health per second. Owned Corpsebloom caps delivery at the smaller of this rate and vanilla 10% divided by stack count: 10%, 5%, 3.33%, 2.5%, etc. Applies next run.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.101f, 1f), Array.Empty<object>())); EnemyGoldExponent = file.Bind<float>("Eclipse E6", "GrowthExponent", 0.75f, new ConfigDescription("Selected provisional exponent on enemy time/stage reward growth relative to the native player baseline. Default 0.75; gameplay affordability tuning remains pending. Applies next run.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 0.99f), Array.Empty<object>())); Eclipse8 = file.Bind<AdjustmentRule>("Eclipse", "E8", AdjustmentRule.Adjusted, "Adjusted exempts verified REX, Void Fiend, Seeker ability costs and Captain Diablo Strike self-hits from E8 curse only. Damage, enemy hits, falls, Blood Shrines and Void Cradles remain native. Vanilla retains all E8 curse. Applies next run."); } public ConfigEntry<bool> Feature(string id) { return features[id]; } public RunSettings Snapshot() { return new RunSettings(this); } private static ConfigEntry<float> Float(ConfigFile file, string section, string key, float value, float minimum, float maximum, string description) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown return file.Bind<float>(section, key, value, new ConfigDescription(description + " Applies next run.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), Array.Empty<object>())); } } public sealed class RunSettings { public IReadOnlyDictionary<string, bool> Features { get; } public IncomeStagePolicy IncomeStages { get; } public float DepartureWindowSeconds { get; } public float HeresyProbability { get; } public float AmbryCreditMultiplier { get; } public float BoardingDebounceSeconds { get; } public float FogInitialMultiplier { get; } public float FogBlockGrowth { get; } public float FogMaximumMultiplier { get; } public ReplacementRule Eclipse1 { get; } public ReplacementRule Eclipse2 { get; } public bool Eclipse2Enabled { get; } public ReplacementRule Eclipse5 { get; } public ReplacementRule Eclipse6 { get; } public float HealingRate { get; } public float EnemyGoldExponent { get; } public AdjustmentRule Eclipse8 { get; } internal RunSettings(Configuration config) { Dictionary<string, bool> dictionary = new Dictionary<string, bool>(); foreach (string featureId in Configuration.FeatureIds) { dictionary.Add(featureId, config.Feature(featureId).Value); } Features = new ReadOnlyDictionary<string, bool>(dictionary); IncomeStages = config.IncomeStages.Value; DepartureWindowSeconds = config.DepartureWindowSeconds.Value; HeresyProbability = config.HeresyProbability.Value; AmbryCreditMultiplier = config.AmbryCreditMultiplier.Value; BoardingDebounceSeconds = config.BoardingDebounceSeconds.Value; FogInitialMultiplier = config.FogInitialMultiplier.Value; FogBlockGrowth = config.FogBlockGrowth.Value; FogMaximumMultiplier = config.FogMaximumMultiplier.Value; Eclipse1 = config.Eclipse1.Value; Eclipse2 = config.Eclipse2.Value; Eclipse2Enabled = config.Eclipse2Enabled.Value; Eclipse5 = config.Eclipse5.Value; Eclipse6 = config.Eclipse6.Value; HealingRate = config.HealingRate.Value; EnemyGoldExponent = config.EnemyGoldExponent.Value; Eclipse8 = config.Eclipse8.Value; } } public enum EclipseMedalTone { Unearned, Silver, Gold } public static class EclipseAccessRules { public static bool IsSelectableLevel(int level) { if (level >= 1) { return level <= 8; } return false; } public static int LobbyPenaltyLevel(int nativeLevel, int selectedLevel) { if (!IsSelectableLevel(selectedLevel)) { return Math.Max(1, Math.Min(8, nativeLevel)); } return selectedLevel; } public static EclipseMedalTone MedalTone(int completed, int allSurvivorMinimum, int level) { if (HasBadge(completed, level)) { if (allSurvivorMinimum < level) { return EclipseMedalTone.Silver; } return EclipseMedalTone.Gold; } return EclipseMedalTone.Unearned; } public static bool HasBadge(int completed, int level) { if (IsSelectableLevel(level)) { return completed >= level; } return false; } public static string ProgressNotice(string survivor, int completed, int selected) { if (!IsSelectableLevel(selected)) { return "Select an Eclipse level. Wins advance one badge at a time."; } if (completed >= 8) { return survivor + " has already earned all Eclipse badges."; } if (selected <= completed) { return "A win at Eclipse " + selected + " will not award a new Eclipse badge for " + survivor + "."; } return "A win at Eclipse " + selected + " will only award the Eclipse " + (completed + 1) + " badge for " + survivor + "."; } public static int ProgressAfterWin(int completed, int played, bool won) { completed = Math.Max(0, Math.Min(8, completed)); if (!won || played < completed + 1 || played > 8) { return completed; } return completed + 1; } } public sealed class EclipseAwardGate<T> { private readonly HashSet<T> processed = new HashSet<T>(); public bool TryAward(T identity, int completed, int played, bool won, out int after) { after = EclipseAccessRules.ProgressAfterWin(completed, played, won); if (after > completed) { return processed.Add(identity); } return false; } } public static class EclipseDescriptionText { public const string Level1 = "Stages start with a boss enemy costing no credits that yields little gold."; public const string Level2 = "Teleporter credits increased +20%; removes stage-wide charging."; public const string Level5 = "Healing -25%; all healing delivered over time."; public const string Level6 = "Gold gain scales worse through runs"; public const string Level8 = "Permanent health loss from damage; Self-damaging abilities excluded"; public static string Highlight(string text) { return "<color=#C586FF><b>" + text + "</b></color>"; } public static string Format(int level, string text) { if (level != 8 || !(text == "Permanent health loss from damage; Self-damaging abilities excluded")) { return Highlight(text); } return "Permanent health loss from damage; " + Highlight("Self-damaging abilities excluded"); } public static string ReplaceLine(string text, int level, string replacement) { return Regex.Replace(text, "(<mspace=[^>]+>\\(" + level + "\\)</mspace>)[^\\r\\n]*", delegate(Match match) { int num = 0; StringBuilder stringBuilder = new StringBuilder(); foreach (Match item in Regex.Matches(match.Value.Substring(match.Groups[1].Length), "<style(?:=[^>]+)?>|</style>")) { if (!item.Value.StartsWith("</")) { num++; } else if (num > 0) { num--; } else { stringBuilder.Append(item.Value); } } return match.Groups[1].Value + " " + Format(level, replacement) + stringBuilder; }); } } public readonly struct EclipseOverviewLayout { public const double HeaderHeight = 44.0; public const double BadgePanelHeight = 120.0; public const double BadgeGap = 12.0; public const double PortraitSpacing = 8.0; public const double NormalBadgeSize = 64.0; public int Rows { get; } public double CellSize { get; } public double ContentHeight { get; } public double ViewportHeight { get; } public double BadgeTop => 44.0 + ViewportHeight + 12.0; public double BadgeSize { get; } public bool Scrolls => ContentHeight > ViewportHeight + 0.01; private EclipseOverviewLayout(int rows, double cell, double content, double viewport, double badge) { Rows = rows; CellSize = cell; ContentHeight = content; ViewportHeight = viewport; BadgeSize = badge; } public static EclipseOverviewLayout Calculate(double width, double height, int count, int columns, double horizontalPadding, double verticalPadding) { if (double.IsNaN(width) || double.IsInfinity(width) || width <= 0.0) { width = 1.0; } if (double.IsNaN(height) || double.IsInfinity(height) || height <= 0.0) { height = 1.0; } count = Math.Max(0, count); columns = Math.Max(1, columns); int num = count / columns + ((count % columns != 0) ? 1 : 0); double num2 = Math.Max(1.0, (width - horizontalPadding - (double)(columns - 1) * 8.0) / (double)columns); double num3 = verticalPadding + (double)num * num2 + (double)Math.Max(0, num - 1) * 8.0; double val = Math.Max(1.0, height - 44.0 - 120.0 - 12.0); double badge = Math.Min(64.0, Math.Max(1.0, (width - 28.0 - 84.0) / 8.0)); return new EclipseOverviewLayout(num, num2, num3, Math.Min(Math.Max(1.0, num3), val), badge); } } internal sealed class AbilityCurseFeature : IDisposable { internal static readonly string[] StateSources = new string[9] { "EntityStates.FireFlower2|OnEnter", "EntityStates.Treebot.Proliferation|OnEnter", "EntityStates.Treebot.Weapon.AimFlower|OnProjectileFiredLocal", "EntityStates.Treebot.Weapon.AimMortar2|OnProjectileFiredLocal", "EntityStates.Treebot.Weapon.FireMortar2|OnEnter", "EntityStates.Treebot.Weapon.FireSonicBoom|OnEnter", "EntityStates.Treebot.Weapon.FirePlantSonicBoom|OnEnter", "EntityStates.VoidSurvivor.Weapon.CrushBase|OnEnter", "EntityStates.VoidSurvivor.Weapon.Suppress|FixedUpdate" }; private readonly Func<PhaseSettings?> settings; private readonly Func<PhaseSettings?> descriptionSettings; private readonly ManualLogSource log; private readonly List<ILHook> hooks = new List<ILHook>(); private readonly Dictionary<DamageInfo, HealthComponent> classified = new Dictionary<DamageInfo, HealthComponent>(); private FieldInfo? passenger; private bool curseAttached; internal bool Ready { get; private set; } private bool Active { get { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 if (Ready) { PhaseSettings phaseSettings = settings(); if (phaseSettings != null && phaseSettings.AbilityCurseExemption && NetworkServer.active && Run.instance is EclipseRun) { return (int)Run.instance.selectedDifficulty >= 10; } } return false; } } internal AbilityCurseFeature(Func<PhaseSettings?> settings, Func<PhaseSettings?> descriptionSettings, ManualLogSource log) { this.settings = settings; this.descriptionSettings = descriptionSettings; this.log = log; } internal void Enable() { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown try { string[] stateSources = StateSources; for (int i = 0; i < stateSources.Length; i++) { string[] array = stateSources[i].Split('|'); Type type = typeof(Run).Assembly.GetType(array[0], throwOnError: true); if (!typeof(EntityState).IsAssignableFrom(type)) { throw new InvalidOperationException("Ability source is no longer a state."); } hooks.Add(new ILHook((MethodBase)Method(type, array[1]), (Manipulator)delegate(ILContext il) { ReplaceDamage(il, vehicle: false); })); } passenger = typeof(SojournVehicleBase).GetField("characterBody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException("SojournVehicleBase.characterBody"); if (passenger.FieldType != typeof(CharacterBody)) { throw new InvalidOperationException("Sojourn passenger field changed."); } hooks.Add(new ILHook((MethodBase)Method(typeof(SojournVehicle), "OnFixedUpdateForTenthSecond"), (Manipulator)delegate(ILContext il) { ReplaceDamage(il, vehicle: true); })); HealthComponent.TakeDamageProcess += new Manipulator(Curse); curseAttached = true; Language.GetLocalizedStringByToken += new hook_GetLocalizedStringByToken(Description); Ready = true; } catch (Exception ex) { HealthComponent.TakeDamageProcess -= new Manipulator(Curse); Dispose(); log.LogError((object)("E8 ability exemption unavailable; native persistent damage retained: " + ex)); } } private static MethodInfo Method(Type type, string name) { return type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMethodException(type.FullName, name); } private void ReplaceDamage(ILContext il, bool vehicle) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) int num = HealingEconomyPatterns.DamageCall(il.Body.Instructions.ToArray()); ILCursor val = new ILCursor(il) { Index = num }; il.Body.Instructions[num].OpCode = OpCodes.Nop; il.Body.Instructions[num].Operand = null; val.Index = num + 1; val.Emit(OpCodes.Ldarg_0); if (vehicle) { val.EmitDelegate<Action<HealthComponent, DamageInfo, SojournVehicle>>((Action<HealthComponent, DamageInfo, SojournVehicle>)delegate(HealthComponent victim, DamageInfo info, SojournVehicle source) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown Deal(victim, info, (CharacterBody)passenger.GetValue(source), "SeekerBody"); }); } else { val.EmitDelegate<Action<HealthComponent, DamageInfo, EntityState>>((Action<HealthComponent, DamageInfo, EntityState>)delegate(HealthComponent victim, DamageInfo info, EntityState source) { Deal(victim, info, ((Component)source.outer).GetComponent<CharacterBody>(), ((object)source).GetType().FullName.Contains("VoidSurvivor") ? "VoidSurvivorBody" : "TreebotBody"); }); } } private void Deal(HealthComponent victim, DamageInfo info, CharacterBody? source, string bodyName) { //IL_0028: 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) if (!Active || (Object)(object)source == (Object)null || !Object.op_Implicit((Object)(object)source) || (Object)(object)source.healthComponent != (Object)(object)victim || source.bodyIndex != BodyCatalog.FindBodyIndex(bodyName)) { victim.TakeDamage(info); return; } HealthComponent value; bool flag = classified.TryGetValue(info, out value); classified[info] = victim; try { victim.TakeDamage(info); } finally { if (flag) { classified[info] = value; } else { classified.Remove(info); } } } private bool Exempt(HealthComponent victim, DamageInfo info) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Invalid comparison between Unknown and I4 if (!Active || !Object.op_Implicit((Object)(object)victim.body) || (int)victim.body.teamComponent.teamIndex != 1) { return false; } if (classified.TryGetValue(info, out HealthComponent value) && (Object)(object)value == (Object)(object)victim) { return true; } if (victim.body.bodyIndex != BodyCatalog.FindBodyIndex("CaptainBody") || (Object)(object)info.attacker != (Object)(object)((Component)victim).gameObject || !Object.op_Implicit((Object)(object)info.inflictor) || (int)info.damageType.damageSource != 4) { return false; } ProjectileController component = info.inflictor.GetComponent<ProjectileController>(); if (!Object.op_Implicit((Object)(object)component) || (Object)(object)component.owner != (Object)(object)((Component)victim).gameObject) { return false; } int num = ProjectileCatalog.FindProjectileIndex("CaptainAirstrikeAltProjectile"); if (num >= 0) { return component.catalogIndex == num; } return false; } private void Curse(ILContext il) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) int num = HealingEconomyPatterns.CurseCoefficient(il.Body.Instructions.ToArray()); ILCursor val = new ILCursor(il) { Index = num + 1 }; val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldarg_1); val.EmitDelegate<Func<float, HealthComponent, DamageInfo, float>>((Func<float, HealthComponent, DamageInfo, float>)((float factor, HealthComponent victim, DamageInfo info) => (!Exempt(victim, info)) ? factor : 0f)); } private string Description(orig_GetLocalizedStringByToken orig, Language self, string token) { string text = orig.Invoke(self, token); if (Ready && !(token != "ECLIPSE_8_DESCRIPTION")) { PhaseSettings phaseSettings = descriptionSettings(); if (phaseSettings != null && phaseSettings.AbilityCurseExemption) { return Regex.Replace(text, "(<mspace=[^>]+>\\(8\\)</mspace>).*?</style>", "$1 Permanent damage; survivor ability self-damage adds no curse</style>"); } } return text; } internal void Reset() { classified.Clear(); } public void Dispose() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Language.GetLocalizedStringByToken -= new hook_GetLocalizedStringByToken(Description); if (curseAttached) { HealthComponent.TakeDamageProcess -= new Manipulator(Curse); } foreach (ILHook hook in hooks) { hook.Dispose(); } hooks.Clear(); Ready = false; curseAttached = false; Reset(); } } internal sealed class AmbryFeature : IDisposable { private sealed class IncomeLease : IDisposable { private static readonly FieldInfo waves = typeof(CombatDirector).GetField("moneyWaves", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException("CombatDirector.moneyWaves"); private static readonly FieldInfo multiplier = waves.FieldType.GetElementType().GetField("multiplier") ?? throw new MissingFieldException("DirectorMoneyWave.multiplier"); private readonly CombatDirector director; private readonly object[] entries; private readonly float factor; private readonly UnityAction<GameObject> spawned; private bool disposed; internal static void Validate() { if (waves.FieldType.GetElementType().IsValueType || multiplier.FieldType != typeof(float)) { throw new InvalidOperationException("Director income field changed."); } } internal IncomeLease(CombatDirector director, float factor, UnityAction<GameObject> spawned) { this.director = director; this.factor = factor; this.spawned = spawned; Array array = ((Array)waves.GetValue(director)) ?? throw new InvalidOperationException("Trial income waves are not initialized."); if (array.Length == 0) { throw new InvalidOperationException("Trial income waves are empty."); } entries = new object[array.Length]; array.CopyTo(entries, 0); object[] array2 = entries; for (int i = 0; i < array2.Length; i++) { if (array2[i] == null) { throw new InvalidOperationException("Invalid trial income wave."); } } int num = 0; try { array2 = entries; foreach (object obj in array2) { multiplier.SetValue(obj, (float)multiplier.GetValue(obj) * factor); num++; } ((UnityEvent<GameObject>)(object)director.onSpawnedServer).AddListener(spawned); } catch { for (int j = 0; j < num; j++) { multiplier.SetValue(entries[j], (float)multiplier.GetValue(entries[j]) / factor); } throw; } } public void Dispose() { if (!disposed) { disposed = true; object[] array = entries; foreach (object obj in array) { multiplier.SetValue(obj, (float)multiplier.GetValue(obj) / factor); } if (Object.op_Implicit((Object)(object)director)) { ((UnityEvent<GameObject>)(object)director.onSpawnedServer).RemoveListener(spawned); } } } } private readonly Func<PhaseSettings?> settings; private readonly ManualLogSource log; private readonly List<IncomeLease> leases = new List<IncomeLease>(); private ILHook? enter; private ILHook? exit; private EntityState? activeState; private float started; private int spawnCount; internal bool Ready { get; private set; } internal AmbryFeature(Func<PhaseSettings?> settings, ManualLogSource log) { this.settings = settings; this.log = log; } internal void Enable() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown try { IncomeLease.Validate(); Type type = typeof(ArtifactTrialMissionController).GetNestedType("CombatState", BindingFlags.NonPublic) ?? throw new MissingMemberException("ArtifactTrialMissionController.CombatState"); enter = new ILHook((MethodBase)type.GetMethod("OnEnter", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public), (Manipulator)delegate(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { delegate(Instruction instruction) { object operand = instruction.Operand; MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null); return val2 != null && ((MemberReference)val2).Name == "OnEnter" && ((MemberReference)((MemberReference)val2).DeclaringType).Name == "ArtifactTrialMissionControllerBaseState"; } })) { throw new InvalidOperationException("Trial base enter call changed."); } val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Action<EntityState>>((Action<EntityState>)Enter); }); exit = new ILHook((MethodBase)type.GetMethod("OnExit", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public), (Manipulator)delegate(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Action<EntityState>>((Action<EntityState>)delegate(EntityState state) { if (activeState == state) { Reset(); } }); }); Ready = true; } catch (Exception ex) { Dispose(); log.LogError((object)("F06 unavailable; vanilla Ambry spawning retained: " + ex)); } } private void Enter(EntityState state) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Invalid comparison between Unknown and I4 PhaseSettings phaseSettings = settings(); SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene(); if (!NetworkServer.active || phaseSettings == null || !phaseSettings.Ambry || !Object.op_Implicit((Object)(object)sceneDefForCurrentScene) || !SpawningRules.AmbryScene(sceneDefForCurrentScene.cachedName)) { return; } Reset(); try { ArtifactTrialMissionController component = ((Component)state.outer).GetComponent<ArtifactTrialMissionController>(); if (!Object.op_Implicit((Object)(object)component) || component.combatDirectors == null) { throw new InvalidOperationException("Active trial has no director references."); } HashSet<CombatDirector> hashSet = new HashSet<CombatDirector>(); CombatDirector[] combatDirectors = component.combatDirectors; foreach (CombatDirector val in combatDirectors) { if (Object.op_Implicit((Object)(object)val) && ((Behaviour)val).enabled && (int)val.teamIndex == 2 && hashSet.Add(val)) { leases.Add(new IncomeLease(val, phaseSettings.AmbryMultiplier, delegate { spawnCount++; })); } } if (leases.Count == 0) { throw new InvalidOperationException("No active Ambry enemy directors were found."); } activeState = state; started = Time.fixedTime; spawnCount = 0; log.LogInfo((object)$"F06: {leases.Count} trial-owned directors use {phaseSettings.AmbryMultiplier:0.##}x income. Spawn cadence, key drops and population limits remain native."); } catch (Exception ex) { Reset(); log.LogError((object)("F06 trial adjustment unavailable; income restored: " + ex)); } } internal void Reset() { foreach (IncomeLease lease in leases) { lease.Dispose(); } leases.Clear(); if (activeState != null) { log.LogInfo((object)$"F06 trial measurement: {spawnCount} spawned masters in {Time.fixedTime - started:0.00}s of gameplay time. Compare matched seeds; budget does not guarantee proportional density."); } activeState = null; spawnCount = 0; } public void Dispose() { ILHook? obj = enter; if (obj != null) { obj.Dispose(); } ILHook? obj2 = exit; if (obj2 != null) { obj2.Dispose(); } Reset(); Ready = false; enter = null; exit = null; } } internal sealed class BazaarFeature : IDisposable { private sealed class RefreshPlan { internal readonly NetworkUser Payer; internal readonly List<SlotSnapshot> Slots = new List<SlotSnapshot>(); internal readonly List<GameObject> Created = new List<GameObject>(); internal BazaarRandom Heresy; internal BazaarRandom Refresh; internal bool CallbackReached; internal bool Cancelled; internal uint Paid; internal Vector3 EffectOrigin; internal EffectIndex SweepIndex; internal EffectIndex SlotIndex; internal RefreshPlan(NetworkUser payer, BazaarRandom heresy, BazaarRandom refresh) { Payer = payer; Heresy = heresy; Refresh = refresh; } } private sealed class SlotSnapshot { internal readonly ShopTerminalBehavior Terminal; internal readonly PurchaseInteraction Purchase; internal readonly UniquePickup Pickup; internal UniquePickup Offer; internal readonly bool Hidden; internal readonly bool Purchased; internal readonly bool Available; internal readonly bool Created; internal readonly int Cost; internal readonly int NormalCost; internal readonly Vector3 EffectOrigin; internal bool Revealed; internal SlotSnapshot(ShopTerminalBehavior terminal, PurchaseInteraction purchase, bool created, int normalCost) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_008a: Unknown result type (might be due to invalid IL or missing references) Terminal = terminal; Purchase = purchase; Created = created; NormalCost = normalCost; Pickup = terminal.CurrentPickup(); Hidden = terminal.pickupIndexIsHidden; Purchased = terminal.NetworkhasBeenPurchased; Available = purchase.available; Cost = purchase.cost; Transform val = ((Component)terminal).transform.Find("RerollEffectRoot"); if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("Native lunar terminal effect origin is missing."); } EffectOrigin = val.position; } } private readonly Func<PhaseSettings?> settings; private readonly ManualLogSource log; private readonly BazaarVisitGate<NetworkUserId> gate = new BazaarVisitGate<NetworkUserId>(); private readonly Func<PhaseMessage?> remote; private readonly Action changed; private Transform? root; private uint visits; private ulong seed; private BazaarRandom heresy; private BazaarRandom refresh; private RefreshPlan? pending; internal const string LunarTerminalAddress = "RoR2/Base/LunarShopTerminal/LunarShopTerminal.prefab"; private AsyncOperationHandle<GameObject>? terminalAsset; internal const string RerollEffectAddress = "RoR2/Base/LunarRecycler/LunarRerollEffect.prefab"; internal const string SweepEffectAddress = "RoR2/Base/LunarRecycler/LunarRecyclerFX.prefab"; private AsyncOperationHandle<GameObject>? rerollEffect; private AsyncOperationHandle<GameObject>? sweepEffect; private RefreshPlan? sweeping; private float sweepStarted; private Action<ShopTerminalBehavior> updateDisplay; private bool poolWarning; private bool refreshUnavailable; private bool visitInitialized; private PurchaseInteraction? refresher; private int originalRefresherCost; private readonly HashSet<string> failures = new HashSet<string>(); internal bool Ready { get; private set; } internal uint RefresherId { get { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)refresher)) { return 0u; } NetworkInstanceId netId = ((Component)refresher).GetComponent<NetworkIdentity>().netId; return ((NetworkInstanceId)(ref netId)).Value; } } internal uint[] UsedUserIds => NetworkUser.readOnlyInstancesList.Where((NetworkUser user) => Object.op_Implicit((Object)(object)user) && gate.HasUsed(user.id)).Select(delegate(NetworkUser user) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) NetworkInstanceId netId = ((NetworkBehaviour)user).netId; return ((NetworkInstanceId)(ref netId)).Value; }).ToArray(); internal BazaarFeature(Func<PhaseSettings?> settings, Func<PhaseMessage?> remote, Action changed, ManualLogSource log) { this.settings = settings; this.remote = remote; this.changed = changed; this.log = log; } private static NetworkUser? Payer(Interactor who) { CharacterBody val = (Object.op_Implicit((Object)(object)who) ? ((Component)who).GetComponent<CharacterBody>() : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } CharacterMaster master = val.master; if (!Object.op_Implicit((Object)(object)master)) { return null; } return ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)((NetworkUser user) => Object.op_Implicit((Object)(object)user) && (Object)(object)user.master == (Object)(object)master)); } private bool HasUsed(PurchaseInteraction self, NetworkUser? payer) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)payer)) { return false; } if (NetworkServer.active) { return gate.HasUsed(payer.id); } PhaseMessage phaseMessage = remote(); NetworkIdentity component = ((Component)self).GetComponent<NetworkIdentity>(); if (phaseMessage != null && Object.op_Implicit((Object)(object)component)) { uint bazaarRefresherId = phaseMessage.BazaarRefresherId; NetworkInstanceId netId = component.netId; if (bazaarRefresherId == ((NetworkInstanceId)(ref netId)).Value) { uint[] bazaarUsedUserIds = phaseMessage.BazaarUsedUserIds; netId = ((NetworkBehaviour)payer).netId; return bazaarUsedUserIds.Contains(((NetworkInstanceId)(ref netId)).Value); } } return false; } internal void Enable() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown try { MethodInfo methodInfo = typeof(ShopTerminalBehavior).GetMethod("UpdatePickupDisplayAndAnimations", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingMethodException("ShopTerminalBehavior.UpdatePickupDisplayAndAnimations"); updateDisplay = (Action<ShopTerminalBehavior>)methodInfo.CreateDelegate(typeof(Action<ShopTerminalBehavior>)); ShopTerminalBehavior.GenerateNewPickupServer_bool += new hook_GenerateNewPickupServer_bool(Generate); ShopTerminalBehavior.RerollPickup += new hook_RerollPickup(Reroll); PurchaseInteraction.Start += new hook_Start(PurchaseStarted); PurchaseInteraction.SetAvailable += new hook_SetAvailable(SetAvailable); PurchaseInteraction.OnInteractionBegin += new hook_OnInteractionBegin(Purchase); PurchaseInteraction.GetInteractability += new hook_GetInteractability(Interactability); PurchaseInteraction.GetDisplayName += new hook_GetDisplayName(DisplayName); PurchaseInteraction.GetContextString += new hook_GetContextString(Context); NetworkUser.DeductLunarCoins += new hook_DeductLunarCoins(Deduct); RoR2Application.onFixedUpdate += UpdateSweep; Ready = true; } catch (Exception ex) { Dispose(); log.LogError((object)("F04/F05 unavailable; vanilla Bazaar retained: " + ex)); } } internal void BeginRun(ulong runSeed) { ResetVisit(); seed = runSeed; visits = 0u; } internal void ResetVisit() { if (pending != null) { pending.Cancelled = true; } sweeping = null; root = null; refresher = null; gate.Reset(); poolWarning = false; refreshUnavailable = false; visitInitialized = false; failures.Clear(); } private static bool ShopRoot(Transform? candidate) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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) //IL_009c: 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) if ((Object)(object)candidate != (Object)null && ((Object)candidate).name == "LunarShop") { Scene scene = ((Component)candidate).gameObject.scene; if (((Scene)(ref scene)).name == "bazaar" && Vector3.SqrMagnitude(candidate.localPosition - new Vector3(11.239998f, -7.76f, -10.86f)) < 0.01f && Quaternion.Angle(candidate.localRotation, new Quaternion(0f, 0.14248678f, 0f, 0.9897967f)) < 2f) { return Vector3.SqrMagnitude(candidate.localScale - Vector3.one) < 0.0001f; } } return false; } private static bool Refresher(PurchaseInteraction purchase) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)purchase != (Object)null && purchase.displayNameToken == "LUNAR_REROLL_NAME" && ShopRoot(((Component)purchase).transform.parent)) { return Vector3.SqrMagnitude(((Component)purchase).transform.localPosition - new Vector3(-5.551644f, -7.301207f, -10.852246f)) < 0.01f; } return false; } private static bool Bud(ShopTerminalBehavior terminal) { if ((Object)(object)terminal != (Object)null && ShopRoot(((Component)terminal).transform.parent)) { return BazaarLayout.Slot(((Component)terminal).transform) >= 0; } return false; } private bool RefreshActive(PurchaseInteraction purchase) { PhaseSettings phaseSettings = settings(); if (phaseSettings != null && phaseSettings.BazaarRefresh && Refresher(purchase)) { if (NetworkServer.active) { return !refreshUnavailable; } return true; } return false; } private void EnsureVisit(Transform shop) { //IL_011d: Unknown result type (might be due to invalid IL or missing references) if (!ShopRoot(shop)) { throw new InvalidOperationException("Bazaar layout root differs from inspected scene."); } HashSet<int> hashSet = new HashSet<int>(); ShopTerminalBehavior[] componentsInChildren = ((Component)shop).GetComponentsInChildren<ShopTerminalBehavior>(true); foreach (ShopTerminalBehavior obj in componentsInChildren) { int num = BazaarLayout.Slot(((Component)obj).transform); PurchaseInteraction component = ((Component)obj).GetComponent<PurchaseInteraction>(); if (num < 0 && Object.op_Implicit((Object)(object)component) && component.displayNameToken == "LUNAR_TERMINAL_NAME") { throw new InvalidOperationException("Lunar merchandise layout moved or gained unclassified slots; re-audit required."); } if (num >= 0 && !hashSet.Add(num)) { throw new InvalidOperationException("Duplicate lunar merchandise slot."); } } if (!((Object)(object)root == (Object)(object)shop)) { if (visitInitialized) { throw new InvalidOperationException("A second/replaced lunar shop root cannot reset this visit's player uses."); } root = shop; visitInitialized = true; visits++; gate.Reset(); heresy = BazaarRandom.ForVisit(seed, visits, heresy: true); refresh = BazaarRandom.ForVisit(seed, visits, heresy: false); log.LogInfo((object)$"Bazaar visit {visits}: mapped left bud at local {BazaarLayout.Positions[0]}; live screenshot confirmation remains pending."); } } private void Generate(orig_GenerateNewPickupServer_bool orig, ShopTerminalBehavior self, bool hidden) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) PhaseSettings phaseSettings = settings(); if (!NetworkServer.active || phaseSettings == null || !phaseSettings.Heresy || !Bud(self) || BazaarLayout.Slot(((Component)self).transform) != 0 || self.NetworkhasBeenPurchased) { orig.Invoke(self, hidden); return; } try { EnsureVisit(((Component)self).transform.parent); BazaarRandom random = heresy; PickupIndex val = ChooseHeresy(phaseSettings.HeresyProbability, ref random); self.SetPickup(new UniquePickup(val), hidden); heresy = random; } catch (Exception error) { Failure("F05 stock roll failed; using vanilla stock", error); orig.Invoke(self, hidden); } } private void Reroll(orig_RerollPickup orig, ShopTerminalBehavior self) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Generate((orig_GenerateNewPickupServer_bool)delegate(ShopTerminalBehavior terminal, bool hidden) { orig.Invoke(terminal); }, self, self.pickupIndexIsHidden); } private PickupIndex ChooseHeresy(float probability, ref BazaarRandom random) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Invalid comparison between Unknown and I4 //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Run.instance)) { throw new InvalidOperationException("No active run."); } HashSet<PickupIndex> targets = new HashSet<PickupIndex>(); string[] array = new string[4] { "LunarPrimaryReplacement", "LunarSecondaryReplacement", "LunarUtilityReplacement", "LunarSpecialReplacement" }; for (int i = 0; i < array.Length; i++) { ItemIndex val = ItemCatalog.FindItemIndex(array[i]); if ((int)val != -1) { targets.Add(PickupCatalog.FindPickupIndex(val)); } } PickupIndex[] source = Run.instance.availableLunarCombinedDropList.Distinct().ToArray(); PickupIndex[] array2 = source.Where(targets.Contains).ToArray(); PickupIndex[] array3 = source.Where((PickupIndex pickup) => !targets.Contains(pickup)).ToArray(); if ((array2.Length == 0 || array3.Length == 0) && !poolWarning) { poolWarning = true; log.LogWarning((object)"F05: one lunar branch has no eligible stock; using the available branch. Exact configured probability requires both pools."); } return HeresyRules.Choose(array2, array3, probability, ref random); } private void PurchaseStarted(orig_Start orig, PurchaseInteraction self) { orig.Invoke(self); if (NetworkServer.active && RefreshActive(self)) { try { RememberRefresher(self); EnsureVisit(((Component)self).transform.parent); self.Networkcost = 3; self.SetAvailable(RefresherAvailable()); } catch (Exception error) { DisableRefresh(self, error); } } } private bool RefresherAvailable() { if (!gate.Busy && sweeping == null) { return gate.HasRemainingUse(from user in NetworkUser.readOnlyInstancesList where Object.op_Implicit((Object)(object)user) select user.id); } return false; } private void SetAvailable(orig_SetAvailable orig, PurchaseInteraction self, bool available) { if (NetworkServer.active && RefreshActive(self)) { available = RefresherAvailable(); } orig.Invoke(self, available); } private void RememberRefresher(PurchaseInteraction self) { if ((Object)(object)refresher != (Object)(object)self) { refresher = self; originalRefresherCost = self.cost; } } private void DisableRefresh(PurchaseInteraction self, Exception error) { refreshUnavailable = true; if ((Object)(object)refresher == (Object)(object)self) { self.Networkcost = originalRefresherCost; } Failure("F04 layout unavailable; vanilla refresher retained for this visit", error); } private void Purchase(orig_OnInteractionBegin orig, PurchaseInteraction self, Interactor activator) { //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Invalid comparison between Unknown and I4 //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Expected O, but got Unknown PhaseSettings phaseSettings = settings(); if (!NetworkServer.active || phaseSettings == null || !phaseSettings.BazaarRefresh || refreshUnavailable) { orig.Invoke(self, activator); return; } ShopTerminalBehavior component = ((Component)self).GetComponent<ShopTerminalBehavior>(); if (gate.Busy && Object.op_Implicit((Object)(object)component) && Bud(component)) { return; } if (!Refresher(self)) { orig.Invoke(self, activator); return; } try { RememberRefresher(self); EnsureVisit(((Component)self).transform.parent); } catch (Exception error) { DisableRefresh(self, error); orig.Invoke(self, activator); return; } if (gate.Busy || sweeping != null || !self.available || !Object.op_Implicit((Object)(object)activator)) { return; } self.Networkcost = 3; if ((int)self.costType != 3 || !self.CanBeAffordedByInteractor(activator) || (int)self.GetInteractability(activator) != 2) { return; } NetworkUser val = Payer(activator); if ((Object)(object)val == (Object)null || !gate.TryBegin(val.id)) { return; } RefreshPlan plan = new RefreshPlan(val, heresy, refresh); PurchaseEvent onPurchase = self.onPurchase; UnityAction<Interactor> val2 = delegate { plan.CallbackReached = true; }; bool flag = false; try { Prepare(plan, phaseSettings); if (!rerollEffect.HasValue) { rerollEffect = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/LunarRecycler/LunarRerollEffect.prefab"); } if (!sweepEffect.HasValue) { sweepEffect = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/LunarRecycler/LunarRecyclerFX.prefab"); } plan.SweepIndex = ResolveEffect(sweepEffect.Value.WaitForCompletion(), "LunarRecyclerFX", "Play_lunar_reroller_activate"); plan.SlotIndex = ResolveEffect(rerollEffect.Value.WaitForCompletion(), "LunarRerollEffect", "Play_lunar_reroller_item_change"); Transform val3 = ((Component)self).transform.Find("UseEffectRoot"); if (!Object.op_Implicit((Object)(object)val3)) { throw new InvalidOperationException("Native lunar recycler effect origin is missing."); } plan.EffectOrigin = val3.position; Apply(plan); self.onPurchase = new PurchaseEvent(); ((UnityEvent<Interactor>)(object)self.onPurchase).AddListener(val2); pending = plan; try { orig.Invoke(self, activator); } catch (Exception ex) { if (!plan.CallbackReached || plan.Paid != 3) { throw; } log.LogError((object)("F04 purchase completed; a downstream native/mod callback failed: " + ex)); } if (!plan.CallbackReached || plan.Paid != 3 || plan.Cancelled) { throw new InvalidOperationException("Native purchase did not complete an exact three-coin transaction."); } flag = true; } catch (Exception error2) { Failure("F04 refresh rolled back", error2); } finally { pending = null; if ((Object)(object)self != (Object)null) { self.onPurchase = onPurchase; } if (flag) { heresy = plan.Heresy; refresh = plan.Refresh; if (gate.Busy) { gate.Finish(success: true); } if ((Object)(object)self != (Object)null) { self.Networkcost = 3; } sweeping = plan; sweepStarted = Time.fixedTime; if ((Object)(object)self != (Object)null) { self.SetAvailable(false); } EffectManager.SpawnEffect(plan.SweepIndex, new EffectData { origin = plan.EffectOrigin }, true); changed(); log.LogInfo((object)"F04: refreshed all five lunar slots; charged the interacting player 3 Lunar Coins; that player's visit use exhausted."); } else { try { Rollback(plan); if (plan.Paid != 0 && (Object)(object)plan.Payer != (Object)null) { plan.Payer.AwardLunarCoins(plan.Paid); } } catch (Exception ex2) { log.LogError((object)("F04 refund/recovery failed; inspect the player's coins and stock: " + ex2)); } finally { if (gate.Busy) { gate.Finish(success: false); } } } } } private static EffectIndex ResolveEffect(GameObject prefab, string name, string sound) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)prefab) || ((Object)prefab).name != name) { throw new InvalidOperationException("Unexpected Bazaar effect asset: " + name); } EffectIndex val = EffectCatalog.FindEffectIndex(name); EffectDef effectDef = EffectCatalog.GetEffectDef(val); if (effectDef == null || effectDef.prefabName != name || !Object.op_Implicit((Object)(object)effectDef.prefab) || effectDef.spawnSoundEventName != sound) { throw new InvalidOperationException("Native Bazaar effect or sound is unavailable in the live catalog: " + name); } return val; } private void UpdateSweep() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown if (!NetworkServer.active) { return; } if (Object.op_Implicit((Object)(object)refresher) && RefreshActive(refresher) && refresher.available != RefresherAvailable()) { refresher.SetAvailable(RefresherAvailable()); } if (sweeping == null) { return; } RefreshPlan refreshPlan = sweeping; float num = Time.fixedTime - sweepStarted; for (int i = 0; i < refreshPlan.Slots.Count; i++) { SlotSnapshot slotSnapshot = refreshPlan.Slots[i]; if (!slotSnapshot.Revealed && !(num < 0.9f - 0.1f * (float)i)) { slotSnapshot.Revealed = true; if (Object.op_Implicit((Object)(object)slotSnapshot.Terminal) && Object.op_Implicit((Object)(object)slotSnapshot.Purchase)) { slotSnapshot.Terminal.SetPickup(slotSnapshot.Offer, false); slotSnapshot.Terminal.SetHasBeenPurchased(false); updateDisplay(slotSnapshot.Terminal); slotSnapshot.Purchase.SetAvailable(true); EffectManager.SpawnEffect(refreshPlan.SlotIndex, new EffectData { origin = slotSnapshot.EffectOrigin }, true); } } } if (!(num < 1f)) { sweeping = null; if (Object.op_Implicit((Object)(object)refresher)) { refresher.SetAvailable(RefresherAvailable()); } } } private void Deduct(orig_DeductLunarCoins orig, NetworkUser self, uint amount) { orig.Invoke(self, amount); if (pending != null && (Object)(object)pending.Payer == (Object)(object)self && !pending.CallbackReached) { pending.Paid = checked(pending.Paid + amount); } } private void Prepare(RefreshPlan plan, PhaseSettings active) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Invalid comparison between Unknown and I4 //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { throw new InvalidOperationException("Bazaar visit ended."); } if (!terminalAsset.HasValue) { terminalAsset = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/Base/LunarShopTerminal/LunarShopTerminal.prefab"); } GameObject val = terminalAsset.Value.WaitForCompletion(); if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("Vanilla lunar terminal prefab is unavailable."); } PurchaseInteraction component = val.GetComponent<PurchaseInteraction>(); if (!Object.op_Implicit((Object)(object)component) || (int)component.costType != 3) { throw new InvalidOperationException("Unexpected lunar terminal currency."); } ShopTerminalBehavior[] array = (ShopTerminalBehavior[])(object)new ShopTerminalBehavior[5]; ShopTerminalBehavior[] componentsInChildren = ((Component)root).GetComponentsInChildren<ShopTerminalBehavior>(true); foreach (ShopTerminalBehavior val2 in componentsInChildren) { int num = BazaarLayout.Slot(((Component)val2).transform); if (num >= 0) { if ((Object)(object)array[num] != (Object)null) { throw new InvalidOperationException("Duplicate lunar merchandise slot."); } array[num] = val2; } } for (int j = 0; j < array.Length; j++) { ShopTerminalBehavior val3 = array[j]; bool flag = (Object)(object)val3 == (Object)null; if (flag) { GameObject val4 = Object.Instantiate<GameObject>(val, root); plan.Created.Add(val4); val4.transform.localPosition = BazaarLayout.Positions[j]; val4.transform.localRotation = BazaarLayout.Rotations[j]; val3 = val4.GetComponent<ShopTerminalBehavior>(); if ((Object)(object)val3 == (Object)null) { throw new InvalidOperationException("Replacement has no stock component."); } val3.selfGeneratePickup = false; } PurchaseInteraction component2 = ((Component)val3).GetComponent<PurchaseInteraction>(); if (!Object.op_Implicit((Object)(object)component2) || (int)component2.costType != 3) { throw new InvalidOperationException("Invalid lunar purchase component."); } SlotSnapshot slotSnapshot = new SlotSnapshot(val3, component2, flag, component.cost); plan.Slots.Add(slotSnapshot); if (active.Heresy && j == 0) { slotSnapshot.Offer = new UniquePickup(ChooseHeresy(active.HeresyProbability, ref plan.Heresy)); } else { if (!Object.op_Implicit((Object)(object)val3.dropTable) || val3.dropTable.GetPickupCount() == 0) { throw new InvalidOperationException("No eligible normal lunar stock."); } Xoroshiro128Plus val5 = new Xoroshiro128Plus(plan.Refresh.Next()); slotSnapshot.Offer = (UniquePickup)((!((UniquePickup)(ref slotSnapshot.Pickup)).isValid) ? val3.dropTable.GeneratePickup(val5) : (((??)ShopTerminalBehavior.GenerateReroll(val3.dropTable, val5, slotSnapshot.Pickup)) ?? val3.dropTable.GeneratePickup(val5))); } if (!((UniquePickup)(ref slotSnapshot.Offer)).isValid || !Run.instance.availableLunarCombinedDropList.Contains(slotSnapshot.Offer.pickupIndex)) { throw new InvalidOperationException("Offer is unavailable for this run."); } } } private static void Apply(RefreshPlan plan) { foreach (SlotSnapshot slot in plan.Slots) { slot.Terminal.SetHasBeenPurchased(false); slot.Purchase.Networkcost = slot.NormalCost; slot.Purchase.SetAvailable(false); if (slot.Created) { NetworkServer.Spawn(((Component)slot.Terminal).gameObject); } if (slot.Terminal.NetworkhasBeenPurchased) { throw new InvalidOperationException("A lunar slot could not be restored."); } } } private void Rollback(RefreshPlan plan) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) foreach (GameObject item in plan.Created) { try { if (Object.op_Implicit((Object)(object)item)) { item.transform.SetParent((Transform)null, true); item.SetActive(false); NetworkIdentity component = item.GetComponent<NetworkIdentity>(); if (!Object.op_Implicit((Object)(object)component)) { goto IL_005b; } NetworkInstanceId netId = component.netId; if (((NetworkInstanceId)(ref netId)).Value == 0) { goto IL_005b; } NetworkServer.Destroy(item); } goto end_IL_0016; IL_005b: Object.Destroy((Object)(object)item); end_IL_0016:; } catch (Exception ex) { log.LogError((object)("F04 rollback could not remove a replacement slot: " + ex)); } } foreach (SlotSnapshot slot in plan.Slots) { try { if (!slot.Created && !((Object)(object)slot.Terminal == (Object)null)) { slot.Terminal.SetPickup(slot.Pickup, slot.Hidden); slot.Terminal.SetHasBeenPurchased(slot.Purchased); slot.Purchase.Networkcost = slot.Cost; slot.Purchase.SetAvailable(slot.Available); } } catch (Exception ex2) { log.LogError((object)("F04 rollback could not restore a lunar slot: " + ex2)); } } } private Interactability Interactability(orig_GetInteractability orig, PurchaseInteraction self, Interactor who) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (RefreshActive(self) && (!self.available || HasUsed(self, Payer(who)) || (NetworkServer.active && (gate.Busy || sweeping != null)))) { return (Interactability)0; } return orig.Invoke(self, who); } private string DisplayName(orig_GetDisplayName orig, PurchaseInteraction self) { if (!RefreshActive(self) || self.cost != 3) { return orig.Invoke(self); } return "Refresh all lunar stock"; } private string Context(orig_GetContextString orig, PurchaseInteraction self, Interactor who) { if (!RefreshActive(self) || self.cost != 3) { return orig.Invoke(self, who); } if (!HasUsed(self, Payer(who))) { return "Refresh all lunar stock — 3 Lunar Coins — 1 use per player per visit"; } return "Refresh all lunar stock — your use is exhausted"; } private void Failure(string message, Exception error) { if (failures.Add(message)) { log.LogError((object)(message + ": " + error)); } } public void Dispose() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) ShopTerminalBehavior.GenerateNewPickupServer_bool -= new hook_GenerateNewPickupServer_bool(Generate); ShopTerminalBehavior.RerollPickup -= new hook_RerollPickup(Reroll); PurchaseInteraction.Start -= new hook_Start(PurchaseStarted); PurchaseInteraction.SetAvailable -= new hook_SetAvailable(SetAvailable); PurchaseInteraction.OnInteractionBegin -= new hook_OnInteractionBegin(Purchase); PurchaseInteraction.GetInteractability -= new hook_GetInteractability(Interactability); PurchaseInteraction.GetDisplayName -= new hook_GetDisplayName(DisplayName); PurchaseInteraction.GetContextString -= new hook_GetContextString(Context); NetworkUser.DeductLunarCoins -= new hook_DeductLunarCoins(Deduct); RoR2Application.onFixedUpdate -= UpdateSweep; if (rerollEffect.HasValue) { Addressables.Release<GameObject>(rerollEffect.Value); rerollEffect = null; } if (sweepEffect.HasValue) { Addressables.Release<GameObject>(sweepEffect.Value); sweepEffect = null; } if (terminalAsset.HasValue) { Addressables.Release<GameObject>(terminalAsset.Value); terminalAsset = null; } ResetVisit(); Ready = false; } } internal static class BazaarLayout { internal static readonly Vector3[] Positions = (Vector3[])(object)new Vector3[5] { new Vector3(4.398342f, -6.118f, 0.959436f), new Vector3(0.865416f, -6.117998f, 0.413354f), new Vector3(-1.130224f, -6.117998f, -2.028714f), new Vector3(-0.873811f, -6.117998f, -4.964099f), new Vector3(1.362406f, -6.117998f, -7.57685f) }; internal static readonly Quaternion[] Rotations = (Quaternion[])(object)new Quaternion[5] { new Quaternion(0f, -0.2051369f, 0f, 0.97873336f), new Quaternion(0f, -0.39634863f, 0f, 0.9181001f), new Quaternion(0f, -0.5726467f, 0f, 0.8198023f), new Quaternion(0f, -0.85562456f, 0f, 0.5175969f), new Quaternion(0f, -0.98170954f, 0f, 0.19038475f) }; internal static int Slot(Transform terminal) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)terminal.parent) || ((Object)terminal.parent).name != "LunarShop") { return -1; } int num = -1; for (int i = 0; i < Positions.Length; i++) { if (Vector3.SqrMagnitude(terminal.localPosition - Positions[i]) < 0.01f && Quaternion.Angle(terminal.localRotation, Rotations[i]) < 2f) { if (num >= 0) { throw new InvalidOperationException("Ambiguous Bazaar slot identity."); } num = i; } } return num; } } internal sealed class DepartureFeature : IDisposable { private readonly Func<PhaseSettings?> settings; private readonly ManualLogSource log; private readonly DepartureGate gate = new DepartureGate(); private FieldInfo? standardState; private FieldInfo? colossusState; private MonoBehaviour? owner; private GameObject? scannerPrefab; private bool scannerFailureLogged; internal bool Ready { get; private set; } internal bool Committed => gate.Committed; private static FieldInfo StateField(Type type) { return type.GetField("exitState", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, "exitState"); } internal DepartureFeature(Func<PhaseSettings?> settings, ManualLogSource log) { this.settings = settings; this.log = log; } internal void Enable() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown try { standardState = StateField(typeof(SceneExitController)); colossusState = StateField(typeof(SceneExitControllerColossusPortal)); SceneExitController.Begin += new hook_Begin(Begin); SceneExitController.UpdateServer += new hook_UpdateServer(Update); SceneExitControllerColossusPortal.Begin += new hook_Begin(BeginColossus); SceneExitControllerColossusPortal.UpdateServer += new hook_UpdateServer(UpdateColossus); Ready = true; } catch (Exception ex) { Dispose(); log.LogError((object)("F01/F02 departure tracking unavailable; vanilla exits retained: " + ex)); } } private void Begin(orig_Begin orig, SceneExitController self) { HandleBegin((MonoBehaviour)(object)self, standardState, delegate { orig.Invoke(self); }); } private void BeginColossus(orig_Begin orig, SceneExitControllerColossusPortal self) { HandleBegin((MonoBehaviour)(object)self, colossusState, delegate { orig.Invoke(self); }); } private void HandleBegin(MonoBehaviour self, FieldInfo state, Action original) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) PhaseSettings phaseSettings = settings(); if (!NetworkServer.active || phaseSettings == null || (!phaseSettings.Income && !phaseSettings.Scanner)) { original(); } else { if (gate.Committed) { return; } if (Convert.ToInt32(state.GetValue(self)) != 0) { original(); return; } SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene(); bool num = phaseSettings.Scanner && Object.op_Implicit((Object)(object)sceneDefForCurrentScene) && GameplayRules.CanScanDeparture(sceneDefForCurrentScene.cachedName) && Object.op_Implicit((Object)(object)Run.instance) && (((object)Run.instance).GetType() == typeof(Run) || Run.instance is EclipseRun); gate.TryCommit(0.0); owner = self; float num2 = 0f; if (num) { try { if (!Object.op_Implicit((Object)(object)scannerPrefab)) { scannerPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/NetworkedObjects/ChestScanner"); } if (!Object.op_Implicit((Object)(object)scannerPrefab)) { throw new InvalidOperationException("Vanilla ChestScanner prefab was not found."); } NetworkServer.Spawn(Object.Instantiate<GameObject>(scannerPrefab, ((Component)self).transform.position, Quaternion.identity)); num2 = phaseSettings.ScanWindow; log.LogInfo((object)$"F02: departure scan committed; minimum reveal window {num2:0.##}s."); } catch (Exception ex) { if (!scannerFailureLogged) { scannerFailureLogged = true; log.LogError((object)("F02 scanner unavailable; departure proceeds normally: " + ex)); } } } gate.Reset(); gate.TryCommit(num2); original(); } } private bool Holding(MonoBehaviour self) { if ((Object)(object)owner == (Object)(object)self) { return gate.Remaining > 0.0; } return false; } private void Update(orig_UpdateServer orig, SceneExitController self) { if (!Holding((MonoBehaviour)(object)self)) { orig.Invoke(self); } } private void UpdateColossus(orig_UpdateServer orig, SceneExitControllerColossusPortal self) { if (!Holding((MonoBehaviour)(object)self)) { orig.Invoke(self); } } internal void Tick(float delta) { gate.Tick(delta); } internal void Reset() { owner = null; gate.Reset(); } public void Dispose() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown SceneExitController.Begin -= new hook_Begin(Begin); SceneExitController.UpdateServer -= new hook_UpdateServer(Update); SceneExitControllerColossusPortal.Begin -= new hook_Begin(BeginColossus); SceneExitControllerColossusPortal.UpdateServer -= new hook_UpdateServer(UpdateColossus); Ready = false; Reset(); } } internal sealed class EclipseAccessFeature : IDisposable { private sealed class RunState { public readonly EclipseAwardGate<(UserProfile, SurvivorDef)> Awards = new EclipseAwardGate<(UserProfile, SurvivorDef)>(); } private readonly ManualLogSource log; private readonly EclipseBadgeIcons badgeIcons; private readonly HashSet<EclipseDifficultyIcon> difficultyIcons = new HashSet<EclipseDifficultyIcon>(); private readonly ConditionalWeakTable<EclipseRun, RunState> runs = new ConditionalWeakTable<EclipseRun, RunState>(); private readonly Dictionary<EclipseRunScreenController, EclipseOverviewView> views = new Dictionary<EclipseRunScreenController, EclipseOverviewView>(); private readonly HashSet<EclipseLobbyNote> notes = new HashSet<EclipseLobbyNote>(); private Func<SurvivorDef, List<UnlockableDef>>? unlocksForSurvivor; private Action<PreGameController>? recalculate; private PreGameController? lastLobby; private int lastLobbyLevel; private bool uiWarning; internal bool Ready { get; private set; } internal static bool IsEclipseLobby { get { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)PreGameController.instance)) { return GameModeCatalog.GetGameModePrefabComponent(PreGameController.instance.gameModeIndex) is EclipseRun; } return false; } } internal EclipseAccessFeature(ManualLogSource log) { this.log = log; badgeIcons = new EclipseBadgeIcons(log); } internal void Enable() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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 //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown try { MethodInfo methodInfo = typeof(EclipseRun).GetMethod("GetEclipseLevelUnlockablesForSurvivor", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMethodException("Eclipse survivor unlock list"); unlocksForSurvivor = (Func<SurvivorDef, List<UnlockableDef>>)methodInfo.CreateDelegate(typeof(Func<SurvivorDef, List<UnlockableDef>>)); MethodInfo methodInfo2 = typeof(PreGameController).GetMethod("RecalculateModifierAvailability", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMethodException("Lobby modifier recalculation"); recalculate = (Action<PreGameController>)methodInfo2.CreateDelegate(typeof(Action<PreGameController>)); EclipseRun.OnClientGameOver += new Manipulator(PatchAward); EclipseRun.OverrideRuleChoices += new Manipulator(PatchSelection); EclipseRun.OverrideRuleChoices += new hook_OverrideRuleChoices(Choices); EclipseRunScreenController.UpdateDisplayedSurvivor += new hook_UpdateDisplayedSurvivor(Display); EclipseRunScreenController.OnDisable += new hook_OnDisable(Hide); RuleCategoryController.SetData += new hook_SetData(Category); PreGameController.StartRun += new hook_StartRun(StartRun); RuleChoiceController.UpdateChoiceDisplay += new hook_UpdateChoiceDisplay(ChoiceDisplay); Ready = true; } catch (Exception ex) { Dispose(); log.LogError((object)("Eclipse access unavailable; native selection/progression retained: " + ex)); } } private void PatchSelection(ILContext il) { EclipseAccessPatchEmitter.Selection(il, delegate(ILCursor cursor) { cursor.EmitDelegate<Func<int, EclipseRun, int>>((Func<int, EclipseRun, int>)delegate(int native, EclipseRun run) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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) int selectedLevel = (Object.op_Implicit((Object)(object)PreGameController.instance) ? (PreGameController.instance.readOnlyRuleBook.FindDifficulty() - 3 + 1) : 0); return EclipseAccessRules.LobbyPenaltyLevel(native, selectedLevel); }); }); } private void Choices(orig_OverrideRuleChoices orig, EclipseRun self, RuleChoiceMask mustInclude, RuleChoiceMask mustExclude, ulong seed) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected I4, but got Unknown orig.Invoke(self, mustInclude, mustExclude, seed); foreach (RuleChoiceDef choice in RuleCatalog.FindRuleDef("Difficulty").choices) { bool flag = EclipseAccessRules.IsSelectableLevel(choice.difficultyIndex - 3 + 1); ((SerializableBitArray)mustInclude)[choice.globalIndex] = flag; ((SerializableBitArray)mustExclude)[choice.globalIndex] = !flag; } } private void PatchAward(ILContext il) { EclipseAccessPatchEmitter.Award(il, delegate(ILCursor cursor) { cursor.EmitDelegate<Action<UserProfile, UnlockableDef, SurvivorDef, EclipseRun>>((Action<UserProfile, UnlockableDef, SurvivorDef, EclipseRun>)Award); }); } private void Award(UserProfile profile, UnlockableDef nativeAward, SurvivorDef survivor, EclipseRun run) { if (profile == null || !Object.op_Implicit((Object)(object)survivor) || !Object.op_Implicit((Object)(object)nativeAward) || !Object.op_Implicit((Object)(object)run)) { return; } RunState value = runs.GetValue(run, (EclipseRun _) => new RunState()); (UserProfile, SurvivorDef) identity = (profile, survivor); List<UnlockableDef> list = unlocksForSurvivor(survivor); int num = list.IndexOf(nativeAward) + EclipseRun.minUnlockableEclipseLevel - 1; if (!list.Contains(nativeAward)) { log.LogWarning((object)"Unknown Eclipse award; no progression was granted."); return; } int num2 = 0; foreach (UnlockableDef item in list) { if (!Object.op_Implicit((Object)(object)item) || !profile.HasUnlockable(item)) { break; } num2++; } if (value.Awards.TryAward(identity, num2, num, won: true, out var after)) { int num3 = after + 1 - EclipseRun.minUnlockableEclipseLevel; if (num3 < 0 || num3 >= list.Count || !Object.op_Implicit((Object)(object)list[num3])) { log.LogWarning((object)"Next Eclipse badge is unavailable; no progression was granted."); return; } profile.GrantUnlockable(list[num3]); log.LogInfo((object)$"Eclipse victory at E{num}: this survivor's progress advanced E{num2} -> E{after}."); } } private void Display(orig_UpdateDisplayedSurvivor orig, EclipseRunScreenController self) { orig.Invoke(self); if (!Ready || views.ContainsKey(self)) { return; } try { views.Add(self, new EclipseOverviewView(self)); } catch (Exception ex) { if (!uiWarning) { uiWarning = true; log.LogError((object)("Eclipse overview could not be built: " + ex)); } } } private void Hide(orig_OnDisable orig, EclipseRunScreenController self) { if (views.TryGetValue(self, out EclipseOverviewView value)) { views.Remove(self); value.Dispose(); } orig.Invoke(self); } private void Category(orig_SetData orig, RuleCategoryController self, RuleCategoryDef category, RuleChoiceMask availability, RuleBook book) { orig.Invoke(self, category, availability, book); EclipseLobbyNote eclipseLobbyNote = ((Component)self).GetComponent<EclipseLobbyNote>(); bool flag = Ready && IsEclipseLobby && category.children.Any((RuleDef rule) => rule.globalName == "Difficulty"); if (flag && !Object.op_Implicit((Object)(object)eclipseLobbyNote)) { eclipseLobbyNote = ((Component)self).gameObject.AddComponent<EclipseLobbyNote>(); notes.Add(eclipseLobbyNote); } if (Object.op_Implicit((Object)(object)eclipseLobbyNote)) { eclipseLobbyNote.Configure(flag ? self : null); } } private void ChoiceDisplay(orig_UpdateChoiceDisplay orig, RuleChoiceController self, RuleChoiceDef choice) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown orig.Invoke(self, choice); EclipseDifficultyIcon eclipseDifficultyIcon = ((Component)self).GetComponent<EclipseDifficultyIcon>(); if (choice == null) { if (Object.op_Implicit((Object)(object)eclipseDifficultyIcon)) { eclipseDifficultyIcon.Clear(); } return; } int level = choice.difficultyIndex - 3 + 1; if (!Ready || !IsEclipseLobby || !EclipseAccessRules.IsSelectableLevel(level)) { if (Object.op_Implicit((Object)(object)eclipseDifficultyIcon)) { eclipseDifficultyIcon.Clear(); } return; } if (!Object.op_Implicit((Object)(object)eclipseDifficultyIcon)) { eclipseDifficultyIcon = ((Component)self).gameObject.AddComponent<EclipseDifficultyIcon>(); difficultyIcons.Add(eclipseDifficultyIcon); } eclipseDifficultyIcon.Configure(self, choice, badgeIcons); } private void StartRun(orig_StartRun orig, PreGameController self) { if (Ready && NetworkServer.active && IsEclipseLobby) { recalculate(self); } orig.Invoke(self); } internal void Tick() { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected I4, but got Unknown if (!Ready) { return; } foreach (KeyValuePair<EclipseRunScreenController, EclipseOverviewView> view in views) { if (!Object.op_Implicit((Object)(object)view.Key) || !((Behaviour)view.Key).isActiveAndEnabled) { continue; } try { view.Value.Tick(); } catch (Exception ex) { view.Value.Dispose(); if (!uiWarning) { uiWarning = true; log.LogError((object)("Eclipse overview update failed; native layout restored: " + ex)); } } } PreGameController instance = PreGameController.instance; if (!NetworkServer.active || !IsEclipseLobby) { lastLobby = null; lastLobbyLevel = 0; return; } int num = instance.readOnlyRuleBook.FindDifficulty() - 3 + 1; if (EclipseAccessRules.IsSelectableLevel(num) && ((Object)(object)lastLobby != (Object)(object)instance || num != lastLobbyLevel)) { lastLobby = instance; lastLobbyLevel = num; recalculate(instance); log.LogInfo((object)$"Eclipse lobby native difficulty selection: E{num}; E1-E8 remain available."); } } public void Dispose() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown Ready = false; RuleChoiceController.UpdateChoiceDisplay -= new hook_UpdateChoiceDisplay(ChoiceDisplay); RuleCategoryController.SetData -= new hook_SetData(Category); PreGameController.StartRun -= new hook_StartRun(StartRun); EclipseRunScreenController.UpdateDisplayedSurvivor -= new hook_UpdateDisplayedSurvivor(Display); EclipseRunScreenController.OnDisable -= new hook_OnDisable(Hide); EclipseRun.OverrideRuleChoices -= new hook_OverrideRuleChoices(Choices); EclipseRun.OverrideRuleChoices -= new Manipulator(PatchSelection); EclipseRun.OnClientGameOver -= new Manipulator(PatchAward); foreach (EclipseOverviewView value in views.Values) { value.Dispose(); } views.Clear(); foreach (EclipseLobbyNote note in notes) { if (Object.op_Implicit((Object)(object)note)) { Object.Destroy((Object)(object)note); } } notes.Clear(); foreach (EclipseDifficultyIcon difficultyIcon in difficultyIcons) { if (Object.op_Implicit((Object)(object)difficultyIcon)) { Object.DestroyImmediate((Object)(object)difficultyIcon); } } difficultyIcons.Clear(); badgeIcons.Dispose(); } } internal static class EclipseAccessPatchEmitter { internal static void Selection(ILContext il, Action<ILCursor> emitSelect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.Goto(EclipseAccessPatterns.Selection(il.Method), (MoveType)0, false); val.Emit(OpCodes.Ldarg_0); emitSelect(val); } internal static void Award(ILContext il, Action<ILCursor> emitAward) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.Goto(EclipseAccessPatterns.Award(il.Method), (MoveType)0, false); VariableDefinition val2 = ((IEnumerable<VariableDefinition>)il.Body.Variables).Single((VariableDefinition variable) => ((MemberReference)((VariableReference)variable).VariableType).FullName == "RoR2.SurvivorDef"); val.Emit(OpCodes.Ldloc, val2); val.Emit(OpCodes.Ldarg_0); val.Remove(); emitAward(val); } } internal static class EclipseAccessPatterns { internal static Instruction Selection(MethodDefinition method) { Instruction[] array = ((IEnumerable<Instruction>)method.Body.Instructions).Where(delegate(Instruction instruction) { //IL_006c: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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) object operand = instruction.Operand; FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null); if (val != null && ((MemberReference)val).FullName == "System.Int32 RoR2.EclipseRun::maxEclipseLevel") { Instruction next = instruction.Next; object obj = ((next != null) ? next.Operand : null); MethodReference val2 = (MethodReference)((obj is MethodReference) ? obj : null); if (val2 != null && ((MemberReference)val2).FullName == "System.Int32 System.Math::Min(System.Int32,System.Int32)") { Instruction next2 = instruction.Next.Next; OpCode? val3 = ((next2 != null) ? new OpCode?(next2.OpCode) : ((OpCode?)null)); OpCode stloc_ = OpCodes.Stloc_0; if (!val3.HasValue) { return false; } return val3.GetValueOrDefault() == stloc_; } } return false; }).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Expected one native Eclipse level cap."); } return array[0].Next.Next; } internal static Instruction Award(MethodDefinition method) { if (((IEnumerable<Instruction>)method.Body.Instr