Decompiled source of COTLMP 8 Player Expansion v2.34.0
BepInEx/plugins/KOMods-COTLMP_8_Player_Expansion/COTLMP - Enemy Spawn Scaling.dll
Decompiled 18 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary3")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary3")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b9a6c547-5fae-4c80-847d-8da4b933a5dd")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerEnemySpawnScaling; internal static class EnemySpawnPatches { private sealed class SpawnScaleCloneMarker : MonoBehaviour { public int RoomToken; } private const string DifficultyPlayerPrefsKey = "COTLMP.8PDifficulty"; private static bool _spawningScaledCopy = false; 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 Type DungeonLeaderMechanicsType = AccessTools.TypeByName("DungeonLeaderMechanics"); private static readonly Type DemonType = AccessTools.TypeByName("Demon"); private static readonly Type FriendlyEnemyType = AccessTools.TypeByName("FriendlyEnemy"); private static int GetActualPlayerCount() { int num = 0; if (PlayerFarming.players != null) { foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null && ((Component)player).gameObject.activeInHierarchy) { num++; } } } return Mathf.Clamp(Mathf.Max(num, PlayerFarming.playersCount), 1, 8); } public static void EnemyRoundsBase_RoundStarted_Prefix(int round, int totalRounds) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) bool flag = round <= 1 || _roomToken == 0; if (flag) { _roomToken++; _scaledEncounterInstanceIds.Clear(); _scaledSpawnInstanceIds.Clear(); _scaledBossHealthIds.Clear(); } _extraSpawnCarry = 0f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] round start {round}/{totalRounds} token={_roomToken} newRoomToken={flag} (players={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } public static void EnemySpawner_Create_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) LogHookSeen("Create"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] Create"); TrySpawnScaledCopies(Position, Parent, Spawn, "Create"); } public static void EnemySpawner_CreateWithAndInit_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) LogHookSeen("CreateWithInit"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] CreateWithInit"); TrySpawnScaledCopies(Position, Parent, Spawn, "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_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_025c: 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_00e2: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null) { return; } if (!ShouldScaleDungeonSpawns(out var blockedReason)) { LogSkip("encounter_gate=" + blockedReason); return; } int num = (((Object)(object)RoomManager.r != (Object)null) ? ((object)RoomManager.r).GetHashCode() : 0); int item = (((Object)((Component)__instance).gameObject).GetInstanceID() * 397) ^ num; if (!_scaledEncounterInstanceIds.Add(item)) { 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>(); UnitObject[] array = componentsInChildren; foreach (UnitObject val in array) { 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) && !IsHealthScaleOnlyEnemy(((Component)val).gameObject) && !((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 num2 = 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"; val3.AddComponent<SpawnScaleCloneMarker>().RoomToken = _roomToken; 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); num2++; } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] room clone failed: " + ex.Message)); } } } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] room-scale eligible={list.Count} spawnedExtra={num2} players={GetActualPlayerCount()} loc={PlayerFarming.Location}"); } private static void TrySpawnScaledCopies(Vector3 position, Transform parent, GameObject spawnPrefab, string source) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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(); string blockedReason; if (_scaledSpawnInstanceIds.Remove(instanceID)) { LogReject("scaled_copy_source"); } else if (IsHealthScaleOnlyEnemy(spawnPrefab)) { LogReject("boss_health_owned_by_main"); } else if ((Object)(object)EnemyRoundsBase.Instance == (Object)null) { LogSkip("static_room_owned_by_encounter"); } else if (!ShouldScaleDungeonSpawns(out 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); val.AddComponent<SpawnScaleCloneMarker>().RoomToken = _roomToken; _scaledSpawnInstanceIds.Add(((Object)val).GetInstanceID()); val.SetActive(false); EnemySpawner.CreateWithAndInitInstantiatedEnemy(nearbySpawnPoint, parent, val); } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] source={source} players={GetActualPlayerCount()} 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 (GetActualPlayerCount() <= 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 + 0.5f * (float)Mathf.Max(0, GetActualPlayerCount() - 2); 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={GetActualPlayerCount()}"); 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_0045: 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={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogHookSeen(string hook) { //IL_0045: 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={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogReject(string reason) { //IL_0045: 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={GetActualPlayerCount()}, 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() { int actualPlayerCount = GetActualPlayerCount(); if (actualPlayerCount <= 2) { return 0; } float num = GetTargetEnemyMultiplier(actualPlayerCount) - 1f; _extraSpawnCarry += num; int num2 = Mathf.FloorToInt(_extraSpawnCarry + 0.0001f); if (num2 > 0) { _extraSpawnCarry -= num2; } return Mathf.Clamp(num2, 0, 4); } private static int GetRoomExtraCount(int baseCount) { if (baseCount <= 0) { return 0; } float targetEnemyMultiplier = GetTargetEnemyMultiplier(GetActualPlayerCount()); return Mathf.Clamp(Mathf.CeilToInt((float)baseCount * targetEnemyMultiplier) - baseCount, 0, 24); } internal static float GetTargetEnemyMultiplier(int playerCount) { if (playerCount <= 2) { return 1f; } switch (Mathf.Clamp(PlayerPrefs.GetInt("COTLMP.8PDifficulty", 1), 0, 3)) { case 0: if (playerCount <= 4) { if (playerCount != 3) { return 1.25f; } return 1.1f; } return Mathf.Lerp(1.5f, 2f, Mathf.Clamp01((float)(playerCount - 5) / 3f)); case 1: if (playerCount <= 4) { if (playerCount != 3) { return 1.6f; } return 1.25f; } return Mathf.Lerp(2f, 3f, Mathf.Clamp01((float)(playerCount - 5) / 3f)); case 2: return 1f + 0.5f * (float)(playerCount - 2); default: return 1.5f + 0.5f * (float)(playerCount - 2); } } internal static string GetDifficultyName() { return Mathf.Clamp(PlayerPrefs.GetInt("COTLMP.8PDifficulty", 1), 0, 3) switch { 0 => "EASY", 2 => "HARD", 3 => "EXTREME", _ => "NORMAL", }; } 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; } } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.cotlmp.enemyspawnscaling", "COTL Multiplayer Enemy Spawn Scaling 8P", "0.7.0")] public sealed class EnemySpawnScalingPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry<bool> DuplicateMiniBosses; internal static ConfigEntry<float> EnemyCountMultiplier3P; internal static ConfigEntry<float> EnemyCountMultiplier4P; internal static ConfigEntry<float> EnemyCountMultiplier8P; private Harmony _harmony; private bool _heartbeatLogged; private void Awake() { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown 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."); EnemyCountMultiplier3P = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "EnemyCountMultiplier3P", 1.15f, "Restrained total regular-enemy count with 3 players. TEST8.7 caps this at 1.15."); EnemyCountMultiplier4P = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "EnemyCountMultiplier4P", 1.25f, "Restrained total regular-enemy count with 4 players. TEST8.7 caps this at 1.25."); EnemyCountMultiplier8P = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "EnemyCountMultiplier8P", 1.75f, "Restrained total regular-enemy count with 8 players. Players 5-7 interpolate up to 1.75."); EnemyCountMultiplier3P.Value = Mathf.Clamp(EnemyCountMultiplier3P.Value, 1f, 1.15f); EnemyCountMultiplier4P.Value = Mathf.Clamp(EnemyCountMultiplier4P.Value, 1f, 1.25f); EnemyCountMultiplier8P.Value = Mathf.Clamp(EnemyCountMultiplier8P.Value, 1f, 1.75f); ((BaseUnityPlugin)this).Config.Save(); _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()"); Log.LogInfo((object)"Enemy spawn scaling addon loaded."); Log.LogInfo((object)"[SpawnScale] Ready v0.7.0-8P TEST8.8 DIFFICULTY SELECTOR"); Log.LogInfo((object)("[SpawnScale] Config DuplicateMiniBosses=" + DuplicateMiniBosses.Value)); Log.LogInfo((object)$"[SpawnScale] 8P Difficulty={EnemySpawnPatches.GetDifficultyName()} targets: 5P x{EnemySpawnPatches.GetTargetEnemyMultiplier(5):0.00}, 8P x{EnemySpawnPatches.GetTargetEnemyMultiplier(8):0.00}"); Log.LogInfo((object)"[SpawnScale] Vanilla enemy rounds and room completion remain authoritative."); Log.LogWarning((object)"[SPAWNSCALE_BOOT_031]"); Debug.Log((object)"[SPAWNSCALE_BOOT_031]"); } private void Update() { if (!_heartbeatLogged) { _heartbeatLogged = true; Log.LogWarning((object)"[SPAWNSCALE_HEARTBEAT_031]"); Debug.Log((object)"[SPAWNSCALE_HEARTBEAT_031]"); } } 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))); } }
BepInEx/plugins/KOMods-COTLMP_8_Player_Expansion/COTLMP - Multiplayer Expansion.dll
Decompiled 18 hours 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.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using DG.Tweening; using DG.Tweening.Core; using HarmonyLib; using Lamb.UI; using Lamb.UI.DeathScreen; using Lamb.UI.FollowerInteractionWheel; using Lamb.UI.KitchenMenu; using Lamb.UI.PauseMenu; using MMBiomeGeneration; using MMTools; using Rewired; using Spine; using Spine.Unity; using TMPro; using Unify; using Unify.Input; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using src.UINavigator; [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: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerExpansion; internal sealed class BuildingTransitionVote : MonoBehaviour { private const float BaseCountdownSeconds = 10f; private static readonly MethodInfo BuildingTriggerMethod = AccessTools.Method(typeof(EnterBuilding), "OnTriggerEnter2D", new Type[1] { typeof(Collider2D) }, (Type[])null); private static readonly FieldInfo TriggeredField = AccessTools.Field(typeof(EnterBuilding), "_triggered"); private static readonly FieldInfo DestinationField = AccessTools.Field(typeof(EnterBuilding), "Destination"); private static BuildingTransitionVote _instance; private static EnterBuilding _replayingBuilding; private EnterBuilding _selectedBuilding; private Collider2D _initiatorCollider; private Vector3 _roomAnchor; private float _remaining; private float _nextBlockedLogAt; private int _initiatorSlot = -1; private bool _active; private readonly HashSet<int> _enteredPlayers = new HashSet<int>(); private readonly HashSet<int> _deniedPlayers = new HashSet<int>(); private Canvas _canvas; private TextMeshProUGUI _titleText; private TextMeshProUGUI _statusText; private TextMeshProUGUI _timerText; private RectTransform _progressFill; private Button _denyButton; internal static bool HasActiveVote { get { if ((Object)(object)_instance != (Object)null) { return _instance._active; } return false; } } internal static bool ShouldBlockBuildingTrigger(EnterBuilding building, Collider2D collision) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)building == (Object)null || (Object)(object)collision == (Object)null || !CoopManager.CoopActive || (Object)(object)_replayingBuilding == (Object)(object)building) { return false; } PlayerFarming val = ResolvePlayer(collision); if ((Object)(object)val == (Object)null || CountActivePlayers() <= 1) { return false; } BuildingTransitionVote buildingTransitionVote = EnsureRunner(); if ((Object)(object)buildingTransitionVote == (Object)null) { return false; } if (DoorTransitionVote.HasActiveVote) { buildingTransitionVote.MovePlayerInside(building, val, ((Component)val).transform.position); return true; } return buildingTransitionVote.HandleBuildingTrigger(building, collision, val); } internal static bool BlockCompetingTrigger(Component transition, Collider2D collision) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!HasActiveVote) { return false; } PlayerFarming val = ResolvePlayer(collision); if ((Object)(object)val != (Object)null) { _instance.MovePlayerInside(_instance._selectedBuilding, val, _instance._roomAnchor); } if (Time.unscaledTime >= _instance._nextBlockedLogAt) { _instance._nextBlockedLogAt = Time.unscaledTime + 1f; Plugin.Log.LogInfo((object)"[RoomVote] Blocked another transition while the temple/building vote was active."); } return true; } internal static void EmergencyCancel() { if (HasActiveVote) { _instance.CancelVote("emergency exit"); } } private static BuildingTransitionVote EnsureRunner() { if ((Object)(object)_instance != (Object)null) { return _instance; } if ((Object)(object)CoopManager.Instance == (Object)null || (Object)(object)((Component)CoopManager.Instance).gameObject == (Object)null) { return null; } _instance = ((Component)CoopManager.Instance).gameObject.GetComponent<BuildingTransitionVote>(); if ((Object)(object)_instance == (Object)null) { _instance = ((Component)CoopManager.Instance).gameObject.AddComponent<BuildingTransitionVote>(); } return _instance; } private void OnDestroy() { TransitionVotePlayerState.RestoreAll(); DestroyVoteUI(); if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } private bool HandleBuildingTrigger(EnterBuilding building, Collider2D collision, PlayerFarming player) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (MMTransition.IsPlaying) { MovePlayerInside(building, player, ((Component)player).transform.position); return true; } if (IsBlockingCinematicActive()) { MovePlayerInside(building, player, ((Component)player).transform.position); return true; } if (GetTriggered(building)) { return false; } if (!_active) { if (TransitionVotePlayerState.IsInitiatorBlocked(player, out var _)) { MovePlayerInside(building, player, ((Component)player).transform.position); return true; } BeginVote(building, collision, player); return true; } if ((Object)(object)_selectedBuilding == (Object)(object)building) { RegisterEntry(player); EvaluateFullyResolvedVote(); return true; } MovePlayerInside(building, player, _roomAnchor); if (Time.unscaledTime >= _nextBlockedLogAt) { _nextBlockedLogAt = Time.unscaledTime + 1f; Plugin.Log.LogInfo((object)"[RoomVote] Blocked a different building entrance while a vote was active."); } return true; } private static bool IsBlockingCinematicActive() { try { if (LetterBox.IsPlaying) { return true; } return MMConversation.isPlaying && !MMConversation.isBark; } catch { return false; } } private void BeginVote(EnterBuilding building, Collider2D collision, PlayerFarming initiator) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) _selectedBuilding = building; _initiatorCollider = collision; _roomAnchor = ((Component)initiator).transform.position; _initiatorSlot = Mathf.Clamp(initiator.playerID, 0, 7); _remaining = 10f; _enteredPlayers.Clear(); _deniedPlayers.Clear(); _active = true; RegisterEntry(initiator); CreateVoteUI(); RefreshVoteUI(); Plugin.Log.LogInfo((object)$"[RoomVote] P{initiator.playerID + 1} started {GetBuildingLabel(building)} vote ({CountActivePlayers()} active players)."); } private void RegisterEntry(PlayerFarming player) { if (!((Object)(object)player == (Object)null)) { int num = Mathf.Clamp(player.playerID, 0, 7); bool num2 = _enteredPlayers.Add(num); _deniedPlayers.Remove(num); if (num2) { TransitionVotePlayerState.Hide(player); Plugin.Log.LogInfo((object)$"[RoomVote] P{num + 1} entered the selected building door; countdown speed is now x{GetCountdownSpeed():0.0}."); } } } private void Update() { if (!_active) { return; } if ((Object)(object)_selectedBuilding == (Object)null || (Object)(object)((Component)_selectedBuilding).gameObject == (Object)null || !((Component)_selectedBuilding).gameObject.activeInHierarchy) { CancelVote("selected building door disappeared"); return; } if (MMTransition.IsPlaying) { ClearVoteState(restoreHiddenPlayers: true); return; } EvaluateFullyResolvedVote(); if (!_active) { return; } PollDenyInputs(); if (_active) { _remaining -= Time.unscaledDeltaTime * GetCountdownSpeed(); RefreshVoteUI(); if (_remaining <= 0f) { ResolveTimedVote(); } } } private void PollDenyInputs() { foreach (PlayerFarming activePlayer in GetActivePlayers()) { if (_enteredPlayers.Contains(activePlayer.playerID)) { continue; } try { if (InputManager.Gameplay.GetHeavyAttackButtonDown(activePlayer)) { ToggleDeny(activePlayer); if (!_active) { break; } } } catch { } } } private void ToggleDeny(PlayerFarming player) { if (!_active || (Object)(object)player == (Object)null) { return; } int num = Mathf.Clamp(player.playerID, 0, 7); if (!_enteredPlayers.Contains(num)) { if (_deniedPlayers.Contains(num)) { _deniedPlayers.Remove(num); Plugin.Log.LogInfo((object)$"[RoomVote] P{num + 1} withdrew DENY ENTRY."); } else { _deniedPlayers.Add(num); Plugin.Log.LogInfo((object)$"[RoomVote] P{num + 1} voted DENY ENTRY."); } RefreshVoteUI(); EvaluateFullyResolvedVote(); } } private void EvaluateFullyResolvedVote() { if (_active) { int num = CountActivePlayers(); int num2 = CountActiveVotes(_enteredPlayers); int num3 = CountActiveVotes(_deniedPlayers); if (num2 >= num) { CommitVote("everyone entered"); } else if (num3 > num2) { CancelVote("DENY votes exceeded entered players", applyInitiatorCooldown: true); } else if (num2 + num3 >= num) { CommitVote("all players resolved without DENY exceeding entry"); } } } private void ResolveTimedVote() { int num = CountActiveVotes(_enteredPlayers); if (CountActiveVotes(_deniedPlayers) > num) { CancelVote("timer expired with DENY exceeding entry", applyInitiatorCooldown: true); } else { CommitVote("timer expired without DENY exceeding entry"); } } private void CommitVote(string reason) { if (!_active) { return; } EnterBuilding selectedBuilding = _selectedBuilding; Collider2D val = FindReplayCollider(); int num = CountActiveVotes(_enteredPlayers); int num2 = CountActiveVotes(_deniedPlayers); Plugin.Log.LogInfo((object)$"[RoomVote] BUILDING ENTRY APPROVED ({num} entered, {num2} denied): {reason}."); TransitionVotePlayerState.PrepareForApprovedTransition(); ClearVoteState(); if ((Object)(object)selectedBuilding == (Object)null || (Object)(object)val == (Object)null || BuildingTriggerMethod == null) { Plugin.Log.LogWarning((object)"[RoomVote] Approved building entry could not replay safely."); SetTriggered(selectedBuilding, value: false); TransitionVotePlayerState.RestoreAll(); return; } try { _replayingBuilding = selectedBuilding; BuildingTriggerMethod.Invoke(selectedBuilding, new object[1] { val }); } catch (Exception exception) { SetTriggered(selectedBuilding, value: false); try { PlayerFarming.SetCollidersActive(true, false); } catch { } Plugin.Log.LogWarning((object)("[RoomVote] Approved building replay failed: " + GetInnermostMessage(exception))); TransitionVotePlayerState.RestoreAll(); } finally { _replayingBuilding = null; } ((MonoBehaviour)this).StartCoroutine(RestoreHiddenPlayersAfterTransition()); } private void CancelVote(string reason, bool applyInitiatorCooldown = false) { if (_active) { int num = CountActiveVotes(_enteredPlayers); int num2 = CountActiveVotes(_deniedPlayers); EnterBuilding building = _selectedBuilding; int initiatorSlot = _initiatorSlot; Plugin.Log.LogInfo((object)$"[RoomVote] BUILDING ENTRY DENIED ({num} entered, {num2} denied): {reason}."); SetTriggered(building, value: false); TransitionVotePlayerState.RestoreAndSpread(delegate(PlayerFarming player) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) MovePlayerInside(building, player, _roomAnchor); }); if (applyInitiatorCooldown) { TransitionVotePlayerState.BlockDeniedInitiator(initiatorSlot); } ClearVoteState(); } } private void ClearVoteState(bool restoreHiddenPlayers = false) { if (restoreHiddenPlayers) { TransitionVotePlayerState.RestoreAll(); } _active = false; _selectedBuilding = null; _initiatorCollider = null; _initiatorSlot = -1; _enteredPlayers.Clear(); _deniedPlayers.Clear(); DestroyVoteUI(); } private IEnumerator RestoreHiddenPlayersAfterTransition() { float transitionStartDeadline = Time.unscaledTime + 0.6f; while (!MMTransition.IsPlaying && Time.unscaledTime < transitionStartDeadline) { yield return null; } float transitionEndDeadline = Time.unscaledTime + 12f; while (MMTransition.IsPlaying && Time.unscaledTime < transitionEndDeadline) { yield return null; } TransitionVotePlayerState.RestoreAll(); } private Collider2D FindReplayCollider() { if ((Object)(object)_initiatorCollider != (Object)null && (Object)(object)ResolvePlayer(_initiatorCollider) != (Object)null) { return _initiatorCollider; } foreach (PlayerFarming activePlayer in GetActivePlayers()) { Collider2D component = ((Component)activePlayer).GetComponent<Collider2D>(); if ((Object)(object)component != (Object)null) { return component; } } return null; } private float GetCountdownSpeed() { int num = Mathf.Max(1, CountActiveVotes(_enteredPlayers)); return 1f + 0.5f * (float)(num - 1); } private static int CountActiveVotes(HashSet<int> votes) { int num = 0; foreach (PlayerFarming activePlayer in GetActivePlayers()) { if (votes.Contains(activePlayer.playerID)) { num++; } } return num; } private static int CountActivePlayers() { return GetActivePlayers().Count; } private static List<PlayerFarming> GetActivePlayers() { List<PlayerFarming> list = new List<PlayerFarming>(); HashSet<int> hashSet = new HashSet<int>(); if (PlayerFarming.players == null) { return list; } foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && player.playerID >= 0 && player.playerID < 8 && (Object)(object)((Component)player).gameObject != (Object)null && ((Component)player).gameObject.activeInHierarchy && hashSet.Add(player.playerID)) { list.Add(player); } } return list; } private static PlayerFarming ResolvePlayer(Collider2D collision) { if ((Object)(object)collision == (Object)null) { return null; } PlayerFarming val = ((Component)collision).gameObject.GetComponent<PlayerFarming>(); if ((Object)(object)val == (Object)null) { val = ((Component)collision).gameObject.GetComponentInParent<PlayerFarming>(); } return val; } private void MovePlayerInside(EnterBuilding building, PlayerFarming player, Vector3 roomAnchor) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Invalid comparison between Unknown and I4 //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Invalid comparison between Unknown and I4 if ((Object)(object)building == (Object)null || (Object)(object)player == (Object)null) { return; } try { Vector3 val = roomAnchor - ((Component)building).transform.position; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { val = ((Component)player).transform.position - ((Component)building).transform.position; } if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { val = Vector3.down; } val.z = 0f; Vector3 normalized = ((Vector3)(ref val)).normalized; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(0f - normalized.y, normalized.x, 0f); float num = ((float)Mathf.Clamp(player.playerID, 0, 7) - 3.5f) * 0.28f; ((Component)player).transform.position = ((Component)building).transform.position + normalized * 2.25f + val2 * num; if ((Object)(object)player.state != (Object)null) { player.state.LockStateChanges = false; if ((int)player.state.CURRENT_STATE == 13 || (int)player.state.CURRENT_STATE == 24) { player.state.CURRENT_STATE = (State)0; } } } catch { } } private static bool GetTriggered(EnterBuilding building) { try { return (Object)(object)building != (Object)null && TriggeredField != null && Convert.ToBoolean(TriggeredField.GetValue(building)); } catch { return false; } } private static void SetTriggered(EnterBuilding building, bool value) { try { if ((Object)(object)building != (Object)null && TriggeredField != null) { TriggeredField.SetValue(building, value); } } catch { } } private void CreateVoteUI() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_020a: 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_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Expected O, but got Unknown //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Expected O, but got Unknown //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04b4: Expected O, but got Unknown //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0518: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_0594: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_05ab: Expected O, but got Unknown //IL_05bd: Unknown result type (might be due to invalid IL or missing references) //IL_05c7: Expected O, but got Unknown //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_05e2: Unknown result type (might be due to invalid IL or missing references) //IL_0605: Unknown result type (might be due to invalid IL or missing references) DestroyVoteUI(); GameObject val = new GameObject("COTLMP Building Vote Canvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); val.transform.SetParent(((Component)this).transform, false); _canvas = val.GetComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 32700; CanvasScaler component = val.GetComponent<CanvasScaler>(); component.uiScaleMode = (ScaleMode)1; component.referenceResolution = new Vector2(1920f, 1080f); component.matchWidthOrHeight = 0.5f; GameObject val2 = new GameObject("Building Vote Panel", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent<RectTransform>(); component2.anchorMin = new Vector2(1f, 0f); component2.anchorMax = new Vector2(1f, 0f); component2.pivot = new Vector2(1f, 0f); component2.anchoredPosition = new Vector2(-14f, 14f); component2.sizeDelta = new Vector2(350f, 101f); Image component3 = val2.GetComponent<Image>(); component3.sprite = TransitionVoteSkin.GetPanelSprite(); component3.preserveAspect = true; ((Graphic)component3).color = (((Object)(object)component3.sprite != (Object)null) ? new Color(1f, 1f, 1f, 0.88f) : new Color(0.018f, 0.015f, 0.013f, 0.76f)); ((Graphic)component3).raycastTarget = false; _titleText = CreateText(val2.transform, "Vote Title", new Vector2(67f, -12f), new Vector2(194f, 29f), 20f, (TextAlignmentOptions)513, new Color(0.96f, 0.9f, 0.74f, 1f)); _timerText = CreateText(val2.transform, "Vote Timer", new Vector2(278f, -12f), new Vector2(52f, 29f), 21f, (TextAlignmentOptions)516, new Color(1f, 0.8f, 0.24f, 1f)); _statusText = CreateText(val2.transform, "Vote Status", new Vector2(67f, -43f), new Vector2(260f, 20f), 14f, (TextAlignmentOptions)513, new Color(0.88f, 0.86f, 0.78f, 1f)); GameObject val3 = new GameObject("Vote Timer Track", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val2.transform, false); RectTransform component4 = val3.GetComponent<RectTransform>(); component4.anchorMin = new Vector2(0f, 1f); component4.anchorMax = new Vector2(0f, 1f); component4.pivot = new Vector2(0f, 1f); component4.anchoredPosition = new Vector2(67f, -78f); component4.sizeDelta = new Vector2(104f, 3f); ((Graphic)val3.GetComponent<Image>()).color = new Color(0.21f, 0.17f, 0.14f, 0.72f); GameObject val4 = new GameObject("Vote Timer Fill", new Type[2] { typeof(RectTransform), typeof(Image) }); val4.transform.SetParent(val3.transform, false); _progressFill = val4.GetComponent<RectTransform>(); _progressFill.anchorMin = new Vector2(0f, 0f); _progressFill.anchorMax = new Vector2(0f, 1f); _progressFill.pivot = new Vector2(0f, 0.5f); _progressFill.anchoredPosition = Vector2.zero; _progressFill.sizeDelta = new Vector2(104f, 0f); ((Graphic)val4.GetComponent<Image>()).color = new Color(0.82f, 0.035f, 0.025f, 1f); GameObject val5 = new GameObject("Deny Entry Button", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); val5.transform.SetParent(val2.transform, false); RectTransform component5 = val5.GetComponent<RectTransform>(); component5.anchorMin = new Vector2(0f, 1f); component5.anchorMax = new Vector2(0f, 1f); component5.pivot = new Vector2(0f, 1f); component5.anchoredPosition = new Vector2(242f, -65f); component5.sizeDelta = new Vector2(88f, 25f); Image component6 = val5.GetComponent<Image>(); ((Graphic)component6).color = new Color(0.58f, 0.035f, 0.025f, 0.94f); _denyButton = val5.GetComponent<Button>(); ((Selectable)_denyButton).targetGraphic = (Graphic)(object)component6; Navigation navigation = ((Selectable)_denyButton).navigation; ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)_denyButton).navigation = navigation; _denyButton.onClick = new ButtonClickedEvent(); ((UnityEvent)_denyButton.onClick).AddListener((UnityAction)delegate { PlayerFarming val6 = FindPlayerBySlot(0); if ((Object)(object)val6 != (Object)null) { ToggleDeny(val6); } }); ((TMP_Text)CreateText(val5.transform, "Deny Entry Label", Vector2.zero, new Vector2(88f, 25f), 13f, (TextAlignmentOptions)514, new Color(1f, 0.93f, 0.8f, 1f))).text = "RT DENY"; } private void RefreshVoteUI() { //IL_010b: Unknown result type (might be due to invalid IL or missing references) if (_active && !((Object)(object)_titleText == (Object)null) && !((Object)(object)_statusText == (Object)null) && !((Object)(object)_timerText == (Object)null)) { int num = CountActivePlayers(); int num2 = CountActiveVotes(_enteredPlayers); int num3 = CountActiveVotes(_deniedPlayers); ((TMP_Text)_titleText).text = GetBuildingLabel(_selectedBuilding); ((TMP_Text)_timerText).text = Mathf.CeilToInt(Mathf.Max(0f, _remaining)).ToString(); ((TMP_Text)_statusText).text = "READY " + num2 + "/" + num + " NO " + num3; if ((Object)(object)_progressFill != (Object)null) { _progressFill.sizeDelta = new Vector2(104f * Mathf.Clamp01(_remaining / 10f), 0f); } } } private static TextMeshProUGUI CreateText(Transform parent, string name, Vector2 anchoredPosition, Vector2 size, float fontSize, TextAlignmentOptions alignment, Color color) { //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) //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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(0f, 1f); component.pivot = new Vector2(0f, 1f); component.anchoredPosition = anchoredPosition; component.sizeDelta = size; TextMeshProUGUI component2 = val.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).font = TMP_Settings.defaultFontAsset; ((TMP_Text)component2).fontSize = fontSize; ((TMP_Text)component2).alignment = alignment; ((Graphic)component2).color = color; ((TMP_Text)component2).outlineColor = Color32.op_Implicit(Color.black); ((TMP_Text)component2).outlineWidth = 0.16f; ((Graphic)component2).raycastTarget = false; return component2; } private void DestroyVoteUI() { if ((Object)(object)_canvas != (Object)null && (Object)(object)((Component)_canvas).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)_canvas).gameObject); } _canvas = null; _titleText = null; _statusText = null; _timerText = null; _progressFill = null; _denyButton = null; } private static PlayerFarming FindPlayerBySlot(int slot) { foreach (PlayerFarming activePlayer in GetActivePlayers()) { if (activePlayer.playerID == slot) { return activePlayer; } } return null; } private static string GetBuildingLabel(EnterBuilding building) { try { object obj = DestinationField?.GetValue(building); string text = ((obj != null) ? obj.ToString() : string.Empty); if (!string.IsNullOrEmpty(text) && !char.IsDigit(text[0])) { return text.ToUpperInvariant() + " ENTRY"; } } catch { } try { if (((object)Unsafe.As<FollowerLocation, FollowerLocation>(ref PlayerFarming.Location)/*cast due to .constrained prefix*/).ToString().IndexOf("Church", StringComparison.OrdinalIgnoreCase) >= 0) { return "CULT ENTRY"; } } catch { } return "TEMPLE ENTRY"; } private static string GetInnermostMessage(Exception exception) { Exception ex = exception; while (ex != null && ex.InnerException != null) { ex = ex.InnerException; } if (ex == null) { return "unknown error"; } return ex.GetType().Name + ": " + ex.Message; } } internal static class CoopConversationSession { private static readonly Dictionary<int, State> StateSnapshot = new Dictionary<int, State>(); private static PlayerFarming _owner; private static bool _active; private static bool _lockOwner; private static float _restoreOthersUntil; private static float _postCloseRecoveryUntil = -100f; private static float _townEventRecoveryUntil = -100f; private static float _nextPresentationRecoveryAt = -100f; private static float _activeSince = -100f; private static int _generation; public static bool Active => _active; public static PlayerFarming Owner => _owner; public static bool AllowAnyPlayerAdvance { get { if (_active) { return IsChurchLocation(); } return false; } } public static bool KeepInputWithOwner(UINavigatorNew navigator) { if (!_active || (Object)(object)navigator == (Object)null) { return false; } if (AllowAnyPlayerAdvance) { return false; } if ((Object)(object)_owner == (Object)null || (Object)(object)((Component)_owner).gameObject == (Object)null || !((Component)_owner).gameObject.activeInHierarchy) { _owner = PlayerFarming.Instance; } if ((Object)(object)_owner != (Object)null && (Object)(object)navigator.AllowInputOnlyFromPlayer != (Object)(object)_owner) { navigator.AllowInputOnlyFromPlayer = _owner; } return (Object)(object)_owner != (Object)null; } public static void Prepare(bool setPlayerInactive) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (PlayerFarming.players == null || PlayerFarming.players.Count <= 1 || GameManager.IsDungeon(PlayerFarming.Location)) { return; } if (_active) { EndSession(_generation); } _generation++; _owner = CoopMenuOverlaySession.ResolveRecentOwner(2.5f); if ((Object)(object)_owner == (Object)null) { _owner = PlayerFarming.Instance; } if ((Object)(object)_owner == (Object)null) { return; } StateSnapshot.Clear(); foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && (Object)(object)player.state != (Object)null) { StateSnapshot[player.playerID] = player.state.CURRENT_STATE; } } _lockOwner = setPlayerInactive; _active = true; _restoreOthersUntil = Time.unscaledTime + 1f; } public static void Activate() { if (_active && !((Object)(object)_owner == (Object)null)) { SimulationManager.UnPause(); Time.timeScale = 1f; _activeSince = Time.unscaledTime; RestoreNonOwnerPlayers(); if (_lockOwner && (Object)(object)_owner.state != (Object)null) { _owner.SetInactive(); } RestoreWorldPresentation(); Plugin.Log.LogInfo((object)$"[QoL] Opened non-blocking NPC conversation for player {_owner.playerID + 1}"); } } public static void Tick() { if (_active) { if (IsChurchLocation() && Time.unscaledTime - _activeSince >= 20f && MMConversation.isPlaying) { Plugin.Log.LogWarning((object)"[QoL] Church conversation exceeded 20 seconds; closing it to prevent a permanent temple lock."); CoopEmergencyEscape.TryForceCloseConversation("church conversation watchdog"); EmergencyRecoverAll(); return; } KeepInputWithOwner(MonoSingleton<UINavigatorNew>.Instance); if (Time.unscaledTime <= _restoreOthersUntil) { RestoreNonOwnerPlayers(); } TryRestoreWorldPresentation(); } else if (Time.unscaledTime <= _postCloseRecoveryUntil || Time.unscaledTime <= _townEventRecoveryUntil) { RecoverAllPlayersAfterConversation(); TryRestoreWorldPresentation(); } } public static void BeginTownEventRecovery(float duration, string reason) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!GameManager.IsDungeon(PlayerFarming.Location) && PlayerFarming.players != null && PlayerFarming.players.Count > 1) { _townEventRecoveryUntil = Mathf.Max(_townEventRecoveryUntil, Time.unscaledTime + Mathf.Max(0.5f, duration)); RecoverAllPlayersAfterConversation(); RestoreWorldPresentation(); Plugin.Log.LogInfo((object)("[QoL] Armed town event movement/camera recovery: " + reason)); } } public static int CaptureGeneration() { return _generation; } public static void EndSession(int expectedGeneration) { if (_active && expectedGeneration == _generation) { _active = false; _postCloseRecoveryUntil = Time.unscaledTime + 1.25f; RecoverAllPlayersAfterConversation(); RestoreWorldPresentation(); Plugin.Log.LogInfo((object)"[QoL] Closed non-blocking NPC conversation"); _owner = null; _lockOwner = false; _activeSince = -100f; StateSnapshot.Clear(); } } public static void ForceEndSession() { EndSession(_generation); } public static void EmergencyRecoverAll() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) _active = false; _owner = null; _lockOwner = false; _activeSince = -100f; StateSnapshot.Clear(); _postCloseRecoveryUntil = Time.unscaledTime + 2f; if (!GameManager.IsDungeon(PlayerFarming.Location)) { RecoverAllPlayersAfterConversation(); RestoreWorldPresentation(); } } private static void RestoreNonOwnerPlayers() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (PlayerFarming.players == null) { return; } foreach (PlayerFarming player in PlayerFarming.players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)_owner) && !TransitionVotePlayerState.IsHidden(player) && !((Object)(object)player.state == (Object)null) && (int)player.state.CURRENT_STATE == 13) { if (StateSnapshot.TryGetValue(player.playerID, out var value) && (int)value != 13) { player.state.LockStateChanges = false; player.state.CURRENT_STATE = value; } else { player.state.LockStateChanges = false; player.state.CURRENT_STATE = (State)0; } } } } private static void RecoverAllPlayersAfterConversation() { //IL_0007: 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_007c: Invalid comparison between Unknown and I4 if (PlayerFarming.players == null || GameManager.IsDungeon(PlayerFarming.Location)) { return; } foreach (PlayerFarming player in PlayerFarming.players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)((Component)player).gameObject == (Object)null) && ((Component)player).gameObject.activeInHierarchy && !TransitionVotePlayerState.IsHidden(player) && !((Object)(object)player.state == (Object)null)) { player.state.LockStateChanges = false; if ((int)player.state.CURRENT_STATE == 13) { player.state.CURRENT_STATE = (State)0; } } } UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)instance != (Object)null) { instance.AllowInputOnlyFromPlayer = PlayerFarming.Instance; } } private static void RestoreWorldPresentation() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) try { SimulationManager.UnPause(); Time.timeScale = 1f; GameManager instance = GameManager.GetInstance(); if ((Object)(object)instance != (Object)null) { CameraFollowTarget camFollowTarget = instance.CamFollowTarget; if ((Object)(object)camFollowTarget != (Object)null) { camFollowTarget.DisablePlayerLook = false; camFollowTarget.IN_CONVERSATION = false; camFollowTarget.SnappyMovement = false; camFollowTarget.SetOffset(Vector3.zero); camFollowTarget.CurrentOffset = Vector3.zero; camFollowTarget.targetDistance = Mathf.Max(camFollowTarget.targetDistance, 12f); camFollowTarget.distance = Mathf.Max(camFollowTarget.distance, 11f); if ((Object)(object)camFollowTarget.TargetCamera != (Object)null) { camFollowTarget.ResetTargetCamera(0f); } } instance.CameraSetConversationMode(false); instance.CameraSetOffset(Vector3.zero); if (PlayerFarming.players != null) { if ((Object)(object)camFollowTarget != (Object)null && camFollowTarget.targets != null) { for (int num = camFollowTarget.targets.Count - 1; num >= 0; num--) { Target val = camFollowTarget.targets[num]; bool flag = false; foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && val != null && (Object)(object)val.gameObject == (Object)(object)player.CameraBone) { flag = true; break; } } if (!flag) { camFollowTarget.targets.RemoveAt(num); } } } foreach (PlayerFarming player2 in PlayerFarming.players) { if ((Object)(object)player2 != (Object)null && !TransitionVotePlayerState.IsHidden(player2) && (Object)(object)player2.CameraBone != (Object)null && !instance.CameraContains(player2.CameraBone)) { instance.AddToCamera(player2.CameraBone, player2.isLamb ? 1f : 0.1f); } } } } LetterBox.Hide(true); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[QoL] Conversation presentation recovery was partial: " + ex.Message)); } } public static void RecoverHubPresentation() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (PlayerFarming.players != null && PlayerFarming.players.Count > 1 && !GameManager.IsDungeon(PlayerFarming.Location)) { RestoreWorldPresentation(); } } private static void TryRestoreWorldPresentation() { if (!(Time.unscaledTime < _nextPresentationRecoveryAt)) { _nextPresentationRecoveryAt = Time.unscaledTime + 0.2f; RestoreWorldPresentation(); } } private static bool IsChurchLocation() { try { return string.Equals(((object)Unsafe.As<FollowerLocation, FollowerLocation>(ref PlayerFarming.Location)/*cast due to .constrained prefix*/).ToString(), "Church", StringComparison.OrdinalIgnoreCase); } catch { return false; } } } internal static class CoopEmergencyEscape { private static float _nextAllowedAt; public static void Tick() { if (Time.unscaledTime < _nextAllowedAt || !Input.GetKeyDown((KeyCode)291)) { return; } _nextAllowedAt = Time.unscaledTime + 0.75f; int num = CoopMenuOverlaySession.EmergencyCloseAll(); DoorTransitionVote.EmergencyCancel(); BuildingTransitionVote.EmergencyCancel(); TransitionVotePlayerState.RestoreAll(); UIMenuBase[] array = Object.FindObjectsOfType<UIMenuBase>(); foreach (UIMenuBase val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { try { val.Hide(true); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Emergency] Could not hide " + ((object)val).GetType().Name + ": " + ex.Message)); } } } if (TryForceCloseConversation("F10 emergency")) { num++; } SimulationManager.UnPause(); Time.timeScale = 1f; CoopConversationSession.EmergencyRecoverAll(); RestorePlayers(); UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)instance != (Object)null) { instance.AllowInputOnlyFromPlayer = PlayerFarming.Instance; } try { LetterBox.Hide(true); if ((Object)(object)HUD_Manager.Instance != (Object)null) { HUD_Manager.Instance.Show(0, false); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Emergency] Presentation recovery was partial: " + ex2.Message)); } Plugin.Log.LogWarning((object)("[Emergency] F10 closed " + num + " active menu(s) and restored keyboard/controller ownership")); } internal static bool TryForceCloseConversation(string reason) { bool isPlaying; try { isPlaying = MMConversation.isPlaying; } catch { return false; } if (!isPlaying) { return false; } try { object obj2 = AccessTools.Field(typeof(MMConversation), "mmConversation")?.GetValue(null); MethodInfo methodInfo = AccessTools.Method(typeof(MMConversation), "DoClose", new Type[3] { typeof(bool), typeof(Action), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(methodInfo.IsStatic ? null : obj2, new object[3] { true, null, MMConversation.isBark }); } MethodInfo methodInfo2 = AccessTools.Method(typeof(MMConversation), "FinishCloseFadeTweenByForce", Type.EmptyTypes, (Type[])null); if (methodInfo2 != null) { methodInfo2.Invoke(methodInfo2.IsStatic ? null : obj2, null); } if (MMConversation.isPlaying) { MethodInfo methodInfo3 = AccessTools.Method(typeof(MMConversation), "ClearConversation", Type.EmptyTypes, (Type[])null); if (methodInfo3 != null) { methodInfo3.Invoke(methodInfo3.IsStatic ? null : obj2, null); } } Plugin.Log.LogWarning((object)("[Emergency] Forcibly closed a stuck conversation (" + reason + ")")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Emergency] Stuck conversation close was partial (" + reason + "): " + ex.Message)); } return true; } private static void RestorePlayers() { //IL_0008: 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_0073: Invalid comparison between Unknown and I4 //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 if (PlayerFarming.players == null) { return; } bool flag = GameManager.IsDungeon(PlayerFarming.Location); foreach (PlayerFarming player in PlayerFarming.players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)((Component)player).gameObject == (Object)null) && ((Component)player).gameObject.activeInHierarchy && !((Object)(object)player.state == (Object)null)) { player.state.LockStateChanges = false; if ((int)player.state.CURRENT_STATE == 13 || (!flag && (int)player.state.CURRENT_STATE == 49)) { player.state.CURRENT_STATE = (State)0; } } } } } internal sealed class CoopEnemyBalanceMarker : MonoBehaviour { public bool Applied; public int PlayerCount; public float Multiplier = 1f; } internal sealed class CoopMenuOverlaySession : MonoBehaviour { private static readonly Dictionary<int, State> StateSnapshot = new Dictionary<int, State>(); private static PlayerFarming _capturedOwner; private static float _capturedOwnerAt = -100f; private static readonly List<CoopMenuOverlaySession> ActiveSessions = new List<CoopMenuOverlaySession>(); private static CoopMenuOverlaySession _focusedSession; private PlayerFarming _owner; private RectTransform _visualRoot; private readonly List<RectTransform> _visualRoots = new List<RectTransform>(); private float _visualScale; private float _backgroundAlpha; private bool _restoreConversationCamera; private string _sessionName; private bool _worldReleased; private bool _visualApplied; private bool _sessionEnded; private UIMenuBase _boundMenu; private GameObject _ownerTag; public static bool BlocksFollowerWheelPresentation { get { for (int i = 0; i < ActiveSessions.Count; i++) { CoopMenuOverlaySession coopMenuOverlaySession = ActiveSessions[i]; if ((Object)(object)coopMenuOverlaySession != (Object)null && !coopMenuOverlaySession._sessionEnded && coopMenuOverlaySession._sessionName == "follower wheel") { return true; } } return false; } } public static void CapturePlayerStates(PlayerFarming owner) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) _capturedOwner = owner; _capturedOwnerAt = Time.unscaledTime; StateSnapshot.Clear(); if (PlayerFarming.players == null) { return; } foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && (Object)(object)player.state != (Object)null) { StateSnapshot[player.playerID] = player.state.CURRENT_STATE; } } } public static PlayerFarming ResolveOwner(PlayerFarming explicitOwner = null) { if ((Object)(object)explicitOwner != (Object)null) { return explicitOwner; } PlayerFarming val = ResolveRecentOwner(2.5f); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)MonoSingleton<UINavigatorNew>.Instance != (Object)null && (Object)(object)MonoSingleton<UINavigatorNew>.Instance.AllowInputOnlyFromPlayer != (Object)null) { return MonoSingleton<UINavigatorNew>.Instance.AllowInputOnlyFromPlayer; } return PlayerFarming.Instance; } public static PlayerFarming ResolveRecentOwner(float maxAge) { if ((Object)(object)_capturedOwner == (Object)null || (Object)(object)((Component)_capturedOwner).gameObject == (Object)null || !((Component)_capturedOwner).gameObject.activeInHierarchy) { return null; } if (!(Time.unscaledTime - _capturedOwnerAt <= Mathf.Max(0f, maxAge))) { return null; } return _capturedOwner; } public static int EmergencyCloseAll() { List<CoopMenuOverlaySession> list = new List<CoopMenuOverlaySession>(ActiveSessions); int num = 0; foreach (CoopMenuOverlaySession item in list) { if ((Object)(object)item == (Object)null || item._sessionEnded) { continue; } num++; try { if ((Object)(object)item._boundMenu != (Object)null && ((Component)item._boundMenu).gameObject.activeInHierarchy) { item._boundMenu.Hide(true); } else { item.EndSession(); } } catch { item.EndSession(); } } return num; } public static int CloseAllForTransition() { List<CoopMenuOverlaySession> list = new List<CoopMenuOverlaySession>(ActiveSessions); int num = 0; foreach (CoopMenuOverlaySession item in list) { if ((Object)(object)item == (Object)null || item._sessionEnded) { continue; } num++; try { if ((Object)(object)item._boundMenu != (Object)null && ((Component)item._boundMenu).gameObject.activeInHierarchy) { item._boundMenu.Hide(true); } if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null && ((Component)item).gameObject.activeSelf) { ((Component)item).gameObject.SetActive(false); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[QoL] Transition menu close was partial for " + item._sessionName + ": " + ex.Message)); } finally { if ((Object)(object)item != (Object)null && !item._sessionEnded) { item.EndSession(); } } } ActiveSessions.Clear(); _focusedSession = null; _capturedOwner = null; _capturedOwnerAt = -100f; StateSnapshot.Clear(); UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)instance != (Object)null) { instance.AllowInputOnlyFromPlayer = PlayerFarming.Instance; } SimulationManager.UnPause(); Time.timeScale = 1f; return num; } public static void Attach(Component menu, PlayerFarming owner, float visualScale, float backgroundAlpha, bool restoreConversationCamera, string sessionName) { if (!((Object)(object)menu == (Object)null) && !((Object)(object)menu.gameObject == (Object)null) && PlayerFarming.players != null && PlayerFarming.players.Count > 1) { CoopMenuOverlaySession coopMenuOverlaySession = menu.gameObject.GetComponent<CoopMenuOverlaySession>(); if ((Object)(object)coopMenuOverlaySession == (Object)null) { coopMenuOverlaySession = menu.gameObject.AddComponent<CoopMenuOverlaySession>(); } coopMenuOverlaySession._owner = ResolveOwner(owner); coopMenuOverlaySession._visualScale = Mathf.Clamp(visualScale, 0.24f, 1f); coopMenuOverlaySession._backgroundAlpha = Mathf.Clamp01(backgroundAlpha); coopMenuOverlaySession._restoreConversationCamera = restoreConversationCamera; coopMenuOverlaySession._sessionName = sessionName ?? "context menu"; coopMenuOverlaySession._sessionEnded = false; coopMenuOverlaySession._worldReleased = false; coopMenuOverlaySession._visualApplied = false; coopMenuOverlaySession._visualRoots.Clear(); if (!ActiveSessions.Contains(coopMenuOverlaySession)) { ActiveSessions.Add(coopMenuOverlaySession); } _focusedSession = coopMenuOverlaySession; if (coopMenuOverlaySession._restoreConversationCamera) { CoopConversationSession.RecoverHubPresentation(); } if ((Object)(object)coopMenuOverlaySession._ownerTag != (Object)null) { Object.Destroy((Object)(object)coopMenuOverlaySession._ownerTag); coopMenuOverlaySession._ownerTag = null; } coopMenuOverlaySession.BindMenu(menu.GetComponent<UIMenuBase>()); } } private void BindMenu(UIMenuBase menu) { if (!((Object)(object)_boundMenu == (Object)(object)menu)) { if ((Object)(object)_boundMenu != (Object)null) { UIMenuBase boundMenu = _boundMenu; boundMenu.OnHide = (Action)Delegate.Remove(boundMenu.OnHide, new Action(EndSession)); } _boundMenu = menu; if ((Object)(object)_boundMenu != (Object)null) { UIMenuBase boundMenu2 = _boundMenu; boundMenu2.OnHide = (Action)Delegate.Combine(boundMenu2.OnHide, new Action(EndSession)); } } } private IEnumerator Start() { yield return null; if (!_sessionEnded) { ReleaseWorldForOtherPlayers(); yield return (object)new WaitForSecondsRealtime(0.25f); if (!_sessionEnded) { ApplyCompactVisual(); } } } private void LateUpdate() { if (_sessionEnded || (Object)(object)_owner == (Object)null || PlayerFarming.players == null || PlayerFarming.players.Count <= 1) { return; } if (!_worldReleased) { ReleaseWorldForOtherPlayers(); } if (_visualApplied) { if (OwnerRequestedMenuFocus()) { _focusedSession = this; } UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)_focusedSession == (Object)(object)this && (Object)(object)instance != (Object)null && (Object)(object)instance.AllowInputOnlyFromPlayer != (Object)(object)_owner) { instance.AllowInputOnlyFromPlayer = _owner; } if (_restoreConversationCamera) { CoopConversationSession.RecoverHubPresentation(); } RestoreNonOwnerPlayers(); } } private void ReleaseWorldForOtherPlayers() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (_worldReleased || (Object)(object)_owner == (Object)null) { return; } _worldReleased = true; try { if (!ActiveSessions.Contains(this)) { ActiveSessions.Add(this); } _focusedSession = this; Time.timeScale = 1f; SimulationManager.UnPause(); UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)instance != (Object)null) { instance.AllowInputOnlyFromPlayer = _owner; } if (_restoreConversationCamera && (Object)(object)GameManager.GetInstance() != (Object)null && !MMConversation.isPlaying) { GameManager.GetInstance().CameraSetConversationMode(false); GameManager.GetInstance().CameraSetOffset(Vector3.zero); LetterBox.Hide(true); foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && (Object)(object)player.CameraBone != (Object)null && !GameManager.GetInstance().CameraContains(player.CameraBone)) { GameManager.GetInstance().AddToCamera(player.CameraBone, player.isLamb ? 1f : 0.1f); } } } RestoreNonOwnerPlayers(); Plugin.Log.LogInfo((object)("[QoL] Opened non-blocking " + _sessionName + " for player " + (_owner.playerID + 1))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[QoL] Could not fully release co-op world for " + _sessionName + ": " + ex.Message)); } } private void EndSession() { if (!_sessionEnded) { _sessionEnded = true; ActiveSessions.Remove(this); if ((Object)(object)_focusedSession == (Object)(object)this) { _focusedSession = ((ActiveSessions.Count > 0) ? ActiveSessions[ActiveSessions.Count - 1] : null); } if ((Object)(object)_ownerTag != (Object)null) { Object.Destroy((Object)(object)_ownerTag); _ownerTag = null; } if ((Object)(object)_owner != (Object)null) { _capturedOwner = _owner; _capturedOwnerAt = Time.unscaledTime; } if (_sessionName == "follower wheel") { CoopConversationSession.BeginTownEventRecovery(1.5f, "follower wheel closed"); } UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.AllowInputOnlyFromPlayer == (Object)(object)_owner) { instance.AllowInputOnlyFromPlayer = PlayerFarming.Instance; } Plugin.Log.LogInfo((object)("[QoL] Closed non-blocking " + _sessionName + " and released its input owner")); } } private bool OwnerRequestedMenuFocus() { try { return (Object)(object)_owner != (Object)null && _owner.rewiredPlayer != null && _owner.rewiredPlayer.GetAnyButtonDown(); } catch { return false; } } private void OnDisable() { EndSession(); } private void OnDestroy() { EndSession(); if ((Object)(object)_boundMenu != (Object)null) { UIMenuBase boundMenu = _boundMenu; boundMenu.OnHide = (Action)Delegate.Remove(boundMenu.OnHide, new Action(EndSession)); } } private void RestoreNonOwnerPlayers() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (PlayerFarming.players == null) { return; } foreach (PlayerFarming player in PlayerFarming.players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player == (Object)(object)_owner) && !((Object)(object)player.state == (Object)null) && (int)player.state.CURRENT_STATE == 13) { State cURRENT_STATE = (State)0; if (StateSnapshot.TryGetValue(player.playerID, out var value)) { cURRENT_STATE = (State)(((int)value != 13) ? ((int)value) : 0); } player.state.LockStateChanges = false; player.state.CURRENT_STATE = cURRENT_STATE; } } } private void ApplyCompactVisual() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (_visualApplied || (Object)(object)_owner == (Object)null) { return; } try { if (_visualScale >= 0.99f) { _visualRoots.Clear(); FindVisualRoots(_visualRoots); for (int i = 0; i < _visualRoots.Count; i++) { MakeBackgroundTransparent(_visualRoots[i]); } CreateOwnerTag(); _visualApplied = true; return; } _visualRoots.Clear(); FindVisualRoots(_visualRoots); if (_visualRoots.Count != 0) { for (int j = 0; j < _visualRoots.Count; j++) { RectTransform val = _visualRoots[j]; ((Transform)val).localScale = Vector3.one * _visualScale; MakeBackgroundTransparent(val); } _visualRoot = _visualRoots[0]; MoveVisualsToReservedSlot(_visualRoots); if (_sessionName == "cooking menu" || _sessionName == "kitchen menu") { ImproveCompactCookingReadability(_visualRoots); } CreateOwnerTag(); _visualApplied = true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[QoL] Compact overlay layout failed for " + _sessionName + ": " + ex.Message)); } } private void ImproveCompactCookingReadability(List<RectTransform> visuals) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: 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_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) if (visuals == null || visuals.Count == 0) { return; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(float.MaxValue, float.MaxValue); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(float.MinValue, float.MinValue); Vector3[] array = (Vector3[])(object)new Vector3[4]; foreach (RectTransform visual in visuals) { visual.GetWorldCorners(array); for (int i = 0; i < array.Length; i++) { Vector2 val3 = RectTransformUtility.WorldToScreenPoint((Camera)null, array[i]); val = Vector2.Min(val, val3); val2 = Vector2.Max(val2, val3); } } float num = Mathf.Max(1f, val2.x - val.x); float num2 = Mathf.Max(1f, val2.y - val.y); float num3 = val.x + num * 0.48f; foreach (RectTransform visual2 in visuals) { TextMeshProUGUI[] componentsInChildren = ((Component)visual2).GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null) && !string.IsNullOrWhiteSpace(((TMP_Text)val4).text)) { float num4 = ((RectTransformUtility.WorldToScreenPoint((Camera)null, ((Transform)((TMP_Text)val4).rectTransform).position).x >= num3) ? 1.7f : 1.25f); ((TMP_Text)val4).enableAutoSizing = false; ((TMP_Text)val4).fontSize = Mathf.Clamp(((TMP_Text)val4).fontSize * num4, 18f, 54f); ((TMP_Text)val4).outlineWidth = Mathf.Max(((TMP_Text)val4).outlineWidth, 0.08f); } } Graphic[] componentsInChildren2 = ((Component)visual2).GetComponentsInChildren<Graphic>(true); foreach (Graphic val5 in componentsInChildren2) { if (!((Object)(object)val5 == (Object)null) && (val5 is Image || val5 is RawImage)) { val5.rectTransform.GetWorldCorners(array); Vector2 val6 = RectTransformUtility.WorldToScreenPoint((Camera)null, array[0]); Vector2 val7 = RectTransformUtility.WorldToScreenPoint((Camera)null, array[2]); float num5 = Mathf.Abs(val7.x - val6.x); float num6 = Mathf.Abs(val7.y - val6.y); float num7 = (val6.x + val7.x) * 0.5f; Color color = val5.color; float num8 = color.r * 0.2126f + color.g * 0.7152f + color.b * 0.0722f; if (num7 < val.x + num * 0.2f && num6 > num2 * 0.22f && num5 < num * 0.18f && num8 < 0.16f) { color.a = 0f; val5.color = color; } } } } Plugin.Log.LogInfo((object)"[QoL] Enlarged compact food text and removed the dark left-side decoration"); } private void FindVisualRoots(List<RectTransform> results) { Transform transform = ((Component)this).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { return; } if ((Object)(object)((Component)this).GetComponent<Canvas>() == (Object)null) { results.Add(val); return; } for (int i = 0; i < ((Transform)val).childCount; i++) { Transform child = ((Transform)val).GetChild(i); RectTransform val2 = (RectTransform)(object)((child is RectTransform) ? child : null); if (!((Object)(object)val2 == (Object)null) && ((Component)val2).GetComponentsInChildren<Graphic>(true).Length > 0) { results.Add(val2); } } if (results.Count == 0) { results.Add(val); } } private void MoveVisualsToReservedSlot(List<RectTransform> visuals) { //IL_0031: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) if (visuals == null || visuals.Count == 0 || (Object)(object)_owner == (Object)null) { return; } Canvas componentInParent = ((Component)visuals[0]).GetComponentInParent<Canvas>(); Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(float.MaxValue, float.MaxValue); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(float.MinValue, float.MinValue); Vector3[] array = (Vector3[])(object)new Vector3[4]; foreach (RectTransform visual in visuals) { visual.GetWorldCorners(array); for (int i = 0; i < array.Length; i++) { Vector2 val4 = RectTransformUtility.WorldToScreenPoint(val, array[i]); val2 = Vector2.Min(val2, val4); val3 = Vector2.Max(val3, val4); } } Vector2 val5 = (val2 + val3) * 0.5f; Vector2 val6 = GetReservedScreenPoint(_owner.playerID) - val5; Vector3 position = default(Vector3); foreach (RectTransform visual2 in visuals) { Transform parent = ((Transform)visual2).parent; RectTransform val7 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (!((Object)(object)val7 == (Object)null)) { Vector2 val8 = RectTransformUtility.WorldToScreenPoint(val, ((Transform)visual2).position) + val6; if (RectTransformUtility.ScreenPointToWorldPointInRectangle(val7, val8, val, ref position)) { ((Transform)visual2).position = position; } } } } private static Vector2 GetReservedScreenPoint(int playerID) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(playerID, 0, 7); int num2 = num % 3; int num3 = num / 3; float num4 = (float)Screen.width * (0.17f + 0.33f * (float)num2); float num5 = (float)Screen.height * (0.81f - 0.31f * (float)num3); return new Vector2(num4, num5); } private void CreateOwnerTag() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: 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_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_ownerTag != (Object)null || (Object)(object)_owner == (Object)null) { return; } Canvas val = ((Component)this).GetComponent<Canvas>() ?? ((Component)this).GetComponentInParent<Canvas>() ?? ((Component)this).GetComponentInChildren<Canvas>(true); if (!((Object)(object)val == (Object)null)) { _ownerTag = new GameObject("COTLMP Menu Owner P" + (_owner.playerID + 1), new Type[2] { typeof(RectTransform), typeof(Image) }); _ownerTag.transform.SetParent(((Component)val).transform, false); _ownerTag.transform.SetAsLastSibling(); RectTransform component = _ownerTag.GetComponent<RectTransform>(); Vector2 reservedScreenPoint = GetReservedScreenPoint(_owner.playerID); component.anchorMin = new Vector2(reservedScreenPoint.x / (float)Screen.width, Mathf.Clamp01(reservedScreenPoint.y / (float)Screen.height + 0.14f)); component.anchorMax = component.anchorMin; component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(72f, 34f); Image component2 = _ownerTag.GetComponent<Image>(); Color color = Plugin.IndicatorColors[Mathf.Clamp(_owner.playerID, 0, Plugin.IndicatorColors.Length - 1)]; color.a = 0.88f; ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = false; GameObject val2 = new GameObject("Label", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val2.transform.SetParent(_ownerTag.transform, false); RectTransform component3 = val2.GetComponent<RectTransform>(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = Vector2.zero; component3.offsetMax = Vector2.zero; TextMeshProUGUI component4 = val2.GetComponent<TextMeshProUGUI>(); TextMeshProUGUI componentInChildren = ((Component)this).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)component4).font = ((TMP_Text)componentInChildren).font; ((TMP_Text)component4).fontSharedMaterial = ((TMP_Text)componentInChildren).fontSharedMaterial; } ((TMP_Text)component4).text = "P" + (_owner.playerID + 1); ((TMP_Text)component4).fontSize = 22f; ((TMP_Text)component4).fontStyle = (FontStyles)1; ((TMP_Text)component4).alignment = (TextAlignmentOptions)514; ((Graphic)component4).color = Color.white; ((Graphic)component4).raycastTarget = false; } } private void MakeBackgroundTransparent(RectTransform visual) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00e2: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) Graphic[] componentsInChildren = ((Component)visual).GetComponentsInChildren<Graphic>(true); foreach (Graphic val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && (val is Image || val is RawImage)) { string text = ((Object)((Component)val).gameObject).name.ToLowerInvariant(); bool num = text.Contains("background") || text.Contains("backdrop") || text.Contains("vignette") || text.Contains("fade") || text.Contains("overlay") || text.Contains("info card") || text.Contains("infocard") || text.Contains("detail") || text == "bg"; RectTransform rectTransform = val.rectTransform; Vector3[] array = (Vector3[])(object)new Vector3[4]; rectTransform.GetWorldCorners(array); Vector2 val2 = RectTransformUtility.WorldToScreenPoint((Camera)null, array[0]); Vector2 val3 = RectTransformUtility.WorldToScreenPoint((Camera)null, array[2]); float num2 = Mathf.Abs(val3.x - val2.x); float num3 = Mathf.Abs(val3.y - val2.y); Color color = val.color; bool flag = num2 > (float)Screen.width * 0.055f && num3 > (float)Screen.height * 0.055f; if (num || flag) { color.a = Mathf.Min(color.a, _backgroundAlpha); val.color = color; } } } } } internal static class CustomSpineCompatibility { private sealed class SelectionRouteState { public bool Changed; public string PreviousSelection; } private sealed class CultTweakerFleeceState { public bool Changed; public int PreviousIndex; } private sealed class CotlApiBleatState { public IDictionary Overrides; public object Key; public object Value; public bool Removed; } private sealed class CotlApiPlayerSkinState { public PlayerFarming Player; public bool OriginalIsGoat; public bool CapturedIdentity; public IDictionary Overrides; public object Key; public object Value; public bool Removed; } private static class CultTweakerFleecePatch { public static void Prefix(PlayerFarming __instance, out CultTweakerFleeceState __state) { __state = new CultTweakerFleeceState(); if (!_active || (Object)(object)__instance == (Object)null || __instance.playerID < 2 || __instance.playerID >= 8 || _cultTweakerFleeceIndexP1Field == null) { return; } try { object value = _cultTweakerFleeceIndexP1Field.GetValue(null); if (value is int) { __state.PreviousIndex = (int)value; if (__state.PreviousIndex != -1) { _cultTweakerFleeceIndexP1Field.SetValue(null, -1); __state.Changed = true; } } } catch (Exception exception) { LogFleeceWarning(exception); } } public static Exception Finalizer(Exception __exception, CultTweakerFleeceState __state) { if (__state != null && __state.Changed && _cultTweakerFleeceIndexP1Field != null) { try { _cultTweakerFleeceIndexP1Field.SetValue(null, __state.PreviousIndex); } catch (Exception exception) { LogFleeceWarning(exception); } } return __exception; } } private static class CotlApiExtraPlayerBleatPatch { public static void Prefix(object __instance, out CotlApiBleatState __state) { __state = new CotlApiBleatState(); if (!_active || __instance == null || _playerBleatOverrideProperty == null) { return; } try { PlayerFarming val = (PlayerFarming)((__instance is PlayerFarming) ? __instance : null); if ((Object)(object)val == (Object)null) { object? obj = AccessTools.Field(__instance.GetType(), "<>4__this")?.GetValue(__instance); val = (PlayerFarming)((obj is PlayerFarming) ? obj : null); } if ((Object)(object)val == (Object)null || val.playerID < 2 || val.playerID >= 8 || !(_playerBleatOverrideProperty.GetValue(null, null) is IDictionary dictionary)) { return; } string b = ((!val.isLamb || val.IsGoat) ? "GOAT" : "LAMB"); object obj2 = null; foreach (object key in dictionary.Keys) { if (key != null && string.Equals(key.ToString(), b, StringComparison.OrdinalIgnoreCase)) { obj2 = key; break; } } if (obj2 != null && dictionary.Contains(obj2)) { __state.Overrides = dictionary; __state.Key = obj2; __state.Value = dictionary[obj2]; dictionary.Remove(obj2); __state.Removed = true; } } catch (Exception exception) { LogBleatWarning(exception); } } public static Exception Finalizer(Exception __exception, CotlApiBleatState __state) { if (__state != null && __state.Removed && __state.Overrides != null && __state.Key != null) { try { __state.Overrides[__state.Key] = __state.Value; } catch (Exception exception) { LogBleatWarning(exception); } } return __exception; } } private static class CotlApiExtraPlayerSkinPatch { public static void Prefix(PlayerFarming __instance, out CotlApiPlayerSkinState __state) { __state = new CotlApiPlayerSkinState(); if (!_active || (Object)(object)__instance == (Object)null || __instance.playerID < 2 || __instance.playerID >= 8) { return; } __state.Player = __instance; __state.OriginalIsGoat = __instance.IsGoat; __state.CapturedIdentity = true; if (_playerSkinOverrideProperty == null) { return; } try { if (_playerSkinOverrideProperty.GetValue(null, null) is IDictionary dictionary) { string name = ((!__instance.isLamb || __instance.IsGoat) ? "GOAT" : "LAMB"); object obj = FindDictionaryKey(dictionary, name); if (obj != null && dictionary.Contains(obj)) { __state.Overrides = dictionary; __state.Key = obj; __state.Value = dictionary[obj]; dictionary.Remove(obj); __state.Removed = true; } } } catch (Exception exception) { LogSkinIdentityWarning(exception); } } public static void Postfix(PlayerFarming __instance, bool BlackAndWhite) { if (!_active || (Object)(object)__instance == (Object)null || __instance.playerID < 2 || __instance.playerID >= 8 || _apiPlayerSetSkinPatchMethod == null || !ShouldPreserveCustomSpineColor(__instance)) { return; } int customHurtLevel = GetCustomHurtLevel(__instance); string selectionSkinName = GetSelectionSkinName(ResolvedSelections[__instance.playerID]); if (customHurtLevel <= 0 || string.IsNullOrEmpty(selectionSkinName) || IsVanillaHurtCompatibleSkin(selectionSkinName)) { return; } HealthPlayer health = __instance.health; if ((Object)(object)health == (Object)null) { return; } float pLAYER_HEALTH = health.PLAYER_HEALTH; try { health.PLAYER_HEALTH = Mathf.Max(pLAYER_HEALTH, 3f); object[] array = new object[3] { null, __instance, BlackAndWhite }; object obj = _apiPlayerSetSkinPatchMethod.Invoke(null, array); if (!(obj is bool) || (bool)obj) { throw new InvalidOperationException("COTL_API declined the routed custom-spine skin rebuild"); } object obj2 = array[0]; ApplyMatchingHurtSkin(__instance, (Skin)((obj2 is Skin) ? obj2 : null), selectionSkinName, customHurtLevel); RefreshCustomSpineAnimation(__instance, "hurt skin", logSuccess: false); if (!HurtSkinGuardLogged[__instance.playerID]) { HurtSkinGuardLogged[__instance.playerID] = true; Plugin.Log.LogInfo((object)$"[MP][CustomSpine] Player {__instance.playerID + 1} now uses only '{selectionSkinName}'-compatible hurt visuals"); } } catch (Exception exception) { LogHurtSkinWarning(exception); } finally { health.PLAYER_HEALTH = pLAYER_HEALTH; } } public static Exception Finalizer(Exception __exception, CotlApiPlayerSkinState __state) { if (__state == null) { return __exception; } try { if (__state.Removed && __state.Overrides != null && __state.Key != null) { __state.Overrides[__state.Key] = __state.Value; } if (__state.CapturedIdentity && (Object)(object)__state.Player != (Object)null) { __state.Player.IsGoat = __state.OriginalIsGoat; } } catch (Exception exception) { LogSkinIdentityWarning(exception); } return __exception; } } private const string ApiGuid = "io.github.xhayper.COTL_API"; private const string LoaderGuid = "InfernoDragon0.cotl.CustomSpineLoader"; private const string MatchPlayerOne = "Match Player 1"; private const string MatchPlayerTwo = "Match Player 2"; private const string CotlmpDefault = "COTLMP default goat"; private static readonly string[] SlotChoices = new string[8]; private static readonly string[] ResolvedSelections = new string[8]; private static readonly object[] ResolvedAssets = new object[8]; private static readonly object[] SlotDropdowns = new object[8]; private static readonly bool[] ApplyScheduled = new bool[8]; private static Plugin _host; private static Type _skinManagerType; private static FieldInfo _selectedSpineField; private static FieldInfo _selectedSpineTwoField; private static IDictionary _customPlayerSpines; private static MethodInfo _playerStartMethod; private static MethodInfo _apiPlayerStartPatchMethod; private static MethodInfo _apiPlayerSetSkinPatchMethod; private static FieldInfo _startCompleteField; private static FieldInfo _dropdownDisplayTypeField; private static FieldInfo _cultTweakerFleeceIndexP1Field; private static MethodInfo _refreshAnimationFromStateMethod; private static PropertyInfo _playerBleatOverrideProperty; private static PropertyInfo _playerSkinOverrideProperty; private static string[] _dropdownOptions = Array.Empty<string>(); private static bool _active; private static bool _routeWarningLogged; private static bool _settingsWarningLogged; private static bool _onEnableWarningLogged; private static bool _fleeceWarningLogged; private static bool _animationWarningLogged; private static bool _bleatWarningLogged; private static bool _skinIdentityWarningLogged; private static bool _hurtSkinWarningLogged; private static readonly bool[] HurtSkinGuardLogged = new bool[8]; internal static void Initialize(Plugin host, Harmony harmony) { //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Expected O, but got Unknown //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown //IL_02fc: Expected O, but got Unknown //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Expected O, but got Unknown //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Expected O, but got Unknown //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Expected O, but got Unknown if (!Chainloader.PluginInfos.ContainsKey("io.github.xhayper.COTL_API") || !Chainloader.PluginInfos.ContainsKey("InfernoDragon0.cotl.CustomSpineLoader")) { Plugin.Log.LogInfo((object)"[MP][CustomSpine] Optional COTL_API/CustomSpineLoader integration inactive"); return; } _host = host; _skinManagerType = AccessTools.TypeByName("COTL_API.CustomSkins.CustomSkinManager"); if (_skinManagerType == null) { Plugin.Log.LogWarning((object)"[MP][CustomSpine] Installed API layout is unsupported; compatibility disabled without changing normal COTLMP behavior"); return; } _selectedSpineField = AccessTools.Field(_skinManagerType, "SelectedSpine"); _selectedSpineTwoField = AccessTools.Field(_skinManagerType, "SelectedSpine2"); FieldInfo fieldInfo = AccessTools.Field(_skinManagerType, "CustomPlayerSpines"); _playerStartMethod = AccessTools.Method(typeof(PlayerFarming), "Start", (Type[])null, (Type[])null); _apiPlayerStartPatchMethod = AccessTools.Method(_skinManagerType, "PlayerFarming_Start", (Type[])null, (Type[])null); _apiPlayerSetSkinPatchMethod = AccessTools.Method(_skinManagerType, "PlayerFarming_SetSkin", (Type[])null, (Type[])null); _startCompleteField = AccessTools.Field(typeof(PlayerFarming), "StartComplete"); _dropdownDisplayTypeField = AccessTools.Field(typeof(MMDropdown), "_displayType"); Type type = AccessTools.TypeByName("CustomSpineLoader.SpineLoaderHelper.PlayerSpineLoader"); _cultTweakerFleeceIndexP1Field = ((type != null) ? AccessTools.Field(type, "currentFleeceIndexP1") : null); _refreshAnimationFromStateMethod = AccessTools.Method(typeof(SimpleSpineAnimator), "UpdateAnimFromState", (Type[])null, (Type[])null); _playerBleatOverrideProperty = AccessTools.Property(_skinManagerType, "PlayerBleatOverride"); _playerSkinOverrideProperty = AccessTools.Property(_skinManagerType, "PlayerSkinOverride"); if (_selectedSpineField == null || _selectedSpineTwoField == null || fieldInfo == null || _playerStartMethod == null) { Plugin.Log.LogWarning((object)"[MP][CustomSpine] Installed API layout is unsupported; compatibility disabled without changing normal COTLMP behavior"); return; } _customPlayerSpines = fieldInfo.GetValue(null) as IDictionary; if (_customPlayerSpines == null) { Plugin.Log.LogWarning((object)"[MP][CustomSpine] COTL_API player-spine registry was unavailable; compatibility disabled"); return; } for (int i = 2; i < 8; i++) { SlotChoices[i] = ((i % 2 == 0) ? "Match Player 1" : "Match Player 2"); } _active = true; try { PatchPlayerLifecycle(harmony, _playerStartMethod); MethodInfo target = AccessTools.Method(typeof(PlayerFarming), "SetSkin", new Type[1] { typeof(bool) }, (Type[])null); PatchPlayerLifecycle(harmony, target); PatchCultTweakerFleeceCompatibility(harmony, target); PatchCotlApiPlayerSkinCompatibility(harmony, target); PatchCotlApiBleatCompatibility(harmony); HarmonyMethod val = new HarmonyMethod(typeof(CustomSpineCompatibility), "PlayerStart_RefreshCustomAnimation_Postfix", (Type[])null); val.priority = 0; val.after = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val2 = val; harmony.Patch((MethodBase)_playerStartMethod, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo = AccessTools.Method(_skinManagerType, "ChangeSelectedPlayerSpine", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CustomSpineCompatibility), "ChangeSelectedPlayerSpine_Prefix", (Type[])null), new HarmonyMethod(typeof(CustomSpineCompatibility), "ChangeSelectedPlayerSpine_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo2 = AccessTools.Method(_skinManagerType, "AddPlayerSpine", (Type[])null, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CustomSpineCompatibility), "AddPlayerSpine_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo3 = AccessTools.Method(_skinManagerType, "PlayerFarming_OnEnable", (Type[])null, (Type[])null); if (methodInfo3 != null && _apiPlayerStartPatchMethod != null) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(CustomSpineCompatibility), "ApiPlayerOnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[MP][CustomSpine] Guarded COTL_API player OnEnable from premature full Start/HUD initialization"); } MethodInfo methodInfo4 = AccessTools.Method(typeof(MMDropdown), "Open", (Type[])null, (Type[])null); if (methodInfo4 != null && _dropdownDisplayTypeField != null) { harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(CustomSpineCompatibility), "CustomSpineDropdown_Open_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } CreateSettingsDropdowns(); RefreshResolvedSelection(2); RefreshResolvedSelection(3); Plugin.Log.LogInfo((object)"[MP8][CustomSpine] P3-P8 compatibility enabled (alternating P1/P2 matches by default)"); } catch (Exception ex) { _active = false; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Compatibility initialization failed safely: " + ex.GetBaseException().Message)); } } private static bool ApiPlayerOnEnable_Prefix(PlayerFarming __0, ref bool __result) { if (!_active || (Object)(object)__0 == (Object)null) { return true; } __result = true; if (!HasCompletedStart(__0)) { return false; } if (PlayerFarming.players == null || PlayerFarming.players.Count <= 0 || (Object)(object)PlayerFarming.Instance == (Object)null) { return false; } PlayerSpineRoute_Prefix(__0, out var __state); try { _apiPlayerStartPatchMethod.Invoke(null, new object[1] { __0 }); RefreshCustomSpineAnimation(__0, "OnEnable"); } catch (Exception ex) { if (!_onEnableWarningLogged) { _onEnableWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Safe OnEnable spine refresh failed; the next normal player Start/SetSkin will retry: " + ex.GetBaseException().Message)); } } finally { PlayerSpineRoute_Finalizer(null, __state); } return false; } private static void PatchPlayerLifecycle(Harmony harmony, MethodInfo target) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown if (!(target == null)) { HarmonyMethod val = new HarmonyMethod(typeof(CustomSpineCompatibility), "PlayerSpineRoute_Prefix", (Type[])null); val.priority = 800; val.before = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val2 = val; val = new HarmonyMethod(typeof(CustomSpineCompatibility), "PlayerSpineRoute_Finalizer", (Type[])null); val.priority = 0; val.after = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val3 = val; harmony.Patch((MethodBase)target, val2, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null); } } private static void PatchCultTweakerFleeceCompatibility(Harmony harmony, MethodInfo target) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown if (target == null || _cultTweakerFleeceIndexP1Field == null) { Plugin.Log.LogWarning((object)"[MP8][CustomSpine] CultTweaker fleece compatibility unavailable; extra-player fleece isolation was not installed"); return; } HarmonyMethod val = new HarmonyMethod(typeof(CultTweakerFleecePatch), "Prefix", (Type[])null); val.priority = 900; val.before = new string[1] { "InfernoDragon0.cotl.CustomSpineLoader" }; HarmonyMethod val2 = val; val = new HarmonyMethod(typeof(CultTweakerFleecePatch), "Finalizer", (Type[])null); val.priority = 0; val.after = new string[1] { "InfernoDragon0.cotl.CustomSpineLoader" }; HarmonyMethod val3 = val; harmony.Patch((MethodBase)target, val2, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[MP8][CustomSpine] Isolated P3-P8 from CultTweaker's two-player fleece override"); } private static void PatchCotlApiBleatCompatibility(Harmony harmony) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown MethodInfo methodInfo = Patches.FindNestedMoveNext(typeof(PlayerFarming), "BleatRoutine"); if (methodInfo == null || _playerBleatOverrideProperty == null) { Plugin.Log.LogWarning((object)"[MP8][CustomSpine] COTL_API bleat compatibility unavailable; extra-player bleat routing was not installed"); return; } HarmonyMethod val = new HarmonyMethod(typeof(CotlApiExtraPlayerBleatPatch), "Prefix", (Type[])null); val.priority = 900; val.before = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val2 = val; val = new HarmonyMethod(typeof(CotlApiExtraPlayerBleatPatch), "Finalizer", (Type[])null); val.priority = 0; val.after = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val3 = val; harmony.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[MP8][CustomSpine] Routed P3-P8 bleats to their own player instances"); } private static void PatchCotlApiPlayerSkinCompatibility(Harmony harmony, MethodInfo target) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown if (target == null || _apiPlayerSetSkinPatchMethod == null) { Plugin.Log.LogWarning((object)"[MP8][CustomSpine] COTL_API player-skin compatibility unavailable; extra-player skin identity guards were not installed"); return; } HarmonyMethod val = new HarmonyMethod(typeof(CotlApiExtraPlayerSkinPatch), "Prefix", (Type[])null); val.priority = 850; val.before = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val2 = val; val = new HarmonyMethod(typeof(CotlApiExtraPlayerSkinPatch), "Postfix", (Type[])null); val.priority = 0; val.after = new string[2] { "io.github.xhayper.COTL_API", "InfernoDragon0.cotl.CustomSpineLoader" }; HarmonyMethod val3 = val; val = new HarmonyMethod(typeof(CotlApiExtraPlayerSkinPatch), "Finalizer", (Type[])null); val.priority = 0; val.after = new string[1] { "io.github.xhayper.COTL_API" }; HarmonyMethod val4 = val; harmony.Patch((MethodBase)target, val2, val3, (HarmonyMethod)null, val4, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[MP8][CustomSpine] Isolated P3-P8 skin, hurt, and player-type state from COTL_API's two-player globals"); } private static void PlayerStart_RefreshCustomAnimation_Postfix(PlayerFarming __instance) { RefreshCustomSpineAnimation(__instance, "Start"); } private static void RefreshCustomSpineAnimation(PlayerFarming player, string source, bool logSuccess = true) { if (!_active || (Object)(object)player == (Object)null || player.playerID < 2 || player.playerID >= 8 || !ShouldPreserveCustomSpineColor(player) || _refreshAnimationFromStateMethod == null) { return; } try { SimpleSpineAnimator val = player.simpleSpineAnimator; if ((Object)(object)val == (Object)null) { val = (player.simpleSpineAnimator = ((Component)player).GetComponentInChildren<SimpleSpineAnimator>()); } if (!((Object)(object)val == (Object)null)) { _refreshAnimationFromStateMethod.Invoke(val, null); if ((Object)(object)player.Spine != (Object)null && player.Spine.AnimationState != null && ((SkeletonRenderer)player.Spine).skeleton != null) { player.Spine.AnimationState.Apply(((SkeletonRenderer)player.Spine).skeleton, true); player.Spine.Update(0f, false); } if (logSuccess) { Plugin.Log.LogInfo((object)$"[MP][CustomSpine] Refreshed player {player.playerID + 1} animation after spine rebuild ({source})"); } } } catch (Exception ex) { if (!_animationWarningLogged) { _animationWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Animation refresh failed safely: " + ex.GetBaseException().Message)); } } } private static void LogFleeceWarning(Exception exception) { if (!_fleeceWarningLogged) { _fleeceWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] CultTweaker fleece isolation failed safely: " + exception.GetBaseException().Message)); } } private static void LogBleatWarning(Exception exception) { if (!_bleatWarningLogged) { _bleatWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Extra-player bleat routing failed safely: " + exception.GetBaseException().Message)); } } private static void LogSkinIdentityWarning(Exception exception) { if (!_skinIdentityWarningLogged) { _skinIdentityWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Extra-player skin identity isolation failed safely: " + exception.GetBaseException().Message)); } } private static void LogHurtSkinWarning(Exception exception) { if (!_hurtSkinWarningLogged) { _hurtSkinWarningLogged = true; Plugin.Log.LogWarning((object)("[MP][CustomSpine] Extra-player hurt-skin correction failed safely: " + exception.GetBaseException().Message)); } } private static object FindDictionaryKey(IDictionary dictionary, string name) { fore
BepInEx/plugins/KOMods-COTLMP_8_Player_Expansion/COTLMP - Room Size Scaling.dll
Decompiled 18 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; 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: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace COTLMultiplayerRoomSizeScaling { [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, }; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.cotlmp.roomsizescaling", "COTL Multiplayer Room Size Scaling 8P", "0.2.0")] public sealed class RoomSizeScalingPlugin : BaseUnityPlugin { public const string PluginGuid = "com.cotlmp.roomsizescaling"; public const string PluginName = "COTL Multiplayer Room Size Scaling 8P"; public const string PluginVersion = "0.2.0"; 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-8 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-8 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.2.0-8P loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } }
BepInEx/plugins/KOMods-COTLMP_8_Player_Expansion/COTLMP - Tarot Addon.dll
Decompiled 18 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Lamb.UI; using MMBiomeGeneration; using Rewired; using TMPro; using Unify.Input; using UnityEngine; using UnityEngine.UI; 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: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerTarotAddon; [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.cotlmp.tarotaddon", "COTL Multiplayer Tarot Addon 8P", "0.3.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(UITarotChoiceOverlayController), "Show", new Type[3] { typeof(TarotCard), typeof(TarotCard), typeof(bool) }, (Type[])null), null, "TarotChoiceOverlay_Show_Postfix", "UITarotChoiceOverlayController.Show"); PatchIfFound(AccessTools.Method(typeof(Shrines), "OnDisable", Type.EmptyTypes, (Type[])null), null, "Shrines_OnDisable_Postfix", "Shrines.OnDisable()"); Log.LogInfo((object)"Tarot addon 8P 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 sealed class TarotChoiceOwnerBanner : MonoBehaviour { private static readonly FieldInfo Indicator1Field = AccessTools.Field(typeof(UITarotChoiceOverlayController), "coopIndicator_card1"); private static readonly FieldInfo Indicator2Field = AccessTools.Field(typeof(UITarotChoiceOverlayController), "coopIndicator_card2"); private UITarotChoiceOverlayController _overlay; private CoopIndicatorIcon _indicator1; private CoopIndicatorIcon _indicator2; private TextMeshProUGUI _label; public static void Attach(UITarotChoiceOverlayController overlay, PlayerFarming owner) { if (!((Object)(object)overlay == (Object)null)) { TarotChoiceOwnerBanner tarotChoiceOwnerBanner = ((Component)overlay).GetComponent<TarotChoiceOwnerBanner>(); if ((Object)(object)tarotChoiceOwnerBanner == (Object)null) { tarotChoiceOwnerBanner = ((Component)overlay).gameObject.AddComponent<TarotChoiceOwnerBanner>(); } tarotChoiceOwnerBanner._overlay = overlay; tarotChoiceOwnerBanner._indicator1 = (CoopIndicatorIcon)((Indicator1Field != null) ? /*isinst with value type is only supported in some contexts*/: null); tarotChoiceOwnerBanner._indicator2 = (CoopIndicatorIcon)((Indicator2Field != null) ? /*isinst with value type is only supported in some contexts*/: null); tarotChoiceOwnerBanner.CreateOrRefreshLabel(owner); tarotChoiceOwnerBanner.HideVanillaOwnerIcons(); } } private void LateUpdate() { HideVanillaOwnerIcons(); if ((Object)(object)_label != (Object)null) { ((TMP_Text)_label).transform.SetAsLastSibling(); } } private void HideVanillaOwnerIcons() { if ((Object)(object)_indicator1 != (Object)null && ((Component)_indicator1).gameObject.activeSelf) { ((Component)_indicator1).gameObject.SetActive(false); } if ((Object)(object)_indicator2 != (Object)null && ((Component)_indicator2).gameObject.activeSelf) { ((Component)_indicator2).gameObject.SetActive(false); } } private void CreateOrRefreshLabel(PlayerFarming owner) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_overlay == (Object)null) { return; } if ((Object)(object)_label == (Object)null) { GameObject val = new GameObject("COTLMP Tarot Owner Label", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.transform.SetParent(((Component)_overlay).transform, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 1f); component.anchorMax = new Vector2(0.5f, 1f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(0f, -38f); component.sizeDelta = new Vector2(900f, 72f); _label = val.GetComponent<TextMeshProUGUI>(); TextMeshProUGUI componentInChildren = ((Component)_overlay).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)_label).font = ((TMP_Text)componentInChildren).font; ((TMP_Text)_label).fontSharedMaterial = ((TMP_Text)componentInChildren).fontSharedMaterial; } ((TMP_Text)_label).fontSize = 38f; ((TMP_Text)_label).fontStyle = (FontStyles)1; ((TMP_Text)_label).alignment = (TextAlignmentOptions)514; ((Graphic)_label).color = Color.white; ((TMP_Text)_label).outlineColor = Color32.op_Implicit(Color.black); ((TMP_Text)_label).outlineWidth = 0.2f; ((Graphic)_label).raycastTarget = false; } int num = ((!((Object)(object)owner != (Object)null)) ? 1 : (owner.playerID + 1)); ((TMP_Text)_label).text = "PLAYER " + num + " - CHOOSE YOUR TAROT CARD"; Color playerColor = GetPlayerColor(num - 1); ((Graphic)_label).color = playerColor; } private static Color GetPlayerColor(int playerID) { //IL_001c: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[8] { new Color(1f, 0.3f, 0.3f, 1f), new Color(0.7f, 0.45f, 1f, 1f), new Color(1f, 0.65f, 0.15f, 1f), new Color(0.45f, 0.75f, 1f, 1f), new Color(0.3f, 1f, 0.4f, 1f), new Color(1f, 0.9f, 0.2f, 1f), new Color(0.2f, 1f, 1f, 1f), new Color(1f, 0.4f, 0.8f, 1f) }; return array[Mathf.Clamp(playerID, 0, array.Length - 1)]; } } internal static class TarotPatches { private const int MaxPlayers = 8; 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 ((Object)(object)playerFarming != (Object)(object)trackedTarotOwner) { int instanceID = ((Object)__instance).GetInstanceID(); if (WarnedSuppressedDiscardByInstance.Add(instanceID)) { TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Suppressed vanilla discard grant; P" + (trackedTarotOwner.playerID + 1) + " keeps only the card they chose.")); } __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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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 ((Object)(object)playerFarming != (Object)(object)trackedShrineTarotOwner) { int instanceID = ((Object)__instance).GetInstanceID(); if (WarnedSuppressedShrineDiscardByInstance.Add(instanceID)) { TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Suppressed vanilla discard grant; P" + (trackedShrineTarotOwner.playerID + 1) + " keeps only the card they chose.")); } __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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_028c: 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_028f: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01de: 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, 8); HashSet<int> hashSet = new HashSet<int>(); int num2 = CountRelaxedActivePlayerSlots(); if (CoopManager.AllPlayerGameObjects != null) { int num3 = Mathf.Min(CoopManager.AllPlayerGameObjects.Length, 8); 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 < 8; 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, 8); } 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 < 8 && !((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 num = Mathf.Clamp(starter.playerID, 0, 7); for (int i = 1; i < 8; i++) { int slot = (num + i) % 8; EnqueueSlotIfActive(queue, slot); } return queue; } public static void TarotChoiceOverlay_Show_Postfix(UITarotChoiceOverlayController __instance) { if (!((Object)(object)__instance == (Object)null)) { PlayerFarming owner = null; try { owner = (((Object)(object)MonoSingleton<UINavigatorNew>.Instance != (Object)null) ? MonoSingleton<UINavigatorNew>.Instance.AllowInputOnlyFromPlayer : null); } catch { } TarotChoiceOwnerBanner.Attach(__instance, owner); } } 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); } } }