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 RepoQuota v1.1.0
plugins\RepoQuota.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using RepoQuota.Systems; using RepoQuota.UI; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("RepoQuota")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RepoQuota")] [assembly: AssemblyTitle("RepoQuota")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 RepoQuota { [BepInPlugin("com.repoquota.mod", "RepoQuota", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public static ConfigEntry<string> DifficultyPreset; public static ConfigEntry<int> BaseQuotaPercent; public static ConfigEntry<int> PercentIncreasePerCycle; public static ConfigEntry<int> MaxQuotaPercent; public static ConfigEntry<int> LevelsPerCycle; public static ConfigEntry<string> FailurePenalty; public static ConfigEntry<int> MoneyPenaltyPercent; public static ConfigEntry<bool> ResetQuotaOnFail; public static ConfigEntry<bool> ShowLevelResults; public static ConfigEntry<bool> ShowCompanyMessages; public static ConfigEntry<KeyCode> ToggleTerminalKey; private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindConfig(); DifficultyPreset.SettingChanged += delegate { if (DifficultyPreset.Value.ToLower() != "custom") { ApplyPreset(DifficultyPreset.Value); Log.LogInfo((object)("Preset changed to: " + DifficultyPreset.Value)); } }; _harmony = new Harmony("com.repoquota.mod"); _harmony.PatchAll(); QuotaManager.Initialize(); ((Component)this).gameObject.AddComponent<QuotaTerminal>(); ((Component)this).gameObject.SetActive(true); ((Behaviour)this).enabled = true; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Log.LogInfo((object)"RepoQuota v1.1.0 loaded!"); Log.LogInfo((object)("[Config] Preset=" + DifficultyPreset.Value + ", " + $"BaseQuota={BaseQuotaPercent.Value}%, " + $"Increase={PercentIncreasePerCycle.Value}%, " + $"Max={MaxQuotaPercent.Value}%, " + $"LevelsPerCycle={LevelsPerCycle.Value}, " + "Penalty=" + FailurePenalty.Value + ", " + $"MoneyLoss={MoneyPenaltyPercent.Value}%, " + $"ResetOnFail={ResetQuotaOnFail.Value}, " + $"ShowResults={ShowLevelResults.Value}, " + $"ShowMessages={ShowCompanyMessages.Value}")); } private void Update() { QuotaManager.UpdateNotification(Time.deltaTime); } private void BindConfig() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown DifficultyPreset = ((BaseUnityPlugin)this).Config.Bind<string>("1. Difficulty Preset", "Preset", "Normal", new ConfigDescription("Choose a difficulty preset or set to 'Custom' to use your own values below.\n\nEasy = 50% quota, 4 levels, warning on fail\nNormal = 60% quota, 3 levels, lose 30% cash on fail\nHard = 70% quota, 3 levels, lose 50% cash on fail\nLethal = 80% quota, 2 levels, game over on fail\nCustom = Use the values you set in sections 2 and 3", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "Easy", "Normal", "Hard", "Lethal", "Custom" }), Array.Empty<object>())); BaseQuotaPercent = ((BaseUnityPlugin)this).Config.Bind<int>("2. Quota Settings", "Starting Quota Percent", 60, new ConfigDescription("What percentage of available loot you need to collect.\nExample: 60 means you must extract 60% of all valuables across the cycle.\nOnly used when Preset is set to 'Custom'.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 99), Array.Empty<object>())); PercentIncreasePerCycle = ((BaseUnityPlugin)this).Config.Bind<int>("2. Quota Settings", "Increase Per Cycle", 5, new ConfigDescription("How much harder it gets each cycle.\nExample: 5 means the quota goes from 60% → 65% → 70% each cycle.\nSet to 0 for a flat difficulty that never changes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); MaxQuotaPercent = ((BaseUnityPlugin)this).Config.Bind<int>("2. Quota Settings", "Max Quota Percent", 90, new ConfigDescription("The quota percentage will never go above this value.\nExample: 90 means even after many cycles, you'll never need more than 90%.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 99), Array.Empty<object>())); LevelsPerCycle = ((BaseUnityPlugin)this).Config.Bind<int>("2. Quota Settings", "Levels Per Cycle", 3, new ConfigDescription("How many levels you get to meet each quota.\nYour loot collection is tracked across all levels in the cycle.\nMore levels = more chances to make up for a bad run.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); FailurePenalty = ((BaseUnityPlugin)this).Config.Bind<string>("3. Penalty Settings", "Failure Penalty", "MoneyDrain", new ConfigDescription("What happens when you fail to meet quota.\n\nWarning = Scary message, no real consequence. Good for casual play.\nMoneyDrain = Lose a percentage of your money (set below). Default.\nGameOver = Run ends. The Taxman terminates your contract.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Warning", "MoneyDrain", "GameOver" }), Array.Empty<object>())); MoneyPenaltyPercent = ((BaseUnityPlugin)this).Config.Bind<int>("3. Penalty Settings", "Money Loss Percent", 30, new ConfigDescription("How much money you lose when the penalty is 'MoneyDrain'.\nExample: 30 means you lose 30% of your current cash.\nIgnored if penalty is set to Warning or GameOver.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 90), Array.Empty<object>())); ResetQuotaOnFail = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Penalty Settings", "Reset Quota On Fail", false, "If true, failing a quota resets the percentage back to the starting value.\nIf false, the next cycle keeps the same difficulty.\nExample: You're at 75% quota, you fail. With this ON, next cycle is 60% again."); ShowLevelResults = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Display", "Show Level Results", true, "Show a notification after each level with your extraction percentage."); ShowCompanyMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Display", "Show Company Messages", true, "Show themed messages from The Taxman when quotas are met or failed."); ToggleTerminalKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("4. Display", "Terminal Key", (KeyCode)285, "Press this key to open the Taxman Terminal with full quota details."); } private void ApplyPreset(string preset) { switch (preset.ToLower()) { case "easy": BaseQuotaPercent.Value = 50; PercentIncreasePerCycle.Value = 3; MaxQuotaPercent.Value = 80; LevelsPerCycle.Value = 4; FailurePenalty.Value = "Warning"; ResetQuotaOnFail.Value = false; break; case "normal": BaseQuotaPercent.Value = 60; PercentIncreasePerCycle.Value = 5; MaxQuotaPercent.Value = 90; LevelsPerCycle.Value = 3; FailurePenalty.Value = "MoneyDrain"; MoneyPenaltyPercent.Value = 30; ResetQuotaOnFail.Value = false; break; case "hard": BaseQuotaPercent.Value = 70; PercentIncreasePerCycle.Value = 5; MaxQuotaPercent.Value = 95; LevelsPerCycle.Value = 3; FailurePenalty.Value = "MoneyDrain"; MoneyPenaltyPercent.Value = 50; ResetQuotaOnFail.Value = false; break; case "lethal": BaseQuotaPercent.Value = 80; PercentIncreasePerCycle.Value = 5; MaxQuotaPercent.Value = 99; LevelsPerCycle.Value = 2; FailurePenalty.Value = "GameOver"; ResetQuotaOnFail.Value = false; break; default: Log.LogInfo((object)"Using custom config values."); break; } Log.LogInfo((object)("Preset applied: " + preset)); } public static float GetBasePercent() { return (float)BaseQuotaPercent.Value / 100f; } public static float GetIncreasePerCycle() { return (float)PercentIncreasePerCycle.Value / 100f; } public static float GetMaxPercent() { return (float)MaxQuotaPercent.Value / 100f; } public static float GetMoneyPenaltyPercent() { return (float)MoneyPenaltyPercent.Value / 100f; } } public static class PluginInfo { public const string GUID = "com.repoquota.mod"; public const string NAME = "RepoQuota"; public const string VERSION = "1.1.0"; } } namespace RepoQuota.UI { public class QuotaSign : MonoBehaviour { private TextMeshPro _quotaText; private TextMeshPro _deadlineText; private ExtractionPoint _extractionPoint; private int _bankedExtraction; private bool _hasCompleted; private static List<QuotaSign> _allSigns = new List<QuotaSign>(); private static FieldInfo _haulCurrentField; private static bool _haulCurrentLookedUp = false; private static int _unclaimedBanked = 0; public static void SpawnAtExtractionPoints() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); foreach (ExtractionPoint val in array) { if (!((Object)(object)((Component)val).transform.Find("QuotaSign") != (Object)null)) { TextMeshPro haulGoalScreen = val.haulGoalScreen; if ((Object)(object)haulGoalScreen == (Object)null) { Plugin.Log.LogWarning((object)"[Quota] ExtractionPoint missing haulGoalScreen."); continue; } GameObject val2 = new GameObject("QuotaSign"); val2.transform.SetParent(((Component)val).transform, false); Vector3 position = haulGoalScreen.transform.position; val2.transform.position = position + haulGoalScreen.transform.up * 0.7f; val2.transform.rotation = haulGoalScreen.transform.rotation; QuotaSign quotaSign = val2.AddComponent<QuotaSign>(); quotaSign._extractionPoint = val; quotaSign.BuildSign(haulGoalScreen); Plugin.Log.LogInfo((object)"[Quota] Sign placed."); } } } private void OnEnable() { _allSigns.Add(this); } private void OnDisable() { _allSigns.Remove(this); } public static void ResetCompleted(ExtractionPoint ep) { foreach (QuotaSign allSign in _allSigns) { if ((Object)(object)allSign._extractionPoint == (Object)(object)ep) { allSign._hasCompleted = false; break; } } } public static void BankExtraction(ExtractionPoint ep, int amount) { foreach (QuotaSign allSign in _allSigns) { if ((Object)(object)allSign._extractionPoint == (Object)(object)ep) { allSign._bankedExtraction += amount; allSign._hasCompleted = true; Plugin.Log.LogInfo((object)$"[Quota] Sign banked ${amount}. Total for this EP: ${allSign._bankedExtraction}"); return; } } _unclaimedBanked += amount; Plugin.Log.LogInfo((object)$"[Quota] No sign for EP, added to unclaimed: ${amount}"); } private static int ReadHaulCurrent(ExtractionPoint ep) { if (!_haulCurrentLookedUp) { _haulCurrentField = typeof(ExtractionPoint).GetField("haulCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _haulCurrentLookedUp = true; if (_haulCurrentField == null) { Plugin.Log.LogWarning((object)"[Quota] Could not find haulCurrent field on ExtractionPoint."); } } if (_haulCurrentField == null || (Object)(object)ep == (Object)null) { return 0; } object value = _haulCurrentField.GetValue(ep); if (value is int) { return (int)value; } return 0; } public static int GetTotalLiveExtraction() { int num = _unclaimedBanked; foreach (QuotaSign allSign in _allSigns) { if (!((Object)(object)allSign._extractionPoint == (Object)null)) { num += allSign._bankedExtraction; if (!allSign._hasCompleted) { num += ReadHaulCurrent(allSign._extractionPoint); } } } return num; } public static int CollectAndResetExtraction() { int totalLiveExtraction = GetTotalLiveExtraction(); _unclaimedBanked = 0; foreach (QuotaSign allSign in _allSigns) { allSign._bankedExtraction = 0; allSign._hasCompleted = false; } return totalLiveExtraction; } private void BuildSign(TextMeshPro reference) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0109: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01d9: 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_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Expected O, but got Unknown //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0334: 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) TMP_FontAsset font = ((TMP_Text)reference).font; GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "SignBacking"; obj.transform.SetParent(((Component)this).transform, false); obj.transform.localPosition = new Vector3(0f, 0f, 0.01f); obj.transform.localRotation = Quaternion.identity; obj.transform.localScale = new Vector3(1.1f, 0.32f, 1f); Collider component = obj.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } MeshRenderer component2 = obj.GetComponent<MeshRenderer>(); if ((Object)(object)component2 != (Object)null) { ((Renderer)component2).material.color = new Color(0.02f, 0.02f, 0.03f, 1f); } GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj2).name = "SignBorder"; obj2.transform.SetParent(((Component)this).transform, false); obj2.transform.localPosition = new Vector3(0f, 0f, 0.015f); obj2.transform.localRotation = Quaternion.identity; obj2.transform.localScale = new Vector3(1.15f, 0.36f, 1f); Collider component3 = obj2.GetComponent<Collider>(); if ((Object)(object)component3 != (Object)null) { Object.Destroy((Object)(object)component3); } MeshRenderer component4 = obj2.GetComponent<MeshRenderer>(); if ((Object)(object)component4 != (Object)null) { ((Renderer)component4).material.color = new Color(0.18f, 0.18f, 0.2f, 1f); } GameObject val = new GameObject("QuotaText"); val.transform.SetParent(((Component)this).transform, false); _quotaText = val.AddComponent<TextMeshPro>(); ((TMP_Text)_quotaText).font = font; ((TMP_Text)_quotaText).fontSize = 2.4f; ((TMP_Text)_quotaText).fontStyle = (FontStyles)1; ((Graphic)_quotaText).color = ((Graphic)reference).color; ((TMP_Text)_quotaText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_quotaText).enableWordWrapping = false; ((TMP_Text)_quotaText).overflowMode = (TextOverflowModes)0; ((TMP_Text)_quotaText).text = "QUOTA: <color=#bd4300>$</color>0 / <color=#bd4300>$</color>0"; ((Transform)((TMP_Text)_quotaText).rectTransform).localPosition = new Vector3(0f, 0.08f, 0f); ((TMP_Text)_quotaText).rectTransform.sizeDelta = new Vector2(1.2f, 0.2f); GameObject val2 = new GameObject("DeadlineText"); val2.transform.SetParent(((Component)this).transform, false); _deadlineText = val2.AddComponent<TextMeshPro>(); ((TMP_Text)_deadlineText).font = font; ((TMP_Text)_deadlineText).fontSize = 1.8f; ((TMP_Text)_deadlineText).fontStyle = (FontStyles)0; ((Graphic)_deadlineText).color = ((Graphic)reference).color; ((TMP_Text)_deadlineText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_deadlineText).enableWordWrapping = false; ((TMP_Text)_deadlineText).overflowMode = (TextOverflowModes)0; ((TMP_Text)_deadlineText).text = "LEVEL 0/0"; ((Transform)((TMP_Text)_deadlineText).rectTransform).localPosition = new Vector3(0f, -0.15f, 0f); ((TMP_Text)_deadlineText).rectTransform.sizeDelta = new Vector2(1.2f, 0.2f); } private void Update() { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_quotaText == (Object)null) && QuotaManager.IsActive) { int num = (QuotaManager.IsInGameLevel ? GetTotalLiveExtraction() : 0); int num2 = QuotaManager.CycleTotalExtracted + num; int dollarTarget = QuotaManager.DollarTarget; string text = SemiFunc.DollarGetString(num2); string text2 = SemiFunc.DollarGetString(dollarTarget); ((TMP_Text)_quotaText).text = "QUOTA: <color=#bd4300>$</color>" + text + " / <color=#bd4300>$</color>" + text2; int num3 = QuotaManager.LevelsCompleted + (QuotaManager.IsInGameLevel ? 1 : 0); int levelsPerCycle = QuotaManager.LevelsPerCycle; if (num2 >= dollarTarget && dollarTarget > 0) { ((TMP_Text)_deadlineText).text = $"LEVEL {num3}/{levelsPerCycle}: QUOTA MET"; ((Graphic)_deadlineText).color = new Color(0.2f, 1f, 0.3f); } else { ((TMP_Text)_deadlineText).text = $"LEVEL {num3}/{levelsPerCycle}: QUOTA NOT MET"; ((Graphic)_deadlineText).color = new Color(1f, 0.3f, 0.2f); } } } } public class QuotaTerminal : MonoBehaviour { private bool _visible; private REPOPopupPage _page; private REPOLabel _cycleLabel; private REPOLabel _targetLabel; private REPOLabel _cycleLevelsLabel; private REPOLabel _breakdownHeaderLabel; private REPOLabel _cycleTotalLabel; private REPOLabel _averageLabel; private REPOLabel[] _levelLabels; private REPOLabel _settingsHeaderLabel; private REPOLabel _difficultyLabel; private REPOLabel _penaltyLabel; private REPOLabel _resetOnFailLabel; private const int MAX_LEVEL_LABELS = 5; private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(Plugin.ToggleTerminalKey.Value)) { _visible = !_visible; if (_visible) { OpenTerminal(); } else { CloseTerminal(); } } if (_visible && (Object)(object)_page != (Object)null) { RefreshLabels(); } } private void OpenTerminal() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Expected O, but got Unknown //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Expected O, but got Unknown //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown if ((Object)(object)_page != (Object)null) { CloseTerminal(); return; } _page = MenuAPI.CreateREPOPopupPage("Quota Status", (PresetSide)1, false, true, 0.15f); _page.onEscapePressed = (ShouldCloseMenuDelegate)delegate { _visible = false; return true; }; Vector3 scale = new Vector3(0.4f, 0.4f, 1f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _cycleLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_cycleLabel.labelTMP).richText = true; ((Component)_cycleLabel).transform.localScale = scale; return ((REPOElement)_cycleLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _targetLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_targetLabel.labelTMP).richText = true; ((Component)_targetLabel).transform.localScale = scale; return ((REPOElement)_targetLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _cycleLevelsLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_cycleLevelsLabel.labelTMP).richText = true; ((Component)_cycleLevelsLabel).transform.localScale = scale; return ((REPOElement)_cycleLevelsLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) _breakdownHeaderLabel = MenuAPI.CreateREPOLabel("Per-Level Breakdown", sv, Vector2.zero); ((Component)_breakdownHeaderLabel).transform.localScale = scale; return ((REPOElement)_breakdownHeaderLabel).rectTransform; }, 0f, 0f); _levelLabels = (REPOLabel[])(object)new REPOLabel[5]; for (int i = 0; i < 5; i++) { int idx = i; _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) _levelLabels[idx] = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_levelLabels[idx].labelTMP).richText = true; ((Component)_levelLabels[idx]).transform.localScale = scale; return ((REPOElement)_levelLabels[idx]).rectTransform; }, 0f, 0f); } _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _cycleTotalLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_cycleTotalLabel.labelTMP).richText = true; ((Component)_cycleTotalLabel).transform.localScale = scale; return ((REPOElement)_cycleTotalLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _averageLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_averageLabel.labelTMP).richText = true; ((Component)_averageLabel).transform.localScale = scale; return ((REPOElement)_averageLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) _settingsHeaderLabel = MenuAPI.CreateREPOLabel("Settings", sv, Vector2.zero); ((Component)_settingsHeaderLabel).transform.localScale = scale; return ((REPOElement)_settingsHeaderLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _difficultyLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_difficultyLabel.labelTMP).richText = true; ((Component)_difficultyLabel).transform.localScale = scale; return ((REPOElement)_difficultyLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _penaltyLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_penaltyLabel.labelTMP).richText = true; ((Component)_penaltyLabel).transform.localScale = scale; return ((REPOElement)_penaltyLabel).rectTransform; }, 0f, 0f); _page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _resetOnFailLabel = MenuAPI.CreateREPOLabel("", sv, Vector2.zero); ((TMP_Text)_resetOnFailLabel.labelTMP).richText = true; ((Component)_resetOnFailLabel).transform.localScale = scale; return ((REPOElement)_resetOnFailLabel).rectTransform; }, 0f, 0f); _page.OpenPage(false); RefreshLabels(); } private void CloseTerminal() { if ((Object)(object)_page != (Object)null) { _page.ClosePage(true); _page = null; _cycleLabel = null; _targetLabel = null; _cycleLevelsLabel = null; _breakdownHeaderLabel = null; _cycleTotalLabel = null; _averageLabel = null; _levelLabels = null; _settingsHeaderLabel = null; _difficultyLabel = null; _penaltyLabel = null; _resetOnFailLabel = null; } _visible = false; } private void RefreshLabels() { if ((Object)(object)_cycleLabel == (Object)null) { return; } ((TMP_Text)_cycleLabel.labelTMP).text = $"Cycle: #{QuotaManager.CycleNumber}"; ((TMP_Text)_targetLabel.labelTMP).text = $"Quota Target: {QuotaManager.RequiredPercent * 100f:F0}%"; int num = QuotaManager.LevelsCompleted + (QuotaManager.IsInGameLevel ? 1 : 0); ((TMP_Text)_cycleLevelsLabel.labelTMP).text = $"Cycle Levels: {num} / {QuotaManager.LevelsPerCycle}"; int num2 = 0; foreach (QuotaManager.LevelRecord currentCycleRecord in QuotaManager.CurrentCycleRecords) { if (num2 >= 5) { break; } int num3 = Mathf.RoundToInt((float)currentCycleRecord.TotalAvailable * QuotaManager.RequiredPercent); string text = ((currentCycleRecord.TotalExtracted >= num3) ? "#44FF44" : "#FF4444"); ((TMP_Text)_levelLabels[num2].labelTMP).text = $" Level {currentCycleRecord.LevelNumber}: <color={text}>${currentCycleRecord.TotalExtracted:N0} / ${num3:N0}</color>"; SetLabelVisible(_levelLabels[num2], visible: true); num2++; } if (QuotaManager.IsInGameLevel && QuotaManager.CurrentLevelAvailable > 1 && num2 < 5) { int num4 = QuotaManager.LevelsCompleted + 1; int totalLiveExtraction = QuotaSign.GetTotalLiveExtraction(); int num5 = Mathf.RoundToInt((float)QuotaManager.CurrentLevelAvailable * QuotaManager.RequiredPercent); string text2 = ((totalLiveExtraction >= num5) ? "#44FF44" : "#FF4444"); ((TMP_Text)_levelLabels[num2].labelTMP).text = $" Level {num4} (Current): <color={text2}>${totalLiveExtraction:N0} / ${num5:N0}</color>"; SetLabelVisible(_levelLabels[num2], visible: true); num2++; } int num6 = QuotaManager.LevelsPerCycle - QuotaManager.LevelsCompleted; if (QuotaManager.IsInGameLevel && QuotaManager.CurrentLevelAvailable > 1) { num6--; } for (int i = 0; i < num6; i++) { if (num2 >= 5) { break; } ((TMP_Text)_levelLabels[num2].labelTMP).text = " Level ?: --- / ---"; SetLabelVisible(_levelLabels[num2], visible: true); num2++; } for (int j = num2; j < 5; j++) { if ((Object)(object)_levelLabels[j] != (Object)null) { SetLabelVisible(_levelLabels[j], visible: false); } } int num7 = (QuotaManager.IsInGameLevel ? QuotaSign.GetTotalLiveExtraction() : 0); int num8 = QuotaManager.CycleTotalExtracted + num7; float num9 = ((QuotaManager.CycleTotalAvailable > 0) ? ((float)num8 / (float)QuotaManager.CycleTotalAvailable) : 0f); ((TMP_Text)_cycleTotalLabel.labelTMP).text = $"Cycle Total: ${num8:N0} / ${QuotaManager.DollarTarget:N0}"; ((TMP_Text)_averageLabel.labelTMP).text = $"Average: {num9 * 100f:F0}% / {QuotaManager.RequiredPercent * 100f:F0}%"; ((TMP_Text)_difficultyLabel.labelTMP).text = " Difficulty: " + Plugin.DifficultyPreset.Value; ((TMP_Text)_penaltyLabel.labelTMP).text = " Penalty: " + Plugin.FailurePenalty.Value; ((TMP_Text)_resetOnFailLabel.labelTMP).text = " Reset on Fail: " + (Plugin.ResetQuotaOnFail.Value ? "Yes" : "No"); } private void SetLabelVisible(REPOLabel label, bool visible) { if ((Object)(object)label != (Object)null && ((Component)label).gameObject.activeSelf != visible) { ((Component)label).gameObject.SetActive(visible); } } } } namespace RepoQuota.Systems { public static class CompanyMessages { private static string Pick(string[] pool) { return pool[Random.Range(0, pool.Length)]; } public static string GetQuotaMetMessage(float pct, int streak) { string text = Pick(new string[4] { $"Quota met — {pct:F0}% collected. Acceptable.", $"{pct:F0}% extraction rate. The Taxman is satisfied... for now.", "Quota fulfilled. You may continue operating.", $"Target reached at {pct:F0}%. Don't get comfortable." }); if (streak >= 3) { text += $"\n{streak} in a row. Impressive."; } else if (streak >= 2) { text += $"\n{streak} consecutive. Keep it up."; } return text; } public static string GetFailureMessage(float got, float needed) { return Pick(new string[4] { $"QUOTA MISSED. Collected {got:F0}%, needed {needed:F0}%.", $"Insufficient extraction. {got:F0}% does not meet the {needed:F0}% requirement.", $"The Taxman is displeased. {got:F0}% vs {needed:F0}% target.", $"You have failed to meet your obligation. {got:F0}% collected." }); } public static string GetGameOverMessage() { return Pick(new string[3] { "CONTRACT TERMINATED.\nThe Company has no further use for you.", "YOUR SERVICES ARE NO LONGER REQUIRED.", "TERMINATED. You have been deemed unprofitable." }); } } public static class QuotaManager { public struct LevelRecord { public int LevelNumber; public int TotalAvailable; public int TotalExtracted; public float CollectionRate; } private static bool _gameOverTriggered = false; public static int CycleNumber { get; private set; } public static float RequiredPercent { get; private set; } public static int LevelsPerCycle { get; private set; } public static int LevelsCompleted { get; private set; } public static int LevelsRemaining => LevelsPerCycle - LevelsCompleted; public static int CycleTotalAvailable { get; private set; } public static int CycleTotalExtracted { get; private set; } public static float CycleCollectionRate { get { if (CycleTotalAvailable <= 0) { return 0f; } return (float)CycleTotalExtracted / (float)CycleTotalAvailable; } } public static bool QuotaMet => CycleCollectionRate >= RequiredPercent; public static int DollarTarget => Mathf.RoundToInt((float)CycleTotalAvailable * RequiredPercent); public static int CurrentLevelAvailable { get; private set; } public static int CurrencyAtLevelStart { get; private set; } public static List<LevelRecord> CurrentCycleRecords { get; private set; } = new List<LevelRecord>(); public static int ConsecutiveQuotasMet { get; private set; } public static int TotalLifetimeExtracted { get; private set; } public static int HighestStreakEver { get; private set; } public static bool IsActive { get; private set; } public static bool IsInGameLevel { get; private set; } public static string PendingNotification { get; private set; } = ""; public static float NotificationTimer { get; private set; } public static event Action OnCycleStart; public static event Action OnLevelEarningsRecorded; public static event Action<bool> OnCycleEnd; public static void Initialize() { CycleNumber = 0; ConsecutiveQuotasMet = 0; TotalLifetimeExtracted = 0; IsActive = true; IsInGameLevel = false; _gameOverTriggered = false; StartNewCycle(); } public static void StartNewCycle() { CycleNumber++; LevelsCompleted = 0; CycleTotalAvailable = 0; CycleTotalExtracted = 0; CurrentCycleRecords.Clear(); CurrentLevelAvailable = 0; LevelsPerCycle = Plugin.LevelsPerCycle.Value; RequiredPercent = CalculateRequiredPercent(); string arg = $"{RequiredPercent * 100f:F0}%"; string text = $"QUOTA #{CycleNumber}: Collect {arg} of loot across {LevelsPerCycle} levels"; ShowNotification(text, 5f); QuotaManager.OnCycleStart?.Invoke(); Plugin.Log.LogInfo((object)text); } private static float CalculateRequiredPercent() { float basePercent = Plugin.GetBasePercent(); float increasePerCycle = Plugin.GetIncreasePerCycle(); float maxPercent = Plugin.GetMaxPercent(); return Mathf.Min(basePercent + increasePerCycle * (float)(CycleNumber - 1), maxPercent); } public static void OnEnterGameLevel() { if (IsActive) { IsInGameLevel = true; CurrentLevelAvailable = 0; CurrencyAtLevelStart = ReadCurrency(); Plugin.Log.LogInfo((object)"[Quota] Entered game level — waiting for level gen to scan valuables."); } } public static void RescanAvailableLoot() { if (IsActive && IsInGameLevel) { CycleTotalAvailable -= CurrentLevelAvailable; CurrentLevelAvailable = ScanAvailableLoot(); CycleTotalAvailable += CurrentLevelAvailable; Plugin.Log.LogInfo((object)($"[Quota] Loot scan complete: ${CurrentLevelAvailable:N0} available. " + $"Cycle total: ${CycleTotalAvailable:N0}")); } } private static int ScanAvailableLoot() { int num = 0; try { ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>(); ValuableObject[] array2 = array; foreach (ValuableObject valuable in array2) { num += GetValuableValue(valuable); } Plugin.Log.LogInfo((object)$"[Quota] Scanned {array.Length} valuables, total: ${num:N0}"); } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] Error scanning valuables: " + ex.Message)); num = EstimateFromExtractionQuota(); } return Mathf.Max(num, 1); } private static int GetValuableValue(ValuableObject valuable) { try { FieldInfo fieldInfo = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent"); if (fieldInfo != null) { object value = fieldInfo.GetValue(valuable); if (value is float num) { return Mathf.RoundToInt(num); } if (value is int result) { return result; } } FieldInfo fieldInfo2 = AccessTools.Field(typeof(ValuableObject), "dollarValueOriginal"); if (fieldInfo2 != null) { object value2 = fieldInfo2.GetValue(valuable); if (value2 is float num2) { return Mathf.RoundToInt(num2); } if (value2 is int result2) { return result2; } } Plugin.Log.LogWarning((object)"[Quota] Could not read valuable value — field name may be wrong."); return 0; } catch { return 0; } } private static int EstimateFromExtractionQuota() { try { ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); int num = 0; ExtractionPoint[] array2 = array; foreach (ExtractionPoint obj in array2) { FieldInfo fieldInfo = AccessTools.Field(typeof(ExtractionPoint), "goalValue"); if (fieldInfo != null) { object value = fieldInfo.GetValue(obj); if (value is int num2) { num += num2; } if (value is float num3) { num += Mathf.RoundToInt(num3); } } } int num4 = Mathf.RoundToInt((float)num * 1.75f); Plugin.Log.LogInfo((object)$"[Quota] Estimated available loot from quotas: ${num4:N0}"); return num4; } catch { return 10000; } } public static void OnLeaveGameLevel() { if (!IsActive || !IsInGameLevel) { return; } IsInGameLevel = false; if (CurrentLevelAvailable <= 1) { Plugin.Log.LogInfo((object)"[Quota] Skipping level with no real loot (likely shop/transition)."); QuotaSign.CollectAndResetExtraction(); return; } int num = QuotaSign.CollectAndResetExtraction(); CycleTotalExtracted += num; TotalLifetimeExtracted += num; LevelsCompleted++; LevelRecord levelRecord = default(LevelRecord); levelRecord.LevelNumber = LevelsCompleted; levelRecord.TotalAvailable = CurrentLevelAvailable; levelRecord.TotalExtracted = num; levelRecord.CollectionRate = ((CurrentLevelAvailable > 0) ? ((float)num / (float)CurrentLevelAvailable) : 0f); LevelRecord item = levelRecord; CurrentCycleRecords.Add(item); Plugin.Log.LogInfo((object)($"[Quota] Level {item.LevelNumber} done: extracted ${num:N0} / ${CurrentLevelAvailable:N0} " + $"({item.CollectionRate * 100f:F0}%). " + $"Cycle: ${CycleTotalExtracted:N0} / ${CycleTotalAvailable:N0} " + $"({CycleCollectionRate * 100f:F0}% — need {RequiredPercent * 100f:F0}%)")); QuotaManager.OnLevelEarningsRecorded?.Invoke(); if (Plugin.ShowLevelResults.Value) { ShowNotification($"Level {item.LevelNumber}: ${num:N0} extracted ({item.CollectionRate * 100f:F0}%)\n" + $"Cycle progress: {CycleCollectionRate * 100f:F0}% / {RequiredPercent * 100f:F0}%", 4f); } if (LevelsCompleted >= LevelsPerCycle) { EndCycle(); } } private static void EndCycle() { bool quotaMet = QuotaMet; QuotaManager.OnCycleEnd?.Invoke(quotaMet); if (quotaMet) { ConsecutiveQuotasMet++; if (ConsecutiveQuotasMet > HighestStreakEver) { HighestStreakEver = ConsecutiveQuotasMet; } float num = CycleCollectionRate * 100f; ShowNotification(Plugin.ShowCompanyMessages.Value ? CompanyMessages.GetQuotaMetMessage(num, ConsecutiveQuotasMet) : $"QUOTA MET — {num:F0}% collected.", 6f); Plugin.Log.LogInfo((object)$"[Quota] CYCLE {CycleNumber} MET! ({num:F0}%)"); StartNewCycle(); } else { ConsecutiveQuotasMet = 0; float num2 = CycleCollectionRate * 100f; string value = Plugin.FailurePenalty.Value; Plugin.Log.LogWarning((object)($"[Quota] CYCLE {CycleNumber} FAILED. " + $"({num2:F0}% vs {RequiredPercent * 100f:F0}% needed)")); ApplyPenalty(value); } } private static void ApplyPenalty(string penalty) { switch (penalty.ToLower()) { case "gameover": IsActive = false; if (Plugin.ShowCompanyMessages.Value) { ShowNotification(CompanyMessages.GetGameOverMessage(), 10f); } try { RunManager instance = RunManager.instance; if ((Object)(object)instance != (Object)null && !_gameOverTriggered) { _gameOverTriggered = true; Plugin.Log.LogInfo((object)"[Quota] GAME OVER — triggering arena (last man standing)."); instance.levelCurrent = instance.levelArena; instance.ChangeLevel(true, false, (ChangeLevelType)3); } break; } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] Failed to trigger game over: " + ex.Message)); break; } case "moneydrain": { int num = ReadCurrency(); float moneyPenaltyPercent = Plugin.GetMoneyPenaltyPercent(); int num2 = Mathf.RoundToInt((float)num * moneyPenaltyPercent); int num3 = num - num2; try { PunManager.instance.SetRunStatSet("currency", num3); } catch { Plugin.Log.LogWarning((object)"[Quota] Failed to set currency."); } ShowNotification((Plugin.ShowCompanyMessages.Value ? CompanyMessages.GetFailureMessage(CycleCollectionRate * 100f, RequiredPercent * 100f) : "QUOTA FAILED.") + $"\n${num2:N0} confiscated ({Plugin.MoneyPenaltyPercent.Value}% penalty).", 8f); if (Plugin.ResetQuotaOnFail.Value) { CycleNumber = 0; } StartNewCycle(); break; } default: ShowNotification(Plugin.ShowCompanyMessages.Value ? CompanyMessages.GetFailureMessage(CycleCollectionRate * 100f, RequiredPercent * 100f) : $"QUOTA MISSED. Collected {CycleCollectionRate * 100f:F0}%, needed {RequiredPercent * 100f:F0}%.", 6f); if (Plugin.ResetQuotaOnFail.Value) { CycleNumber = 0; } StartNewCycle(); break; } } public static int ReadCurrency() { try { return StatsManager.instance.runStats["currency"]; } catch { Plugin.Log.LogWarning((object)"[Quota] Could not read currency from StatsManager."); return 0; } } public static void SnapshotCurrencyAtLevelStart() { CurrencyAtLevelStart = ReadCurrency(); } public static bool IsGameLevel() { try { RunManager instance = RunManager.instance; return (Object)(object)instance.levelCurrent != (Object)(object)instance.levelShop && (Object)(object)instance.levelCurrent != (Object)(object)instance.levelMainMenu && (Object)(object)instance.levelCurrent != (Object)(object)instance.levelLobbyMenu && (Object)(object)instance.levelCurrent != (Object)(object)instance.levelTutorial && (Object)(object)instance.levelCurrent != (Object)(object)instance.levelArena; } catch { return false; } } public static void ShowNotification(string message, float duration) { PendingNotification = message; NotificationTimer = duration; } public static void UpdateNotification(float deltaTime) { if (NotificationTimer > 0f) { NotificationTimer -= deltaTime; if (NotificationTimer <= 0f) { PendingNotification = ""; } } } } } namespace RepoQuota.Patches { [HarmonyPatch(typeof(RunManager), "ChangeLevel")] public class RunManager_ChangeLevel_Patch { [HarmonyPrefix] private static void Prefix() { try { if (QuotaManager.IsActive && QuotaManager.IsGameLevel()) { QuotaManager.OnLeaveGameLevel(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] ChangeLevel Prefix error: " + ex.Message)); } } [HarmonyPostfix] private static void Postfix(RunManager __instance) { try { if (QuotaManager.IsActive && !((Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelMainMenu) && !((Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelLobbyMenu) && !((Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelTutorial) && (Object)(object)__instance.levelCurrent != (Object)(object)__instance.levelShop && (Object)(object)__instance.levelCurrent != (Object)(object)__instance.levelArena) { QuotaManager.OnEnterGameLevel(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] ChangeLevel Postfix error: " + ex.Message)); } } } [HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")] public class RoundDirector_StartRoundLogic_Patch { [HarmonyPostfix] private static void Postfix() { try { if (QuotaManager.IsActive) { QuotaManager.SnapshotCurrencyAtLevelStart(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] StartRoundLogic Postfix error: " + ex.Message)); } } } [HarmonyPatch(typeof(StatsManager), "ResetAllStats")] public class StatsManager_ResetAllStats_Patch { [HarmonyPostfix] private static void Postfix() { QuotaManager.Initialize(); Plugin.Log.LogInfo((object)"[Quota] New run — quota initialized."); } } [HarmonyPatch(typeof(ExtractionPoint), "StateSetRPC")] public class ExtractionPoint_StateSetRPC_Patch { [HarmonyPrefix] private static void Prefix(ExtractionPoint __instance, State state) { //IL_0016: 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_0028: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 try { if (!QuotaManager.IsActive) { return; } Plugin.Log.LogInfo((object)$"[Quota] EP StateSetRPC: {state}"); if ((int)state == 2) { QuotaSign.ResetCompleted(__instance); } if ((int)state != 7 && (int)state != 1) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(ExtractionPoint), "haulCurrent"); if (fieldInfo != null) { int num = (int)fieldInfo.GetValue(__instance); if (num > 0) { QuotaSign.BankExtraction(__instance, num); Plugin.Log.LogInfo((object)$"[Quota] Banked ${num} from extraction point."); } } } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] StateSetRPC Prefix error: " + ex.Message)); } } } [HarmonyPatch(typeof(GameDirector), "SetStart")] public class GameDirector_SetStart_Patch { [HarmonyPostfix] private static void Postfix() { try { if (QuotaManager.IsActive && QuotaManager.IsGameLevel()) { QuotaManager.RescanAvailableLoot(); QuotaSign.SpawnAtExtractionPoints(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Quota] GameDirector.SetStart Postfix error: " + ex.Message)); } } } }