Decompiled source of MissionRandomizer v0.1.25

plugins/MissionRandomizer/MissionRandomizer.dll

Decompiled 17 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ScrapVisbility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ScrapVisbility")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a6853bd-bdc9-4741-95c7-5aa2c8c6a6f9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LC_MissionRandomizer;

[BepInPlugin("yellowcube.lc.mission_randomizer", "Mission Randomizer", "0.1.25")]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "yellowcube.lc.mission_randomizer";

	public const string PluginName = "Mission Randomizer";

	public const string PluginVersion = "0.1.25";

	internal static ManualLogSource Log;

	internal static Plugin Instance;

	private Harmony harmony;

	internal ConfigEntry<bool> ModEnabled;

	internal ConfigEntry<bool> BlockMoonTerminalCommands;

	internal ConfigEntry<bool> ForceStarterMoon;

	internal ConfigEntry<bool> RestrictTerminalMoonCatalogue;

	internal ConfigEntry<bool> DisableOutsideAndDaytimePower;

	internal ConfigEntry<bool> ShowMonitorStats;

	internal ConfigEntry<bool> DebugLogging;

	internal ConfigEntry<bool> AutoFileDebugLogging;

	internal ConfigEntry<bool> HostDebugTeleportScrapWithP;

	internal ConfigEntry<bool> NormalizeGeneratedScrapValue;

	internal ConfigEntry<bool> EnforceGeneratedScrapCount;

	internal ConfigEntry<bool> PreClampScrapValuesBeforeGeneration;

	internal ConfigEntry<bool> CleaningCompanySupport;

	internal ConfigEntry<int> MinimumTotalScrapValue;

	internal ConfigEntry<int> MaximumTotalScrapValueCap;

	internal ConfigEntry<float> QuotaCoverageMultiplier;

	internal ConfigEntry<int> MinimumScrapCount;

	internal ConfigEntry<int> MaximumScrapCountCap;

	internal ConfigEntry<int> MaximumMissionScrapCount;

	internal ConfigEntry<int> MaximumInsideEnemyPowerCap;

	internal ConfigEntry<int> MaximumMinEnemiesToSpawn;

	internal ConfigEntry<float> MaximumFactorySizeMultiplier;

	internal ConfigEntry<float> MaximumScrapAmountMultiplier;

	internal ConfigEntry<float> MaximumScrapValueMultiplier;

	internal ConfigEntry<float> CleaningCompanyConversionSearchRadius;

	internal ConfigEntry<float> CleaningCompanyConversionWindowSeconds;

	internal ConfigEntry<int> SeedOffset;

	private void Awake()
	{
		//IL_042a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0434: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		try
		{
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
		}
		catch
		{
		}
		ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable or disable the mission randomizer.");
		BlockMoonTerminalCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BlockMoonTerminalCommands", true, "If true, the moons and route terminal commands are replaced with mission information / blocked.");
		ForceStarterMoon = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceStarterMoon", true, "If true, the current level is forced back to the starter moon while in orbit and before loading a level.");
		RestrictTerminalMoonCatalogue = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RestrictTerminalMoonCatalogue", false, "If true, the terminal moon catalogue is trimmed to only the starter moon and the Company. Leave false if another mod expects the normal catalogue.");
		DisableOutsideAndDaytimePower = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableOutsideAndDaytimePower", true, "If true, outside and daytime enemy power caps are forced to zero.");
		ShowMonitorStats = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowMonitorStats", true, "If true, ship monitor text is replaced with the current randomized mission stats.");
		DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugEnabled", false, "If true, enable diagnostic console logging and allow debug files to be written.");
		AutoFileDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "WriteDebugFiles", true, "If true and DebugEnabled is true, write orbit / landed / manual diagnostic snapshots to BepInEx/MissionRandomizerDebugLogs.");
		HostDebugTeleportScrapWithP = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "HostCanPressPToTeleportScrap", false, "If true, the host can press P to teleport scrap to themselves for testing. This only works on the host/server and does not sync scrap positions to clients.");
		NormalizeGeneratedScrapValue = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "NormalizeGeneratedScrapValue", true, "If true, the host keeps generated mission scrap inside the displayed total value range. It no longer forces the total to always equal the max value.");
		EnforceGeneratedScrapCount = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "EnforceGeneratedScrapCount", true, "If true, the host removes excess generated mission scrap above the mission profile max count before vanilla scrap values sync.");
		PreClampScrapValuesBeforeGeneration = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "PreScaleScrapValuesBeforeGeneration", false, "Legacy option. Keep this off. The mod now edits the real vanilla scrap values before the vanilla scrap sync instead of using temporary fake item ranges.");
		CleaningCompanySupport = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "CleaningCompanySupport", true, "If true, preserve scrap value for direct Cleaning Company conversions: Scav Goop / Thumper Drool to Dirty Water, and Bone Pile / Nail Pile Item / BrackenDustItem / Ethereal Essence / Hoarding Bug Eggs / Spore Pile to Full Garbage Bag.");
		MinimumTotalScrapValue = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MinimumTotalScrapValue", 220, "Lowest maxTotalScrapValue the mod will generate.");
		MaximumTotalScrapValueCap = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MaximumTotalScrapValueCap", 1250, "Hard cap for generated maxTotalScrapValue.");
		QuotaCoverageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "QuotaCoverageMultiplier", 1.2f, "Each remaining day tries to offer at least quotaRemaining / daysLeft multiplied by this value.");
		MinimumScrapCount = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MinimumScrapCount", 12, "Lowest generated minScrap count.");
		MaximumScrapCountCap = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MaximumScrapCountCap", 48, "Legacy cap for generated maxScrap count. MaximumMissionScrapCount is the main physical scrap cap.");
		MaximumMissionScrapCount = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MaximumMissionScrapCount", 48, "Hard cap for how many physical mission scrap items can spawn. This does not cap total scrap value.");
		MaximumInsideEnemyPowerCap = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MaximumInsideEnemyPowerCap", 14, "Hard cap for generated maxEnemyPowerCount.");
		MaximumMinEnemiesToSpawn = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MaximumMinEnemiesToSpawn", 3, "Hard cap for RoundManager.minEnemiesToSpawn.");
		MaximumFactorySizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "MaximumFactorySizeMultiplier", 2.05f, "Hard cap for generated factorySizeMultiplier / RoundManager.mapSizeMultiplier.");
		MaximumScrapAmountMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "MaximumScrapAmountMultiplier", 2.25f, "Hard cap for RoundManager.scrapAmountMultiplier.");
		MaximumScrapValueMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "MaximumScrapValueMultiplier", 2.25f, "Hard cap for RoundManager.scrapValueMultiplier.");
		CleaningCompanyConversionSearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Compatibility", "CleaningCompanyConversionSearchRadius", 6f, "How close a newly appeared scrap item must be to a disappeared Cleaning Company mess or converted scrap item to inherit its value.");
		CleaningCompanyConversionWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Compatibility", "CleaningCompanyConversionWindowSeconds", 4f, "How long a disappeared scrap value stays pending while waiting for a nearby newly appeared scrap item.");
		SeedOffset = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "SeedOffset", 7319, "Change this to produce a different deterministic mission sequence.");
		try
		{
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Mission Randomizer config saved to: " + ((BaseUnityPlugin)this).Config.ConfigFilePath));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Mission Randomizer config save failed: " + ex.Message));
		}
		MissionController.Initialize(this);
		harmony = new Harmony("yellowcube.lc.mission_randomizer");
		harmony.PatchAll(typeof(Plugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Mission Randomizer 0.1.25 loaded.");
	}

	private void OnDestroy()
	{
		try
		{
			MissionController.Shutdown();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
		catch
		{
		}
	}

	private void OnGUI()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Invalid comparison between Unknown and I4
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Invalid comparison between Unknown and I4
		Event current = Event.current;
		if (current != null && (int)current.type == 4 && (int)current.keyCode == 112 && HostDebugTeleportScrapWithP != null && HostDebugTeleportScrapWithP.Value)
		{
			MissionController.DebugTeleportScrapToPlayer();
		}
	}
}
internal static class MissionController
{
	[CompilerGenerated]
	private sealed class <DelayedNormalizeSpawnedScrap>d__65 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string reason;

		public float delaySeconds;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (delaySeconds > 0f)
				{
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				}
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			NormalizeSpawnedScrapForCurrentRoll(reason, writeLog: true);
			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();
		}
	}

	[CompilerGenerated]
	private sealed class <DelayedRestoreTemporaryScrapValueRanges>d__67 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string reason;

		public float delaySeconds;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (delaySeconds > 0f)
				{
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				}
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			RestoreTemporaryScrapValueRanges(reason, writeLog: true);
			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();
		}
	}

	[CompilerGenerated]
	private sealed class <DelayedSendRollToClient>d__174 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public ulong clientId;

		public float delaySeconds;

		private StartOfRound <start>5__1;

		private RoundManager <round>5__2;

		private TimeOfDay <time>5__3;

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

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

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

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<start>5__1 = null;
			<round>5__2 = null;
			<time>5__3 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (delaySeconds > 0f)
				{
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				}
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			if (IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance))
			{
				return false;
			}
			if (!HasRoll)
			{
				<start>5__1 = StartOfRound.Instance;
				<round>5__2 = RoundManager.Instance;
				<time>5__3 = TimeOfDay.Instance;
				if ((Object)(object)<start>5__1 != (Object)null && (Object)(object)<round>5__2 != (Object)null && (Object)(object)<time>5__3 != (Object)null)
				{
					EnsureRollForCurrentOrbit(<start>5__1, <round>5__2, <time>5__3);
				}
				<start>5__1 = null;
				<round>5__2 = null;
				<time>5__3 = null;
			}
			if (HasRoll)
			{
				SendRollToClient(CurrentRoll, clientId);
			}
			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();
		}
	}

	[CompilerGenerated]
	private sealed class <DelayedSendScrapValuesToClient>d__159 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public ulong clientId;

		public float delaySeconds;

		private List<ScrapComponentRecord> <records>5__1;

		private int <total>5__2;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (delaySeconds > 0f)
				{
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				}
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			if (!HasRoll)
			{
				return false;
			}
			if (IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance))
			{
				return false;
			}
			<records>5__1 = CollectMissionScrapRecords(includeHeld: false);
			<total>5__2 = SumScrapRecordValues(<records>5__1);
			if (<records>5__1.Count > 0)
			{
				SendScrapValueSyncToClient(<records>5__1, <total>5__2, clientId, "late join delayed sync");
			}
			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();
		}
	}

	[CompilerGenerated]
	private sealed class <EarlyLiveScrapNormalizeCoroutine>d__61 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string reason;

		public float delaySeconds;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (delaySeconds > 0f)
				{
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				}
				break;
			case 1:
				<>1__state = -1;
				break;
			}
			earlyLiveScrapNormalizeScheduled = false;
			NormalizeSpawnedScrapForCurrentRoll(reason, writeLog: true);
			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();
		}
	}

	[CompilerGenerated]
	private sealed class <RapidNormalizeSpawnedScrapAtRoundStart>d__66 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = null;
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				NormalizeSpawnedScrapForCurrentRoll("round start rapid single-frame safety", writeLog: false);
				<>2__current = (object)new WaitForSeconds(0.5f);
				<>1__state = 2;
				return true;
			case 2:
				<>1__state = -1;
				NormalizeSpawnedScrapForCurrentRoll("round start rapid 0.50s safety", writeLog: 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();
		}
	}

	private const string NetMessageName = "yellowcube.mission_randomizer.roll.v1";

	private const string NetRequestMessageName = "yellowcube.mission_randomizer.roll_request.v1";

	private const string NetScrapValuesMessageName = "yellowcube.mission_randomizer.scrap_values.v1";

	private const string NetScrapValuesRequestMessageName = "yellowcube.mission_randomizer.scrap_values_request.v1";

	private static Plugin plugin;

	private static bool networkRegistered;

	private static bool sceneHooked;

	private static NetworkManager registeredNetworkManager;

	private static StartOfRound trackedStartOfRound;

	private static RoundManager trackedRoundManager;

	private static float nextClientRollRequestTime;

	private static float nextClientScrapValueRequestTime;

	private static int clientScrapValueRequestSeed = int.MinValue;

	private static int clientScrapValueRequestAttempts;

	private static int clientScrapValueSyncReceivedSeed = int.MinValue;

	private static float nextPendingScrapApplyTime;

	private static int pendingScrapValueTotal;

	private static int runtimeRandomSeedSalt;

	private static string runtimeRandomSeedReason = string.Empty;

	private static readonly List<PendingScrapValue> pendingScrapValues = new List<PendingScrapValue>();

	private static readonly Dictionary<ulong, float> nextServerScrapValueResponseByClient = new Dictionary<ulong, float>();

	private static readonly List<CleaningConversionRecord> pendingCleaningConversions = new List<CleaningConversionRecord>();

	private static readonly List<OriginalScrapItemValueRange> temporaryScrapValueRanges = new List<OriginalScrapItemValueRange>();

	private static string lastRollKey = string.Empty;

	private static float nextPeriodicBroadcastTime;

	private static float nextMonitorRefreshTime;

	private static float nextMissingMonitorLogTime;

	private static float nextScrapDebugTime;

	private static float nextCleaningConversionCleanupTime;

	private static int orbitDebugSequence;

	private static int landingSnapshotStage;

	private static float nextLandingSnapshotTime;

	private static string currentMissionDebugPrefix = string.Empty;

	private static TerminalNode missionTerminalNode;

	private static TerminalNode routeMissionTerminalNode;

	private static TerminalNode routeCompanyTerminalNode;

	private static TerminalNode routeMissionPromptTerminalNode;

	private static TerminalNode routeCompanyPromptTerminalNode;

	private static int pendingTerminalRoute;

	private static float pendingTerminalRouteExpiresAt;

	private static bool earlyLiveScrapNormalizeScheduled;

	private static float nextEarlyLiveScrapNormalizeAllowedTime;

	internal static MissionRoll CurrentRoll;

	internal static bool HasRoll;

	internal static void Initialize(Plugin owner)
	{
		plugin = owner;
		if (!sceneHooked)
		{
			SceneManager.sceneLoaded += OnSceneLoaded;
			sceneHooked = true;
		}
	}

	internal static void Shutdown()
	{
		ResetMissionRuntimeState("Plugin shutdown", keepTrackedInstances: false);
		ResetNetworkRegistration("Plugin shutdown");
		if (sceneHooked)
		{
			try
			{
				SceneManager.sceneLoaded -= OnSceneLoaded;
			}
			catch
			{
			}
			sceneHooked = false;
		}
	}

	private static bool ShouldDebugLog()
	{
		return (Object)(object)plugin != (Object)null && plugin.DebugLogging != null && plugin.DebugLogging.Value;
	}

	private static bool ShouldWriteDebugFiles()
	{
		return (Object)(object)plugin != (Object)null && plugin.DebugLogging != null && plugin.DebugLogging.Value && plugin.AutoFileDebugLogging != null && plugin.AutoFileDebugLogging.Value;
	}

	private static int GetConfiguredMaxMissionScrapCount()
	{
		if ((Object)(object)plugin == (Object)null || plugin.MaximumMissionScrapCount == null)
		{
			return 48;
		}
		return Mathf.Clamp(plugin.MaximumMissionScrapCount.Value, 1, 500);
	}

	private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		nextMonitorRefreshTime = 0f;
		if (((Scene)(ref scene)).name.Equals("MainMenu", StringComparison.OrdinalIgnoreCase))
		{
			ResetMissionRuntimeState("Main menu loaded", keepTrackedInstances: false);
			ResetNetworkRegistration("Main menu loaded");
		}
		TryRegisterNetworking();
	}

	internal static void UpdateLoop()
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value)
		{
			return;
		}
		TryRegisterNetworking();
		StartOfRound instance = StartOfRound.Instance;
		RoundManager instance2 = RoundManager.Instance;
		TimeOfDay instance3 = TimeOfDay.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)instance3 == (Object)null)
		{
			return;
		}
		DetectNewGameSystemInstances(instance, instance2);
		RequestRollFromHostIfNeeded();
		CleanupExpiredCleaningCompanyConversions();
		bool @bool = GetBool(instance, "inShipPhase", fallback: false);
		bool bool2 = GetBool(instance, "shipHasLanded", fallback: false);
		bool bool3 = GetBool(instance, "shipIsLeaving", fallback: false);
		if (!IsServerOrNoNetwork())
		{
			ApplyPendingScrapValues("client update loop");
			RequestScrapValuesFromHostIfNeeded(bool2, bool3);
		}
		HandleAutomaticLandingDebugSnapshots(instance, instance2, instance3, @bool, bool2, bool3);
		bool flag = IsCompanyRouteActive(instance2, instance3);
		if (@bool && !bool2 && !bool3 && !flag)
		{
			EnsureRollForCurrentOrbit(instance, instance2, instance3);
			if (plugin.ForceStarterMoon.Value)
			{
				ForceStarterLevel(instance2, instance3);
			}
			if (plugin.RestrictTerminalMoonCatalogue.Value)
			{
				RestrictTerminalCatalogueToStarter();
			}
		}
		if (!flag && IsServerOrNoNetwork() && HasRoll && Time.realtimeSinceStartup >= nextPeriodicBroadcastTime)
		{
			nextPeriodicBroadcastTime = Time.realtimeSinceStartup + 5f;
			BroadcastRoll(CurrentRoll);
		}
		if (!flag && HasRoll)
		{
			ApplyRollToGameObjects(CurrentRoll, "UpdateLoop");
		}
		if (!flag && plugin.ShowMonitorStats.Value && Time.realtimeSinceStartup >= nextMonitorRefreshTime)
		{
			nextMonitorRefreshTime = Time.realtimeSinceStartup + 0.75f;
			UpdateMonitorText();
		}
	}

	private static void DetectNewGameSystemInstances(StartOfRound start, RoundManager round)
	{
		if ((Object)(object)trackedStartOfRound != (Object)null && trackedStartOfRound != start)
		{
			ResetMissionRuntimeState("StartOfRound instance changed", keepTrackedInstances: true);
		}
		if ((Object)(object)trackedRoundManager != (Object)null && trackedRoundManager != round)
		{
			ResetMissionRuntimeState("RoundManager instance changed", keepTrackedInstances: true);
		}
		trackedStartOfRound = start;
		trackedRoundManager = round;
	}

	private static void ResetMissionRuntimeState(string reason, bool keepTrackedInstances)
	{
		HasRoll = false;
		CurrentRoll = default(MissionRoll);
		lastRollKey = string.Empty;
		nextPeriodicBroadcastTime = 0f;
		nextMonitorRefreshTime = 0f;
		nextMissingMonitorLogTime = 0f;
		nextClientRollRequestTime = 0f;
		nextClientScrapValueRequestTime = 0f;
		clientScrapValueRequestSeed = int.MinValue;
		clientScrapValueRequestAttempts = 0;
		clientScrapValueSyncReceivedSeed = int.MinValue;
		nextPendingScrapApplyTime = 0f;
		pendingScrapValueTotal = 0;
		pendingScrapValues.Clear();
		nextServerScrapValueResponseByClient.Clear();
		pendingCleaningConversions.Clear();
		nextCleaningConversionCleanupTime = 0f;
		nextScrapDebugTime = 0f;
		orbitDebugSequence = 0;
		landingSnapshotStage = 0;
		nextLandingSnapshotTime = 0f;
		currentMissionDebugPrefix = string.Empty;
		missionTerminalNode = null;
		routeMissionTerminalNode = null;
		routeCompanyTerminalNode = null;
		routeMissionPromptTerminalNode = null;
		routeCompanyPromptTerminalNode = null;
		pendingTerminalRoute = 0;
		pendingTerminalRouteExpiresAt = 0f;
		earlyLiveScrapNormalizeScheduled = false;
		nextEarlyLiveScrapNormalizeAllowedTime = 0f;
		RestoreTemporaryScrapValueRanges("runtime reset: " + reason, writeLog: false);
		if (!keepTrackedInstances || reason.IndexOf("Main menu", StringComparison.OrdinalIgnoreCase) >= 0 || reason.IndexOf("Plugin shutdown", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			runtimeRandomSeedSalt = 0;
			runtimeRandomSeedReason = string.Empty;
		}
		if (!keepTrackedInstances)
		{
			trackedStartOfRound = null;
			trackedRoundManager = null;
		}
		if ((Object)(object)plugin != (Object)null && plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission runtime state reset: " + reason));
		}
	}

	private static void RequestRollFromHostIfNeeded()
	{
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || !singleton.IsClient || singleton.IsServer || HasRoll || Time.realtimeSinceStartup < nextClientRollRequestTime)
			{
				return;
			}
			nextClientRollRequestTime = Time.realtimeSinceStartup + 2f;
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1);
			try
			{
				singleton.CustomMessagingManager.SendNamedMessage("yellowcube.mission_randomizer.roll_request.v1", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
		}
		catch (Exception ex)
		{
			if ((Object)(object)plugin != (Object)null && plugin.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("Mission roll request failed: " + ex.Message));
			}
		}
	}

	internal static void BeforeLoadNewLevel()
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value)
		{
			return;
		}
		StartOfRound instance = StartOfRound.Instance;
		RoundManager instance2 = RoundManager.Instance;
		TimeOfDay instance3 = TimeOfDay.Instance;
		if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null) && !((Object)(object)instance3 == (Object)null) && !IsCompanyRouteActive(instance2, instance3))
		{
			EnsureRollForCurrentOrbit(instance, instance2, instance3);
			if (HasRoll)
			{
				ApplyMissionRandomSeedToGameObjects(CurrentRoll, instance2, instance, "BeforeLoadNewLevel");
			}
			if (plugin.ForceStarterMoon.Value)
			{
				ForceStarterLevel(instance2, instance3);
			}
			if (HasRoll)
			{
				ApplyRollToGameObjects(CurrentRoll, "BeforeLoadNewLevel");
			}
		}
	}

	internal static void AfterGrabbableObjectStart(Component component)
	{
		HandleCleaningCompanyGrabbableStart(component);
	}

	internal static void BeforeSpawnScrapInLevel(RoundManager round)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !IsServerOrNoNetwork() || IsCompanyRouteActive(round, TimeOfDay.Instance))
		{
			return;
		}
		if (!HasRoll)
		{
			StartOfRound instance = StartOfRound.Instance;
			TimeOfDay instance2 = TimeOfDay.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)round != (Object)null && (Object)(object)instance2 != (Object)null)
			{
				EnsureRollForCurrentOrbit(instance, round, instance2);
			}
		}
		if (HasRoll)
		{
			ApplyRollToGameObjects(CurrentRoll, "BeforeSpawnScrapInLevel");
			RestoreTemporaryScrapValueRanges("BeforeSpawnScrapInLevel cleanup", writeLog: false);
		}
	}

	internal static void AfterSpawnScrapInLevel(RoundManager round)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !IsServerOrNoNetwork() || IsCompanyRouteActive(round, TimeOfDay.Instance))
		{
			return;
		}
		if (!HasRoll)
		{
			StartOfRound instance = StartOfRound.Instance;
			TimeOfDay instance2 = TimeOfDay.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)round != (Object)null && (Object)(object)instance2 != (Object)null)
			{
				EnsureRollForCurrentOrbit(instance, round, instance2);
			}
		}
		if (!HasRoll)
		{
			return;
		}
		ApplyRollToGameObjects(CurrentRoll, "AfterSpawnScrapInLevel");
		try
		{
			RestoreTemporaryScrapValueRanges("right after SpawnScrapInLevel", writeLog: false);
			((MonoBehaviour)plugin).StartCoroutine(DelayedRestoreTemporaryScrapValueRanges("after round start scrap generation safety", 1f));
		}
		catch (Exception ex)
		{
			if (plugin.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("Mission scrap normalization scheduling failed: " + ex.Message));
			}
		}
	}

	internal static void BeforeVanillaScrapValuesSync(RoundManager round, NetworkObjectReference[] spawnedScrap, int[] scrapValues)
	{
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !IsServerOrNoNetwork() || IsCompanyRouteActive(round, TimeOfDay.Instance))
		{
			return;
		}
		if (!HasRoll)
		{
			StartOfRound instance = StartOfRound.Instance;
			TimeOfDay instance2 = TimeOfDay.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)round != (Object)null && (Object)(object)instance2 != (Object)null)
			{
				EnsureRollForCurrentOrbit(instance, round, instance2);
			}
		}
		if (!HasRoll || spawnedScrap == null || scrapValues == null || spawnedScrap.Length == 0 || scrapValues.Length == 0)
		{
			return;
		}
		int num = Mathf.Min(spawnedScrap.Length, scrapValues.Length);
		List<ScrapComponentRecord> list = new List<ScrapComponentRecord>();
		List<int> list2 = new List<int>();
		for (int i = 0; i < num; i++)
		{
			NetworkObject val = null;
			try
			{
				NetworkObjectReference val2 = spawnedScrap[i];
				if (!((NetworkObjectReference)(ref val2)).TryGet(ref val, NetworkManager.Singleton))
				{
					val = null;
				}
			}
			catch
			{
				val = null;
			}
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
			{
				continue;
			}
			Component val3 = (Component)(object)((Component)val).GetComponent<GrabbableObject>();
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = (Component)(object)((Component)val).GetComponentInChildren<GrabbableObject>(true);
			}
			if ((Object)(object)val3 == (Object)null || !IsScrapGrabbable(val3))
			{
				continue;
			}
			object field = GetField<object>(val3, "itemProperties", null);
			if (field != null && GetBool(field, "isScrap", fallback: false))
			{
				string itemDisplayName = GetItemDisplayName(val3, field);
				if (!IsApparatusItem(val3, val3.gameObject, itemDisplayName))
				{
					int value = Mathf.Max(1, scrapValues[i]);
					list.Add(new ScrapComponentRecord
					{
						Component = val3,
						GameObject = val3.gameObject,
						Value = value,
						ItemName = itemDisplayName,
						SyncIndex = i
					});
					list2.Add(i);
				}
			}
		}
		if (list.Count == 0)
		{
			return;
		}
		int num2 = Mathf.Min(CurrentRoll.MaxScrap, GetConfiguredMaxMissionScrapCount());
		int num3 = 0;
		int num4 = 0;
		if (plugin.EnforceGeneratedScrapCount.Value && list.Count > num2)
		{
			list.Sort((ScrapComponentRecord a, ScrapComponentRecord b) => b.Value.CompareTo(a.Value));
			int num5 = list.Count - num2;
			for (int j = 0; j < num5; j++)
			{
				ScrapComponentRecord scrapComponentRecord = list[j];
				num3++;
				num4 += Mathf.Max(0, scrapComponentRecord.Value);
				if (scrapComponentRecord.SyncIndex >= 0 && scrapComponentRecord.SyncIndex < scrapValues.Length)
				{
					scrapValues[scrapComponentRecord.SyncIndex] = 0;
				}
				DespawnOrDestroyScrap(scrapComponentRecord.Component);
			}
			list.RemoveRange(0, num5);
		}
		int minimumTotalScrapValueForRoll = GetMinimumTotalScrapValueForRoll(CurrentRoll);
		int num6 = Mathf.Max(minimumTotalScrapValueForRoll, CurrentRoll.MaxTotalScrapValue);
		int num7 = 0;
		for (int k = 0; k < list.Count; k++)
		{
			num7 += Mathf.Max(1, list[k].Value);
		}
		int num8 = num7;
		bool flag = num3 > 0;
		if (plugin.NormalizeGeneratedScrapValue.Value && list.Count > 0 && (num7 < minimumTotalScrapValueForRoll || num7 > num6))
		{
			int targetValue = ChooseScrapTotalValueWithinRange(CurrentRoll, num7, list.Count);
			NormalizeScrapRecordValues(list, targetValue);
			num8 = SumScrapRecordValues(list);
			flag = true;
		}
		else
		{
			for (int l = 0; l < list.Count; l++)
			{
				SetScrapValue(list[l].Component, list[l].Value);
			}
		}
		for (int m = 0; m < list.Count; m++)
		{
			int syncIndex = list[m].SyncIndex;
			if (syncIndex >= 0 && syncIndex < scrapValues.Length)
			{
				scrapValues[syncIndex] = Mathf.Max(1, list[m].Value);
			}
		}
		if ((Object)(object)round != (Object)null)
		{
			SetField(round, "totalScrapValueInLevel", (float)num8);
		}
		BroadcastScrapValueSync(list, num8, "before vanilla scrap value sync");
		RestoreTemporaryScrapValueRanges("before vanilla scrap value sync", writeLog: false);
		if (plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission scrap values applied before vanilla sync: final " + list.Count + " / $" + num8 + ", removed " + num3 + " / $" + num4 + ", allowed count <= " + num2 + ", allowed range $" + minimumTotalScrapValueForRoll + "-$" + num6 + ", adjusted=" + flag + "."));
		}
	}

	internal static void BeforeFinishGeneratingNewLevelClientRpc(RoundManager round)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !IsServerOrNoNetwork())
		{
			return;
		}
		if ((Object)(object)round == (Object)null)
		{
			round = RoundManager.Instance;
		}
		if (IsCompanyRouteActive(round, TimeOfDay.Instance))
		{
			return;
		}
		if (!HasRoll)
		{
			StartOfRound instance = StartOfRound.Instance;
			TimeOfDay instance2 = TimeOfDay.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)round != (Object)null && (Object)(object)instance2 != (Object)null)
			{
				EnsureRollForCurrentOrbit(instance, round, instance2);
			}
		}
		if (!HasRoll)
		{
			return;
		}
		ApplyRollToGameObjects(CurrentRoll, "BeforeFinishGeneratingNewLevelClientRpc");
		NormalizeSpawnedScrapForCurrentRoll("BeforeFinishGeneratingNewLevelClientRpc live scrap pass", writeLog: true);
		try
		{
			ScheduleEarlyLiveScrapNormalize("post FinishGeneratingNewLevelClientRpc safety", 0.1f);
		}
		catch
		{
		}
	}

	private static void ScheduleEarlyLiveScrapNormalize(string reason, float delaySeconds)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !IsServerOrNoNetwork() || !HasRoll || IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance))
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (earlyLiveScrapNormalizeScheduled || realtimeSinceStartup < nextEarlyLiveScrapNormalizeAllowedTime)
		{
			return;
		}
		earlyLiveScrapNormalizeScheduled = true;
		nextEarlyLiveScrapNormalizeAllowedTime = realtimeSinceStartup + 0.2f;
		try
		{
			((MonoBehaviour)plugin).StartCoroutine(EarlyLiveScrapNormalizeCoroutine(reason, delaySeconds));
		}
		catch
		{
			earlyLiveScrapNormalizeScheduled = false;
		}
	}

	[IteratorStateMachine(typeof(<EarlyLiveScrapNormalizeCoroutine>d__61))]
	private static IEnumerator EarlyLiveScrapNormalizeCoroutine(string reason, float delaySeconds)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <EarlyLiveScrapNormalizeCoroutine>d__61(0)
		{
			reason = reason,
			delaySeconds = delaySeconds
		};
	}

	private static int GetMinimumTotalScrapValueForRoll(MissionRoll roll)
	{
		int num = Mathf.Max(1, roll.MaxTotalScrapValue);
		int num2 = Mathf.RoundToInt((float)num * 0.8f);
		return Mathf.Clamp(num2, 1, num);
	}

	private static int ChooseScrapTotalValueWithinRange(MissionRoll roll, int sourceTotal, int scrapCount)
	{
		int minimumTotalScrapValueForRoll = GetMinimumTotalScrapValueForRoll(roll);
		int num = Mathf.Max(minimumTotalScrapValueForRoll, roll.MaxTotalScrapValue);
		if (num <= minimumTotalScrapValueForRoll)
		{
			return num;
		}
		int seed = roll.Seed;
		seed = (seed * 397) ^ Mathf.Max(1, sourceTotal);
		seed = (seed * 397) ^ Mathf.Max(1, scrapCount);
		seed = (seed * 397) ^ 0x4D52564C;
		Random random = new Random(seed);
		return random.Next(minimumTotalScrapValueForRoll, num + 1);
	}

	private static string TotalScrapValueRangeText(MissionRoll roll)
	{
		return "$" + GetMinimumTotalScrapValueForRoll(roll) + " - $" + Mathf.Max(GetMinimumTotalScrapValueForRoll(roll), roll.MaxTotalScrapValue);
	}

	[IteratorStateMachine(typeof(<DelayedNormalizeSpawnedScrap>d__65))]
	private static IEnumerator DelayedNormalizeSpawnedScrap(string reason, float delaySeconds)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <DelayedNormalizeSpawnedScrap>d__65(0)
		{
			reason = reason,
			delaySeconds = delaySeconds
		};
	}

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

	[IteratorStateMachine(typeof(<DelayedRestoreTemporaryScrapValueRanges>d__67))]
	private static IEnumerator DelayedRestoreTemporaryScrapValueRanges(string reason, float delaySeconds)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <DelayedRestoreTemporaryScrapValueRanges>d__67(0)
		{
			reason = reason,
			delaySeconds = delaySeconds
		};
	}

	internal static void AfterMapScreenUpdate()
	{
		if (!((Object)(object)plugin == (Object)null) && plugin.ModEnabled.Value && plugin.ShowMonitorStats.Value && !IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance))
		{
			UpdateMonitorText();
		}
	}

	internal static void DebugTeleportScrapToPlayer()
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || plugin.HostDebugTeleportScrapWithP == null || !plugin.HostDebugTeleportScrapWithP.Value || Time.realtimeSinceStartup < nextScrapDebugTime)
		{
			return;
		}
		nextScrapDebugTime = Time.realtimeSinceStartup + 0.75f;
		if (!IsServerOrNoNetwork())
		{
			SendDebugChatLine("Mission Debug: press P on the host/server. Scrap movement must be server authoritative.");
			return;
		}
		StartOfRound instance = StartOfRound.Instance;
		RoundManager instance2 = RoundManager.Instance;
		TimeOfDay instance3 = TimeOfDay.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)instance3 == (Object)null)
		{
			SendDebugChatLine("Mission Debug: StartOfRound, RoundManager, or TimeOfDay is missing.");
			return;
		}
		if (IsCompanyRouteActive(instance2, instance3))
		{
			SendDebugChatLine("Mission Debug: Company route active. Mission randomizer scrap controls are disabled at the Company.");
			return;
		}
		if (!HasRoll)
		{
			EnsureRollForCurrentOrbit(instance, instance2, instance3);
		}
		NormalizeSpawnedScrapForCurrentRoll("Before manual P teleport", writeLog: true);
		ScrapDebugStats scrapDebugStats = TeleportSpawnedScrapToLocalPlayer(instance);
		string text = BuildActualVsExpectedDebugReport(instance2, instance3, scrapDebugStats);
		string[] array = SplitDebugReportForChat(text);
		foreach (string message in array)
		{
			SendDebugChatLine(message);
		}
		if (plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission Debug P report:\n" + text));
		}
		if (!ShouldWriteDebugFiles())
		{
			return;
		}
		try
		{
			WriteDebugTextFile("MANUAL_P_TELEPORT", BuildFullMissionDebugFileReport("MANUAL_P_TELEPORT", instance2, instance3, scrapDebugStats));
		}
		catch (Exception ex)
		{
			if (plugin.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("Could not write manual P debug file: " + ex.Message));
			}
		}
	}

	private static void ResetLandingDebugStateForNewMission()
	{
		orbitDebugSequence++;
		landingSnapshotStage = 0;
		nextLandingSnapshotTime = 0f;
		currentMissionDebugPrefix = DateTime.Now.ToString("yyyyMMdd_HHmmss") + "_mission" + orbitDebugSequence.ToString("000") + "_seed" + (HasRoll ? CurrentRoll.Seed.ToString() : "none");
	}

	private static void HandleAutomaticLandingDebugSnapshots(StartOfRound start, RoundManager round, TimeOfDay time, bool inShipPhase, bool shipHasLanded, bool shipIsLeaving)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !ShouldWriteDebugFiles() || !IsServerOrNoNetwork() || !HasRoll)
		{
			return;
		}
		if (inShipPhase && !shipHasLanded && !shipIsLeaving)
		{
			landingSnapshotStage = 0;
			nextLandingSnapshotTime = 0f;
		}
		else if (!inShipPhase || shipHasLanded)
		{
			if (landingSnapshotStage == 0)
			{
				landingSnapshotStage = 1;
				nextLandingSnapshotTime = Time.realtimeSinceStartup + 8f;
			}
			else if (landingSnapshotStage == 1 && Time.realtimeSinceStartup >= nextLandingSnapshotTime)
			{
				WriteLandedScrapDebugFile("AUTO_8_SECONDS_AFTER_LANDING", start, round, time);
				landingSnapshotStage = 2;
				nextLandingSnapshotTime = Time.realtimeSinceStartup + 12f;
			}
			else if (landingSnapshotStage == 2 && Time.realtimeSinceStartup >= nextLandingSnapshotTime)
			{
				WriteLandedScrapDebugFile("AUTO_20_SECONDS_AFTER_LANDING", start, round, time);
				landingSnapshotStage = 3;
			}
		}
	}

	private static void WriteOrbitMissionDebugFile(string reason, RoundManager round, TimeOfDay time)
	{
		if ((Object)(object)plugin == (Object)null || !ShouldWriteDebugFiles())
		{
			return;
		}
		try
		{
			string report = BuildFullMissionDebugFileReport(reason, round, time, null);
			WriteDebugTextFile("ORBIT_" + SanitizeFilePart(reason), report);
		}
		catch (Exception ex)
		{
			if (plugin.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("Could not write orbit mission debug file: " + ex.Message));
			}
		}
	}

	private static void WriteLandedScrapDebugFile(string reason, StartOfRound start, RoundManager round, TimeOfDay time)
	{
		if ((Object)(object)plugin == (Object)null || !ShouldWriteDebugFiles())
		{
			return;
		}
		try
		{
			NormalizeSpawnedScrapForCurrentRoll("Before landed debug snapshot " + reason, writeLog: true);
			ScrapDebugStats value = CollectSpawnedScrapStats(start, teleportToPlayer: false);
			string report = BuildFullMissionDebugFileReport(reason, round, time, value);
			WriteDebugTextFile("LANDED_" + SanitizeFilePart(reason), report);
			if (plugin.DebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)("Mission Randomizer wrote landed debug snapshot: " + reason + " actualMissionScrapCount=" + value.SpawnedScrapCount + " actualMissionScrapValue=$" + value.TotalScrapValue));
			}
		}
		catch (Exception ex)
		{
			if (plugin.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("Could not write landed scrap debug file: " + ex.Message));
			}
		}
	}

	private static void WriteDebugTextFile(string suffix, string report)
	{
		string text = Path.Combine(Paths.BepInExRootPath, "MissionRandomizerDebugLogs");
		Directory.CreateDirectory(text);
		if (string.IsNullOrWhiteSpace(currentMissionDebugPrefix))
		{
			currentMissionDebugPrefix = DateTime.Now.ToString("yyyyMMdd_HHmmss") + "_mission_unknown";
		}
		string path = currentMissionDebugPrefix + "_" + suffix + ".txt";
		string text2 = Path.Combine(text, path);
		File.WriteAllText(text2, report ?? string.Empty);
		if ((Object)(object)plugin != (Object)null && plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission Randomizer debug file written: " + text2));
		}
	}

	private static string SanitizeFilePart(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return "UNKNOWN";
		}
		StringBuilder stringBuilder = new StringBuilder(value.Length);
		foreach (char c in value)
		{
			if (char.IsLetterOrDigit(c) || c == '_' || c == '-')
			{
				stringBuilder.Append(c);
			}
			else
			{
				stringBuilder.Append('_');
			}
		}
		return stringBuilder.ToString();
	}

	private static ScrapDebugStats TeleportSpawnedScrapToLocalPlayer(StartOfRound start)
	{
		return CollectSpawnedScrapStats(start, teleportToPlayer: true);
	}

	private static ScrapDebugStats CollectSpawnedScrapStats(StartOfRound start, bool teleportToPlayer)
	{
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_026f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_034e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0353: Unknown result type (might be due to invalid IL or missing references)
		//IL_035c: Unknown result type (might be due to invalid IL or missing references)
		//IL_042a: Unknown result type (might be due to invalid IL or missing references)
		//IL_042f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0438: Unknown result type (might be due to invalid IL or missing references)
		//IL_039d: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0505: Unknown result type (might be due to invalid IL or missing references)
		//IL_050a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0513: Unknown result type (might be due to invalid IL or missing references)
		//IL_0479: Unknown result type (might be due to invalid IL or missing references)
		//IL_047e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0482: Unknown result type (might be due to invalid IL or missing references)
		//IL_0484: Unknown result type (might be due to invalid IL or missing references)
		//IL_0486: Unknown result type (might be due to invalid IL or missing references)
		ScrapDebugStats result = default(ScrapDebugStats);
		StringBuilder stringBuilder = new StringBuilder();
		StringBuilder stringBuilder2 = new StringBuilder();
		StringBuilder stringBuilder3 = new StringBuilder();
		StringBuilder stringBuilder4 = new StringBuilder();
		Component val = (((Object)(object)start != (Object)null) ? GetField<Component>(start, "localPlayerController", null) : null);
		Transform val2 = (((Object)(object)val != (Object)null) ? val.transform : null);
		if ((Object)(object)val2 == (Object)null && (Object)(object)Camera.main != (Object)null)
		{
			val2 = ((Component)Camera.main).transform;
		}
		if (teleportToPlayer && (Object)(object)val2 == (Object)null)
		{
			result.Error = "Could not find local player transform.";
			return result;
		}
		Vector3 val3 = (((Object)(object)val2 != (Object)null) ? (val2.position + val2.forward * 2f + Vector3.up * 0.75f) : Vector3.zero);
		Component[] array = Resources.FindObjectsOfTypeAll<Component>();
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = int.MaxValue;
		int num6 = int.MinValue;
		Component[] array2 = array;
		foreach (Component val4 in array2)
		{
			if ((Object)(object)val4 == (Object)null || (Object)(object)val4.gameObject == (Object)null)
			{
				continue;
			}
			GameObject gameObject = val4.gameObject;
			Scene scene = gameObject.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				continue;
			}
			scene = gameObject.scene;
			if (!((Scene)(ref scene)).isLoaded || !gameObject.activeInHierarchy || !IsScrapGrabbable(val4))
			{
				continue;
			}
			int @int = GetInt(val4, "scrapValue", 0);
			bool flag = GetBool(val4, "isHeld", fallback: false) || GetBool(val4, "isHeldByEnemy", fallback: false);
			bool inShip = GetBool(val4, "isInShipRoom", fallback: false) || GetBool(val4, "isInElevator", fallback: false);
			bool @bool = GetBool(val4, "isInFactory", fallback: false);
			object field = GetField<object>(val4, "itemProperties", null);
			bool flag2 = field != null && GetBool(field, "isScrap", fallback: false);
			string itemDisplayName = GetItemDisplayName(val4, field);
			bool flag3 = IsApparatusItem(val4, gameObject, itemDisplayName);
			bool flag4 = flag2 && !flag3;
			string path = GetPath(gameObject.transform);
			Vector3 position = val4.transform.position;
			num4++;
			result.RawValuedGrabbableCount++;
			result.RawValuedGrabbableValue += @int;
			int index = num4;
			string name = ((Object)gameObject).name;
			string name2 = ((object)val4).GetType().Name;
			scene = gameObject.scene;
			string value = BuildScrapLine(index, @int, itemDisplayName, name, name2, ((Scene)(ref scene)).name, position, flag2, flag3, flag, inShip, @bool, path);
			stringBuilder4.Append(value);
			if (flag4)
			{
				result.SpawnedScrapCount++;
				result.TotalScrapValue += @int;
				result.ItemPropertiesScrapCount++;
				result.ItemPropertiesScrapValue += @int;
				num5 = Mathf.Min(num5, @int);
				num6 = Mathf.Max(num6, @int);
				num++;
				int index2 = num;
				string name3 = ((Object)gameObject).name;
				string name4 = ((object)val4).GetType().Name;
				scene = gameObject.scene;
				stringBuilder.Append(BuildScrapLine(index2, @int, itemDisplayName, name3, name4, ((Scene)(ref scene)).name, position, flag2, flag3, flag, inShip, @bool, path));
				if (flag)
				{
					result.HeldOrSkippedCount++;
				}
				else if (teleportToPlayer)
				{
					Vector3 val5 = ScrapDebugOffset(result.TeleportedCount);
					TeleportScrapComponent(val4, val3 + val5);
					result.TeleportedCount++;
				}
			}
			else if (flag3 && flag2)
			{
				result.ApparatusCount++;
				result.ApparatusValue += @int;
				result.TrueScrapIncludingApparatusCount++;
				result.TrueScrapIncludingApparatusValue += @int;
				num2++;
				int index3 = num2;
				string name5 = ((Object)gameObject).name;
				string name6 = ((object)val4).GetType().Name;
				scene = gameObject.scene;
				stringBuilder2.Append(BuildScrapLine(index3, @int, itemDisplayName, name5, name6, ((Scene)(ref scene)).name, position, flag2, flag3, flag, inShip, @bool, path));
				if (flag)
				{
					result.HeldOrSkippedCount++;
				}
				else if (teleportToPlayer)
				{
					Vector3 val6 = ScrapDebugOffset(result.TeleportedCount);
					TeleportScrapComponent(val4, val3 + val6);
					result.TeleportedCount++;
				}
			}
			else
			{
				if (flag2)
				{
					result.TrueScrapIncludingApparatusCount++;
					result.TrueScrapIncludingApparatusValue += @int;
				}
				else
				{
					result.ValueOnlyCount++;
					result.ValueOnlyValue += @int;
				}
				num3++;
				int index4 = num3;
				string name7 = ((Object)gameObject).name;
				string name8 = ((object)val4).GetType().Name;
				scene = gameObject.scene;
				stringBuilder3.Append(BuildScrapLine(index4, @int, itemDisplayName, name7, name8, ((Scene)(ref scene)).name, position, flag2, flag3, flag, inShip, @bool, path));
			}
		}
		result.TrueScrapIncludingApparatusCount = result.SpawnedScrapCount + result.ApparatusCount;
		result.TrueScrapIncludingApparatusValue = result.TotalScrapValue + result.ApparatusValue;
		result.MinScrapValue = ((result.SpawnedScrapCount > 0) ? num5 : 0);
		result.MaxScrapValue = ((result.SpawnedScrapCount > 0) ? num6 : 0);
		result.ItemBreakdown = stringBuilder.ToString();
		result.ApparatusBreakdown = stringBuilder2.ToString();
		result.NonScrapBreakdown = stringBuilder3.ToString();
		result.RawBreakdown = stringBuilder4.ToString();
		return result;
	}

	private static void PreClampSpawnableScrapValueRanges(RoundManager round, string reason)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.PreClampScrapValuesBeforeGeneration.Value || !HasRoll || !IsServerOrNoNetwork() || IsCompanyRouteActive(round, TimeOfDay.Instance))
		{
			return;
		}
		SelectableLevel val = null;
		if ((Object)(object)round != (Object)null)
		{
			val = GetField<SelectableLevel>(round, "currentLevel", null);
		}
		if ((Object)(object)val == (Object)null && (Object)(object)TimeOfDay.Instance != (Object)null)
		{
			val = GetField<SelectableLevel>(TimeOfDay.Instance, "currentLevel", null);
		}
		if ((Object)(object)val == (Object)null)
		{
			val = GetStarterLevel();
		}
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		object field = GetField<object>(val, "spawnableScrap", null);
		if (!(field is IEnumerable enumerable))
		{
			return;
		}
		List<SpawnableItemValueFields> list = new List<SpawnableItemValueFields>();
		foreach (object item in enumerable)
		{
			if (item == null)
			{
				continue;
			}
			object obj = GetField<object>(item, "spawnableItem", null);
			if (obj == null)
			{
				obj = item;
			}
			if (obj == null)
			{
				continue;
			}
			FieldInfo fieldInfo = FindField(obj.GetType(), "minValue");
			FieldInfo fieldInfo2 = FindField(obj.GetType(), "maxValue");
			if (fieldInfo == null || fieldInfo2 == null)
			{
				continue;
			}
			int num = 0;
			int num2 = 0;
			try
			{
				num = Convert.ToInt32(fieldInfo.GetValue(obj));
			}
			catch
			{
				num = 0;
			}
			try
			{
				num2 = Convert.ToInt32(fieldInfo2.GetValue(obj));
			}
			catch
			{
				num2 = 0;
			}
			if (num <= 0 && num2 <= 0)
			{
				continue;
			}
			bool flag = false;
			for (int i = 0; i < temporaryScrapValueRanges.Count; i++)
			{
				if (temporaryScrapValueRanges[i].Item == obj)
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				temporaryScrapValueRanges.Add(new OriginalScrapItemValueRange
				{
					Item = obj,
					MinValue = num,
					MaxValue = num2
				});
			}
			list.Add(new SpawnableItemValueFields
			{
				Item = obj,
				MinField = fieldInfo,
				MaxField = fieldInfo2,
				OldMin = num,
				OldMax = num2,
				OldMid = Mathf.Max(1f, (float)(num + num2) * 0.5f)
			});
		}
		if (list.Count == 0)
		{
			return;
		}
		float num3 = 0f;
		for (int j = 0; j < list.Count; j++)
		{
			num3 += list[j].OldMid;
		}
		num3 = Mathf.Max(1f, num3 / (float)list.Count);
		int num4 = Mathf.Max(1, Mathf.RoundToInt((float)(CurrentRoll.MinScrap + CurrentRoll.MaxScrap) * 0.5f));
		float num5 = Mathf.Max(1f, (float)CurrentRoll.MaxTotalScrapValue / (float)num4);
		float num6 = Mathf.Clamp(num5 / num3, 0.02f, 2f);
		int num7 = 0;
		for (int k = 0; k < list.Count; k++)
		{
			SpawnableItemValueFields spawnableItemValueFields = list[k];
			int num8 = Mathf.Max(1, Mathf.RoundToInt((float)spawnableItemValueFields.OldMin * num6));
			int num9 = Mathf.Max(num8, Mathf.RoundToInt((float)spawnableItemValueFields.OldMax * num6));
			int num10 = Mathf.Max(num8, Mathf.CeilToInt(num5 * 3f));
			num9 = Mathf.Min(num9, num10);
			num8 = Mathf.Min(num8, num9);
			try
			{
				spawnableItemValueFields.MinField.SetValue(spawnableItemValueFields.Item, num8);
			}
			catch
			{
			}
			try
			{
				spawnableItemValueFields.MaxField.SetValue(spawnableItemValueFields.Item, num9);
			}
			catch
			{
			}
			num7++;
		}
		if (num7 > 0 && plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission pre-scaled scrap item value ranges before generation (" + reason + "): " + num7 + " spawnable entries. Expected count " + num4 + ", target average $" + Mathf.RoundToInt(num5) + ", scale " + num6.ToString("0.000") + "."));
		}
	}

	private static void RestoreTemporaryScrapValueRanges(string reason, bool writeLog)
	{
		if (temporaryScrapValueRanges.Count == 0)
		{
			return;
		}
		int num = 0;
		for (int i = 0; i < temporaryScrapValueRanges.Count; i++)
		{
			OriginalScrapItemValueRange originalScrapItemValueRange = temporaryScrapValueRanges[i];
			if (originalScrapItemValueRange == null || originalScrapItemValueRange.Item == null)
			{
				continue;
			}
			try
			{
				FieldInfo fieldInfo = FindField(originalScrapItemValueRange.Item.GetType(), "minValue");
				FieldInfo fieldInfo2 = FindField(originalScrapItemValueRange.Item.GetType(), "maxValue");
				if (fieldInfo != null)
				{
					fieldInfo.SetValue(originalScrapItemValueRange.Item, originalScrapItemValueRange.MinValue);
				}
				if (fieldInfo2 != null)
				{
					fieldInfo2.SetValue(originalScrapItemValueRange.Item, originalScrapItemValueRange.MaxValue);
				}
				num++;
			}
			catch
			{
			}
		}
		temporaryScrapValueRanges.Clear();
		if (writeLog && (Object)(object)plugin != (Object)null && plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission restored temporary scrap item value ranges (" + reason + "): " + num + " items restored."));
		}
	}

	private static void NormalizeSpawnedScrapForCurrentRoll(string reason, bool writeLog)
	{
		if ((Object)(object)plugin == (Object)null || !plugin.ModEnabled.Value || !HasRoll || !IsServerOrNoNetwork() || IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance) || (!plugin.NormalizeGeneratedScrapValue.Value && !plugin.EnforceGeneratedScrapCount.Value))
		{
			return;
		}
		RoundManager instance = RoundManager.Instance;
		List<ScrapComponentRecord> list = CollectMissionScrapRecords(includeHeld: false);
		int count = list.Count;
		int num = SumScrapRecordValues(list);
		int num2 = 0;
		int num3 = 0;
		int num4 = Mathf.Min(CurrentRoll.MaxScrap, GetConfiguredMaxMissionScrapCount());
		if (plugin.EnforceGeneratedScrapCount.Value && list.Count > num4)
		{
			list.Sort((ScrapComponentRecord a, ScrapComponentRecord b) => b.Value.CompareTo(a.Value));
			int num5 = list.Count - num4;
			for (int i = 0; i < num5; i++)
			{
				ScrapComponentRecord scrapComponentRecord = list[i];
				num2++;
				num3 += scrapComponentRecord.Value;
				DespawnOrDestroyScrap(scrapComponentRecord.Component);
			}
			list.RemoveRange(0, num5);
		}
		int minimumTotalScrapValueForRoll = GetMinimumTotalScrapValueForRoll(CurrentRoll);
		int num6 = Mathf.Max(minimumTotalScrapValueForRoll, CurrentRoll.MaxTotalScrapValue);
		int num7 = SumScrapRecordValues(list);
		bool flag = false;
		if (plugin.NormalizeGeneratedScrapValue.Value && list.Count > 0 && (num7 < minimumTotalScrapValueForRoll || num7 > num6))
		{
			int targetValue = ChooseScrapTotalValueWithinRange(CurrentRoll, num7, list.Count);
			NormalizeScrapRecordValues(list, targetValue);
			num7 = SumScrapRecordValues(list);
			flag = true;
		}
		if ((Object)(object)instance != (Object)null)
		{
			SetField(instance, "totalScrapValueInLevel", (float)num7);
		}
		BroadcastScrapValueSync(list, num7, reason);
		if (writeLog && plugin.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mission scrap value range check (" + reason + "): original " + count + " / $" + num + ", removed " + num2 + " / $" + num3 + ", final " + list.Count + " / $" + num7 + ", allowed count <= " + num4 + ", allowed value range $" + minimumTotalScrapValueForRoll + "-$" + num6 + ", adjustedValue=" + flag + "."));
		}
	}

	private static void HandleCleaningCompanyGrabbableStart(Component component)
	{
		if (!CleaningCompanyEnabled() || (Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null || !IsServerOrNoNetwork() || IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance) || !IsScrapGrabbable(component))
		{
			return;
		}
		object field = GetField<object>(component, "itemProperties", null);
		if (field == null || !GetBool(field, "isScrap", fallback: false))
		{
			return;
		}
		string name = ((object)component).GetType().Name;
		string itemDisplayName = GetItemDisplayName(component, field);
		string objectName = ((Object)component.gameObject).name ?? string.Empty;
		if (IsApparatusItem(component, component.gameObject, itemDisplayName))
		{
			return;
		}
		TryApplyCleaningCompanyConvertedValue(component, "new scrap appeared");
		string messType;
		string outputType;
		bool flag = TryGetCleaningCompanyMessConversion(name, itemDisplayName, objectName, out messType, out outputType);
		bool flag2 = IsCleaningCompanyOutputType(name, itemDisplayName, objectName);
		if (flag || flag2)
		{
			CleaningMessValueTracker cleaningMessValueTracker = component.gameObject.GetComponent<CleaningMessValueTracker>();
			if ((Object)(object)cleaningMessValueTracker == (Object)null)
			{
				cleaningMessValueTracker = component.gameObject.AddComponent<CleaningMessValueTracker>();
			}
			if (flag)
			{
				cleaningMessValueTracker.Initialize(component, messType, outputType, tracksCleaningSource: true);
			}
			else
			{
				cleaningMessValueTracker.Initialize(component, GetCleaningCompanyOutputType(name, itemDisplayName, objectName), string.Empty, tracksCleaningSource: false);
			}
		}
	}

	private static bool CleaningCompanyEnabled()
	{
		return (Object)(object)plugin != (Object)null && plugin.ModEnabled.Value && plugin.CleaningCompanySupport != null && plugin.CleaningCompanySupport.Value;
	}

	internal static void NotifyCleaningCompanyMessGone(CleaningMessValueTracker tracker, string reason)
	{
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: 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_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		if (!CleaningCompanyEnabled() || (Object)(object)tracker == (Object)null || !IsServerOrNoNetwork() || IsCompanyRouteActive(RoundManager.Instance, TimeOfDay.Instance))
		{
			return;
		}
		Component scrapComponent = tracker.ScrapComponent;
		int num = Mathf.Max(1, tracker.LastKnownValue);
		if ((Object)(object)scrapComponent != (Object)null)
		{
			num = Mathf.Max(1, GetInt(scrapComponent, "scrapValue", num));
		}
		Vector3 position = tracker.LastKnownPosition;
		try
		{
			if ((Object)(object)((Component)tracker).transform != (Object)null)
			{
				position = ((Component)tracker).transform.position;
			}
		}
		catch
		{
		}
		CleaningConversionRecord cleaningConversionRecord = new CleaningConversionRecord
		{
			MessType = (string.IsNullOrEmpty(tracker.MessType) ? "scrap" : tracker.MessType),
			OutputType = (string.IsNullOrEmpty(tracker.OutputType) ? "*" : tracker.OutputType),
			Value = num,
			Position = position,
			CreatedAt = Time.realtimeSinceStartup,
			SourceInstanceId = tracker.SourceInstanceId,
			SourceObjectName = (tracker.SourceObjectName ?? string.Empty)
		};
		pendingCleaningConversions.Add(cleaningConversionRecord);
		CleanupExpiredCleaningCompanyConversions();
		TryApplyCleaningCompanyQueuedValuesToExistingOutputs("scrap disappeared " + reason);
		if (ShouldDebugLog())
		{
			Plugin.Log.LogInfo((object)("Cleaning Company value queued: " + cleaningConversionRecord.MessType + " -> " + cleaningConversionRecord.OutputType + " value=$" + cleaningConversionRecord.Value + " reason=" + reason + "."));
		}
	}

	private static void CleanupExpiredCleaningCompanyConversions()
	{
		if (pendingCleaningConversions.Count == 0 || Time.realtimeSinceStartup < nextCleaningConversionCleanupTime)
		{
			return;
		}
		nextCleaningConversionCleanupTime = Time.realtimeSinceStartup + 0.5f;
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		float num = (((Object)(object)plugin != (Object)null && plugin.CleaningCompanyConversionWindowSeconds != null) ? Mathf.Max(0.25f, plugin.CleaningCompanyConversionWindowSeconds.Value) : 4f);
		for (int num2 = pendingCleaningConversions.Count - 1; num2 >= 0; num2--)
		{
			if (realtimeSinceStartup - pendingCleaningConversions[num2].CreatedAt > num)
			{
				pendingCleaningConversions.RemoveAt(num2);
			}
		}
	}

	private static void TryApplyCleaningCompanyQueuedValuesToExistingOutputs(string reason)
	{
		if (!CleaningCompanyEnabled() || pendingCleaningConversions.Count == 0)
		{
			return;
		}
		GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
		for (int i = 0; i < array.Length; i++)
		{
			if (pendingCleaningConversions.Count == 0)
			{
				break;
			}
			Component val = (Component)(object)array[i];
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val.gameObject == (Object)null) && IsRecentlyStartedCleaningTrackedScrap(val))
			{
				TryApplyCleaningCompanyConvertedValue(val, reason);
			}
		}
	}

	private static bool TryApplyCleaningCompanyConvertedValue(Component outputComponent, string reason)
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		if (!CleaningCompanyEnabled() || (Object)(object)outputComponent == (Object)null || (Object)(object)outputComponent.gameObject == (Object)null || pendingCleaningConversions.Count == 0)
		{
			return false;
		}
		if (!IsScrapGrabbable(outputComponent))
		{
			return false;
		}
		object field = GetField<object>(outputComponent, "itemProperties", null);
		if (field == null || !GetBool(field, "isScrap", fallback: false))
		{
			return false;
		}
		string name = ((object)outputComponent).GetType().Name;
		string itemDisplayName = GetItemDisplayName(outputComponent, field);
		string objectName = ((Object)outputComponent.gameObject).name ?? string.Empty;
		if (IsApparatusItem(outputComponent, outputComponent.gameObject, itemDisplayName))
		{
			return false;
		}
		Vector3 position = outputComponent.transform.position;
		float num = (((Object)(object)plugin != (Object)null && plugin.CleaningCompanyConversionSearchRadius != null) ? Mathf.Max(0.25f, plugin.CleaningCompanyConversionSearchRadius.Value) : 6f);
		float num2 = num * num;
		int instanceID = ((Object)outputComponent.gameObject).GetInstanceID();
		int num3 = -1;
		float num4 = float.MaxValue;
		for (int i = 0; i < pendingCleaningConversions.Count; i++)
		{
			CleaningConversionRecord cleaningConversionRecord = pendingCleaningConversions[i];
			if (cleaningConversionRecord.SourceInstanceId != instanceID && CleaningCompanyOutputMatches(cleaningConversionRecord.OutputType, name, itemDisplayName, objectName))
			{
				Vector3 val = cleaningConversionRecord.Position - position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude <= num2 && sqrMagnitude < num4)
				{
					num4 = sqrMagnitude;
					num3 = i;
				}
			}
		}
		if (num3 < 0)
		{
			return false;
		}
		CleaningConversionRecord cleaningConversionRecord2 = pendingCleaningConversions[num3];
		pendingCleaningConversions.RemoveAt(num3);
		int num5 = Mathf.Max(1, cleaningConversionRecord2.Value);
		SetScrapValue(outputComponent, num5);
		RoundManager instance = RoundManager.Instance;
		int num6 = (((Object)(object)instance != (Object)null) ? Mathf.RoundToInt(GetField(instance, "totalScrapValueInLevel", 0f)) : 0);
		if (num6 <= 0)
		{
			num6 = num5;
		}
		List<ScrapComponentRecord> list = new List<ScrapComponentRecord>();
		list.Add(new ScrapComponentRecord
		{
			Component = outputComponent,
			GameObject = outputComponent.gameObject,
			Value = num5,
			ItemName = itemDisplayName,
			SyncIndex = -1
		});
		BroadcastScrapValueSync(list, num6, "Cleaning Company nearby conversion " + reason);
		CleaningMessValueTracker component = outputComponent.gameObject.GetComponent<CleaningMessValueTracker>();
		if ((Object)(object)component != (Object)null)
		{
			component.LastKnownValue = num5;
		}
		if (ShouldDebugLog())
		{
			Plugin.Log.LogInfo((object)("Cleaning Company preserved nearby value: " + cleaningConversionRecord2.MessType + " -> " + BuildCleaningCompanyScrapToken(name, itemDisplayName, objectName) + " value=$" + num5 + " reason=" + reason + "."));
		}
		return true;
	}

	private static bool IsRecentlyStartedCleaningTrackedScrap(Component component)
	{
		if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null)
		{
			return false;
		}
		CleaningMessValueTracker component2 = component.gameObject.GetComponent<CleaningMessValueTracker>();
		if ((Object)(object)component2 == (Object)null)
		{
			return false;
		}
		float num = (((Object)(object)plugin != (Object)null && plugin.CleaningCompanyConversionWindowSeconds != null) ? Mathf.Max(0.25f, plugin.CleaningCompanyConversionWindowSeconds.Value) : 4f);
		return Time.realtimeSinceStartup - component2.CreatedAt <= num;
	}

	private static bool CleaningCompanyOutputMatches(string expectedOutputType, string typeName, string itemName, string objectName)
	{
		if (string.IsNullOrEmpty(expectedOutputType))
		{
			return false;
		}
		string cleaningCompanyOutputType = GetCleaningCompanyOutputType(typeName, itemName, objectName);
		return !string.IsNullOrEmpty(cleaningCompanyOutputType) && string.Equals(cleaningCompanyOutputType, expectedOutputType, StringComparison.OrdinalIgnoreCase);
	}

	private static bool TryGetCleaningCompanyMessConversion(string typeName, string itemName, string objectName, out string messType, out string outputType)
	{
		string token = BuildCleaningCompanyScrapToken(typeName, itemName, objectName);
		messType = string.Empty;
		outputType = string.Empty;
		if (TokenMatchesAnyCleaningName(token, "Scav Goop"))
		{
			messType = "Scav Goop";
			outputType = "Dirty Water";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Thumper Drool"))
		{
			messType = "Thumper Drool";
			outputType = "Dirty Water";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Bone Pile"))
		{
			messType = "Bone Pile";
			outputType = "Full Garbage Bag";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Nail Pile Item"))
		{
			messType = "Nail Pile Item";
			outputType = "Full Garbage Bag";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "BrackenDustItem"))
		{
			messType = "BrackenDustItem";
			outputType = "Full Garbage Bag";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Ethereal Essence"))
		{
			messType = "Ethereal Essence";
			outputType = "Full Garbage Bag";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Hoarding Bug Eggs"))
		{
			messType = "Hoarding Bug Eggs";
			outputType = "Full Garbage Bag";
			return true;
		}
		if (TokenMatchesAnyCleaningName(token, "Spore Pile"))
		{
			messType = "Spore Pile";
			outputType = "Full Garbage Bag";
			return true;
		}
		return false;
	}

	private static bool IsCleaningCompanyOutputType(string typeName, string itemName, string objectName)
	{
		string cleaningCompanyOutputType = GetCleaningCompanyOutputType(typeName, itemName, objectName);
		return !string.IsNullOrEmpty(cleaningCompanyOutputType);
	}

	private static string GetCleaningCompanyOutputType(string typeName, string itemName, string objectName)
	{
		string token = BuildCleaningCompanyScrapToken(typeName, itemName, objectName);
		if (TokenMatchesAnyCleaningName(token, "Dirty Water"))
		{
			return "Dirty Water";
		}
		if (TokenMatchesAnyCleaningName(token, "Full Garbage Bag"))
		{
			return "Full Garbage Bag";
		}
		return string.Empty;
	}

	private static string BuildCleaningCompanyScrapToken(string typeName, string itemName, string objectName)
	{
		string text = ((typeName ?? string.Empty) + " " + (itemName ?? string.Empty) + " " + (objectName ?? string.Empty)).Trim();
		return string.IsNullOrEmpty(text) ? "scrap" : text;
	}

	private static bool TokenMatchesAnyCleaningName(string token, params string[] names)
	{
		for (int i = 0; i < names.Length; i++)
		{
			if (TokenMatchesCleaningName(token, names[i]))
			{
				return true;
			}
		}
		return false;
	}

	private static bool TokenMatchesCleaningName(string token, string name)
	{
		if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(name))
		{
			return false;
		}
		string text = NormalizeCleaningName(token);
		string value = NormalizeCleaningName(name);
		return text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string NormalizeCleaningName(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		StringBuilder stringBuilder = new StringBuilder(value.Length);
		foreach (char c in value)
		{
			if (char.IsLetterOrDigit(c))
			{
				stringBuilder.Append(char.ToLowerInvariant(c));
			}
		}
		return stringBuilder.ToString();
	}

	private static List<ScrapComponentRecord> CollectMissionScrapRecords(bool includeHeld)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		List<ScrapComponentRecord> list = new List<ScrapComponentRecord>();
		Component[] array = Resources.FindObjectsOfTypeAll<Component>();
		Component[] array2 = array;
		foreach (Component val in array2)
		{
			if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null)
			{
				continue;
			}
			GameObject gameObject = val.gameObject;
			Scene scene = gameObject.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				continue;
			}
			scene = gameObject.scene;
			if (!((Scene)(ref scene)).isLoaded || !gameObject.activeInHierarchy || !IsScrapGrabbable(val) || ((GetBool(val, "isHeld", fallback: false) || GetBool(val, "isHeldByEnemy", fallback: false)) && !includeHeld))
			{
				continue;
			}
			object field = GetField<object>(val, "itemProperties", null);
			if (field != null && GetBool(field, "isScrap", fallback: false))
			{
				string itemDisplayName = GetItemDisplayName(val, field);
				if (!IsApparatusItem(val, gameObject, itemDisplayName))
				{
					list.Add(new ScrapComponentRecord
					{
						Component = val,
						GameObject = gameObject,
						Value = GetInt(val, "scrapValue", 0),
						ItemName = itemDisplayName
					});
				}
			}
		}
		return list;
	}

	private static int SumScrapRecordValues(List<ScrapComponentRecord> records)
	{
		int num = 0;
		if (records == null)
		{
			return 0;
		}
		for (int i = 0; i < records.Count; i++)
		{
			if (records[i] != null && (Object)(object)records[i].Component != (Object)null)
			{
				num += Mathf.Max(0, GetInt(records[i].Component, "scrapValue", records[i].Value));
			}
		}
		return num;
	}

	private static void NormalizeScrapRecordValues(List<ScrapComponentRecord> records, int targetValue)
	{
		if (records == null || records.Count == 0)
		{
			return;
		}
		targetValue = Mathf.Max(records.Count, targetValue);
		int num = 0;
		for (int i = 0; i < records.Count; i++)
		{
			num += Mathf.Max(1, records[i].Value);
		}
		if (num <= 0)
		{
			num = records.Count;
		}
		int[] array = new int[records.Count];
		int num2 = 0;
		for (int j = 0; j < records.Count; j++)
		{
			float num3 = (float)Mathf.Max(1, records[j].Value) / (float)num * (float)targetValue;
			num2 += (array[j] = Mathf.Max(1, Mathf.FloorToInt(num3)));
		}
		int num4 = targetValue - num2;
		int num5 = 0;
		while (num4 != 0 && num5 < 100000)
		{
			int num6 = num5 % array.Length;
			if (num4 > 0)
			{
				array[num6]++;
				num4--;
			}
			else if (array[num6] > 1)
			{
				array[num6]--;
				num4++;
			}
			num5++;
		}
		for (int k = 0; k < records.Count; k++)
		{
			SetScrapValue(records[k].Component, array[k]);
			records[k].Value = array[k];
		}
	}

	private static void SetScrapValue(Component component, int value)
	{
		if ((Object)(object)component == (Object)null)
		{
			return;
		}
		value = Mathf.Max(0, value);
		try
		{
			MethodInfo method = ((object)component).GetType().GetMethod("SetScrapValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
			if (method != null)
			{
				method.Invoke(component, new object[1] { value });
			}
		}
		catch
		{
		}
		ForceScrapValueFields(component, value);
	}

	private static void ForceScrapValueFields(Component component, int value)
	{
		if ((Object)(object)component == (Object)null)
		{
			return;
		}
		value = Mathf.Max(0, value);
		try
		{
			SetField(component, "scrapValue", value);
		}
		catch
		{
		}
		try
		{
			ScanNodeProperties val = (((Object)(object)component.gameObject != (Object)null) ? component.gameObject.GetComponentInChildren<ScanNodeProperties>(true) : null);
			if ((Object)(object)val != (Object)null)
			{
				val.scrapValue = value;
				val.subText = "Value: $" + value;
			}
		}
		catch
		{
		}
	}

	private static void DespawnOrDestroyScrap(Component component)
	{
		if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null)
		{
			return;
		}
		try
		{
			NetworkObject component2 = component.GetComponent<NetworkObject>();
			if ((Object)(object)component2 != (Object)null && component2.IsSpawned && IsServerOrNoNetwork())
			{
				component2.Despawn(true);
				return;
			}
		}
		catch
		{
		}
		try
		{
			Object.Destroy((Object)(object)component.gameObject);
		}
		catch
		{
		}
	}

	private static string BuildScrapLine(int index, int scrapValue, string itemName, string objectName, string typeName, string sceneName, Vector3 pos, bool itemPropertiesSaysScrap, bool isApparatus, bool held, bool inShip, bool inFactory, string path)
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append("  ");
		stringBuilder.Append(index.ToString("000"));
		stringBuilder.Append(" | value=$");
		stringBuilder.Append(scrapValue);
		stringBuilder.Append(" | item='");
		stringBuilder.Append(itemName);
		stringBuilder.Append("' | object='");
		stringBuilder.Append(objectName);
		stringBuilder.Append("' | type=");
		stringBuilder.Append(typeName);
		stringBuilder.Append(" | scene=");
		stringBuilder.Append(sceneName);
		stringBuilder.Append(" | pos=");
		stringBuilder.Append(FormatVector(pos));
		stringBuilder.Append(" | itemProperties.isScrap=");
		stringBuilder.Append(itemPropertiesSaysScrap);
		stringBuilder.Append(" | apparatus=");
		stringBuilder.Append(isApparatus);
		stringBuilder.Append(" | held=");
		stringBuilder.Append(held);
		stringBuilder.Append(" | inShip=");
		stringBuilder.Append(inShip);
		stringBuilder.Append(" | inFactory=");
		stringBuilder.Append(inFactory);
		stringBuilder.Append(" | path=");
		stringBuilder.Append(path);
		stringBuilder.AppendLine();
		return stringBuilder.ToString();
	}

	private static bool IsApparatusItem(Component component, GameObject gameObject, string itemName)
	{
		string text = (((Object)(object)component != (Object)null) ? ((object)component).GetType().Name : string.Empty);
		string text2 = (((Object)(object)gameObject != (Object)null) ? ((Object)gameObject).name : string.Empty);
		if (!string.IsNullOrEmpty(text) && text.IndexOf("Lung", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return true;
		}
		if (!string.IsNullOrEmpty(text2) && text2.IndexOf("LungApparatus", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return true;
		}
		if (!string.IsNullOrEmpty(itemName) && itemName.IndexOf("Apparatus", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return true;
		}
		return false;
	}

	private static string GetItemDisplayName(Component component, object itemProperties)
	{
		string text = ((itemProperties != null) ? GetField<string>(itemProperties, "itemName", null) : null);
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text;
		}
		text = GetField<string>(component, "itemName", null);
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text;
		}
		return ((Object)(object)component != (Object)null && (Object)(object)component.gameObject != (Object)null) ? ((Object)component.gameObject).name : "unknown";
	}

	private static string FormatVector(Vector3 value)
	{
		return "(" + value.x.ToString("0.00") + ", " + value.y.ToString("0.00") + ", " + value.z.ToString("0.00") + ")";
	}

	private static Vector3 ScrapDebugOffset(int index)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		int num = index % 7;
		int num2 = index / 7 % 7;
		int num3 = index / 49;
		float num4 = (float)(num - 3) * 0.55f;
		float num5 = (float)(num2 - 3) * 0.55f;
		float num6 = (float)num3 * 0.2f;
		return new Vector3(num4, num6, num5);
	}

	private static void TeleportScrapComponent(Component component, Vector3 position)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = component.transform;
		transform.position = position;
		transform.rotation = Quaternion.identity;
		Rigidbody component2 = component.GetComponent<Rigidbody>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.velocity = Vector3.zero;
			component2.angularVelocity = Vector3.zero;
			component2.position = position;
		}
		SetField(component, "startFallingPosition", position);
		SetField(component, "targetFloorPosition", position);
		SetField(component, "floorYRot", transform.eulerAngles.y);
		SetField(component, "fallTime", 1f);
		SetField(component, "hasHitGround", true);
		SetField(component, "reachedFloorTarget", true);
		SetField(component, "isInElevator", false);
		SetField(component, "isInShipRoom", false);
		SetField(component, "isInFactory", true);
	}

	private static bool IsScrapGrabbable(Component component)
	{
		if ((Object)(object)component == (Object)null)
		{
			return false;
		}
		if (!TypeIsOrInherits(((object)component).GetType(), "GrabbableObject"))
		{
			return false;
		}
		object field = GetField<object>(component, "itemProperties", null);
		if (field != null && GetBool(field, "isScrap", fallback: false))
		{
			return true;
		}
		return GetInt(component, "scrapValue", 0) > 0;
	}

	private static bool TypeIsOrInherits(Type type, string typeName)
	{
		while (type != null)
		{
			if (string.Equals(type.Name, typeName, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			type = type.BaseType;
		}
		return false;
	}

	private static string BuildActualVsExpectedDebugReport(RoundManager round, TimeOfDay time, ScrapDebugStats scrapStats)
	{
		StringBuilder stringBuilder = new StringBuilder();
		MissionRoll currentRoll = CurrentRoll;
		SelectableLevel activeLevel = GetActiveLevel(round, time);
		stringBuilder.AppendLine("Mission Debug: actual mission scrap " + scrapStats.SpawnedScrapCount + " items / $" + scrapStats.TotalScrapValue + " excluding apparatus. Apparatus " + scrapStats.ApparatusCount + " / $" + scrapStats.ApparatusValue + ". Raw valued objects " + scrapStats.RawValuedGrabbableCount + " / $" + scrapStats.RawValuedGrabbableValue + ". Teleported true scrap " + scrapStats.TeleportedCount + ".");
		if (!string.IsNullOrEmpty(scrapStats.Error))
		{
			stringBuilder.AppendLine("Mission Debug error: " + scrapStats.Error);
		}
		if (HasRoll)
		{
			float num = (((Object)(object)round != (Object)null) ? GetFloat(round, "scrapAmountMultiplier", currentRoll.ScrapAmountMultiplier) : currentRoll.ScrapAmountMultiplier);
			int num2 = Mathf.RoundToInt((float)currentRoll.MinScrap * num);
			int num3 = Mathf.CeilToInt((float)currentRoll.MaxScrap * num);
			stringBuilder.AppendLine("Expected orbit roll: rank " + currentRoll.Rank + ", displayed scrap " + currentRoll.MinScrap + "-" + currentRoll.MaxScrap + ", effective if multiplier applies " + num2 + "-" + num3 + ", value range " + TotalScrapValueRangeText(currentRoll) + ", power " + currentRoll.MaxInsideEnemyPower + ", size " + currentRoll.FactorySizeMultiplier.ToString("0.00") + ", amount x" + num.ToString("0.00") + ", value x" + currentRoll.ScrapValueMultiplier.ToString("0.00") + ".");
		}
		else
		{
			stringBuilder.AppendLine("Expected orbit roll: no mission roll exists yet.");
		}
		if ((Object)(object)activeLevel != (Object)null)
		{
			string field = GetField(activeLevel, "riskLevel", "?");
			int @int = GetInt(activeLevel, "minScrap", -1);
			int int2 = GetInt(activeLevel, "maxScrap", -1);
			int int3 = GetInt(activeLevel, "maxTotalScrapValue", -1);
			int int4 = GetInt(activeLevel, "maxEnemyPowerCount", -1);
			int int5 = GetInt(activeLevel, "maxOutsideEnemyPowerCount", -1);
			int int6 = GetInt(activeLevel, "maxDaytimeEnemyPowerCount", -1);
			float @float = GetFloat(activeLevel, "factorySizeMultiplier", -1f);
			stringBuilder.AppendLine("Actual level values: risk " + field + ", scrap " + @int + "-" + int2 + ", max value $" + int3 + ", inside power " + int4 + ", outside/day " + int5 + "/" + int6 + ", size " + @float.ToString("0.00") + ".");
		}
		else
		{
			stringBuilder.AppendLine("Actual level values: no active SelectableLevel found.");
		}
		if ((Object)(object)round != (Object)null)
		{
			float float2 = GetFloat(round, "currentMaxInsidePower", -1f);
			float float3 = GetFloat(round, "currentMaxOutsidePower", -1f);
			int int7 = GetInt(round, "currentMaxInsideDiversityLevel", -1);
			int int8 = GetInt(round, "minEnemiesToSpawn", -1);
			int int9 = GetInt(round, "hourTimeBetweenEnemySpawnBatches", -1);
			float float4 = GetFloat(round, "mapSizeMultiplier", -1f);
			float float5 = GetFloat(round, "scrapAmountMultiplier", -1f);
			float float6 = GetFloat(round, "scrapValueMultiplier", -1f);
			stringBuilder.AppendLine("Actual round values: inside/outside power " + float2.ToString("0.##") + "/" + float3.ToString("0.##") + ", diversity " + int7 + ", min enemies " + int8 + ", batch hours " + int9 + ", size " + float4.ToString("0.00") + ", amount x" + float5.ToString("0.00") + ", value x" + float6.ToString("0.00") + ".");
		}
		return stringBuilder.ToString().Trim();
	}

	private static string BuildFullMissionDebugFileReport(string reason, RoundManager round, TimeOfDay time, ScrapDebugStats? scrapStatsNullable)
	{
		StringBuilder stringBuilder = new StringBuilder();
		SelectableLevel activeLevel = GetActiveLevel(round, time);
		stringBuilder.AppendLine("============================================================");
		stringBuilder.AppendLine("LC MISSION RANDOMIZER DEBUG FILE");
		stringBuilder.AppendLine("============================================================");
		stringBuilder.AppendLine("Reason: " + reason);
		stringBuilder.AppendLine("Local time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
		stringBuilder.AppendLine("UTC time: " + DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + " UTC");
		stringBuilder.AppendLine("Plugin version: 0.1.25");
		stringBuilder.AppendLine("Server/host authoritative side: " + IsServerOrNoNetwork());
		stringBuilder.AppendLine("Has mission roll: " + HasRoll);
		stringBuilder.AppendLine();
		AppendSceneList(stringBuilder);
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== EXPECTED ORBIT MISSION ROLL ===");
		if (HasRoll)
		{
			AppendExpectedRoll(stringBuilder, CurrentRoll);
		}
		else
		{
			stringBuilder.AppendLine("No mission roll exists yet.");
		}
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== VALUES THIS MOD TRIED TO APPLY ===");
		if (HasRoll)
		{
			AppendAttemptedChangedValues(stringBuilder, CurrentRoll);
		}
		else
		{
			stringBuilder.AppendLine("No attempted values. No roll exists yet.");
		}
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== ACTUAL SELECTABLE LEVEL VALUES NOW ===");
		AppendSelectableLevelValues(stringBuilder, activeLevel);
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== ACTUAL ROUND MANAGER VALUES NOW ===");
		AppendRoundManagerValues(stringBuilder, round);
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== ACTUAL TIME OF DAY VALUES NOW ===");
		AppendTimeOfDayValues(stringBuilder, time);
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("=== TERMINAL / CATALOGUE SNAPSHOT ===");
		AppendTerminalSnapshot(stringBuilder);
		stringBuilder.AppendLine();
		if (scrapStatsNullable.HasValue)
		{
			ScrapDebugStats value = scrapStatsNullable.Value;
			stringBuilder.AppendLine("=== SPAWNED SCRAP SNAPSHOT ===");
			stringBuilder.AppendLine("Raw valued grabbables found: " + value.RawValuedGrabbableCount + " / $" + value.RawValuedGrabbableValue);
			stringBuilder.AppendLine("Actual mission scrap only, excluding apparatus and non-scrap valued items: " + value.SpawnedScrapCount + " / $" + value.TotalScrapValue);
			stringBuilder.AppendLine("True scrap including apparatus: " + value.TrueScrapIncludingApparatusCount + " / $" + value.TrueScrapIncludingApparatusValue);
			stringBuilder.AppendLine("Apparatus only: " + value.ApparatusCount + " / $" + value.ApparatusValue);
			stringBuilder.AppendLine("Non-scrap valued grabbables, keys/manual/sticky notes/etc: " + value.ValueOnlyCount + " / $" + value.ValueOnlyValue);
			stringBuilder.AppendLine("Held/skipped count: " + value.HeldOrSkippedCount);
			stringBuilder.AppendLine("Teleported true scrap count: " + value.TeleportedCount);
			stringBuilder.AppendLine("Mission scrap min item value: $" + value.MinScrapValue);
			stringBuilder.AppendLine("Mission scrap max item value: $" + value.MaxScrapValue);
			if (!string.IsNullOrEmpty(value.Error))
			{
				stringBuilder.AppendLine("Error: " + value.Error);
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("--- ACTUAL LOADED MISSION SCRAP ITEMS ONLY ---");
			stringBuilder.Append(value.ItemBreakdown);
			if (string.IsNullOrWhiteSpace(value.ItemBreakdown))
			{
				stringBuilder.AppendLine("No actual mission scrap items were found.");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("--- APPARATUS ITEMS, LOGGED SEPARATELY ---");
			stringBuilder.Append(value.ApparatusBreakdown);
			if (string.IsNullOrWhiteSpace(value.ApparatusBreakdown))
			{
				stringBuilder.AppendLine("No apparatus item was found.");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("--- NON-SCRAP VALUED GRABBABLES, EXCLUDED FROM MISSION SCRAP TOTAL ---");
			stringBuilder.Append(value.NonScrapBreakdown);
			if (string.IsNullOrWhiteSpace(value.NonScrapBreakdown))
			{
				stringBuilder.AppendLine("No non-scrap valued grabbables were found.");
			}
			stringBuilder.AppendLine();
			if (HasRoll)
			{
				float num = (((Object)(object)round != (Object)null) ? GetFloat(round, "scrapAmountMultiplier", CurrentRoll.ScrapAmountMultiplier) : CurrentRoll.ScrapAmountMultiplier);
				int num2 = Mathf.RoundToInt((float)CurrentRoll.MinScrap * num);
				int num3 = Mathf.CeilToInt((float)CurrentRoll.MaxScrap * num);
				stringBuilder.AppendLine("=== EXPECTED VS ACTUAL QUICK CHECK ===");
				stringBuilder.AppendLine("Displayed/base scrap count range: " + CurrentRoll.MinScrap + " - " + CurrentRoll.MaxScrap);
				stringBuilder.AppendLine("RoundManager.scrapAmountMultiplier at snapshot: " + num.ToString("0.000"));
				stringBuilder.AppendLine("Effective generated range if the game applies that multiplier: " + num2 + " - " + num3);
				stringBuilder.AppendLine("Actual loaded mission scrap count, excluding apparatus: " + value.SpawnedScrapCount);
				stringBuilder.AppendLine("Base displayed count inside expected range: " + (value.SpawnedScrapCount >= CurrentRoll.MinScrap && value.SpawnedScrapCount <= CurrentRoll.MaxScrap));
				stringBuilder.AppendLine("Effective multiplied count inside expected range: " + (value.SpawnedScrapCount >= num2 && value.SpawnedScrapCount <= num3));
				stringBuilder.AppendLine("Expected total scrap value range target: " + TotalScrapValueRangeText(CurrentRoll));
				stringBuilder.AppendLine("Actual loaded mission scrap value, excluding apparatus: $" + value.TotalScrapValue);
				stringBuilder.AppendLine("Actual loaded mission scrap over max target by: $" + (value.TotalScrapValue - CurrentRoll.MaxTotalScrapValue));
				stringBuilder.AppendLine("Actual true scrap including apparatus over max target by: $" + (value.TrueScrapIncludingApparatusValue - CurrentRoll.MaxTotalScrapValue));
				stringBuilder.AppendLine();
			}
		}
		return stringBuilder.ToString();
	}

	private static void AppendSceneList(StringBuilder sb)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		sb.AppendLine("=== LOADED SCENES ===");
		for (int i = 0; i < SceneManager.sceneCount; i++)
		{
			Scene sceneAt = SceneManager.GetSceneAt(i);
			sb.AppendLine("Scene[" + i + "] name='" + ((Scene)(ref sceneAt)).name + "' path='" + ((Scene)(ref sceneAt)).path + "' loaded=" + ((Scene)(ref sceneAt)).isLoaded + " rootCount=" + ((Scene)(ref sceneAt)).rootCount);
		}
	}

	private static void AppendExpectedRoll(StringBuilder sb, MissionRoll r)
	{
		sb.AppendLine("Seed: " + r.Seed);
		sb.AppendLine("Quota: $" + r.QuotaFulfilled + " / $" + r.ProfitQuota + " remaining=$" + r.QuotaRemaining + " daysLeft=" + r.DaysLeft + " quotaNumber=" + r.QuotaNumber);
		sb.AppendLine("Rank: " + r.Rank + " rankIndex=" + r.RankIndex + " difficultyScore=" + r.DifficultyScore.ToString("0.000"));
		sb.AppendLine("Scrap count target: " + r.MinScrap + " - " + r.MaxScrap);
		sb.AppendLine("Total scrap value range target: " + TotalScrapValueRangeText(r));
		sb.AppendLine("Inside/outside/daytime enemy power: " + r.MaxInsideEnemyPower + " / " + r.MaxOutsideEnemyPower + " / " + r.MaxDaytimeEnemyPower);
		sb.AppendLine("Min enemies to spawn: " + r.MinEnemiesToSpawn);
		sb.AppendLine("Spawn batch hours: " + r.SpawnBatchHours);
		sb.AppendLine("Inside diversity: " + r.MaxInsideDiversity);
		sb.AppendLine("Factory size multiplier: " + r.FactorySizeMultiplier.ToString("0.000"));
		sb.AppendLine("RoundManager scrap amount multiplier: " + r.ScrapAmountMultiplier.ToString("0.000"));
		sb.AppendLine("RoundManager scrap value multiplier: " + r.ScrapValueMultiplier.ToString("0.000"));
	}

	private static void AppendAttemptedChangedValues(StringBuilder sb, MissionRoll r)
	{
		int minimumTotalScrapValueForRoll = GetMinimumTotalScrapValueForRoll(r);
		sb.AppendLine("SelectableLevel.PlanetName -> Mission Zone");
		sb.AppendLine("SelectableLevel.riskLevel -> " + r.Rank);
		sb.AppendLine("SelectableLevel.factorySizeMultiplier -> " + r.FactorySizeMultiplier.ToString("0.000"));
		sb.AppendLine("SelectableLevel.minScrap -> " + r.MinScrap);
		sb.AppendLine("SelectableLevel.maxScrap -> " + r.MaxScrap);
		sb.AppendLine("SelectableLevel.minTotalScrapValue -> " + minimumTotalScrapValueForRoll);
		sb.AppendLine("SelectableLevel.maxTotalScrapValue -> " + r.MaxTotalScrapValue);
		sb.AppendLine("SelectableLevel.maxEnemyPowerCount -> " + r.MaxInsideEnemyPower);
		sb.AppendLine("SelectableLevel.maxOutsideEnemyPowerCount -> " + r.MaxOutsideEnemyPower);
		sb.AppendLine("SelectableLevel.maxDaytimeEnemyPowerCount -> " + r.MaxDaytimeEnemyPower);
		sb.AppendLine("SelectableLevel.spawnEnemiesAndScrap -> true");
		sb.AppendLine("RoundManager.scrapValueMultiplier -> " + r.ScrapValueMultiplier.ToString("0.000"));
		sb.AppendLine("RoundManager.scrapAmountMultiplier -> " + r.ScrapAmountMultiplier.ToString("0.000"));
		sb.AppendLine("RoundManager.mapSizeMultiplier -> " + r.FactorySizeMultiplier.ToString("0.000"));
		sb.AppendLine("RoundManager.currentMaxInsidePower -> " + r.MaxInsideEnemyPower);
		sb.AppendLine("RoundManager.currentMaxOutsidePower -> " + r.MaxOutsideEnemyPower);
		sb.AppendLine("RoundManager.currentMaxInsideDiversityLevel -> " + r.MaxInsideDiversity);
		sb.AppendLine("RoundManager.currentMaxOutsideDiversityLevel -> " + ((!plugin.DisableOutsideAndDaytimePower.Value) ? r.MaxInsideDiversity : 0));
		sb.AppendLine("RoundManager.minEnemiesToSpawn -> " + r.MinEnemiesToSpawn);
		sb.AppendLine("RoundManager.minOutsideEnemiesToSpawn -> 0");
		sb.AppendLine("RoundManager.hourTimeBetweenEnemySpawnBatches -> " + r.SpawnBatchHours);
	}

	private static void AppendSelectableLevelValues(StringBuilder sb, SelectableLevel level)
	{
		if ((Object)(object)level == (Object)null)
		{
			sb.AppendLine("No active SelectableLevel found.");
			return;
		}
		sb.AppendLine("Unity name: " + ((Object)level).name);
		sb.AppendLine("PlanetName: " + GetField(level, "PlanetName", "?"));
		sb.AppendLine("sceneName: " + GetField(level, "sceneName", "?"));
		sb.Append