Decompiled source of StoreRotationConfig v2.5.0

StoreRotationConfig.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CSync.Extensions;
using CSync.Lib;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using StoreRotationConfig.Api;
using StoreRotationConfig.Compatibility;
using StoreRotationConfig.Patches;
using TerminalFormatter.Nodes;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("StoreRotationConfig")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Configure the number of items in each store rotation, show them all, remove purchases, sort them, and/or enable sales for them.")]
[assembly: AssemblyFileVersion("2.5.0.0")]
[assembly: AssemblyInformationalVersion("2.5.0")]
[assembly: AssemblyProduct("StoreRotationConfig")]
[assembly: AssemblyTitle("StoreRotationConfig")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.5.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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 StoreRotationConfig
{
	[DataContract]
	public class Config : SyncedConfig2<Config>
	{
		[field: SyncedEntryField]
		public SyncedEntry<int> MIN_ITEMS { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> MAX_ITEMS { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> STOCK_ALL { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> REMOVE_PURCHASED { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<string> ITEM_WHITELIST { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<string> ITEM_BLACKLIST { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> SALE_CHANCE { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> MIN_SALE_ITEMS { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> MAX_SALE_ITEMS { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> MIN_DISCOUNT { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<int> MAX_DISCOUNT { get; private set; }

		[field: SyncedEntryField]
		public SyncedEntry<bool> ROUND_TO_NEAREST_TEN { get; private set; }

		public ConfigEntry<bool> SORT_ITEMS { get; private set; }

		public ConfigEntry<bool> RELATIVE_SCROLL { get; private set; }

		public ConfigEntry<int> LINES_TO_SCROLL { get; private set; }

		public ConfigEntry<bool> TERMINAL_FORMATTER_COMPAT { get; private set; }

		public Config(ConfigFile cfg)
			: base("pacoito.StoreRotationConfig")
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			cfg.SaveOnConfigSet = false;
			MIN_ITEMS = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "General", "minItems", 8, "Minimum number of items in the store rotation.");
			MAX_ITEMS = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "General", "maxItems", 12, "Maximum number of items in the store rotation.");
			STOCK_ALL = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "General", "stockAll", false, "Make every item available in the store rotation.");
			REMOVE_PURCHASED = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "General", "removePurchased", false, "Remove purchased items from the current and future store rotations.If disabled, prevents purchased items from showing up again in future store rotations, and removes them from the current one.");
			ITEM_WHITELIST = SyncedBindingExtensions.BindSyncedEntry<string>(cfg, "General", "itemWhitelist", "", "The comma-separated names of items that will be guaranteed to show up in every store rotation. Whitelisted items are always added on top of the range defined by the 'minItems' and 'maxItems' settings, and take priority over the blacklist. Has no effect with the 'stockAll' setting enabled.\nExample: \"Bee suit,Goldfish,Television\"");
			ITEM_BLACKLIST = SyncedBindingExtensions.BindSyncedEntry<string>(cfg, "General", "itemBlacklist", "", "The comma-separated names of items that will never show up in the store rotation. You're a mean one, Mr. Grinch.\nExample: \"Bee suit,Goldfish,Television\"");
			SALE_CHANCE = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "Sales", "saleChance", 33, new ConfigDescription("The percentage chance for ANY item to be on sale in the store rotation. Setting this to '0' disables the entire sales system.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			MIN_SALE_ITEMS = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "Sales", "minSaleItems", 1, "The minimum number of items that can be on sale at a time.");
			MAX_SALE_ITEMS = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "Sales", "maxSaleItems", 5, "The maximum number of items that can be on sale at a time.");
			MIN_DISCOUNT = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "Sales", "minDiscount", 10, new ConfigDescription("The minimum discount to apply to items on sale.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			MAX_DISCOUNT = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "Sales", "maxDiscount", 50, new ConfigDescription("The maximum discount to apply to items on sale.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			ROUND_TO_NEAREST_TEN = SyncedBindingExtensions.BindSyncedEntry<bool>(cfg, "Sales", "roundToNearestTen", true, "Round rotation store discounts to the nearest ten (like the regular store).");
			SORT_ITEMS = cfg.Bind<bool>("Miscellaneous", "sortItems", false, "Sort every item in the store rotation alphabetically.");
			RELATIVE_SCROLL = cfg.Bind<bool>("Miscellaneous", "relativeScroll", true, "Adapt terminal scroll to the number of lines in the current terminal page, instead of a flat value. Should fix cases where scrolling skips over several lines, which is especially noticeable when enabling 'stockAll' with a large number of items added to the rotating store.");
			LINES_TO_SCROLL = cfg.Bind<int>("Miscellaneous", "linesToScroll", 20, new ConfigDescription("Number of lines to scroll at a time with 'relativeScroll' enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 28), Array.Empty<object>()));
			TERMINAL_FORMATTER_COMPAT = cfg.Bind<bool>("Compatibility", "terminalFormatterCompat", true, "Enable compatibility between 'TerminalFormatter' and the item sales system. Patches some methods so a restart is probably required.\nNOTE: This setting will be removed once compatibility is handled within 'TerminalFormatter' itself, since it wouldn't require any complex patching from their end.");
			LINES_TO_SCROLL.SettingChanged += delegate
			{
				TerminalScrollMousePatch.CurrentText = "";
			};
			ClearOrphanedEntries(cfg);
			cfg.SaveOnConfigSet = true;
			cfg.Save();
			ConfigManager.Register<Config>((SyncedConfig2<Config>)(object)this);
		}

		private void ClearOrphanedEntries(ConfigFile config)
		{
			PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");
			((Dictionary<ConfigDefinition, string>)propertyInfo.GetValue(config))?.Clear();
		}
	}
	[BepInPlugin("pacoito.StoreRotationConfig", "StoreRotationConfig", "2.5.0")]
	[BepInDependency("com.sigurd.csync", "5.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal const string GUID = "pacoito.StoreRotationConfig";

		internal const string PLUGIN_NAME = "StoreRotationConfig";

		internal const string VERSION = "2.5.0";

		private static Terminal? _terminal;

		internal static ManualLogSource? StaticLogger { get; private set; }

		internal static Harmony? Harmony { get; private set; }

		public static Config? Settings { get; private set; }

		public static Terminal? Terminal
		{
			get
			{
				if ((Object)(object)_terminal == (Object)null)
				{
					Terminal = Object.FindObjectOfType<Terminal>();
				}
				return _terminal;
			}
			private set
			{
				_terminal = value;
			}
		}

		private void Awake()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			StaticLogger = ((BaseUnityPlugin)this).Logger;
			try
			{
				Settings = new Config(((BaseUnityPlugin)this).Config);
				Harmony = new Harmony("pacoito.StoreRotationConfig");
				Harmony.PatchAll(typeof(RotateShipDecorSelectionPatch));
				Harmony.PatchAll(typeof(SyncShipUnlockablesPatch));
				Harmony.PatchAll(typeof(TerminalItemSalesPatches));
				Harmony.PatchAll(typeof(TerminalScrollMousePatch));
				Harmony.PatchAll(typeof(UnlockShipObjectPatches));
				StaticLogger.LogInfo((object)"'StoreRotationConfig' loaded!");
			}
			catch (Exception arg)
			{
				StaticLogger.LogError((object)string.Format("Error while initializing '{0}': {1}", "StoreRotationConfig", arg));
			}
		}
	}
}
namespace StoreRotationConfig.Patches
{
	[HarmonyPatch(typeof(Terminal), "RotateShipDecorSelection")]
	internal class RotateShipDecorSelectionPatch
	{
		private static void RotateShipDecorSelection(List<TerminalNode> shipDecorSelection, Random random)
		{
			List<TerminalNode> shipDecorSelection2 = shipDecorSelection;
			if (!NetworkManager.Singleton.IsHost && !SyncShipUnlockablesPatch.UnlockablesSynced)
			{
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogInfo((object)"Waiting for sync from server before rotating store...");
				}
				return;
			}
			if (Plugin.Settings == null)
			{
				ManualLogSource? staticLogger2 = Plugin.StaticLogger;
				if (staticLogger2 != null)
				{
					staticLogger2.LogError((object)"Configuration could not be loaded or is missing; rotating store won't work.");
				}
				return;
			}
			int num = Math.Abs(SyncedEntry<int>.op_Implicit(Plugin.Settings.MAX_ITEMS));
			int num2 = Math.Abs(SyncedEntry<int>.op_Implicit(Plugin.Settings.MIN_ITEMS));
			bool flag = SyncedEntry<bool>.op_Implicit(Plugin.Settings.STOCK_ALL);
			bool value = Plugin.Settings.SORT_ITEMS.Value;
			if (shipDecorSelection2.Count == 0)
			{
				CollectionExtensions.DoIf<UnlockableItem>((IEnumerable<UnlockableItem>)StartOfRound.Instance.unlockablesList.unlockables, (Func<UnlockableItem, bool>)((UnlockableItem item) => (Object)(object)item.shopSelectionNode != (Object)null && !item.alwaysInStock && (!SyncedEntry<bool>.op_Implicit(Plugin.Settings.REMOVE_PURCHASED) || !item.hasBeenUnlockedByPlayer)), (Action<UnlockableItem>)RotationItemsAPI.RegisterItem);
				if (Plugin.Settings.ITEM_WHITELIST.Value.Length > 0 && !SyncedEntry<bool>.op_Implicit(Plugin.Settings.STOCK_ALL))
				{
					List<string> whitelist = (from name in Plugin.Settings.ITEM_WHITELIST.Value.Split(',')
						select name.Trim()).ToList();
					CollectionExtensions.DoIf<UnlockableItem>((IEnumerable<UnlockableItem>)RotationItemsAPI.AllItems, (Func<UnlockableItem, bool>)((UnlockableItem item) => (Object)(object)item.shopSelectionNode != (Object)null && whitelist.Contains(item.shopSelectionNode.creatureName)), (Action<UnlockableItem>)RotationItemsAPI.AddPermanentItem);
					ManualLogSource? staticLogger3 = Plugin.StaticLogger;
					if (staticLogger3 != null)
					{
						staticLogger3.LogInfo((object)$"{RotationItemsAPI.PermanentItems.Count} items permanently added to the rotating store!");
					}
				}
				if (Plugin.Settings.ITEM_BLACKLIST.Value.Length > 0)
				{
					List<string> blacklist = (from name in Plugin.Settings.ITEM_BLACKLIST.Value.Split(',')
						select name.Trim()).ToList();
					int num3 = RotationItemsAPI.AllItems.RemoveAll((UnlockableItem item) => blacklist.Contains(item.shopSelectionNode.creatureName));
					ManualLogSource? staticLogger4 = Plugin.StaticLogger;
					if (staticLogger4 != null)
					{
						staticLogger4.LogInfo((object)$"{num3} items removed from the rotating store.");
					}
				}
				if (flag)
				{
					if (value)
					{
						RotationItemsAPI.AllItems.Sort((UnlockableItem x, UnlockableItem y) => string.Compare(x.shopSelectionNode.creatureName, y.shopSelectionNode.creatureName));
					}
					RotationItemsAPI.AllItems.ForEach(delegate(UnlockableItem item)
					{
						shipDecorSelection2.Add(item.shopSelectionNode);
					});
					ManualLogSource? staticLogger5 = Plugin.StaticLogger;
					if (staticLogger5 != null)
					{
						staticLogger5.LogInfo((object)$"All {RotationItemsAPI.AllItems.Count} items added to the store rotation!");
					}
				}
			}
			if (flag)
			{
				return;
			}
			ManualLogSource? staticLogger6 = Plugin.StaticLogger;
			if (staticLogger6 != null)
			{
				staticLogger6.LogInfo((object)"Rotating store...");
			}
			shipDecorSelection2.Clear();
			if (num2 > num)
			{
				ManualLogSource? staticLogger7 = Plugin.StaticLogger;
				if (staticLogger7 != null)
				{
					staticLogger7.LogWarning((object)"Value for 'minItems' is larger than 'maxItems', using it instead...");
				}
				num = num2;
			}
			int num4 = ((num2 != num) ? random.Next(num2, num + 1) : num);
			List<UnlockableItem> storeRotation = new List<UnlockableItem>(num4);
			List<UnlockableItem> allItems = new List<UnlockableItem>(RotationItemsAPI.AllItems);
			if (RotationItemsAPI.PermanentItems.Count > 0)
			{
				CollectionExtensions.Do<UnlockableItem>((IEnumerable<UnlockableItem>)RotationItemsAPI.PermanentItems, (Action<UnlockableItem>)delegate(UnlockableItem item)
				{
					allItems.Remove(item);
					storeRotation.Add(item);
				});
			}
			for (int i = 0; i < num4; i++)
			{
				if (allItems.Count == 0)
				{
					break;
				}
				int index = random.Next(0, allItems.Count);
				storeRotation.Add(allItems[index]);
				allItems.RemoveAt(index);
			}
			if (value && storeRotation.Count > 1)
			{
				storeRotation.Sort((UnlockableItem x, UnlockableItem y) => string.Compare(x.shopSelectionNode.creatureName, y.shopSelectionNode.creatureName));
			}
			storeRotation.ForEach(delegate(UnlockableItem item)
			{
				shipDecorSelection2.Add(item.shopSelectionNode);
			});
			ManualLogSource? staticLogger8 = Plugin.StaticLogger;
			if (staticLogger8 != null)
			{
				staticLogger8.LogInfo((object)"Store rotated!");
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[3]
			{
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(Terminal), "ShipDecorSelection"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(List<TerminalNode>), "Clear", (Type[])null, (Type[])null), (string)null)
			}).Insert((CodeInstruction[])(object)new CodeInstruction[5]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Terminal), "ShipDecorSelection")),
				new CodeInstruction(OpCodes.Ldloc_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(RotateShipDecorSelectionPatch), "RotateShipDecorSelection", (Type[])null, (Type[])null)),
				new CodeInstruction(OpCodes.Ret, (object)null)
			}).InstructionEnumeration();
		}
	}
	[HarmonyPatch]
	internal class SyncShipUnlockablesPatch
	{
		public static bool UnlockablesSynced { get; private set; }

		[HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesServerRpc")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> SyncShipUnlockablesServerRpcTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).End().MatchBack(true, (CodeMatch[])(object)new CodeMatch[3]
			{
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldloc_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null)
			}).Advance(10)
				.SetInstructionAndAdvance(Transpilers.EmitDelegate<Func<List<int>, int[]>>((Func<List<int>, int[]>)delegate(List<int> storedItems)
				{
					storedItems.Clear();
					for (int i = 0; i < StartOfRound.Instance.unlockablesList.unlockables.Count; i++)
					{
						if (StartOfRound.Instance.unlockablesList.unlockables[i].inStorage)
						{
							storedItems.Add(i);
						}
					}
					return storedItems.ToArray();
				}))
				.InstructionEnumeration();
		}

		[HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesServerRpc")]
		[HarmonyPostfix]
		private static void SyncShipUnlockablesServerRpcPost(StartOfRound __instance)
		{
			if (((NetworkBehaviour)__instance).IsHost)
			{
				Terminal? terminal = Plugin.Terminal;
				if (terminal != null)
				{
					terminal.RotateShipDecorSelection();
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesClientRpc")]
		[HarmonyPostfix]
		private static void SyncShipUnlockablesClientRpcPost(StartOfRound __instance, UnlockablesList ___unlockablesList, int[] placeableObjects, int[] storedItems)
		{
			UnlockablesList ___unlockablesList2 = ___unlockablesList;
			if (((NetworkBehaviour)__instance).IsHost)
			{
				return;
			}
			if (!UnlockablesSynced)
			{
				CollectionExtensions.Do<UnlockableItem>(from suit in Resources.FindObjectsOfTypeAll<UnlockableSuit>()
					select ___unlockablesList2.unlockables[suit.suitID], (Action<UnlockableItem>)delegate(UnlockableItem item)
				{
					if (item != null)
					{
						item.hasBeenUnlockedByPlayer = !item.alreadyUnlocked;
					}
				});
				CollectionExtensions.Do<UnlockableItem>(placeableObjects.Select((int furnitureID) => ___unlockablesList2.unlockables[furnitureID]), (Action<UnlockableItem>)delegate(UnlockableItem item)
				{
					if (item != null)
					{
						item.hasBeenUnlockedByPlayer = !item.alreadyUnlocked;
					}
				});
				CollectionExtensions.Do<UnlockableItem>(storedItems.Select((int storedID) => ___unlockablesList2.unlockables[storedID]), (Action<UnlockableItem>)delegate(UnlockableItem item)
				{
					if (item != null)
					{
						item.hasBeenUnlockedByPlayer = !item.alreadyUnlocked;
						item.inStorage = true;
					}
				});
				if ((Object)(object)Object.FindObjectOfType<CozyLights>() != (Object)null)
				{
					UnlockableItem val = ___unlockablesList2.unlockables.Find((UnlockableItem item) => string.CompareOrdinal(item.unlockableName, "Cozy lights") == 0);
					if (val != null)
					{
						val.hasBeenUnlockedByPlayer = true;
					}
				}
				UnlockablesSynced = true;
			}
			Terminal? terminal = Plugin.Terminal;
			if (terminal != null)
			{
				terminal.RotateShipDecorSelection();
			}
		}

		[HarmonyPatch(typeof(MenuManager), "Start")]
		[HarmonyPrefix]
		private static void MenuManagerStartPre()
		{
			UnlockablesSynced = false;
			if (Plugin.Settings != null && Plugin.Settings.TERMINAL_FORMATTER_COMPAT.Value && TerminalFormatterCompatibility.Enabled && !TerminalFormatterCompatibility.Patched)
			{
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogInfo((object)"Patching 'TerminalFormatter'...");
				}
				Harmony? harmony = Plugin.Harmony;
				if (harmony != null)
				{
					harmony.PatchAll(typeof(TerminalFormatterCompatibility));
				}
				Harmony? harmony2 = Plugin.Harmony;
				if (harmony2 != null)
				{
					harmony2.Unpatch((MethodBase)AccessTools.Method(typeof(PlayerControllerB), "ScrollMouse_performed", (Type[])null, (Type[])null), (HarmonyPatchType)3, "pacoito.StoreRotationConfig");
				}
				TerminalFormatterCompatibility.Patched = true;
				ManualLogSource? staticLogger2 = Plugin.StaticLogger;
				if (staticLogger2 != null)
				{
					staticLogger2.LogInfo((object)"'TerminalFormatter' patched!");
				}
			}
		}
	}
	[HarmonyPatch(typeof(Terminal))]
	internal class TerminalItemSalesPatches
	{
		[HarmonyPatch("RotateShipDecorSelection")]
		[HarmonyPostfix]
		private static void SetRotationSales(Terminal __instance)
		{
			if (Plugin.Settings == null || SyncedEntry<int>.op_Implicit(Plugin.Settings.SALE_CHANCE) == 0)
			{
				return;
			}
			if (!NetworkManager.Singleton.IsHost && !SyncShipUnlockablesPatch.UnlockablesSynced)
			{
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogInfo((object)"Waiting for sync from server before assigning sales...");
				}
				return;
			}
			Random random = new Random(StartOfRound.Instance.randomMapSeed + 90);
			if (random.Next(0, 100) > SyncedEntry<int>.op_Implicit(Plugin.Settings.SALE_CHANCE) - 1)
			{
				ManualLogSource? staticLogger2 = Plugin.StaticLogger;
				if (staticLogger2 != null)
				{
					staticLogger2.LogInfo((object)"No items on sale for this rotation...");
				}
				return;
			}
			int num = Math.Abs(SyncedEntry<int>.op_Implicit(Plugin.Settings.MIN_SALE_ITEMS));
			int num2 = Math.Abs(SyncedEntry<int>.op_Implicit(Plugin.Settings.MAX_SALE_ITEMS));
			int num3 = SyncedEntry<int>.op_Implicit(Plugin.Settings.MIN_DISCOUNT);
			int num4 = SyncedEntry<int>.op_Implicit(Plugin.Settings.MAX_DISCOUNT);
			if (num > num2)
			{
				ManualLogSource? staticLogger3 = Plugin.StaticLogger;
				if (staticLogger3 != null)
				{
					staticLogger3.LogWarning((object)"Value for 'minSaleItems' is larger than 'maxSaleItems', using it instead...");
				}
				num2 = num;
			}
			if (num3 > num4)
			{
				ManualLogSource? staticLogger4 = Plugin.StaticLogger;
				if (staticLogger4 != null)
				{
					staticLogger4.LogWarning((object)"Value for 'minDiscount' is larger than 'maxDiscount', using it instead...");
				}
				num4 = num3;
			}
			int num5 = random.Next(num, num2 + 1);
			if (num5 <= 0)
			{
				ManualLogSource? staticLogger5 = Plugin.StaticLogger;
				if (staticLogger5 != null)
				{
					staticLogger5.LogInfo((object)"No items on sale for this rotation...");
				}
				return;
			}
			RotationSalesAPI.ResetSales(num5);
			List<TerminalNode> list = new List<TerminalNode>(__instance.ShipDecorSelection);
			for (int i = 0; i < num5; i++)
			{
				if (list.Count == 0)
				{
					break;
				}
				int num6 = random.Next(num3, num4 + 1);
				if (SyncedEntry<bool>.op_Implicit(Plugin.Settings.ROUND_TO_NEAREST_TEN))
				{
					num6 = (int)Math.Round((float)num6 / 10f) * 10;
				}
				int index = random.Next(0, list.Count);
				RotationSalesAPI.AddItemDiscount(list[index], num6);
				list.RemoveAt(index);
			}
			ManualLogSource? staticLogger6 = Plugin.StaticLogger;
			if (staticLogger6 != null)
			{
				staticLogger6.LogInfo((object)$"{RotationSalesAPI.CountSales()} items on sale!");
			}
		}

		[HarmonyPatch("LoadNewNodeIfAffordable")]
		[HarmonyPriority(600)]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TerminalLoadNewNodeIfAffordableTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: 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_0036: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(TerminalNode), "itemCost"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Stfld, (object)AccessTools.Field(typeof(Terminal), "totalCostOfItems"), (string)null)
			}).Advance(2).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[4]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldarg_1, (object)null),
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Terminal), "totalCostOfItems"))
			})
				.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Func<TerminalNode, int, int>>((Func<TerminalNode, int, int>)delegate(TerminalNode node, int totalCostOfItems)
				{
					if (node.buyRerouteToMoon != -1)
					{
						return totalCostOfItems;
					}
					UnlockableItem val = StartOfRound.Instance.unlockablesList.unlockables[node.shipUnlockableID];
					if (Plugin.Settings == null || SyncedEntry<int>.op_Implicit(Plugin.Settings.SALE_CHANCE) == 0 || !RotationSalesAPI.IsOnSale(val.shopSelectionNode))
					{
						return totalCostOfItems;
					}
					int discount;
					int discountedPrice = RotationSalesAPI.GetDiscountedPrice(val.shopSelectionNode, out discount);
					ManualLogSource? staticLogger = Plugin.StaticLogger;
					if (staticLogger != null)
					{
						staticLogger.LogDebug((object)$"Applying discount of {discount}% to '{val.shopSelectionNode.creatureName}'...");
					}
					return discountedPrice;
				}) })
				.Insert((CodeInstruction[])(object)new CodeInstruction[1]
				{
					new CodeInstruction(OpCodes.Stfld, (object)AccessTools.Field(typeof(Terminal), "totalCostOfItems"))
				})
				.InstructionEnumeration();
		}

		[HarmonyPatch("TextPostProcess")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TextPostProcessTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[3]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"\n{0}  //  ${1}", (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(Terminal), "ShipDecorSelection"), (string)null)
			}).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(TerminalNode), "itemCost"), (string)null)
			}).SetInstructionAndAdvance(Transpilers.EmitDelegate<Func<TerminalNode, string>>((Func<TerminalNode, string>)delegate(TerminalNode item)
			{
				if (Plugin.Settings == null || SyncedEntry<int>.op_Implicit(Plugin.Settings.SALE_CHANCE) == 0 || !RotationSalesAPI.IsOnSale(item, out var discount))
				{
					return $"{item.itemCost}";
				}
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogDebug((object)$"Appending sale tag of '{discount}%' to {item.creatureName}...");
				}
				return RotationSalesAPI.GetTerminalString(item);
			}))
				.SetOperandAndAdvance((object)typeof(string))
				.InstructionEnumeration();
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed", new Type[] { typeof(CallbackContext) })]
	internal class TerminalScrollMousePatch
	{
		private static float scrollAmount = 1f / 3f;

		public static string CurrentText { get; internal set; } = "";


		private static void ScrollMouse_performed(Scrollbar scrollbar, float scrollDirection)
		{
			if ((Object)(object)Plugin.Terminal == (Object)null || Plugin.Settings == null || !Plugin.Settings.RELATIVE_SCROLL.Value)
			{
				scrollbar.value += scrollDirection / 3f;
				return;
			}
			if (string.CompareOrdinal(Plugin.Terminal.currentText, CurrentText) != 0)
			{
				CurrentText = Plugin.Terminal.currentText;
				int num = CurrentText.Count((char c) => c.Equals('\n')) + 1;
				scrollAmount = (float)Plugin.Settings.LINES_TO_SCROLL.Value / (float)num;
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogDebug((object)$"Setting terminal scroll amount to '{scrollAmount}'!");
				}
			}
			scrollbar.value += scrollDirection * scrollAmount;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(PlayerControllerB), "terminalScrollVertical"), (string)null)
			}).Insert((CodeInstruction[])(object)new CodeInstruction[5]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(PlayerControllerB), "terminalScrollVertical")),
				new CodeInstruction(OpCodes.Ldloc_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(TerminalScrollMousePatch), "ScrollMouse_performed", (Type[])null, (Type[])null)),
				new CodeInstruction(OpCodes.Ret, (object)null)
			}).InstructionEnumeration();
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal class UnlockShipObjectPatches
	{
		private static void RemoveFromRotation(UnlockableItem? item, int unlockableID = -1)
		{
			if (item == null || (Object)(object)item.shopSelectionNode == (Object)null)
			{
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogWarning((object)$"Item #{unlockableID} and/or its terminal node could not be found.");
				}
				return;
			}
			ManualLogSource? staticLogger2 = Plugin.StaticLogger;
			if (staticLogger2 != null)
			{
				staticLogger2.LogDebug((object)("Attempting to remove item '" + item.unlockableName + "' from the store rotation on local client..."));
			}
			if (RotationItemsAPI.RemovePermanentItem(item))
			{
				ManualLogSource? staticLogger3 = Plugin.StaticLogger;
				if (staticLogger3 != null)
				{
					staticLogger3.LogDebug((object)("Removed item '" + item.shopSelectionNode.creatureName + "' from the list of permanent items."));
				}
			}
			Terminal? terminal = Plugin.Terminal;
			if (terminal != null && terminal.ShipDecorSelection.Remove(item.shopSelectionNode))
			{
				ManualLogSource? staticLogger4 = Plugin.StaticLogger;
				if (staticLogger4 != null)
				{
					staticLogger4.LogDebug((object)("Removed item '" + item.shopSelectionNode.creatureName + "' from the current store rotation."));
				}
			}
			if (RotationItemsAPI.UnregisterItem(item))
			{
				ManualLogSource? staticLogger5 = Plugin.StaticLogger;
				if (staticLogger5 != null)
				{
					staticLogger5.LogDebug((object)("Removed item '" + item.shopSelectionNode.creatureName + "' from future store rotations."));
				}
			}
		}

		[HarmonyPatch("UnlockShipObject")]
		[HarmonyPrefix]
		private static void UnlockShipObjectPre(StartOfRound __instance, int unlockableID)
		{
			if (((NetworkBehaviour)__instance).IsHost && unlockableID != -1)
			{
				Config? settings = Plugin.Settings;
				if (settings == null || settings.REMOVE_PURCHASED.Value)
				{
					RemoveFromRotation(__instance.unlockablesList?.unlockables[unlockableID], unlockableID);
				}
			}
		}

		[HarmonyPatch("BuyShipUnlockableClientRpc")]
		[HarmonyPrefix]
		private static void BuyShipUnlockableClientRpcPre(StartOfRound __instance, int unlockableID = -1)
		{
			if (!((NetworkBehaviour)__instance).IsHost && unlockableID != -1)
			{
				Config? settings = Plugin.Settings;
				if (settings == null || settings.REMOVE_PURCHASED.Value)
				{
					RemoveFromRotation(__instance.unlockablesList?.unlockables[unlockableID], unlockableID);
				}
			}
		}
	}
}
namespace StoreRotationConfig.Compatibility
{
	[HarmonyPatch]
	internal static class TerminalFormatterCompatibility
	{
		private static bool? _enabled;

		public static bool Enabled
		{
			get
			{
				bool valueOrDefault = _enabled.GetValueOrDefault();
				if (!_enabled.HasValue)
				{
					valueOrDefault = Chainloader.PluginInfos.ContainsKey("TerminalFormatter");
					_enabled = valueOrDefault;
				}
				return _enabled.Value;
			}
		}

		public static bool Patched { get; internal set; }

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		[HarmonyPatch(typeof(Store), "GetNodeText")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> GetNodeTextTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"[DECORATIONS]", (string)null)
			}).MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(TerminalNode), "itemCost"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Box, (object)typeof(int), (string)null)
			}).SetInstructionAndAdvance(Transpilers.EmitDelegate<Func<TerminalNode, string>>((Func<TerminalNode, string>)delegate(TerminalNode item)
			{
				if (Plugin.Settings == null || SyncedEntry<int>.op_Implicit(Plugin.Settings.SALE_CHANCE) == 0 || !RotationSalesAPI.IsOnSale(item, out var discount))
				{
					return $"{item.itemCost}";
				}
				ManualLogSource? staticLogger = Plugin.StaticLogger;
				if (staticLogger != null)
				{
					staticLogger.LogDebug((object)$"Appending sale tag of '{discount}%' to {item.creatureName}...");
				}
				return RotationSalesAPI.GetTerminalString(item);
			}))
				.SetOperandAndAdvance((object)typeof(string))
				.InstructionEnumeration();
		}
	}
}
namespace StoreRotationConfig.Api
{
	public static class RotationItemsAPI
	{
		private static List<UnlockableItem>? _allItems;

		private static List<UnlockableItem>? _permanentItems;

		public static List<UnlockableItem> AllItems
		{
			get
			{
				return _allItems ?? (_allItems = new List<UnlockableItem>(StartOfRound.Instance.unlockablesList.unlockables.Count + 1));
			}
			private set
			{
				_allItems = value;
			}
		}

		public static List<UnlockableItem> PermanentItems
		{
			get
			{
				return _permanentItems ?? (_permanentItems = new List<UnlockableItem>(StartOfRound.Instance.unlockablesList.unlockables.Count + 1));
			}
			private set
			{
				_permanentItems = value;
			}
		}

		public static void RegisterItem(UnlockableItem? item)
		{
			if (item != null && !AllItems.Contains(item))
			{
				AllItems.Add(item);
			}
		}

		public static bool UnregisterItem(UnlockableItem? item)
		{
			return item != null && AllItems.Count != 0 && AllItems.Remove(item);
		}

		public static void AddPermanentItem(UnlockableItem? item)
		{
			if (item != null && !PermanentItems.Contains(item))
			{
				PermanentItems.Add(item);
			}
		}

		public static bool RemovePermanentItem(UnlockableItem? item)
		{
			return item != null && PermanentItems.Count != 0 && PermanentItems.Remove(item);
		}
	}
	public static class RotationSalesAPI
	{
		private static Dictionary<TerminalNode, int>? RotationSales { get; set; }

		public static bool IsOnSale(TerminalNode? item)
		{
			int discount;
			return IsOnSale(item, out discount);
		}

		public static bool IsOnSale(TerminalNode? item, out int discount)
		{
			discount = 0;
			return (Object)(object)item != (Object)null && RotationSales != null && RotationSales.TryGetValue(item, out discount);
		}

		public static int GetDiscount(TerminalNode? item)
		{
			IsOnSale(item, out var discount);
			return discount;
		}

		public static int GetDiscountedPrice(TerminalNode? item)
		{
			int discount;
			return GetDiscountedPrice(item, out discount);
		}

		public static int GetDiscountedPrice(TerminalNode? item, out int discount)
		{
			discount = 0;
			return ((Object)(object)item != (Object)null) ? (IsOnSale(item, out discount) ? (item.itemCost - (int)((float)item.itemCost * ((float)discount / 100f))) : item.itemCost) : 0;
		}

		public static string GetTerminalString(TerminalNode? item)
		{
			int discount;
			return $"{GetDiscountedPrice(item, out discount)}" + ((discount > 0) ? $"   ({discount}% OFF!)" : "");
		}

		public static bool AddItemDiscount(TerminalNode? item, int discount)
		{
			return (Object)(object)item != (Object)null && RotationSales != null && RotationSales.TryAdd(item, Math.Clamp(discount, 1, 100));
		}

		public static bool RemoveItemDiscount(TerminalNode? item)
		{
			int discount;
			return RemoveItemDiscount(item, out discount);
		}

		public static bool RemoveItemDiscount(TerminalNode? item, out int discount)
		{
			discount = 0;
			return (Object)(object)item != (Object)null && RotationSales != null && RotationSales.Count > 0 && RotationSales.Remove(item, out discount);
		}

		public static void ResetSales(int itemsOnSale)
		{
			RotationSales = new Dictionary<TerminalNode, int>((itemsOnSale <= 0) ? 1 : itemsOnSale);
		}

		public static int CountSales()
		{
			return RotationSales?.Count ?? 0;
		}
	}
}