Decompiled source of CultOfTheLambMultiplayer v3.0.0
plugins/COTLMP - Enemy Spawn Scaling.dll
Decompiled 4 days 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.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; 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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace COTLMultiplayerEnemySpawnScaling { [BepInDependency(/*Could not decode attribute arguments.*/)] [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; } 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)Unsafe.As<Team, Team>(ref component.team)/*cast due to .constrained prefix*/).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; } string text = ((object)component.EnemyType/*cast due to .constrained prefix*/).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"; } return ((object)val.EnemyType/*cast due to .constrained prefix*/).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; } private static IEnumerator DeferredRoomCompletionRoutine() { float deadline = Time.unscaledTime + 25f; while (Time.unscaledTime < deadline && CountAliveTrackedScaledClones() > 0) { yield return (object)new WaitForSecondsRealtime(0.2f); } 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})"); } else { RoomLockController.RoomCompleted(true, _deferredDoorsDown); } } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] deferred completion failed: " + ex.Message)); } finally { _allowRoomCompletePassThrough = false; } } } }
plugins/COTLMP - Multiplayer Expansion.dll
Decompiled 4 days 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.Security; using System.Security.Permissions; 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 Microsoft.CodeAnalysis; 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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace 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_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_0205: Expected O, but got Unknown //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Expected O, but got Unknown //IL_027a: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_02ed: Expected O, but got Unknown //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Expected O, but got Unknown //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Expected O, but got Unknown //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Expected O, but got Unknown //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Expected O, but got Unknown //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Expected O, but got Unknown //IL_0456: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Expected O, but got Unknown //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Expected O, but got Unknown //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Expected O, but got Unknown //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_0505: Expected O, but got Unknown //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Expected O, but got Unknown //IL_0576: Unknown result type (might be due to invalid IL or missing references) //IL_0583: Expected O, but got Unknown //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05b9: Expected O, but got Unknown //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_05ef: Expected O, but got Unknown //IL_061a: Unknown result type (might be due to invalid IL or missing references) //IL_0625: Expected O, but got Unknown //IL_06b0: Unknown result type (might be due to invalid IL or missing references) //IL_06c5: Unknown result type (might be due to invalid IL or missing references) //IL_06d0: Expected O, but got Unknown //IL_06d0: Expected O, but got Unknown //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_0720: Unknown result type (might be due to invalid IL or missing references) //IL_0735: Unknown result type (might be due to invalid IL or missing references) //IL_0740: Expected O, but got Unknown //IL_0740: Expected O, but got Unknown //IL_0740: Expected O, but got Unknown //IL_077a: Unknown result type (might be due to invalid IL or missing references) //IL_0791: Unknown result type (might be due to invalid IL or missing references) //IL_079c: Expected O, but got Unknown //IL_079c: Expected O, but got Unknown //IL_07d0: Unknown result type (might be due to invalid IL or missing references) //IL_07e7: Unknown result type (might be due to invalid IL or missing references) //IL_07f2: Expected O, but got Unknown //IL_07f2: Expected O, but got Unknown //IL_066c: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Expected O, but got Unknown //IL_0846: Unknown result type (might be due to invalid IL or missing references) //IL_085d: Unknown result type (might be due to invalid IL or missing references) //IL_0868: Expected O, but got Unknown //IL_0868: Expected O, but got Unknown //IL_08bc: Unknown result type (might be due to invalid IL or missing references) //IL_08d2: Unknown result type (might be due to invalid IL or missing references) //IL_08e7: Unknown result type (might be due to invalid IL or missing references) //IL_08f2: Expected O, but got Unknown //IL_08f2: Expected O, but got Unknown //IL_08f2: Expected O, but got Unknown //IL_0946: Unknown result type (might be due to invalid IL or missing references) //IL_095d: Unknown result type (might be due to invalid IL or missing references) //IL_0968: Expected O, but got Unknown //IL_0968: Expected O, but got Unknown //IL_09bc: Unknown result type (might be due to invalid IL or missing references) //IL_09d3: Unknown result type (might be due to invalid IL or missing references) //IL_09de: Expected O, but got Unknown //IL_09de: Expected O, but got Unknown //IL_0a36: Unknown result type (might be due to invalid IL or missing references) //IL_0a4b: Unknown result type (might be due to invalid IL or missing references) //IL_0a56: Expected O, but got Unknown //IL_0a56: Expected O, but got Unknown //IL_0a8e: Unknown result type (might be due to invalid IL or missing references) //IL_0aa3: Unknown result type (might be due to invalid IL or missing references) //IL_0aae: Expected O, but got Unknown //IL_0aae: Expected O, but got Unknown //IL_0b3d: Unknown result type (might be due to invalid IL or missing references) //IL_0b54: Unknown result type (might be due to invalid IL or missing references) //IL_0b5f: Expected O, but got Unknown //IL_0b5f: Expected O, but got Unknown //IL_0ba6: Unknown result type (might be due to invalid IL or missing references) //IL_0bb4: Expected O, but got Unknown //IL_0bee: Unknown result type (might be due to invalid IL or missing references) //IL_0c05: Unknown result type (might be due to invalid IL or missing references) //IL_0c10: Expected O, but got Unknown //IL_0c10: Expected O, but got Unknown //IL_0c38: Unknown result type (might be due to invalid IL or missing references) //IL_0c4e: Unknown result type (might be due to invalid IL or missing references) //IL_0c63: Unknown result type (might be due to invalid IL or missing references) //IL_0c6e: Expected O, but got Unknown //IL_0c6e: Expected O, but got Unknown //IL_0c6e: Expected O, but got Unknown //IL_0ca8: Unknown result type (might be due to invalid IL or missing references) //IL_0cb6: Expected O, but got Unknown //IL_0ae6: Unknown result type (might be due to invalid IL or missing references) //IL_0afb: Unknown result type (might be due to invalid IL or missing references) //IL_0b06: Expected O, but got Unknown //IL_0b06: Expected O, but got Unknown //IL_0cea: Unknown result type (might be due to invalid IL or missing references) //IL_0d00: Unknown result type (might be due to invalid IL or missing references) //IL_0d15: Unknown result type (might be due to invalid IL or missing references) //IL_0d20: Expected O, but got Unknown //IL_0d20: Expected O, but got Unknown //IL_0d20: Expected O, but got Unknown //IL_0d7a: Unknown result type (might be due to invalid IL or missing references) //IL_0d91: Unknown result type (might be due to invalid IL or missing references) //IL_0d9c: Expected O, but got Unknown //IL_0d9c: Expected O, but got Unknown //IL_0dc4: Unknown result type (might be due to invalid IL or missing references) //IL_0dda: Unknown result type (might be due to invalid IL or missing references) //IL_0def: Unknown result type (might be due to invalid IL or missing references) //IL_0dfa: Expected O, but got Unknown //IL_0dfa: Expected O, but got Unknown //IL_0dfa: Expected O, but got Unknown //IL_0e2e: Unknown result type (might be due to invalid IL or missing references) //IL_0e44: Unknown result type (might be due to invalid IL or missing references) //IL_0e59: Unknown result type (might be due to invalid IL or missing references) //IL_0e64: Expected O, but got Unknown //IL_0e64: Expected O, but got Unknown //IL_0e64: Expected O, but got Unknown //IL_0ebe: Unknown result type (might be due to invalid IL or missing references) //IL_0ed5: Unknown result type (might be due to invalid IL or missing references) //IL_0ee0: Expected O, but got Unknown //IL_0ee0: Expected O, but got Unknown //IL_0f08: Unknown result type (might be due to invalid IL or missing references) //IL_0f1e: Unknown result type (might be due to invalid IL or missing references) //IL_0f33: Unknown result type (might be due to invalid IL or missing references) //IL_0f3e: Expected O, but got Unknown //IL_0f3e: Expected O, but got Unknown //IL_0f3e: Expected O, but got Unknown //IL_0f72: Unknown result type (might be due to invalid IL or missing references) //IL_0f88: Unknown result type (might be due to invalid IL or missing references) //IL_0f9d: Unknown result type (might be due to invalid IL or missing references) //IL_0fa8: Expected O, but got Unknown //IL_0fa8: Expected O, but got Unknown //IL_0fa8: Expected O, but got Unknown //IL_1002: Unknown result type (might be due to invalid IL or missing references) //IL_1019: Unknown result type (might be due to invalid IL or missing references) //IL_1024: Expected O, but got Unknown //IL_1024: Expected O, but got Unknown //IL_104c: Unknown result type (might be due to invalid IL or missing references) //IL_1062: Unknown result type (might be due to invalid IL or missing references) //IL_1077: Unknown result type (might be due to invalid IL or missing references) //IL_1082: Expected O, but got Unknown //IL_1082: Expected O, but got Unknown //IL_1082: Expected O, but got Unknown //IL_10b6: Unknown result type (might be due to invalid IL or missing references) //IL_10cc: Unknown result type (might be due to invalid IL or missing references) //IL_10e1: Unknown result type (might be due to invalid IL or missing references) //IL_10ec: Expected O, but got Unknown //IL_10ec: Expected O, but got Unknown //IL_10ec: Expected O, but got Unknown //IL_1134: Unknown result type (might be due to invalid IL or missing references) //IL_1142: Expected O, but got Unknown //IL_116b: Unknown result type (might be due to invalid IL or missing references) //IL_1178: Expected O, but got Unknown //IL_11b3: Unknown result type (might be due to invalid IL or missing references) //IL_11c0: Expected O, but got Unknown //IL_11ed: Unknown result type (might be due to invalid IL or missing references) //IL_11fa: Expected O, but got Unknown //IL_1269: Unknown result type (might be due to invalid IL or missing references) //IL_1276: Expected O, but got Unknown //IL_12cf: Unknown result type (might be due to invalid IL or missing references) //IL_12dc: Expected O, but got Unknown //IL_1328: Unknown result type (might be due to invalid IL or missing references) //IL_1335: Expected O, but got Unknown //IL_1370: Unknown result type (might be due to invalid IL or missing references) //IL_137d: Expected O, but got Unknown //IL_13aa: Unknown result type (might be due to invalid IL or missing references) //IL_13b7: Expected O, but got Unknown //IL_13f1: Unknown result type (might be due to invalid IL or missing references) //IL_1406: Unknown result type (might be due to invalid IL or missing references) //IL_1413: Expected O, but got Unknown //IL_1413: Expected O, but got Unknown //IL_143c: Unknown result type (might be due to invalid IL or missing references) //IL_1449: Expected O, but got Unknown //IL_1471: Unknown result type (might be due to invalid IL or missing references) //IL_147f: Expected O, but got Unknown //IL_14a7: Unknown result type (might be due to invalid IL or missing references) //IL_14b5: Expected O, but got Unknown //IL_14dd: Unknown result type (might be due to invalid IL or missing references) //IL_14eb: Expected O, but got Unknown //IL_1525: Unknown result type (might be due to invalid IL or missing references) //IL_1533: Expected O, but got Unknown //IL_156d: Unknown result type (might be due to invalid IL or missing references) //IL_157b: Expected O, but got Unknown //IL_15a3: Unknown result type (might be due to invalid IL or missing references) //IL_15b1: Expected O, but got Unknown //IL_1605: Unknown result type (might be due to invalid IL or missing references) //IL_1613: Expected O, but got Unknown //IL_165a: Unknown result type (might be due to invalid IL or missing references) //IL_1668: Expected O, but got Unknown //IL_1690: Unknown result type (might be due to invalid IL or missing references) //IL_169e: Expected O, but got Unknown //IL_16e5: Unknown result type (might be due to invalid IL or missing references) //IL_16f3: Expected O, but got Unknown //IL_173a: Unknown result type (might be due to invalid IL or missing references) //IL_174f: Unknown result type (might be due to invalid IL or missing references) //IL_175c: Expected O, but got Unknown //IL_175c: Expected O, but got Unknown //IL_1796: Unknown result type (might be due to invalid IL or missing references) //IL_17ad: Unknown result type (might be due to invalid IL or missing references) //IL_17b8: Expected O, but got Unknown //IL_17b8: Expected O, but got Unknown //IL_17f2: Unknown result type (might be due to invalid IL or missing references) //IL_1809: Unknown result type (might be due to invalid IL or missing references) //IL_1814: Expected O, but got Unknown //IL_1814: 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(Interaction_TempleAltar), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_TempleAltar), "DoCancel", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null); MethodInfo method = typeof(Interaction_TempleAltar).GetMethod("TryOnboardSin", BindingFlags.Instance | BindingFlags.NonPublic); if (method != null) { val.Patch((MethodBase)method, new HarmonyMethod(typeof(Patches), "TempleAltar_TryOnboardSin_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.TryOnboardSin coop-safe guard"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.TryOnboardSin not found"); } MethodInfo methodInfo = AccessTools.Method(typeof(Interaction_TempleAltar), "<DoCancel>b__68_1", (Type[])null, (Type[])null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_CancelCleanup_Postfix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.DoCancel cleanup callback finalizer"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.DoCancel cleanup callback not found"); } MethodInfo methodInfo2 = Patches.FindNestedMoveNext(typeof(Interaction_TempleAltar), "DelayMenu"); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Patches), "TempleAltarTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltarTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.DelayMenu player presentation scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.DelayMenu MoveNext not found"); } 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 methodInfo3 = AccessTools.Method(type, "Update", (Type[])null, (Type[])null); if (methodInfo3 != null) { val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched SkeletonAnimationLODGlobalManager.Update finalizer"); } } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "UpdatePlayer", new Type[1] { typeof(int) }, (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "InteractionFishing_BlockExtraPlayers_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null); MethodInfo methodInfo4 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "WaitForPlayersToArriveIE"); if (methodInfo4 != null) { val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Fishing.WaitForPlayersToArriveIE two-player scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.WaitForPlayersToArriveIE MoveNext not found"); } MethodInfo methodInfo5 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "BeganFishingIE"); if (methodInfo5 != null) { val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Fishing.BeganFishingIE two-player scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.BeganFishingIE MoveNext not found"); } MethodInfo methodInfo6 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "CastLineIE"); if (methodInfo6 != null) { val.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Fishing.CastLineIE two-player scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.CastLineIE MoveNext not found"); } MethodInfo methodInfo7 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "FishCaughtIE"); if (methodInfo7 != null) { val.Patch((MethodBase)methodInfo7, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Fishing.FishCaughtIE two-player scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.FishCaughtIE MoveNext not found"); } MethodInfo methodInfo8 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "NoCatch"); if (methodInfo8 != null) { val.Patch((MethodBase)methodInfo8, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Fishing.NoCatch two-player scope"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.NoCatch MoveNext not found"); } MethodInfo methodInfo9 = AccessTools.Method(typeof(Fishable), "Update", (Type[])null, (Type[])null); if (methodInfo9 != null) { val.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null); } MethodInfo methodInfo10 = AccessTools.Method(typeof(Fishable), "HookLanded", (Type[])null, (Type[])null); if (methodInfo10 != null) { val.Patch((MethodBase)methodInfo10, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null); } MethodInfo methodInfo11 = AccessTools.Method(typeof(BiomeConstants), "BiomeGenerator_OnBiomeChangeRoom", (Type[])null, (Type[])null); if (methodInfo11 != null) { val.Patch((MethodBase)methodInfo11, (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, new HarmonyMethod(typeof(Patches), "TeleporterAnimEvent_Finalizer", (Type[])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(Interaction_PlayerClearRubble), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_PlayerClearRubble), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_PlayerClearRubble), "DoBuild", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_DoBuild_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo12 = Patches.FindNestedMoveNext(typeof(Interaction_PlayerClearRubble), "DoBuild"); if (methodInfo12 != null) { val.Patch((MethodBase)methodInfo12, new HarmonyMethod(typeof(Patches), "PlayerClearRubble_DoBuildMoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_PlayerClearRubble.DoBuild MoveNext safety"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_PlayerClearRubble.DoBuild MoveNext not found"); } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Woodcutting), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Woodcutting), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); MethodInfo methodInfo13 = Patches.FindNestedMoveNext(typeof(Interaction_Woodcutting), "DoWoodCutting"); if (methodInfo13 != null) { val.Patch((MethodBase)methodInfo13, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Woodcutting.DoWoodCutting MoveNext safety"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Woodcutting.DoWoodCutting MoveNext not found"); } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WoodcuttingRubble), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WoodcuttingRubble), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); MethodInfo methodInfo14 = Patches.FindNestedMoveNext(typeof(Interaction_WoodcuttingRubble), "DoWoodCutting"); if (methodInfo14 != null) { val.Patch((MethodBase)methodInfo14, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_WoodcuttingRubble.DoWoodCutting MoveNext safety"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_WoodcuttingRubble.DoWoodCutting MoveNext not found"); } val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Berries), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Berries), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); MethodInfo methodInfo15 = Patches.FindNestedMoveNext(typeof(Interaction_Berries), "PickBerries"); if (methodInfo15 != null) { val.Patch((MethodBase)methodInfo15, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null); Log.LogInfo((object)"[MP] Patched Interaction_Berries.PickBerries MoveNext safety"); } else { Log.LogWarning((object)"[MP] Skipped patch: Interaction_Berries.PickBerries MoveNext not found"); } 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 method2 = typeof(CoopManager).GetMethod("ResetMainPlayerOnConversationEnd", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2] { typeof(bool), typeof(bool) }, null); if (method2 != null) { val.Patch((MethodBase)method2, (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 { private sealed class PlayerListScopeState { public int SavedPlayersCount; public List<PlayerFarming> HiddenPlayers; } 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 readonly FieldInfo _teleporterStencilField = typeof(Interaction_BaseTeleporter).GetField("PlayerStencilRT", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _clearRubbleButtonDownField = typeof(Interaction_PlayerClearRubble).GetField("buttonDown", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _clearRubbleActivatingPlayersField = typeof(Interaction_PlayerClearRubble).GetField("activatingPlayers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarActivatedField = typeof(Interaction_TempleAltar).GetField("Activated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarActivatingField = typeof(Interaction_TempleAltar).GetField("Activating", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarCoopPlayerFrozenField = typeof(Interaction_TempleAltar).GetField("CoopPlayerFrozen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarInteractedField = typeof(Interaction_TempleAltar).GetField("<Interacted>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarColliderField = typeof(Interaction_TempleAltar).GetField("Collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _templeAltarForceRitualField = typeof(Interaction_TempleAltar).GetField("forceRitual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static bool _templeExtraReleaseQueued = false; private static bool _loggedFishingBoundsSuppressed = false; private static readonly Stack<PlayerListScopeState> _twoPlayerListScopes = new Stack<PlayerListScopeState>(); 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) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown if (!CoopManager.CoopActive || PlayerFarming.playersCount >= 4 || (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)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)val); } RewiredInputManager.GetPlayer(playersCount).controllers.AddController((Controller)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 val = MonoSingleton<UIManager>.Instance.ShowCoopAssignMenu(); ((UIMenuBase)val).OnHidden = (Action)Delegate.Combine(((UIMenuBase)val).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) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown 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)val == (Object)null || (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) { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_058f: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Expected O, but got Unknown //IL_05e5: Unknown result type (might be due to invalid IL or missing references) //IL_05ef: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_04df: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Expected O, but got Unknown //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_050b: Expected O, but got Unknown //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Expected O, but got Unknown //IL_06ad: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Expected O, but got Unknown //IL_06d0: Unknown result type (might be due to invalid IL or missing references) //IL_06db: Expected O, but got Unknown //IL_0651: Unknown result type (might be due to invalid IL or missing references) //IL_065c: Expected O, but got Unknown //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Expected O, but got Unknown //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Expected O, but got Unknown //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Expected O, but got Unknown //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Expected O, but got Unknown 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)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)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)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)value != (Object)null) { Object.Destroy((Object)value); } _extraHeartBars.Remove(playerID); if (PlayerFarming.playersCount > 1 && (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)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 num4 = 1; num4 < 4; num4++) { if (RewiredInputManager.GetPlayer(num4).controllers.ContainsController((Controller)val2)) { flag = true; break; } } if (!flag) { for (int num5 = 1; num5 < 4; num5++) { RewiredInputManager.GetPlayer(num5).controllers.RemoveController((Controller)val2); } if (!RewiredInputManager.GetPlayer(0).controllers.ContainsController((Controller)val2)) { RewiredInputManager.GetPlayer(0).controllers.AddController((Controller)val2, true); } Plugin.Log.LogInfo((object)$"Joystick [{i}] assigned to Lamb (slot 0)"); } } num2 = PlayerFarming.playersCount; if (num2 < 1) { num2 = 1; } for (int num6 = 0; num6 < array.Length && num6 < list.Count; num6++) { if (num2 >= 4) { break; } CoopAssignInputOption val4 = array[num6]; if ((Object)val4 == (Object)null || !((Component)val4).gameObject.activeSelf || val4.selection != 1) { continue; } val4.inputLock = false; Joystick val5 = list[num6]; if (val5 == null) { continue; } bool flag2 = false; for (int num7 = 1; num7 < 4; num7++) { if (!RewiredInputManager.GetPlayer(num7).controllers.ContainsController((Controller)val5)) { continue; } PlayerFarming val6 = null; foreach (PlayerFarming player5 in PlayerFarming.players) { if (!player5.isLamb && player5.playerID == num7 && ((Component)player5).gameObject.activeSelf) { val6 = player5; break; } } if ((Object)val6 != (Object)null) { flag2 = true; Plugin.Log.LogInfo((object)$"Joystick [{num6}] already active goat slot {num7}, skipping"); break; } } if (!flag2) { for (int num8 = 0; num8 < 4; num8++) { RewiredInputManager.GetPlayer(num8).controllers.RemoveController((Controller)val5); } RewiredInputManager.GetPlayer(num2).controllers.AddController((Controller)val5, true); Plugin.Log.LogInfo((object)$"Joystick [{num6}] -> 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) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown 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)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)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() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Expected O, but got Unknown //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Expected O, but got Unknown 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)val2 == (Object)null) && !val2.isLamb && !((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)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)value != (Object)null) { Object.Destroy((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)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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_007f: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown if ((Object)__instance == (Object)null || __instance.isLamb || __instance.playerID <= 1 || !GameManager.IsDungeon(PlayerFarming.Location) || (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)CoopManager.AllPlayerGameObjects[i] == (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)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)); } } private static IEnumerator RespawnExtraSlots(int count) { _startupExtraRespawnDepth++; try { bool inDungeon = GameManager.IsDungeon(PlayerFarming.Location); float timeout = 30f; float waited; for (waited = 0f; waited < timeout; waited += 0.25f) { if ((Object)PlayerFarming.Instance != (Object)null && ((Component)PlayerFarming.Instance).gameObject.activeSelf && PlayerFarming.playersCount >= 1 && (Object)PlayerFarming.Instance.state != (Object)null && (inDungeon || (int)PlayerFarming.Instance.state.CURRENT_STATE != 13)) { break; } yield return (object)new WaitForSeconds(0.25f); } if (waited >= timeout) { Plugin.Log.LogWarning((object)"RespawnExtraSlots: timed out waiting for PlayerFarming.Instance"); yield break; } if (inDungeon) { _dungeonIntroProtectUntil = Time.unscaledTime + 8f; } yield return (object)new WaitForSeconds(1f); for (int s = 2; s < 2 + count && s < 4; s++) { if (_pendingSpawnSlots.Contains(s)) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already pending, skipping duplicate startup spawn"); continue; } if ((Object)FindActivePlayerBySlot(s) != (Object)null) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already active, skipping startup spawn"); continue; } if (CoopManager.AllPlayerGameObjects != null && s < CoopManager.AllPlayerGameObjects.Length && (Object)CoopManager.AllPlayerGameObjects[s] != (Object)null && CoopManager.AllPlayerGameObjects[s].activeInHierarchy) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already has active gameobject, skipping startup spawn"); continue; } Player player = RewiredInputManager.GetPlayer(s); if (player == null || player.controllers == null || player.controllers.Joysticks == null || player.controllers.Joysticks.Count <= 0) { Plugin.Log.LogWarning((object)$"RespawnExtraSlots: slot {s} has no joystick, skipping"); continue; } Plugin.Log.LogInfo((object)$"RespawnExtraSlots: spawning slot {s}"); float spawnWait = 0f; while ((Object)CoopManager.Instance != (Object)null && CoopManager.Instance.IsSpawningOrRemovingPlayer && spawnWait < 10f) { yield return (object)new WaitForSeconds(0.1f); spawnWait += 0.1f; } if (_pendingSpawnSlots.Contains(s)) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} became pending during wait, skipping duplicate startup spawn"); continue; } if ((Object)FindActivePlayerBySlot(s) != (Object)null) { Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} became active during wait, skipping duplicate startup spawn"); continue; } _pendingSpawnSlots.Add(s); bool flag = false; _pendingSpawnUseAnimation[s] = flag; Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} spawn mode animated={flag}"); try { CoopManager.Instance.SpawnCoopPlayer(s, flag, -1f); } catch (Exception ex) { _pendingSpawnSlots.Remove(s); _pendingSpawnUseAnimation.Remove(s); Plugin.Log.LogWarning((object)$"RespawnExtraSlots: failed spawning slot {s}: {ex.GetType().Name}: {ex.Message}"); } yield return (object)new WaitForSeconds(1f); } } finally { _startupExtraRespawnDepth = Math.Max(0, _startupExtraRespawnDepth - 1); } } public static void NewRun_Prefix() { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown _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)extraHeartBar.Value != (Object)null) { Object.Destroy((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; _roomEntryDistanceRepositionGraceUntil = 0f; Plugin.Log.LogInfo((object)"NewRun_Prefix: all visual caches cleared"); } public static void CoopManager_Start_Postfix() { CoopManager_Start_Postfix_Inner(); } public static void CoopManager_Start_Postfix_Inner() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) RefreshDesiredExtraSlotsFromActivePlayers(); _spawnedExtraWeaponPodiums = false; _spawnedExtraSpellPodiums = false; _delayedRespawnAfterReviveQueued = false; _nextAutoRespawnHeartbeatAt = 0f; _nextDungeonRoomStabilizeCheckAt = 0f; if (!CoopManager.CoopActive) { return; } if (GameManager.IsDungeon(PlayerFarming.Location) && (Object)CoopManager.Instance != (Object)null && !_p2EntranceGuardRunning) { ((MonoBehaviour)CoopManager.Instance).StartCoroutine(EnsureP2DungeonEntranceNotStuckRoutine()); } int savedExtraSlotCount = _savedExtraSlotCount; if (savedExtraSlotCount > 0) { if (GameManager.IsDungeon(PlayerFarming.Location)) { _dungeonIntroProtectUntil = Time.unscaledTime + 8f; } _podiumExtraCount = savedExtraSlotCount; _savedExtraSlotCount = 0; Plugin.Log.LogInfo((object)$"CoopManager_Start_Postfix: scheduling respawn of {savedExtraSlotCount} extra slot(s)"); ((MonoBehaviour)CoopManager.Instance).StartCoroutine(RespawnExtraSlots(savedExtraSlotCount)); _nextAutoRespawnAttemptAt = Time.unscaledTime + 5f; } else { TryAutoRespawnMissingExtraSlots("CoopManager.Start"); } } private static IEnumerator ApplyVisualDelayed(int slot) { if (slot < 2) { _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); yield break; } PlayerFarming player = null; float waited = 0f; while ((Object)player == (Object)null && waited < 10f) { foreach (PlayerFarming player2 in PlayerFarming.players) { if ((Object)player2 != (Object)null && player2.playerID == slot) { player = player2; break; } } if ((Object)player == (Object)null) { yield return (object)new WaitForSeconds(0.2f); waited += 0.2f; } } if ((Object)player == (Object)null) { Plugin.Log.LogWarning((object)$"ApplyVisualDelayed: gave up waiting for playerID={slot}"); _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); yield break; } bool useAnimatedSpawn = false; _pendingSpawnUseAnimation.TryGetValue(slot, out useAnimatedSpawn); bool inDungeon = GameManager.IsDungeon(PlayerFarming.Location); MeshRenderer meshR = null; if (slot >= 2 && (Object)player.Spine != (Object)null) { meshR = ((Component)player.Spine).GetComponent<MeshRenderer>(); if (!useAnimatedSpawn && (Object)meshR != (Object)null && ((Renderer)meshR).enabled) { ((Renderer)meshR).enabled = false; } } if (slot >= 2 && (Object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length && (!useAnimatedSpawn || !inDungeon)) { ((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } if (slot >= 2 && inDungeon) { float waitLimit = 8f; for (float w = 0f; w < waitLimit; w += 0.1f) { if (!((Object)player.state != (Object)null)) { break; } if (!player.state.LockStateChanges && (int)player.state.CURRENT_STATE != 13 && (int)player.state.CURRENT_STATE != 49) { break; } yield return (object)new WaitForSeconds(0.1f); } yield return null; if ((!useAnimatedSpawn || !inDungeon) && (Object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length) { ((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } } else { for (float waitLimit = 0f; waitLimit < 2f; waitLimit += 0.1f) { if (!((Object)PlayerFarming.Instance == (Object)null) && !((Object)PlayerFarming.Instance.state == (Object)null) && (int)PlayerFarming.Instance.state.CURRENT_STATE != 13) { break; } yield return (object)new WaitForSeconds(0.1f); } yield return null; if (slot < SpawnOffsets.Length && (Object)PlayerFarming.Instance != (Object)null) { ((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot]; } } if (slot >= 2 && (Object)meshR != (Object)null && !((Renderer)meshR).enabled) { ((Renderer)meshR).enabled = true; } if (slot < Plugin.GoatColors.Length && (Object)player.Spine != (Object)null && ((SkeletonRenderer)player.Spine).skeleton != null) { ((SkeletonRenderer)player.Spine).skeleton.R = Plugin.GoatColors[slot].r; ((SkeletonRenderer)player.Spine).skeleton.G = Plugin.GoatColors[slot].g; ((SkeletonRenderer)player.Spine).skeleton.B = Plugin.GoatColors[slot].b; ((SkeletonRenderer)player.Spine).skeleton.A = Plugin.GoatColors[slot].a; } if ((Object)GameManager.GetInstance() != (Object)null) { GameManager.GetInstance().AddToCamera(player.CameraBone, 0.1f); } EnsurePlayerAwakeAfterRespawn(player, $"ApplyVisualDelayed(slot={slot})"); if (slot >= 2) { EnsureExtraSpawnStateUnlocked(player, $"ApplyVisualDelayed(slot={slot})"); } if (_podiumClones.TryGetValue(slot, out var value)) { foreach (Interaction_WeaponSelectionPodium item in value) { if ((Object)item != (Object)null) { EnsurePodiumCloneOwner(item, slot, player, forceInteractable: true); Plugin.Log.LogInfo((object)$"[MP] Refreshed podium clone state for slot {slot} on {((Object)item).name}"); } } } HUD_Hearts val = player.hudHearts; if ((Object)val != (Object)null && (Object)val.playerFarming != (Object)player) { Plugin.Log.LogWarning((object)$"[MP] ApplyVisualDelayed: stale hudHearts owner for slot {slot}, skipping direct faithAmmo init"); val = null; } if ((Object)val == (Object)null && _extraHeartBars.TryGetValue(slot, out var value2) && (Object)value2 != (Object)null) { HUD_Hearts component = value2.GetComponent<HUD_Hearts>(); if ((Object)component != (Object)null && (Object)component.playerFarming == (Object)player) { val = component; player.hudHearts = component; } } if (slot >= 2 && (Object)val != (Object)null) { SyncFaithAmmoForOwner(player, val, $"ApplyVisualDelayed(slot={slot})", forceReinit: true); } _pendingSpawnSlots.Remove(slot); _pendingSpawnUseAnimation.Remove(slot); Plugin.Log.LogInfo((object)$"ApplyVisualDelayed done for slot {slot}"); } private static bool IsDungeonContextLikely() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (GameManager.IsDungeon(PlayerFarming.Location)) { return true; } try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!string.IsNullOrEmpty(name) && name.IndexOf("Dungeon", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } catch { } return false; } private unsafe static bool IsReviveDesyncState(State st) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if ((int)st != 13 && (int)st != 49 && (int)st != 24 && (int)st != 16 && !string.Equals(((object)(*(State*)(&st))/*cast due to .constrained prefix*/).ToString(), "CoopReviving", StringComparison.Ordinal) && (int)st != 63 && (int)st != 35 && (int)st != 56 && (int)st != 41 && (int)st != 42 && (int)st != 32 && (int)st != 33 && (int)st != 52) { return (int)st == 62; } return true; } private unsafe static bool IsHardDownedOrReviveState(State st) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if ((int)st != 63 && !string.Equals(((object)(*(State*)(&st))/*cast due to .constrained prefix*/).ToString(), "CoopReviving", StringComparison.Ordinal) && (int)st != 35 && (int)st != 56 && (int)st != 32 && (int)st != 33 && (int)st != 52) { return (int)st == 62; } return true; } private static bool IsFalseDownedVisualStateForExtra(PlayerFarming player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if ((Object)player == (Object)null || player.isLamb || player.playerID < 2) { return false; } try { if ((Object)player.health == (Object)null || ((Health)player.health).CurrentHP <= 0f) { return false; } } catch { return false; } try { if (Health.team2 != null && Health.team2.Count > 0) { return false; } } catch { } if (IsLikelyLegitDownedState(player)) { return false; } bool flag = false; try { flag = player.IsKnockedOut; } catch { } if (!flag) { try { flag = (Object)player.state != (Object)null && (int)player.state.CURRENT_STATE == 63; } catch { } } return flag; } private static bool IsLikelyLegitDownedState(PlayerFarming player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_003c: 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_0027: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } bool flag = false; try { Interaction_CoopRevive component = ((Component)player).GetComponent<Interaction_CoopRevive>(); flag = (Object)component != (Object)null && ((Behaviour)component).enabled; } catch { } bool flag2 = false; State val = (State)0; try { if ((Object)player.state != (Object)null) { val = player.state.CURRENT_STATE; flag2 = true; } } catch { } if (flag2 && IsHardDownedOrReviveState(val)) { return true; } bool flag3 = false; try { flag3 = player.IsKnockedOut; } catch { } if (!flag3) { return false; } if (flag) { return true; } if (flag2 && (int)val == 49) { return true; } try { if ((Object)player.health != (Object)null && ((Health)player.health).CurrentHP <= 0f) { return true; } } catch { } return false; } private static bool ShouldForceIdleForSpawnUnlock(State st) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return IsHardDownedOrReviveState(st); } private static bool IsLikelyReviveDesyncState(PlayerFarming player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) if ((Object)player == (Object)null || (Object)player.health == (Object)null || ((Health)player.health).CurrentHP <= 0f) { return false; } if (player.IsKnockedOut) { return true; } try { if ((Object)player.state != (Object)null && IsReviveDesyncState(player.state.CURRENT_STATE)) { return true; } } catch { } return false; } private static bool NeedsWakeRecovery(PlayerFarming player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)player == (Object)null || (Object)player.health == (Object)null) { return false; } bool flag = ((Health)player.health)
plugins/COTLMP - Room Size Scaling.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MMBiomeGeneration; using MMRoomGeneration; using Microsoft.CodeAnalysis; 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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace COTLMultiplayerRoomSizeScaling { [BepInDependency(/*Could not decode attribute arguments.*/)] [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, }; } } }
plugins/COTLMP - Tarot Addon.dll
Decompiled 4 days 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.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using MMBiomeGeneration; using Microsoft.CodeAnalysis; 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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace COTLMultiplayerTarotAddon { [BepInDependency(/*Could not decode attribute arguments.*/)] [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 { 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 unsafe 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*)(&reward))/*cast due to .constrained prefix*/).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; } } } private static IEnumerator WrapAndAdvanceTarotQueue(IEnumerator original, Interaction_Tarot interaction, PlayerFarming owner) { while (SafeMoveNext(original)) { yield return original.Current; } if ((Object)(object)interaction == (Object)null) { yield break; } int id = ((Object)interaction).GetInstanceID(); if (!PendingTarotQueueByInstance.TryGetValue(id, out var queue) || queue == null || queue.Count == 0) { CleanupInteractionState(id); yield break; } yield return (object)new WaitForSeconds(0.05f); while (queue.Count > 0) { PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(queue.Dequeue()); if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner))) { if (!((Behaviour)interaction).isActiveAndEnabled) { CleanupInteractionState(id); } else { TriggerNextTarotInteraction(interaction, activePlayerBySlot); } yield break; } } CleanupInteractionState(id); } 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()); } } private static IEnumerator WrapAndAdvanceShrineTarotQueue(IEnumerator original, Shrines shrine, PlayerFarming owner) { while (SafeMoveNext(original)) { yield return original.Current; } if ((Object)(object)shrine == (Object)null) { yield break; } int id = ((Object)shrine).GetInstanceID(); if (!PendingShrineTarotQueueByInstance.TryGetValue(id, out var queue) || queue == null || queue.Count == 0) { CleanupShrineTarotState(id); yield break; } yield return (object)new WaitForSeconds(0.05f); while (queue.Count > 0) { PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(queue.Dequeue()); if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner))) { if (!((Behaviour)shrine).isActiveAndEnabled) { CleanupShrineTarotState(id); } else { TriggerNextShrineTarotInteraction(shrine, activePlayerBySlot); } yield break; } } CleanupShrineTarotState(id); } 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); } private static IEnumerator SuppressedDelayRoutine(float delay) { yield return (object)new WaitForSeconds(0.2f + 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); } } } }
plugins/FlockFocus.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MMTools; using Microsoft.CodeAnalysis; using Rewired; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FlockFocus")] [assembly: AssemblyDescription("Player camera focus controls for Cult of the Lamb")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("FlockFocus")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4A42DA4C-3665-499C-A66C-D5A63C1D3105")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FlockFocus { [BepInPlugin("com.cotl.flockfocus", "FlockFocus", "0.1.0")] public sealed class FlockFocusPlugin : BaseUnityPlugin { public const string PluginGuid = "com.cotl.flockfocus"; public const string PluginName = "FlockFocus"; public const string PluginVersion = "0.1.0"; internal static ManualLogSource Log; internal static FlockFocusController Controller; private Harmony _harmony; public static bool IsFocusActive { get { if ((Object)(object)Controller != (Object)null) { return Controller.ShouldFocus; } return false; } } public static bool ShouldHideIndicatorsNow { get { if (FlockFocusConfig.HidePlayerIndicatorsWhileFocused.Value) { return IsFocusActive; } return false; } } private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_006c: 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_008e: Expected O, but got Unknown //IL_008e: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; FlockFocusConfig.Bind(((BaseUnityPlugin)this).Config); GameObject val = new GameObject("FlockFocusController"); Object.DontDestroyOnLoad((Object)val); Controller = val.AddComponent<FlockFocusController>(); _harmony = new Harmony("com.cotl.flockfocus"); _harmony.Patch((MethodBase)AccessTools.Method(typeof(CameraFollowTarget), "LateUpdate", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CameraFollowTargetPatch), "Prefix", (Type[])null), new HarmonyMethod(typeof(CameraFollowTargetPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); HarmonyMethod val2 = new HarmonyMethod(typeof(PlayerFarmingIndicatorPatch), "Postfix", (Type[])null); val2.priority = 0; _harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"FlockFocus loaded."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal enum ActivationMode { Hold, Toggle } internal enum ReleaseBehavior { ReturnToDefault, StayOnLastTarget, TimedReturn } internal enum ControllerBindingMode { UnityJoystickButtons, RewiredActionIds, RewiredTemplateAxes, RewiredTemplateButtons } internal enum ActivationSource { None, Keyboard, Controller } internal static class FlockFocusConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<ActivationMode> ActivationMode; internal static ConfigEntry<float> FocusZoom; internal static ConfigEntry<bool> DefaultZoomRestore; internal static ConfigEntry<float> PanSpeedMultiplier; internal static ConfigEntry<ReleaseBehavior> ReleaseBehavior; internal static ConfigEntry<float> TimedReturnSeconds; internal static ConfigEntry<bool> AllowKeyboard; internal static ConfigEntry<string> KeyboardKey; internal static ConfigEntry<string> KeyboardModifierKey; internal static ConfigEntry<bool> AllowControllers; internal static ConfigEntry<ControllerBindingMode> ControllerBindingMode; internal static ConfigEntry<bool> ControllerRequireCombo; internal static ConfigEntry<int> ControllerPrimaryButton; internal static ConfigEntry<int> ControllerModifierButton; internal static ConfigEntry<float> ControllerAxisThreshold; internal static ConfigEntry<bool> HidePlayerIndicatorsWhileFocused; internal static ConfigEntry<bool> DisableDuringScriptedCamera; internal static ConfigEntry<bool> DisableDuringConversations; internal static ConfigEntry<bool> DebugLogging; internal static void Bind(ConfigFile config) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown Enabled = config.Bind<bool>("General", "Enabled", true, "Enable FlockFocus camera authority."); ActivationMode = config.Bind<ActivationMode>("Input", "ActivationMode", FlockFocus.ActivationMode.Toggle, "Hold or Toggle."); FocusZoom = config.Bind<float>("Camera", "FocusZoom", 10f, new ConfigDescription("Camera target zoom while focused. Set 0 or less to keep vanilla zoom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 25f), Array.Empty<object>())); DefaultZoomRestore = config.Bind<bool>("Camera", "DefaultZoomRestore", true, "Restore the vanilla target zoom after the focus frame/release."); PanSpeedMultiplier = config.Bind<float>("Camera", "PanSpeedMultiplier", 1.25f, new ConfigDescription("Temporary camera MoveSpeed multiplier while focused.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); ReleaseBehavior = config.Bind<ReleaseBehavior>("Input", "ReleaseBehavior", FlockFocus.ReleaseBehavior.ReturnToDefault, "ReturnToDefault, StayOnLastTarget, or TimedReturn."); TimedReturnSeconds = config.Bind<float>("Input", "TimedReturnSeconds", 1.5f, new ConfigDescription("Seconds to keep focus after release when ReleaseBehavior is TimedReturn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); AllowKeyboard = config.Bind<bool>("Keyboard", "AllowKeyboard", true, "Allow keyboard activation."); KeyboardKey = config.Bind<string>("Keyboard", "KeyboardKey", "LeftAlt", "Unity KeyCode name for keyboard focus. Examples: LeftAlt, Tab, Mouse3."); KeyboardModifierKey = config.Bind<string>("Keyboard", "KeyboardModifierKey", "", "Optional second Unity KeyCode that must be held with KeyboardKey."); AllowControllers = config.Bind<bool>("Controller", "AllowControllers", true, "Allow controller activation."); ControllerBindingMode = config.Bind<ControllerBindingMode>("Controller", "ControllerBindingMode", FlockFocus.ControllerBindingMode.RewiredTemplateAxes, "UnityJoystickButtons reads physical joystick buttons. RewiredActionIds reads game action ids. RewiredTemplateAxes reads standardized gamepad axes such as triggers. RewiredTemplateButtons reads standardized gamepad buttons such as stick clicks."); ControllerRequireCombo = config.Bind<bool>("Controller", "ControllerRequireCombo", false, "Require both controller buttons to be held."); ControllerPrimaryButton = config.Bind<int>("Controller", "ControllerPrimaryButton", 11, "Unity joystick button index, Rewired action id, or Rewired gamepad template axis id. Default 11 is left trigger for RewiredTemplateAxes."); ControllerModifierButton = config.Bind<int>("Controller", "ControllerModifierButton", -1, "Second Unity joystick button index, Rewired action id, or Rewired gamepad template axis id. Use -1 to disable."); ControllerAxisThreshold = config.Bind<float>("Controller", "ControllerAxisThreshold", 0.55f, new ConfigDescription("How far an axis must be pressed for RewiredTemplateAxes mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>())); HidePlayerIndicatorsWhileFocused = config.Bind<bool>("Visuals", "HidePlayerIndicatorsWhileFocused", true, "Temporarily disable the game's Coop Visual Indicators setting while FlockFocus is active."); DisableDuringScriptedCamera = config.Bind<bool>("Safety", "DisableDuringScriptedCamera", true, "Do nothing while the game is using a scripted TargetCamera, photo mode, or letterbox camera sequence."); DisableDuringConversations = config.Bind<bool>("Safety", "DisableDuringConversations", true, "Do nothing while MMConversation is playing."); DebugLogging = config.Bind<bool>("Debug", "DebugLogging", false, "Verbose FlockFocus logs."); } } internal sealed class FlockFocusController : MonoBehaviour { private PlayerFarming _authority; private ActivationSource _source; private bool _active; private float _releaseAt = -1f; private KeyCode _keyboardKey = (KeyCode)308; private KeyCode _keyboardModifier; private string _lastKeyboardKeyRaw; private string _lastKeyboardModifierRaw; private readonly Dictionary<int, bool> _lastControllerHeld = new Dictionary<int, bool>(); internal bool ShouldFocus { get { if (!_active) { return false; } if (_releaseAt > 0f && Time.unscaledTime >= _releaseAt) { ClearAuthority("timed return"); return false; } if (!IsValidPlayer(_authority)) { ClearAuthority("authority missing"); return false; } return true; } } internal PlayerFarming Authority => _authority; private void Update() { if (!FlockFocusConfig.Enabled.Value) { ClearAuthority("disabled"); return; } RefreshKeyboardCodes(); HandleKeyboard(); HandleControllers(); HandleHoldRelease(); } private void LateUpdate() { PlayerFarmingIndicatorPatch.ApplyGlobalSuppression(); } private void RefreshKeyboardCodes() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) if (_lastKeyboardKeyRaw != FlockFocusConfig.KeyboardKey.Value) { _lastKeyboardKeyRaw = FlockFocusConfig.KeyboardKey.Value; _keyboardKey = ParseKeyCode(_lastKeyboardKeyRaw, (KeyCode)308); } if (_lastKeyboardModifierRaw != FlockFocusConfig.KeyboardModifierKey.Value) { _lastKeyboardModifierRaw = FlockFocusConfig.KeyboardModifierKey.Value; _keyboardModifier = ParseKeyCode(_lastKeyboardModifierRaw, (KeyCode)0); } } private void HandleKeyboard() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (FlockFocusConfig.AllowKeyboard.Value && (int)_keyboardKey != 0 && ((int)_keyboardModifier == 0 || Input.GetKey(_keyboardModifier)) && Input.GetKeyDown(_keyboardKey)) { PlayerFarming val = FindKeyboardPlayer(); if ((Object)(object)val == (Object)null) { DebugLog("Keyboard focus ignored: no keyboard-owned player found."); } else { Activate(val, ActivationSource.Keyboard); } } } private void HandleControllers() { if (!FlockFocusConfig.AllowControllers.Value) { return; } foreach (PlayerFarming item in EnumeratePlayers()) { if (IsValidPlayer(item)) { GetControllerComboState(item, out var pressed, out var _, updateEdgeState: true); if (pressed) { Activate(item, ActivationSource.Controller); } } } } private void HandleHoldRelease() { if (_active && FlockFocusConfig.ActivationMode.Value == ActivationMode.Hold) { bool held = false; if (_source == ActivationSource.Keyboard) { held = IsKeyboardHeld(); } else if (_source == ActivationSource.Controller && IsValidPlayer(_authority)) { GetControllerComboState(_authority, out var _, out held, updateEdgeState: false); } if (!held) { Release(); } } } private bool IsKeyboardHeld() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if ((int)_keyboardKey == 0) { return false; } if ((int)_keyboardModifier == 0 || Input.GetKey(_keyboardModifier)) { return Input.GetKey(_keyboardKey); } return false; } private void Activate(PlayerFarming player, ActivationSource source) { if (IsValidPlayer(player)) { if (FlockFocusConfig.ActivationMode.Value == ActivationMode.Toggle && _active && (Object)(object)_authority == (Object)(object)player) { ClearAuthority("toggle off"); return; } _authority = player; _source = source; _active = true; _releaseAt = -1f; DebugLog("Authority -> P" + (player.playerID + 1) + " via " + source); } } private void Release() { switch (FlockFocusConfig.ReleaseBehavior.Value) { case ReleaseBehavior.StayOnLastTarget: _releaseAt = -1f; break; case ReleaseBehavior.TimedReturn: _releaseAt = Time.unscaledTime + Mathf.Max(0f, FlockFocusConfig.TimedReturnSeconds.Value); break; default: ClearAuthority("released"); break; } } private void ClearAuthority(string reason) { if (_active) { DebugLog("Cleared focus: " + reason); } _authority = null; _source = ActivationSource.None; _active = false; _releaseAt = -1f; } private void GetControllerComboState(PlayerFarming player, out bool pressed, out bool held, bool updateEdgeState) { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) pressed = false; held = false; if ((Object)(object)player == (Object)null) { return; } int value = FlockFocusConfig.ControllerPrimaryButton.Value; int value2 = FlockFocusConfig.ControllerModifierButton.Value; bool flag = FlockFocusConfig.ControllerRequireCombo.Value && value2 >= 0; bool flag2; bool flag3; bool flag4; bool flag5; if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateAxes) { flag2 = value >= 0 && SafeGetTemplateAxis(player, value); flag3 = false; flag4 = !flag || SafeGetTemplateAxis(player, value2); flag5 = false; } else if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateButtons) { flag2 = value >= 0 && SafeGetTemplateButton(player, value); flag3 = false; flag4 = !flag || SafeGetTemplateButton(player, value2); flag5 = false; } else if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredActionIds) { Player rewiredPlayer = player.rewiredPlayer; if (rewiredPlayer == null) { return; } flag2 = value >= 0 && SafeGetButton(rewiredPlayer, value); flag3 = value >= 0 && SafeGetButtonDown(rewiredPlayer, value); flag4 = !flag || SafeGetButton(rewiredPlayer, value2); flag5 = flag && SafeGetButtonDown(rewiredPlayer, value2); } else { int joystickNumber = Mathf.Clamp(player.playerID + 1, 1, 8); flag2 = value >= 0 && Input.GetKey(UnityJoystickButton(joystickNumber, value)); flag3 = value >= 0 && Input.GetKeyDown(UnityJoystickButton(joystickNumber, value)); flag4 = !flag || Input.GetKey(UnityJoystickButton(joystickNumber, value2)); flag5 = flag && Input.GetKeyDown(UnityJoystickButton(joystickNumber, value2)); } held = (flag ? (flag2 && flag4) : flag2); if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateAxes || FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateButtons) { int playerID = player.playerID; bool flag6 = _lastControllerHeld.ContainsKey(playerID) && _lastControllerHeld[playerID]; pressed = held && !flag6; if (updateEdgeState) { _lastControllerHeld[playerID] = held; } } else { pressed = ((!flag) ? flag3 : (held && (flag3 || flag5))); } } private static bool SafeGetTemplateAxis(PlayerFarming player, int templateElementId) { try { if ((Object)(object)player == (Object)null || player.rewiredPlayer == null || player.rewiredPlayer.controllers == null) { return false; } float num = Mathf.Clamp01(FlockFocusConfig.ControllerAxisThreshold.Value); Controller firstControllerWithTemplate = player.rewiredPlayer.controllers.GetFirstControllerWithTemplate<IGamepadTemplate>(); if (firstControllerWithTemplate == null) { return false; } IControllerTemplate template = (IControllerTemplate)(object)firstControllerWithTemplate.GetTemplate<IGamepadTemplate>(); IControllerTemplateAxis val = ((template != null) ? template.GetElement<IControllerTemplateAxis>(templateElementId) : null); return val != null && ((IControllerTemplateElement)val).exists && val.value > num; } catch { return false; } } private static bool SafeGetTemplateButton(PlayerFarming player, int templateElementId) { try { if ((Object)(object)player == (Object)null || player.rewiredPlayer == null || player.rewiredPlayer.controllers == null) { return false; } Controller firstControllerWithTemplate = player.rewiredPlayer.controllers.GetFirstControllerWithTemplate<IGamepadTemplate>(); if (firstControllerWithTemplate == null) { return false; } IControllerTemplate template = (IControllerTemplate)(object)firstControllerWithTemplate.GetTemplate<IGamepadTemplate>(); IControllerTemplateButton val = ((template != null) ? template.GetElement<IControllerTemplateButton>(templateElementId) : null); return val != null && ((IControllerTemplateElement)val).exists && val.value; } catch { return false; } } private static bool SafeGetButton(Player player, int actionId) { try { return player != null && player.GetButton(actionId); } catch { return false; } } private static bool SafeGetButtonDown(Player player, int actionId) { try { return player != null && player.GetButtonDown(actionId); } catch { return false; } } private static KeyCode UnityJoystickButton(int joystickNumber, int button) { return (KeyCode)(350 + (joystickNumber - 1) * 20 + button); } private unsafe static KeyCode ParseKeyCode(string raw, KeyCode fallback) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(raw)) { return (KeyCode)0; } try { return (KeyCode)Enum.Parse(typeof(KeyCode), raw.Trim(), ignoreCase: true); } catch { FlockFocusPlugin.Log.LogWarning((object)("Invalid KeyCode '" + raw + "', using " + ((object)(*(KeyCode*)(&fallback))/*cast due to .constrained prefix*/).ToString())); return fallback; } } private static PlayerFarming FindKeyboardPlayer() { foreach (PlayerFarming item in EnumeratePlayers()) { if (!IsValidPlayer(item) || item.rewiredPlayer == null) { continue; } try { if (item.rewiredPlayer.controllers != null && item.rewiredPlayer.controllers.hasKeyboard) { return item; } } catch { return item; } } if (!IsValidPlayer(PlayerFarming.Instance)) { return null; } return PlayerFarming.Instance; } internal static IEnumerable<PlayerFarming> EnumeratePlayers() { if (PlayerFarming.players != null) { for (int i = 0; i < PlayerFarming.players.Count; i++) { PlayerFarming val = PlayerFarming.players[i]; if ((Object)(object)val != (Object)null) { yield return val; } } } else if ((Object)(object)PlayerFarming.Instance != (Object)null) { yield return PlayerFarming.Instance; } } internal static bool IsValidPlayer(PlayerFarming player) { if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null && ((Component)player).gameObject.activeInHierarchy) { if (!((Object)(object)player.CameraBone != (Object)null)) { return (Object)(object)((Component)player).transform != (Object)null; } return true; } return false; } internal static void DebugLog(string message) { if (FlockFocusConfig.DebugLogging.Value && FlockFocusPlugin.Log != null) { FlockFocusPlugin.Log.LogInfo((object)("[FlockFocus] " + message)); } } } internal sealed class CameraFocusPatchState { internal List<Target> Targets; internal float TargetDistance; internal float MoveSpeed; internal bool HasState; } internal static class CameraFollowTargetPatch { public static void Prefix(CameraFollowTarget __instance, ref CameraFocusPatchState __state) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown __state = null; FlockFocusController controller = FlockFocusPlugin.Controller; if ((Object)(object)controller == (Object)null || !controller.ShouldFocus || (Object)(object)__instance == (Object)null || !CanTouchCamera(__instance)) { return; } PlayerFarming authority = controller.Authority; if (!FlockFocusController.IsValidPlayer(authority)) { return; } GameObject val = (((Object)(object)authority.CameraBone != (Object)null) ? authority.CameraBone : ((Component)authority).gameObject); if (!((Object)(object)val == (Object)null)) { __state = new CameraFocusPatchState { Targets = ((__instance.targets != null) ? new List<Target>(__instance.targets) : null), TargetDistance = __instance.targetDistance, MoveSpeed = __instance.MoveSpeed, HasState = true }; __instance.targets = new List<Target> { new Target(val, 1f) }; if (FlockFocusConfig.FocusZoom.Value > 0f) { __instance.targetDistance = FlockFocusConfig.FocusZoom.Value; } __instance.MoveSpeed = Mathf.Max(0.1f, __instance.MoveSpeed * FlockFocusConfig.PanSpeedMultiplier.Value); } } public static void Postfix(CameraFollowTarget __instance, CameraFocusPatchState __state) { if (!((Object)(object)__instance == (Object)null) && __state != null && __state.HasState) { __instance.targets = __state.Targets ?? new List<Target>(); if (FlockFocusConfig.DefaultZoomRestore.Value) { __instance.targetDistance = __state.TargetDistance; } __instance.MoveSpeed = __state.MoveSpeed; } } private static bool CanTouchCamera(CameraFollowTarget camera) { if ((Object)(object)camera.TargetCamera != (Object)null && FlockFocusConfig.DisableDuringScriptedCamera.Value) { return false; } if (FlockFocusConfig.DisableDuringScriptedCamera.Value) { try { if (PhotoModeManager.PhotoModeActive || LetterBox.IsPlaying) { return false; } } catch { return false; } } if (FlockFocusConfig.DisableDuringConversations.Value) { try { if (MMConversation.isPlaying) { return false; } } catch { return false; } } return Time.timeScale > 0f; } } internal static class PlayerFarmingIndicatorPatch { private static bool _overrideActive; private static bool _previousCoopVisualIndicators; public static void Postfix(PlayerFarming __instance) { ApplyGlobalSuppression(); } internal static void ApplyGlobalSuppression() { if (!ShouldSuppress()) { RestoreCoopVisualIndicators(); } else if (SettingsManager.Settings != null && SettingsManager.Settings.Accessibility != null) { if (!_overrideActive) { _previousCoopVisualIndicators = SettingsManager.Settings.Accessibility.CoopVisualIndicators; _overrideActive = true; } SettingsManager.Settings.Accessibility.CoopVisualIndicators = false; } } private static bool ShouldSuppress() { FlockFocusController controller = FlockFocusPlugin.Controller; if (FlockFocusConfig.HidePlayerIndicatorsWhileFocused.Value && (Object)(object)controller != (Object)null) { return controller.ShouldFocus; } return false; } private static void RestoreCoopVisualIndicators() { if (_overrideActive) { if (SettingsManager.Settings != null && SettingsManager.Settings.Accessibility != null) { SettingsManager.Settings.Accessibility.CoopVisualIndicators = _previousCoopVisualIndicators; } _overrideActive = false; } } } }
plugins/FlockFocusCOTLMPCompat.dll
Decompiled 4 days agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FlockFocusCOTLMPCompat")] [assembly: AssemblyDescription("COTLMP compatibility patch for FlockFocus.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("FlockFocusCOTLMPCompat")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("5865765f-2797-4117-bb83-f07d06f5d1d4")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FlockFocusCOTLMPCompat { [BepInPlugin("com.cotl.flockfocus.cotlmpcompat", "FlockFocus COTLMP Compat", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class FlockFocusCOTLMPCompatPlugin : BaseUnityPlugin { public const string PluginGuid = "com.cotl.flockfocus.cotlmpcompat"; public const string PluginName = "FlockFocus COTLMP Compat"; public const string PluginVersion = "0.1.0"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Type type = ResolveCOTLMPPatchesType(); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "HUD_Update", (Type[])null, (Type[])null) : null); if (methodInfo == null) { Log.LogInfo((object)"COTLMP HUD_Update was not found. Compat patch is idle."); return; } COTLMPHudUpdatePatch.Bind(type); _harmony = new Harmony("com.cotl.flockfocus.cotlmpcompat"); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(COTLMPHudUpdatePatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"FlockFocus COTLMP compat loaded."); } private static Type ResolveCOTLMPPatchesType() { Type type = AccessTools.TypeByName("COTLMultiplayerExpansion.Patches"); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type2 = assemblies[i].GetType("COTLMultiplayerExpansion.Patches", throwOnError: false); if (type2 != null) { return type2; } } return null; } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class COTLMPHudUpdatePatch { private static readonly string[] WidgetFieldNames = new string[3] { "_p2Widget", "_p3Widget", "_p4Widget" }; private static PropertyInfo _shouldHideIndicatorsNow; private static FieldInfo[] _widgetFields = new FieldInfo[0]; internal static void Bind(Type cotlmpPatchesType) { Type type = AccessTools.TypeByName("FlockFocus.FlockFocusPlugin"); _shouldHideIndicatorsNow = ((type != null) ? AccessTools.Property(type, "ShouldHideIndicatorsNow") : null); if (cotlmpPatchesType == null) { _widgetFields = new FieldInfo[0]; return; } FieldInfo[] array = new FieldInfo[WidgetFieldNames.Length]; for (int i = 0; i < WidgetFieldNames.Length; i++) { array[i] = AccessTools.Field(cotlmpPatchesType, WidgetFieldNames[i]); } _widgetFields = array; } public static bool Prefix() { if (!ShouldSuppressCOTLMPWidgets()) { return true; } HideExistingWidgets(); return false; } private static bool ShouldSuppressCOTLMPWidgets() { try { return _shouldHideIndicatorsNow != null && (bool)_shouldHideIndicatorsNow.GetValue(null, null); } catch (Exception ex) { ManualLogSource log = FlockFocusCOTLMPCompatPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to query FlockFocus indicator state: " + ex.Message)); } return false; } } private static void HideExistingWidgets() { for (int i = 0; i < _widgetFields.Length; i++) { FieldInfo fieldInfo = _widgetFields[i]; if (!(fieldInfo == null)) { object? value = fieldInfo.GetValue(null); Component val = (Component)((value is Component) ? value : null); if ((Object)(object)val != (Object)null && (Object)(object)val.gameObject != (Object)null && val.gameObject.activeSelf) { val.gameObject.SetActive(false); } } } } } }