Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AddSkillTree v1.0.0
BepInEx\plugins\REPOModSkillTree\REPOModSkillTree.dll
Decompiled a week 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("REPOModSkillTree")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5aa026f94994f6f93e9dca634333522a1e5097a0")] [assembly: AssemblyProduct("REPO Skill Tree")] [assembly: AssemblyTitle("REPOModSkillTree")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOModSkillTree { [Serializable] internal sealed class CreditProgressData { public int version = 7; public long totalCredits; public int carryRate = 100; public int highestFloorReached; public int healthLevel; public int staminaLevel; public int strengthLevel; public int rangeLevel; public int crouchStaminaRecoveryLevel; public int speedLevel; public int jumpLevel; public int mapPlayerCountLevel; public int healthAppliedLevel; public string healthRunRoomName = string.Empty; public string healthRestartRoomName = string.Empty; public int healthRestartServerTimestamp; public string[] purchasedNodeIds = Array.Empty<string>(); } internal static class CreditDataStore { private const string FileName = "RepoSkillTreeProgress.json"; private const int CurrentDataVersion = 7; private static CreditProgressData _data = new CreditProgressData(); private static string _filePath; internal static long TotalCredits => _data.totalCredits; internal static int CarryRate => CreditMath.NormalizeCarryRate(_data.carryRate); internal static int HighestFloorReached => _data.highestFloorReached; internal static int HealthLevel => _data.healthLevel; internal static int StaminaLevel => _data.staminaLevel; internal static int PendingHealthLevels => Math.Max(0, _data.healthLevel - _data.healthAppliedLevel); internal static bool PreserveCurrentHealthOnLoadedSave { get; private set; } internal static string FilePath => _filePath ?? Path.Combine(Paths.ConfigPath, "RepoSkillTreeProgress.json"); internal static void Load() { _filePath = Path.Combine(Paths.ConfigPath, "RepoSkillTreeProgress.json"); _data = new CreditProgressData(); PreserveCurrentHealthOnLoadedSave = false; try { Directory.CreateDirectory(Paths.ConfigPath); if (!File.Exists(_filePath)) { TrySave(); return; } CreditProgressData creditProgressData = JsonUtility.FromJson<CreditProgressData>(File.ReadAllText(_filePath)); if (creditProgressData == null) { throw new InvalidDataException("Progress JSON did not contain an object."); } bool flag = creditProgressData.version < 7; if (creditProgressData.purchasedNodeIds == null) { creditProgressData.purchasedNodeIds = Array.Empty<string>(); flag = true; } if (creditProgressData.highestFloorReached < 0) { creditProgressData.highestFloorReached = 0; flag = true; } int num = Math.Max(0, Math.Min(10, creditProgressData.healthLevel)); if (num != creditProgressData.healthLevel) { creditProgressData.healthLevel = num; flag = true; } int num2 = Math.Max(0, Math.Min(11, creditProgressData.staminaLevel)); if (num2 != creditProgressData.staminaLevel) { creditProgressData.staminaLevel = num2; flag = true; } flag |= NormalizeLevel(ref creditProgressData.strengthLevel, 4); flag |= NormalizeLevel(ref creditProgressData.rangeLevel, 2); flag |= NormalizeLevel(ref creditProgressData.crouchStaminaRecoveryLevel, 1); flag |= NormalizeLevel(ref creditProgressData.speedLevel, 2); flag |= NormalizeLevel(ref creditProgressData.jumpLevel, 1); flag |= NormalizeLevel(ref creditProgressData.mapPlayerCountLevel, 1); if (creditProgressData.version < 3) { creditProgressData.healthAppliedLevel = 0; flag = true; } int num3 = Math.Max(0, Math.Min(creditProgressData.healthLevel, creditProgressData.healthAppliedLevel)); if (num3 != creditProgressData.healthAppliedLevel) { creditProgressData.healthAppliedLevel = num3; flag = true; } if (Array.IndexOf(creditProgressData.purchasedNodeIds, "tier1_hp_up") >= 0 && creditProgressData.healthLevel == 0) { creditProgressData.healthLevel = 1; flag = true; } if (creditProgressData.healthLevel > 0 && Array.IndexOf(creditProgressData.purchasedNodeIds, "tier1_hp_up") < 0) { string[] array = new string[creditProgressData.purchasedNodeIds.Length + 1]; Array.Copy(creditProgressData.purchasedNodeIds, array, creditProgressData.purchasedNodeIds.Length); array[^1] = "tier1_hp_up"; creditProgressData.purchasedNodeIds = array; flag = true; } if (Array.IndexOf(creditProgressData.purchasedNodeIds, "tier1_stamina_up") >= 0 && creditProgressData.staminaLevel == 0) { creditProgressData.staminaLevel = 1; flag = true; } if (creditProgressData.staminaLevel > 0 && Array.IndexOf(creditProgressData.purchasedNodeIds, "tier1_stamina_up") < 0) { string[] array2 = new string[creditProgressData.purchasedNodeIds.Length + 1]; Array.Copy(creditProgressData.purchasedNodeIds, array2, creditProgressData.purchasedNodeIds.Length); array2[^1] = "tier1_stamina_up"; creditProgressData.purchasedNodeIds = array2; flag = true; } if (creditProgressData.totalCredits < 0) { Plugin.Logger.LogWarning((object)"Saved totalCredits was negative; using 0K."); creditProgressData.totalCredits = 0L; } if (creditProgressData.carryRate <= 0) { creditProgressData.carryRate = 100; } else { creditProgressData.carryRate = CreditMath.NormalizeCarryRate(creditProgressData.carryRate); } if (creditProgressData.version != 7) { creditProgressData.version = 7; flag = true; } _data = creditProgressData; if (flag) { TrySave(); Plugin.Logger.LogInfo((object)$"Migrated progress data to schema version {7}."); } Plugin.Logger.LogInfo((object)$"Loaded {TotalCredits}K credits at {CarryRate}% carry rate."); } catch (Exception ex) { _data = new CreditProgressData(); Plugin.Logger.LogWarning((object)("Could not load credit data; using 0K. " + ex.Message)); } } internal static int GetLevel(PermanentUpgradeStat stat) { return stat switch { PermanentUpgradeStat.Health => _data.healthLevel, PermanentUpgradeStat.Stamina => _data.staminaLevel, PermanentUpgradeStat.Strength => _data.strengthLevel, PermanentUpgradeStat.Range => _data.rangeLevel, PermanentUpgradeStat.CrouchStaminaRecovery => _data.crouchStaminaRecoveryLevel, PermanentUpgradeStat.Speed => _data.speedLevel, PermanentUpgradeStat.ExtraJump => _data.jumpLevel, PermanentUpgradeStat.MapPlayerCount => _data.mapPlayerCountLevel, PermanentUpgradeStat.CarryRate => Math.Max(0, Math.Min(5, (_data.carryRate - 100) / 20)), _ => throw new ArgumentOutOfRangeException("stat", stat, "Unsupported permanent upgrade stat."), }; } internal static bool IsNodePurchased(string nodeId) { return Array.IndexOf(_data.purchasedNodeIds, nodeId) >= 0; } internal static NodePurchaseResult TryPurchaseNode(UpgradeNodeDefinition node) { if (node == null) { throw new ArgumentNullException("node"); } switch (UpgradeNodeService.GetAvailability(node)) { case NodeAvailability.Owned: return NodePurchaseResult.AlreadyOwned; case NodeAvailability.Locked: return NodePurchaseResult.Locked; case NodeAvailability.InsufficientCredits: return NodePurchaseResult.InsufficientCredits; case NodeAvailability.MaximumReached: return NodePurchaseResult.MaximumReached; case NodeAvailability.SinglePlayerOnly: return NodePurchaseResult.SinglePlayerOnly; default: throw new ArgumentOutOfRangeException("node", "Unknown node availability."); case NodeAvailability.Available: { long totalCredits = _data.totalCredits; int level = GetLevel(node.Stat); int carryRate = _data.carryRate; string[] purchasedNodeIds = _data.purchasedNodeIds; _data.totalCredits -= node.CostK; if (node.Stat == PermanentUpgradeStat.CarryRate) { _data.carryRate = Math.Min(200, _data.carryRate + 20); } else { SetLevel(node.Stat, level + 1); } _data.purchasedNodeIds = new string[purchasedNodeIds.Length + 1]; Array.Copy(purchasedNodeIds, _data.purchasedNodeIds, purchasedNodeIds.Length); _data.purchasedNodeIds[_data.purchasedNodeIds.Length - 1] = node.Id; if (TrySave()) { Plugin.Logger.LogInfo((object)$"Purchased {node.Id} for {node.CostK}K."); return NodePurchaseResult.Purchased; } _data.totalCredits = totalCredits; SetLevel(node.Stat, level); _data.carryRate = carryRate; _data.purchasedNodeIds = purchasedNodeIds; return NodePurchaseResult.SaveFailed; } } } private static bool NormalizeLevel(ref int level, int maximum) { int num = Math.Max(0, Math.Min(maximum, level)); if (num == level) { return false; } level = num; return true; } private static void SetLevel(PermanentUpgradeStat stat, int level) { switch (stat) { case PermanentUpgradeStat.Health: _data.healthLevel = level; break; case PermanentUpgradeStat.Stamina: _data.staminaLevel = level; break; case PermanentUpgradeStat.Strength: _data.strengthLevel = level; break; case PermanentUpgradeStat.Range: _data.rangeLevel = level; break; case PermanentUpgradeStat.CrouchStaminaRecovery: _data.crouchStaminaRecoveryLevel = level; break; case PermanentUpgradeStat.Speed: _data.speedLevel = level; break; case PermanentUpgradeStat.ExtraJump: _data.jumpLevel = level; break; case PermanentUpgradeStat.MapPlayerCount: _data.mapPlayerCountLevel = level; break; case PermanentUpgradeStat.CarryRate: _data.carryRate = 100 + level * 20; break; default: throw new ArgumentOutOfRangeException("stat", stat, "Unsupported permanent upgrade stat."); } } internal static bool TryMarkHealthBonusApplied(int appliedLevel) { int num = Math.Max(_data.healthAppliedLevel, Math.Min(_data.healthLevel, appliedLevel)); if (num == _data.healthAppliedLevel) { return true; } int healthAppliedLevel = _data.healthAppliedLevel; _data.healthAppliedLevel = num; if (TrySave()) { return true; } _data.healthAppliedLevel = healthAppliedLevel; return false; } internal static bool ResetHealthBonusForNewGame() { PreserveCurrentHealthOnLoadedSave = false; if (_data.healthAppliedLevel == 0) { return true; } int healthAppliedLevel = _data.healthAppliedLevel; _data.healthAppliedLevel = 0; if (TrySave()) { Plugin.Logger.LogInfo((object)$"Reset current-HP bonus marker for a new game save (previously applied level {healthAppliedLevel})."); return true; } _data.healthAppliedLevel = healthAppliedLevel; return false; } internal static void PreserveCurrentHealthForLoadedSave() { PreserveCurrentHealthOnLoadedSave = true; Plugin.Logger.LogInfo((object)"Existing REPO save selected; preserving its saved current HP."); } internal static bool PrepareHealthBonusForMultiplayerRun(string roomName) { if (string.IsNullOrEmpty(roomName)) { throw new ArgumentException("A multiplayer room name is required.", "roomName"); } if (_data.healthRunRoomName == roomName) { return true; } string healthRunRoomName = _data.healthRunRoomName; int healthAppliedLevel = _data.healthAppliedLevel; _data.healthRunRoomName = roomName; _data.healthAppliedLevel = 0; if (TrySave()) { Plugin.Logger.LogInfo((object)"Prepared the current-HP bonus for a new multiplayer run."); return true; } _data.healthRunRoomName = healthRunRoomName; _data.healthAppliedLevel = healthAppliedLevel; return false; } internal static bool PrepareHealthBonusForMultiplayerRestart(string roomName, int serverTimestamp, out bool isNewRestart) { if (string.IsNullOrEmpty(roomName)) { throw new ArgumentException("A multiplayer room name is required.", "roomName"); } isNewRestart = false; if (_data.healthRestartRoomName == roomName && serverTimestamp - _data.healthRestartServerTimestamp <= 0) { return true; } string healthRunRoomName = _data.healthRunRoomName; string healthRestartRoomName = _data.healthRestartRoomName; int healthRestartServerTimestamp = _data.healthRestartServerTimestamp; int healthAppliedLevel = _data.healthAppliedLevel; _data.healthRunRoomName = roomName; _data.healthRestartRoomName = roomName; _data.healthRestartServerTimestamp = serverTimestamp; _data.healthAppliedLevel = 0; if (TrySave()) { isNewRestart = true; Plugin.Logger.LogInfo((object)"Prepared the current-HP bonus for a multiplayer restart after game over."); return true; } _data.healthRunRoomName = healthRunRoomName; _data.healthRestartRoomName = healthRestartRoomName; _data.healthRestartServerTimestamp = healthRestartServerTimestamp; _data.healthAppliedLevel = healthAppliedLevel; return false; } internal static bool TryCommitClearedFloor(int floorReached, long creditedK) { if (floorReached <= 0) { throw new ArgumentOutOfRangeException("floorReached"); } if (creditedK < 0) { throw new ArgumentOutOfRangeException("creditedK"); } long totalCredits; try { totalCredits = checked(_data.totalCredits + creditedK); } catch (OverflowException) { Plugin.Logger.LogError((object)"Credit total overflow; refusing to update saved balance."); return false; } long totalCredits2 = _data.totalCredits; int highestFloorReached = _data.highestFloorReached; _data.totalCredits = totalCredits; _data.highestFloorReached = Math.Max(highestFloorReached, floorReached); if (_data.totalCredits == totalCredits2 && _data.highestFloorReached == highestFloorReached) { return true; } if (TrySave()) { return true; } _data.totalCredits = totalCredits2; _data.highestFloorReached = highestFloorReached; return false; } private static bool TrySave() { string text = _filePath + ".tmp"; try { Directory.CreateDirectory(Paths.ConfigPath); File.WriteAllText(text, JsonUtility.ToJson((object)_data, true)); if (File.Exists(_filePath)) { File.Replace(text, _filePath, null); } else { File.Move(text, _filePath); } return true; } catch (Exception arg) { Plugin.Logger.LogError((object)$"Could not save credit data to '{_filePath}': {arg}"); try { if (File.Exists(text)) { File.Delete(text); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not remove temporary credit file: " + ex.Message)); } return false; } } } internal static class CreditMath { internal const int MinimumCarryRate = 100; internal const int MaximumCarryRate = 200; internal static long RecoveredAmountToK(long rawAmount) { if (rawAmount > 0) { return rawAmount / 1000; } return 0L; } internal static int NormalizeCarryRate(int carryRate) { return Math.Max(100, Math.Min(200, carryRate)); } internal static long CalculateCarryCredits(long rawAmount, int carryRate) { return CalculateCreditsAtRate(rawAmount, NormalizeCarryRate(carryRate)); } internal static long CalculateMultiplayerCarryCredits(long rawAmount, int singlePlayerCarryRate) { int multiplayerCarryRate = GetMultiplayerCarryRate(singlePlayerCarryRate); return CalculateCreditsAtRate(rawAmount, multiplayerCarryRate); } internal static int GetMultiplayerCarryRate(int singlePlayerCarryRate) { return NormalizeCarryRate(singlePlayerCarryRate) / 2; } private static long CalculateCreditsAtRate(long rawAmount, int carryRate) { return (long)decimal.Floor((decimal)RecoveredAmountToK(rawAmount) * (decimal)carryRate / 100m); } } internal static class FloorHaulTracker { private sealed class ClearedFloorReward { internal int Floor { get; } internal long RawRecovered { get; } internal long CreditsK { get; } internal ClearedFloorReward(int floor, long rawRecovered, long creditsK) { Floor = floor; RawRecovered = rawRecovered; CreditsK = creditsK; } } private static long _pendingRawRecovered; private static readonly Queue<ClearedFloorReward> UncommittedRewards = new Queue<ClearedFloorReward>(); private static bool _saveFailureReported; internal static long PendingRawRecovered => _pendingRawRecovered; internal static bool HasUncommittedRewards => UncommittedRewards.Count > 0; internal static void RecordSuccessfulExtraction(long rawRecovered) { if (rawRecovered <= 0) { return; } checked { try { _pendingRawRecovered += rawRecovered; Plugin.Logger.LogInfo((object)$"Recorded extracted haul for this floor: {rawRecovered}; pending total {_pendingRawRecovered}."); } catch (OverflowException) { Plugin.Logger.LogError((object)"Extracted haul overflow; discarding this floor's unbanked haul."); _pendingRawRecovered = 0L; } } } internal static void CompleteSuccessfulFloor(int floorNumber, bool isMultiplayer) { if (floorNumber <= 0) { Plugin.Logger.LogError((object)$"Cannot record an invalid cleared floor number: {floorNumber}."); return; } long pendingRawRecovered = _pendingRawRecovered; _pendingRawRecovered = 0L; int carryRate = CreditDataStore.CarryRate; int num = (isMultiplayer ? CreditMath.GetMultiplayerCarryRate(carryRate) : carryRate); long num2 = (isMultiplayer ? CreditMath.CalculateMultiplayerCarryCredits(pendingRawRecovered, carryRate) : CreditMath.CalculateCarryCredits(pendingRawRecovered, carryRate)); Plugin.Logger.LogInfo((object)($"Calculating floor reward: raw={pendingRawRecovered}, personalCarryRate={carryRate}%, " + $"multiplayer={isMultiplayer}, appliedCarryRate={num}%, credited={num2}K.")); UncommittedRewards.Enqueue(new ClearedFloorReward(floorNumber, pendingRawRecovered, num2)); RetryUncommittedRewards(); } internal static void RetryUncommittedRewards() { while (UncommittedRewards.Count > 0) { ClearedFloorReward clearedFloorReward = UncommittedRewards.Peek(); if (!CreditDataStore.TryCommitClearedFloor(clearedFloorReward.Floor, clearedFloorReward.CreditsK)) { if (!_saveFailureReported) { _saveFailureReported = true; Plugin.Logger.LogError((object)"A cleared-floor reward could not be saved. It will be retried while the game remains open."); CreditBalanceView.RefreshAll(); } return; } UncommittedRewards.Dequeue(); Plugin.Logger.LogInfo((object)($"Banked {clearedFloorReward.CreditsK}K from {clearedFloorReward.RawRecovered} recovered on cleared floor {clearedFloorReward.Floor}. " + $"Balance: {CreditDataStore.TotalCredits}K.")); CreditBalanceView.RefreshAll(); } if (_saveFailureReported) { _saveFailureReported = false; Plugin.Logger.LogInfo((object)"All previously unsaved cleared-floor rewards have been saved."); } } internal static void DiscardIncompleteFloor() { if (_pendingRawRecovered > 0) { Plugin.Logger.LogInfo((object)$"Discarding {_pendingRawRecovered} unbanked haul from an incomplete floor."); } _pendingRawRecovered = 0L; } internal static void ResetForNewRun() { DiscardIncompleteFloor(); } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class RunStartCreditResetPatch { [HarmonyPrefix] private static void Prefix() { FloorHaulTracker.ResetForNewRun(); } } [HarmonyPatch(typeof(ExtractionPoint), "StateComplete")] internal static class ExtractionCompletePatch { private sealed class ExtractionCapture { internal readonly long RawRecovered; internal ExtractionCapture(long rawRecovered) { RawRecovered = rawRecovered; } } private static readonly FieldInfo StateStartField = AccessTools.Field(typeof(ExtractionPoint), "stateStart"); private static readonly FieldInfo CurrentStateField = AccessTools.Field(typeof(ExtractionPoint), "currentState"); private static readonly FieldInfo HaulSurplusField = AccessTools.Field(typeof(ExtractionPoint), "haulSurplus"); private static readonly FieldInfo IsShopField = AccessTools.Field(typeof(ExtractionPoint), "isShop"); private static readonly FieldInfo TaxReturnField = AccessTools.Field(typeof(ExtractionPoint), "taxReturn"); [HarmonyPrefix] private static void Prefix(ExtractionPoint __instance, out ExtractionCapture __state) { __state = null; if ((Object)(object)__instance == (Object)null || !SemiFunc.RunIsLevel()) { return; } try { if ((bool)StateStartField.GetValue(__instance) && string.Equals(CurrentStateField.GetValue(__instance)?.ToString(), "Complete", StringComparison.Ordinal) && !(bool)IsShopField.GetValue(__instance) && !(bool)TaxReturnField.GetValue(__instance)) { long num = (long)__instance.haulGoal + (long)(int)HaulSurplusField.GetValue(__instance); if (num > 0) { __state = new ExtractionCapture(num); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not inspect completed extraction: " + ex.Message)); } } [HarmonyPostfix] private static void Postfix(ExtractionCapture __state) { if (__state == null) { return; } try { FloorHaulTracker.RecordSuccessfulExtraction(__state.RawRecovered); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not record completed extraction: " + ex.Message)); } } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] internal static class FloorCompletionPatch { [HarmonyPrefix] private static void Prefix(RunManager __instance, bool _completedLevel, bool _levelFailed) { bool flag = SemiFunc.IsMultiplayer(); if (!flag || SemiFunc.IsMasterClientOrSingleplayer()) { if ((Object)(object)__instance == (Object)null) { Plugin.Logger.LogError((object)"RunManager.ChangeLevel received a null manager; floor outcome was not processed."); } else if (_levelFailed) { FloorHaulTracker.DiscardIncompleteFloor(); } else if (_completedLevel && SemiFunc.RunIsLevel()) { FloorHaulTracker.CompleteSuccessfulFloor(__instance.levelsCompleted + 1, flag); } } } } [HarmonyPatch(typeof(RunManagerPUN), "UpdateLevelRPC")] internal static class GuestFloorCompletionPatch { [HarmonyPrefix] private static void Prefix(out int __state) { __state = (((Object)(object)RunManager.instance != (Object)null) ? RunManager.instance.levelsCompleted : (-1)); } [HarmonyPostfix] private static void Postfix(string _levelName, int _levelsCompleted, bool _gameOver, PhotonMessageInfo _info, int __state) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.MasterOnlyRPC(_info)) { return; } Plugin.Logger.LogInfo((object)($"Received host level update: level={_levelName}, completed={_levelsCompleted}, " + $"previousCompleted={__state}, gameOver={_gameOver}, " + $"local pending haul={FloorHaulTracker.PendingRawRecovered}.")); if (_gameOver) { UpgradeMultiplayerSync.OnGuestRunRestarted(_info); FloorHaulTracker.DiscardIncompleteFloor(); return; } if (__state < 0 || _levelsCompleted != __state + 1) { Plugin.Logger.LogInfo((object)("Not crediting host level update '" + _levelName + "': floor count did not advance by exactly one.")); FloorHaulTracker.DiscardIncompleteFloor(); return; } Level val = (((Object)(object)RunManager.instance != (Object)null) ? RunManager.instance.levelCurrent : null); if (_levelsCompleted > 0 && (Object)(object)val != (Object)null && SemiFunc.IsLevelShop(val)) { FloorHaulTracker.CompleteSuccessfulFloor(_levelsCompleted, isMultiplayer: true); } else { Plugin.Logger.LogInfo((object)("Not crediting host level update '" + _levelName + "': destination is not a shop.")); } } } internal sealed class CreditBalanceView : MonoBehaviour { private TMP_Text _label; private CanvasGroup _canvasGroup; private bool _isInitialized; private Coroutine _revealCoroutine; internal bool IsRevealInProgress => _revealCoroutine != null; internal void Initialize(TMP_Text source) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00b5: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) bool num = !_isInitialized; _canvasGroup = ((Component)this).GetComponent<CanvasGroup>(); if ((Object)(object)_canvasGroup == (Object)null) { _canvasGroup = ((Component)this).gameObject.AddComponent<CanvasGroup>(); } if (num) { _canvasGroup.alpha = 0f; _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; } if ((Object)(object)_label == (Object)null) { GameObject val = new GameObject("CreditBalanceLabel", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)this).transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(12f, 0f); val2.offsetMax = new Vector2(-12f, 0f); _label = (TMP_Text)(object)val.AddComponent<TextMeshProUGUI>(); if ((Object)(object)source != (Object)null) { _label.font = source.font; _label.fontSharedMaterial = source.fontSharedMaterial; _label.fontSize = Mathf.Clamp(source.fontSize, 14f, 18f); _label.fontStyle = source.fontStyle; } else { _label.fontSize = 18f; } ((Graphic)_label).color = Color.white; _label.alignment = (TextAlignmentOptions)4097; _label.textWrappingMode = (TextWrappingModes)0; ((Graphic)_label).raycastTarget = false; } Refresh(); _isInitialized = true; } internal void RevealForMainMenuActivation() { if (_isInitialized && !((Object)(object)_canvasGroup == (Object)null)) { if (_revealCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_revealCoroutine); _revealCoroutine = null; } _canvasGroup.alpha = 0f; _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; BeginReveal(); } } internal void HideForGameplayStart() { if (_revealCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_revealCoroutine); _revealCoroutine = null; } if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 0f; _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; } } private void OnDisable() { if (_revealCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_revealCoroutine); _revealCoroutine = null; } } internal void ShowImmediately() { if (!((Object)(object)_canvasGroup == (Object)null) && _isInitialized) { if (_revealCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_revealCoroutine); _revealCoroutine = null; } _canvasGroup.alpha = 1f; _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; } } private void BeginReveal() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvasGroup == (Object)null) && ((Behaviour)this).isActiveAndEnabled && _revealCoroutine == null) { MenuPage componentInParent = ((Component)this).GetComponentInParent<MenuPage>(); if (!((Object)(object)componentInParent == (Object)null) && (int)componentInParent.menuPageIndex == 0 && !((Object)(object)UpgradeMenuPageRegistry.GetCurrentPage() != (Object)(object)componentInParent) && !(_canvasGroup.alpha >= 0.999f)) { _canvasGroup.alpha = 0f; _canvasGroup.interactable = false; _canvasGroup.blocksRaycasts = false; _revealCoroutine = ((MonoBehaviour)this).StartCoroutine(RevealAfterMenuEntrance()); } } } private IEnumerator RevealAfterMenuEntrance() { yield return (object)new WaitForSecondsRealtime(6f); float elapsed = 0f; while (elapsed < 0.35f) { if (!((Behaviour)this).isActiveAndEnabled) { _revealCoroutine = null; yield break; } elapsed += Time.unscaledDeltaTime; _canvasGroup.alpha = Mathf.Clamp01(elapsed / 0.35f); yield return null; } _canvasGroup.alpha = 1f; _revealCoroutine = null; } internal void Refresh() { //IL_006d: 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) if ((Object)(object)_label != (Object)null) { _label.text = $"CREDITS : {CreditDataStore.TotalCredits} K" + (FloorHaulTracker.HasUncommittedRewards ? "\nSAVE FAILED" : string.Empty); ((Graphic)_label).color = (Color)(FloorHaulTracker.HasUncommittedRewards ? new Color(1f, 0.55f, 0.3f, 1f) : Color.white); } } internal static void RefreshAll() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) CreditBalanceView[] array = Resources.FindObjectsOfTypeAll<CreditBalanceView>(); foreach (CreditBalanceView creditBalanceView in array) { if ((Object)(object)creditBalanceView != (Object)null) { Scene scene = ((Component)creditBalanceView).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { creditBalanceView.Refresh(); } } } UpgradeScreenView.RefreshAllCredits(); } } [HarmonyPatch(typeof(MenuPageMain), "Start")] internal static class MainMenuCreditIntegration { private const string PanelName = "REPOModSkillTree_CreditBalance"; private const float CreditPanelHeight = 54f; private const float AddonVerticalGap = 10f; private static Vector2 _creditPanelPosition = new Vector2(-42f, 230f); private static Vector2 _upgradeButtonPosition = new Vector2(-42f, 294f); private static Transform _addonParent; private static bool _suppressMainMenuReveal; private static bool _initialMainRevealStarted; private static bool _gameplayReturnRevealPending; internal static Vector2 CreditPanelPosition => _creditPanelPosition; internal static Vector2 UpgradeButtonPosition => _upgradeButtonPosition; internal static Transform AddonParent => _addonParent; [HarmonyPostfix] private static void Postfix(MenuPageMain __instance) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) try { MenuPage component = ((Component)__instance).GetComponent<MenuPage>(); TextMeshProUGUI val = (((Object)(object)component != (Object)null) ? component.menuHeader : null); Canvas val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent<Canvas>() : ((Component)__instance).GetComponentInParent<Canvas>()); if ((Object)(object)val2 == (Object)null) { Plugin.Logger.LogWarning((object)"Could not find a Canvas for the main-menu credit display."); return; } RectTransform component2 = ((Component)val2).GetComponent<RectTransform>(); bool flag = ConfigureAddonPositions(__instance, component2); Transform val3 = FindPanel(((Component)__instance).transform); if ((Object)(object)val3 == (Object)null) { GameObject val4 = new GameObject("REPOModSkillTree_CreditBalance", new Type[3] { typeof(RectTransform), typeof(Image), typeof(CreditBalanceView) }); val3 = val4.transform; val3.SetParent(((Object)(object)_addonParent != (Object)null) ? _addonParent : ((Component)__instance).transform, false); Image component3 = val4.GetComponent<Image>(); ((Graphic)component3).color = new Color(0.025f, 0.035f, 0.05f, 0.78f); ((Graphic)component3).raycastTarget = false; } ConfigurePanelRect((RectTransform)val3, _creditPanelPosition); PlacePanelBehindCursor(val3, ((Component)__instance).transform); TMP_Text val5 = FindMenuFontSource(__instance, (TMP_Text)(object)val); ((Component)val3).GetComponent<CreditBalanceView>().Initialize(val5); UpgradeShopView.Install(__instance, val5); if (!flag) { ((MonoBehaviour)__instance).StartCoroutine(ConfigureAddonPositionsAfterLayout(__instance)); } NotifyMainPageActivation(component); Plugin.Logger.LogInfo((object)"Main-menu credit balance display is ready."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Could not create the main-menu credit display: {arg}"); } } private static bool ConfigureAddonPositions(MenuPageMain mainPage, RectTransform canvasRect) { //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_0035: 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_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_0067: 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_006d: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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) //IL_009a: 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_00ce: 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_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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014b: 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) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) _addonParent = (((Object)(object)mainPage != (Object)null) ? ((Component)mainPage).transform : null); _creditPanelPosition = new Vector2(-42f, 230f); _upgradeButtonPosition = new Vector2(-42f, 294f); RectTransform val = (((Object)(object)mainPage != (Object)null) ? ((Component)mainPage).GetComponent<RectTransform>() : null); if ((Object)(object)val == (Object)null) { return false; } Rect rect = val.rect; Vector2 size = ((Rect)(ref rect)).size; if ((size.x <= 0f || size.y <= 0f) && (Object)(object)canvasRect != (Object)null) { rect = canvasRect.rect; size = ((Rect)(ref rect)).size; } RectTransform val2 = MainMenuAddonLayout.FindNewUpdatePanel(((Component)mainPage).transform, size); if ((Object)(object)val2 == (Object)null) { return false; } RectTransform val3 = val; Vector3[] array = (Vector3[])(object)new Vector3[4]; val2.GetWorldCorners(array); Vector3 val4 = ((Transform)val3).InverseTransformPoint(array[2]); float x = val4.x; rect = val3.rect; float num = x - ((Rect)(ref rect)).xMax; float y = val4.y; rect = val3.rect; float num2 = y - ((Rect)(ref rect)).yMin + 14f; _addonParent = ((Component)mainPage).transform; _creditPanelPosition = new Vector2(num, num2); _upgradeButtonPosition = new Vector2(num, num2 + 54f + 10f); ManualLogSource logger = Plugin.Logger; rect = val2.rect; string text = $"Placed CREDITS and UPGRADES above NEW UPDATE: promo={((Rect)(ref rect)).size}, "; string name = ((Object)val3).name; rect = val3.rect; logger.LogInfo((object)(text + $"parent={name} {((Rect)(ref rect)).size}, " + $"credits={_creditPanelPosition}, upgrades={_upgradeButtonPosition}.")); return true; } private static IEnumerator ConfigureAddonPositionsAfterLayout(MenuPageMain mainPage) { for (int frame = 0; frame < 120; frame++) { yield return null; if ((Object)(object)mainPage == (Object)null) { yield break; } Canvas componentInParent = ((Component)mainPage).GetComponentInParent<Canvas>(); if (!ConfigureAddonPositions(mainPage, ((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponent<RectTransform>() : null)) { continue; } Transform obj = FindPanel(((Component)mainPage).transform); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)_addonParent != (Object)null && (Object)(object)((Transform)val).parent != (Object)(object)_addonParent) { ((Transform)val).SetParent(_addonParent, false); } ConfigurePanelRect(val, _creditPanelPosition); } ((Component)mainPage).GetComponent<UpgradeShopView>()?.SetEntryLayout(_addonParent, _upgradeButtonPosition); yield break; } Plugin.Logger.LogWarning((object)"The main-menu page did not expose a measurable NEW UPDATE panel after layout; keeping safe fallback addon positions."); } private static void ConfigurePanelRect(RectTransform rect, Vector2 position) { //IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = new Vector2(1f, 0f); rect.anchorMax = new Vector2(1f, 0f); rect.pivot = new Vector2(1f, 0f); rect.anchoredPosition = position; rect.sizeDelta = new Vector2(130f, 54f); } internal static void NotifyMainPageActivation(MenuPage page) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)page == (Object)null || (int)page.menuPageIndex != 0 || (Object)(object)UpgradeMenuPageRegistry.GetCurrentPage() != (Object)(object)page) { return; } CreditBalanceView componentInChildren = ((Component)page).GetComponentInChildren<CreditBalanceView>(true); UpgradeShopView component = ((Component)page).GetComponent<UpgradeShopView>(); if (!((Object)(object)componentInChildren == (Object)null) || !((Object)(object)component == (Object)null)) { if (_suppressMainMenuReveal) { componentInChildren?.ShowImmediately(); component?.ShowImmediately(); } else if (!_initialMainRevealStarted || _gameplayReturnRevealPending) { _initialMainRevealStarted = true; _gameplayReturnRevealPending = false; componentInChildren?.RevealForMainMenuActivation(); component?.RevealForMainMenuActivation(); Plugin.Logger.LogInfo((object)"Started Main-menu 6-second reveal for CREDITS and UPGRADES."); } else if ((componentInChildren == null || !componentInChildren.IsRevealInProgress) && (component == null || !component.IsRevealInProgress)) { componentInChildren?.ShowImmediately(); component?.ShowImmediately(); } } } internal static void HideForGameplayStart() { //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) //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) _gameplayReturnRevealPending = true; CreditBalanceView[] array = Resources.FindObjectsOfTypeAll<CreditBalanceView>(); Scene scene; foreach (CreditBalanceView creditBalanceView in array) { if ((Object)(object)creditBalanceView != (Object)null) { scene = ((Component)creditBalanceView).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { creditBalanceView.HideForGameplayStart(); } } } UpgradeShopView[] array2 = Resources.FindObjectsOfTypeAll<UpgradeShopView>(); foreach (UpgradeShopView upgradeShopView in array2) { if ((Object)(object)upgradeShopView != (Object)null) { scene = ((Component)upgradeShopView).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { upgradeShopView.HideForGameplayStart(); } } } } internal static void SuppressMainMenuReveal() { _suppressMainMenuReveal = true; } internal static void ResumeMainMenuReveal() { _suppressMainMenuReveal = false; } internal static void ShowCurrentMainMenuImmediately() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)MenuManager.instance == (Object)null)) { MenuPage currentPage = UpgradeMenuPageRegistry.GetCurrentPage(); if (!((Object)(object)currentPage == (Object)null) && (int)currentPage.menuPageIndex == 0) { ((Component)currentPage).GetComponentInChildren<CreditBalanceView>(true)?.ShowImmediately(); ((Component)currentPage).GetComponent<UpgradeShopView>()?.ShowImmediately(); } } } private static TMP_Text FindMenuFontSource(MenuPageMain page, TMP_Text fallback) { TMP_Text[] componentsInChildren = ((Component)page).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { string a = val.text?.Trim(); if (string.Equals(a, "PRIVATE GAME", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "SINGLEPLAYER", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "QUIT GAME", StringComparison.OrdinalIgnoreCase)) { return val; } } return fallback; } private static Transform FindPanel(Transform root) { for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); if (((Object)child).name == "REPOModSkillTree_CreditBalance") { return child; } Transform val = FindPanel(child); if ((Object)(object)val != (Object)null) { return val; } } return null; } internal static void PlacePanelBehindCursor(Transform panel, Transform pageRoot) { Transform val = FindCursor(pageRoot); if ((Object)(object)panel.parent != (Object)(object)pageRoot) { panel.SetAsLastSibling(); return; } Transform val2 = val; while ((Object)(object)val2 != (Object)null && (Object)(object)val2.parent != (Object)(object)pageRoot) { val2 = val2.parent; } if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)panel) { panel.SetSiblingIndex(val2.GetSiblingIndex()); } else { panel.SetAsLastSibling(); } } private static Transform FindCursor(Transform root) { for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); if (((Object)child).name.IndexOf("cursor", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)child).name.IndexOf("mouse", StringComparison.OrdinalIgnoreCase) >= 0) { return child; } Transform val = FindCursor(child); if ((Object)(object)val != (Object)null) { return val; } } return null; } } internal static class MainMenuAddonLayout { internal static RectTransform FindNewUpdatePanel(Transform root, Vector2 pageSize) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: 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) RectTransform result = null; float num = 0f; TMP_Text[] componentsInChildren = ((Component)root).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if ((Object)(object)val == (Object)null || val.text.IndexOf("NEW UPDATE", StringComparison.OrdinalIgnoreCase) < 0) { continue; } Transform val2 = val.transform; while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)root) { RectTransform val3 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val2).GetComponent<Image>() != (Object)null) { Rect rect = val3.rect; Vector2 size = ((Rect)(ref rect)).size; if (size.x >= pageSize.x * 0.12f && size.y >= pageSize.y * 0.06f && size.x <= pageSize.x * 0.8f && size.y <= pageSize.y * 0.5f) { float num2 = size.x * size.y; if (num2 > num) { num = num2; result = val3; } } } val2 = val2.parent; } } return result; } } [HarmonyPatch(typeof(MenuManager), "PageSetCurrent")] internal static class MainMenuPageActivationPatch { [HarmonyPatch(typeof(TutorialDirector), "Start")] internal static class TutorialMainMenuUiHidePatch { [HarmonyPrefix] private static void Prefix() { MainMenuCreditIntegration.HideForGameplayStart(); } } [HarmonyPatch(typeof(PlayerAvatar), "LateStart")] internal static class GameplayMainMenuUiHidePatch { [HarmonyPrefix] private static void Prefix() { if (SemiFunc.RunIsLevel()) { MainMenuCreditIntegration.HideForGameplayStart(); } } } [HarmonyPostfix] private static void Postfix(MenuPageIndex __0, MenuPage __1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)__0 == 0 && (Object)(object)__1 != (Object)null) { MainMenuCreditIntegration.NotifyMainPageActivation(__1); } } } internal static class MenuPageIdAllocator { internal static int FindAvailable(IEnumerable<int> reservedIds) { if (reservedIds == null) { throw new ArgumentNullException("reservedIds"); } HashSet<int> hashSet = new HashSet<int>(reservedIds); int i; for (i = 0; hashSet.Contains(i); i++) { if (i == int.MaxValue) { throw new InvalidOperationException("No non-negative menu page ID is available."); } } return i; } } internal static class MenuRevealTiming { internal const float DelaySeconds = 6f; internal const float FadeSeconds = 0.35f; } internal sealed class NativeMenuButtonAdapter { private static readonly FieldInfo ButtonField = AccessTools.Field(typeof(MenuButton), "button"); private static readonly FieldInfo TextField = AccessTools.Field(typeof(MenuButton), "buttonText"); private static readonly FieldInfo DisabledField = AccessTools.Field(typeof(MenuButton), "disabled"); private static readonly FieldInfo HoveringField = AccessTools.Field(typeof(MenuButton), "hovering"); private static readonly FieldInfo HoverTimerField = AccessTools.Field(typeof(MenuButton), "hoverTimer"); private static readonly FieldInfo ClickedField = AccessTools.Field(typeof(MenuButton), "clicked"); private static readonly FieldInfo ClickTimerField = AccessTools.Field(typeof(MenuButton), "clickTimer"); private static readonly MethodInfo OnHoverEndMethod = AccessTools.Method(typeof(MenuButton), "OnHoverEnd", (Type[])null, (Type[])null); private static readonly FieldInfo PopupField = AccessTools.Field(typeof(MenuButton), "menuButtonPopUp"); private static readonly FieldInfo TextStringField = AccessTools.Field(typeof(MenuButton), "buttonTextString"); private static readonly FieldInfo SelectableElementField = FindField(typeof(MenuButton), "menuSelectableElement", "<menuSelectableElement>k__BackingField"); private static readonly PropertyInfo SelectableElementProperty = AccessTools.Property(typeof(MenuButton), "menuSelectableElement"); private static readonly FieldInfo SelectableMenuIdField = FindField(typeof(MenuSelectableElement), "menuID", "<menuID>k__BackingField"); private static readonly PropertyInfo SelectableMenuIdProperty = AccessTools.Property(typeof(MenuSelectableElement), "menuID"); private static readonly FieldInfo ClickEventField = typeof(Button).GetField("m_OnClick", BindingFlags.Instance | BindingFlags.NonPublic); internal MenuButton Component { get; } internal Button Button => GetValue<Button>(ButtonField); internal TextMeshProUGUI Text => GetValue<TextMeshProUGUI>(TextField); internal GameObject GameObject => ((Component)Component).gameObject; internal bool Disabled { get { return GetValue<bool>(DisabledField); } set { SetValue(DisabledField, value); } } internal bool HasPopup => GetValue<Object>(PopupField) != (Object)null; internal string EnsureSelectableMenuId() { object? obj = SelectableElementProperty?.GetValue(Component, null); MenuSelectableElement val = (MenuSelectableElement)(((obj is MenuSelectableElement) ? obj : null) ?? GetValue<MenuSelectableElement>(SelectableElementField)); if ((Object)(object)val == (Object)null) { return null; } string text = (SelectableMenuIdProperty?.GetValue(val, null) as string) ?? (SelectableMenuIdField?.GetValue(val) as string); if (string.IsNullOrEmpty(text)) { text = SemiFunc.MenuGetSelectableID(((Component)val).gameObject); MethodInfo methodInfo = SelectableMenuIdProperty?.GetSetMethod(nonPublic: true); if (methodInfo != null) { methodInfo.Invoke(val, new object[1] { text }); } else { if (SelectableMenuIdField == null) { Plugin.Logger.LogError((object)"Could not set the MenuSelectableElement ID; required field/property is missing."); return null; } SelectableMenuIdField.SetValue(val, text); } } return text; } internal NativeMenuButtonAdapter(MenuButton component) { Component = component; } internal bool ReplaceClickAction(UnityAction listener) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown Button button = Button; if ((Object)(object)button == (Object)null || ClickEventField == null) { return false; } ButtonClickedEvent val = new ButtonClickedEvent(); ((UnityEvent)val).AddListener(listener); ClickEventField.SetValue(button, val); return true; } internal T GetValue<T>(FieldInfo field) { if (!(field == null)) { return (T)field.GetValue(Component); } return default(T); } internal void SetValue(string fieldName, object value) { FieldInfo fieldInfo = FindField(typeof(MenuButton), fieldName, "<" + fieldName + ">k__BackingField"); if (fieldInfo != null) { SetValue(fieldInfo, value); return; } MethodInfo methodInfo = AccessTools.Property(typeof(MenuButton), fieldName)?.GetSetMethod(nonPublic: true); if (methodInfo != null) { methodInfo.Invoke(Component, new object[1] { value }); } else { Plugin.Logger.LogWarning((object)("Could not set MenuButton member '" + fieldName + "' because no field or writable property exists.")); } } private static FieldInfo FindField(Type type, string fieldName, string backingFieldName) { return type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField(backingFieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } private void SetValue(FieldInfo field, object value) { field?.SetValue(Component, value); } internal void SetTextString(string value) { SetValue(TextStringField, value); } internal void SetText(string value) { ((TMP_Text)Text).text = value; SetTextString(value); } internal void SetDisabled(bool disabled) { Disabled = disabled; if ((Object)(object)Button != (Object)null) { ((Selectable)Button).interactable = !disabled; } } internal void ResetHoverState() { if (GetValue<bool>(HoveringField)) { OnHoverEndMethod?.Invoke(Component, null); } SetValue(HoveringField, false); SetValue(HoverTimerField, 0f); SetValue(ClickedField, false); SetValue(ClickTimerField, 0f); } } internal static class NativeMenuButtonFactory { private static Sprite _circleSprite; internal static NativeMenuButtonAdapter Create(Transform parent, MenuPage ownerPage, string objectName, string label, TMP_Text fontSource, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 position, Vector2 size, UnityAction onClick, Color backgroundColor, bool circleBackground = false, UnityAction onHover = null) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0088: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0128: 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_0135: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: 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_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0512: Unknown result type (might be due to invalid IL or missing references) //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_058e: Unknown result type (might be due to invalid IL or missing references) //IL_059a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)parent == (Object)null || (Object)(object)ownerPage == (Object)null) { Plugin.Logger.LogError((object)("Cannot create native menu control '" + objectName + "' without a parent MenuPage.")); return null; } GameObject val = new GameObject(objectName, new Type[1] { typeof(RectTransform) }); val.SetActive(false); val.transform.SetParent(parent, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = anchorMin; val2.anchorMax = anchorMax; val2.pivot = pivot; val2.anchoredPosition = position; val2.sizeDelta = size; Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = Color.clear; ((Graphic)val3).raycastTarget = true; Button val4 = val.AddComponent<Button>(); ((Selectable)val4).targetGraphic = (Graphic)(object)val3; ((Selectable)val4).transition = (Transition)0; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)val4).navigation = navigation; GameObject val5 = new GameObject("MenuSelectableElement", new Type[1] { typeof(RectTransform) }); val5.transform.SetParent(val.transform, false); RectTransform val6 = (RectTransform)val5.transform; val6.anchorMin = Vector2.zero; val6.anchorMax = Vector2.one; val6.offsetMin = Vector2.zero; val6.offsetMax = Vector2.zero; MenuSelectableElement val7 = val5.AddComponent<MenuSelectableElement>(); ((Selectable)val7).transition = (Transition)0; navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)val7).navigation = navigation; ((Selectable)val7).targetGraphic = (Graphic)(object)val3; SetSelectableMember(val7, "parentPage", ownerPage); SetSelectableMember(val7, "rectTransform", val6); MenuButton component = val.AddComponent<MenuButton>(); NativeMenuButtonHitboxMarker nativeMenuButtonHitboxMarker = val.AddComponent<NativeMenuButtonHitboxMarker>(); nativeMenuButtonHitboxMarker.SetHoverAction(onHover); nativeMenuButtonHitboxMarker.SetClickAction((UnityAction)delegate { Plugin.Logger.LogInfo((object)("Menu button '" + objectName + "' clicked.")); UnityAction obj = onClick; if (obj != null) { obj.Invoke(); } }); ((UnityEvent)val4.onClick).AddListener(new UnityAction(nativeMenuButtonHitboxMarker.InvokeClickAction)); NativeMenuButtonAdapter nativeMenuButtonAdapter = new NativeMenuButtonAdapter(component); nativeMenuButtonAdapter.SetValue("buttonTextString", label); nativeMenuButtonAdapter.SetValue("customColors", true); nativeMenuButtonAdapter.SetValue("colorNormal", Color.white); nativeMenuButtonAdapter.SetValue("colorHover", Color.white); nativeMenuButtonAdapter.SetValue("colorClick", (object)new Color(1f, 0.77f, 0.12f, 1f)); nativeMenuButtonAdapter.SetValue("doButtonEffect", false); nativeMenuButtonAdapter.SetValue("doHoverEffect", true); nativeMenuButtonAdapter.SetValue("resizeButton", false); nativeMenuButtonAdapter.SetValue("customHoverArea", false); nativeMenuButtonAdapter.SetValue("button", val4); nativeMenuButtonAdapter.SetValue("menuSelectableElement", val7); nativeMenuButtonAdapter.SetValue("rectTransform", val2); nativeMenuButtonAdapter.SetValue("rectTransformSelection", val6); nativeMenuButtonAdapter.SetValue("parentPage", ownerPage); GameObject val8 = new GameObject("VisualBackground", new Type[2] { typeof(RectTransform), typeof(Image) }); val8.transform.SetParent(val.transform, false); val8.transform.SetAsFirstSibling(); RectTransform val9 = (RectTransform)val8.transform; val9.anchorMin = Vector2.zero; val9.anchorMax = Vector2.one; val9.offsetMin = Vector2.zero; val9.offsetMax = Vector2.zero; Image component2 = val8.GetComponent<Image>(); ((Graphic)component2).color = backgroundColor; ((Graphic)component2).raycastTarget = false; ((Selectable)val7).targetGraphic = (Graphic)(object)component2; if (circleBackground) { component2.sprite = GetCircleSprite(); component2.type = (Type)0; component2.preserveAspect = true; } GameObject val10 = new GameObject("Label", new Type[1] { typeof(RectTransform) }); val10.transform.SetParent(val.transform, false); RectTransform val11 = (RectTransform)val10.transform; val11.anchorMin = Vector2.zero; val11.anchorMax = Vector2.one; val11.offsetMin = new Vector2(4f, 3f); val11.offsetMax = new Vector2(-4f, -3f); TextMeshProUGUI val12 = val10.AddComponent<TextMeshProUGUI>(); if ((Object)(object)fontSource != (Object)null) { ((TMP_Text)val12).font = fontSource.font; ((TMP_Text)val12).fontSharedMaterial = fontSource.fontSharedMaterial; ((Graphic)val12).color = Color.white; ((TMP_Text)val12).fontStyle = fontSource.fontStyle; } else { ((Graphic)val12).color = Color.white; } ((TMP_Text)val12).fontSize = (circleBackground ? 13f : 17f); ((TMP_Text)val12).alignment = (TextAlignmentOptions)514; ((TMP_Text)val12).textWrappingMode = (TextWrappingModes)0; ((Graphic)val12).raycastTarget = false; ((TMP_Text)val12).text = label; nativeMenuButtonAdapter.SetValue("buttonText", val12); val.SetActive(true); nativeMenuButtonAdapter.SetValue("button", val4); nativeMenuButtonAdapter.SetValue("menuSelectableElement", val7); nativeMenuButtonAdapter.SetValue("buttonText", val12); val2.anchorMin = anchorMin; val2.anchorMax = anchorMax; val2.pivot = pivot; val2.anchoredPosition = position; val2.sizeDelta = size; nativeMenuButtonAdapter.SetValue("rectTransformSelection", val6); nativeMenuButtonAdapter.SetText(label); ((TMP_Text)nativeMenuButtonAdapter.Text).fontSize = (circleBackground ? 13f : 17f); nativeMenuButtonAdapter.SetValue("rectTransformSelection", val6); nativeMenuButtonAdapter.SetValue("rectTransform", val2); ((Selectable)val4).interactable = true; Plugin.Logger.LogDebug((object)$"Created REPO menu button '{objectName}' with screen hitbox {size.x:0}x{size.y}."); return nativeMenuButtonAdapter; } private static void SetSelectableMember(MenuSelectableElement selectable, string memberName, object value) { FieldInfo fieldInfo = AccessTools.Field(typeof(MenuSelectableElement), memberName); if (fieldInfo != null) { fieldInfo.SetValue(selectable, value); return; } MethodInfo methodInfo = AccessTools.Property(typeof(MenuSelectableElement), memberName)?.GetSetMethod(nonPublic: true); if (methodInfo != null) { methodInfo.Invoke(selectable, new object[1] { value }); } else { Plugin.Logger.LogWarning((object)("Could not set MenuSelectableElement member '" + memberName + "' because no field or writable property exists.")); } } private static Sprite GetCircleSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_00d3: 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_0057: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_circleSprite != (Object)null) { return _circleSprite; } Texture2D val = new Texture2D(96, 96, (TextureFormat)4, false) { name = "REPOModSkillTree_NodeCircle", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; Color[] array = (Color[])(object)new Color[9216]; float num = 47.5f; for (int i = 0; i < 96; i++) { for (int j = 0; j < 96; j++) { float num2 = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2(num, num)); float num3 = Mathf.Clamp01(46.75f - num2); array[i * 96 + j] = new Color(1f, 1f, 1f, num3); } } val.SetPixels(array); val.Apply(); _circleSprite = Sprite.Create(val, new Rect(0f, 0f, 96f, 96f), new Vector2(0.5f, 0.5f), 96f); ((Object)_circleSprite).name = "REPOModSkillTree_NodeCircle"; return _circleSprite; } } internal sealed class NativeMenuButtonHitboxMarker : MonoBehaviour { private UnityAction _onHover; private UnityAction _onClick; private string _lastHitTestReason; private bool _hasClicked; private float _lastClickTime; private bool _missingMouseReported; private static bool _suppressNativeMenuClick; internal static bool SuppressNativeMenuClick => _suppressNativeMenuClick; internal static void SuppressNativeMenuClickForCurrentMousePress() { _suppressNativeMenuClick = true; } internal void SetHoverAction(UnityAction onHover) { _onHover = onHover; } internal void SetClickAction(UnityAction onClick) { _onClick = onClick; } internal void InvokeHoverAction() { UnityAction onHover = _onHover; if (onHover != null) { onHover.Invoke(); } } internal void InvokeClickAction() { if (_onClick != null && (!_hasClicked || !(Time.unscaledTime - _lastClickTime < 0.35f))) { _hasClicked = true; _lastClickTime = Time.unscaledTime; _onClick.Invoke(); } } internal bool RecordHitTest(string reason) { if (_lastHitTestReason == reason) { return false; } _lastHitTestReason = reason; return true; } private void Update() { if (_onClick == null || !((Behaviour)this).isActiveAndEnabled) { return; } Mouse current = Mouse.current; if (current == null) { if (!_missingMouseReported && ((Object)((Component)this).gameObject).name == "REPOModSkillTree_UpgradeEntry") { _missingMouseReported = true; Plugin.Logger.LogWarning((object)"UPGRADES direct-click fallback cannot find the current mouse."); } return; } _missingMouseReported = false; if (!current.leftButton.isPressed) { _suppressNativeMenuClick = false; } if (!current.leftButton.wasPressedThisFrame) { return; } MenuButton component = ((Component)this).GetComponent<MenuButton>(); if ((Object)(object)component == (Object)null) { return; } MenuPage componentInParent = ((Component)this).GetComponentInParent<MenuPage>(); MenuManager instance = MenuManager.instance; NativeMenuButtonAdapter nativeMenuButtonAdapter = new NativeMenuButtonAdapter(component); RectTransform value = nativeMenuButtonAdapter.GetValue<RectTransform>(AccessTools.Field(typeof(MenuButton), "rectTransformSelection")); if ((Object)(object)value == (Object)null || !NativeMenuButtonRegistrationPatch.IsPointerInside(value, nativeMenuButtonAdapter.GetValue<bool>(AccessTools.Field(typeof(MenuButton), "customHoverArea")) ? 0f : 10f)) { return; } string pageInputBlockReason = NativeMenuButtonRegistrationPatch.GetPageInputBlockReason(componentInParent, instance); if (pageInputBlockReason != null) { LogBlockedUpgradeClick(pageInputBlockReason); return; } CanvasGroup component2 = ((Component)this).gameObject.GetComponent<CanvasGroup>(); if (nativeMenuButtonAdapter.Disabled || (Object)(object)nativeMenuButtonAdapter.Button == (Object)null || !((Selectable)nativeMenuButtonAdapter.Button).interactable || ((Object)(object)component2 != (Object)null && (component2.alpha < 0.99f || !component2.interactable || !component2.blocksRaycasts))) { LogBlockedUpgradeClick($"disabled={nativeMenuButtonAdapter.Disabled}, buttonInteractable={(Object)(object)nativeMenuButtonAdapter.Button != (Object)null && ((Selectable)nativeMenuButtonAdapter.Button).interactable}, " + $"canvasAlpha={(((Object)(object)component2 != (Object)null) ? component2.alpha : 1f):0.00}, " + $"canvasInteractable={(Object)(object)component2 == (Object)null || component2.interactable}, " + $"canvasRaycasts={(Object)(object)component2 == (Object)null || component2.blocksRaycasts}"); return; } Plugin.Logger.LogInfo((object)("Dispatched direct screen click fallback for " + ((Object)((Component)this).gameObject).name + ".")); SuppressNativeMenuClickForCurrentMousePress(); InvokeClickAction(); } private void LogBlockedUpgradeClick(string reason) { if (((Object)((Component)this).gameObject).name == "REPOModSkillTree_UpgradeEntry") { Plugin.Logger.LogWarning((object)("UPGRADES click was blocked because " + reason + ".")); } } } [HarmonyPatch(typeof(SemiFunc), "InputUIClick")] internal static class NativeMenuClickSuppressionPatch { [HarmonyPrefix] private static bool Prefix(ref bool __result) { if (!NativeMenuButtonHitboxMarker.SuppressNativeMenuClick) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(MenuButton), "OnHoverStart")] internal static class NativeMenuButtonHoverActionPatch { [HarmonyPostfix] private static void Postfix(MenuButton __instance) { if (__instance != null) { ((Component)__instance).GetComponent<NativeMenuButtonHitboxMarker>()?.InvokeHoverAction(); } } } [HarmonyPatch(typeof(MenuButton), "RegisterHover")] internal static class NativeMenuButtonRegistrationPatch { private static readonly FieldInfo CurrentMenuPageField = AccessTools.Field(typeof(MenuManager), "currentMenuPage"); private static readonly FieldInfo CurrentMenuIdField = AccessTools.Field(typeof(MenuManager), "currentMenuID"); private static readonly FieldInfo CurrentMenuIdTransformField = AccessTools.Field(typeof(MenuManager), "currentMenuIDTransform"); private static readonly PropertyInfo PageInteractableProperty = AccessTools.Property(typeof(MenuPage), "isInteractable"); private static readonly FieldInfo PagePriorityField = AccessTools.Field(typeof(MenuPage), "interactionPriority"); private static readonly MethodInfo HighestOpenPriorityMethod = AccessTools.Method(typeof(MenuManager), "HighestOpenPriority", (Type[])null, (Type[])null); private static bool _missingHudWarningLogged; private static bool _missingPageInteractivityReported; [HarmonyPrefix] private static bool Prefix(MenuButton __instance) { if ((Object)(object)__instance == (Object)null) { return true; } NativeMenuButtonHitboxMarker component = ((Component)__instance).GetComponent<NativeMenuButtonHitboxMarker>(); if ((Object)(object)component == (Object)null) { return true; } NativeMenuButtonAdapter nativeMenuButtonAdapter = new NativeMenuButtonAdapter(__instance); RectTransform value = nativeMenuButtonAdapter.GetValue<RectTransform>(AccessTools.Field(typeof(MenuButton), "rectTransformSelection")); MenuPage componentInParent = ((Component)__instance).GetComponentInParent<MenuPage>(); MenuManager instance = MenuManager.instance; if (!IsVisibleAndInteractive(nativeMenuButtonAdapter, out var _)) { RecordUpgradeButtonHitTest(__instance, component, "not-visible-or-interactive", isInside: false); ClearHover(nativeMenuButtonAdapter, instance); return false; } if ((Object)(object)value == (Object)null || (Object)(object)componentInParent == (Object)null || (Object)(object)instance == (Object)null || !((Component)value).gameObject.activeInHierarchy) { string reason = (((Object)(object)value == (Object)null) ? "missing-selection-rect" : (((Object)(object)componentInParent == (Object)null) ? "missing-page" : (((Object)(object)instance == (Object)null) ? "missing-manager" : "inactive-selection-rect"))); RecordUpgradeButtonHitTest(__instance, component, reason, isInside: false); ClearHover(nativeMenuButtonAdapter, instance); return false; } object? obj = CurrentMenuPageField?.GetValue(instance); if ((Object)((obj is MenuPage) ? obj : null) != (Object)(object)componentInParent) { RecordUpgradeButtonHitTest(__instance, component, "another-page-current", isInside: false); ClearHover(nativeMenuButtonAdapter, instance); return false; } string pageInputBlockReason = GetPageInputBlockReason(componentInParent, instance); if (pageInputBlockReason != null) { RecordUpgradeButtonHitTest(__instance, component, pageInputBlockReason, isInside: false); ClearHover(nativeMenuButtonAdapter, instance); return false; } float xPadding = (nativeMenuButtonAdapter.GetValue<bool>(AccessTools.Field(typeof(MenuButton), "customHoverArea")) ? 0f : 10f); bool flag = IsPointerInside(value, xPadding); RecordUpgradeButtonHitTest(__instance, component, flag ? "inside" : "outside", flag); if (!flag) { ClearHover(nativeMenuButtonAdapter, instance); return false; } if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame) { NativeMenuButtonHitboxMarker.SuppressNativeMenuClickForCurrentMousePress(); } string value2 = nativeMenuButtonAdapter.EnsureSelectableMenuId(); if (string.IsNullOrEmpty(value2)) { Plugin.Logger.LogError((object)"Could not register hover for a MOD menu button because its selectable ID is empty."); return false; } CurrentMenuIdField?.SetValue(instance, value2); CurrentMenuIdTransformField?.SetValue(instance, value); return false; } internal static bool IsPointerInside(RectTransform rect, float xPadding) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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) if (!TryGetPointerLocal(rect, out var localPoint)) { return false; } Rect rect2 = rect.rect; if (localPoint.x >= ((Rect)(ref rect2)).xMin - xPadding && localPoint.x <= ((Rect)(ref rect2)).xMax + xPadding && localPoint.y >= ((Rect)(ref rect2)).yMin) { return localPoint.y <= ((Rect)(ref rect2)).yMax; } return false; } internal static string GetPageInputBlockReason(MenuPage page, MenuManager manager) { if ((Object)(object)page == (Object)null || (Object)(object)manager == (Object)null) { return "missing-page-or-manager"; } if (!((Component)page).gameObject.activeInHierarchy || (Object)(object)UpgradeMenuPageRegistry.GetCurrentPage() != (Object)(object)page) { return "page-not-active-or-current"; } if (PageInteractableProperty == null || PagePriorityField == null || HighestOpenPriorityMethod == null) { if (!_missingPageInteractivityReported) { _missingPageInteractivityReported = true; Plugin.Logger.LogError((object)"Cannot verify native menu page interactivity: required members are unavailable."); } return "missing-page-interactivity-members"; } object value = PageInteractableProperty.GetValue(page, null); object value2 = PagePriorityField.GetValue(page); object obj = HighestOpenPriorityMethod.Invoke(manager, null); if (!(value is bool flag) || !(value2 is int num) || !(obj is int num2)) { if (!_missingPageInteractivityReported) { _missingPageInteractivityReported = true; Plugin.Logger.LogError((object)"Cannot verify native menu page interactivity: unexpected member values."); } return "invalid-page-interactivity-values"; } _missingPageInteractivityReported = false; if (!flag) { return "native-page-not-interactable"; } if (num2 > num) { return $"higher-priority-page-{num2}-over-{num}"; } CanvasGroup component = ((Component)page).GetComponent<CanvasGroup>(); if ((Object)(object)component != (Object)null && (!component.interactable || !component.blocksRaycasts)) { return "page-canvas-not-interactable"; } return null; } internal static bool TryGetPointerLocal(RectTransform rect, out Vector3 localPoint) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) //IL_005c: 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_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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) localPoint = Vector3.zero; HUDCanvas instance = HUDCanvas.instance; RectTransform val = (((Object)(object)instance != (Object)null) ? ((Component)instance).GetComponent<RectTransform>() : null); if ((Object)(object)val == (Object)null) { if (!_missingHudWarningLogged) { _missingHudWarningLogged = true; Plugin.Logger.LogWarning((object)"Cannot test MOD menu buttons while the HUD canvas is unavailable."); } return false; } _missingHudWarningLogged = false; Vector2 val2 = SemiFunc.UIMousePosToUIPos(); Rect rect2 = val.rect; Vector2 val3 = val2 + ((Rect)(ref rect2)).min; localPoint = ((Transform)rect).InverseTransformPoint(((Transform)val).TransformPoint(new Vector3(val3.x, val3.y, 0f))); return true; } private static void RecordUpgradeButtonHitTest(MenuButton button, NativeMenuButtonHitboxMarker marker, string reason, bool isInside) { if (((Object)((Component)button).gameObject).name == "REPOModSkillTree_UpgradeEntry" && marker.RecordHitTest(reason)) { Plugin.Logger.LogInfo((object)$"UPGRADES screen hit test: inside={isInside}, reason={reason}."); } } private static bool IsVisibleAndInteractive(NativeMenuButtonAdapter button, out CanvasGroup canvasGroup) { canvasGroup = button.GameObject.GetComponent<CanvasGroup>(); if ((Object)(object)canvasGroup == (Object)null) { return true; } if (canvasGroup.alpha >= 0.99f && canvasGroup.interactable) { return canvasGroup.blocksRaycasts; } return false; } private static void ClearHover(NativeMenuButtonAdapter button, MenuManager manager) { button.ResetHoverState(); if (!((Object)(object)manager == (Object)null)) { string text = button.EnsureSelectableMenuId(); string text2 = CurrentMenuIdField?.GetValue(manager) as string; if (!string.IsNullOrEmpty(text) && text2 == text) { CurrentMenuIdField?.SetValue(manager, string.Empty); CurrentMenuIdTransformField?.SetValue(manager, null); } } } } [BepInPlugin("REPOModSkillTree", "REPO Skill Tree", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private float _nextRewardRetryAt; private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; CreditDataStore.Load(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "REPOModSkillTree"); Logger.LogInfo((object)string.Format("Plugin {0} is loaded; saved balance is {1}K.", "REPOModSkillTree", CreditDataStore.TotalCredits)); } private void Update() { UpgradeMultiplayerSync.Update(); if (FloorHaulTracker.HasUncommittedRewards && !(Time.unscaledTime < _nextRewardRetryAt)) { _nextRewardRetryAt = Time.unscaledTime + 5f; FloorHaulTracker.RetryUncommittedRewards(); } } private void OnGUI() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) float num = 20f; if (FloorHaulTracker.HasUncommittedRewards) { GUI.Box(new Rect(((float)Screen.width - 560f) / 2f, num, 560f, 40f), "SKILL TREE: SAVE FAILED. Keep the game open while saving is retried."); num += 45f; } if (UpgradeMultiplayerSync.ShowHostRequiredWarning) { GUI.Box(new Rect(((float)Screen.width - 560f) / 2f, num, 560f, 40f), "SKILL TREE: host MOD required; permanent bonuses will not be added."); } } private void OnDestroy() { if (FloorHaulTracker.HasUncommittedRewards) { Logger.LogError((object)"The game is closing while cleared-floor rewards are still unsaved."); } } } internal sealed class UpgradePageTemplateMarker : MonoBehaviour { } internal static class UpgradeMenuPageRegistry { private static MenuPageIndex? _pageIndex; private static GameObject _pageTemplate; private static MenuManager _registeredManager; private static MenuPages _registeredEntry; private static GameObject _templateHost; internal static MenuPageIndex PageIndex => _pageIndex ?? throw new InvalidOperationException("The upgrade menu page has not been registered."); internal static bool IsCurrentPage() { return IsUpgradePage(GetCurrentPage()); } internal static bool IsUpgradePage(MenuPage page) { //IL_0016: 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) if ((Object)(object)page != (Object)null && _pageIndex.HasValue && page.menuPageIndex == _pageIndex.Value) { return (Object)(object)((Component)page).GetComponent<UpgradePageTemplateMarker>() != (Object)null; } return false; } internal static MenuPage GetCurrentPage() { object? obj = AccessTools.Field(typeof(MenuManager), "currentMenuPage")?.GetValue(MenuManager.instance); return (MenuPage)((obj is MenuPage) ? obj : null); } internal static bool FitPageToCanvas(MenuPage page, MenuPage referencePage) { //IL_0037: 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_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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_025a: 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_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0285: 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_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_0377: 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_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)page == (Object)null || (Object)(object)referencePage == (Object)null) { return false; } RectTransform component = ((Component)page).GetComponent<RectTransform>(); RectTransform component2 = ((Component)referencePage).GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { return false; } Rect rect = component2.rect; if (!(((Rect)(ref rect)).width <= 0f)) { rect = component2.rect; if (!(((Rect)(ref rect)).height <= 0f)) { component.anchorMin = component2.anchorMin; component.anchorMax = component2.anchorMax; component.pivot = component2.pivot; component.sizeDelta = component2.sizeDelta; component.anchoredPosition = component2.anchoredPosition; ((Transform)component).localScale = ((Transform)component2).localScale; rect = component.rect; if (((Rect)(ref rect)).width > 0f) { rect = component.rect; return ((Rect)(ref rect)).height > 0f; } return false; } } rect = component.rect; if (((Rect)(ref rect)).width > 0f) { rect = component.rect; if (((Rect)(ref rect)).height > 0f) { Canvas componentInParent = ((Component)page).GetComponentInParent<Canvas>(); ManualLogSource logger = Plugin.Logger; rect = component.rect; logger.LogInfo((object)("Main page has no measurable RectTransform; preserving the upgrade page's native Canvas geometry " + string.Format("(page={0}, canvas={1}).", ((Rect)(ref rect)).size, ((Object)(object)componentInParent != (Object)null) ? ((Object)componentInParent).name : "missing"))); return true; } } Transform parent = ((Transform)component).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val != (Object)null) { rect = val.rect; if (((Rect)(ref rect)).width > 0f) { rect = val.rect; if (((Rect)(ref rect)).height > 0f) { component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.pivot = val.pivot; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; component.anchoredPosition = Vector2.zero; ((Transform)component).localScale = Vector3.one; rect = component.rect; if (((Rect)(ref rect)).width > 0f) { rect = component.rect; return ((Rect)(ref rect)).height > 0f; } return false; } } } Canvas componentInParent2 = ((Component)page).GetComponentInParent<Canvas>(); RectTransform val2 = (((Object)(object)componentInParent2 != (Object)null) ? ((Component)componentInParent2).GetComponent<RectTransform>() : null); if (!((Object)(object)((Transform)component).parent == (Object)null) && !((Object)(object)val2 == (Object)null)) { rect = val2.rect; if (!(((Rect)(ref rect)).width <= 0f)) { rect = val2.rect; if (!(((Rect)(ref rect)).height <= 0f)) { Vector3[] array = (Vector3[])(object)new Vector3[4]; val2.GetWorldCorners(array); float num = float.PositiveInfinity; float num2 = float.NegativeInfinity; float num3 = float.PositiveInfinity; float num4 = float.NegativeInfinity; Vector3[] array2 = array; foreach (Vector3 val3 in array2) { Vector3 val4 = ((Transform)component).parent.InverseTransformPoint(val3); num = Mathf.Min(num, val4.x); num2 = Mathf.Max(num2, val4.x); num3 = Mathf.Min(num3, val4.y); num4 = Mathf.Max(num4, val4.y); } if (num2 <= num || num4 <= num3) { Plugin.Logger.LogError((object)"Could not convert Canvas geometry to the upgrade page's parent space."); return false; } component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(num2 - num, num4 - num3); ((Transform)component).localScale = Vector3.one; ((Transform)component).localRotation = Quaternion.identity; ((Transform)component).localPosition = new Vector3((num + num2) * 0.5f, (num3 + num4) * 0.5f, ((Transform)component).localPosition.z); ManualLogSource logger2 = Plugin.Logger; rect = component.rect; logger2.LogInfo((object)$"Sized the independent upgrade page from Canvas geometry: {((Rect)(ref rect)).size}."); rect = component.rect; if (((Rect)(ref rect)).width > 0f) { rect = component.rect; return ((Rect)(ref rect)).height > 0f; } return false; } } } Plugin.Logger.LogError((object)"Neither the Main page, upgrade page, MenuHolder, nor Canvas has measurable geometry."); return false; } internal static bool EnsureRegistered() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected I4, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected I4, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Expected I4, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Expected O, but got Unknown //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invali