Decompiled source of RepoQuota v1.0.1

plugins\RepoQuota.dll

Decompiled 3 hours ago
using 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 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.0.0")]
	[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;

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			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.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.0.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.");
		}

		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;
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public static class PluginInfo
	{
		public const string GUID = "com.repoquota.mod";

		public const string NAME = "RepoQuota";

		public const string VERSION = "1.0.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(0.9f, 0.28f, 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(0.95f, 0.32f, 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 = 3f;
			((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.055f, 0f);
			((TMP_Text)_quotaText).rectTransform.sizeDelta = new Vector2(1f, 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 = 2f;
			((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 = "DEADLINE: 3 DAYS";
			((Transform)((TMP_Text)_deadlineText).rectTransform).localPosition = new Vector3(0f, -0.18f, 0f);
			((TMP_Text)_deadlineText).rectTransform.sizeDelta = new Vector2(1f, 0.2f);
		}

		private void Update()
		{
			//IL_00a2: 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_00dc: 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 levelsRemaining = QuotaManager.LevelsRemaining;
				string arg = ((levelsRemaining == 1) ? "DAY" : "DAYS");
				if (QuotaManager.QuotaMet)
				{
					((TMP_Text)_deadlineText).text = "QUOTA MET";
					((Graphic)_deadlineText).color = new Color(0.2f, 1f, 0.3f);
				}
				else
				{
					((TMP_Text)_deadlineText).text = $"DEADLINE: {levelsRemaining} {arg}";
					((Graphic)_deadlineText).color = (Color)((levelsRemaining <= 1) ? new Color(1f, 0.3f, 0.2f) : ((Graphic)_quotaText).color);
				}
			}
		}
	}
}
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 string GetProgressComment(float currentRate, float targetRate)
		{
			float num = ((targetRate > 0f) ? (currentRate / targetRate) : 0f);
			if (num >= 1f)
			{
				return "On track. Quota already met.";
			}
			if (num >= 0.75f)
			{
				return "Nearly there. Stay thorough.";
			}
			if (num >= 0.5f)
			{
				return "Adequate pace. Don't slack off.";
			}
			if (num >= 0.25f)
			{
				return "Below expectations. Extract more.";
			}
			return "The Taxman is watching. Pick up the pace.";
		}
	}
	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)
			{
				IsInGameLevel = false;
				int num = QuotaSign.CollectAndResetExtraction();
				CycleTotalExtracted += num;
				TotalLifetimeExtracted += num;
				LevelsCompleted++;
				LevelRecord levelRecord = default(LevelRecord);
				levelRecord.LevelNumber = RunManager.instance.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 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 = "";
				}
			}
		}

		public static string GetStatusText()
		{
			if (!IsActive)
			{
				return "Quota system inactive.";
			}
			return $"Cycle #{CycleNumber} | " + $"Collected: {CycleCollectionRate * 100f:F0}% / {RequiredPercent * 100f:F0}% | " + $"Levels: {LevelsCompleted}/{LevelsPerCycle} | " + $"${CycleTotalExtracted:N0} / ${CycleTotalAvailable:N0} | " + $"Streak: {ConsecutiveQuotasMet}";
		}
	}
}
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));
			}
		}
	}
}