Decompiled source of CultOfTheLambMultiplayer v1.0.0
COTLMP/COTLMP.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Logging; using DG.Tweening; using DG.Tweening.Core; using HarmonyLib; using Lamb.UI; using Lamb.UI.DeathScreen; using Lamb.UI.PauseMenu; using MMBiomeGeneration; using MMTools; using Rewired; using Spine; using Spine.Unity; using TMPro; using Unify; using Unify.Input; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary1")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary1")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b05729a9-afdc-413c-b90e-c3a9f2cd0a2c")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerExpansion; [BepInPlugin("com.cotlmp.multiplayerexpansion", "COTL Multiplayer Expansion", "2.20.14")] public class Plugin : BaseUnityPlugin { public static ManualLogSource Log; public const int MAX_PLAYERS = 4; public static readonly Color[] GoatColors = (Color[])(object)new Color[4] { Color.white, Color.white, new Color(0.4f, 0.7f, 1f, 1f), new Color(1f, 0.4f, 0.4f, 1f) }; public static readonly Color[] IndicatorColors = (Color[])(object)new Color[4] { new Color(0.9f, 0.2f, 0.2f, 1f), new Color(0.6f, 0.3f, 0.9f, 1f), new Color(1f, 0.6f, 0.1f, 1f), new Color(0.4f, 0.7f, 1f, 1f) }; private void Awake() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Expected O, but got Unknown //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Expected O, but got Unknown //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Expected O, but got Unknown //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Expected O, but got Unknown //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Expected O, but got Unknown //IL_039b: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Expected O, but got Unknown //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Expected O, but got Unknown //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Expected O, but got Unknown //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Expected O, but got Unknown //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Expected O, but got Unknown //IL_0589: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Expected O, but got Unknown //IL_05bf: Unknown result type (might be due to invalid IL or missing references) //IL_05cd: Expected O, but got Unknown //IL_05f6: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Expected O, but got Unknown //IL_063e: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Expected O, but got Unknown //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_0685: Expected O, but got Unknown //IL_06f4: Unknown result type (might be due to invalid IL or missing references) //IL_0701: Expected O, but got Unknown //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Expected O, but got Unknown //IL_04fd: Expected O, but got Unknown //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Expected O, but got Unknown //IL_0757: Unknown result type (might be due to invalid IL or missing references) //IL_0764: Expected O, but got Unknown //IL_07b0: Unknown result type (might be due to invalid IL or missing references) //IL_07bd: Expected O, but got Unknown //IL_07f8: Unknown result type (might be due to invalid IL or missing references) //IL_0805: Expected O, but got Unknown //IL_0832: Unknown result type (might be due to invalid IL or missing references) //IL_083f: Expected O, but got Unknown //IL_0879: Unknown result type (might be due to invalid IL or missing references) //IL_088e: Unknown result type (might be due to invalid IL or missing references) //IL_089b: Expected O, but got Unknown //IL_089b: Expected O, but got Unknown //IL_08c4: Unknown result type (might be due to invalid IL or missing references) //IL_08d1: Expected O, but got Unknown //IL_08f9: Unknown result type (might be due to invalid IL or missing references) //IL_0907: Expected O, but got Unknown //IL_092f: Unknown result type (might be due to invalid IL or missing references) //IL_093d: Expected O, but got Unknown //IL_0965: Unknown result type (might be due to invalid IL or missing references) //IL_0973: Expected O, but got Unknown //IL_09ad: Unknown result type (might be due to invalid IL or missing references) //IL_09bb: Expected O, but got Unknown //IL_09f5: Unknown result type (might be due to invalid IL or missing references) //IL_0a03: Expected O, but got Unknown //IL_0a2b: Unknown result type (might be due to invalid IL or missing references) //IL_0a39: Expected O, but got Unknown //IL_0a8d: Unknown result type (might be due to invalid IL or missing references) //IL_0a9b: Expected O, but got Unknown //IL_0ae2: Unknown result type (might be due to invalid IL or missing references) //IL_0af0: Expected O, but got Unknown //IL_0b18: Unknown result type (might be due to invalid IL or missing references) //IL_0b26: Expected O, but got Unknown //IL_0b6d: Unknown result type (might be due to invalid IL or missing references) //IL_0b7b: Expected O, but got Unknown //IL_0bc2: Unknown result type (might be due to invalid IL or missing references) //IL_0bd7: Unknown result type (might be due to invalid IL or missing references) //IL_0be4: Expected O, but got Unknown //IL_0be4: Expected O, but got Unknown //IL_0c1e: Unknown result type (might be due to invalid IL or missing references) //IL_0c35: Unknown result type (might be due to invalid IL or missing references) //IL_0c40: Expected O, but got Unknown //IL_0c40: Expected O, but got Unknown //IL_0c7a: Unknown result type (might be due to invalid IL or missing references) //IL_0c91: Unknown result type (might be due to invalid IL or missing references) //IL_0c9c: Expected O, but got Unknown //IL_0c9c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"COTL Multiplayer Expansion loading..."); Harmony val = new Harmony("com.cotlmp.multiplayerexpansion"); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_Awake", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "OnControllerConnected", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnControllerConnected", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "AddPlayerFromMenu", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "AddPlayerFromMenu", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(UICoopAssignController), "ConfirmSpawnButtonPress", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "ConfirmSpawnButtonPress", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "RemovePlayerFromMenu", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "RemovePlayerFromMenu", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerFarming_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "SpawnCoopPlayer", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SpawnCoopPlayer_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "SetSkin", new Type[1] { typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SetSkin_Recolor", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(UIPauseMenuController), "OnCoopButtonPressed", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "OnCoopButtonPressed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(UIPauseMenuController), "RefreshCoopText", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshCoopText", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Manager), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "HUD_Update", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "RefreshCoopPlayerRewired", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshCoopPlayerRewired", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "RefreshPlayersCount", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshPlayersCount_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopIndicatorIcon), "SetUsername", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SetUsername_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopIndicatorIcon), "AnimateDamageOnIcons", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "CoopIndicatorIcon_AnimateDamageOnIcons_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerFarming_Update_Camera", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_DLCShrine), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_DLCShrine), "OnDisable", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); Type type = AccessTools.TypeByName("SkeletonAnimationLODGlobalManager"); if (type != null) { MethodInfo methodInfo = AccessTools.Method(type, "Update", (Type[])null, (Type[])null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched SkeletonAnimationLODGlobalManager.Update finalizer"); } } MethodInfo methodInfo2 = AccessTools.Method(typeof(BiomeConstants), "BiomeGenerator_OnBiomeChangeRoom", (Type[])null, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched BiomeConstants.BiomeGenerator_OnBiomeChangeRoom (safe particle-clear transpiler)"); } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_BaseTeleporter), "HandleAnimationStateEvent", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "TeleporterAnimEvent_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_BaseTeleporter), "SpawnPlayerIE", new Type[2] { typeof(PlayerFarming), typeof(Vector3) }, (Type[])null), new HarmonyMethod(typeof(Patches), "SpawnPlayerIE_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(GameManager), "NewRun", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "NewRun_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_Start_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeAllKnockedOutPlayersWithHealth", new Type[1] { typeof(float) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeAllKnockedOutPlayersWithHealth_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeAllKnockedOutPlayers", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeAllKnockedOutPlayers_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeKnockedOutPlayer", new Type[5] { typeof(PlayerFarming), typeof(float), typeof(PlayerFarming), typeof(HeartPickupType), typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeKnockedOutPlayer_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo method = typeof(CoopManager).GetMethod("ResetMainPlayerOnConversationEnd", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2] { typeof(bool), typeof(bool) }, null); if (method != null) { val.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_ResetMainPlayerOnConversationEnd_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"[MP] Skipped patch: CoopManager.ResetMainPlayerOnConversationEnd(bool,bool) not found"); } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "SetWeapon", new Type[1] { typeof(int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_SetWeapon_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "SetCurse", new Type[1] { typeof(int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_SetCurse_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "OnEnable", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_OnEnable_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "WeaponPodium_OnInteract_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "WeaponPodium_OnInteract_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)typeof(UICoopAssignController).GetMethod("OnShowStarted", BindingFlags.Instance | BindingFlags.NonPublic), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopAssign_OnShowStarted", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Health_Manager), "Init", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HealthManager_Init", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "SetWeapon", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_SetWeapon_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "SetCurse", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_SetCurse_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnHPUpdated", new Type[1] { typeof(HealthPlayer) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnHPUpdated_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnTotalHPUpdated", new Type[1] { typeof(HealthPlayer) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnTotalHPUpdated_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Heart), "SetSprite", new Type[3] { typeof(HeartState), typeof(bool), typeof(HeartType) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHeart_SetSprite_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Heart), "Activate", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHeart_Activate_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "OnGetSoul", new Type[2] { typeof(int), typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_OnGetSoul_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "UseAmmo", new Type[2] { typeof(float), typeof(bool) }, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_UseAmmo_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_UseAmmo_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_ActiveTrinketsCoOp), "OnTrinketsChanged", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDActiveTrinketsCoOp_OnTrinketsChanged_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(HUD_ActiveTrinketsCoOp), "OnRomanNumeralSettingChanged", new Type[1] { typeof(bool) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDActiveTrinketsCoOp_OnRomanNumeralSettingChanged_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)("All patches applied. MAX_PLAYERS = " + 4)); } } public static class Patches { [CompilerGenerated] private sealed class <ApplyVisualDelayed>d__64 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int slot; private PlayerFarming <player>5__2; private float <waited>5__3; private bool <useAnimatedSpawn>5__4; private bool <inDungeon>5__5; private MeshRenderer <meshR>5__6; private float <waitLimit>5__7; private float <w>5__8; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ApplyVisualDelayed>d__64(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <player>5__2 = null; <meshR>5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_0493: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Expected O, but got Unknown //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Invalid comparison between Unknown and I4 //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Invalid comparison between Unknown and I4 //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Invalid comparison between Unknown and I4 //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0264: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (slot < 2) { _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); return false; } <player>5__2 = null; <waited>5__3 = 0f; goto IL_0105; case 1: <>1__state = -1; <waited>5__3 += 0.2f; goto IL_0105; case 2: <>1__state = -1; <w>5__8 += 0.1f; goto IL_02d5; case 3: <>1__state = -1; if ((!<useAnimatedSpawn>5__4 || !<inDungeon>5__5) && (Object)(object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length) { ((Component)<player>5__2).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } break; case 4: <>1__state = -1; <w>5__8 += 0.1f; goto IL_03f7; case 5: { <>1__state = -1; if (slot < SpawnOffsets.Length && (Object)(object)PlayerFarming.Instance != (Object)null) { ((Component)<player>5__2).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } break; } IL_02d5: if (<w>5__8 < <waitLimit>5__7 && (Object)(object)<player>5__2.state != (Object)null && (<player>5__2.state.LockStateChanges || (int)<player>5__2.state.CURRENT_STATE == 13 || (int)<player>5__2.state.CURRENT_STATE == 49)) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 2; return true; } <>2__current = null; <>1__state = 3; return true; IL_03f7: if (<w>5__8 < 2f && ((Object)(object)PlayerFarming.Instance == (Object)null || (Object)(object)PlayerFarming.Instance.state == (Object)null || (int)PlayerFarming.Instance.state.CURRENT_STATE == 13)) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 4; return true; } <>2__current = null; <>1__state = 5; return true; IL_0105: while ((Object)(object)<player>5__2 == (Object)null && <waited>5__3 < 10f) { foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && player.playerID == slot) { <player>5__2 = player; break; } } if ((Object)(object)<player>5__2 == (Object)null) { <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 1; return true; } } if ((Object)(object)<player>5__2 == (Object)null) { Plugin.Log.LogWarning((object)$"ApplyVisualDelayed: gave up waiting for playerID={slot}"); _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); return false; } <useAnimatedSpawn>5__4 = false; _pendingSpawnUseAnimation.TryGetValue(slot, out <useAnimatedSpawn>5__4); <inDungeon>5__5 = GameManager.IsDungeon(PlayerFarming.Location); <meshR>5__6 = null; if (slot >= 2 && (Object)(object)<player>5__2.Spine != (Object)null) { <meshR>5__6 = ((Component)<player>5__2.Spine).GetComponent<MeshRenderer>(); if (!<useAnimatedSpawn>5__4 && (Object)(object)<meshR>5__6 != (Object)null && ((Renderer)<meshR>5__6).enabled) { ((Renderer)<meshR>5__6).enabled = false; } } if (slot >= 2 && (Object)(object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length && (!<useAnimatedSpawn>5__4 || !<inDungeon>5__5)) { ((Component)<player>5__2).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } if ((slot >= 2) & <inDungeon>5__5) { <waitLimit>5__7 = 8f; <w>5__8 = 0f; goto IL_02d5; } <w>5__8 = 0f; goto IL_03f7; } if (slot >= 2 && (Object)(object)<meshR>5__6 != (Object)null && !((Renderer)<meshR>5__6).enabled) { ((Renderer)<meshR>5__6).enabled = true; } if (slot < Plugin.GoatColors.Length && (Object)(object)<player>5__2.Spine != (Object)null && ((SkeletonRenderer)<player>5__2.Spine).skeleton != null) { ((SkeletonRenderer)<player>5__2.Spine).skeleton.R = Plugin.GoatColors[slot].r; ((SkeletonRenderer)<player>5__2.Spine).skeleton.G = Plugin.GoatColors[slot].g; ((SkeletonRenderer)<player>5__2.Spine).skeleton.B = Plugin.GoatColors[slot].b; ((SkeletonRenderer)<player>5__2.Spine).skeleton.A = Plugin.GoatColors[slot].a; } if ((Object)(object)GameManager.GetInstance() != (Object)null) { GameManager.GetInstance().AddToCamera(<player>5__2.CameraBone, 0.1f); } EnsurePlayerAwakeAfterRespawn(<player>5__2, $"ApplyVisualDelayed(slot={slot})"); if (slot >= 2) { EnsureExtraSpawnStateUnlocked(<player>5__2, $"ApplyVisualDelayed(slot={slot})"); SnapSpawnedPlayerToAwakeIdle(<player>5__2, $"ApplyVisualDelayed(slot={slot})"); } if (_podiumClones.TryGetValue(slot, out var value)) { foreach (Interaction_WeaponSelectionPodium item in value) { if ((Object)(object)item != (Object)null) { EnsurePodiumCloneOwner(item, slot, <player>5__2, forceInteractable: true); Plugin.Log.LogInfo((object)$"[MP] Refreshed podium clone state for slot {slot} on {((Object)item).name}"); } } } HUD_Hearts val = <player>5__2.hudHearts; if ((Object)(object)val != (Object)null && (Object)(object)val.playerFarming != (Object)(object)<player>5__2) { Plugin.Log.LogWarning((object)$"[MP] ApplyVisualDelayed: stale hudHearts owner for slot {slot}, skipping direct faithAmmo init"); val = null; } if ((Object)(object)val == (Object)null && _extraHeartBars.TryGetValue(slot, out var value2) && (Object)(object)value2 != (Object)null) { HUD_Hearts component = value2.GetComponent<HUD_Hearts>(); if ((Object)(object)component != (Object)null && (Object)(object)component.playerFarming == (Object)(object)<player>5__2) { val = component; <player>5__2.hudHearts = component; } } if (slot >= 2 && (Object)(object)val != (Object)null) { SyncFaithAmmoForOwner(<player>5__2, val, $"ApplyVisualDelayed(slot={slot})", forceReinit: true); } _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); Plugin.Log.LogInfo((object)$"ApplyVisualDelayed done for slot {slot}"); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <AutoRespawnMissingSlotsRoutine>d__153 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string reason; public List<int> missingSlots; private bool <allowDuringReviveFlow>5__2; private float <interSpawnDelay>5__3; private List<int>.Enumerator <>7__wrap3; private int <slot>5__5; private float <spawnWait>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AutoRespawnMissingSlotsRoutine>d__153(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 2) <= 1u) { try { } finally { <>m__Finally1(); } } <>7__wrap3 = default(List<int>.Enumerator); <>1__state = -2; } private bool MoveNext() { //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; _autoRespawnRoutineRunning = true; <allowDuringReviveFlow>5__2 = IsWakeAllRespawnReason(reason) || IsSacrificialReviveFlowReason(reason); <interSpawnDelay>5__3 = (<allowDuringReviveFlow>5__2 ? 0.15f : 0.75f); <>2__current = (object)new WaitForSeconds(0.35f); <>1__state = 1; return true; case 1: <>1__state = -1; <>7__wrap3 = missingSlots.GetEnumerator(); <>1__state = -3; goto IL_0318; case 2: <>1__state = -3; <spawnWait>5__6 += 0.1f; goto IL_01d2; case 3: { <>1__state = -3; goto IL_0318; } IL_0318: while (true) { if (<>7__wrap3.MoveNext()) { <slot>5__5 = <>7__wrap3.Current; if (!((Object)(object)CoopManager.Instance == (Object)null) && CoopManager.CoopActive) { if (!_pendingSpawnSlots.Contains(<slot>5__5) && !((Object)(object)FindActivePlayerBySlot(<slot>5__5) != (Object)null)) { Player player = RewiredInputManager.GetPlayer(<slot>5__5); if (((player != null && player.controllers != null && player.controllers.Joysticks != null && player.controllers.Joysticks.Count > 0) || _desiredExtraSlots.Contains(<slot>5__5)) && !((Object)(object)PlayerFarming.Instance == (Object)null) && !((Object)(object)PlayerFarming.Instance.state == (Object)null) && (!IsReviveOrCutsceneFlowActive() || <allowDuringReviveFlow>5__2)) { break; } } continue; } } <>m__Finally1(); <>7__wrap3 = default(List<int>.Enumerator); _autoRespawnRoutineRunning = false; _nextAutoRespawnAttemptAt = Time.unscaledTime + 1.5f; return false; } <spawnWait>5__6 = 0f; goto IL_01d2; IL_01d2: if (CoopManager.Instance.IsSpawningOrRemovingPlayer && <spawnWait>5__6 < 10f) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 2; return true; } if (!_pendingSpawnSlots.Contains(<slot>5__5) && !((Object)(object)FindActivePlayerBySlot(<slot>5__5) != (Object)null)) { Plugin.Log.LogInfo((object)$"[MP] Auto-respawn ({reason}): spawning missing slot {<slot>5__5}"); _pendingSpawnSlots.Add(<slot>5__5); bool flag = false; Plugin.Log.LogInfo((object)$"[MP] Auto-respawn spawn mode slot {<slot>5__5}: animated={flag}"); _pendingSpawnUseAnimation[<slot>5__5] = flag; try { CoopManager.Instance.SpawnCoopPlayer(<slot>5__5, flag, -1f); } catch (Exception ex) { _pendingSpawnSlots.Remove(<slot>5__5); _pendingSpawnUseAnimation.Remove(<slot>5__5); Plugin.Log.LogWarning((object)$"[MP] Auto-respawn failed for slot {<slot>5__5}: {ex.GetType().Name}: {ex.Message}"); } <>2__current = (object)new WaitForSeconds(<interSpawnDelay>5__3); <>1__state = 3; return true; } goto IL_0318; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap3).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__230 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IDisposable, IEnumerator { private int <>1__state; private CodeInstruction <>2__current; private int <>l__initialThreadId; private IEnumerable<CodeInstruction> instructions; public IEnumerable<CodeInstruction> <>3__instructions; private MethodInfo <clearNoArgs>5__2; private MethodInfo <clearWithChildren>5__3; private MethodInfo <safeNoArgs>5__4; private MethodInfo <safeWithChildren>5__5; private int <replacedNoArgs>5__6; private int <replacedWithChildren>5__7; private IEnumerator<CodeInstruction> <>7__wrap7; CodeInstruction IEnumerator<CodeInstruction>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__230(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <clearNoArgs>5__2 = null; <clearWithChildren>5__3 = null; <safeNoArgs>5__4 = null; <safeWithChildren>5__5 = null; <>7__wrap7 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <clearNoArgs>5__2 = AccessTools.Method(typeof(ParticleSystem), "Clear", Type.EmptyTypes, (Type[])null); <clearWithChildren>5__3 = AccessTools.Method(typeof(ParticleSystem), "Clear", new Type[1] { typeof(bool) }, (Type[])null); <safeNoArgs>5__4 = AccessTools.Method(typeof(Patches), "SafeParticleClear_NoArgs", (Type[])null, (Type[])null); <safeWithChildren>5__5 = AccessTools.Method(typeof(Patches), "SafeParticleClear_WithChildren", (Type[])null, (Type[])null); <replacedNoArgs>5__6 = 0; <replacedWithChildren>5__7 = 0; <>7__wrap7 = instructions.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } if (<>7__wrap7.MoveNext()) { CodeInstruction current = <>7__wrap7.Current; if (<clearNoArgs>5__2 != null && CodeInstructionExtensions.Calls(current, <clearNoArgs>5__2)) { <replacedNoArgs>5__6++; current.opcode = OpCodes.Call; current.operand = <safeNoArgs>5__4; } else if (<clearWithChildren>5__3 != null && CodeInstructionExtensions.Calls(current, <clearWithChildren>5__3)) { <replacedWithChildren>5__7++; current.opcode = OpCodes.Call; current.operand = <safeWithChildren>5__5; } <>2__current = current; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap7 = null; int num = <replacedNoArgs>5__6 + <replacedWithChildren>5__7; if (_safeBiomeClearPatchCountLog != num) { _safeBiomeClearPatchCountLog = num; Plugin.Log.LogInfo((object)($"[MP] Safe biome particle-clear transpiler replaced {num} ParticleSystem.Clear call(s) " + $"(noArgs={<replacedNoArgs>5__6}, withChildren={<replacedWithChildren>5__7})")); } return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap7 != null) { <>7__wrap7.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator() { <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__230 <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__ = this; } else { <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__ = new <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__230(0); } <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__.instructions = <>3__instructions; return <BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<CodeInstruction>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <DeferredEnsurePodiumOwner>d__178 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Interaction_WeaponSelectionPodium podium; public int slot; private float <waited>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DeferredEnsurePodiumOwner>d__178(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <waited>5__2 = 0f; break; case 1: <>1__state = -1; <waited>5__2 += 0.1f; break; case 2: <>1__state = -1; <waited>5__2 += 0.1f; break; } if (<waited>5__2 < 3f) { if ((Object)(object)podium == (Object)null) { return false; } if (!((Component)podium).gameObject.activeInHierarchy) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; } PlayerFarming val = FindActivePlayerBySlot(slot); EnsurePodiumCloneOwner(podium, slot, val, forceInteractable: true); if ((Object)(object)val != (Object)null) { return false; } <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 2; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DeferredPositionAllPlayers>d__188 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int expectedTotal; public Vector3 pos; public bool increment; private float <waited>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DeferredPositionAllPlayers>d__188(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00a0: 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_0034: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <waited>5__2 = 0f; break; case 1: <>1__state = -1; <waited>5__2 += 0.25f; break; } if (PlayerFarming.playersCount < expectedTotal && <waited>5__2 < 15f) { <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; } Plugin.Log.LogInfo((object)$"DeferredPositionAllPlayers: proceeding with {PlayerFarming.playersCount} players after {<waited>5__2:F1}s"); _positioningDeferred = false; PlayerFarming.PositionAllPlayers(pos, increment); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DelayedRespawnAfterReviveRoutine>d__140 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string reason; private bool <allowDuringReviveFlow>5__2; private float <deadline>5__3; private int <attempts>5__4; private int <i>5__5; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedRespawnAfterReviveRoutine>d__140(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 2u) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Expected O, but got Unknown //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Expected O, but got Unknown bool result; try { List<int> missingSpawnableExtraSlots; bool flag; bool isSpawningOrRemovingPlayer; bool flag2; switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: { <>1__state = -1; <>1__state = -3; if ((Object)(object)CoopManager.Instance == (Object)null) { result = false; break; } <allowDuringReviveFlow>5__2 = IsWakeAllRespawnReason(reason) || IsSacrificialReviveFlowReason(reason); float num = (<allowDuringReviveFlow>5__2 ? 0.05f : 0.35f); <>2__current = (object)new WaitForSeconds(num); <>1__state = 1; result = true; goto end_IL_0000; } case 1: <>1__state = -3; <deadline>5__3 = Time.unscaledTime + 15f; <attempts>5__4 = 0; goto IL_0281; case 2: <>1__state = -3; goto IL_0281; case 3: { <>1__state = -3; if (AnyNonLambPlayerNeedsWakeRecovery()) { EnsureAllPlayersAwake(reason + ".DelayedRespawnAfterReviveRoutine.stabilize"); EnsureAllExtraPlayersUnlocked(reason + ".DelayedRespawnAfterReviveRoutine.stabilize"); QueuePostReviveHardResetForAll(reason + ".DelayedRespawnAfterReviveRoutine.stabilize"); } <i>5__5++; goto IL_03bd; } IL_03bd: if (<i>5__5 >= 8 || (Object)(object)CoopManager.Instance == (Object)null || !CoopManager.CoopActive) { goto IL_03c9; } <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 3; result = true; goto end_IL_0000; IL_0281: if (!(Time.unscaledTime < <deadline>5__3)) { goto IL_0291; } if ((Object)(object)CoopManager.Instance == (Object)null || !CoopManager.CoopActive) { result = false; break; } _nextAutoRespawnAttemptAt = 0f; TryAutoRespawnMissingExtraSlots($"{reason}.poll{<attempts>5__4}"); if ((reason.StartsWith("WakeAllKnockedOutPlayers", StringComparison.Ordinal) || IsSacrificialReviveFlowReason(reason)) && AnyNonLambPlayerNeedsWakeRecovery()) { EnsureAllPlayersAwake(reason + ".DelayedRespawnAfterReviveRoutine"); EnsureAllExtraPlayersUnlocked(reason + ".DelayedRespawnAfterReviveRoutine"); QueuePostReviveHardResetForAll(reason + ".DelayedRespawnAfterReviveRoutine"); } PrunePendingSpawnSlots(); missingSpawnableExtraSlots = GetMissingSpawnableExtraSlots(); flag = _pendingSpawnSlots.Count > 0; isSpawningOrRemovingPlayer = CoopManager.Instance.IsSpawningOrRemovingPlayer; flag2 = IsReviveOrCutsceneFlowActive(); if (missingSpawnableExtraSlots.Count == 0 && !flag && !isSpawningOrRemovingPlayer && (!flag2 | <allowDuringReviveFlow>5__2)) { if (<attempts>5__4 > 0) { Plugin.Log.LogInfo((object)$"[MP] Delayed revive respawn settled after {<attempts>5__4} poll(s) ({reason})"); } goto IL_0291; } if (<attempts>5__4 % 8 == 0) { Plugin.Log.LogInfo((object)$"[MP] Delayed revive respawn polling ({reason}): missing={missingSpawnableExtraSlots.Count}, pending={_pendingSpawnSlots.Count}, blocked={flag2}, spawning={isSpawningOrRemovingPlayer}"); } <attempts>5__4++; <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 2; result = true; goto end_IL_0000; IL_0291: _nextAutoRespawnAttemptAt = 0f; TryAutoRespawnMissingExtraSlots(reason + ".final"); if (reason.StartsWith("WakeAllKnockedOutPlayers", StringComparison.Ordinal) || IsSacrificialReviveFlowReason(reason)) { if (AnyNonLambPlayerNeedsWakeRecovery()) { EnsureAllPlayersAwake(reason + ".DelayedRespawnAfterReviveRoutine.final"); EnsureAllExtraPlayersUnlocked(reason + ".DelayedRespawnAfterReviveRoutine.final"); QueuePostReviveHardResetForAll(reason + ".DelayedRespawnAfterReviveRoutine.final"); } <i>5__5 = 0; goto IL_03bd; } goto IL_03c9; IL_03c9: <>m__Finally1(); result = false; goto end_IL_0000; } <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _delayedRespawnAfterReviveQueued = false; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DungeonRoomTransitionStabilizeRoutine>d__195 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int roomId; public string reason; private int <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DungeonRoomTransitionStabilizeRoutine>d__195(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Expected O, but got Unknown bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; if (_dungeonRoomStabilizeRunning) { result = false; } else { _dungeonRoomStabilizeRunning = true; <>1__state = -3; <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 1; result = true; } goto end_IL_0000; case 1: <>1__state = -3; <i>5__2 = 0; break; case 2: <>1__state = -3; <i>5__2++; break; } if (<i>5__2 < 18) { if ((Object)(object)CoopManager.Instance == (Object)null || !CoopManager.CoopActive) { result = false; goto IL_01bf; } if (!GameManager.IsDungeon(PlayerFarming.Location)) { result = false; goto IL_01bf; } int currentDungeonRoomInstanceIdSafe = GetCurrentDungeonRoomInstanceIdSafe(); if (currentDungeonRoomInstanceIdSafe != int.MinValue && currentDungeonRoomInstanceIdSafe != roomId) { result = false; goto IL_01bf; } ClearFalseKnockedOutStatesForAll("RoomStabilize:" + reason); EnsureAllExtraPlayersUnlocked("RoomStabilize:" + reason); if (<i>5__2 <= 4) { StabilizeGoatRoomEntryState($"{reason}.tick{<i>5__2}"); } if (<i>5__2 == 0 || <i>5__2 == 2 || <i>5__2 == 6 || <i>5__2 == 12) { AggressiveControllerOwnershipHeartbeat("RoomStabilize:" + reason); _nextAutoRespawnAttemptAt = 0f; TryAutoRespawnMissingExtraSlots($"RoomStabilize:{reason}.tick{<i>5__2}"); } <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 2; result = true; } else { <>m__Finally1(); result = false; } goto end_IL_0000; IL_01bf: <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _dungeonRoomStabilizeRunning = false; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <EnsureP2DungeonEntranceNotStuckRoutine>d__172 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private float <waited>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnsureP2DungeonEntranceNotStuckRoutine>d__172(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01db: 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_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_017a: 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_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Invalid comparison between Unknown and I4 //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Invalid comparison between Unknown and I4 //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Invalid comparison between Unknown and I4 //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Invalid comparison between Unknown and I4 bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; if (_p2EntranceGuardRunning) { result = false; } else { _p2EntranceGuardRunning = true; <>1__state = -3; if (!GameManager.IsDungeon(PlayerFarming.Location)) { result = false; break; } <>2__current = (object)new WaitForSeconds(1.8f); <>1__state = 1; result = true; } goto end_IL_0000; case 1: <>1__state = -3; <waited>5__2 = 0f; goto IL_02dc; case 2: { <>1__state = -3; <waited>5__2 += 0.2f; goto IL_02dc; } IL_02dc: if (<waited>5__2 < 3f) { if (!GameManager.IsDungeon(PlayerFarming.Location)) { result = false; break; } PlayerFarming instance = PlayerFarming.Instance; PlayerFarming val = FindActivePlayerBySlot(1); if ((Object)(object)instance != (Object)null && (Object)(object)val != (Object)null && (Object)(object)((Component)instance).gameObject != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && ((Component)instance).gameObject.activeInHierarchy && ((Component)val).gameObject.activeInHierarchy) { Vector3 val2 = ((Component)val).transform.position - ((Component)instance).transform.position; float magnitude = ((Vector3)(ref val2)).magnitude; bool flag = val2.y < -2.8f && Mathf.Abs(val2.x) < 4.5f && magnitude > 3.2f; bool flag2 = false; bool flag3 = IsFalseKnockedOutState(val); try { if ((Object)(object)val.state != (Object)null) { State cURRENT_STATE = val.state.CURRENT_STATE; flag2 = val.state.LockStateChanges || (int)cURRENT_STATE == 13 || (int)cURRENT_STATE == 49 || (int)cURRENT_STATE == 24 || (int)cURRENT_STATE == 16; } } catch { } if (flag && (flag2 || flag3)) { Vector3 position = ((Component)instance).transform.position + new Vector3(1.25f, -0.1f, 0f); ((Component)val).transform.position = position; try { if ((Object)(object)val.state != (Object)null) { val.state.LockStateChanges = false; val.state.CURRENT_STATE = (State)0; } } catch { } try { if ((Object)(object)val.health != (Object)null && ((Health)val.health).CurrentHP <= 0f) { ((Health)val.health).HP = 2f; } } catch { } val.IsKnockedOut = false; Plugin.Log.LogWarning((object)$"[MP] P2 entrance unstuck applied (dy={val2.y:0.00}, dist={magnitude:0.00}, stateLocked={flag2}, falseDowned={flag3})"); result = false; break; } } <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 2; result = true; } else { <>m__Finally1(); result = false; } goto end_IL_0000; } <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _p2EntranceGuardRunning = false; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <PostReviveHardResetRoutine>d__110 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int slot; public string reason; private int <settledTicks>5__2; private int <i>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PostReviveHardResetRoutine>d__110(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <settledTicks>5__2 = 0; <i>5__3 = 0; break; case 1: <>1__state = -3; goto IL_0156; case 2: { <>1__state = -3; goto IL_0156; } IL_0156: <i>5__3++; break; } PlayerFarming val; int num; int num2; if (<i>5__3 < 18) { if ((Object)(object)CoopManager.Instance == (Object)null || !CoopManager.CoopActive) { result = false; goto IL_017d; } val = FindActivePlayerBySlot(slot); if (!((Object)(object)val == (Object)null)) { if (!NeedsWakeRecovery(val)) { num = (IsFalseKnockedOutState(val) ? 1 : 0); if (num == 0) { num2 = ((<i>5__3 < 6) ? 1 : 0); goto IL_00bd; } } else { num = 1; } num2 = 0; goto IL_00bd; } <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; result = true; } else { <>m__Finally1(); result = false; } goto end_IL_0000; IL_00bd: bool flag = (byte)num2 != 0; if (num != 0 || <i>5__3 < 2 || flag) { ForceReviveVisualState(val, $"PostRevive:{reason}.tick{<i>5__3}"); } if (num == 0) { <settledTicks>5__2++; if (<settledTicks>5__2 >= 6 && <i>5__3 >= 8) { result = false; goto IL_017d; } } else { <settledTicks>5__2 = 0; } <>2__current = (object)new WaitForSeconds((<i>5__3 < 6) ? 0.12f : 0.18f); <>1__state = 2; result = true; goto end_IL_0000; IL_017d: <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _postReviveHardResetSlots.Remove(slot); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <ReassignHudHeartsNextFrame>d__207 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public PlayerFarming p; public HUD_Hearts hearts; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ReassignHudHeartsNextFrame>d__207(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)p != (Object)null && (Object)(object)hearts != (Object)null) { if ((Object)(object)hearts.playerFarming != (Object)(object)p) { hearts.playerFarming = p; } if ((Object)(object)p.hudHearts != (Object)(object)hearts) { p.hudHearts = hearts; Plugin.Log.LogInfo((object)$"[MP] Re-assigned hudHearts for playerID={p.playerID} after frame delay"); } } <>2__current = null; <>1__state = 2; return true; case 2: <>1__state = -1; SyncFaithAmmoForOwner(p, hearts, "ReassignHudHeartsNextFrame", forceReinit: true); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <RespawnExtraSlots>d__58 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int count; private bool <inDungeon>5__2; private float <timeout>5__3; private float <waited>5__4; private int <s>5__5; private float <spawnWait>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RespawnExtraSlots>d__58(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 3u) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Invalid comparison between Unknown and I4 bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; _startupExtraRespawnDepth++; <>1__state = -3; <inDungeon>5__2 = GameManager.IsDungeon(PlayerFarming.Location); <timeout>5__3 = 30f; <waited>5__4 = 0f; goto IL_00f9; case 1: <>1__state = -3; <waited>5__4 += 0.25f; goto IL_00f9; case 2: <>1__state = -3; <s>5__5 = 2; goto IL_048b; case 3: <>1__state = -3; <spawnWait>5__6 += 0.1f; goto IL_030a; case 4: { <>1__state = -3; goto IL_0479; } IL_048b: if (<s>5__5 >= 2 + count || <s>5__5 >= 4) { break; } if (_pendingSpawnSlots.Contains(<s>5__5)) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} already pending, skipping duplicate startup spawn"); } else if ((Object)(object)FindActivePlayerBySlot(<s>5__5) != (Object)null) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} already active, skipping startup spawn"); } else if (CoopManager.AllPlayerGameObjects != null && <s>5__5 < CoopManager.AllPlayerGameObjects.Length && (Object)(object)CoopManager.AllPlayerGameObjects[<s>5__5] != (Object)null && CoopManager.AllPlayerGameObjects[<s>5__5].activeInHierarchy) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} already has active gameobject, skipping startup spawn"); } else { Player player = RewiredInputManager.GetPlayer(<s>5__5); if (player != null && player.controllers != null && player.controllers.Joysticks != null && player.controllers.Joysticks.Count > 0) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: spawning slot {<s>5__5}"); <spawnWait>5__6 = 0f; goto IL_030a; } Plugin.Log.LogWarning((object)$"RespawnExtraSlots: slot {<s>5__5} has no joystick, skipping"); } goto IL_0479; IL_0479: <s>5__5++; goto IL_048b; IL_030a: if ((Object)(object)CoopManager.Instance != (Object)null && CoopManager.Instance.IsSpawningOrRemovingPlayer && <spawnWait>5__6 < 10f) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 3; result = true; } else { if (_pendingSpawnSlots.Contains(<s>5__5)) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} became pending during wait, skipping duplicate startup spawn"); goto IL_0479; } if ((Object)(object)FindActivePlayerBySlot(<s>5__5) != (Object)null) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} became active during wait, skipping duplicate startup spawn"); goto IL_0479; } _pendingSpawnSlots.Add(<s>5__5); bool flag = false; _pendingSpawnUseAnimation[<s>5__5] = flag; Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {<s>5__5} spawn mode animated={flag}"); try { CoopManager.Instance.SpawnCoopPlayer(<s>5__5, flag, -1f); } catch (Exception ex) { _pendingSpawnSlots.Remove(<s>5__5); _pendingSpawnUseAnimation.Remove(<s>5__5); Plugin.Log.LogWarning((object)$"RespawnExtraSlots: failed spawning slot {<s>5__5}: {ex.GetType().Name}: {ex.Message}"); } <>2__current = (object)new WaitForSeconds(1f); <>1__state = 4; result = true; } goto end_IL_0000; IL_00f9: if (<waited>5__4 < <timeout>5__3 && (!((Object)(object)PlayerFarming.Instance != (Object)null) || !((Component)PlayerFarming.Instance).gameObject.activeSelf || PlayerFarming.playersCount < 1 || !((Object)(object)PlayerFarming.Instance.state != (Object)null) || (!<inDungeon>5__2 && (int)PlayerFarming.Instance.state.CURRENT_STATE == 13))) { <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; result = true; } else if (<waited>5__4 >= <timeout>5__3) { Plugin.Log.LogWarning((object)"RespawnExtraSlots: timed out waiting for PlayerFarming.Instance"); result = false; <>m__Finally1(); } else { if (<inDungeon>5__2) { _dungeonIntroProtectUntil = Time.unscaledTime + 8f; } <>2__current = (object)new WaitForSeconds(1f); <>1__state = 2; result = true; } goto end_IL_0000; } <>m__Finally1(); result = false; end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _startupExtraRespawnDepth = Math.Max(0, _startupExtraRespawnDepth - 1); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _removingPlayer = false; private static bool _manualRemoveRequested = false; private static float _autoRemoveSuppressedUntil = 0f; private const float AutoRemoveSuppressSeconds = 8f; private static int _savedExtraSlotCount = 0; private static bool _autoRespawnRoutineRunning = false; private static float _nextAutoRespawnAttemptAt = 0f; private static float _nextAutoRespawnHeartbeatAt = 0f; private static float _nextControllerHealHeartbeatAt = 0f; private static float _nextRumbleCleanupAt = 0f; private static readonly HashSet<int> _pendingSpawnSlots = new HashSet<int>(); private static readonly Dictionary<int, bool> _pendingSpawnUseAnimation = new Dictionary<int, bool>(); private static readonly Dictionary<int, float> _pendingSpawnFirstSeenAt = new Dictionary<int, float>(); private static readonly HashSet<int> _desiredExtraSlots = new HashSet<int>(); private static int _startupExtraRespawnDepth = 0; private static float _dungeonIntroProtectUntil = 0f; private static float _nextPendingBlockLogAt = 0f; private static string _lastLoggedDungeonRoomPath = null; private static bool _p2EntranceGuardRunning = false; private static bool _dungeonRoomStabilizeRunning = false; private static int _lastStabilizedDungeonRoomId = int.MinValue; private static float _nextDungeonRoomStabilizeCheckAt = 0f; private static float _roomEntryDistanceRepositionGraceUntil = 0f; private static readonly HashSet<int> _warnedOrphanSlotObjects = new HashSet<int>(); private static readonly HashSet<int> _postReviveHardResetSlots = new HashSet<int>(); private static float _lastObservedWakeHealth = 2f; private static float _lastObservedSacrificialWakeHealth = 2f; private static float _sacrificialReviveWindowUntil = 0f; private const float SacrificialReviveWindowSeconds = 7f; private const float BoostedWakeHealthThreshold = 2.01f; private static readonly Dictionary<int, float> _nextExtraFalseDownedWatchdogAt = new Dictionary<int, float>(); private static readonly Dictionary<int, float> _postReviveRaceGuardUntil = new Dictionary<int, float>(); private static readonly Dictionary<int, float> _nextPostReviveForceApplyAt = new Dictionary<int, float>(); private static float _nextPostReviveRaceGuardHeartbeatAt = 0f; private const float PostReviveRaceGuardSeconds = 4f; private const float PostReviveRaceGuardPollInterval = 0.18f; private static Image _p2Widget; private static Image _p3Widget; private static Image _p4Widget; private static RectTransform _p2Arrow; private static RectTransform _p3Arrow; private static RectTransform _p4Arrow; private static readonly Dictionary<int, Sprite> _cachedSprites = new Dictionary<int, Sprite>(); private static readonly Dictionary<int, Image> _cachedTextImages = new Dictionary<int, Image>(); private static readonly Dictionary<int, SpriteRenderer> _cachedHaloSR = new Dictionary<int, SpriteRenderer>(); private static readonly HashSet<int> _haloLogged = new HashSet<int>(); private static readonly Dictionary<int, Sprite> _cachedHaloSprites = new Dictionary<int, Sprite>(); private static readonly Dictionary<int, string> _haloSpriteNames = new Dictionary<int, string> { { 2, "halo_orange.png" }, { 3, "halo_blue.png" } }; private static int _podiumExtraCount = 0; private static readonly Vector3[] SpawnOffsets = (Vector3[])(object)new Vector3[4] { Vector3.zero, new Vector3(1.5f, 0f, 0f), new Vector3(0f, 1.5f, 0f), new Vector3(0f, -1.5f, 0f) }; private static readonly FieldInfo _playerControllerUntouchableTimerField = AccessTools.Field(typeof(PlayerController), "untouchableTimer"); private static readonly FieldInfo _playerControllerInvincibleTimerField = AccessTools.Field(typeof(PlayerController), "invincibleTimer"); private static readonly FieldInfo _playerControllerImmuneToProjectilesTimerField = AccessTools.Field(typeof(PlayerController), "immuneToProjectilesTimer"); private static readonly FieldInfo _playerControllerUntouchableTimerFlashField = AccessTools.Field(typeof(PlayerController), "untouchableTimerFlash"); private static Type _cachedHealthReflectionType; private static FieldInfo _healthInvincibleField; private static FieldInfo _healthUntouchableField; private static FieldInfo _healthIgnoreProjectilesField; private static FieldInfo _healthMaxHpField; private static FieldInfo _healthTotalHpField; private static PropertyInfo _healthIgnoreProjectilesProperty; private static PropertyInfo _healthMaxHpProperty; private static PropertyInfo _healthTotalHpProperty; private static MethodInfo _healthClearAllStasisEffectsMethod; private static bool _faithAmmoDumped = false; private static bool _delayedRespawnAfterReviveQueued = false; private const float DelayedReviveRespawnInitialDelay = 0.35f; private const float DelayedReviveRespawnPollInterval = 0.2f; private const float DelayedReviveRespawnMaxDuration = 15f; private static readonly Vector3[] _teleporterOffsets = (Vector3[])(object)new Vector3[4] { new Vector3(-0.75f, 0f, 0f), new Vector3(0.75f, 0f, 0f), new Vector3(-1.5f, 0f, 0f), new Vector3(1.5f, 0f, 0f) }; private static bool _spawnedExtraWeaponPodiums = false; private static bool _spawnedExtraSpellPodiums = false; private static readonly FieldInfo _twinPodiumField = typeof(Interaction_WeaponSelectionPodium).GetField("TwinPodium", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Dictionary<int, List<Interaction_WeaponSelectionPodium>> _podiumClones = new Dictionary<int, List<Interaction_WeaponSelectionPodium>>(); private static readonly Dictionary<int, int> _podiumOwnerSlotByInstanceId = new Dictionary<int, int>(); private static bool _positioningDeferred = false; private static readonly Dictionary<int, GameObject> _extraHeartBars = new Dictionary<int, GameObject>(); private static readonly HashSet<int> _suppressedHeartEffectLogs = new HashSet<int>(); private static readonly HashSet<int> _suppressedFaithAmmoLogs = new HashSet<int>(); private static readonly HashSet<int> _suppressedTrinketHudLogs = new HashSet<int>(); private static readonly HashSet<int> _missingDamageIconLogs = new HashSet<int>(); private static readonly MethodInfo _hudHeartsClearHealthEvents = typeof(HUD_Hearts).GetMethod("ClearHealthEvents", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _faithAmmoUpdateBar = typeof(FaithAmmo).GetMethod("UpdateBar", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _coopIndicatorAnimateOnDamagedField = typeof(CoopIndicatorIcon).GetField("animateIconOnDamaged", BindingFlags.Instance | BindingFlags.NonPublic); private static bool _reroutingFaithAmmoUse = false; private static int _safeBiomeClearPatchCountLog = -1; private static readonly Dictionary<char, int[]> _digitBitmaps = new Dictionary<char, int[]> { { '1', new int[35] { 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0 } }, { '2', new int[35] { 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1 } }, { '3', new int[35] { 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0 } }, { '4', new int[35] { 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0 } } }; public static void CoopManager_Awake() { GameObject[] allPlayerGameObjects = CoopManager.AllPlayerGameObjects; if (allPlayerGameObjects.Length < 4) { GameObject[] array = (GameObject[])(object)new GameObject[4]; for (int i = 0; i < allPlayerGameObjects.Length; i++) { array[i] = allPlayerGameObjects[i]; } typeof(CoopManager).GetField("AllPlayerGameObjects", BindingFlags.Static | BindingFlags.Public).SetValue(null, array); Plugin.Log.LogInfo((object)$"AllPlayerGameObjects expanded to {4}"); } } public static void OnControllerConnected(ControllerStatusChangedEventArgs args) { if (!CoopManager.CoopActive || PlayerFarming.playersCount >= 4 || (Object)(object)CoopManager.Instance == (Object)null || CoopManager.Instance.IsSpawningOrRemovingPlayer) { return; } Joystick val = null; foreach (Joystick joystick in ReInput.controllers.Joysticks) { if (((Controller)joystick).id == args.controllerId) { val = joystick; break; } } if (val == null) { return; } for (int i = 0; i < 4; i++) { Player player = RewiredInputManager.GetPlayer(i); if (player != null && player.controllers.ContainsController((Controller)(object)val)) { Plugin.Log.LogInfo((object)$"OnControllerConnected: already assigned to slot {i}, skipping"); return; } } int playersCount = PlayerFarming.playersCount; Plugin.Log.LogInfo((object)$"OnControllerConnected: auto-assigning to slot {playersCount}"); for (int j = 0; j < 4; j++) { RewiredInputManager.GetPlayer(j).controllers.RemoveController((Controller)(object)val); } RewiredInputManager.GetPlayer(playersCount).controllers.AddController((Controller)(object)val, true); MarkDesiredExtraSlot(playersCount, desired: true); if (playersCount >= 2) { _pendingSpawnSlots.Add(playersCount); } CoopManager.Instance.SpawnCoopPlayer(playersCount, true, -1f); CoopManager.CoopActive = true; CoopManager.RefreshCoopPlayerRewired(); TryStopAllRumble("OnControllerConnected"); Plugin.Log.LogInfo((object)$"Auto-spawned goat at slot {playersCount}"); } public static bool AddPlayerFromMenu() { if (PlayerFarming.playersCount < 4) { Plugin.Log.LogInfo((object)$"AddPlayerFromMenu: players={PlayerFarming.playersCount}, opening assign menu"); UICoopAssignController obj = MonoSingleton<UIManager>.Instance.ShowCoopAssignMenu(); ((UIMenuBase)obj).OnHidden = (Action)Delegate.Combine(((UIMenuBase)obj).OnHidden, (Action)delegate { if (CoopManager.CoopActive) { CoopManager.AddtionalUser = UserHelper.GetPlayer(1); } }); } return false; } public static bool OnCoopButtonPressed(UIPauseMenuController __instance) { Plugin.Log.LogInfo((object)$"OnCoopButtonPressed: players={PlayerFarming.playersCount}, coopActive={CoopManager.CoopActive}"); if (PlayerFarming.playersCount < 4) { ((UIMenuBase)__instance).Hide(true); CoopManager.AddPlayerFromMenu(); return false; } if (CoopManager.CoopActive) { ((UIMenuBase)__instance).Hide(false); _manualRemoveRequested = true; CoopManager.RemovePlayerFromMenu(); return false; } return true; } public static void RefreshCoopText(UIPauseMenuController __instance) { FieldInfo field = typeof(UIPauseMenuController).GetField("_coopButtonText", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(UIPauseMenuController).GetField("_coopButton", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field3 = typeof(UIPauseMenuController).GetField("DenyCoop", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null || field2 == null) { return; } object? value = field.GetValue(__instance); TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null); object? value2 = field2.GetValue(__instance); MMButton val2 = (MMButton)((value2 is MMButton) ? value2 : null); bool flag = field3 != null && (bool)field3.GetValue(__instance); if (!((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || flag)) { if (PlayerFarming.playersCount < 4) { ((TMP_Text)val).text = $"Add Player ({PlayerFarming.playersCount}/{4})"; val2.Interactable = true; } else { ((TMP_Text)val).text = $"Remove Player ({PlayerFarming.playersCount}/{4})"; val2.Interactable = true; } } } public static bool ConfirmSpawnButtonPress(UICoopAssignController __instance) { FieldInfo field = typeof(UICoopAssignController).GetField("displayedConnectedGamepads", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field2 = typeof(UICoopAssignController).GetField("keyboardInputOption", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field3 = typeof(UICoopAssignController).GetField("preventSpawnBufferTime", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field4 = typeof(UICoopAssignController).GetField("confirmLock", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field2 == null) { Plugin.Log.LogError((object)"Reflection failed"); return true; } if (field3 != null) { float num = (float)field3.GetValue(__instance); if (Time.realtimeSinceStartup < num + 0.25f) { return false; } } if (field4 != null) { if ((bool)field4.GetValue(__instance)) { return false; } field4.SetValue(__instance, true); } if (!(field.GetValue(__instance) is CoopAssignInputOption[] array)) { return true; } int num2 = PlayerFarming.playersCount; if (num2 < 1) { num2 = 1; } Plugin.Log.LogInfo((object)$"ConfirmSpawnButtonPress: nextSlot={num2}, players={PlayerFarming.playersCount}"); EnforceKeyboardOwnership(); List<Joystick> list = new List<Joystick>(ReInput.controllers.Joysticks); for (int i = 0; i < array.Length && i < list.Count; i++) { CoopAssignInputOption val = array[i]; if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf || val.selection != -1) { continue; } Joystick val2 = list[i]; if (val2 == null) { continue; } for (int j = 1; j < 4; j++) { if (!RewiredInputManager.GetPlayer(j).controllers.ContainsController((Controller)(object)val2)) { continue; } PlayerFarming val3 = null; foreach (PlayerFarming player2 in PlayerFarming.players) { if (!player2.isLamb && player2.playerID == j && ((Component)player2).gameObject.activeSelf) { val3 = player2; break; } } if ((Object)(object)val3 == (Object)null) { continue; } Plugin.Log.LogInfo((object)$"Joystick [{i}] moved to Lamb side — removing goat slot {j} (dropout)"); int playerID = val3.playerID; MarkDesiredExtraSlot(playerID, desired: false); _pendingSpawnSlots.Remove(playerID); _pendingSpawnUseAnimation.Remove(playerID); if (_cachedSprites.ContainsKey(playerID)) { _cachedSprites.Remove(playerID); } if (_cachedTextImages.ContainsKey(playerID)) { _cachedTextImages.Remove(playerID); } if (_cachedHaloSR.ContainsKey(playerID)) { _cachedHaloSR.Remove(playerID); } if (_cachedHaloSprites.ContainsKey(playerID)) { _cachedHaloSprites.Remove(playerID); } _haloLogged.Remove(playerID); _removingPlayer = true; CoopManager.AddtionalUser = null; UnhookVanillaCoopDelegates(); CoopManager.Instance.RemoveCoopPlayer(val3, true, false, true); if (_extraHeartBars.TryGetValue(playerID, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } _extraHeartBars.Remove(playerID); if (PlayerFarming.playersCount > 1 && (Object)(object)HUD_Manager.Instance != (Object)null) { foreach (PlayerFarming player3 in PlayerFarming.players) { if (!player3.isLamb) { HUD_Manager.Instance.healthManager.Init(player3); } } } int num3 = 0; foreach (PlayerFarming player4 in PlayerFarming.players) { if (!player4.isLamb) { num3++; } } if (num3 <= 0) { UICoopAssignController.SetInputForSoloPlay(); UserHelper.DisengagePlayer(1); CoopManager.CoopActive = false; CoopManager.EnableCoopBlockers(false, true); DifficultyManager.LoadCurrentDifficulty(); } else { Player player = RewiredInputManager.GetPlayer(playerID); if (player != null) { foreach (Joystick item in new List<Joystick>(player.controllers.Joysticks)) { player.controllers.RemoveController((Controller)(object)item); } } EnforceKeyboardOwnership(); CoopManager.RefreshCoopPlayerRewired(); } GameManager.GetInstance().WaitForSeconds(2f, (Action)delegate { _removingPlayer = false; Plugin.Log.LogInfo((object)"Dropout remove guard released"); }); break; } bool flag = false; for (int k = 1; k < 4; k++) { if (RewiredInputManager.GetPlayer(k).controllers.ContainsController((Controller)(object)val2)) { flag = true; break; } } if (!flag) { for (int l = 1; l < 4; l++) { RewiredInputManager.GetPlayer(l).controllers.RemoveController((Controller)(object)val2); } if (!RewiredInputManager.GetPlayer(0).controllers.ContainsController((Controller)(object)val2)) { RewiredInputManager.GetPlayer(0).controllers.AddController((Controller)(object)val2, true); } Plugin.Log.LogInfo((object)$"Joystick [{i}] assigned to Lamb (slot 0)"); } } num2 = PlayerFarming.playersCount; if (num2 < 1) { num2 = 1; } for (int m = 0; m < array.Length && m < list.Count; m++) { if (num2 >= 4) { break; } CoopAssignInputOption val4 = array[m]; if ((Object)(object)val4 == (Object)null || !((Component)val4).gameObject.activeSelf || val4.selection != 1) { continue; } val4.inputLock = false; Joystick val5 = list[m]; if (val5 == null) { continue; } bool flag2 = false; for (int n = 1; n < 4; n++) { if (!RewiredInputManager.GetPlayer(n).controllers.ContainsController((Controller)(object)val5)) { continue; } PlayerFarming val6 = null; foreach (PlayerFarming player5 in PlayerFarming.players) { if (!player5.isLamb && player5.playerID == n && ((Component)player5).gameObject.activeSelf) { val6 = player5; break; } } if ((Object)(object)val6 != (Object)null) { flag2 = true; Plugin.Log.LogInfo((object)$"Joystick [{m}] already active goat slot {n}, skipping"); break; } } if (!flag2) { for (int num4 = 0; num4 < 4; num4++) { RewiredInputManager.GetPlayer(num4).controllers.RemoveController((Controller)(object)val5); } RewiredInputManager.GetPlayer(num2).controllers.AddController((Controller)(object)val5, true); Plugin.Log.LogInfo((object)$"Joystick [{m}] -> new goat slot {num2}"); MarkDesiredExtraSlot(num2, desired: true); if (num2 >= 2) { _pendingSpawnSlots.Add(num2); } CoopManager.Instance.SpawnCoopPlayer(num2, true, -1f); CoopManager.CoopActive = true; num2++; } } Plugin.Log.LogInfo((object)"Spawn complete"); ((UIMenuBase)__instance).Hide(false); return false; } public static void CoopAssign_OnShowStarted(UICoopAssignController __instance) { if (!CoopManager.CoopActive) { return; } FieldInfo field = typeof(UICoopAssignController).GetField("displayedConnectedGamepads", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field2 = typeof(UICoopAssignController).GetField("keyboardInputOption", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field2 == null) { return; } CoopAssignInputOption[] array = field.GetValue(__instance) as CoopAssignInputOption[]; object? value = field2.GetValue(__instance); CoopAssignInputOption val = (CoopAssignInputOption)((value is CoopAssignInputOption) ? value : null); if (array == null) { return; } FieldInfo field3 = typeof(UICoopAssignController).GetField("keyboardInputOptionPreviousSelection", BindingFlags.Static | BindingFlags.NonPublic); if ((Object)(object)val != (Object)null) { int num = ((!RewiredInputManager.GetPlayer(0).controllers.hasKeyboard) ? 1 : (-1)); field3?.SetValue(null, num); val.SetSelection(num, true); } List<Joystick> list = new List<Joystick>(ReInput.controllers.Joysticks); for (int i = 0; i < array.Length && i < list.Count; i++) { Joystick val2 = list[i]; int num2 = -1; for (int j = 0; j < 4; j++) { Player player = RewiredInputManager.GetPlayer(j); if (player != null && player.controllers.ContainsController((Controller)(object)val2)) { num2 = j; break; } } int num3 = ((num2 != 0) ? 1 : (-1)); UICoopAssignController.displayedConnectedGamepadsPreviousSelection[i] = num3; array[i].SetSelection(num3, true); Plugin.Log.LogInfo((object)$"CoopAssign_OnShowStarted: joystick[{i}] ownerSlot={num2} → selection={num3}"); } } public static bool RemovePlayerFromMenu() { if (_removingPlayer) { Plugin.Log.LogInfo((object)"Remove guard active, skipping"); return false; } if (PlayerFarming.playersCount > 1 && CoopManager.CoopActive) { bool manualRemoveRequested = _manualRemoveRequested; _manualRemoveRequested = false; if (!manualRemoveRequested && Time.unscaledTime < _autoRemoveSuppressedUntil) { float num = Math.Max(0f, _autoRemoveSuppressedUntil - Time.unscaledTime); Plugin.Log.LogInfo((object)$"Auto remove suppressed ({num:0.00}s left)"); return false; } PlayerFarming val = null; for (int num2 = PlayerFarming.players.Count - 1; num2 >= 0; num2--) { PlayerFarming val2 = PlayerFarming.players[num2]; if (!((Object)(object)val2 == (Object)null) && !val2.isLamb && !((Object)(object)((Component)val2).gameObject == (Object)null) && ((Component)val2).gameObject.activeSelf) { if (manualRemoveRequested) { val = val2; break; } int playerID = val2.playerID; if (playerID > 0 && playerID < 4 && !SlotHasJoystick(playerID)) { val = val2; break; } } } if ((Object)(object)val == (Object)null) { if (!manualRemoveRequested) { Plugin.Log.LogInfo((object)"Auto remove ignored: no goat slot lost joystick ownership"); } else { Plugin.Log.LogWarning((object)"No Goat found to remove"); } return false; } _removingPlayer = true; CoopManager.AddtionalUser = null; int playerID2 = val.playerID; MarkDesiredExtraSlot(playerID2, desired: false); _pendingSpawnSlots.Remove(playerID2); _pendingSpawnUseAnimation.Remove(playerID2); if (_cachedSprites.ContainsKey(playerID2)) { _cachedSprites.Remove(playerID2); } if (_cachedTextImages.ContainsKey(playerID2)) { _cachedTextImages.Remove(playerID2); } if (_cachedHaloSR.ContainsKey(playerID2)) { _cachedHaloSR.Remove(playerID2); } if (_cachedHaloSprites.ContainsKey(playerID2)) { _cachedHaloSprites.Remove(playerID2); } _haloLogged.Remove(playerID2); Plugin.Log.LogInfo((object)string.Format("Removing playerID {0} ({1})", playerID2, manualRemoveRequested ? "manual" : "auto-disconnect")); UnhookVanillaCoopDelegates(); CoopManager.Instance.RemoveCoopPlayer(val, true, false, true); if (_extraHeartBars.TryGetValue(playerID2, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } _extraHeartBars.Remove(playerID2); int num3 = 0; foreach (PlayerFarming player2 in PlayerFarming.players) { if (!player2.isLamb) { num3++; } } if (num3 <= 0) { _desiredExtraSlots.Clear(); UICoopAssignController.SetInputForSoloPlay(); UserHelper.DisengagePlayer(1); } else { Player player = RewiredInputManager.GetPlayer(playerID2); if (player != null) { foreach (Joystick item in new List<Joystick>(player.controllers.Joysticks)) { player.controllers.RemoveController((Controller)(object)item); } } EnforceKeyboardOwnership(); CoopManager.RefreshCoopPlayerRewired(); } if (PlayerFarming.playersCount <= 1) { CoopManager.CoopActive = false; CoopManager.EnableCoopBlockers(false, true); DifficultyManager.LoadCurrentDifficulty(); } TryStopAllRumble($"RemovePlayerFromMenu(slot={playerID2})"); if (!manualRemoveRequested) { _autoRemoveSuppressedUntil = Time.unscaledTime + 8f; Plugin.Log.LogInfo((object)$"Auto remove cooldown armed for {8f:0.0}s"); } GameManager.GetInstance().WaitForSeconds(2f, (Action)delegate { _removingPlayer = false; Plugin.Log.LogInfo((object)"Remove guard released"); }); } return false; } public static void PlayerFarming_OnEnable_Prefix(PlayerFarming __instance) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || __instance.isLamb || __instance.playerID <= 1 || !GameManager.IsDungeon(PlayerFarming.Location) || (Object)(object)__instance.Spine == (Object)null) { return; } int num = __instance.playerID; if (num < 2 || num >= 4) { for (int i = 2; i < 4; i++) { if (CoopManager.AllPlayerGameObjects != null && i < CoopManager.AllPlayerGameObjects.Length && (Object)(object)CoopManager.AllPlayerGameObjects[i] == (Object)(object)((Component)__instance).gameObject) { num = i; break; } } } if (num < 2 || num >= 4) { return; } bool value = false; _pendingSpawnUseAnimation.TryGetValue(num, out value); if (value) { Plugin.Log.LogInfo((object)$"[MP] PlayerFarming_OnEnable_Prefix: keeping mesh visible for animated spawn slot {num}"); return; } MeshRenderer component = ((Component)__instance.Spine).GetComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = false; } Plugin.Log.LogInfo((object)$"[MP] PlayerFarming_OnEnable_Prefix: hid mesh for slot {num}"); } public static void SpawnCoopPlayer_Postfix(int slot) { if (slot < 2) { _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); } else { MarkDesiredExtraSlot(slot, desired: true); ((MonoBehaviour)CoopManager.Instance).StartCoroutine(ApplyVisualDelayed(slot)); } } [IteratorStateMachine(typeof(<RespawnExtraSlots>d__58))] private static IEnumerator RespawnExtraSlots(int count) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RespawnExtraSlots>d__58(0) { count = count }; } public static void NewRun_Prefix() { _spawnedExtraWeaponPodiums = false; _spawnedExtraSpellPodiums = false; _podiumClones.Clear(); _podiumOwnerSlotByInstanceId.Clear(); int num = 0; foreach (PlayerFarming player in PlayerFarming.players) { if (!player.isLamb && player.playerID >= 2) { num++; } } if (num > 0) { _savedExtraSlotCount = num; Plugin.Log.LogInfo((object)$"NewRun_Prefix: saved {num} extra slot(s) for dungeon respawn"); } _cachedHaloSR.Clear(); _cachedHaloSprites.Clear(); _cachedTextImages.Clear(); _cachedSprites.Clear(); _haloLogged.Clear(); foreach (KeyValuePair<int, GameObject> extraHeartBar in _extraHeartBars) { if ((Object)(object)extraHeartBar.Value != (Object)null) { Object.Destroy((Object)(object)extraHeartBar.Value); } } _extraHeartBars.Clear(); _positioningDeferred = false; _podiumExtraCount = 0; _autoRespawnRoutineRunning = false; _nextAutoRespawnAttemptAt = 0f; _nextAutoRespawnHeartbeatAt = 0f; _pendingSpawnSlots.Clear(); _pendingSpawnUseAnimation.Clear(); _pendingSpawnFirstSeenAt.Clear(); _nextPendingBlockLogAt = 0f; _delayedRespawnAfterReviveQueued = false; _lastObservedWakeHealth = 2f; _lastObservedSacrificialWakeHealth = 2f; _sacrificialReviveWindowUntil = 0f; _nextExtraFalseDownedWatchdogAt.Clear(); _postReviveRaceGuardUntil.Clear(); _nextPostReviveForceApplyAt.Clear(); _nextPostReviveRaceGuardHeartbeatAt = 0f; _lastLoggedDungeonRoomPath = null; _p2EntranceGuardRunning = false; _dungeonRoomStabilizeRunning = false; _lastStabilizedDungeonRoomId = int.MinValue; _nextDungeonRoomStabilizeCheckAt = 0f; _ro
COTLMP/DungeonRoomSizeScaler.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MMBiomeGeneration; using MMRoomGeneration; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary4")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary4")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d7f3cd4f-957c-4fa6-a4ae-1b1904889467")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerRoomSizeScaling; [BepInPlugin("com.cotlmp.roomsizescaling", "COTL Multiplayer Room Size Scaling", "0.1.1")] public sealed class RoomSizeScalingPlugin : BaseUnityPlugin { public const string PluginGuid = "com.cotlmp.roomsizescaling"; public const string PluginName = "COTL Multiplayer Room Size Scaling"; public const string PluginVersion = "0.1.1"; internal ConfigEntry<bool> Enabled; internal ConfigEntry<float> BiasStrength; internal ConfigEntry<float> KeepVanillaChance; internal ConfigEntry<float> BiasStrength3P; internal ConfigEntry<float> KeepVanillaChance3P; internal ConfigEntry<float> BiasStrength4P; internal ConfigEntry<float> KeepVanillaChance4P; internal ConfigEntry<float> MinAreaGainPct; internal ConfigEntry<bool> DebugLogging; private Harmony _harmony; internal static RoomSizeScalingPlugin Instance { get; private set; } internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger; private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable generated room size scaling."); BiasStrength = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BiasStrength", 0.7f, new ConfigDescription("Chance to attempt an upsize roll each generated room (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); KeepVanillaChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "KeepVanillaChance", 0.35f, new ConfigDescription("Chance to keep vanilla selection for room-flow variety (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); BiasStrength3P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "BiasStrength3P", 0.85f, new ConfigDescription("Upsize roll chance when 3 players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); KeepVanillaChance3P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "KeepVanillaChance3P", 0.25f, new ConfigDescription("Chance to keep vanilla room size when 3 players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); BiasStrength4P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "BiasStrength4P", 0.9f, new ConfigDescription("Upsize roll chance when 4+ players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); KeepVanillaChance4P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "KeepVanillaChance4P", 0.12f, new ConfigDescription("Chance to keep vanilla room size when 4+ players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); MinAreaGainPct = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinAreaGainPct", 0.15f, new ConfigDescription("Minimum area gain required to swap to a larger generated room piece.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Verbose room-size selection logs."); _harmony = new Harmony("com.cotlmp.roomsizescaling"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"COTL Multiplayer Room Size Scaling v0.1.1 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } [HarmonyPatch] internal static class GenerateRoomGetRandomEncounterIslandPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(GenerateRoom), "GetRandomEncounterIsland", (Type[])null, (Type[])null); } private static void Postfix(GenerateRoom __instance, ref IslandPiece __result) { RoomSizeScalingPlugin instance = RoomSizeScalingPlugin.Instance; if ((Object)(object)instance == (Object)null || !instance.Enabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__result == (Object)null) { return; } BiomeRoom val = (((Object)(object)BiomeGenerator.Instance != (Object)null) ? BiomeGenerator.Instance.CurrentRoom : null); if (val != null && !string.IsNullOrEmpty(val.GameObjectPath)) { return; } List<IslandPiece> startPieces = __instance.StartPieces; if (startPieces == null || startPieces.Count == 0) { return; } int num = Mathf.Max(1, PlayerFarming.playersCount); float value = instance.KeepVanillaChance.Value; float value2 = instance.BiasStrength.Value; if (num >= 4) { value = instance.KeepVanillaChance4P.Value; value2 = instance.BiasStrength4P.Value; } else if (num >= 3) { value = instance.KeepVanillaChance3P.Value; value2 = instance.BiasStrength3P.Value; } if (Random.value < value || Random.value > value2) { return; } List<IslandPiece> list = CollectAvailableCandidates(startPieces); if (list.Count == 0) { return; } float area = GetArea(__result); IslandPiece val2 = __result; float num2 = area; for (int i = 0; i < list.Count; i++) { IslandPiece val3 = list[i]; float area2 = GetArea(val3); if (area2 > num2) { num2 = area2; val2 = val3; } } if ((Object)(object)val2 == (Object)null || val2 == __result) { return; } float num3 = area * (1f + instance.MinAreaGainPct.Value); if (!(num2 < num3)) { if (instance.DebugLogging.Value) { RoomSizeScalingPlugin.Log.LogInfo((object)$"[RoomSizeScale] Up-sized generated room piece area {area:0.00} -> {num2:0.00} (players={num}, keep={value:0.00}, bias={value2:0.00})"); } __result = val2; } } private static List<IslandPiece> CollectAvailableCandidates(List<IslandPiece> source) { List<IslandPiece> list = new List<IslandPiece>(); for (int i = 0; i < source.Count; i++) { IslandPiece val = source[i]; if ((Object)(object)val == (Object)null || val.Encounters == null || val.Encounters.ObjectList == null) { continue; } int num = 0; int num2 = 0; for (int j = 0; j < val.Encounters.ObjectList.Count; j++) { GameObjectAndProbability val2 = val.Encounters.ObjectList[j]; if (val2 != null && IsAvailableOnCurrentLayer(val2)) { num++; if (!string.IsNullOrEmpty(val2.GameObjectPath) && BiomeGenerator.EncounterAlreadyUsed(val2.GameObjectPath)) { num2++; } } } if (num > 0 && num2 < num) { list.Add(val); } } if (list.Count == 0) { for (int k = 0; k < source.Count; k++) { if ((Object)(object)source[k] != (Object)null) { list.Add(source[k]); } } } return list; } private static float GetArea(IslandPiece piece) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null) { return 0f; } PolygonCollider2D collider = piece.Collider; if ((Object)(object)collider == (Object)null) { return 0f; } Vector2[] points = collider.points; Bounds bounds; if (points == null || points.Length < 3) { bounds = ((Collider2D)collider).bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = ((Collider2D)collider).bounds; return x * ((Bounds)(ref bounds)).size.y; } double num = 0.0; for (int i = 0; i < points.Length; i++) { Vector2 val = points[i]; Vector2 val2 = points[(i + 1) % points.Length]; num += (double)(val.x * val2.y - val2.x * val.y); } float num2 = Mathf.Abs((float)(num * 0.5)); if (!(num2 > 0f)) { bounds = ((Collider2D)collider).bounds; float x2 = ((Bounds)(ref bounds)).size.x; bounds = ((Collider2D)collider).bounds; return x2 * ((Bounds)(ref bounds)).size.y; } return num2; } private static bool IsAvailableOnCurrentLayer(GameObjectAndProbability encounter) { if (GameManager.DungeonUseAllLayers) { return true; } return GameManager.CurrentDungeonLayer switch { 1 => encounter.LayerOne, 2 => encounter.LayerTwo, 3 => encounter.LayerThree, 4 => encounter.LayerFour, _ => false, }; } }
COTLMP/EnemySpawnScaler.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary3")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary3")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b9a6c547-5fae-4c80-847d-8da4b933a5dd")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerEnemySpawnScaling; [BepInPlugin("com.cotlmp.enemyspawnscaling", "COTL Multiplayer Enemy Spawn Scaling", "0.2.9")] public sealed class EnemySpawnScalingPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static EnemySpawnScalingPlugin Instance; internal static ConfigEntry<bool> DuplicateMiniBosses; private Harmony _harmony; private bool _heartbeatLogged; private void Awake() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; DuplicateMiniBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("Scaling", "DuplicateMiniBosses", false, "If true, mini-boss enemies can be duplicated instead of health-scaled. Can cause instability/softlocks for some encounters."); _harmony = new Harmony("com.cotlmp.enemyspawnscaling"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Create", new Type[3] { typeof(Vector3), typeof(Transform), typeof(GameObject) }, (Type[])null), null, "EnemySpawner_Create_Postfix", "EnemySpawner.Create(Vector3,Transform,GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "CreateWithAndInitInstantiatedEnemy", new Type[3] { typeof(Vector3), typeof(Transform), typeof(GameObject) }, (Type[])null), null, "EnemySpawner_CreateWithAndInit_Postfix", "EnemySpawner.CreateWithAndInitInstantiatedEnemy(Vector3,Transform,GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "InitAndInstantiate", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_InitAndInstantiate_Postfix", "EnemySpawner.InitAndInstantiate(GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Init", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_Init_Postfix", "EnemySpawner.Init(GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "RoundStarted", new Type[2] { typeof(int), typeof(int) }, (Type[])null), "EnemyRoundsBase_RoundStarted_Prefix", null, "EnemyRoundsBase.RoundStarted(int,int)"); PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "AddEnemyToRound", new Type[1] { typeof(Health) }, (Type[])null), null, "EnemyRoundsBase_AddEnemyToRound_Postfix", "EnemyRoundsBase.AddEnemyToRound(Health)"); PatchIfFound(AccessTools.Method(typeof(EnemyEncounterChanceEvents), "AssignShieldsAndGroups", new Type[0], (Type[])null), null, "EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix", "EnemyEncounterChanceEvents.AssignShieldsAndGroups()"); PatchIfFound(AccessTools.Method(typeof(RoomLockController), "RoomCompleted", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null), "RoomLockController_RoomCompleted_Prefix", "RoomLockController_RoomCompleted_Postfix", "RoomLockController.RoomCompleted(bool,bool)"); Log.LogInfo((object)"Enemy spawn scaling addon loaded."); Log.LogInfo((object)"[SpawnScale] Ready v0.2.9"); Log.LogInfo((object)("[SpawnScale] Config DuplicateMiniBosses=" + DuplicateMiniBosses.Value)); Log.LogWarning((object)"[SPAWNSCALE_BOOT_017]"); Debug.Log((object)"[SPAWNSCALE_BOOT_017]"); } private void Update() { if (!_heartbeatLogged) { _heartbeatLogged = true; Log.LogWarning((object)"[SPAWNSCALE_HEARTBEAT_017]"); Debug.Log((object)"[SPAWNSCALE_HEARTBEAT_017]"); } } private void PatchIfFound(MethodInfo original, string prefix = null, string postfix = null, string label = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (original == null) { Log.LogWarning((object)("[SpawnScale] Skipped patch (missing): " + (label ?? "unknown"))); return; } HarmonyMethod val = null; HarmonyMethod val2 = null; if (!string.IsNullOrEmpty(prefix)) { val = new HarmonyMethod(typeof(EnemySpawnPatches), prefix, (Type[])null); } if (!string.IsNullOrEmpty(postfix)) { val2 = new HarmonyMethod(typeof(EnemySpawnPatches), postfix, (Type[])null); } _harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("[SpawnScale] Patched: " + (label ?? original.Name))); } } internal static class EnemySpawnPatches { private sealed class SpawnScaleCloneMarker : MonoBehaviour { public int RoomToken; } [CompilerGenerated] private sealed class <DeferredRoomCompletionRoutine>d__43 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private float <deadline>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DeferredRoomCompletionRoutine>d__43(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <deadline>5__2 = Time.unscaledTime + 25f; break; case 1: <>1__state = -1; break; } if (Time.unscaledTime < <deadline>5__2 && CountAliveTrackedScaledClones() > 0) { <>2__current = (object)new WaitForSecondsRealtime(0.2f); <>1__state = 1; return true; } int num = CountAliveTrackedScaledClones(); if (num > 0) { EnemySpawnScalingPlugin.Log.LogWarning((object)$"[SpawnScale] deferred completion timeout pendingScaled={num}; allowing completion to prevent softlock."); } _deferredRoomCompletionQueued = false; _allowRoomCompletePassThrough = true; try { if (!GameManager.IsDungeon(PlayerFarming.Location)) { EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] deferred completion canceled (left dungeon, loc={PlayerFarming.Location})"); return false; } RoomLockController.RoomCompleted(true, _deferredDoorsDown); } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] deferred completion failed: " + ex.Message)); } finally { _allowRoomCompletePassThrough = false; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _spawningScaledCopy = false; private static bool _allowRoomCompletePassThrough = false; private static bool _deferredRoomCompletionQueued = false; private static bool _deferredDoorsDown = true; private static float _extraSpawnCarry = 0f; private static float _nextSkipLogAt = 0f; private static float _nextHookLogAt = 0f; private static float _nextRejectLogAt = 0f; private static int _roomToken = 0; private static readonly HashSet<int> _scaledEncounterInstanceIds = new HashSet<int>(); private static readonly HashSet<int> _scaledSpawnInstanceIds = new HashSet<int>(); private static readonly HashSet<int> _scaledBossHealthIds = new HashSet<int>(); private static readonly HashSet<SpawnScaleCloneMarker> _trackedScaledClones = new HashSet<SpawnScaleCloneMarker>(); private static readonly List<SpawnScaleCloneMarker> _cloneSweep = new List<SpawnScaleCloneMarker>(); private static readonly Type DungeonLeaderMechanicsType = AccessTools.TypeByName("DungeonLeaderMechanics"); private static readonly Type DemonType = AccessTools.TypeByName("Demon"); private static readonly Type FriendlyEnemyType = AccessTools.TypeByName("FriendlyEnemy"); public static void EnemyRoundsBase_RoundStarted_Prefix(int round, int totalRounds) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) bool flag = round <= 1 || _roomToken == 0; if (flag) { _roomToken++; _trackedScaledClones.Clear(); _scaledEncounterInstanceIds.Clear(); _scaledSpawnInstanceIds.Clear(); _scaledBossHealthIds.Clear(); } _extraSpawnCarry = 0f; _deferredRoomCompletionQueued = false; _allowRoomCompletePassThrough = false; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] round start {round}/{totalRounds} token={_roomToken} newRoomToken={flag} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } public static bool RoomLockController_RoomCompleted_Prefix(bool wasCombatRoom, bool doorsDown) { //IL_0018: 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) if (!wasCombatRoom) { return true; } if (_allowRoomCompletePassThrough) { return true; } if (PlayerFarming.playersCount <= 2) { return true; } if (!GameManager.IsDungeon(PlayerFarming.Location)) { return true; } int num = CountAliveTrackedScaledClones(); if (num <= 0) { return true; } if (!_deferredRoomCompletionQueued && (Object)(object)EnemySpawnScalingPlugin.Instance != (Object)null) { _deferredRoomCompletionQueued = true; _deferredDoorsDown = doorsDown; ((MonoBehaviour)EnemySpawnScalingPlugin.Instance).StartCoroutine(DeferredRoomCompletionRoutine()); } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] delaying room completion pendingScaled={num} (players={PlayerFarming.playersCount}, loc={PlayerFarming.Location})"); return false; } public static void RoomLockController_RoomCompleted_Postfix(bool wasCombatRoom, bool doorsDown) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (wasCombatRoom) { _extraSpawnCarry = 0f; _deferredRoomCompletionQueued = false; _allowRoomCompletePassThrough = false; _trackedScaledClones.Clear(); _scaledEncounterInstanceIds.Clear(); _scaledBossHealthIds.Clear(); EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] combat room completed reset carry (doorsDown={doorsDown}, players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } public static void EnemySpawner_Create_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { LogHookSeen("Create"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] Create"); } public static void EnemySpawner_CreateWithAndInit_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { LogHookSeen("CreateWithInit"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] CreateWithInit"); } public static void EnemySpawner_InitAndInstantiate_Postfix(EnemySpawner __instance, GameObject __0) { LogHookSeen("InitAndInstantiate"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] InitAndInstantiate"); } public static void EnemySpawner_Init_Postfix(EnemySpawner __instance, GameObject __0) { LogHookSeen("Init"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] Init"); } public static void EnemyRoundsBase_AddEnemyToRound_Postfix(Health e) { if (!((Object)(object)e == (Object)null)) { LogHookSeen("AddEnemyToRound"); } } public static void EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix(EnemyEncounterChanceEvents __instance) { //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null) { return; } if (!ShouldScaleDungeonSpawns(out var blockedReason)) { LogSkip("encounter_gate=" + blockedReason); return; } int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (!_scaledEncounterInstanceIds.Add(instanceID)) { return; } UnitObject[] componentsInChildren; try { componentsInChildren = ((Component)__instance).GetComponentsInChildren<UnitObject>(true); } catch { return; } if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } List<UnitObject> list = new List<UnitObject>(); foreach (UnitObject val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !((Object)(object)val.health == (Object)null) && (int)val.health.team == 2 && !((Object)(object)((Component)val).GetComponent<SpawnScaleCloneMarker>() != (Object)null) && !TryScaleBossLikeHealth(((Component)val).gameObject, "RoomEncounter") && !((Object)(object)((Component)val).GetComponent<MinionProtector>() != (Object)null) && !((Object)(object)((Component)val).GetComponent<LineRenderer>() != (Object)null) && !HasComponentInChildren(((Component)val).gameObject, DungeonLeaderMechanicsType) && !HasComponentInChildren(((Component)val).gameObject, DemonType) && !HasComponentInChildren(((Component)val).gameObject, FriendlyEnemyType)) { list.Add(val); } } if (list.Count == 0) { return; } int roomExtraCount = GetRoomExtraCount(list.Count); if (roomExtraCount <= 0) { return; } int num = 0; for (int j = 0; j < roomExtraCount; j++) { UnitObject val2 = list[j % list.Count]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null)) { try { GameObject val3 = Object.Instantiate<GameObject>(((Component)val2).gameObject, ((Component)val2).transform.parent); ((Object)val3).name = ((Object)((Component)val2).gameObject).name + "_SpawnScale"; SpawnScaleCloneMarker spawnScaleCloneMarker = val3.AddComponent<SpawnScaleCloneMarker>(); spawnScaleCloneMarker.RoomToken = _roomToken; _trackedScaledClones.Add(spawnScaleCloneMarker); SanitizeClone(val3); _scaledSpawnInstanceIds.Add(((Object)val3).GetInstanceID()); val3.transform.position = GetNearbySpawnPoint(((Component)val2).transform.position, j); val3.SetActive(false); EnemySpawner.CreateWithAndInitInstantiatedEnemy(val3.transform.position, val3.transform.parent, val3); num++; } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] room clone failed: " + ex.Message)); } } } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] room-scale eligible={list.Count} spawnedExtra={num} players={PlayerFarming.playersCount} loc={PlayerFarming.Location}"); } private static void TrySpawnScaledCopies(Vector3 position, Transform parent, GameObject spawnPrefab, string source) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (_spawningScaledCopy || (Object)(object)spawnPrefab == (Object)null) { return; } int instanceID = ((Object)spawnPrefab).GetInstanceID(); if (_scaledSpawnInstanceIds.Remove(instanceID)) { LogReject("scaled_copy_source"); } else { if (TryScaleBossLikeHealth(spawnPrefab, source)) { return; } if (!ShouldScaleDungeonSpawns(out var blockedReason)) { LogSkip("gate=" + blockedReason); } else { if (!IsEligibleEnemyPrefab(spawnPrefab)) { return; } int extraSpawnsForThisEnemy = GetExtraSpawnsForThisEnemy(); if (extraSpawnsForThisEnemy <= 0) { return; } try { _spawningScaledCopy = true; for (int i = 0; i < extraSpawnsForThisEnemy; i++) { Vector3 nearbySpawnPoint = GetNearbySpawnPoint(position, i); GameObject val = Object.Instantiate<GameObject>(spawnPrefab, parent); SpawnScaleCloneMarker spawnScaleCloneMarker = val.AddComponent<SpawnScaleCloneMarker>(); spawnScaleCloneMarker.RoomToken = _roomToken; _trackedScaledClones.Add(spawnScaleCloneMarker); _scaledSpawnInstanceIds.Add(((Object)val).GetInstanceID()); val.SetActive(false); EnemySpawner.CreateWithAndInitInstantiatedEnemy(nearbySpawnPoint, parent, val); } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] source={source} players={PlayerFarming.playersCount} spawnedExtra={extraSpawnsForThisEnemy} carry={_extraSpawnCarry:0.00}"); } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] Failed to spawn scaled enemy copy: " + ex.Message)); } finally { _spawningScaledCopy = false; } } } } private static bool ShouldScaleDungeonSpawns(out string blockedReason) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) blockedReason = string.Empty; if (PlayerFarming.playersCount <= 2) { blockedReason = "players_le_2"; return false; } if (!GameManager.IsDungeon(PlayerFarming.Location)) { blockedReason = "not_dungeon"; return false; } if ((Object)(object)EnemyRoundsBase.Instance != (Object)null && EnemyRoundsBase.Instance.Completed) { blockedReason = "rounds_completed"; return false; } return true; } private static bool IsEligibleEnemyPrefab(GameObject spawnPrefab) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 Health component = spawnPrefab.GetComponent<Health>(); if ((Object)(object)component == (Object)null) { LogReject("no_health"); return false; } if ((int)component.team != 2) { LogReject("team_" + ((object)(Team)(ref component.team)).ToString()); return false; } if (HasComponentInChildren(spawnPrefab, DungeonLeaderMechanicsType)) { LogReject("dungeon_leader"); return false; } if (HasComponentInChildren(spawnPrefab, DemonType)) { LogReject("demon"); return false; } if (HasComponentInChildren(spawnPrefab, FriendlyEnemyType)) { LogReject("friendly_enemy"); return false; } return true; } private static bool TryScaleBossLikeHealth(GameObject go, string source) { if ((Object)(object)go == (Object)null) { return false; } if (!IsHealthScaleOnlyEnemy(go)) { return false; } Health component = go.GetComponent<Health>(); if ((Object)(object)component == (Object)null) { return false; } int instanceID = ((Object)go).GetInstanceID(); if (!_scaledBossHealthIds.Add(instanceID)) { return true; } float num = 1f; if (PlayerFarming.playersCount >= 4) { num = 2f; } else if (PlayerFarming.playersCount == 3) { num = 1.5f; } if (num <= 1f) { return true; } component.totalHP *= num; component.HP *= num; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] boss-health source={source} enemy={GetEnemyName(go)} mult={num:0.00} players={PlayerFarming.playersCount}"); return true; } private static bool IsHealthScaleOnlyEnemy(GameObject go) { //IL_002d: 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) if ((Object)(object)go == (Object)null) { return false; } if (HasComponentInChildren(go, DungeonLeaderMechanicsType)) { return true; } UnitObject component = go.GetComponent<UnitObject>(); if ((Object)(object)component == (Object)null) { return false; } Enemy enemyType = component.EnemyType; string text = ((object)(Enemy)(ref enemyType)).ToString(); if (string.IsNullOrEmpty(text)) { return false; } if ((text.IndexOf("MiniBoss", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Miniboss", StringComparison.OrdinalIgnoreCase) >= 0) && EnemySpawnScalingPlugin.DuplicateMiniBosses != null && EnemySpawnScalingPlugin.DuplicateMiniBosses.Value) { return false; } if (text.IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } private static string GetEnemyName(GameObject go) { //IL_0023: 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) UnitObject val = (((Object)(object)go != (Object)null) ? go.GetComponent<UnitObject>() : null); if (!((Object)(object)val != (Object)null)) { return "Unknown"; } Enemy enemyType = val.EnemyType; return ((object)(Enemy)(ref enemyType)).ToString(); } private static void LogSkip(string reason) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextSkipLogAt)) { _nextSkipLogAt = unscaledTime + 2f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] skip {reason} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogHookSeen(string hook) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextHookLogAt)) { _nextHookLogAt = unscaledTime + 1.5f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] hook={hook} seen (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogReject(string reason) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextRejectLogAt)) { _nextRejectLogAt = unscaledTime + 2f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] reject {reason} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static string GetCoopStateForLog() { if ((Object)(object)CoopManager.Instance == (Object)null) { return "null"; } if (!CoopManager.CoopActive) { return "inactive"; } return "active"; } private static bool HasComponentInChildren(GameObject go, Type componentType) { if ((Object)(object)go == (Object)null || componentType == null) { return false; } try { return (Object)(object)go.GetComponentInChildren(componentType, true) != (Object)null; } catch { return false; } } private static int GetExtraSpawnsForThisEnemy() { float num; if (PlayerFarming.playersCount >= 4) { num = 1f; } else { if (PlayerFarming.playersCount != 3) { return 0; } num = 0.5f; } _extraSpawnCarry += num; int num2 = Mathf.FloorToInt(_extraSpawnCarry + 0.0001f); if (num2 > 0) { _extraSpawnCarry -= num2; } return Mathf.Clamp(num2, 0, 3); } private static int GetRoomExtraCount(int baseCount) { if (baseCount <= 0) { return 0; } if (PlayerFarming.playersCount >= 4) { return Mathf.Clamp(Mathf.Max(4, Mathf.CeilToInt((float)baseCount * 2.25f)) - baseCount, 0, 12); } if (PlayerFarming.playersCount == 3) { return Mathf.Clamp(Mathf.Max(3, Mathf.CeilToInt((float)baseCount * 1.75f)) - baseCount, 0, 8); } return 0; } private static Vector3 GetNearbySpawnPoint(Vector3 origin, int index) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = (float)(Time.frameCount * 37 + index * 113) % 360f * ((float)Math.PI / 180f); return origin + new Vector3(Mathf.Cos(num) * 0.75f, Mathf.Sin(num) * 0.75f, 0f); } private static void SanitizeClone(GameObject clone) { if (!((Object)(object)clone == (Object)null)) { MinionProtector component = clone.GetComponent<MinionProtector>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } LineRenderer component2 = clone.GetComponent<LineRenderer>(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } UnitObject component3 = clone.GetComponent<UnitObject>(); if ((Object)(object)component3 != (Object)null) { component3.orderIndicator = null; component3.RemoveModifier(); component3.CanHaveModifier = false; } } } private static int CountAliveTrackedScaledClones() { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Invalid comparison between Unknown and I4 try { SpawnScaleCloneMarker[] array = Object.FindObjectsOfType<SpawnScaleCloneMarker>(true); foreach (SpawnScaleCloneMarker spawnScaleCloneMarker in array) { if (!((Object)(object)spawnScaleCloneMarker == (Object)null) && !((Object)(object)((Component)spawnScaleCloneMarker).gameObject == (Object)null) && spawnScaleCloneMarker.RoomToken == _roomToken) { _trackedScaledClones.Add(spawnScaleCloneMarker); } } } catch { } _cloneSweep.Clear(); int num = 0; foreach (SpawnScaleCloneMarker trackedScaledClone in _trackedScaledClones) { if ((Object)(object)trackedScaledClone == (Object)null || (Object)(object)((Component)trackedScaledClone).gameObject == (Object)null) { _cloneSweep.Add(trackedScaledClone); continue; } if (trackedScaledClone.RoomToken != _roomToken) { _cloneSweep.Add(trackedScaledClone); continue; } Health component = ((Component)trackedScaledClone).GetComponent<Health>(); if ((Object)(object)component == (Object)null) { _cloneSweep.Add(trackedScaledClone); } else if ((int)component.team != 2 || component.HP <= 0f) { _cloneSweep.Add(trackedScaledClone); } else { num++; } } for (int j = 0; j < _cloneSweep.Count; j++) { _trackedScaledClones.Remove(_cloneSweep[j]); } return num; } [IteratorStateMachine(typeof(<DeferredRoomCompletionRoutine>d__43))] private static IEnumerator DeferredRoomCompletionRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DeferredRoomCompletionRoutine>d__43(0); } }
COTLMP/TarotPatch.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using MMBiomeGeneration; using Rewired; using Unify.Input; using UnityEngine; using src.UINavigator; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary2")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary2")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("528f5a23-6efd-4f6b-85e8-1d3d74dfddd6")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerTarotAddon; [BepInPlugin("com.cotlmp.tarotaddon", "COTL Multiplayer Tarot Addon", "0.1.0")] public sealed class TarotAddonPlugin : BaseUnityPlugin { internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.cotlmp.tarotaddon"); TarotPatches.EnsureClauneckTarotRoomsOnly(null, "Awake"); PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "InteractionTarot_OnInteract_Prefix", null, "Interaction_Tarot.OnInteract(StateMachine)"); PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "DelayEffectsRoutine", new Type[3] { typeof(TarotCard), typeof(float), typeof(PlayerFarming) }, (Type[])null), "InteractionTarot_DelayEffectsRoutine_Prefix", "InteractionTarot_DelayEffectsRoutine_Postfix", "Interaction_Tarot.DelayEffectsRoutine"); PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "OnDisable", (Type[])null, (Type[])null), null, "InteractionTarot_OnDisable_Postfix", "Interaction_Tarot.OnDisable"); PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "DoRoutine", (Type[])null, (Type[])null), "InteractionTarot_DoRoutine_Prefix", null, "Interaction_Tarot.DoRoutine()"); PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "InteractionTarotCard_OnInteract_Prefix", null, "Interaction_TarotCard.OnInteract(StateMachine)"); PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "DoRoutine", (Type[])null, (Type[])null), "InteractionTarotCard_DoRoutine_Prefix", null, "Interaction_TarotCard.DoRoutine()"); PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "DelayEffectsRoutine", new Type[1] { typeof(PlayerFarming) }, (Type[])null), "InteractionTarotCard_DelayEffectsRoutine_Prefix", null, "Interaction_TarotCard.DelayEffectsRoutine(PlayerFarming)"); PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "OnDisable", (Type[])null, (Type[])null), null, "InteractionTarotCard_OnDisable_Postfix", "Interaction_TarotCard.OnDisable"); PatchIfFound(AccessTools.Method(typeof(Interaction_Chest), "SelectReward", (Type[])null, (Type[])null), null, "InteractionChest_SelectReward_Postfix", "Interaction_Chest.SelectReward()"); PatchIfFound(AccessTools.Method(typeof(Interaction_Chest), "OnDisableInteraction", Type.EmptyTypes, (Type[])null), null, "InteractionChest_OnDisableInteraction_Postfix", "Interaction_Chest.OnDisableInteraction()"); PatchIfFound(AccessTools.Method(typeof(InventoryItem), "Spawn", new Type[5] { typeof(ITEM_TYPE), typeof(int), typeof(Vector3), typeof(float), typeof(Action<PickUp>) }, (Type[])null), "InventoryItem_Spawn_Prefix", null, "InventoryItem.Spawn(ITEM_TYPE,int,Vector3,float,Action<PickUp>)"); PatchIfFound(AccessTools.Method(typeof(BiomeGenerator), "Generate", Type.EmptyTypes, (Type[])null), "BiomeGenerator_Generate_Prefix", null, "BiomeGenerator.Generate()"); PatchIfFound(AccessTools.Method(typeof(Shrines), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "Shrines_OnInteract_Prefix", null, "Shrines.OnInteract(StateMachine)"); PatchIfFound(AccessTools.Method(typeof(Shrines), "DrawCardRoutine", new Type[1] { typeof(PlayerFarming) }, (Type[])null), "Shrines_DrawCardRoutine_Prefix", null, "Shrines.DrawCardRoutine(PlayerFarming)"); PatchIfFound(AccessTools.Method(typeof(Shrines), "DelayEffectsRoutine", new Type[3] { typeof(TarotCard), typeof(float), typeof(PlayerFarming) }, (Type[])null), "Shrines_DelayEffectsRoutine_Prefix", "Shrines_DelayEffectsRoutine_Postfix", "Shrines.DelayEffectsRoutine(TarotCard,float,PlayerFarming)"); PatchIfFound(AccessTools.Method(typeof(Shrines), "OnDisable", Type.EmptyTypes, (Type[])null), null, "Shrines_OnDisable_Postfix", "Shrines.OnDisable()"); Log.LogInfo((object)"Tarot addon loaded."); } private void PatchIfFound(MethodInfo original, string prefix = null, string postfix = null, string originalLabel = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (original == null) { Log.LogWarning((object)("[Tarot] Skipped patch (method missing): " + (originalLabel ?? "unknown"))); return; } HarmonyMethod val = null; HarmonyMethod val2 = null; if (!string.IsNullOrEmpty(prefix)) { val = new HarmonyMethod(typeof(TarotPatches), prefix, (Type[])null); } if (!string.IsNullOrEmpty(postfix)) { val2 = new HarmonyMethod(typeof(TarotPatches), postfix, (Type[])null); } _harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } internal static class TarotPatches { [CompilerGenerated] private sealed class <SuppressedDelayRoutine>d__68 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public float delay; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SuppressedDelayRoutine>d__68(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.2f + delay); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <WrapAndAdvanceShrineTarotQueue>d__56 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public IEnumerator original; public Shrines shrine; public PlayerFarming owner; private int <id>5__2; private Queue<int> <queue>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WrapAndAdvanceShrineTarotQueue>d__56(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <queue>5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; goto IL_0045; case 1: <>1__state = -1; goto IL_0045; case 2: { <>1__state = -1; while (<queue>5__3.Count > 0) { PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(<queue>5__3.Dequeue()); if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner))) { if (!((Behaviour)shrine).isActiveAndEnabled) { CleanupShrineTarotState(<id>5__2); return false; } TriggerNextShrineTarotInteraction(shrine, activePlayerBySlot); return false; } } CleanupShrineTarotState(<id>5__2); return false; } IL_0045: if (SafeMoveNext(original)) { <>2__current = original.Current; <>1__state = 1; return true; } if ((Object)(object)shrine == (Object)null) { return false; } <id>5__2 = ((Object)shrine).GetInstanceID(); if (!PendingShrineTarotQueueByInstance.TryGetValue(<id>5__2, out <queue>5__3) || <queue>5__3 == null || <queue>5__3.Count == 0) { CleanupShrineTarotState(<id>5__2); return false; } <>2__current = (object)new WaitForSeconds(0.05f); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <WrapAndAdvanceTarotQueue>d__54 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public IEnumerator original; public Interaction_Tarot interaction; public PlayerFarming owner; private int <id>5__2; private Queue<int> <queue>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WrapAndAdvanceTarotQueue>d__54(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <queue>5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; goto IL_0045; case 1: <>1__state = -1; goto IL_0045; case 2: { <>1__state = -1; while (<queue>5__3.Count > 0) { PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(<queue>5__3.Dequeue()); if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner))) { if (!((Behaviour)interaction).isActiveAndEnabled) { CleanupInteractionState(<id>5__2); return false; } TriggerNextTarotInteraction(interaction, activePlayerBySlot); return false; } } CleanupInteractionState(<id>5__2); return false; } IL_0045: if (SafeMoveNext(original)) { <>2__current = original.Current; <>1__state = 1; return true; } if ((Object)(object)interaction == (Object)null) { return false; } <id>5__2 = ((Object)interaction).GetInstanceID(); if (!PendingTarotQueueByInstance.TryGetValue(<id>5__2, out <queue>5__3) || <queue>5__3 == null || <queue>5__3.Count == 0) { CleanupInteractionState(<id>5__2); return false; } <>2__current = (object)new WaitForSeconds(0.05f); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool VERBOSE_LOGS = false; private const string ClauneckTarotRoomPath = "Assets/_Rooms/Reward Room Tarot.prefab"; private const string CardsOnlyTarotRoomPath = "Assets/_Rooms/Special Free Tarot Cards.prefab"; private static bool _loggedTarotRoomPolicy = false; private static readonly Dictionary<int, PlayerFarming> TarotOwnerByInstance = new Dictionary<int, PlayerFarming>(); private static readonly Dictionary<int, Queue<int>> PendingTarotQueueByInstance = new Dictionary<int, Queue<int>>(); private static readonly HashSet<int> WarnedSuppressedDiscardByInstance = new HashSet<int>(); private static readonly Dictionary<int, PlayerFarming> TarotCardOwnerByInstance = new Dictionary<int, PlayerFarming>(); private static readonly Dictionary<int, PlayerFarming> ShrineTarotOwnerByInstance = new Dictionary<int, PlayerFarming>(); private static readonly Dictionary<int, Queue<int>> PendingShrineTarotQueueByInstance = new Dictionary<int, Queue<int>>(); private static readonly HashSet<int> WarnedSuppressedShrineDiscardByInstance = new HashSet<int>(); private static readonly Dictionary<int, int> PendingChestTarotExtraByChest = new Dictionary<int, int>(); private static readonly Dictionary<int, float> PendingChestTarotExpiryByChest = new Dictionary<int, float>(); private static int PendingChestTarotExtraAny = 0; private static float PendingChestTarotAnyExpiry = -1f; private static int PendingChestTarotSourceChestId = 0; private static readonly Dictionary<int, float> RecentChestRewardWindowByChest = new Dictionary<int, float>(); private static readonly Dictionary<int, Vector3> RecentChestRewardPositionByChest = new Dictionary<int, Vector3>(); private static int RecentChestRewardSourceChestId = 0; private static float RecentChestRewardAnyExpiry = -1f; private const float RecentChestRewardWindowSeconds = 8f; private const float ChestTarotFallbackRadius = 6f; private static readonly FieldInfo InteractionChestRewardField = typeof(Interaction_Chest).GetField("Reward", BindingFlags.Instance | BindingFlags.NonPublic); private static bool IsSpawningExtraChestTarot = false; public static void BiomeGenerator_Generate_Prefix(BiomeGenerator __instance) { EnsureClauneckTarotRoomsOnly(__instance, "BiomeGenerator.Generate"); } internal static void EnsureClauneckTarotRoomsOnly(BiomeGenerator biomeGenerator, string reason) { try { if (!_loggedTarotRoomPolicy) { _loggedTarotRoomPolicy = true; TarotAddonPlugin.Log.LogInfo((object)"[TarotRoom] Vanilla tarot-room behavior preserved (no tarot room path filtering)."); } } catch (Exception ex) { TarotAddonPlugin.Log.LogWarning((object)("[TarotRoom] Failed to enforce tarot room policy during " + reason + ": " + ex.Message)); } } private static int RemoveDisallowedTarotRoomPaths(List<string> roomPaths) { return 0; } private static bool IsDisallowedTarotRoomPath(string candidate) { if (string.IsNullOrEmpty(candidate)) { return false; } string text = NormalizeRoomPath(candidate); if (string.Equals(text, NormalizeRoomPath("Assets/_Rooms/Reward Room Tarot.prefab"), StringComparison.OrdinalIgnoreCase)) { return false; } return text.IndexOf("tarot", StringComparison.OrdinalIgnoreCase) >= 0; } private static string NormalizeRoomPath(string path) { if (string.IsNullOrEmpty(path)) { return string.Empty; } return path.Replace('\\', '/').Trim(); } public static void InteractionTarot_OnInteract_Prefix(Interaction_Tarot __instance, StateMachine state) { if ((Object)(object)__instance == (Object)null || (Object)(object)state == (Object)null) { return; } PlayerFarming val = ResolvePlayerFromState(state); if ((Object)(object)val == (Object)null) { return; } TrySetAllowedInputPlayer(val); SetInteractionContext(__instance, val, state); int instanceID = ((Object)__instance).GetInstanceID(); if (PendingTarotQueueByInstance.ContainsKey(instanceID) || __instance.Activated) { return; } Queue<int> queue = BuildTarotQueue(val); if (queue.Count > 0) { PendingTarotQueueByInstance[instanceID] = queue; if (VERBOSE_LOGS) { TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Queue created for instance " + instanceID + ".")); } } } public static bool InteractionTarot_DelayEffectsRoutine_Prefix(Interaction_Tarot __instance, TarotCard card, float delay, ref PlayerFarming playerFarming, ref IEnumerator __result) { if ((Object)(object)__instance == (Object)null) { return true; } PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance); if ((Object)(object)trackedTarotOwner == (Object)null) { return true; } if (trackedTarotOwner.playerID >= 2 && (Object)(object)playerFarming != (Object)(object)trackedTarotOwner) { int instanceID = ((Object)__instance).GetInstanceID(); if (WarnedSuppressedDiscardByInstance.Add(instanceID)) { TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Suppressed extra discard grant from P" + (trackedTarotOwner.playerID + 1) + ".")); } __result = SuppressedDelayRoutine(delay); return false; } if ((Object)(object)playerFarming == (Object)null) { playerFarming = trackedTarotOwner; } return true; } public static void InteractionTarot_DelayEffectsRoutine_Postfix(Interaction_Tarot __instance, TarotCard card, float delay, PlayerFarming playerFarming, ref IEnumerator __result) { if (!((Object)(object)__instance == (Object)null) && __result != null) { PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance); if (!((Object)(object)trackedTarotOwner == (Object)null) && !((Object)(object)playerFarming != (Object)(object)trackedTarotOwner)) { __result = WrapAndAdvanceTarotQueue(__result, __instance, trackedTarotOwner); } } } public static void InteractionTarot_OnDisable_Postfix(Interaction_Tarot __instance) { if (!((Object)(object)__instance == (Object)null)) { int instanceID = ((Object)__instance).GetInstanceID(); TarotOwnerByInstance.Remove(instanceID); PendingTarotQueueByInstance.Remove(instanceID); WarnedSuppressedDiscardByInstance.Remove(instanceID); } } public static void InteractionTarot_DoRoutine_Prefix(Interaction_Tarot __instance) { if ((Object)(object)__instance == (Object)null) { return; } int instanceID = ((Object)__instance).GetInstanceID(); PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance); if ((Object)(object)trackedTarotOwner == (Object)null) { return; } StateMachine val = TryResolveInteractionState(__instance); if ((Object)(object)val == (Object)null && (Object)(object)trackedTarotOwner != (Object)null) { val = trackedTarotOwner.state; } TrySetAllowedInputPlayer(trackedTarotOwner); SetInteractionContext(__instance, trackedTarotOwner, val); if (!PendingTarotQueueByInstance.ContainsKey(instanceID)) { Queue<int> queue = BuildTarotQueue(trackedTarotOwner); if (queue.Count > 0) { PendingTarotQueueByInstance[instanceID] = queue; TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Bootstrapped chained queue for P" + (trackedTarotOwner.playerID + 1) + ".")); } } } public static void Shrines_OnInteract_Prefix(Shrines __instance, StateMachine state) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null || (Object)(object)state == (Object)null || (int)__instance.TypeOfShrine != 7) { return; } PlayerFarming val = ResolvePlayerFromState(state); if ((Object)(object)val == (Object)null) { return; } TrySetAllowedInputPlayer(val); SetInteractionContext(__instance, val, state); int instanceID = ((Object)__instance).GetInstanceID(); ShrineTarotOwnerByInstance[instanceID] = val; if (!PendingShrineTarotQueueByInstance.ContainsKey(instanceID)) { Queue<int> queue = BuildTarotQueue(val); if (queue.Count > 0) { PendingShrineTarotQueueByInstance[instanceID] = queue; TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Queue created for instance " + instanceID + ".")); } } } public static void Shrines_DrawCardRoutine_Prefix(Shrines __instance, ref PlayerFarming playerFarming) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null || (int)__instance.TypeOfShrine != 7) { return; } PlayerFarming val = playerFarming; if ((Object)(object)val == (Object)null) { val = GetTrackedShrineTarotOwner(__instance); } if ((Object)(object)val == (Object)null) { val = TryResolveInteractionPlayer(__instance); } if ((Object)(object)val == (Object)null) { return; } playerFarming = val; TrySetAllowedInputPlayer(val); SetInteractionContext(__instance, val, val.state); int instanceID = ((Object)__instance).GetInstanceID(); ShrineTarotOwnerByInstance[instanceID] = val; if (!PendingShrineTarotQueueByInstance.ContainsKey(instanceID)) { Queue<int> queue = BuildTarotQueue(val); if (queue.Count > 0) { PendingShrineTarotQueueByInstance[instanceID] = queue; TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Bootstrapped queue for P" + (val.playerID + 1) + ".")); } } } public static bool Shrines_DelayEffectsRoutine_Prefix(Shrines __instance, TarotCard card, float delay, ref PlayerFarming playerFarming, ref IEnumerator __result) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null) { return true; } if ((int)__instance.TypeOfShrine != 7) { return true; } PlayerFarming trackedShrineTarotOwner = GetTrackedShrineTarotOwner(__instance); if ((Object)(object)trackedShrineTarotOwner == (Object)null) { return true; } if (trackedShrineTarotOwner.playerID >= 2 && (Object)(object)playerFarming != (Object)(object)trackedShrineTarotOwner) { int instanceID = ((Object)__instance).GetInstanceID(); if (WarnedSuppressedShrineDiscardByInstance.Add(instanceID)) { TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Suppressed extra discard grant from P" + (trackedShrineTarotOwner.playerID + 1) + ".")); } __result = SuppressedDelayRoutine(delay); return false; } if ((Object)(object)playerFarming == (Object)null) { playerFarming = trackedShrineTarotOwner; } return true; } public static void Shrines_DelayEffectsRoutine_Postfix(Shrines __instance, TarotCard card, float delay, PlayerFarming playerFarming, ref IEnumerator __result) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if (!((Object)(object)__instance == (Object)null) && __result != null && (int)__instance.TypeOfShrine == 7) { PlayerFarming trackedShrineTarotOwner = GetTrackedShrineTarotOwner(__instance); if (!((Object)(object)trackedShrineTarotOwner == (Object)null) && !((Object)(object)playerFarming != (Object)(object)trackedShrineTarotOwner)) { __result = WrapAndAdvanceShrineTarotQueue(__result, __instance, trackedShrineTarotOwner); } } } public static void Shrines_OnDisable_Postfix(Shrines __instance) { if (!((Object)(object)__instance == (Object)null)) { CleanupShrineTarotState(((Object)__instance).GetInstanceID()); } } public static void InteractionTarotCard_OnInteract_Prefix(Interaction_TarotCard __instance, StateMachine state) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)state == (Object)null)) { PlayerFarming val = ResolvePlayerFromState(state); if (!((Object)(object)val == (Object)null)) { TrySetAllowedInputPlayer(val); SetInteractionContext(__instance, val, state); int instanceID = ((Object)__instance).GetInstanceID(); TarotCardOwnerByInstance[instanceID] = val; } } } public static void InteractionTarotCard_DoRoutine_Prefix(Interaction_TarotCard __instance) { if ((Object)(object)__instance == (Object)null) { return; } int instanceID = ((Object)__instance).GetInstanceID(); if (!TarotCardOwnerByInstance.TryGetValue(instanceID, out var value) || (Object)(object)value == (Object)null) { value = TryResolveInteractionPlayer(__instance); } if (!((Object)(object)value == (Object)null)) { StateMachine val = TryResolveInteractionState(__instance); if ((Object)(object)val == (Object)null && (Object)(object)value != (Object)null) { val = value.state; } TrySetAllowedInputPlayer(value); SetInteractionContext(__instance, value, val); TarotCardOwnerByInstance[instanceID] = value; } } public static bool InteractionTarotCard_DelayEffectsRoutine_Prefix(Interaction_TarotCard __instance, ref PlayerFarming playerFarming) { if ((Object)(object)__instance == (Object)null) { return true; } int instanceID = ((Object)__instance).GetInstanceID(); if (!TarotCardOwnerByInstance.TryGetValue(instanceID, out var value)) { value = TryResolveInteractionPlayer(__instance); } if ((Object)(object)value == (Object)null) { return true; } if ((Object)(object)playerFarming == (Object)null || (Object)(object)playerFarming != (Object)(object)value) { playerFarming = value; if (VERBOSE_LOGS) { TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Rebound reward ownership to P" + (value.playerID + 1) + ".")); } } return true; } public static void InteractionChest_SelectReward_Postfix(Interaction_Chest __instance) { //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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null) { return; } int instanceID = ((Object)__instance).GetInstanceID(); ClearPendingChestTarot(instanceID); Vector3 value = Vector3.zero; bool flag = false; try { value = ((Component)__instance).transform.position; flag = true; } catch { } int effectivePlayerCountForChestTarot = GetEffectivePlayerCountForChestTarot(); int num = Mathf.Clamp(effectivePlayerCountForChestTarot - 2, 0, 2); if (num <= 0) { return; } RecentChestRewardWindowByChest[instanceID] = Time.unscaledTime + 8f; if (flag) { RecentChestRewardPositionByChest[instanceID] = value; } RecentChestRewardSourceChestId = instanceID; RecentChestRewardAnyExpiry = Time.unscaledTime + 6f; if (TryGetChestReward(__instance, out var reward)) { TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest reward rolled: " + ((object)(ITEM_TYPE)(ref reward)).ToString())); if ((int)reward != 26) { TarotAddonPlugin.Log.LogInfo((object)"[TarotCard] Chest did not roll tarot card this time."); return; } PendingChestTarotExtraByChest[instanceID] = num; PendingChestTarotExpiryByChest[instanceID] = Time.unscaledTime + 10f; PendingChestTarotExtraAny = num; PendingChestTarotAnyExpiry = Time.unscaledTime + 6f; PendingChestTarotSourceChestId = instanceID; TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot bonus prepared: +" + num + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot + " (playersCount=" + PlayerFarming.playersCount + ").")); } } public static void InteractionChest_OnDisableInteraction_Postfix(Interaction_Chest __instance) { _ = (Object)(object)__instance == (Object)null; } public static void InventoryItem_Spawn_Prefix(ITEM_TYPE type, int quantity, Vector3 position, float StartSpeed, Action<PickUp> result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_01df: Unknown result type (might be due to invalid IL or missing references) if (IsSpawningExtraChestTarot || (int)type != 26 || quantity <= 0) { return; } int value = 0; int num = 0; Interaction_Chest instance = Interaction_Chest.Instance; if ((Object)(object)instance != (Object)null) { int instanceID = ((Object)instance).GetInstanceID(); num = instanceID; if (PendingChestTarotExpiryByChest.TryGetValue(instanceID, out var value2) && Time.unscaledTime > value2) { ClearPendingChestTarot(instanceID); } else if (!PendingChestTarotExtraByChest.TryGetValue(instanceID, out value)) { value = 0; } } if (value <= 0 && PendingChestTarotExtraAny > 0) { if (PendingChestTarotAnyExpiry > 0f && Time.unscaledTime > PendingChestTarotAnyExpiry) { ClearPendingChestTarotAny(); } else { value = PendingChestTarotExtraAny; if (num == 0) { num = PendingChestTarotSourceChestId; } } } if (value <= 0) { Interaction_Chest instance2 = Interaction_Chest.Instance; if ((Object)(object)instance2 != (Object)null) { int instanceID2 = ((Object)instance2).GetInstanceID(); if (RecentChestRewardWindowByChest.TryGetValue(instanceID2, out var value3)) { if (Time.unscaledTime > value3) { ClearRecentChestRewardState(instanceID2); } else { bool flag = true; try { flag = Vector3.Distance(((Component)instance2).transform.position, position) <= 6f; } catch { flag = true; } if (flag) { int effectivePlayerCountForChestTarot = GetEffectivePlayerCountForChestTarot(); value = Mathf.Clamp(effectivePlayerCountForChestTarot - 2, 0, 2); if (value > 0) { num = instanceID2; TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot fallback engaged: +" + value + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot + ".")); } } } } } else if (RecentChestRewardAnyExpiry > 0f) { if (Time.unscaledTime > RecentChestRewardAnyExpiry) { if (RecentChestRewardSourceChestId != 0) { RecentChestRewardPositionByChest.Remove(RecentChestRewardSourceChestId); } RecentChestRewardAnyExpiry = -1f; RecentChestRewardSourceChestId = 0; } else { int recentChestRewardSourceChestId = RecentChestRewardSourceChestId; bool flag2 = true; if (recentChestRewardSourceChestId != 0 && RecentChestRewardPositionByChest.TryGetValue(recentChestRewardSourceChestId, out var value4)) { flag2 = Vector3.Distance(value4, position) <= 6f; } if (flag2) { int effectivePlayerCountForChestTarot2 = GetEffectivePlayerCountForChestTarot(); value = Mathf.Clamp(effectivePlayerCountForChestTarot2 - 2, 0, 2); if (value > 0) { num = recentChestRewardSourceChestId; TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot fallback(any) engaged: +" + value + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot2 + ".")); } } } } } if (value <= 0) { return; } try { IsSpawningExtraChestTarot = true; Vector3 val = default(Vector3); for (int i = 0; i < value; i++) { ((Vector3)(ref val))..ctor(Random.Range(-0.35f, 0.35f), Random.Range(-0.2f, 0.2f), 0f); InventoryItem.Spawn(type, 1, position + val, StartSpeed, result); } TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot multiplied: base drop + " + value + " extra.")); } catch (Exception ex) { TarotAddonPlugin.Log.LogWarning((object)("[TarotCard] Failed chest tarot multiply: " + ex.Message)); } finally { IsSpawningExtraChestTarot = false; if (num != 0) { if (PendingChestTarotExpiryByChest.TryGetValue(num, out var value5) && Time.unscaledTime > value5) { ClearPendingChestTarot(num); } if (RecentChestRewardWindowByChest.TryGetValue(num, out var value6) && Time.unscaledTime > value6) { ClearRecentChestRewardState(num); } } if (PendingChestTarotAnyExpiry > 0f && Time.unscaledTime > PendingChestTarotAnyExpiry) { ClearPendingChestTarotAny(); } if (RecentChestRewardAnyExpiry > 0f && Time.unscaledTime > RecentChestRewardAnyExpiry) { if (RecentChestRewardSourceChestId != 0) { RecentChestRewardPositionByChest.Remove(RecentChestRewardSourceChestId); } RecentChestRewardAnyExpiry = -1f; RecentChestRewardSourceChestId = 0; } } } public static void InteractionTarotCard_OnDisable_Postfix(Interaction_TarotCard __instance) { if (!((Object)(object)__instance == (Object)null)) { CleanupTarotCardState(((Object)__instance).GetInstanceID()); } } private static void CleanupTarotCardState(int interactionId) { TarotCardOwnerByInstance.Remove(interactionId); } private static bool TryGetChestReward(Interaction_Chest chest, out ITEM_TYPE reward) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown reward = (ITEM_TYPE)0; if ((Object)(object)chest == (Object)null || InteractionChestRewardField == null) { return false; } try { object value = InteractionChestRewardField.GetValue(chest); if (value is ITEM_TYPE) { reward = (ITEM_TYPE)(int)(ITEM_TYPE)value; return true; } } catch { } return false; } private static int GetEffectivePlayerCountForChestTarot() { int num = Mathf.Clamp(PlayerFarming.playersCount, 0, 4); HashSet<int> hashSet = new HashSet<int>(); int num2 = CountRelaxedActivePlayerSlots(); if (CoopManager.AllPlayerGameObjects != null) { int num3 = Mathf.Min(CoopManager.AllPlayerGameObjects.Length, 4); for (int i = 0; i < num3; i++) { GameObject val = CoopManager.AllPlayerGameObjects[i]; if ((Object)(object)val != (Object)null && val.activeInHierarchy) { hashSet.Add(i); } } } int num4 = Mathf.Max(hashSet.Count, num2); int num5 = 0; for (int j = 0; j < 4; j++) { if (HasInputAssignedToSlot(j)) { num5++; } } int num6 = Mathf.Max(new int[3] { num, num4, num5 }); if (num6 <= 0) { num6 = 1; } return Mathf.Clamp(num6, 1, 4); } private static int CountRelaxedActivePlayerSlots() { if (PlayerFarming.players == null) { return 0; } HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < PlayerFarming.players.Count; i++) { PlayerFarming val = PlayerFarming.players[i]; if (!((Object)(object)val == (Object)null) && val.playerID >= 0 && val.playerID <= 3 && !((Object)(object)val.state == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && HasUsableInput(val)) { hashSet.Add(val.playerID); } } return hashSet.Count; } private static bool HasInputAssignedToSlot(int slot) { try { Player player = RewiredInputManager.GetPlayer(slot); if (player == null || player.controllers == null) { return false; } if (player.controllers.hasKeyboard) { return true; } return player.controllers.Joysticks != null && player.controllers.Joysticks.Count > 0; } catch { return false; } } private static void ClearPendingChestTarot(int chestId) { PendingChestTarotExtraByChest.Remove(chestId); PendingChestTarotExpiryByChest.Remove(chestId); } private static void ClearPendingChestTarotAny() { PendingChestTarotExtraAny = 0; PendingChestTarotAnyExpiry = -1f; PendingChestTarotSourceChestId = 0; } private static void ClearRecentChestRewardState(int chestId) { if (chestId != 0) { RecentChestRewardWindowByChest.Remove(chestId); RecentChestRewardPositionByChest.Remove(chestId); if (RecentChestRewardSourceChestId == chestId) { RecentChestRewardSourceChestId = 0; RecentChestRewardAnyExpiry = -1f; } } } [IteratorStateMachine(typeof(<WrapAndAdvanceTarotQueue>d__54))] private static IEnumerator WrapAndAdvanceTarotQueue(IEnumerator original, Interaction_Tarot interaction, PlayerFarming owner) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WrapAndAdvanceTarotQueue>d__54(0) { original = original, interaction = interaction, owner = owner }; } private static void TriggerNextTarotInteraction(Interaction_Tarot interaction, PlayerFarming player) { if ((Object)(object)interaction == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.state == (Object)null) { return; } try { TrySetAllowedInputPlayer(player); SetInteractionContext(interaction, player, player.state); interaction.Activated = false; TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Chained selection owner -> P" + (player.playerID + 1) + ".")); ((Interaction)interaction).OnInteract(player.state); if (VERBOSE_LOGS) { TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Triggered chained interaction for P" + (player.playerID + 1) + ".")); } } catch (Exception ex) { TarotAddonPlugin.Log.LogWarning((object)("[Tarot] Failed to trigger chained interaction: " + ex.Message)); CleanupInteractionState(((Object)interaction).GetInstanceID()); } } [IteratorStateMachine(typeof(<WrapAndAdvanceShrineTarotQueue>d__56))] private static IEnumerator WrapAndAdvanceShrineTarotQueue(IEnumerator original, Shrines shrine, PlayerFarming owner) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WrapAndAdvanceShrineTarotQueue>d__56(0) { original = original, shrine = shrine, owner = owner }; } private static void TriggerNextShrineTarotInteraction(Shrines shrine, PlayerFarming player) { if ((Object)(object)shrine == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.state == (Object)null) { return; } int instanceID = ((Object)shrine).GetInstanceID(); try { TrySetAllowedInputPlayer(player); SetInteractionContext(shrine, player, player.state); ShrineTarotOwnerByInstance[instanceID] = player; if (!(Traverse.Create((object)shrine).Method("DrawCardRoutine", new object[1] { player }).GetValue() is IEnumerator enumerator)) { TarotAddonPlugin.Log.LogWarning((object)("[ShrineTarot] Failed to resolve DrawCardRoutine for P" + (player.playerID + 1) + ".")); CleanupShrineTarotState(instanceID); } else { TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Chained purchase owner -> P" + (player.playerID + 1) + ".")); ((MonoBehaviour)shrine).StartCoroutine(enumerator); } } catch (Exception ex) { TarotAddonPlugin.Log.LogWarning((object)("[ShrineTarot] Failed to trigger chained purchase: " + ex.Message)); CleanupShrineTarotState(instanceID); } } private static Queue<int> BuildTarotQueue(PlayerFarming starter) { Queue<int> queue = new Queue<int>(); if ((Object)(object)starter == (Object)null) { return queue; } int playerID = starter.playerID; if (playerID <= 1) { EnqueueSlotIfActive(queue, 2); EnqueueSlotIfActive(queue, 3); return queue; } int preferredVanillaOwnerSlot = GetPreferredVanillaOwnerSlot(starter); EnqueueSlotIfActive(queue, preferredVanillaOwnerSlot); switch (playerID) { case 2: EnqueueSlotIfActive(queue, 3); break; case 3: EnqueueSlotIfActive(queue, 2); break; } return queue; } private static void EnqueueSlotIfActive(Queue<int> queue, int slot) { if (queue != null && (Object)(object)GetActivePlayerBySlot(slot) != (Object)null) { queue.Enqueue(slot); } } private static PlayerFarming GetActivePlayerBySlot(int slot) { if (PlayerFarming.players == null) { return null; } for (int i = 0; i < PlayerFarming.players.Count; i++) { PlayerFarming val = PlayerFarming.players[i]; if (!((Object)(object)val == (Object)null) && val.playerID == slot && (PlayerFarming.playersCount <= 0 || val.playerID < PlayerFarming.playersCount) && !((Object)(object)val.state == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && HasUsableInput(val)) { return val; } } return null; } private static int GetPreferredVanillaOwnerSlot(PlayerFarming directStarter) { if ((Object)(object)directStarter != (Object)null && directStarter.playerID == 1) { return 1; } if ((Object)(object)GetActivePlayerBySlot(0) != (Object)null) { return 0; } return 1; } private static PlayerFarming ResolvePlayerFromState(StateMachine state) { if ((Object)(object)state == (Object)null) { return null; } PlayerFarming component = ((Component)state).GetComponent<PlayerFarming>(); if ((Object)(object)component != (Object)null) { return component; } try { if ((Object)(object)((Component)state).gameObject != (Object)null) { return PlayerFarming.GetPlayerFarmingComponent(((Component)state).gameObject); } } catch { } return PlayerFarming.Instance; } private static PlayerFarming GetTrackedTarotOwner(Interaction_Tarot interaction) { if ((Object)(object)interaction == (Object)null) { return null; } if (TarotOwnerByInstance.TryGetValue(((Object)interaction).GetInstanceID(), out var value) && (Object)(object)value != (Object)null) { return value; } value = TryResolveInteractionPlayer(interaction); if ((Object)(object)value != (Object)null) { TarotOwnerByInstance[((Object)interaction).GetInstanceID()] = value; } return value; } private static PlayerFarming GetTrackedShrineTarotOwner(Shrines shrine) { if ((Object)(object)shrine == (Object)null) { return null; } int instanceID = ((Object)shrine).GetInstanceID(); if (ShrineTarotOwnerByInstance.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { return value; } value = TryResolveInteractionPlayer(shrine); if ((Object)(object)value != (Object)null) { ShrineTarotOwnerByInstance[instanceID] = value; } return value; } private static void SetInteractionContext(object interaction, PlayerFarming player, StateMachine state) { if (interaction != null) { if ((Object)(object)player != (Object)null) { TrySetInteractionPlayer(interaction, player); } if ((Object)(object)state != (Object)null) { TrySetInteractionState(interaction, state); } Interaction_Tarot val = (Interaction_Tarot)((interaction is Interaction_Tarot) ? interaction : null); if ((Object)(object)val != (Object)null && (Object)(object)player != (Object)null) { TarotOwnerByInstance[((Object)val).GetInstanceID()] = player; } Interaction_TarotCard val2 = (Interaction_TarotCard)((interaction is Interaction_TarotCard) ? interaction : null); if ((Object)(object)val2 != (Object)null && (Object)(object)player != (Object)null) { TarotCardOwnerByInstance[((Object)val2).GetInstanceID()] = player; } Shrines val3 = (Shrines)((interaction is Shrines) ? interaction : null); if ((Object)(object)val3 != (Object)null && (Object)(object)player != (Object)null) { ShrineTarotOwnerByInstance[((Object)val3).GetInstanceID()] = player; } } } private static void CleanupInteractionState(int interactionId) { TarotOwnerByInstance.Remove(interactionId); PendingTarotQueueByInstance.Remove(interactionId); WarnedSuppressedDiscardByInstance.Remove(interactionId); } private static void CleanupShrineTarotState(int interactionId) { ShrineTarotOwnerByInstance.Remove(interactionId); PendingShrineTarotQueueByInstance.Remove(interactionId); WarnedSuppressedShrineDiscardByInstance.Remove(interactionId); } [IteratorStateMachine(typeof(<SuppressedDelayRoutine>d__68))] private static IEnumerator SuppressedDelayRoutine(float delay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SuppressedDelayRoutine>d__68(0) { delay = delay }; } private static bool HasUsableInput(PlayerFarming player) { if ((Object)(object)player == (Object)null) { return false; } try { if (player.isLamb) { return true; } if (player.rewiredPlayer == null || player.rewiredPlayer.controllers == null) { return false; } return player.rewiredPlayer.controllers.hasKeyboard || player.rewiredPlayer.controllers.joystickCount > 0; } catch { return true; } } private static void TrySetAllowedInputPlayer(PlayerFarming player) { if ((Object)(object)player == (Object)null) { return; } try { UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if (!((Object)(object)instance == (Object)null)) { instance.AllowInputOnlyFromPlayer = player; instance.TemporarilyAllowInputOnlyFromAnyPlayer = false; } } catch { } } private static bool SafeMoveNext(IEnumerator iterator) { if (iterator == null) { return false; } try { return iterator.MoveNext(); } catch (Exception ex) { TarotAddonPlugin.Log.LogWarning((object)("[Tarot] Coroutine iteration failed: " + ex.Message)); return false; } } private static T TryGetField<T>(object instance, string fieldName) where T : class { if (instance == null || string.IsNullOrEmpty(fieldName)) { return null; } try { return Traverse.Create(instance).Field(fieldName).GetValue() as T; } catch { return null; } } private static T TryGetProperty<T>(object instance, string propertyName) where T : class { if (instance == null || string.IsNullOrEmpty(propertyName)) { return null; } try { return Traverse.Create(instance).Property(propertyName, (object[])null).GetValue() as T; } catch { return null; } } private static bool TrySetField(object instance, string fieldName, object value) { if (instance == null || string.IsNullOrEmpty(fieldName)) { return false; } try { Traverse.Create(instance).Field(fieldName).SetValue(value); return true; } catch { return false; } } private static bool TrySetProperty(object instance, string propertyName, object value) { if (instance == null || string.IsNullOrEmpty(propertyName)) { return false; } try { Traverse.Create(instance).Property(propertyName, (object[])null).SetValue(value); return true; } catch { return false; } } private static PlayerFarming TryResolveInteractionPlayer(object interaction) { if (interaction == null) { return null; } PlayerFarming val = TryGetProperty<PlayerFarming>(interaction, "playerFarming"); if ((Object)(object)val != (Object)null) { return val; } val = TryGetField<PlayerFarming>(interaction, "playerFarming"); if ((Object)(object)val != (Object)null) { return val; } val = TryGetField<PlayerFarming>(interaction, "_playerFarming"); if ((Object)(object)val != (Object)null) { return val; } return TryGetField<PlayerFarming>(interaction, "<playerFarming>k__BackingField"); } private static StateMachine TryResolveInteractionState(object interaction) { if (interaction == null) { return null; } StateMachine val = TryGetProperty<StateMachine>(interaction, "state"); if ((Object)(object)val != (Object)null) { return val; } val = TryGetField<StateMachine>(interaction, "state"); if ((Object)(object)val != (Object)null) { return val; } val = TryGetField<StateMachine>(interaction, "_state"); if ((Object)(object)val != (Object)null) { return val; } return TryGetField<StateMachine>(interaction, "<state>k__BackingField"); } private static void TrySetInteractionPlayer(object interaction, PlayerFarming player) { if (interaction != null && !TrySetProperty(interaction, "playerFarming", player) && !TrySetField(interaction, "playerFarming", player) && !TrySetField(interaction, "_playerFarming", player)) { TrySetField(interaction, "<playerFarming>k__BackingField", player); } } private static void TrySetInteractionState(object interaction, StateMachine state) { if (interaction != null && !TrySetProperty(interaction, "state", state) && !TrySetField(interaction, "state", state) && !TrySetField(interaction, "_state", state)) { TrySetField(interaction, "<state>k__BackingField", state); } } }