Decompiled source of SkipDropshipCompany v0.2.5

com.aoirint.SkipDropshipCompany.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using SkipDropshipCompany.Core.Handlers;
using SkipDropshipCompany.Core.Ports;
using SkipDropshipCompany.Core.State;
using SkipDropshipCompany.Core.UseCases;
using SkipDropshipCompany.Core.Validation;
using SkipDropshipCompany.Interop;
using SkipDropshipCompany.Interop.Game;
using SkipDropshipCompany.Interop.Game.Adapters;
using SkipDropshipCompany.Interop.Game.Patches;
using Unity.Mathematics;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.aoirint.SkipDropshipCompany")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.5.0")]
[assembly: AssemblyInformationalVersion("0.2.5+1ae3967a6ac254d67bc4a7301324beb2bda02882")]
[assembly: AssemblyProduct("SkipDropshipCompany")]
[assembly: AssemblyTitle("com.aoirint.SkipDropshipCompany")]
[assembly: AssemblyVersion("0.2.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SkipDropshipCompany
{
	internal sealed class PluginController
	{
		private readonly RoundCallbackHandler roundCallbackHandler;

		private readonly TerminalSyncGroupCreditsHandler terminalSyncGroupCreditsHandler;

		private PluginController(RoundCallbackHandler roundCallbackHandler, TerminalSyncGroupCreditsHandler terminalSyncGroupCreditsHandler)
		{
			this.roundCallbackHandler = roundCallbackHandler;
			this.terminalSyncGroupCreditsHandler = terminalSyncGroupCreditsHandler;
		}

		public static PluginController Create(IPluginConfig config, IPluginLogger logger, IValidationLogger validationLogger)
		{
			IGameInterop gameInterop = new GameInterop(logger);
			LandingHistoryStore landingHistoryStore = new LandingHistoryStore(logger);
			PreparedInstantPurchaseStore preparedInstantPurchaseStore = new PreparedInstantPurchaseStore();
			InstantPurchaseEligibilityUseCase eligibilityUseCase = new InstantPurchaseEligibilityUseCase(config, gameInterop, landingHistoryStore, logger, validationLogger);
			PrepareInstantPurchaseUseCase prepareInstantPurchaseUseCase = new PrepareInstantPurchaseUseCase(gameInterop, eligibilityUseCase, preparedInstantPurchaseStore, logger, validationLogger);
			SpawnPreparedInstantPurchasedItemsUseCase spawnPreparedInstantPurchasedItemsUseCase = new SpawnPreparedInstantPurchasedItemsUseCase(gameInterop, preparedInstantPurchaseStore, logger, validationLogger);
			validationLogger.Record(ValidationLogRecord.ControllerCreated());
			RecordLandingUseCase recordLandingUseCase = new RecordLandingUseCase(gameInterop, landingHistoryStore, logger, validationLogger);
			ClearLandingHistoryUseCase clearLandingHistoryUseCase = new ClearLandingHistoryUseCase(gameInterop, landingHistoryStore, logger, validationLogger);
			return new PluginController(new RoundCallbackHandler(gameInterop, recordLandingUseCase, clearLandingHistoryUseCase), new TerminalSyncGroupCreditsHandler(gameInterop, prepareInstantPurchaseUseCase, spawnPreparedInstantPurchasedItemsUseCase, logger, validationLogger));
		}

		public void HandleStartGame()
		{
			roundCallbackHandler.HandleStartGame();
		}

		public void HandleResetShip()
		{
			roundCallbackHandler.HandleResetShip();
		}

		public PrepareInstantPurchaseResult? HandleTerminalSyncGroupCreditsClientRpcPrefix()
		{
			return terminalSyncGroupCreditsHandler.HandlePrefix();
		}

		public void HandleTerminalSyncGroupCreditsClientRpcPostfix()
		{
			terminalSyncGroupCreditsHandler.HandlePostfix();
		}
	}
	[BepInPlugin("com.aoirint.SkipDropshipCompany", "SkipDropshipCompany", "0.2.5")]
	[BepInProcess("Lethal Company.exe")]
	public class SkipDropshipCompany : BaseUnityPlugin
	{
		private static PluginController? controller;

		internal static PluginController Controller => controller;

		private void Awake()
		{
			BepInExPluginLogger bepInExPluginLogger = new BepInExPluginLogger(((BaseUnityPlugin)this).Logger);
			BepInExPluginConfig bepInExPluginConfig = BepInExPluginConfig.Bind(((BaseUnityPlugin)this).Config);
			IValidationLogger validationLogger;
			if (!bepInExPluginConfig.ValidationLogging)
			{
				IValidationLogger instance = DisabledValidationLogger.Instance;
				validationLogger = instance;
			}
			else
			{
				IValidationLogger instance = new BepInExValidationLogger(bepInExPluginLogger, DateTime.UtcNow);
				validationLogger = instance;
			}
			IValidationLogger validationLogger2 = validationLogger;
			validationLogger2.Record(ValidationLogRecord.PluginLoaded("0.2.5", bepInExPluginConfig.ValidationLogging, bepInExPluginConfig.Enabled, bepInExPluginConfig.RequireReroutingOnFirstDay));
			controller = PluginController.Create(bepInExPluginConfig, bepInExPluginLogger, validationLogger2);
			HarmonyCallbackGuard.Configure(new HarmonyCallbackDiagnosticReporter(bepInExPluginLogger, validationLogger2));
			HarmonyPatchInstaller.Install();
			bepInExPluginLogger.LogInfo("Plugin SkipDropshipCompany v0.2.5 is loaded!");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "com.aoirint.SkipDropshipCompany";

		public const string PLUGIN_NAME = "SkipDropshipCompany";

		public const string PLUGIN_VERSION = "0.2.5";
	}
}
namespace SkipDropshipCompany.Interop
{
	internal sealed class BepInExPluginConfig : IPluginConfig
	{
		private readonly ConfigEntry<bool> enabledConfig;

		private readonly ConfigEntry<bool> requireReroutingOnFirstDayConfig;

		private readonly ConfigEntry<bool> validationLoggingConfig;

		public bool Enabled => enabledConfig.Value;

		public bool RequireReroutingOnFirstDay => requireReroutingOnFirstDayConfig.Value;

		public bool ValidationLogging => validationLoggingConfig.Value;

		private BepInExPluginConfig(ConfigEntry<bool> enabledConfig, ConfigEntry<bool> requireReroutingOnFirstDayConfig, ConfigEntry<bool> validationLoggingConfig)
		{
			this.enabledConfig = enabledConfig;
			this.requireReroutingOnFirstDayConfig = requireReroutingOnFirstDayConfig;
			this.validationLoggingConfig = validationLoggingConfig;
		}

		public static BepInExPluginConfig Bind(ConfigFile config)
		{
			ConfigEntry<bool> obj = config.Bind<bool>("General", "Enabled", true, "Set to false to disable this mod.");
			ConfigEntry<bool> val = config.Bind<bool>("General", "RequireReroutingOnFirstDay", false, "If true, rerouting to the company will be required to skip the dropship on the first day.");
			ConfigEntry<bool> val2 = config.Bind<bool>("Debug", "ValidationLogging", false, "Enable structured validation logs for release validation and troubleshooting.");
			return new BepInExPluginConfig(obj, val, val2);
		}
	}
	internal sealed class BepInExPluginLogger : IPluginLogger
	{
		private readonly ManualLogSource logger;

		public BepInExPluginLogger(ManualLogSource logger)
		{
			this.logger = logger;
		}

		public void LogDebug(string message)
		{
			logger.LogDebug((object)message);
		}

		public void LogInfo(string message)
		{
			logger.LogInfo((object)message);
		}

		public void LogError(string message)
		{
			logger.LogError((object)message);
		}
	}
	internal sealed class BepInExValidationLogger : IValidationLogger
	{
		private const int SchemaVersion = 1;

		private const string Prefix = "[SDC_VALIDATION] ";

		private readonly IPluginLogger logger;

		private readonly string runId;

		private int sequence;

		public BepInExValidationLogger(IPluginLogger logger, DateTime startupTimeUtc)
		{
			this.logger = logger;
			runId = CreateRunId(startupTimeUtc);
		}

		public void Record(ValidationLogRecord record)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["schema"] = 1,
				["ts"] = FormatTimestamp(DateTime.UtcNow),
				["run"] = runId,
				["seq"] = ++sequence,
				["event"] = record.EventName
			};
			if (record.Fields != null)
			{
				foreach (KeyValuePair<string, object> field in record.Fields)
				{
					dictionary[field.Key] = field.Value;
				}
			}
			logger.LogInfo("[SDC_VALIDATION] " + JsonConvert.SerializeObject((object)dictionary, (Formatting)0));
		}

		private static string CreateRunId(DateTime startupTimeUtc)
		{
			string text = startupTimeUtc.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
			string text2 = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture).Substring(0, 6);
			return text + "-" + text2;
		}

		private static string FormatTimestamp(DateTime timestampUtc)
		{
			return timestampUtc.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
		}
	}
}
namespace SkipDropshipCompany.Interop.Game
{
	internal sealed class GameInterop : IGameInterop
	{
		private readonly NetworkAdapter networkAdapter;

		private readonly RoundAdapter roundAdapter;

		private readonly TerminalAdapter terminalAdapter;

		private readonly ItemSpawnAdapter itemSpawnAdapter;

		public GameInterop(IPluginLogger logger)
		{
			networkAdapter = new NetworkAdapter(logger);
			roundAdapter = new RoundAdapter(logger);
			terminalAdapter = new TerminalAdapter(logger);
			itemSpawnAdapter = new ItemSpawnAdapter(logger);
		}

		public bool IsServer()
		{
			return networkAdapter.IsServer();
		}

		public RoundState GetRoundState()
		{
			return roundAdapter.GetRoundState();
		}

		public string? GetCurrentLevelSceneName()
		{
			return roundAdapter.GetCurrentLevelSceneName();
		}

		public List<int>? GetTerminalOrderedItemIndexes()
		{
			return terminalAdapter.GetOrderedItemIndexes();
		}

		public bool SetTerminalOrderedItemIndexes(List<int> boughtItemIndexes)
		{
			return terminalAdapter.SetOrderedItemIndexes(boughtItemIndexes);
		}

		public bool SpawnBuyableItemInShip(int buyableItemIndex)
		{
			Item buyableItemByIndex = terminalAdapter.GetBuyableItemByIndex(buyableItemIndex);
			if ((Object)(object)buyableItemByIndex == (Object)null)
			{
				return false;
			}
			return itemSpawnAdapter.SpawnItemInShip(buyableItemByIndex);
		}
	}
}
namespace SkipDropshipCompany.Interop.Game.Patches
{
	internal sealed class HarmonyCallbackDiagnosticReporter
	{
		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public HarmonyCallbackDiagnosticReporter(IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public void RecordCallbackException(string callback, Exception exception)
		{
			string text = exception.GetType().FullName ?? exception.GetType().Name;
			logger.LogError("Harmony callback exception: callback=" + callback + ", exception_type=" + text);
			validationLogger.Record(ValidationLogRecord.CallbackException(callback, text));
		}
	}
	internal static class HarmonyCallbackGuard
	{
		private static HarmonyCallbackDiagnosticReporter? diagnosticReporter;

		public static void Configure(HarmonyCallbackDiagnosticReporter reporter)
		{
			diagnosticReporter = reporter;
		}

		public static bool TryNotifyHarmonyCallback(string callback, Action notify)
		{
			try
			{
				notify();
				return true;
			}
			catch (Exception exception)
			{
				TryRecordCallbackException(callback, exception);
				return false;
			}
		}

		public static bool TryNotifyHarmonyCallback<T>(string callback, Func<T> notify, out T? result)
		{
			try
			{
				result = notify();
				return true;
			}
			catch (Exception exception)
			{
				result = default(T);
				TryRecordCallbackException(callback, exception);
				return false;
			}
		}

		private static void TryRecordCallbackException(string callback, Exception exception)
		{
			try
			{
				diagnosticReporter?.RecordCallbackException(callback, exception);
			}
			catch
			{
			}
		}
	}
	internal static class HarmonyCallbackTokens
	{
		public const string TerminalSyncGroupCreditsClientRpcPrefix = "terminal_sync_group_credits_client_rpc_prefix";

		public const string TerminalSyncGroupCreditsClientRpcPostfix = "terminal_sync_group_credits_client_rpc_postfix";

		public const string StartOfRoundStartGamePostfix = "start_of_round_start_game_postfix";

		public const string StartOfRoundResetShipPostfix = "start_of_round_reset_ship_postfix";
	}
	internal static class HarmonyPatchInstaller
	{
		private static readonly Harmony harmony = new Harmony("com.aoirint.SkipDropshipCompany");

		public static void Install()
		{
			harmony.PatchAll(typeof(HarmonyPatchInstaller).Assembly);
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatch
	{
		[HarmonyPatch("StartGame")]
		[HarmonyPostfix]
		public static void StartGamePostfix()
		{
			HarmonyCallbackGuard.TryNotifyHarmonyCallback("start_of_round_start_game_postfix", delegate
			{
				SkipDropshipCompany.Controller.HandleStartGame();
			});
		}

		[HarmonyPatch("ResetShip")]
		[HarmonyPostfix]
		public static void ResetShipPostfix()
		{
			HarmonyCallbackGuard.TryNotifyHarmonyCallback("start_of_round_reset_ship_postfix", delegate
			{
				SkipDropshipCompany.Controller.HandleResetShip();
			});
		}
	}
	[HarmonyPatch(typeof(Terminal))]
	internal static class TerminalPatch
	{
		[HarmonyPatch("SyncGroupCreditsClientRpc")]
		[HarmonyPrefix]
		public static void SyncGroupCreditsClientRpcPrefix(Terminal __instance, int newGroupCredits, ref int numItemsInShip)
		{
			if (HarmonyCallbackGuard.TryNotifyHarmonyCallback("terminal_sync_group_credits_client_rpc_prefix", () => SkipDropshipCompany.Controller.HandleTerminalSyncGroupCreditsClientRpcPrefix(), out PrepareInstantPurchaseResult result) && result != null)
			{
				numItemsInShip = result.DropShipBoughtItemIndexes.Count;
			}
		}

		[HarmonyPatch("SyncGroupCreditsClientRpc")]
		[HarmonyPostfix]
		public static void SyncGroupCreditsClientRpcPostfix()
		{
			HarmonyCallbackGuard.TryNotifyHarmonyCallback("terminal_sync_group_credits_client_rpc_postfix", delegate
			{
				SkipDropshipCompany.Controller.HandleTerminalSyncGroupCreditsClientRpcPostfix();
			});
		}
	}
}
namespace SkipDropshipCompany.Interop.Game.Adapters
{
	internal sealed class ItemSpawnAdapter
	{
		private readonly IPluginLogger logger;

		private readonly Dictionary<int, float> cachedSpawnOffsetXByItemId = new Dictionary<int, float>();

		public ItemSpawnAdapter(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public bool SpawnItemInShip(Item item)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				logger.LogError("StartOfRound.Instance is null.");
				return false;
			}
			GameObject spawnPrefab = item.spawnPrefab;
			if ((Object)(object)spawnPrefab == (Object)null)
			{
				logger.LogError("Item.spawnPrefab is null.");
				return false;
			}
			Transform elevatorTransform = instance.elevatorTransform;
			if ((Object)(object)elevatorTransform == (Object)null)
			{
				logger.LogError("StartOfRound.Instance.elevatorTransform is null.");
				return false;
			}
			Vector3? baseSpawnPosition = GetBaseSpawnPosition(instance);
			if (!baseSpawnPosition.HasValue)
			{
				logger.LogError("Failed to get spawn position.");
				return false;
			}
			Vector3 value = baseSpawnPosition.Value;
			float spawnOffsetXByItemId = GetSpawnOffsetXByItemId(item.itemId);
			Vector3 val = value + new Vector3(spawnOffsetXByItemId, 0.5f, 1f);
			GameObject val2 = Object.Instantiate<GameObject>(spawnPrefab, val, Quaternion.identity, elevatorTransform);
			GrabbableObject component = val2.GetComponent<GrabbableObject>();
			if ((Object)(object)component == (Object)null)
			{
				logger.LogError("Failed to get GrabbableObject component from spawned item.");
				Object.Destroy((Object)(object)val2);
				return false;
			}
			component.fallTime = 0f;
			component.isInElevator = true;
			component.isInShipRoom = true;
			component.hasHitGround = true;
			NetworkObject component2 = val2.GetComponent<NetworkObject>();
			if ((Object)(object)component2 == (Object)null)
			{
				logger.LogError("Failed to get NetworkObject component from spawned item.");
				Object.Destroy((Object)(object)val2);
				return false;
			}
			component2.Spawn(false);
			return true;
		}

		private Vector3? GetBaseSpawnPosition(StartOfRound startOfRound)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Transform[] playerSpawnPositions = startOfRound.playerSpawnPositions;
			if (playerSpawnPositions == null)
			{
				logger.LogError("StartOfRound.Instance.playerSpawnPositions is null.");
				return null;
			}
			Transform val = playerSpawnPositions.ElementAtOrDefault(1);
			if ((Object)(object)val == (Object)null)
			{
				logger.LogError("Player spawn position is null for ID 1.");
				return null;
			}
			return val.position;
		}

		private float GetSpawnOffsetXByItemId(int itemId)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (cachedSpawnOffsetXByItemId.TryGetValue(itemId, out var value))
			{
				return value;
			}
			uint num = math.hash(new uint4((uint)itemId, 3735928559u, 305419896u, 2271560481u));
			if (num == 0)
			{
				num = 1u;
			}
			Random val = default(Random);
			((Random)(ref val))..ctor(num);
			float num2 = ((Random)(ref val)).NextFloat(-0.7f, 0.7f);
			cachedSpawnOffsetXByItemId[itemId] = num2;
			logger.LogDebug($"Generated offset X for an item ID. itemId={itemId}, offsetX={num2}");
			return num2;
		}
	}
	internal sealed class NetworkAdapter
	{
		private readonly IPluginLogger logger;

		public NetworkAdapter(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public bool IsServer()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null)
			{
				logger.LogError("NetworkManager.Singleton is null.");
				return false;
			}
			return singleton.IsServer;
		}
	}
	internal sealed class RoundAdapter
	{
		private readonly IPluginLogger logger;

		public RoundAdapter(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public RoundState GetRoundState()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				logger.LogError("StartOfRound.Instance is null.");
				return new RoundState(isInOrbit: false, isFirstDay: false, isRoutingToCompany: false);
			}
			bool inShipPhase = instance.inShipPhase;
			bool isFirstDay = IsFirstDay(instance);
			bool isRoutingToCompany = IsRoutingToCompany(instance);
			return new RoundState(inShipPhase, isFirstDay, isRoutingToCompany);
		}

		public string? GetCurrentLevelSceneName()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				logger.LogError("StartOfRound.Instance is null.");
				return null;
			}
			return instance.currentLevel?.sceneName;
		}

		private bool IsFirstDay(StartOfRound startOfRound)
		{
			EndOfGameStats gameStats = startOfRound.gameStats;
			if (gameStats == null)
			{
				logger.LogError("StartOfRound.Instance.gameStats is null.");
				return false;
			}
			int daysSpent = gameStats.daysSpent;
			logger.LogDebug($"daysSpent={daysSpent}");
			return daysSpent == 0;
		}

		private bool IsRoutingToCompany(StartOfRound startOfRound)
		{
			SelectableLevel currentLevel = startOfRound.currentLevel;
			if ((Object)(object)currentLevel == (Object)null)
			{
				logger.LogError("StartOfRound.Instance.currentLevel is null.");
				return false;
			}
			string sceneName = currentLevel.sceneName;
			logger.LogDebug("IsSceneNameCompany? sceneName=" + sceneName);
			return CompanyScene.IsCompanyScene(sceneName);
		}
	}
	internal sealed class TerminalAdapter
	{
		private readonly IPluginLogger logger;

		private Terminal? cachedTerminal;

		public TerminalAdapter(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public Item? GetBuyableItemByIndex(int index)
		{
			Terminal terminal = GetTerminal();
			if ((Object)(object)terminal == (Object)null)
			{
				logger.LogError("Terminal is null.");
				return null;
			}
			Item[] buyableItemsList = terminal.buyableItemsList;
			if (buyableItemsList == null)
			{
				logger.LogError("Terminal.buyableItemsList is null.");
				return null;
			}
			return buyableItemsList.ElementAtOrDefault(index);
		}

		public List<int>? GetOrderedItemIndexes()
		{
			Terminal terminal = GetTerminal();
			if ((Object)(object)terminal == (Object)null)
			{
				return null;
			}
			return terminal.orderedItemsFromTerminal;
		}

		public bool SetOrderedItemIndexes(List<int> boughtItemIndexes)
		{
			Terminal terminal = GetTerminal();
			if ((Object)(object)terminal == (Object)null)
			{
				return false;
			}
			terminal.orderedItemsFromTerminal = boughtItemIndexes;
			return true;
		}

		private Terminal? GetTerminal()
		{
			if ((Object)(object)cachedTerminal != (Object)null)
			{
				return cachedTerminal;
			}
			Terminal val = Object.FindObjectOfType<Terminal>();
			if ((Object)(object)val == (Object)null)
			{
				logger.LogError("Failed to find Terminal instance in the scene.");
				return null;
			}
			cachedTerminal = val;
			return val;
		}
	}
}
namespace SkipDropshipCompany.Core.Validation
{
	internal sealed class DisabledValidationLogger : IValidationLogger
	{
		public static DisabledValidationLogger Instance { get; } = new DisabledValidationLogger();


		private DisabledValidationLogger()
		{
		}

		public void Record(ValidationLogRecord record)
		{
		}
	}
	internal enum ValidationLogRole
	{
		Server,
		Client
	}
	internal enum ValidationLogScene
	{
		Company,
		Other,
		Unknown
	}
	internal enum ValidationLogPrepareResult
	{
		NoServer,
		NotAllowed,
		Success
	}
	internal enum ValidationLogSpawnResult
	{
		NoServer,
		NoPreparedPurchase,
		SpawnFailed,
		Success
	}
	internal enum ValidationLogLandingHistoryResult
	{
		NoServer,
		NullScene,
		EmptyScene,
		Success
	}
	internal enum ValidationLogTerminalOrderReadResult
	{
		NullOrderedItems
	}
	internal enum ValidationLogTerminalOrderRestoreResult
	{
		Failed,
		Success
	}
	internal sealed class ValidationLogRecord
	{
		public string EventName { get; }

		public Dictionary<string, object?>? Fields { get; }

		private ValidationLogRecord(string eventName, Dictionary<string, object?>? fields = null)
		{
			EventName = eventName;
			Fields = fields;
		}

		public static ValidationLogRecord PluginLoaded(string version, bool validationLogging, bool enabled, bool requireReroutingOnFirstDay)
		{
			return new ValidationLogRecord("plugin_loaded", new Dictionary<string, object>
			{
				["version"] = version,
				["validation_logging"] = validationLogging,
				["enabled"] = enabled,
				["require_rerouting_on_first_day"] = requireReroutingOnFirstDay
			});
		}

		public static ValidationLogRecord ControllerCreated()
		{
			return new ValidationLogRecord("controller_created");
		}

		public static ValidationLogRecord CallbackException(string callback, string exceptionType)
		{
			return new ValidationLogRecord("callback_exception", new Dictionary<string, object>
			{
				["callback"] = callback,
				["exception_type"] = exceptionType
			});
		}

		public static ValidationLogRecord InstantPurchaseEligibilityDecision(RoundState roundState, bool enabled, bool requireReroutingOnFirstDay, bool lastLandedOnCompany, bool allowed, string reason)
		{
			return new ValidationLogRecord("instant_purchase_eligibility_decision", new Dictionary<string, object>
			{
				["enabled"] = enabled,
				["require_rerouting_on_first_day"] = requireReroutingOnFirstDay,
				["is_in_orbit"] = roundState.IsInOrbit,
				["is_first_day"] = roundState.IsFirstDay,
				["is_routing_to_company"] = roundState.IsRoutingToCompany,
				["last_landed_on_company"] = lastLandedOnCompany,
				["allowed"] = allowed,
				["reason"] = reason
			});
		}

		public static ValidationLogRecord InstantPurchaseEligibilityConfigDisabled()
		{
			return new ValidationLogRecord("instant_purchase_eligibility_decision", new Dictionary<string, object>
			{
				["enabled"] = false,
				["allowed"] = false,
				["reason"] = "disabled"
			});
		}

		public static ValidationLogRecord PrepareInstantPurchaseResult(ValidationLogRole role, ValidationLogPrepareResult result, int originalItemCount, PrepareInstantPurchaseResult? preparedResult)
		{
			return new ValidationLogRecord("prepare_instant_purchase_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = ToValidationPrepareResultToken(result),
				["original_item_count"] = originalItemCount,
				["dropship_item_count"] = preparedResult?.DropShipBoughtItemIndexes.Count,
				["instant_item_count"] = preparedResult?.InstantBoughtItemIndexes.Count
			});
		}

		public static ValidationLogRecord SpawnInstantPurchaseResult(ValidationLogRole role, ValidationLogSpawnResult result, int preparedInstantItemCount, int preparedDropShipItemCount, int spawnedItemCount)
		{
			return new ValidationLogRecord("spawn_instant_purchase_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = ToValidationSpawnResultToken(result),
				["prepared_instant_item_count"] = preparedInstantItemCount,
				["prepared_dropship_item_count"] = preparedDropShipItemCount,
				["spawned_item_count"] = spawnedItemCount
			});
		}

		public static ValidationLogRecord LandingHistoryUpdated(ValidationLogRole role, ValidationLogLandingHistoryResult result, ValidationLogScene scene)
		{
			return new ValidationLogRecord("landing_history_updated", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = ToValidationLandingHistoryResultToken(result),
				["scene"] = ToValidationSceneToken(scene)
			});
		}

		public static ValidationLogRecord LandingHistoryCleared(ValidationLogRole role, bool cleared)
		{
			return new ValidationLogRecord("landing_history_cleared", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["cleared"] = cleared
			});
		}

		public static ValidationLogRecord TerminalOrderReadResult(ValidationLogRole role, ValidationLogTerminalOrderReadResult result)
		{
			return new ValidationLogRecord("terminal_order_read_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = ToValidationTerminalOrderReadResultToken(result)
			});
		}

		public static ValidationLogRecord TerminalOrderRestoreResult(ValidationLogRole role, ValidationLogTerminalOrderRestoreResult result, int dropshipItemCount)
		{
			return new ValidationLogRecord("terminal_order_restore_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = ToValidationTerminalOrderRestoreResultToken(result),
				["dropship_item_count"] = dropshipItemCount
			});
		}

		public static ValidationLogScene ToValidationScene(string? sceneName)
		{
			if (sceneName == null)
			{
				return ValidationLogScene.Unknown;
			}
			if (!CompanyScene.IsCompanyScene(sceneName))
			{
				return ValidationLogScene.Other;
			}
			return ValidationLogScene.Company;
		}

		private static string ToValidationRoleToken(ValidationLogRole role)
		{
			return role switch
			{
				ValidationLogRole.Server => "server", 
				ValidationLogRole.Client => "client", 
				_ => "unknown", 
			};
		}

		private static string ToValidationSceneToken(ValidationLogScene scene)
		{
			return scene switch
			{
				ValidationLogScene.Company => "company", 
				ValidationLogScene.Other => "other", 
				ValidationLogScene.Unknown => "unknown", 
				_ => "unknown", 
			};
		}

		private static string ToValidationPrepareResultToken(ValidationLogPrepareResult result)
		{
			return result switch
			{
				ValidationLogPrepareResult.NoServer => "no_server", 
				ValidationLogPrepareResult.NotAllowed => "not_allowed", 
				ValidationLogPrepareResult.Success => "success", 
				_ => "unknown", 
			};
		}

		private static string ToValidationSpawnResultToken(ValidationLogSpawnResult result)
		{
			return result switch
			{
				ValidationLogSpawnResult.NoServer => "no_server", 
				ValidationLogSpawnResult.NoPreparedPurchase => "no_prepared_purchase", 
				ValidationLogSpawnResult.SpawnFailed => "spawn_failed", 
				ValidationLogSpawnResult.Success => "success", 
				_ => "unknown", 
			};
		}

		private static string ToValidationLandingHistoryResultToken(ValidationLogLandingHistoryResult result)
		{
			return result switch
			{
				ValidationLogLandingHistoryResult.NoServer => "no_server", 
				ValidationLogLandingHistoryResult.NullScene => "null_scene", 
				ValidationLogLandingHistoryResult.EmptyScene => "empty_scene", 
				ValidationLogLandingHistoryResult.Success => "success", 
				_ => "unknown", 
			};
		}

		private static string ToValidationTerminalOrderReadResultToken(ValidationLogTerminalOrderReadResult result)
		{
			if (result == ValidationLogTerminalOrderReadResult.NullOrderedItems)
			{
				return "null_ordered_items";
			}
			return "unknown";
		}

		private static string ToValidationTerminalOrderRestoreResultToken(ValidationLogTerminalOrderRestoreResult result)
		{
			return result switch
			{
				ValidationLogTerminalOrderRestoreResult.Failed => "failed", 
				ValidationLogTerminalOrderRestoreResult.Success => "success", 
				_ => "unknown", 
			};
		}
	}
}
namespace SkipDropshipCompany.Core.UseCases
{
	internal sealed class ClearLandingHistoryUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly LandingHistoryStore landingHistoryStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public ClearLandingHistoryUseCase(IGameInterop gameInterop, LandingHistoryStore landingHistoryStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.landingHistoryStore = landingHistoryStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public void Execute()
		{
			if (!gameInterop.IsServer())
			{
				logger.LogDebug("Not the server. Skipping landing history clear.");
				validationLogger.Record(ValidationLogRecord.LandingHistoryCleared(ValidationLogRole.Client, cleared: false));
				return;
			}
			logger.LogDebug("Clearing landing history.");
			landingHistoryStore.ClearLandingHistory();
			logger.LogDebug("Cleared landing history.");
			validationLogger.Record(ValidationLogRecord.LandingHistoryCleared(ValidationLogRole.Server, cleared: true));
		}
	}
	internal sealed class InstantPurchaseEligibilityUseCase
	{
		private readonly IPluginConfig config;

		private readonly IGameInterop gameInterop;

		private readonly LandingHistoryStore landingHistoryStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public InstantPurchaseEligibilityUseCase(IPluginConfig config, IGameInterop gameInterop, LandingHistoryStore landingHistoryStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.config = config;
			this.gameInterop = gameInterop;
			this.landingHistoryStore = landingHistoryStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public bool IsInstantPurchaseAllowed()
		{
			logger.LogDebug("Checking if instant purchase is allowed.");
			if (!config.Enabled)
			{
				logger.LogDebug("Not enabled.");
				validationLogger.Record(ValidationLogRecord.InstantPurchaseEligibilityConfigDisabled());
				return false;
			}
			bool requireReroutingOnFirstDay = config.RequireReroutingOnFirstDay;
			logger.LogDebug($"Configs: isFirstDayRerouteRequired={requireReroutingOnFirstDay}");
			RoundState roundState = gameInterop.GetRoundState();
			bool flag = IsLandedOnCompany(roundState);
			bool flag2 = IsInFirstDayOrbit(roundState);
			bool flag3 = IsInFirstDayOrbitAndRoutingToCompany(roundState);
			bool lastLandedOnCompany = roundState.IsInOrbit && landingHistoryStore.IsLastLandedOnCompany();
			bool flag4 = IsInOrbitAndLastLandedOnCompanyAndRoutingToCompany(roundState, lastLandedOnCompany);
			logger.LogDebug("Flags:" + $" IsLandedOnCompany={flag}" + $" IsInFirstDayOrbit={flag2}" + $" IsInFirstDayOrbitAndRoutingToCompany={flag3}" + $" isInOrbitAndLastLandedOnCompanyAndRoutingToCompany={flag4}");
			bool flag5 = flag || (!requireReroutingOnFirstDay && flag2) || flag3 || flag4;
			validationLogger.Record(ValidationLogRecord.InstantPurchaseEligibilityDecision(roundState, enabled: true, requireReroutingOnFirstDay, lastLandedOnCompany, flag5, GetEligibilityReason(flag, requireReroutingOnFirstDay, flag2, flag3, flag4)));
			return flag5;
		}

		private bool IsInFirstDayOrbit(RoundState roundState)
		{
			if (!roundState.IsInOrbit)
			{
				logger.LogDebug("Not in orbit.");
				return false;
			}
			if (!roundState.IsFirstDay)
			{
				logger.LogDebug("Not first day.");
				return false;
			}
			return true;
		}

		private bool IsInFirstDayOrbitAndRoutingToCompany(RoundState roundState)
		{
			if (!IsInFirstDayOrbit(roundState))
			{
				logger.LogDebug("Not in first day orbit.");
				return false;
			}
			if (!roundState.IsRoutingToCompany)
			{
				logger.LogDebug("Not routing to company.");
				return false;
			}
			return true;
		}

		private bool IsLandedOnCompany(RoundState roundState)
		{
			if (roundState.IsInOrbit)
			{
				logger.LogDebug("In orbit.");
				return false;
			}
			if (!roundState.IsRoutingToCompany)
			{
				logger.LogDebug("Not routing to company.");
				return false;
			}
			return true;
		}

		private bool IsInOrbitAndLastLandedOnCompanyAndRoutingToCompany(RoundState roundState, bool lastLandedOnCompany)
		{
			if (!roundState.IsInOrbit)
			{
				logger.LogDebug("Not in orbit.");
				return false;
			}
			if (!lastLandedOnCompany)
			{
				logger.LogDebug("Last landed level is not company.");
				return false;
			}
			if (!roundState.IsRoutingToCompany)
			{
				logger.LogDebug("Not routing to company.");
				return false;
			}
			return true;
		}

		private static string GetEligibilityReason(bool isLandedOnCompany, bool isFirstDayRerouteRequired, bool isInFirstDayOrbit, bool isInFirstDayOrbitAndRoutingToCompany, bool isInOrbitAndLastLandedOnCompanyAndRoutingToCompany)
		{
			if (isLandedOnCompany)
			{
				return "landed_on_company";
			}
			if (!isFirstDayRerouteRequired && isInFirstDayOrbit)
			{
				return "first_day_orbit";
			}
			if (isInFirstDayOrbitAndRoutingToCompany)
			{
				return "first_day_orbit_routing_to_company";
			}
			if (isInOrbitAndLastLandedOnCompanyAndRoutingToCompany)
			{
				return "orbit_after_company_landing";
			}
			return "conditions_not_met";
		}
	}
	internal sealed class PrepareInstantPurchaseUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly InstantPurchaseEligibilityUseCase eligibilityUseCase;

		private readonly PreparedInstantPurchaseStore preparedInstantPurchaseStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public PrepareInstantPurchaseUseCase(IGameInterop gameInterop, InstantPurchaseEligibilityUseCase eligibilityUseCase, PreparedInstantPurchaseStore preparedInstantPurchaseStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.eligibilityUseCase = eligibilityUseCase;
			this.preparedInstantPurchaseStore = preparedInstantPurchaseStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public PrepareInstantPurchaseResult? Execute(List<int> boughtItemIndexes)
		{
			if (!gameInterop.IsServer())
			{
				logger.LogDebug("Not the server. Skipping instant purchase logic.");
				validationLogger.Record(ValidationLogRecord.PrepareInstantPurchaseResult(ValidationLogRole.Client, ValidationLogPrepareResult.NoServer, boughtItemIndexes.Count, null));
				return null;
			}
			logger.LogDebug("Preparing instant purchase.");
			if (!eligibilityUseCase.IsInstantPurchaseAllowed())
			{
				logger.LogDebug("Instant purchase is not allowed in the current game state.");
				validationLogger.Record(ValidationLogRecord.PrepareInstantPurchaseResult(ValidationLogRole.Server, ValidationLogPrepareResult.NotAllowed, boughtItemIndexes.Count, null));
				return null;
			}
			PrepareInstantPurchaseResult prepareInstantPurchaseResult = new PrepareInstantPurchaseResult(new List<int>(), boughtItemIndexes);
			validationLogger.Record(ValidationLogRecord.PrepareInstantPurchaseResult(ValidationLogRole.Server, ValidationLogPrepareResult.Success, boughtItemIndexes.Count, prepareInstantPurchaseResult));
			preparedInstantPurchaseStore.SetPreparedInstantPurchaseResult(prepareInstantPurchaseResult);
			return prepareInstantPurchaseResult;
		}
	}
	internal sealed class RecordLandingUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly LandingHistoryStore landingHistoryStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public RecordLandingUseCase(IGameInterop gameInterop, LandingHistoryStore landingHistoryStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.landingHistoryStore = landingHistoryStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public void Execute(string? sceneName)
		{
			if (!gameInterop.IsServer())
			{
				logger.LogDebug("Not the server. Skipping landing history addition.");
				validationLogger.Record(ValidationLogRecord.LandingHistoryUpdated(ValidationLogRole.Client, ValidationLogLandingHistoryResult.NoServer, ValidationLogRecord.ToValidationScene(sceneName)));
				return;
			}
			if (sceneName == null)
			{
				logger.LogError("StartOfRound.currentLevel.sceneName is null.");
				validationLogger.Record(ValidationLogRecord.LandingHistoryUpdated(ValidationLogRole.Server, ValidationLogLandingHistoryResult.NullScene, ValidationLogScene.Unknown));
				return;
			}
			logger.LogDebug("Adding landing history. sceneName=" + sceneName);
			if (!landingHistoryStore.AddLandingHistory(sceneName))
			{
				logger.LogError("Failed to add landing history. sceneName=" + sceneName);
				validationLogger.Record(ValidationLogRecord.LandingHistoryUpdated(ValidationLogRole.Server, ValidationLogLandingHistoryResult.EmptyScene, ValidationLogRecord.ToValidationScene(sceneName)));
			}
			else
			{
				logger.LogDebug("Added landing history. sceneName=" + sceneName);
				validationLogger.Record(ValidationLogRecord.LandingHistoryUpdated(ValidationLogRole.Server, ValidationLogLandingHistoryResult.Success, ValidationLogRecord.ToValidationScene(sceneName)));
			}
		}
	}
	internal sealed class SpawnPreparedInstantPurchasedItemsUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly PreparedInstantPurchaseStore preparedInstantPurchaseStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public SpawnPreparedInstantPurchasedItemsUseCase(IGameInterop gameInterop, PreparedInstantPurchaseStore preparedInstantPurchaseStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.preparedInstantPurchaseStore = preparedInstantPurchaseStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public SpawnPreparedInstantPurchasedItemsResult? Execute()
		{
			if (!gameInterop.IsServer())
			{
				logger.LogDebug("Not the server. Skipping instant purchase logic.");
				validationLogger.Record(ValidationLogRecord.SpawnInstantPurchaseResult(ValidationLogRole.Client, ValidationLogSpawnResult.NoServer, 0, 0, 0));
				return null;
			}
			logger.LogDebug("Spawning prepared instant purchased items.");
			PrepareInstantPurchaseResult preparedInstantPurchaseResult = preparedInstantPurchaseStore.GetPreparedInstantPurchaseResult();
			if (preparedInstantPurchaseResult == null)
			{
				logger.LogDebug("No prepared instant purchase to spawn.");
				validationLogger.Record(ValidationLogRecord.SpawnInstantPurchaseResult(ValidationLogRole.Server, ValidationLogSpawnResult.NoPreparedPurchase, 0, 0, 0));
				return null;
			}
			int num = 0;
			foreach (int instantBoughtItemIndex in preparedInstantPurchaseResult.InstantBoughtItemIndexes)
			{
				if (!gameInterop.SpawnBuyableItemInShip(instantBoughtItemIndex))
				{
					logger.LogError($"Failed to spawn instant purchased item. buyableItemIndex={instantBoughtItemIndex}");
					validationLogger.Record(ValidationLogRecord.SpawnInstantPurchaseResult(ValidationLogRole.Server, ValidationLogSpawnResult.SpawnFailed, preparedInstantPurchaseResult.InstantBoughtItemIndexes.Count, preparedInstantPurchaseResult.DropShipBoughtItemIndexes.Count, num));
					return null;
				}
				num++;
			}
			SpawnPreparedInstantPurchasedItemsResult result = new SpawnPreparedInstantPurchasedItemsResult(preparedInstantPurchaseResult.DropShipBoughtItemIndexes, preparedInstantPurchaseResult.InstantBoughtItemIndexes);
			preparedInstantPurchaseStore.ClearPreparedInstantPurchaseResult();
			validationLogger.Record(ValidationLogRecord.SpawnInstantPurchaseResult(ValidationLogRole.Server, ValidationLogSpawnResult.Success, preparedInstantPurchaseResult.InstantBoughtItemIndexes.Count, preparedInstantPurchaseResult.DropShipBoughtItemIndexes.Count, num));
			return result;
		}
	}
	internal sealed class PrepareInstantPurchaseResult
	{
		public List<int> DropShipBoughtItemIndexes { get; }

		public List<int> InstantBoughtItemIndexes { get; }

		public PrepareInstantPurchaseResult(List<int> dropShipBoughtItemIndexes, List<int> instantBoughtItemIndexes)
		{
			DropShipBoughtItemIndexes = dropShipBoughtItemIndexes;
			InstantBoughtItemIndexes = instantBoughtItemIndexes;
		}
	}
	internal sealed class SpawnPreparedInstantPurchasedItemsResult
	{
		public List<int> DropShipBoughtItemIndexes { get; }

		public List<int> InstantBoughtItemIndexes { get; }

		public SpawnPreparedInstantPurchasedItemsResult(List<int> dropShipBoughtItemIndexes, List<int> instantBoughtItemIndexes)
		{
			DropShipBoughtItemIndexes = dropShipBoughtItemIndexes;
			InstantBoughtItemIndexes = instantBoughtItemIndexes;
		}
	}
}
namespace SkipDropshipCompany.Core.State
{
	internal static class CompanyScene
	{
		private const string CompanySceneName = "CompanyBuilding";

		public static bool IsCompanyScene(string sceneName)
		{
			return sceneName == "CompanyBuilding";
		}
	}
	internal sealed class LandingHistoryStore
	{
		private const int LandingHistorySize = 1;

		private readonly IPluginLogger logger;

		private List<string> landingEntries = new List<string>();

		public LandingHistoryStore(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public bool AddLandingHistory(string sceneName)
		{
			if (string.IsNullOrEmpty(sceneName))
			{
				logger.LogError("Scene name is null or empty. Cannot add to landing history.");
				return false;
			}
			landingEntries.Add(sceneName);
			landingEntries = landingEntries.TakeLast(1).ToList();
			logger.LogDebug("Updated landing history. landingEntries=" + string.Join(", ", landingEntries));
			return true;
		}

		public bool IsLastLandedOnCompany()
		{
			string text = landingEntries.LastOrDefault();
			if (text == null)
			{
				logger.LogDebug("Last landed scene name is null.");
				return false;
			}
			if (!CompanyScene.IsCompanyScene(text))
			{
				logger.LogDebug("Last landed scene is not company.");
				return false;
			}
			return true;
		}

		public void ClearLandingHistory()
		{
			landingEntries.Clear();
		}
	}
	internal sealed class PreparedInstantPurchaseStore
	{
		private PrepareInstantPurchaseResult? preparedInstantPurchaseResult;

		public PrepareInstantPurchaseResult? GetPreparedInstantPurchaseResult()
		{
			return preparedInstantPurchaseResult;
		}

		public void SetPreparedInstantPurchaseResult(PrepareInstantPurchaseResult result)
		{
			preparedInstantPurchaseResult = result;
		}

		public void ClearPreparedInstantPurchaseResult()
		{
			preparedInstantPurchaseResult = null;
		}
	}
	internal sealed class RoundState
	{
		public bool IsInOrbit { get; }

		public bool IsFirstDay { get; }

		public bool IsRoutingToCompany { get; }

		public RoundState(bool isInOrbit, bool isFirstDay, bool isRoutingToCompany)
		{
			IsInOrbit = isInOrbit;
			IsFirstDay = isFirstDay;
			IsRoutingToCompany = isRoutingToCompany;
		}
	}
}
namespace SkipDropshipCompany.Core.Ports
{
	internal interface IGameInterop
	{
		bool IsServer();

		RoundState GetRoundState();

		string? GetCurrentLevelSceneName();

		List<int>? GetTerminalOrderedItemIndexes();

		bool SetTerminalOrderedItemIndexes(List<int> boughtItemIndexes);

		bool SpawnBuyableItemInShip(int buyableItemIndex);
	}
	internal interface IPluginConfig
	{
		bool Enabled { get; }

		bool RequireReroutingOnFirstDay { get; }

		bool ValidationLogging { get; }
	}
	internal interface IPluginLogger
	{
		void LogDebug(string message);

		void LogInfo(string message);

		void LogError(string message);
	}
	internal interface IValidationLogger
	{
		void Record(ValidationLogRecord record);
	}
}
namespace SkipDropshipCompany.Core.Handlers
{
	internal sealed class RoundCallbackHandler
	{
		private readonly IGameInterop gameInterop;

		private readonly RecordLandingUseCase recordLandingUseCase;

		private readonly ClearLandingHistoryUseCase clearLandingHistoryUseCase;

		public RoundCallbackHandler(IGameInterop gameInterop, RecordLandingUseCase recordLandingUseCase, ClearLandingHistoryUseCase clearLandingHistoryUseCase)
		{
			this.gameInterop = gameInterop;
			this.recordLandingUseCase = recordLandingUseCase;
			this.clearLandingHistoryUseCase = clearLandingHistoryUseCase;
		}

		public void HandleStartGame()
		{
			recordLandingUseCase.Execute(gameInterop.GetCurrentLevelSceneName());
		}

		public void HandleResetShip()
		{
			clearLandingHistoryUseCase.Execute();
		}
	}
	internal sealed class TerminalSyncGroupCreditsHandler
	{
		private readonly IGameInterop gameInterop;

		private readonly PrepareInstantPurchaseUseCase prepareInstantPurchaseUseCase;

		private readonly SpawnPreparedInstantPurchasedItemsUseCase spawnPreparedInstantPurchasedItemsUseCase;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public TerminalSyncGroupCreditsHandler(IGameInterop gameInterop, PrepareInstantPurchaseUseCase prepareInstantPurchaseUseCase, SpawnPreparedInstantPurchasedItemsUseCase spawnPreparedInstantPurchasedItemsUseCase, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.prepareInstantPurchaseUseCase = prepareInstantPurchaseUseCase;
			this.spawnPreparedInstantPurchasedItemsUseCase = spawnPreparedInstantPurchasedItemsUseCase;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public PrepareInstantPurchaseResult? HandlePrefix()
		{
			List<int> terminalOrderedItemIndexes = gameInterop.GetTerminalOrderedItemIndexes();
			if (terminalOrderedItemIndexes == null)
			{
				logger.LogError("Terminal.orderedItemsFromTerminal is null.");
				validationLogger.Record(ValidationLogRecord.TerminalOrderReadResult(GetRole(), ValidationLogTerminalOrderReadResult.NullOrderedItems));
				return null;
			}
			PrepareInstantPurchaseResult prepareInstantPurchaseResult = prepareInstantPurchaseUseCase.Execute(terminalOrderedItemIndexes);
			if (prepareInstantPurchaseResult == null)
			{
				logger.LogDebug("Prepare instant purchase failed or not allowed. Skipping instant purchase logic.");
				return null;
			}
			logger.LogDebug("Prepared instant purchase." + $" originalDropShipItemCount={terminalOrderedItemIndexes.Count}" + $" preparedDropShipItemCount={prepareInstantPurchaseResult.DropShipBoughtItemIndexes.Count}" + $" preparedInstantPurchaseItemCount={prepareInstantPurchaseResult.InstantBoughtItemIndexes.Count}");
			return prepareInstantPurchaseResult;
		}

		public void HandlePostfix()
		{
			SpawnPreparedInstantPurchasedItemsResult spawnPreparedInstantPurchasedItemsResult = spawnPreparedInstantPurchasedItemsUseCase.Execute();
			if (spawnPreparedInstantPurchasedItemsResult == null)
			{
				logger.LogDebug("Spawning prepared instant purchased items failed or none to spawn.");
			}
			else if (!gameInterop.SetTerminalOrderedItemIndexes(spawnPreparedInstantPurchasedItemsResult.DropShipBoughtItemIndexes))
			{
				logger.LogError("Failed to restore Terminal.orderedItemsFromTerminal.");
				validationLogger.Record(ValidationLogRecord.TerminalOrderRestoreResult(GetRole(), ValidationLogTerminalOrderRestoreResult.Failed, spawnPreparedInstantPurchasedItemsResult.DropShipBoughtItemIndexes.Count));
			}
			else
			{
				validationLogger.Record(ValidationLogRecord.TerminalOrderRestoreResult(GetRole(), ValidationLogTerminalOrderRestoreResult.Success, spawnPreparedInstantPurchasedItemsResult.DropShipBoughtItemIndexes.Count));
				logger.LogDebug("Spawned all prepared instant purchased items.");
			}
		}

		private ValidationLogRole GetRole()
		{
			if (!gameInterop.IsServer())
			{
				return ValidationLogRole.Client;
			}
			return ValidationLogRole.Server;
		}
	}
}