Decompiled source of Configurable Quota v1.0.1

BepInEx/plugins/ConfigurableQuota/ConfigurableQuota.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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 ConfigurableQuota.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using LethalNetworkAPI;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;

[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("ConfigurableQuota")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Allows users to configure every aspect of the quota the way they want.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+88ee3608198092b05327c7d4d2ee35ee56a73a45")]
[assembly: AssemblyProduct("ConfigurableQuota")]
[assembly: AssemblyTitle("ConfigurableQuota")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ConfigurableQuota
{
	public static class ConfigManager
	{
		public static ConfigEntry<int> StartingCredits;

		public static ConfigEntry<int> StartingQuota;

		public static ConfigEntry<int> DaysToDeadline;

		public static ConfigEntry<int> BaseIncrease;

		public static ConfigEntry<float> CurveSharpness;

		public static ConfigEntry<float> RandomizerMultiplier;

		public static ConfigEntry<int> FinalLevel;

		public static ConfigEntry<int> FinalIncrease;

		public static ConfigEntry<int> QuotaCap;

		public static ConfigEntry<bool> EnablePlayerMultiplier;

		public static ConfigEntry<int> PlayerThreshold;

		public static ConfigEntry<int> PlayerCap;

		public static ConfigEntry<float> MultPerPlayer;

		public static ConfigEntry<bool> DisableQuota;

		public static ConfigEntry<float> RolloverAmount;

		public static ConfigEntry<bool> CreditPenaltiesEnabled;

		public static ConfigEntry<bool> CreditPenaltiesOnGordion;

		public static ConfigEntry<float> CreditPenaltyPercentPerPlayer;

		public static ConfigEntry<bool> CreditPenaltiesDynamic;

		public static ConfigEntry<float> CreditPenaltyPercentCap;

		public static ConfigEntry<float> CreditPenaltyPercentThreshold;

		public static ConfigEntry<float> CreditPenaltyRecoveryBonus;

		public static ConfigEntry<bool> QuotaPenaltiesEnabled;

		public static ConfigEntry<bool> QuotaPenaltiesOnGordion;

		public static ConfigEntry<float> QuotaPenaltyPercentPerPlayer;

		public static ConfigEntry<bool> QuotaPenaltiesDynamic;

		public static ConfigEntry<float> QuotaPenaltyPercentCap;

		public static ConfigEntry<float> QuotaPenaltyPercentThreshold;

		public static ConfigEntry<float> QuotaPenaltyRecoveryBonus;

		public static ConfigEntry<bool> ScrapLossEnabled;

		public static ConfigEntry<float> ItemsSafeChance;

		public static ConfigEntry<float> LoseEachScrapChance;

		public static ConfigEntry<int> MaxLostScrapItems;

		public static ConfigEntry<bool> ValueLossEnabled;

		public static ConfigEntry<float> ValueLossPercent;

		public static ConfigEntry<bool> RandomizeDeadline;

		public static ConfigEntry<int> DeadlineMin;

		public static ConfigEntry<int> DeadlineMax;

		public static ConfigEntry<bool> DeadlineMustChange;

		public static ConfigEntry<bool> EnableGrowthDampening;

		public static ConfigEntry<int> DampeningStartAt;

		public static ConfigEntry<float> DampeningSharpness;

		public static ConfigEntry<bool> EquipmentLossEnabled;

		public static ConfigEntry<float> LoseEachEquipmentChance;

		public static ConfigEntry<int> MaxLostEquipmentItems;

		public static ConfigEntry<float> QuotaAnimationSpeed;

		internal static void Initialize(ConfigFile config)
		{
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Expected O, but got Unknown
			//IL_049f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a9: Expected O, but got Unknown
			//IL_050d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0517: Expected O, but got Unknown
			//IL_0560: Unknown result type (might be due to invalid IL or missing references)
			//IL_056a: Expected O, but got Unknown
			//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bd: Expected O, but got Unknown
			StartingCredits = config.Bind<int>("0. Basic", "StartingCredits", 60, "Starting credits for a new lobby.");
			StartingQuota = config.Bind<int>("0. Basic", "StartingQuota", 130, "Starting quota for a new lobby.");
			DaysToDeadline = config.Bind<int>("0. Basic", "DaysToDeadline", 3, "Number of days to meet each quota. Ignored if RandomizeDeadline is enabled.");
			RandomizeDeadline = config.Bind<bool>("0. Basic", "RandomizeDeadline", false, "Randomize the deadline each quota using Deadline Min/Max instead of a fixed Days To Deadline.");
			DeadlineMin = config.Bind<int>("0. Basic", "DeadlineMin", 3, "Minimum days for deadline. REQUIRES 'Randomize Deadline' SET TO TRUE");
			DeadlineMax = config.Bind<int>("0. Basic", "DeadlineMax", 5, "Maximum days for deadline. REQUIRES 'Randomize Deadline' SET TO TRUE");
			DeadlineMustChange = config.Bind<bool>("0. Basic", "DeadlineMustChange", true, "New deadline after fulfilling quota must differ from the previous one. REQUIRES 'Randomize Deadline' SET TO TRUE");
			BaseIncrease = config.Bind<int>("0. Basic", "BaseIncrease", 100, "Base quota increase. Combined with 'Curve Sharpness' to calculate growth.");
			CurveSharpness = config.Bind<float>("0. Basic", "CurveSharpness", 16f, "Quota growth curve. Higher = slower growth. Formula: increase ≈ BaseIncrease x (1 + quotaCount²/Sharpness)");
			RandomizerMultiplier = config.Bind<float>("0. Basic", "RandomizerMultiplier", 1f, "Adds variation to quota increases. 1 = ±50% variance (vanilla), 0 = no randomness, 2 = ±100% variance.");
			FinalLevel = config.Bind<int>("1. Leveling", "FinalLevel", -1, "When quota reaches this value, the Base Increase and Curve Sharpness are ignored. Set -1 to disable.");
			FinalIncrease = config.Bind<int>("1. Leveling", "FinalIncrease", 200, "Fixed increase amount used after reaching Final Level value.");
			QuotaCap = config.Bind<int>("1. Leveling", "QuotaCap", -1, "Maximum quota value, quota will never increase more than this amount. Set -1 for no limit.");
			EnableGrowthDampening = config.Bind<bool>("1. Leveling", "EnableGrowthDampening", false, "Gradually reduces quota growth after a number of fulfilled cycles, growth will slow down the longer you play.");
			DampeningStartAt = config.Bind<int>("1. Leveling", "DampeningStartAt", 6, "Number of quota cycles before dampening begins.");
			DampeningSharpness = config.Bind<float>("1. Leveling", "DampeningSharpness", 11f, "Controls dampening intensity. Lower values reduce growth more aggressively.");
			EnablePlayerMultiplier = config.Bind<bool>("2. PlayerScaling", "EnablePlayerMultiplier", false, "Scale quota increases based on player count.");
			PlayerThreshold = config.Bind<int>("2. PlayerScaling", "PlayerThreshold", 2, "Player count where scaling begins. Example: 2 means scaling starts at 3+ players.");
			PlayerCap = config.Bind<int>("2. PlayerScaling", "PlayerCap", 4, "Maximum players counted for scaling. Example: 4 means player 5+ will not increase the quota multiplier.");
			MultPerPlayer = config.Bind<float>("2. PlayerScaling", "MultPerPlayer", 0.25f, "Quota increase multiplier per extra player. Example: 0.5 = +50% increase per player above threshold.");
			DisableQuota = config.Bind<bool>("3. Optional", "DisableQuota", false, "Completely disables the quota system.");
			RolloverAmount = config.Bind<float>("3. Optional", "RolloverAmount", 0f, new ConfigDescription("Percentage of extra scrap value that goes above the set limit and is added to the next quota. 0 = none (vanilla), 0.5 = 50%, 1.0 = 100%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			CreditPenaltiesEnabled = config.Bind<bool>("4. Penalties.Credits", "Enabled", false, "Reduce credits when crew members die.");
			CreditPenaltiesOnGordion = config.Bind<bool>("4. Penalties.Credits", "OnGordion", false, "Apply credit penalties even when visiting The Company.");
			CreditPenaltyPercentPerPlayer = config.Bind<float>("4. Penalties.Credits", "PercentPerPlayer", 0.15f, "Credits lost per dead player. Example: 0.15 = lose 15% of credits per death. Ignored if 'Dynamic' is true.");
			CreditPenaltiesDynamic = config.Bind<bool>("4. Penalties.Credits", "Dynamic", false, "Use team death ratio instead of per-player. Example: 2 dead out of 4 total = 50% penalty, not 30% (2x15%).");
			CreditPenaltyPercentCap = config.Bind<float>("4. Penalties.Credits", "PercentCap", 0.8f, "Maximum percentage of credits that can be lost.");
			CreditPenaltyPercentThreshold = config.Bind<float>("4. Penalties.Credits", "PercentThreshold", 0f, "Ignore penalties below this percentage. Example: 0.1 = penalties under 10% are not applied.");
			CreditPenaltyRecoveryBonus = config.Bind<float>("4. Penalties.Credits", "RecoveryBonus", 0f, "Reduce penalty if you recover bodies. Example: 0.5 = 50% penalty forgiveness if bodies brought back.");
			QuotaPenaltiesEnabled = config.Bind<bool>("5. Penalties.Quota", "Enabled", false, "Increase the current quota when crew members die.");
			QuotaPenaltiesOnGordion = config.Bind<bool>("5. Penalties.Quota", "OnGordion", false, "Apply quota penalties even when visiting The Company.");
			QuotaPenaltyPercentPerPlayer = config.Bind<float>("5. Penalties.Quota", "PercentPerPlayer", 0.1f, "Quota increase per dead player. Example: 0.1 = +10% to current quota per death. Ignored if 'Dynamic' is true.");
			QuotaPenaltiesDynamic = config.Bind<bool>("5. Penalties.Quota", "Dynamic", false, "Use team death ratio instead of per-player. Example: 2 dead out of 4 total = 50% quota increase.");
			QuotaPenaltyPercentCap = config.Bind<float>("5. Penalties.Quota", "PercentCap", 0.5f, "Maximum percentage the quota can increase. Example: 0.5 = quota can increase by at most 50%.");
			QuotaPenaltyPercentThreshold = config.Bind<float>("5. Penalties.Quota", "PercentThreshold", 0f, "Ignore penalties below this percentage. Example: 0.15 = increases under 15% are not applied.");
			QuotaPenaltyRecoveryBonus = config.Bind<float>("5. Penalties.Quota", "RecoveryBonus", 0f, "Reduce penalty if you recover bodies. Example: 0.5 = 50% penalty forgiveness if bodies brought back.");
			ScrapLossEnabled = config.Bind<bool>("6. Loss.Scrap", "Enabled", false, "Randomly lose collected scrap when all crew dies.");
			ItemsSafeChance = config.Bind<float>("6. Loss.Scrap", "ItemsSafeChance", 0.5f, new ConfigDescription("Chance for each scrap to be protected from loss. Example: 0.7 = 70% chance each scrap is safe.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			LoseEachScrapChance = config.Bind<float>("6. Loss.Scrap", "LoseEachScrapChance", 0.1f, new ConfigDescription("If scrap is not safe, this is the chance it gets lost. Example: 0.2 = 20% chance to lose unprotected scrap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MaxLostScrapItems = config.Bind<int>("6. Loss.Scrap", "MaxLostScrapItems", 2, "Maximum scrap that can be lost per round.");
			ValueLossEnabled = config.Bind<bool>("7. Loss.Value", "Enabled", false, "Reduce the scrap value of all ship items when the entire crew is wiped.");
			ValueLossPercent = config.Bind<float>("7. Loss.Value", "Percent", 0.2f, new ConfigDescription("Percentage to reduce scrap value. Example: 0.25 = all scrap items lose 25% of their value, stacks on repeated wipes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			EquipmentLossEnabled = config.Bind<bool>("8. Loss.Equipment", "Enabled", false, "Randomly lose purchased equipment when all crew dies.");
			LoseEachEquipmentChance = config.Bind<float>("8. Loss.Equipment", "LoseEachEquipmentChance", 0.05f, new ConfigDescription("Chance for each equipment item to be lost. Example: 0.1 = 10% chance per item.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MaxLostEquipmentItems = config.Bind<int>("8. Loss.Equipment", "MaxLostEquipmentItems", 1, "Maximum equipment items that can be lost per round. Example: 1 = lose at most 1 item.");
			QuotaAnimationSpeed = config.Bind<float>("9. UI", "QuotaAnimationSpeed", 1f, new ConfigDescription("Speed multiplier for the new quota animation. Higher = faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
		}
	}
	internal static class Metadata
	{
		public const string GUID = "com.seeya.configurablequota";

		public const string PLUGIN_NAME = "Configurable Quota";

		public const string VERSION = "1.0.0";
	}
	[BepInPlugin("com.seeya.configurablequota", "Configurable Quota", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("com.seeya.configurablequota");

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Initializing Configurable Quota");
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
			NetworkSync.Initialize();
			_harmony.PatchAll();
			Log.LogInfo((object)"Configurable Quota is loaded!");
		}
	}
}
namespace ConfigurableQuota.Patches
{
	[HarmonyPatch(typeof(HUDManager))]
	internal static class HudQuotaAnimationPatch
	{
		[CompilerGenerated]
		private sealed class <CustomRackUp>d__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float speed;

			public HUDManager hud;

			private int <quotaTextAmount>5__2;

			private int <target>5__3;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CustomRackUp>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Expected O, but got Unknown
				//IL_0154: Unknown result type (might be due to invalid IL or missing references)
				//IL_015e: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(3.5f / speed);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<quotaTextAmount>5__2 = 0;
					<target>5__3 = TimeOfDay.Instance.profitQuota;
					goto IL_00dd;
				case 2:
					<>1__state = -1;
					goto IL_00dd;
				case 3:
					{
						<>1__state = -1;
						hud.displayingNewQuota = false;
						hud.reachedProfitQuotaAnimator.SetBool("display", false);
						return false;
					}
					IL_00dd:
					if (<quotaTextAmount>5__2 < <target>5__3)
					{
						float num = Time.deltaTime * 250f * speed;
						<quotaTextAmount>5__2 = (int)Mathf.Clamp((float)<quotaTextAmount>5__2 + num, (float)(<quotaTextAmount>5__2 + 3), (float)(<target>5__3 + 10));
						((TMP_Text)hud.newProfitQuotaText).text = "$" + <quotaTextAmount>5__2;
						<>2__current = null;
						<>1__state = 2;
						return true;
					}
					((TMP_Text)hud.newProfitQuotaText).text = "$" + <target>5__3;
					TimeOfDay.Instance.UpdateProfitQuotaCurrentTime();
					hud.UIAudio.PlayOneShot(hud.newProfitQuotaSFX);
					<>2__current = (object)new WaitForSeconds(1.25f / Mathf.Clamp(speed, 0.5f, 2f));
					<>1__state = 3;
					return true;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[HarmonyPatch("rackUpNewQuotaText")]
		[HarmonyPrefix]
		private static bool RackUpNewQuotaText_Prefix(HUDManager __instance, ref IEnumerator __result)
		{
			float speed = Mathf.Clamp(ConfigManager.QuotaAnimationSpeed.Value, 0.1f, 2f);
			__result = CustomRackUp(__instance, speed);
			return false;
		}

		[IteratorStateMachine(typeof(<CustomRackUp>d__1))]
		private static IEnumerator CustomRackUp(HUDManager hud, float speed)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CustomRackUp>d__1(0)
			{
				hud = hud,
				speed = speed
			};
		}

		[HarmonyPatch("ApplyPenalty")]
		[HarmonyPrefix]
		private static bool ApplyPenalty_Prefix()
		{
			return false;
		}

		[HarmonyPatch("ApplyPenalty")]
		[HarmonyPostfix]
		private static void ApplyPenalty_Postfix(HUDManager __instance, int playersDead, int bodiesInsured)
		{
			try
			{
				int num;
				int num2;
				int num3;
				if (PenaltiesOnLandingPatch.HasPenaltyCache)
				{
					num = PenaltiesOnLandingPatch.CachedDead;
					num2 = PenaltiesOnLandingPatch.CachedTotal;
					num3 = PenaltiesOnLandingPatch.CachedRecovered;
				}
				else
				{
					(num, num2, num3) = PenaltyHelpers.CountDeathsAndRecovered();
					if (num == 0 && playersDead > 0)
					{
						num = playersDead;
						num3 = bodiesInsured;
						num2 = Mathf.Max(num + 1, num2);
					}
				}
				bool flag = PenaltyHelpers.IsOnGordion();
				float num4 = 0f;
				int num5 = 0;
				if (ConfigManager.CreditPenaltiesEnabled.Value && num > 0 && (!flag || ConfigManager.CreditPenaltiesOnGordion.Value))
				{
					int num6 = Object.FindObjectOfType<Terminal>()?.groupCredits ?? 0;
					num4 = PenaltyHelpers.ComputePenaltyPercent(ConfigManager.CreditPenaltiesDynamic.Value, ConfigManager.CreditPenaltyPercentPerPlayer.Value, ConfigManager.CreditPenaltyPercentCap.Value, ConfigManager.CreditPenaltyPercentThreshold.Value, ConfigManager.CreditPenaltyRecoveryBonus.Value, num, num2, num3);
					num5 = Mathf.RoundToInt((float)num6 * num4);
				}
				float num7 = 0f;
				int num8 = 0;
				if (ConfigManager.QuotaPenaltiesEnabled.Value && num > 0 && (!flag || ConfigManager.QuotaPenaltiesOnGordion.Value))
				{
					num7 = PenaltyHelpers.ComputePenaltyPercent(ConfigManager.QuotaPenaltiesDynamic.Value, ConfigManager.QuotaPenaltyPercentPerPlayer.Value, ConfigManager.QuotaPenaltyPercentCap.Value, ConfigManager.QuotaPenaltyPercentThreshold.Value, ConfigManager.QuotaPenaltyRecoveryBonus.Value, num, num2, num3);
					TimeOfDay instance = TimeOfDay.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						num8 = Mathf.RoundToInt((float)Mathf.Max(1, instance.profitQuota) * num7);
					}
				}
				string text = ((num4 > 0f) ? $"{num} casualties: -{Mathf.RoundToInt(num4 * 100f)}%" : $"{num} casualties");
				string text2 = $"({num3} of {num} bodies recovered.)";
				string text3 = text + "\n" + text2;
				if (num7 > 0f)
				{
					text3 += $"\n\nQuota: {Mathf.RoundToInt(num7 * 100f)}% (${num8})";
				}
				((TMP_Text)__instance.statsUIElements.penaltyAddition).text = text3;
				((TMP_Text)__instance.statsUIElements.penaltyTotal).text = ((num5 > 0) ? $"DUE: ${num5}" : "");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ApplyPenalty display patch failed: " + ex.Message));
			}
		}
	}
	internal static class NetworkSync
	{
		private static LNetworkMessage<int>? _syncCreditsMessage;

		private static LNetworkMessage<int>? _syncQuotaMessage;

		private static LNetworkMessage<SyncValueLossData>? _syncValueLossMessage;

		private static LNetworkMessage<int>? _syncDeadlineMessage;

		public static void Initialize()
		{
			try
			{
				_syncCreditsMessage = LNetworkMessage<int>.Connect("ConfigurableQuota_SyncCredits", (Action<int, ulong>)null, (Action<int>)OnCreditsReceived, (Action<int, ulong>)null);
				_syncQuotaMessage = LNetworkMessage<int>.Connect("ConfigurableQuota_SyncQuota", (Action<int, ulong>)null, (Action<int>)OnQuotaReceived, (Action<int, ulong>)null);
				_syncValueLossMessage = LNetworkMessage<SyncValueLossData>.Connect("ConfigurableQuota_SyncValueLoss", (Action<SyncValueLossData, ulong>)null, (Action<SyncValueLossData>)OnValueLossReceived, (Action<SyncValueLossData, ulong>)null);
				_syncDeadlineMessage = LNetworkMessage<int>.Connect("ConfigurableQuota_SyncDeadline", (Action<int, ulong>)null, (Action<int>)OnDeadlineReceived, (Action<int, ulong>)null);
				Plugin.Log.LogInfo((object)"Network initialized");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to initialize: " + ex.Message));
			}
		}

		public static void SyncCreditsToClients(int credits)
		{
			try
			{
				if (_syncCreditsMessage == null)
				{
					Plugin.Log.LogWarning((object)"Credits message not initialized");
					return;
				}
				_syncCreditsMessage.SendClients(credits);
				Plugin.Log.LogDebug((object)$"Sent credits sync to clients: {credits}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to sync credits: " + ex.Message));
			}
		}

		private static void OnCreditsReceived(int credits)
		{
			try
			{
				Terminal val = Object.FindObjectOfType<Terminal>();
				if ((Object)(object)val != (Object)null)
				{
					val.groupCredits = credits;
				}
				Plugin.Log.LogDebug((object)$"Client received credits sync: {credits}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply credits on client: " + ex.Message));
			}
		}

		public static void SyncQuotaToClients(int quota)
		{
			try
			{
				if (_syncQuotaMessage == null)
				{
					Plugin.Log.LogWarning((object)"Quota message not initialized");
					return;
				}
				_syncQuotaMessage.SendClients(quota);
				Plugin.Log.LogDebug((object)$"Sent quota sync to clients: {quota}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to sync quota: " + ex.Message));
			}
		}

		private static void OnQuotaReceived(int quota)
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.profitQuota = quota;
					Plugin.Log.LogDebug((object)$"Client received quota sync: {quota}");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply quota on client: " + ex.Message));
			}
		}

		public static void SyncValueLossToClients(SyncValueLossData[] items)
		{
			try
			{
				if (_syncValueLossMessage == null)
				{
					Plugin.Log.LogWarning((object)"Value loss message not initialized");
					return;
				}
				foreach (SyncValueLossData syncValueLossData in items)
				{
					_syncValueLossMessage.SendClients(syncValueLossData);
				}
				Plugin.Log.LogDebug((object)$"Sent {items.Length} value loss updates to clients");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to sync value loss: " + ex.Message));
			}
		}

		private static void OnValueLossReceived(SyncValueLossData data)
		{
			try
			{
				GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
				GrabbableObject[] array2 = array;
				foreach (GrabbableObject val in array2)
				{
					try
					{
						NetworkObject component = ((Component)val).GetComponent<NetworkObject>();
						if (!((Object)(object)val != (Object)null) || !((Object)(object)component != (Object)null) || component.NetworkObjectId != data.NetworkObjectId)
						{
							continue;
						}
						Item itemProperties = val.itemProperties;
						if (itemProperties != null && itemProperties.isScrap)
						{
							val.scrapValue = data.NewValue;
							try
							{
								val.SetScrapValue(data.NewValue);
							}
							catch
							{
							}
							Plugin.Log.LogDebug((object)$"Client updated scrap value for {val.itemProperties.itemName}: {data.NewValue}");
							break;
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply value loss on client: " + ex.Message));
			}
		}

		public static void SyncDeadlineToClients(int days)
		{
			try
			{
				if (_syncDeadlineMessage == null)
				{
					Plugin.Log.LogWarning((object)"Deadline message not initialized");
					return;
				}
				_syncDeadlineMessage.SendClients(days);
				Plugin.Log.LogDebug((object)$"Sent deadline sync to clients: {days} days");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to sync deadline: " + ex.Message));
			}
		}

		private static void OnDeadlineReceived(int days)
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					if (instance.quotaVariables != null)
					{
						instance.quotaVariables.deadlineDaysAmount = days;
					}
					instance.daysUntilDeadline = days;
					instance.timeUntilDeadline = (float)days * instance.totalTime;
					Plugin.Log.LogDebug((object)$"Client updated deadline: {days} days");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply deadline on client: " + ex.Message));
			}
		}
	}
	[Serializable]
	public struct SyncValueLossData
	{
		public ulong NetworkObjectId;

		public int NewValue;

		public SyncValueLossData(ulong networkObjectId, int newValue)
		{
			NetworkObjectId = networkObjectId;
			NewValue = newValue;
		}
	}
	internal static class PenaltyHelpers
	{
		public static bool IsServerSafe
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return false;
			}
		}

		public static (int dead, int total, int recovered) CountDeathsAndRecovered()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return (0, 0, 0);
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				bool isPlayerControlled = val.isPlayerControlled;
				bool isPlayerDead = val.isPlayerDead;
				if (isPlayerControlled || isPlayerDead)
				{
					num2++;
				}
				if (!isPlayerDead)
				{
					continue;
				}
				num++;
				GrabbableObject obj = val.deadBody?.grabBodyObject;
				RagdollGrabbableObject val2 = (RagdollGrabbableObject)(object)((obj is RagdollGrabbableObject) ? obj : null);
				if ((Object)(object)val2 == (Object)null)
				{
					RagdollGrabbableObject[] array = Object.FindObjectsOfType<RagdollGrabbableObject>();
					foreach (RagdollGrabbableObject val3 in array)
					{
						if ((Object)(object)((val3 == null) ? null : ((Component)val3).GetComponent<DeadBodyInfo>()?.playerScript) == (Object)(object)val)
						{
							val2 = val3;
							break;
						}
					}
				}
				bool flag = false;
				if ((Object)(object)val2 != (Object)null)
				{
					bool isInShipRoom = ((GrabbableObject)val2).isInShipRoom;
					bool flag2 = IsPositionInsideShip(((Component)val2).transform.position);
					flag = isInShipRoom || flag2;
				}
				if (flag)
				{
					num3++;
				}
			}
			return (num, Math.Max(num2, 1), Mathf.Clamp(num3, 0, num));
		}

		public static bool IsPositionInsideShip(Vector3 pos)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Collider val = StartOfRound.Instance?.shipBounds;
				int result;
				if ((Object)(object)val != (Object)null)
				{
					Bounds bounds = val.bounds;
					result = (((Bounds)(ref bounds)).Contains(pos) ? 1 : 0);
				}
				else
				{
					result = 0;
				}
				return (byte)result != 0;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsOnGordion()
		{
			try
			{
				SelectableLevel val = StartOfRound.Instance?.currentLevel;
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.sceneName == "CompanyBuilding";
			}
			catch
			{
				return false;
			}
		}

		public static float ComputePenaltyPercent(bool dynamicMode, float percentPerPlayer, float cap, float threshold, float recoveryBonus, int dead, int total, int recovered)
		{
			if (dead <= 0 || total <= 0)
			{
				return 0f;
			}
			float num = (dynamicMode ? ((float)dead / (float)total) : ((float)dead * Mathf.Max(0f, percentPerPlayer)));
			if (recovered > 0 && dead > 0)
			{
				float num2 = Mathf.Clamp01((float)recovered / (float)dead);
				num *= Mathf.Clamp01(1f - Mathf.Clamp01(recoveryBonus) * num2);
			}
			if (cap >= 0f)
			{
				num = Mathf.Min(num, Mathf.Clamp01(cap));
			}
			if (!(num < threshold))
			{
				return Mathf.Clamp01(num);
			}
			return 0f;
		}
	}
	[HarmonyPatch(typeof(RoundManager))]
	internal static class PenaltiesOnLandingPatch
	{
		[CompilerGenerated]
		private sealed class <FinalizeCreditPenaltyAfterDelay>d__12 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int desiredFinal;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FinalizeCreditPenaltyAfterDelay>d__12(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(1.5f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						StartOfRound instance = StartOfRound.Instance;
						if ((Object)(object)instance == (Object)null)
						{
							return false;
						}
						int currentCredits = GetCurrentCredits();
						SetCredits(desiredFinal);
						Plugin.Log.LogInfo((object)$"[Penalty] Credits: {currentCredits} → {desiredFinal} (-{currentCredits - desiredFinal})");
					}
					finally
					{
						_creditScheduled = false;
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		internal static bool _appliedThisRound;

		private static bool _creditScheduled;

		internal static bool _lossesAppliedThisRound;

		internal static int CachedDead;

		internal static int CachedTotal;

		internal static int CachedRecovered;

		internal static bool HasPenaltyCache;

		internal static void CachePenaltyCounts(int dead, int total, int recovered)
		{
			CachedDead = dead;
			CachedTotal = total;
			CachedRecovered = recovered;
			HasPenaltyCache = true;
		}

		[HarmonyPatch("DespawnPropsAtEndOfRound")]
		[HarmonyPrefix]
		private static bool DespawnPrefix(bool despawnAllItems)
		{
			try
			{
				if (!PenaltyHelpers.IsServerSafe)
				{
					return true;
				}
				bool flag = PenaltyHelpers.IsOnGordion();
				var (num, num2, recovered) = PenaltyHelpers.CountDeathsAndRecovered();
				if (!despawnAllItems && !flag && num >= num2 && !_lossesAppliedThisRound)
				{
					DespawnFacilityItems();
					ApplyLossesWhenAllDead();
					_lossesAppliedThisRound = true;
					CachePenaltyCounts(num, num2, recovered);
					if (ConfigManager.CreditPenaltiesEnabled.Value)
					{
						ScheduleCreditPenalty(num, num2, recovered);
					}
					if (ConfigManager.QuotaPenaltiesEnabled.Value)
					{
						ApplyQuotaPenalty(num, num2, recovered);
					}
					_appliedThisRound = true;
					Plugin.Log.LogDebug((object)"Bypassed vanilla despawn to preserve kept items");
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Error in despawn prefix: " + ex.Message));
				return true;
			}
		}

		[HarmonyPatch("DespawnPropsAtEndOfRound")]
		[HarmonyPostfix]
		private static void DespawnPostfix(bool despawnAllItems)
		{
			try
			{
				if (despawnAllItems || !PenaltyHelpers.IsServerSafe || _appliedThisRound)
				{
					return;
				}
				var (num, total, recovered) = PenaltyHelpers.CountDeathsAndRecovered();
				if (num > 0)
				{
					CachePenaltyCounts(num, total, recovered);
					bool flag = PenaltyHelpers.IsOnGordion();
					if (ConfigManager.CreditPenaltiesEnabled.Value && (!flag || ConfigManager.CreditPenaltiesOnGordion.Value))
					{
						ScheduleCreditPenalty(num, total, recovered);
					}
					if (ConfigManager.QuotaPenaltiesEnabled.Value && (!flag || ConfigManager.QuotaPenaltiesOnGordion.Value))
					{
						ApplyQuotaPenalty(num, total, recovered);
					}
					_appliedThisRound = true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Error in despawn postfix: " + ex.Message));
			}
		}

		private static void ScheduleCreditPenalty(int dead, int total, int recovered)
		{
			try
			{
				if (_creditScheduled)
				{
					return;
				}
				StartOfRound instance = StartOfRound.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					int currentCredits = GetCurrentCredits();
					float num = PenaltyHelpers.ComputePenaltyPercent(ConfigManager.CreditPenaltiesDynamic.Value, ConfigManager.CreditPenaltyPercentPerPlayer.Value, ConfigManager.CreditPenaltyPercentCap.Value, ConfigManager.CreditPenaltyPercentThreshold.Value, ConfigManager.CreditPenaltyRecoveryBonus.Value, dead, total, recovered);
					if (!(num <= 0f))
					{
						int desiredFinal = Mathf.Max(0, currentCredits - Mathf.RoundToInt((float)currentCredits * num));
						_creditScheduled = true;
						((MonoBehaviour)instance).StartCoroutine(FinalizeCreditPenaltyAfterDelay(desiredFinal));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to schedule credit penalty: " + ex.Message));
			}
		}

		private static int GetCurrentCredits()
		{
			try
			{
				Terminal val = Object.FindObjectOfType<Terminal>();
				if ((Object)(object)val != (Object)null)
				{
					return val.groupCredits;
				}
			}
			catch
			{
			}
			return 0;
		}

		[IteratorStateMachine(typeof(<FinalizeCreditPenaltyAfterDelay>d__12))]
		private static IEnumerator FinalizeCreditPenaltyAfterDelay(int desiredFinal)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FinalizeCreditPenaltyAfterDelay>d__12(0)
			{
				desiredFinal = desiredFinal
			};
		}

		private static void SetCredits(int value)
		{
			try
			{
				Terminal val = Object.FindObjectOfType<Terminal>();
				if ((Object)(object)val != (Object)null)
				{
					val.SyncGroupCreditsServerRpc(value, val.numberOfItemsInDropship);
				}
			}
			catch
			{
			}
		}

		private static void ApplyQuotaPenalty(int dead, int total, int recovered)
		{
			float num = PenaltyHelpers.ComputePenaltyPercent(ConfigManager.QuotaPenaltiesDynamic.Value, ConfigManager.QuotaPenaltyPercentPerPlayer.Value, ConfigManager.QuotaPenaltyPercentCap.Value, ConfigManager.QuotaPenaltyPercentThreshold.Value, ConfigManager.QuotaPenaltyRecoveryBonus.Value, dead, total, recovered);
			if (!(num <= 0f))
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					int num2 = Mathf.RoundToInt((float)Math.Max(1, instance.profitQuota) * num);
					int num3 = (instance.profitQuota = Mathf.Max(1, instance.profitQuota + num2));
					NetworkSync.SyncQuotaToClients(num3);
					int num4 = instance.profitQuota - num2;
					Plugin.Log.LogInfo((object)$"[Penalty] Quota: {num4} → {num3} (+{num2}, {num:P0} penalty, {dead}/{total} dead)");
				}
			}
		}

		private static void DespawnFacilityItems()
		{
			try
			{
				GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
				if (array == null || array.Length == 0)
				{
					return;
				}
				Transform shipRoot = null;
				try
				{
					StartOfRound instance = StartOfRound.Instance;
					object obj;
					if (instance == null)
					{
						obj = null;
					}
					else
					{
						Collider shipBounds = instance.shipBounds;
						obj = ((shipBounds != null) ? ((Component)shipBounds).transform : null);
					}
					shipRoot = (Transform)obj;
				}
				catch
				{
				}
				int num = 0;
				GrabbableObject[] array2 = array;
				foreach (GrabbableObject g in array2)
				{
					try
					{
						if (!IsShipItem(g, shipRoot))
						{
							DespawnObject(g);
							num++;
						}
					}
					catch
					{
					}
				}
				Plugin.Log.LogDebug((object)$"Despawned {num} facility items");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Error despawning facility items: " + ex.Message));
			}
		}

		private static void ApplyLossesWhenAllDead()
		{
			try
			{
				GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
				if (array == null || array.Length == 0)
				{
					return;
				}
				Transform shipRoot = null;
				try
				{
					StartOfRound instance = StartOfRound.Instance;
					object obj;
					if (instance == null)
					{
						obj = null;
					}
					else
					{
						Collider shipBounds = instance.shipBounds;
						obj = ((shipBounds != null) ? ((Component)shipBounds).transform : null);
					}
					shipRoot = (Transform)obj;
				}
				catch
				{
				}
				GrabbableObject[] source = array.Where((GrabbableObject g) => IsShipItem(g, shipRoot)).ToArray();
				GrabbableObject[] array2 = source.Where((GrabbableObject g) => g.itemProperties.isScrap).ToArray();
				GrabbableObject[] array3 = source.Where((GrabbableObject g) => !g.itemProperties.isScrap && !IsBodyOrBlacklisted(g)).ToArray();
				Plugin.Log.LogDebug((object)$"Ship items: {array2.Length} scrap, {array3.Length} equipment");
				if (ConfigManager.ValueLossEnabled.Value && array2.Length != 0)
				{
					ApplyValueLoss(array2);
				}
				if (ConfigManager.ScrapLossEnabled.Value && array2.Length != 0)
				{
					SelectAndRemoveScrap(array2);
				}
				if (ConfigManager.EquipmentLossEnabled.Value && array3.Length != 0)
				{
					SelectAndRemoveEquipment(array3);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Error applying losses: " + ex.Message));
			}
		}

		private static bool IsShipItem(GrabbableObject g, Transform? shipRoot)
		{
			if ((Object)(object)g == (Object)null || (Object)(object)g.itemProperties == (Object)null || !g.isInShipRoom)
			{
				return false;
			}
			try
			{
				NetworkObject component = ((Component)g).GetComponent<NetworkObject>();
				if ((Object)(object)component == (Object)null || !component.IsSpawned)
				{
					return false;
				}
			}
			catch
			{
				return false;
			}
			if ((Object)(object)shipRoot == (Object)null)
			{
				return true;
			}
			Transform val = ((Component)g).transform;
			for (int i = 0; i < 8; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				if ((Object)(object)val == (Object)(object)shipRoot)
				{
					return true;
				}
				val = val.parent;
			}
			return true;
		}

		private static bool IsBodyOrBlacklisted(GrabbableObject g)
		{
			if ((Object)(object)g == (Object)null)
			{
				return true;
			}
			if (g is RagdollGrabbableObject)
			{
				return true;
			}
			if (g is ClipboardItem)
			{
				return true;
			}
			try
			{
				string text = g.itemProperties?.itemName ?? ((Object)g).name;
				return text.IndexOf("sticky note", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch
			{
				return false;
			}
		}

		private static void SelectAndRemoveScrap(GrabbableObject[] scrapItems)
		{
			int num = Mathf.Max(0, ConfigManager.MaxLostScrapItems.Value);
			float num2 = Mathf.Clamp01(ConfigManager.ItemsSafeChance.Value);
			float num3 = Mathf.Clamp01(ConfigManager.LoseEachScrapChance.Value);
			int num4 = 0;
			int num5 = 0;
			List<string> list = new List<string>();
			foreach (GrabbableObject val in scrapItems)
			{
				try
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Item itemProperties = val.itemProperties;
					if (itemProperties != null && itemProperties.isScrap)
					{
						num4++;
						if ((num <= 0 || num5 < num) && !(Random.value < num2) && Random.value < num3)
						{
							DespawnObject(val);
							num5++;
							list.Add(val.itemProperties.itemName);
						}
					}
				}
				catch
				{
				}
			}
			Plugin.Log.LogInfo((object)string.Format("Scrap: {0}/{1} removed [{2}]", num5, num4, string.Join(", ", list)));
		}

		private static void SelectAndRemoveEquipment(GrabbableObject[] equipItems)
		{
			int num = Mathf.Max(0, ConfigManager.MaxLostEquipmentItems.Value);
			float num2 = Mathf.Clamp01(ConfigManager.LoseEachEquipmentChance.Value);
			int num3 = 0;
			int num4 = 0;
			List<string> list = new List<string>();
			foreach (GrabbableObject val in equipItems)
			{
				try
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Item itemProperties = val.itemProperties;
					if (itemProperties != null && !itemProperties.isScrap)
					{
						num3++;
						if ((num <= 0 || num4 < num) && Random.value < num2)
						{
							DespawnObject(val);
							num4++;
							list.Add(val.itemProperties.itemName);
						}
					}
				}
				catch
				{
				}
			}
			Plugin.Log.LogInfo((object)string.Format("[Losses] Equipment: {0}/{1} removed [{2}]", num4, num3, string.Join(", ", list)));
		}

		private static void ApplyValueLoss(GrabbableObject[] scrapItems)
		{
			float num = Mathf.Clamp01(ConfigManager.ValueLossPercent.Value);
			if (num <= 0f)
			{
				return;
			}
			float num2 = 1f - num;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			List<SyncValueLossData> list = new List<SyncValueLossData>();
			foreach (GrabbableObject val in scrapItems)
			{
				try
				{
					if (val != null && (val.itemProperties?.isScrap).GetValueOrDefault() && val.scrapValue > 0)
					{
						int scrapValue = val.scrapValue;
						int num6 = (val.scrapValue = Mathf.Max(0, Mathf.RoundToInt((float)val.scrapValue * num2)));
						try
						{
							val.SetScrapValue(num6);
						}
						catch
						{
						}
						NetworkObject component = ((Component)val).GetComponent<NetworkObject>();
						if ((Object)(object)component != (Object)null)
						{
							list.Add(new SyncValueLossData(component.NetworkObjectId, num6));
						}
						num4 += scrapValue;
						num5 += num6;
						num3++;
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Error reducing value for item: " + ex.Message));
				}
			}
			if (list.Count > 0)
			{
				NetworkSync.SyncValueLossToClients(list.ToArray());
			}
			Plugin.Log.LogInfo((object)$"Value: {num3} items reduced by {num:P0} (${num4} → ${num5})");
		}

		private static void DespawnObject(GrabbableObject g)
		{
			try
			{
				NetworkObject component = ((Component)g).GetComponent<NetworkObject>();
				if ((Object)(object)component != (Object)null && component.IsSpawned)
				{
					component.Despawn(true);
				}
				else
				{
					Object.Destroy((Object)(object)((Component)g).gameObject);
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class ResetPenaltyFlags
	{
		[HarmonyPatch("StartGame")]
		[HarmonyPostfix]
		private static void ResetFlagsOnNewGame()
		{
			PenaltiesOnLandingPatch._appliedThisRound = false;
			PenaltiesOnLandingPatch._lossesAppliedThisRound = false;
			PenaltiesOnLandingPatch.HasPenaltyCache = false;
		}
	}
	[HarmonyPatch(typeof(Terminal))]
	internal static class StartOfRoundPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void ApplyStartingCredits(Terminal __instance)
		{
			try
			{
				int value = ConfigManager.StartingCredits.Value;
				if (value >= 0)
				{
					__instance.groupCredits = value;
					Plugin.Log.LogInfo((object)$"Applied starting credits: {value}");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to apply StartingCredits: " + ex.Message));
			}
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if (!((Object)(object)instance == (Object)null) && instance.timesFulfilledQuota == 0 && ((NetworkBehaviour)instance).IsServer && ConfigManager.RandomizeDeadline.Value)
				{
					NetworkSync.SyncDeadlineToClients(instance.daysUntilDeadline);
					Plugin.Log.LogInfo((object)$"[Lobby] Initial deadline synced: {instance.daysUntilDeadline}d");
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("Failed to sync initial deadline: " + ex2.Message));
			}
		}
	}
	[HarmonyPatch(typeof(TimeOfDay))]
	internal static class TimeOfDayQuotaPatch
	{
		[HarmonyPatch("SetNewProfitQuota")]
		[HarmonyPrefix]
		[HarmonyPriority(200)]
		[HarmonyAfter(new string[] { "Jade.ChocoQuota", "luciusoflegend.lethalcompany.quotaoverhaul" })]
		private static bool SetNewProfitQuota_Prefix(TimeOfDay __instance, ref int ___timesFulfilledQuota, ref int ___profitQuota, ref float ___timeUntilDeadline, ref int ___quotaFulfilled, ref int ___daysUntilDeadline, ref float ___totalTime)
		{
			try
			{
				if (!((NetworkBehaviour)__instance).IsServer)
				{
					return false;
				}
				if (ConfigManager.DisableQuota.Value)
				{
					___profitQuota = Mathf.Max(0, ConfigManager.StartingQuota.Value);
					SetDeadlineTimer(___totalTime, ref ___daysUntilDeadline, ref ___timeUntilDeadline);
					return false;
				}
				int num = ___profitQuota;
				___timesFulfilledQuota++;
				int num2 = CalculateNewQuota(num, ___timesFulfilledQuota);
				int num3 = ___daysUntilDeadline;
				int num4 = ___quotaFulfilled - num;
				int num5 = num4 / 5 + 15 * num3;
				___profitQuota = num2;
				___quotaFulfilled = CalculateRollover(num4);
				int num6 = SetDeadlineTimer(___totalTime, ref ___daysUntilDeadline, ref ___timeUntilDeadline, num3);
				__instance.quotaVariables.deadlineDaysAmount = num6;
				__instance.SyncNewProfitQuotaClientRpc(___profitQuota, num5, ___timesFulfilledQuota);
				___daysUntilDeadline = num6;
				___timeUntilDeadline = ___totalTime * (float)num6;
				NetworkSync.SyncDeadlineToClients(num6);
				Plugin.Log.LogInfo((object)($"[Quota #{___timesFulfilledQuota}] {num} → {num2}" + $" | Deadline: {num6}d" + $" | Rollover: {___quotaFulfilled}" + $" | Overtime: {num5}cr"));
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Quota SetNewProfitQuota patch error: {arg}");
				return true;
			}
		}

		private static int CalculateNewQuota(int previousQuota, int timesFulfilled)
		{
			int value = ConfigManager.FinalLevel.Value;
			int num;
			if (value != -1 && previousQuota >= value)
			{
				num = previousQuota + Math.Max(0, ConfigManager.FinalIncrease.Value);
			}
			else
			{
				float num2 = Mathf.Max(0.1f, ConfigManager.CurveSharpness.Value);
				float num3 = timesFulfilled;
				float num4 = Mathf.Clamp(1f + num3 * (num3 / num2), 0f, 10000f);
				float num5 = 1f;
				float value2 = ConfigManager.RandomizerMultiplier.Value;
				if (value2 > 0f)
				{
					num5 = 1f + Random.Range(-0.5f, 0.5f) * value2;
				}
				float num6 = (float)ConfigManager.BaseIncrease.Value * num4 * num5;
				if (ConfigManager.EnablePlayerMultiplier.Value)
				{
					num6 *= CalculatePlayerMultiplier();
				}
				if (ConfigManager.EnableGrowthDampening.Value)
				{
					int value3 = ConfigManager.DampeningStartAt.Value;
					if (timesFulfilled > value3)
					{
						float num7 = timesFulfilled - value3;
						float num8 = Mathf.Max(0.1f, ConfigManager.DampeningSharpness.Value);
						num6 /= 1f + Mathf.Pow(num7 / num8, 2f);
					}
				}
				num = Mathf.RoundToInt(Mathf.Clamp((float)previousQuota + num6, 0f, 1E+09f));
			}
			int value4 = ConfigManager.QuotaCap.Value;
			if (value4 == -1)
			{
				return num;
			}
			return Mathf.Min(num, value4);
		}

		private static float CalculatePlayerMultiplier()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				return 1f;
			}
			int num = Mathf.Max(1, singleton.ConnectedClientsList?.Count ?? 1);
			int value = ConfigManager.PlayerThreshold.Value;
			int num2 = num - value;
			if (num2 <= 0)
			{
				return 1f;
			}
			int value2 = ConfigManager.PlayerCap.Value;
			int num3 = Mathf.Max(0, value2 - value);
			num2 = Mathf.Clamp(num2, 0, num3);
			return 1f + (float)num2 * Mathf.Max(0f, ConfigManager.MultPerPlayer.Value);
		}

		private static int CalculateRollover(int overage)
		{
			float value = ConfigManager.RolloverAmount.Value;
			if (value <= 0f || overage <= 0)
			{
				return 0;
			}
			return Mathf.RoundToInt((float)overage * Mathf.Clamp01(value));
		}

		private static int SetDeadlineTimer(float dayDuration, ref int days, ref float timeUntilDeadline, int prevDays = -1)
		{
			int num3;
			if (ConfigManager.RandomizeDeadline.Value)
			{
				int num = Math.Max(1, ConfigManager.DeadlineMin.Value);
				int num2 = Math.Max(num, ConfigManager.DeadlineMax.Value);
				num3 = Random.Range(num, num2 + 1);
				if (ConfigManager.DeadlineMustChange.Value && num3 == prevDays && num != num2)
				{
					num3 = Random.Range(num, num2 + 1);
				}
			}
			else
			{
				num3 = Math.Max(1, ConfigManager.DaysToDeadline.Value);
			}
			days = num3;
			timeUntilDeadline = (float)num3 * dayDuration;
			return num3;
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void TimeOfDay_Awake_Postfix(TimeOfDay __instance)
		{
			ApplyQuotaVariables(__instance);
		}

		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void TimeOfDay_Start_Postfix(TimeOfDay __instance)
		{
			if (__instance.timesFulfilledQuota == 0)
			{
				if (((NetworkBehaviour)__instance).IsServer && ConfigManager.RandomizeDeadline.Value)
				{
					NetworkSync.SyncDeadlineToClients(__instance.daysUntilDeadline);
				}
				string arg = (ConfigManager.RandomizeDeadline.Value ? $"randomized {ConfigManager.DeadlineMin.Value}-{ConfigManager.DeadlineMax.Value}d (first: {__instance.daysUntilDeadline}d)" : $"fixed {ConfigManager.DaysToDeadline.Value}d");
				string text = ((ConfigManager.QuotaCap.Value != -1) ? $", cap={ConfigManager.QuotaCap.Value}" : "");
				string text2 = ((ConfigManager.FinalLevel.Value != -1) ? $", finalLevel={ConfigManager.FinalLevel.Value} (+{ConfigManager.FinalIncrease.Value} flat)" : "");
				string text3 = (ConfigManager.CreditPenaltiesEnabled.Value ? $"credits={ConfigManager.CreditPenaltyPercentPerPlayer.Value:P0}/player (cap {ConfigManager.CreditPenaltyPercentCap.Value:P0})" : "credits=off");
				string text4 = (ConfigManager.QuotaPenaltiesEnabled.Value ? $"quota={ConfigManager.QuotaPenaltyPercentPerPlayer.Value:P0}/player (cap {ConfigManager.QuotaPenaltyPercentCap.Value:P0})" : "quota=off");
				string text5 = $"scrap={ConfigManager.ScrapLossEnabled.Value}" + $", value={ConfigManager.ValueLossEnabled.Value}({ConfigManager.ValueLossPercent.Value:P0})" + $", equip={ConfigManager.EquipmentLossEnabled.Value}";
				Plugin.Log.LogInfo((object)$"Quota: start={ConfigManager.StartingQuota.Value}, base+{ConfigManager.BaseIncrease.Value}/cycle, sharpness={ConfigManager.CurveSharpness.Value}{text}{text2}");
				Plugin.Log.LogInfo((object)$"Deadline: {arg} | Credits start: {ConfigManager.StartingCredits.Value}");
				Plugin.Log.LogInfo((object)("Penalties: " + text3 + " | " + text4));
				Plugin.Log.LogInfo((object)("Losses: " + text5));
			}
		}

		private static void ApplyQuotaVariables(TimeOfDay instance)
		{
			try
			{
				if (instance.quotaVariables != null)
				{
					instance.quotaVariables.startingQuota = ConfigManager.StartingQuota.Value;
					instance.quotaVariables.startingCredits = ConfigManager.StartingCredits.Value;
					if (ConfigManager.RandomizeDeadline.Value)
					{
						int num = Math.Max(1, ConfigManager.DeadlineMin.Value);
						int num2 = Math.Max(num, ConfigManager.DeadlineMax.Value);
						instance.quotaVariables.deadlineDaysAmount = Random.Range(num, num2 + 1);
					}
					else
					{
						instance.quotaVariables.deadlineDaysAmount = ConfigManager.DaysToDeadline.Value;
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to apply quota variables: {arg}");
			}
		}

		[HarmonyPatch("UpdateProfitQuotaCurrentTime")]
		[HarmonyPostfix]
		private static void UpdateProfitQuotaCurrentTime_Postfix(TimeOfDay __instance, ref float ___timeUntilDeadline, ref float ___totalTime, ref int ___daysUntilDeadline)
		{
			if (ConfigManager.DisableQuota.Value)
			{
				ApplyDisableQuotaState(ref ___daysUntilDeadline, ref ___totalTime, ref ___timeUntilDeadline);
			}
		}

		[HarmonyPatch("SetBuyingRateForDay")]
		[HarmonyPostfix]
		private static void SetBuyingRateForDay_Postfix(TimeOfDay __instance, ref float ___timeUntilDeadline, ref float ___totalTime, ref int ___daysUntilDeadline)
		{
			if (ConfigManager.DisableQuota.Value)
			{
				ApplyDisableQuotaState(ref ___daysUntilDeadline, ref ___totalTime, ref ___timeUntilDeadline);
			}
		}

		private static void ApplyDisableQuotaState(ref int days, ref float totalTime, ref float timeUntilDeadline)
		{
			try
			{
				SetDeadlineTimer(totalTime, ref days, ref timeUntilDeadline);
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.companyBuyingRate = 1f;
					if ((Object)(object)instance.deadlineMonitorText != (Object)null)
					{
						((TMP_Text)instance.deadlineMonitorText).text = "DEADLINE:\nNEVER";
					}
					if ((Object)(object)instance.profitQuotaMonitorText != (Object)null)
					{
						((TMP_Text)instance.profitQuotaMonitorText).text = "QUOTA:\nDISABLED";
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("DisableQuota state update failed: " + ex.Message));
			}
		}
	}
}