Decompiled source of LethalMoonUnlocks v2.2.3

BepInEx/plugins/LethalMoonUnlocks.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalConstellations.ConfigManager;
using LethalConstellations.EventStuff;
using LethalConstellations.PluginCore;
using LethalLevelLoader;
using LethalMoonUnlocks.Compatibility;
using LethalMoonUnlocks.Patches;
using LethalMoonUnlocks.Util;
using LethalNetworkAPI;
using LethalQuantities;
using LethalQuantities.Json;
using LethalQuantities.Objects;
using Microsoft.CodeAnalysis;
using OpenLib.Events;
using TerminalStuff.SpecialStuff;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WeatherTweaks;

[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("LethalMoonUnlocks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Permanently unlock moons and more")]
[assembly: AssemblyFileVersion("2.2.3.0")]
[assembly: AssemblyInformationalVersion("2.2.3+795b5756c4e3c21ed0f0100163887245c26042c5")]
[assembly: AssemblyProduct("LethalMoonUnlocks")]
[assembly: AssemblyTitle("LethalMoonUnlocks")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.2.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	internal static class IsExternalInit
	{
	}
}
namespace LethalMoonUnlocks
{
	public class ConfigManager
	{
		private static ConfigFile _configFile;

		private static int _quotaUnlockCountMin;

		private static int _quotaUnlockCountMax;

		private static int _quotaDiscountCountMin;

		private static int _quotaDiscountCountMax;

		private static int _quotaFullDiscountCountMin;

		private static int _quotaFullDiscountCountMax;

		private static int _quotaDiscoveryCountMin;

		private static int _quotaDiscoveryCountMax;

		private static int _travelDiscoveryCountMin;

		private static int _travelDiscoveryCountMax;

		private static int _newDayDiscoveryCountMin;

		private static int _newDayDiscoveryCountMax;

		private static int _salesRateMin;

		private static int _salesRateMax;

		internal static bool ResetWhenFired { get; private set; }

		internal static bool DisplayTerminalTags { get; private set; }

		internal static bool ShowTagInOrbit { get; private set; }

		internal static bool ShowTagNewDiscovery { get; private set; }

		internal static bool ShowTagExplored { get; private set; }

		internal static bool ShowTagUnlockDiscount { get; private set; }

		internal static bool ShowTagPermanentDiscovery { get; private set; }

		internal static bool ShowTagSale { get; private set; }

		internal static bool ShowTagGroups { get; private set; }

		internal static bool ShowAlerts { get; private set; }

		internal static bool ChatMessages { get; private set; }

		internal static bool UnlockMode { get; private set; }

		internal static int UnlocksResetAfterVisits { get; private set; }

		internal static bool UnlocksResetAfterVisitsPermDiscovery { get; private set; }

		internal static bool QuotaUnlocks { get; private set; }

		internal static int QuotaUnlockChance { get; private set; }

		internal static int QuotaUnlockCount => Random.Range(_quotaUnlockCountMin, _quotaUnlockCountMax + 1);

		internal static int QuotaUnlockMaxCount { get; private set; }

		internal static int QuotaUnlockMaxPrice { get; private set; }

		internal static bool DiscountMode { get; private set; }

		private static string DiscountsString { get; set; }

		internal static List<int> Discounts
		{
			get
			{
				string[] array = DiscountsString.Split(',', StringSplitOptions.RemoveEmptyEntries);
				List<int> list = new List<int>();
				string[] array2 = array;
				foreach (string s in array2)
				{
					list.Add(int.Parse(s));
				}
				return list;
			}
		}

		internal static int DiscountsCount => Discounts.Count();

		internal static int DiscountsResetAfterVisits { get; private set; }

		internal static bool DiscountsResetAfterVisitsPermDiscovery { get; private set; }

		internal static bool QuotaDiscounts { get; private set; }

		internal static int QuotaDiscountChance { get; private set; }

		internal static int QuotaDiscountCount => Random.Range(_quotaDiscountCountMin, _quotaDiscountCountMax + 1);

		internal static int QuotaDiscountMaxCount { get; private set; }

		internal static int QuotaDiscountMaxPrice { get; private set; }

		internal static bool QuotaFullDiscounts { get; private set; }

		internal static int QuotaFullDiscountChance { get; private set; }

		internal static int QuotaFullDiscountCount => Random.Range(_quotaFullDiscountCountMin, _quotaFullDiscountCountMax + 1);

		internal static int QuotaFullDiscountMaxCount { get; private set; }

		internal static int QuotaFullDiscountMaxPrice { get; private set; }

		internal static bool DiscoveryMode { get; private set; }

		private static string DiscoveryWhitelist { get; set; }

		internal static List<string> DiscoveryWhitelistMoons => (from m in DiscoveryWhitelist.Split(",", StringSplitOptions.RemoveEmptyEntries)
			select m.Trim()).ToList();

		internal static bool DiscoveryKeepUnlocks { get; private set; }

		internal static bool DiscoveryKeepDiscounts { get; private set; }

		internal static int DiscoveryFreeCountBase { get; private set; }

		internal static int DiscoveryFreeCountIncreaseBy { get; private set; }

		internal static int DiscoveryDynamicFreeCountBase { get; private set; }

		internal static int DiscoveryDynamicFreeCountIncreaseBy { get; private set; }

		internal static int DiscoveryPaidCountBase { get; private set; }

		internal static int DiscoveryPaidCountIncreaseBy { get; private set; }

		internal static int PermanentlyDiscoverFreeMoonsOnLanding { get; private set; }

		internal static int PermanentlyDiscoverPaidMoonsOnLanding { get; private set; }

		internal static bool PermanentlyDiscoverHiddenMoonsOnVisit { get; private set; }

		internal static bool DiscoveryShuffleEveryDay { get; private set; }

		internal static bool DiscoveryNeverShuffle { get; private set; }

		internal static bool QuotaDiscoveries { get; private set; }

		internal static int QuotaDiscoveryChance { get; private set; }

		internal static int QuotaDiscoveryCount => Random.Range(_quotaDiscoveryCountMin, _quotaDiscoveryCountMax + 1);

		internal static bool QuotaDiscoveryPermanent { get; private set; }

		internal static bool QuotaDiscoveryCheapestGroup { get; private set; }

		internal static bool QuotaDiscoveryCheapestGroupFallback { get; private set; }

		internal static bool QuotaDiscoveryCheapestConstellation { get; private set; }

		internal static bool TravelDiscoveries { get; private set; }

		internal static int TravelDiscoveryChance { get; private set; }

		internal static int TravelDiscoveryCount => Random.Range(_travelDiscoveryCountMin, _travelDiscoveryCountMax + 1);

		internal static bool TravelDiscoveryPermanent { get; private set; }

		internal static bool TravelDiscoveryMatchGroup { get; private set; }

		internal static bool TravelDiscoveryMatchGroupFallback { get; private set; }

		internal static bool NewDayDiscoveries { get; private set; }

		internal static int NewDayDiscoveryChance { get; private set; }

		internal static int NewDayDiscoveryCount => Random.Range(_newDayDiscoveryCountMin, _newDayDiscoveryCountMax + 1);

		internal static bool NewDayDiscoveryPermanent { get; private set; }

		internal static bool NewDayDiscoveryMatchGroup { get; private set; }

		internal static bool NewDayDiscoveryMatchGroupFallback { get; private set; }

		internal static bool Sales { get; private set; }

		internal static int SalesChance { get; private set; }

		internal static bool SalesShuffleDaily { get; private set; }

		internal static int SalesRate => Random.Range(_salesRateMin, _salesRateMax);

		private static bool AdvancedPrintMoonNames { get; set; }

		internal static bool AutoRerouteToCompany { get; set; }

		internal static bool GroupCreditsSavingBandAid { get; private set; }

		internal static bool LMUStoryProgression { get; private set; }

		internal static bool EnableStoryProgression { get; private set; }

		internal static bool CheapMoonBiasIgnorePriceChanges { get; private set; }

		internal static bool CheapMoonBiasPaidRotation { get; private set; }

		internal static float CheapMoonBiasPaidRotationValue { get; private set; }

		internal static bool CheapMoonBiasQuotaDiscovery { get; private set; }

		internal static float CheapMoonBiasQuotaDiscoveryValue { get; private set; }

		internal static bool CheapMoonBiasNewDayDiscovery { get; private set; }

		internal static float CheapMoonBiasNewDayDiscoveryValue { get; private set; }

		internal static bool CheapMoonBiasTravelDiscovery { get; private set; }

		internal static float CheapMoonBiasTravelDiscoveryValue { get; private set; }

		internal static bool CheapMoonBiasQuotaUnlock { get; private set; }

		internal static float CheapMoonBiasQuotaUnlockValue { get; private set; }

		internal static bool CheapMoonBiasQuotaDiscount { get; private set; }

		internal static float CheapMoonBiasQuotaDiscountValue { get; private set; }

		internal static bool CheapMoonBiasQuotaFullDiscount { get; private set; }

		internal static float CheapMoonBiasQuotaFullDiscountValue { get; private set; }

		internal static string MoonGroupMatchingMethod { get; private set; }

		internal static int MoonGroupMatchingPriceRange { get; private set; }

		private static string MoonGroupMatchingCustom { get; set; }

		internal static Dictionary<string, List<string>> MoonGroupMatchingCustomDict { get; private set; }

		internal static int TerminalTagLineWidth { get; set; }

		internal static bool TerminalFontSizeOverride { get; set; }

		internal static float TerminalFontSize { get; set; }

		internal static int TerminalScrollAmount { get; set; }

		internal static bool TerminalShowRiskWeather { get; set; }

		internal static bool PreferLQRisk { get; private set; }

		internal static bool MalfunctionsNavigation { get; private set; }

		internal static bool AlertMessageQueueing { get; private set; }

		internal static bool LethalConstellationsOverridePrice { get; private set; }

		internal static bool OverrideHidden { get; private set; }

		private static string OverrideHiddenList { get; set; }

		internal static List<string> OverrideHiddenListMoons => (from m in OverrideHiddenList.Split(",", StringSplitOptions.RemoveEmptyEntries)
			select m.Trim()).ToList();

		internal static bool OverrideLocked { get; private set; }

		private static string OverrideLockedList { get; set; }

		internal static List<string> OverrideLockedListMoons => (from m in OverrideLockedList.Split(",", StringSplitOptions.RemoveEmptyEntries)
			select m.Trim()).ToList();

		internal static void Initialize(ConfigFile cfg)
		{
			string text = Path.Combine(Paths.ConfigPath, "LethalMoonUnlocks.cfg");
			if (File.Exists(text))
			{
				Logger.LogWarning("Legacy config file found. Migrating to default config..");
				MigrateLegacyConfig(text, cfg);
			}
			else
			{
				_configFile = cfg;
			}
		}

		internal static void RefreshConfig()
		{
			Logger.LogInfo("Refreshing config..");
			RefreshValues();
			if (MoonGroupMatchingCustomDict == null)
			{
				MoonGroupMatchingCustomDict = ParseCustomMoonGroups();
			}
		}

		internal static Dictionary<string, List<string>> ParseCustomMoonGroups()
		{
			if (AdvancedPrintMoonNames)
			{
				Logger.LogWarning("Printing available moon names for custom moon groups..");
				Logger.LogWarning(string.Join(", ", from level in PatchedContent.ExtendedLevels
					where level.NumberlessPlanetName != "Gordion" && level.NumberlessPlanetName != "Liquidation"
					select ((Object)level).name) ?? "");
			}
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
			if (MoonGroupMatchingCustom == string.Empty)
			{
				Logger.LogInfo("No custom moon group defined. Skip parsing..");
				return dictionary;
			}
			string[] array = MoonGroupMatchingCustom.Split('|');
			foreach (string obj in array)
			{
				string text = obj.Split(":").First().Trim();
				string text2 = obj.Split(':').Last().Trim();
				string[] array2 = text2.Split(",");
				List<string> list = new List<string>();
				string[] array3 = array2;
				foreach (string text3 in array3)
				{
					list.Add(text3.Trim());
				}
				if (text == null || text == string.Empty)
				{
					Logger.LogWarning("Couldn't parse custom moon group name. Make sure you're using the correct format!");
				}
				else if (text2 == string.Empty || list.Count == 0)
				{
					Logger.LogWarning("Couldn't parse custom moon group! Name null or empty or members not found!");
				}
				else
				{
					dictionary[text] = list;
				}
			}
			Logger.LogInfo("Parsing custom moon groups:");
			Logger.LogInfo(string.Format("{0,-32} {1,-60}", "Name", "Members"));
			foreach (KeyValuePair<string, List<string>> item in dictionary)
			{
				Logger.LogInfo(string.Format("{0,-32} [{1,-60}", item.Key, string.Join(", ", item.Value) + "]"));
			}
			return dictionary;
		}

		private static void RefreshValues()
		{
			//IL_0bb2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bbc: Expected O, but got Unknown
			ResetWhenFired = GetConfigValue("1 - General settings", "Reset when fired", defaultValue: true, "Reset your progress when being fired. Unlocks, Discounts, and permanently discovered moons will all be wiped.\nUnlocks, Discounts, Permanently Discovered moons, ..  all of it will persist unless you create a new save.\nThe only exception to this option is the base selection of moons in Discovery Mode.");
			ChatMessages = GetConfigValue("1 - General settings", "Show chat messages", defaultValue: true, "When enabled, LethalMoonUnlocks will send messages to the in-game chat whenever something relevant happens.");
			ShowAlerts = GetConfigValue("1 - General settings", "Show alert messages", defaultValue: false, "When enabled, LethalMoonUnlocks will display alert messages whenever something relevant happens.");
			DisplayTerminalTags = GetConfigValue("1.1 - Terminal moon tags", "Display tags in terminal", defaultValue: false, "When enabled, LethalMoonUnlocks will display additional tags in the Terminal moon catalog.\nThese tags will indicate various conditions, such as a moon being unlocked or discounted, being on sale, etc.\nIf custom moon groups or matching by LLL tag are enabled, you'll also see the custom groups or LLL tags a moon is associated with.\nNOTE: At this time additional tags will only show in the standard LLL moon catalog and TerminalFormatter. Any other mod replacing the 'moons' command will probably cause issues.");
			ShowTagInOrbit = GetConfigValue("1.1 - Terminal moon tags", "In orbit tag", defaultValue: true, "Display a tag to indicate the moon you're currently orbiting.");
			ShowTagExplored = GetConfigValue("1.1 - Terminal moon tags", "Exploration tag", defaultValue: true, "Display a tag to indicate which moons have not been landed on yet. After landing once, it will keep track of how many times you've landed in total.");
			ShowTagUnlockDiscount = GetConfigValue("1.1 - Terminal moon tags", "Unlock discount tag", defaultValue: true, "Display a tag to indicate Unlocks and Discounts as well as how many routes are left before they expire.");
			ShowTagNewDiscovery = GetConfigValue("1.1 - Terminal moon tags", "New discovery tag", defaultValue: true, "Discovery Mode only: display a tag to indicate which moons are new discoveries i.e. available in the moon catalog for the first time. The tag will vanish when you route to the moon or the moon catalog is shuffled.");
			ShowTagPermanentDiscovery = GetConfigValue("1.1 - Terminal moon tags", "Permanent discovery tag", defaultValue: true, "Discovery Mode only: display a tag to indicate permanently discovered moons.\nDisplays as [PINNED].");
			ShowTagSale = GetConfigValue("1.1 - Terminal moon tags", "Sales tag", defaultValue: true, "Moon Sales only: display a tag to indicate which moons are on sale, as well as the percentage of the sale.");
			ShowTagGroups = GetConfigValue("1.1 - Terminal moon tags", "Group tag", defaultValue: true, "Moon Group Matching only: display a tag to indicate groups a moon belongs to. Limited to custom group, LethalConstellations and LLL tag matching methods.");
			UnlockMode = GetConfigValue("2 - Unlock Mode (Default)", "Enable Unlock Mode", defaultValue: true, "Unlock Mode is the default mode, akin to the original Permanent Moons mod. In Unlock Mode, when you buy a paid moon, it will be 'unlocked'.\nOnce unlocked, moons are completely free, and by default, will stay free permanently.\nNOTE: This setting and all settings relating to Unlocks will have no effect if Discount Mode is enabled!");
			UnlocksResetAfterVisits = GetConfigValue("2 - Unlock Mode (Default)", "Unlocks expire", 0, "Unlocks will expire after a set number of free routes, after which they will become paid again.\nSet to 0 to disable this feature.");
			DiscoveryKeepUnlocks = GetConfigValue("2 - Unlock Mode (Default)", "Unlocked moons are permanently discovered", defaultValue: false, "Discovery Mode only: Every unlocked moon is also permanently discovered i.e. added to the moon catalog on top of your base selection.");
			UnlocksResetAfterVisitsPermDiscovery = GetConfigValue("2 - Unlock Mode (Default)", "Reset permanent discovery on unlock expiry", defaultValue: false, "Discovery Mode only: Reset a moon's permanent discovery status when its unlock expires.\nThis is the only way permanent discoveries can vanish during a run in Unlock Mode.\n");
			QuotaUnlocks = GetConfigValue("2.1 - Quota Unlocks", "Enable Quota Unlocks", defaultValue: false, "Quota Unlocks are rewarded for meeting the quota. When triggered, Quota Unlocks will grant you one or more unlocks for free.\nThe moons that are unlocked are randomly selected.");
			QuotaUnlockChance = GetConfigValue("2.1 - Quota Unlocks", "Quota Unlock trigger chance", 100, "The chance to trigger a Quota Unlock every time you meet the quota.", new AcceptableValueRange<int>(0, 100));
			_quotaUnlockCountMin = GetConfigValue("2.1 - Quota Unlocks", "Minimum unlocked moon count", 1, "The minimum number of moons that will be unlocked each time a Quota Unlock is triggered.", new AcceptableValueRange<int>(1, 10));
			_quotaUnlockCountMax = GetConfigValue("2.1 - Quota Unlocks", "Maximum unlocked moon count", 1, "The maximum number of moons that will be unlocked each time a Quota Unlock is triggered.", new AcceptableValueRange<int>(1, 10));
			QuotaUnlockMaxPrice = GetConfigValue("2.1 - Quota Unlocks", "Maximum moon price to unlock", 0, "Only consider moons up to this price to be unlocked.\nSet to 0 to disable this feature.");
			QuotaUnlockMaxCount = GetConfigValue("2.1 - Quota Unlocks", "Limit number of unlocks", 0, "Limit how many Quota Unlocks you can receive during a run. After reaching the limit, Quota Unlocks will no longer be granted.\nSet to 0 to disable this feature.");
			DiscountMode = GetConfigValue("3 - Discount Mode", "Enable Discount Mode", defaultValue: false, "In Discount Mode, Unlocks are replaced with Discounts.\nEach time you route to a paid moon, you will unlock the next available discount rate until the final discount is reached.\nThe discount rates are fully customizable.");
			DiscountsString = GetConfigValue("3 - Discount Mode", "Discount rates", "50,75,100", "The discount rates that are applied to moon prices as a % off of the original routing price.\nFor example, '50,75,100', would make each moon 50% off after the first purchase, 75% off after the second purchase, and free after the third purchase.\nDiscount rates are separated by commas and can contain any number of rates");
			Logger.LogInfo("Discount rates (% off): " + string.Join(", ", Discounts.Select((int discount) => discount + "%")));
			DiscountsResetAfterVisits = GetConfigValue("3 - Discount Mode", "Discounts expire", 0, "Discounts will expire after a set number of free routes, after which they will return to their original price.\nSet to 0 to disable this feature.\nNOTE: The final discount rate must be set to '100' for this to work!");
			DiscoveryKeepDiscounts = GetConfigValue("3 - Discount Mode", "Discounted moons are permanently discovered", defaultValue: false, "Discovery Mode only: Every discounted moon is also permanently discovered i.e. added to the moon catalog on top of your base selection.");
			DiscountsResetAfterVisitsPermDiscovery = GetConfigValue("3 - Discount Mode", "Reset permanent discoveries on discount expiry", defaultValue: false, "Discovery Mode only: Reset a moon's permanent discovery status when its discount expires.\nThis is the only way permanent discoveries can vanish during a run in Discount Mode.\n");
			QuotaDiscounts = GetConfigValue("3.1 - Quota Discounts", "Enable Quota Discounts", defaultValue: false, "Quota Discounts are rewarded for meeting the quota. When triggered Quota Discounts will grant you one or more discounts for free.\nThe moons that are discounted are randomly selected.");
			QuotaDiscountChance = GetConfigValue("3.1 - Quota Discounts", "Quota Discount trigger chance", 100, "The chance to trigger a Quota Discount every time you meet the quota.\n", new AcceptableValueRange<int>(0, 100));
			_quotaDiscountCountMin = GetConfigValue("3.1 - Quota Discounts", "Minimum discounted moon count", 1, "The minimum number of moons that will receive a discount each time a Quota Discount is triggered.", new AcceptableValueRange<int>(1, 10));
			_quotaDiscountCountMax = GetConfigValue("3.1 - Quota Discounts", "Maximum discounted moon count", 1, "The maximum number of moons that will receive a discount each time a Quota Discount is triggered.", new AcceptableValueRange<int>(1, 10));
			QuotaDiscountMaxPrice = GetConfigValue("3.1 - Quota Discounts", "Maximum moon price to discount", 0, "Only consider moons up to this price to receive a discount.\nSet to 0 to disable this feature");
			QuotaDiscountMaxCount = GetConfigValue("3.1 - Quota Discounts", "Limit number of discounts", 0, "Limit how many Quota Discounts you can receive during a run. After reaching the limit, Quota Discounts will no longer be granted.\nSet to 0 to disable this feature");
			QuotaFullDiscounts = GetConfigValue("3.2 - Quota Full Discounts", "Enable Quota Full Discounts", defaultValue: false, "Quota Full Discounts are rewarded for meeting the quota. When triggered, Quota Full Discounts will apply the final discount rate to one or more moons for free.\nThe moons that are discounted are randomly selected.");
			QuotaFullDiscountChance = GetConfigValue("3.2 - Quota Full Discounts", "Quota Full Discount trigger chance", 100, "The chance to trigger a Quota Full Discount every time you meet the quota.", new AcceptableValueRange<int>(0, 100));
			_quotaFullDiscountCountMin = GetConfigValue("3.2 - Quota Full Discounts", "Minimum fully discounted moon count", 1, "The minimum number of moons that will receive a full discount each time a Quota Full Discount is triggered.", new AcceptableValueRange<int>(1, 10));
			_quotaFullDiscountCountMax = GetConfigValue("3.2 - Quota Full Discounts", "Maximum fully discounted moon count", 1, "The maximum number of moons that will receive a full discount each time a Quota Full Discount is triggered.", new AcceptableValueRange<int>(1, 10));
			QuotaFullDiscountMaxPrice = GetConfigValue("3.2 - Quota Full Discounts", "Maximum moon price to fully discount", 0, "Only consider moons up to this price to receive a full discount.\nSet to 0 to disable this feature");
			QuotaFullDiscountMaxCount = GetConfigValue("3.2 - Quota Full Discounts", "Limit number of full discounts", 0, "Limit how many Quota Full Discounts you can receive during a run. After reaching the limit, Quota Full Discounts will no longer be granted.\nSet to 0 to disable this feature");
			DiscoveryMode = GetConfigValue("4 - Discovery Mode", "Enable Discovery Mode", defaultValue: false, "In Discovery Mode, you start with a limited selection of moons in the Terminal's moon catalog.\nBy default, this base selection of moons will be shuffled after every quota, and can also be configured to expand over time.\nThere are also various options to discover additional moons as you play.\nPermanently discovered moons are added to the moon catalog on top of the base selection, and are not lost on shuffle.");
			DiscoveryNeverShuffle = GetConfigValue("4 - Discovery Mode", "Never shuffle", defaultValue: false, "Never shuffle the rotation of moons available in the moon catalog.\nNew moons must be discovered through other means, but once discovered, they won't vanish, since the selection is never shuffled.\nNOTE: Overrides the 'Shuffle every day' option.");
			DiscoveryShuffleEveryDay = GetConfigValue("4 - Discovery Mode", "Shuffle every day", defaultValue: false, "Shuffle the rotation of moons available in the moon catalog every day, instead of after every quota.");
			DiscoveryWhitelist = GetConfigValue("4 - Discovery Mode", "Whitelist", "", "List of moons to keep discovered at all times.\nFor example, 'Experimentation, Assurance, Vow' would make these three moons start out as permanently discovered on every run.\nMoon names must be separated by commas and must be exact matches. You can print the moon names to console/log by using the option in 'Advanced Settings'.");
			DiscoveryFreeCountBase = GetConfigValue("4 - Discovery Mode", "Free moons base count", 1, "The base amount of randomly selected free moons available in the moon catalog.\nNOTE: 'Free' only considers moons that are free by default, or configured to be free. Moons that are free due to unlocks or discounts are excluded!");
			DiscoveryDynamicFreeCountBase = GetConfigValue("4 - Discovery Mode", "Dynamic free moons base count", 2, "The base amount of randomly selected dynamic free moons available in the moon catalog.\nNOTE: 'Dynamic free' considers moons that are free due to unlocks or discounts in addition to those that are free by default, or configured to be free.");
			DiscoveryPaidCountBase = GetConfigValue("4 - Discovery Mode", "Paid moons base count", 3, "The base amount of randomly selected paid moons available in the moon catalog.\nThis is your paid moon rotation and typically the main way to discover new moons to buy - earning unlocks and discounts as you progress.");
			DiscoveryFreeCountIncreaseBy = GetConfigValue("4 - Discovery Mode", "Increase free moon count on shuffle", 0, "The amount of randomly selected free moons added to the rotation each time it's shuffled.\nSet to 0 to disable this feature.");
			DiscoveryDynamicFreeCountIncreaseBy = GetConfigValue("4 - Discovery Mode", "Increase dynamic free moon count on shuffle by", 0, "The amount of randomly selected dynamic free moons added to the rotation each time it's shuffled.\nSet to 0 to disable this feature.");
			DiscoveryPaidCountIncreaseBy = GetConfigValue("4 - Discovery Mode", "Increase paid moon count on shuffle", 0, "The amount of randomly selected paid moons added to the rotation each time it's shuffled.\nSet to 0 to disable this feature.");
			PermanentlyDiscoverFreeMoonsOnLanding = GetConfigValue("4 - Discovery Mode", "Landings required to permanently discover free moons", -1, "Any free moon will be permanently discovered after a set amount of landings.\nSet to -1 to disable this feature.\nNOTE: A value of 0 makes every free moon ever discovered in any way permanently discovered. Not recommended.");
			PermanentlyDiscoverPaidMoonsOnLanding = GetConfigValue("4 - Discovery Mode", "Landings required to permanently discover paid moons", -1, "Any free moon will be permanently discovered after a set amount of landings.\nSet to -1 to disable this feature.\nNOTE: A value of 0 makes every paid moon ever discovered in any way permanently discovered. Not recommended.");
			PermanentlyDiscoverHiddenMoonsOnVisit = GetConfigValue("4 - Discovery Mode", "Permanently discover hidden moons after routing", defaultValue: false, "Any hidden (LLL config e.g. Embrion) will be permanently discovered after routed to once.");
			QuotaDiscoveries = GetConfigValue("4.1 - Quota Discoveries", "Enable Quota Discoveries", defaultValue: false, "Quota Discoveries grant additional moon discoveries when a new quota begins.\nThe moons that are discovered are randomly selected.");
			QuotaDiscoveryChance = GetConfigValue("4.1 - Quota Discoveries", "Quota Discovery trigger chance", 100, "The chance to trigger a Quota Discovery every time you meet the quota.", new AcceptableValueRange<int>(0, 100));
			_quotaDiscoveryCountMin = GetConfigValue("4.1 - Quota Discoveries", "Minimum quota discovery moon count", 1, "The minimum number of moons that will be discovered each time a Quota Discovery is triggered.", new AcceptableValueRange<int>(1, 10));
			_quotaDiscoveryCountMax = GetConfigValue("4.1 - Quota Discoveries", "Maximum quota discovery moon count", 1, "The maximum number of moons that will be discovered each time a Quota Discovery is triggered.", new AcceptableValueRange<int>(1, 10));
			QuotaDiscoveryPermanent = GetConfigValue("4.1 - Quota Discoveries", "Quota Discoveries are permanent", defaultValue: false, "Moons discovered through Quota Discoveries will stay permanently discovered i.e. they won't vanish on shuffle.");
			QuotaDiscoveryCheapestGroup = GetConfigValue("4.1 - Quota Discoveries", "Quota Discovery match cheapest group", defaultValue: false, "Only considers moons from the group/constellation that has the currently cheapest undiscovered moon.\nCan effectively discover the 'next tier' or group of moons. Set counts high to discover the entire group.\nNOTE: Highly recommended to only use this with 'Quota Discoveries are permanent' or 'Never shuffle'!");
			QuotaDiscoveryCheapestGroupFallback = GetConfigValue("4.1 - Quota Discoveries", "Quota Discovery match cheapest group fallback", defaultValue: true, "When enabled will fallback to selecting from all discoverable moons when no moons could be matched.\nNOTE: Only relevant when you have moons that are not assigned to any group/constellation.");
			QuotaDiscoveryCheapestConstellation = GetConfigValue("4.1 - Quota Discoveries", "Quota Discovery match cheapest constellation", defaultValue: false, "Only consider moons of the cheapest constellation. Overrides behaviour of 'match cheapest group'. \nNOTE: Match cheapest group needs to be enabled.");
			TravelDiscoveries = GetConfigValue("4.2 - Travel Discoveries", "Enable Travel Discoveries", defaultValue: false, "Travel Discoveries grant additional moon discoveries when routing to a paid moon\nThe moons that are discovered are randomly selected.");
			TravelDiscoveryChance = GetConfigValue("4.2 - Travel Discoveries", "Travel Discovery trigger chance", 20, "The chance to trigger a Travel Discovery every time you route to a paid moon.", new AcceptableValueRange<int>(0, 100));
			_travelDiscoveryCountMin = GetConfigValue("4.2 - Travel Discoveries", "Minimum travel discovery moon count", 1, "The minimum number of moons that will be discovered each time a Travel Discovery is triggered.", new AcceptableValueRange<int>(1, 10));
			_travelDiscoveryCountMax = GetConfigValue("4.2 - Travel Discoveries", "Maximum travel discovery moon count", 1, "The maximum number of moons that will be discovered each time a Travel Discovery is triggered.", new AcceptableValueRange<int>(1, 10));
			TravelDiscoveryPermanent = GetConfigValue("4.2 - Travel Discoveries", "Travel Discoveries are permanent", defaultValue: false, "Moons discovered through Travel Discoveries will stay permanently discovered i.e. they won't vanish on shuffle.");
			TravelDiscoveryMatchGroup = GetConfigValue("4.2 - Travel Discoveries", "Travel Discovery group matching", defaultValue: false, "Only consider moons of the same group you're routing to for Travel Discoveries.");
			TravelDiscoveryMatchGroupFallback = GetConfigValue("4.2 - Travel Discoveries", "Travel Discovery group matching fallback", defaultValue: true, "When enabled will fallback to selecting from all discoverable moons when no moons could be matched.\nNOTE: It is recommended to keep this on for matching by exact price but with other methods you might prefer to turn it off.");
			NewDayDiscoveries = GetConfigValue("4.3 - New Day Discoveries", "Enable New Day Discoveries", defaultValue: false, "New Day Discoveries grant additional moon discoveries at the start of a new day.\nThe moons that are discovered are randomly selected.");
			NewDayDiscoveryChance = GetConfigValue("4.3 - New Day Discoveries", "New Day Discovery trigger chance", 20, "The chance to trigger a New Day Discovery at the start of a new day.\nMake it a random occurence or guaranteed.", new AcceptableValueRange<int>(0, 100));
			_newDayDiscoveryCountMin = GetConfigValue("4.3 - New Day Discoveries", "Minimum new day discovery moon count", 1, "The minimum number of moons to be discovered each time a New Day Discovery is granted.", new AcceptableValueRange<int>(1, 10));
			_newDayDiscoveryCountMax = GetConfigValue("4.3 - New Day Discoveries", "Maximum new day discovery moon count", 1, "The maximum number of moons to be discovered each time a New Day Discovery is granted.", new AcceptableValueRange<int>(1, 10));
			NewDayDiscoveryPermanent = GetConfigValue("4.3 - New Day Discoveries", "New Day Discoveries are permanent", defaultValue: false, "Moons discovered through New Day Discoveries will stay permanently discovered i.e. they won't vanish on shuffle.");
			NewDayDiscoveryMatchGroup = GetConfigValue("4.3 - New Day Discoveries", "New Day Discovery group matching", defaultValue: false, "Only consider moons of the same group as the moon you're currently orbiting for New Day Discoveries.");
			NewDayDiscoveryMatchGroupFallback = GetConfigValue("4.3 - New Day Discoveries", "New Day Discovery group matching fallback", defaultValue: true, "When enabled will fallback to selecting from all discoverable moons when no moons could be matched.\nNOTE: It is recommended to keep this on for matching by exact price but with other methods you might prefer to turn it off.");
			Sales = GetConfigValue("5 - Moon Sales", "Moon Sales", defaultValue: false, "Each moon has a chance to go on sale for a reduced routing price.\nBy default, Moon Sales are shuffled after every quota. Only non-free moons can go on sale.\nNOTE: These sales are separate from discounts received via Discount Mode.");
			SalesShuffleDaily = GetConfigValue("5 - Moon Sales", "Shuffle sales daily", defaultValue: false, "Shuffle moon sales daily, instead of after every quota");
			SalesChance = GetConfigValue("5 - Moon Sales", "Moon Sale chance", 20, "The chance for each moon to go on sale every time sales are shuffled.", new AcceptableValueRange<int>(0, 100));
			_salesRateMin = GetConfigValue("5 - Moon Sales", "Minimum sale percent", 5, "The minimum sale percentage a moon can receive.", new AcceptableValueRange<int>(0, 100));
			_salesRateMax = GetConfigValue("5 - Moon Sales", "Maximum sale percent", 30, "The maximum sale percentage a moon can receive", new AcceptableValueRange<int>(1, 100));
			GetConfigValue("6 - Advanced Settings", "I have read this", "false", "This section contains advanced configuration options for various features of the mod. Incorrectly tweaking these might cause unexpected behaviour!\nThis setting has no effect.");
			GroupCreditsSavingBandAid = GetConfigValue("6 - Advanced Settings", "Group credits saving fix", defaultValue: true, "When LMU saves data it will also save the credits balance.\nThis prevents the 'free moon exploit'. This band aid should not cause any issues but I don't think I should need to do this in the first place..");
			AdvancedPrintMoonNames = GetConfigValue("6 - Advanced Settings", "Print moon names to console", defaultValue: false, "Print the names you need to define your custom groups to console/log. They will be logged after you've loaded into a save game. You can also grab moons names from the LMU table that is periodically printed to logs even when this is not enabled.");
			AutoRerouteToCompany = GetConfigValue("6 - Advanced Settings", "Auto reroute to company", defaultValue: true, "When enabled automatically reroutes the ship to the company on deadline day.");
			CheapMoonBiasPaidRotation = GetConfigValue("6.1 - Cheap Moon Bias", "Discovery Mode paid rotation", defaultValue: true, "Use Cheap Moon Bias when selecting moons for the paid moon rotation when it's shuffled.");
			CheapMoonBiasPaidRotationValue = GetConfigValue("6.1 - Cheap Moon Bias", "Discovery Mode paid rotation bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasQuotaDiscovery = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Discovery", defaultValue: true, "Use Cheap Moon Bias when selecting moons during Quota Discovery.");
			CheapMoonBiasQuotaDiscoveryValue = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Discovery bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasTravelDiscovery = GetConfigValue("6.1 - Cheap Moon Bias", "Travel Discovery", defaultValue: true, "Use Cheap Moon Bias when selecting moons to discover during Travel Discovery.");
			CheapMoonBiasTravelDiscoveryValue = GetConfigValue("6.1 - Cheap Moon Bias", "Travel Discovery bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasNewDayDiscovery = GetConfigValue("6.1 - Cheap Moon Bias", "New Day Discovery", defaultValue: true, "Use Cheap Moon Bias when selecting moons during New Day Discovery.");
			CheapMoonBiasNewDayDiscoveryValue = GetConfigValue("6.1 - Cheap Moon Bias", "New Day Discovery bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasQuotaUnlock = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Unlock", defaultValue: true, "Use Cheap Moon Bias when selecting moons during Quota Unlocks.");
			CheapMoonBiasQuotaUnlockValue = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Unlock bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasQuotaDiscount = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Discount", defaultValue: true, "Use Cheap Moon Bias when selecting moons during Quota Discounts.");
			CheapMoonBiasQuotaDiscountValue = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Discount bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasQuotaFullDiscount = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Full Discount", defaultValue: true, "Use Cheap Moon Bias when selecting moons during Quota Full Discounts.");
			CheapMoonBiasQuotaFullDiscountValue = GetConfigValue("6.1 - Cheap Moon Bias", "Quota Full Discount bias value", 0.66f, "Set bias value to adjust how heavily cheap moons are preferred.\nNOTE: the bias is exponential to the inverse proportion of moon price to the total of all moon prices. Let's say we have Moon A (100 credits) and Moon B (400 credits).\nB is four times the price of A => Weight of A = (4^bias) * weight of B. So at 1.0 A is four times the chance of B, at 2.0 16 times, at 0.5 sqrt(4) = 2 times and at 0.0 both have equal weights.", new AcceptableValueRange<float>(0f, 2f));
			CheapMoonBiasIgnorePriceChanges = GetConfigValue("6.1 - Cheap Moon Bias", "Ignore price changes", defaultValue: true, "Ignore any changes to moon prices by discounts or sales and only consider original price for biased selections.");
			MoonGroupMatchingMethod = _configFile.Bind<string>("6.2 - Moon Group Matching", "Group Matching Method", "Price", new ConfigDescription("The method used to group moons. Group Matching can be used to limit some discoveries to moons of the same group.\n'Price': All moons of the same price are considered a group. This method ignores price changes by unlocks, discounts, or sales.\n'PriceRange': All moons within a set price range are considered a group. Upper and lower range is defined by the price range setting below.\n'PriceRangeUpper': All moons within a set upper price range are considered a group. Upper range is defined by the price range setting below.\n'Tag': All moons that have at least one tag in common are considered a group.\n'LethalConstellations': Match moons to their constellations as they are configured in LethalConstellations. See settings in Advanced section.'Custom': Define custom named groups of moons below.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "Price", "PriceRange", "PriceRangeUpper", "Tag", "LethalConstellations", "Custom" }), Array.Empty<object>())).Value;
			MoonGroupMatchingPriceRange = GetConfigValue("6.2 - Moon Group Matching", "Price range", 200, "The price range used for matching moons via 'PriceRange' and 'PriceRangeUpper' methods.\nIt will match all moons priced within the original price +- this value (+ this value for upper range).");
			MoonGroupMatchingCustom = GetConfigValue("6.2 - Moon Group Matching", "Custom moon groups", "", "Define your own custom moon groups.\nExpected Format: Separate moon groups by \"|\" and moons by \",\".\nExample: 'Group name 1: Experimentation, Assurance, Vow | Group name 2: Offense, March, Adamance'\nNames must be exact matches. The option below can be used to get the names.");
			TerminalTagLineWidth = GetConfigValue("6.3 - Terminal", "Maximum tag line length", 49, "By default LMU tries to fit as many tags as possible into a single line.\nDecrease this value if you want to have a more organized look at the cost of more scrolling depending on the amount of tags you see.\nNOTE: Don't worry about setting it too low. It will always put at least one tag per line. Only if any additional tag would exceed this value it puts a line break.\nDo not set it larger than default unless you are also decreasing font size below.", new AcceptableValueRange<int>(10, 100));
			TerminalFontSizeOverride = GetConfigValue("6.3 - Terminal", "Override Terminal font size", defaultValue: true, "Override the font size in the Terminal's moon catalog.\nPrevents inconsistencies with formatting. Disable to let LLL dynamically size the font depending on the number of moons visible\nNOTE: With very few moons you might see some ugly line breaks with custom weathers with long names (Meteor Shower).");
			TerminalFontSize = GetConfigValue("6.3 - Terminal", "Terminal font size", 15f, "Customize the Terminal's moon catalog font size.\nNOTE: When using smaller fonts you can increase the maximum tag line width above.", new AcceptableValueRange<float>(8f, 15f));
			TerminalScrollAmount = GetConfigValue("6.3 - Terminal", "Terminal scroll amount", 0, "Override the Terminal's moon catalog scroll amount. Lines per scroll action. 0 to disable\nNOTE: This can help when you have so many moons that some are skipped when scrolling.", new AcceptableValueRange<int>(0, 20));
			TerminalShowRiskWeather = GetConfigValue("6.3 - Terminal", "Terminal show weather in risk preview", defaultValue: false, "Also show the weather when using `preview difficulty`");
			AlertMessageQueueing = GetConfigValue("6.4 - Compatibility", "Avoid alert messages overlapping", defaultValue: true, "When enabled, LethalMoonUnlocks will intercept all alert messages (yellow/red pop-up) and add them to a queue. This avoids alert messages from other mods and Vanilla from overlapping or not showing at all. Disable if you experience issues.");
			PreferLQRisk = GetConfigValue("6.4 - Compatibility", "Prefer LethalQuantities risk level", defaultValue: false, "Show the moon risk levels set by LethalQuantities in the moon catalog instead of the default risk levels.");
			MalfunctionsNavigation = GetConfigValue("6.4 - Compatibility", "Malfunctions navigation buys moon", defaultValue: false, "When the Malfunctions navigation malfunction is triggered LMU will interpret it as if the moon routed to was bought.");
			LethalConstellationsOverridePrice = GetConfigValue("6.4 - Compatibility", "LethalConstellations override price", defaultValue: false, "When enabled and LethalConstellations is present override the constellation routing price with the default moon's routing price.\nRouting to the constellation will be considered buying the default moon. Consequently unlocks, discounts and sales of the default moon will be granted and will also apply to the constellation routing price.\nNOTE: In Discovery Mode the default moon will always be set to the cheapest currently discovered moon of that constellation regardless of this setting.");
			OverrideHidden = GetConfigValue("6.5 - Overrides", "Override moons hidden by default", defaultValue: false, "Enable to hard override any hidden by default information using the list below. Any other information will be ignored. This includes moons hidden in vanilla, via LLL config, etc.");
			OverrideHiddenList = GetConfigValue("6.5 - Overrides", "Override hidden list", "", "List of moons LMU will consider to be hidden by default.\nFor example, 'Vow, March, Artifice'. Those three will be the only moons hidden by default. You can still unhide them in various ways. Note that setting this would make Embrion not hidden.\nMoon names must be separated by commas and must be exact matches. You can print the moon names to console/log by using the option in 'Advanced Settings'.");
			OverrideLocked = GetConfigValue("6.5 - Overrides", "Override moons locked by default", defaultValue: false, "Enable to hard override any locked by default information using the list below. Any other information will be ignored. This includes moons locked in vanilla, via LLL config, etc.");
			OverrideLockedList = GetConfigValue("6.5 - Overrides", "Override locked list", "", "List of moons LMU will consider to be locked by default.\nFor example, 'Vow, March, Artifice'. Those three will be the only moons locked by default.\nMoon names must be separated by commas and must be exact matches. You can print the moon names to console/log by using the option in 'Advanced Settings'.");
			EnableStoryProgression = GetConfigValue("6.6 - Story Progression", "Enable Story Progression", defaultValue: true, "Story progression allows locking moons behind various conditions. This can be employed by other mods like Wesley's moons (JLL).\nDisabling this settings will globally ignore any requests to lock moons behind story progressions inlcuding LMU's own Vanilla Story progression.");
			LMUStoryProgression = GetConfigValue("6.6 - Story Progression", "Vanilla Story Progression", defaultValue: false, "Enable to lock the two hidden vanilla moons behind story progression. To release the lock for Artifice you have to land three times on Adamance, for Embrion you have to scan an old bird. After completing these tasks the moons will be available (for discovery). They will not be hidden.");
		}

		private static T GetConfigValue<T>(string section, string key, T defaultValue, string description)
		{
			return _configFile.Bind<T>(section, key, defaultValue, description).Value;
		}

		private static T GetConfigValue<T>(string section, string key, T defaultValue, string description, AcceptableValueRange<int> range)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			return _configFile.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)range, Array.Empty<object>())).Value;
		}

		private static T GetConfigValue<T>(string section, string key, T defaultValue, string description, AcceptableValueRange<float> range)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			return _configFile.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)range, Array.Empty<object>())).Value;
		}

		private static void MigrateLegacyConfig(string legacyConfigPath, ConfigFile cfg)
		{
			File.Copy(legacyConfigPath, Path.Combine(Paths.ConfigPath, "com.xmods.lethalmoonunlocks.cfg"), overwrite: true);
			_configFile = cfg;
			_configFile.Reload();
			RefreshValues();
			Logger.LogInfo("Legacy configuration migrated. Renaming legacy config file..");
			File.Copy(legacyConfigPath, Path.Combine(Paths.ConfigPath, "com.xmods.lethalmoonunlocks.cfg.legacy"), overwrite: true);
			File.Delete(legacyConfigPath);
		}
	}
	internal readonly struct LMGroup
	{
		internal string Name { get; init; }

		internal List<LMUnlockable> Members { get; init; }

		public LMGroup()
		{
			Name = string.Empty;
			Members = new List<LMUnlockable>();
		}
	}
	[Serializable]
	[ES3Serializable]
	internal class LMUnlockable
	{
		[NonSerialized]
		[ES3NonSerializable]
		internal ExtendedLevel ExtendedLevel;

		[SerializeField]
		[ES3Serializable]
		private bool _originallyLocked;

		[SerializeField]
		[ES3Serializable]
		private bool _originallyHidden;

		[SerializeField]
		[ES3Serializable]
		internal string Name { get; private set; }

		[ES3NonSerializable]
		internal int OriginalPrice { get; private set; }

		internal bool OriginallyLocked
		{
			get
			{
				if (ConfigManager.OverrideLocked)
				{
					return ConfigManager.OverrideLockedListMoons.Contains(Name);
				}
				return _originallyLocked;
			}
			private set
			{
				_originallyLocked = value;
			}
		}

		internal bool OriginallyHidden
		{
			get
			{
				if (ConfigManager.OverrideHidden)
				{
					return ConfigManager.OverrideHiddenListMoons.Contains(Name);
				}
				return _originallyHidden;
			}
			private set
			{
				_originallyHidden = value;
			}
		}

		[SerializeField]
		[ES3Serializable]
		internal bool RemainingHidden { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool StoryUnlock { get; private set; }

		[SerializeField]
		[ES3Serializable]
		internal bool StoryIsUnlocked { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal int BuyCount { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal int VisitCount { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal int FreeVisitCount { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal int LandingCount { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool Discovered { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool NewDiscovery { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool DiscoveredOnce { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool PermanentlyDiscovered { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal bool OnSale { get; set; }

		[SerializeField]
		[ES3Serializable]
		internal int SalesRate { get; set; }

		[SerializeField]
		[ES3NonSerializable]
		private int RoutePrice { get; set; }

		internal LMUnlockable(ExtendedLevel extendedLevel)
		{
			Name = extendedLevel.NumberlessPlanetName;
			ExtendedLevel = extendedLevel;
			OriginalPrice = extendedLevel.RoutePrice;
			OriginallyHidden = extendedLevel.IsRouteHidden;
			OriginallyLocked = extendedLevel.IsRouteLocked;
			if (OriginallyHidden && !OriginallyLocked)
			{
				RemainingHidden = true;
			}
		}

		internal void OverrideData(LMUnlockable newData)
		{
			if (Name != newData.Name)
			{
				Logger.LogError("Name mismatch during override LMUnlockable data!");
				return;
			}
			OriginallyHidden = newData.OriginallyHidden;
			OriginallyLocked = newData.OriginallyLocked;
			RemainingHidden = newData.RemainingHidden;
			StoryUnlock = newData.StoryUnlock;
			StoryIsUnlocked = newData.StoryIsUnlocked;
			BuyCount = newData.BuyCount;
			VisitCount = newData.VisitCount;
			FreeVisitCount = newData.FreeVisitCount;
			LandingCount = newData.LandingCount;
			Discovered = newData.Discovered;
			NewDiscovery = newData.NewDiscovery;
			DiscoveredOnce = newData.DiscoveredOnce;
			PermanentlyDiscovered = newData.PermanentlyDiscovered;
			OnSale = newData.OnSale;
			SalesRate = newData.SalesRate;
			RoutePrice = newData.RoutePrice;
		}

		internal void RestoreOriginalState()
		{
			ExtendedLevel.RoutePrice = OriginalPrice;
			ExtendedLevel.IsRouteHidden = _originallyHidden;
			ExtendedLevel.IsRouteLocked = _originallyLocked;
		}

		internal void DesignateAsStoryLocked()
		{
			StoryUnlock = true;
			OriginallyHidden = true;
			OriginallyLocked = true;
		}

		internal void IterateState()
		{
			RoutePrice = CalculatePrice();
			if (BuyCount > 0 && ((ConfigManager.UnlockMode && !ConfigManager.DiscountMode && ConfigManager.DiscoveryKeepUnlocks) || (ConfigManager.DiscountMode && ConfigManager.DiscoveryKeepDiscounts)) && !PermanentlyDiscovered)
			{
				PermanentlyDiscovered = true;
				Logger.LogInfo(Name + " set to permanently discovered because it's " + (ConfigManager.UnlockMode ? "unlocked" : "discounted."));
			}
			if (Discovered && !OriginallyHidden && !OriginallyLocked && OriginalPrice == 0 && ConfigManager.PermanentlyDiscoverFreeMoonsOnLanding >= 0 && LandingCount >= ConfigManager.PermanentlyDiscoverFreeMoonsOnLanding && !PermanentlyDiscovered)
			{
				PermanentlyDiscovered = true;
				Logger.LogInfo($"{Name} set to permanently discovered because it's been landed on {LandingCount} times.");
			}
			if (Discovered && !OriginallyHidden && !OriginallyLocked && OriginalPrice > 0 && ConfigManager.PermanentlyDiscoverPaidMoonsOnLanding >= 0 && LandingCount >= ConfigManager.PermanentlyDiscoverPaidMoonsOnLanding && !PermanentlyDiscovered)
			{
				PermanentlyDiscovered = true;
				Logger.LogInfo($"{Name} set to permanently discovered because it's been landed on {LandingCount} times.");
			}
			if (OriginallyHidden && !OriginallyLocked)
			{
				if (ConfigManager.PermanentlyDiscoverHiddenMoonsOnVisit && VisitCount > 0)
				{
					RemainingHidden = false;
					PermanentlyDiscovered = true;
				}
				RemainingHidden = true;
				PermanentlyDiscovered = false;
			}
			else
			{
				RemainingHidden = false;
			}
			if (!DiscoveredOnce && Discovered)
			{
				NewDiscovery = true;
				DiscoveredOnce = true;
			}
			if (Name == "Adamance" && LandingCount > 2)
			{
				LMUnlockable lMUnlockable = UnlockManager.Instance.Unlocks.FirstOrDefault((LMUnlockable u) => u.Name == "Artifice");
				if (lMUnlockable != null && lMUnlockable.StoryUnlock && !lMUnlockable.StoryIsUnlocked)
				{
					lMUnlockable.StoryIsUnlocked = true;
					Logger.LogInfo(lMUnlockable.Name + ": Releasing story lock.. " + lMUnlockable.Name + " now available (for discovery).");
					NetworkManager.Instance.ServerSendAlertMessage(new Notification
					{
						Header = "Autopilot",
						Text = "Incoming transmission! Decoding location data...",
						Key = "LMU_StoryLockReleasedArtifice"
					});
				}
			}
			if (Name == "Embrion" && UnlockManager.Instance.Terminal.scannedEnemyIDs.Contains(18) && StoryUnlock && !StoryIsUnlocked)
			{
				StoryIsUnlocked = true;
				Logger.LogInfo(Name + ": Releasing story lock.. " + Name + " now available (for discovery).");
				NetworkManager.Instance.ServerSendAlertMessage(new Notification
				{
					Header = "Autopilot",
					Text = "Extracting location data from bestiary entry...",
					Key = "LMU_StoryLockReleasedEmbrion"
				});
			}
		}

		internal void ApplyState()
		{
			ApplyPrice();
			if (StoryUnlock)
			{
				if (StoryIsUnlocked)
				{
					if (!ConfigManager.DiscoveryMode)
					{
						Unlock();
						return;
					}
					if (ConfigManager.DiscoveryMode && (Discovered || PermanentlyDiscovered))
					{
						Unlock();
						return;
					}
				}
				LockAndHide();
			}
			else if (OriginallyLocked)
			{
				LockAndHide();
			}
			else if (OriginallyHidden)
			{
				Unlock();
			}
			else if (!ConfigManager.DiscoveryMode)
			{
				Unlock();
			}
			else if (ConfigManager.DiscoveryMode)
			{
				if (Discovered || PermanentlyDiscovered)
				{
					Unlock();
				}
				else
				{
					LockAndHide();
				}
			}
		}

		internal void Unlock()
		{
			ExtendedLevel.IsRouteLocked = false;
			if (RemainingHidden)
			{
				ExtendedLevel.IsRouteHidden = true;
			}
			else
			{
				ExtendedLevel.IsRouteHidden = false;
			}
		}

		internal void LockAndHide()
		{
			ExtendedLevel.IsRouteHidden = true;
			ExtendedLevel.IsRouteLocked = true;
		}

		internal void ApplyPrice()
		{
			if (RoutePrice != OriginalPrice)
			{
				ExtendedLevel.RoutePrice = RoutePrice;
			}
		}

		internal void RefreshSale()
		{
			if (Random.Range(0, 100) < ConfigManager.SalesChance && ExtendedLevel.RoutePrice > 0)
			{
				OnSale = true;
				SalesRate = ConfigManager.SalesRate;
				Logger.LogDebug($"{Name} is on SALE for {SalesRate}% OFF!");
			}
			else
			{
				OnSale = false;
				SalesRate = 0;
			}
		}

		internal void Land()
		{
			LandingCount++;
		}

		internal void VisitMoon()
		{
			VisitCount++;
			Logger.LogDebug($"{Name}: Set visit count to {VisitCount}");
			if ((ExtendedLevel.RoutePrice == 0 || (ConfigManager.DiscountMode && BuyCount == ConfigManager.DiscountsCount)) && OriginalPrice != ExtendedLevel.RoutePrice)
			{
				FreeVisitCount++;
				Logger.LogDebug($"{Name}: Set free visit count to {FreeVisitCount}");
				if (ConfigManager.UnlockMode && !ConfigManager.DiscountMode && ConfigManager.UnlocksResetAfterVisits > 0)
				{
					if (FreeVisitCount > ConfigManager.UnlocksResetAfterVisits)
					{
						Logger.LogInfo($"{Name}: Reset unlock due to free visit count ({FreeVisitCount - 1}) reached.");
						NotificationHelper.SendChatMessage("Unlock expired:\n<color=red>" + Name + "</color>");
						NetworkManager.Instance.ServerSendAlertMessage(new Notification
						{
							Header = "Unlock expired!",
							Text = "Your unlock for " + Name + " has been used " + (FreeVisitCount - 1).NumberOfWords("time") + " and expired.",
							IsWarning = true,
							Key = "LMU_UnlockExpired"
						});
						BuyCount = 0;
						FreeVisitCount = 0;
						if (ConfigManager.UnlocksResetAfterVisitsPermDiscovery)
						{
							Logger.LogInfo(Name + ": Also resetting permanent discovery status.");
							PermanentlyDiscovered = false;
						}
					}
					else if (FreeVisitCount > 1)
					{
						NetworkManager.Instance.ServerSendAlertMessage(new Notification
						{
							Header = "Unlock: " + Name,
							Text = "Unlock used! " + (FreeVisitCount - 1).CountToText() + " use.\nYou have " + (ConfigManager.UnlocksResetAfterVisits - FreeVisitCount + 1).NumberOfWords("use") + " left.",
							Key = "LMU_UnlockUsed"
						});
					}
				}
				if (ConfigManager.DiscountMode && ConfigManager.DiscountsResetAfterVisits > 0)
				{
					if (FreeVisitCount > ConfigManager.DiscountsResetAfterVisits)
					{
						Logger.LogInfo($"{Name}: Reset discount due to free visit count ({FreeVisitCount - 1}) reached.");
						NotificationHelper.SendChatMessage("Discount expired:\n<color=red>" + Name + "</color>");
						NetworkManager.Instance.ServerSendAlertMessage(new Notification
						{
							Header = "Discount expired!",
							Text = "Your discount for " + Name + " has been used " + (FreeVisitCount - 1).NumberOfWords("time") + " and expired.",
							IsWarning = true,
							Key = "LMU_DiscountExpired"
						});
						BuyCount = 0;
						FreeVisitCount = 0;
						if (ConfigManager.DiscountsResetAfterVisitsPermDiscovery)
						{
							Logger.LogInfo(Name + ": Also resetting permanent discovery status.");
							PermanentlyDiscovered = false;
						}
					}
					else if (FreeVisitCount > 1)
					{
						NetworkManager.Instance.ServerSendAlertMessage(new Notification
						{
							Header = "Discount: " + Name,
							Text = "Discount redeemed! " + (FreeVisitCount - 1).CountToText() + " use.\nYou have " + (ConfigManager.DiscountsResetAfterVisits - FreeVisitCount + 1).NumberOfWords("use") + " left.",
							Key = "LMU_DiscountUsed"
						});
					}
				}
			}
			DelayHelper.Instance.ExecuteAfterDelay(NetworkManager.Instance.ServerSendAlertQueueEvent, 1f);
		}

		internal Dictionary<string, List<string>> GetMatchingCustomGroups()
		{
			Dictionary<string, List<string>> moonGroupMatchingCustomDict = ConfigManager.MoonGroupMatchingCustomDict;
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
			foreach (KeyValuePair<string, List<string>> item in moonGroupMatchingCustomDict)
			{
				if (item.Value.Contains(Name))
				{
					dictionary.Add(item.Key, item.Value);
				}
			}
			return dictionary;
		}

		internal string GetMoonPreviewText(PreviewInfoType infoType)
		{
			int num = Name.Count();
			string format = "{0, -" + Math.Clamp(18 - num, 0, 18) + "} {1, -7} {2, -9} {3, -13}";
			string text = string.Empty;
			string empty = string.Empty;
			string empty2 = string.Empty;
			empty2 = ((!Plugin.WeatherTweaksPresent) ? ((object)(LevelWeatherType)(ref ExtendedLevel.SelectableLevel.currentWeather)).ToString() : WTCompatibility.GetWeatherTweaksWeather(this));
			if (empty2.Trim().Equals("None"))
			{
				empty2 = string.Empty;
			}
			if (empty2.Count() > 13)
			{
				empty2 = empty2.Substring(0, 11) + "..";
			}
			string text2 = string.Empty;
			if (Plugin.LQPresent && ConfigManager.PreferLQRisk)
			{
				text2 = LQCompatibility.GetLQRiskLevel(this);
			}
			if (string.IsNullOrEmpty(text2))
			{
				text2 = ExtendedLevel.SelectableLevel.riskLevel;
			}
			if (text2.Count() > 7)
			{
				text2 = text2.Substring(0, 5) + "..";
			}
			if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)2))
			{
				text = string.Format(format, empty, empty, "$" + ExtendedLevel.RoutePrice, empty2);
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)0))
			{
				text = string.Format(format, empty, empty, "$" + ExtendedLevel.RoutePrice, empty);
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)1))
			{
				text = ((!ConfigManager.TerminalShowRiskWeather) ? string.Format(format, empty, text2, empty, empty) : string.Format(format, empty, text2, empty, empty2));
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)3))
			{
				text = string.Format(format, empty, empty, empty, empty);
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)4))
			{
				text = string.Format(format, empty, text2, "$" + ExtendedLevel.RoutePrice, empty2);
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)6))
			{
				text = string.Format(format, empty, empty, empty, empty);
			}
			else if (((object)(PreviewInfoType)(ref infoType)).Equals((object)(PreviewInfoType)7))
			{
				text = string.Format(format, empty, empty, empty, empty);
			}
			if (ExtendedLevel.IsRouteLocked)
			{
				text += "\n  * (Locked)";
			}
			if (!ConfigManager.DisplayTerminalTags)
			{
				return text;
			}
			string text3 = BuildTagString();
			if (!string.IsNullOrEmpty(text3))
			{
				text += text3;
			}
			return text;
		}

		internal string BuildShortTagString()
		{
			string text = string.Empty;
			if (NewDiscovery && ConfigManager.DiscoveryMode && ConfigManager.ShowTagNewDiscovery)
			{
				text = AddTagToPreviewText("[!]", text);
			}
			if (LandingCount > 0 && ConfigManager.ShowTagExplored)
			{
				text = AddTagToPreviewText($"[EXPLORED:{LandingCount}]", text);
			}
			else if (LandingCount == 0 && ConfigManager.ShowTagExplored)
			{
				text = AddTagToPreviewText("[UNEXPLORED]", text);
			}
			if (FreeVisitCount > 0 && ConfigManager.UnlockMode && !ConfigManager.DiscountMode && ConfigManager.UnlocksResetAfterVisits > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText($"[{ConfigManager.UnlocksResetAfterVisits - FreeVisitCount + 1}]", text);
			}
			else if (FreeVisitCount > 0 && ConfigManager.DiscountMode && ConfigManager.DiscountsResetAfterVisits > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText($"[{ConfigManager.DiscountsResetAfterVisits - FreeVisitCount + 1}]", text);
			}
			else if (ConfigManager.UnlockMode && !ConfigManager.DiscountMode && BuyCount > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText("[U]", text);
			}
			else if (ConfigManager.DiscountMode && BuyCount > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				int num = 100 - (int)(Plugin.GetDiscountRate(BuyCount) * 100f);
				text = ((num == 100) ? AddTagToPreviewText("[U]", text) : AddTagToPreviewText($"[{num}%]", text));
			}
			if (PermanentlyDiscovered && !ConfigManager.DiscoveryNeverShuffle && ConfigManager.DiscoveryMode && ConfigManager.ShowTagPermanentDiscovery && ((OriginalPrice == 0 && ConfigManager.PermanentlyDiscoverFreeMoonsOnLanding != 0) || (OriginalPrice > 0 && ConfigManager.PermanentlyDiscoverPaidMoonsOnLanding != 0)))
			{
				text = AddTagToPreviewText("[P]", text);
			}
			if (OnSale && SalesRate > 0 && ExtendedLevel.RoutePrice > 0 && ConfigManager.Sales && ConfigManager.ShowTagSale)
			{
				text = AddTagToPreviewText($"[{SalesRate}%]", text);
			}
			return text;
		}

		private string BuildTagString()
		{
			string text = string.Empty;
			if ((Object)(object)ExtendedLevel == (Object)(object)LevelManager.CurrentExtendedLevel && ConfigManager.ShowTagInOrbit)
			{
				text = AddTagToPreviewText("[IN ORBIT]", text);
			}
			if (NewDiscovery && ConfigManager.DiscoveryMode && ConfigManager.ShowTagNewDiscovery)
			{
				text = AddTagToPreviewText("[NEW]", text);
			}
			if (LandingCount > 0 && ConfigManager.ShowTagExplored)
			{
				text = AddTagToPreviewText($"[EXPLORED:{LandingCount}]", text);
			}
			else if (LandingCount == 0 && ConfigManager.ShowTagExplored)
			{
				text = AddTagToPreviewText("[UNEXPLORED]", text);
			}
			if (FreeVisitCount > 0 && ConfigManager.UnlockMode && !ConfigManager.DiscountMode && ConfigManager.UnlocksResetAfterVisits > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText($"[UNLOCK EXPIRES:{ConfigManager.UnlocksResetAfterVisits - FreeVisitCount + 1}]", text);
			}
			else if (FreeVisitCount > 0 && ConfigManager.DiscountMode && ConfigManager.DiscountsResetAfterVisits > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText($"[DISCOUNT EXPIRES:{ConfigManager.DiscountsResetAfterVisits - FreeVisitCount + 1}]", text);
			}
			else if (ConfigManager.UnlockMode && !ConfigManager.DiscountMode && BuyCount > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				text = AddTagToPreviewText("[UNLOCKED]", text);
			}
			else if (ConfigManager.DiscountMode && BuyCount > 0 && ConfigManager.ShowTagUnlockDiscount)
			{
				int num = 100 - (int)(Plugin.GetDiscountRate(BuyCount) * 100f);
				text = ((num == 100) ? AddTagToPreviewText("[FULL DISCOUNT]", text) : AddTagToPreviewText($"[DISCOUNT {num}%]", text));
			}
			if (PermanentlyDiscovered && !ConfigManager.DiscoveryNeverShuffle && ConfigManager.DiscoveryMode && ConfigManager.ShowTagPermanentDiscovery && ((OriginalPrice == 0 && ConfigManager.PermanentlyDiscoverFreeMoonsOnLanding != 0) || (OriginalPrice > 0 && ConfigManager.PermanentlyDiscoverPaidMoonsOnLanding != 0)))
			{
				text = AddTagToPreviewText("[PINNED]", text);
			}
			if (OnSale && SalesRate > 0 && ExtendedLevel.RoutePrice > 0 && ConfigManager.Sales && ConfigManager.ShowTagSale)
			{
				text = AddTagToPreviewText($"[SALE {SalesRate}%]", text);
			}
			Dictionary<string, List<string>> matchingCustomGroups = GetMatchingCustomGroups();
			if (ConfigManager.MoonGroupMatchingMethod == "Custom" && matchingCustomGroups.Count > 0 && ConfigManager.ShowTagGroups)
			{
				string text2 = string.Empty;
				if (matchingCustomGroups.Count > 1)
				{
					text2 = string.Join("/", matchingCustomGroups.Keys);
				}
				else if (matchingCustomGroups.Count == 1)
				{
					text2 = matchingCustomGroups.Keys.First();
				}
				text = AddTagToPreviewText("[" + text2.Trim().ToUpper() + "]", text);
			}
			else if (Plugin.LethalConstellationsPresent && Plugin.LethalConstellationsExtension != null && ConfigManager.MoonGroupMatchingMethod == "LethalConstellations" && ConfigManager.ShowTagGroups)
			{
				text = AddTagToPreviewText("[" + Plugin.LethalConstellationsExtension.GetConstellationName(this).ToUpper() + "]", text);
			}
			else if (ConfigManager.MoonGroupMatchingMethod == "Tag")
			{
				List<ContentTag> contentTags = ((ExtendedContent)ExtendedLevel).ContentTags;
				string text3 = string.Empty;
				if (contentTags.Count > 1)
				{
					text3 = string.Join("/", contentTags.Select((ContentTag tag) => tag.contentTagName.ToUpper()));
				}
				else if (contentTags.Count == 1)
				{
					text3 = ((object)contentTags.FirstOrDefault()).ToString();
				}
				if (!string.IsNullOrEmpty(text3))
				{
					text = AddTagToPreviewText("[" + text3 + "]", text);
				}
			}
			return text;
		}

		private string AddTagToPreviewText(string newTag, string previewText)
		{
			if (previewText == string.Empty)
			{
				previewText = "\n  *";
			}
			string[] array = previewText.Split('\n', StringSplitOptions.RemoveEmptyEntries);
			previewText = ((array[^1].Length + newTag.Length >= ConfigManager.TerminalTagLineWidth && !(array[^1] == "  *")) ? (previewText + "\n  * " + newTag) : (previewText + " " + newTag));
			return previewText;
		}

		private int CalculatePrice()
		{
			string text = Name + ": Calculating price.. ";
			int num = OriginalPrice;
			text += $"Original price -> {OriginalPrice}";
			if (BuyCount > 0)
			{
				if (ConfigManager.DiscountMode)
				{
					float discountRate = Plugin.GetDiscountRate(BuyCount);
					num = (int)((float)num * discountRate);
					if (num <= 0 && discountRate > 0f)
					{
						num = 1;
					}
					text += $", Discount -> {num}";
				}
				else if (ConfigManager.UnlockMode)
				{
					num = 0;
					text += $", Unlock -> {num}, ";
				}
			}
			if (OnSale && num > 0)
			{
				num = (int)((float)(num * (100 - SalesRate)) / 100f);
				if (num <= 0)
				{
					num = 1;
				}
				text += $", Sales rate ({SalesRate}%) -> {num}";
			}
			Logger.LogDebug(text);
			return num;
		}

		public override string ToString()
		{
			int num = VisitCount;
			if (FreeVisitCount > VisitCount)
			{
				num = FreeVisitCount;
			}
			string text = "✓";
			if (ExtendedLevel.IsRouteHidden && ExtendedLevel.IsRouteLocked)
			{
				text = string.Empty;
			}
			else if (ExtendedLevel.IsRouteHidden)
			{
				text = "Hidden";
			}
			else if (ExtendedLevel.IsRouteLocked)
			{
				text = "Locked";
			}
			string text2 = string.Empty;
			if (Discovered)
			{
				text2 = "✓";
			}
			if (NewDiscovery)
			{
				text2 = "New";
			}
			if (!DiscoveredOnce)
			{
				text2 = "Never";
			}
			if (PermanentlyDiscovered)
			{
				text2 = "Permanent";
			}
			string text3 = "-";
			if (SalesRate > 0)
			{
				text3 = SalesRate + "%";
			}
			string text4 = "✓";
			if (OriginallyHidden && !OriginallyLocked)
			{
				text4 = "Hidden";
			}
			else if (!OriginallyHidden && OriginallyLocked)
			{
				text4 = "Locked";
			}
			else if (OriginallyHidden && OriginallyLocked)
			{
				text4 = string.Empty;
			}
			string text5 = "-";
			if (StoryUnlock && !StoryIsUnlocked)
			{
				text5 = "Active";
			}
			else if (StoryUnlock && StoryIsUnlocked)
			{
				text5 = "Released";
			}
			return string.Format(UnlockManager.LogFormatString, Name, RoutePrice, BuyCount, num, text, text2, text3, OriginalPrice, text4, text5);
		}
	}
	internal static class Logger
	{
		private static ManualLogSource _mls;

		internal static void Initialize(ManualLogSource mls)
		{
			_mls = mls;
		}

		internal static void LogDebug(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogDebug(data);
			}
		}

		internal static void LogMessage(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogMessage(data);
			}
		}

		internal static void LogInfo(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogInfo(data);
			}
		}

		internal static void LogWarning(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogWarning(data);
			}
		}

		internal static void LogError(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogError(data);
			}
		}

		internal static void LogFatal(object data)
		{
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.LogFatal(data);
			}
		}

		internal static void Log(LogLevel level, object data)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource mls = _mls;
			if (mls != null)
			{
				mls.Log(level, data);
			}
		}
	}
	internal class NetworkManager
	{
		private static LNetworkMessage<List<LMUnlockable>> UnlockablesMessage;

		private static LNetworkMessage<string> BuyMoonMessage;

		private static LNetworkEvent RequestSyncEvent;

		private static LNetworkMessage<Notification> AlertMessage;

		private static LNetworkEvent SendAlertQueueEvent;

		internal static NetworkManager Instance { get; private set; }

		internal NetworkManager()
		{
			if (Instance == null)
			{
				Instance = this;
			}
			Logger.LogInfo("Register Network messages..");
			UnlockablesMessage = LNetworkMessage<List<LMUnlockable>>.Connect("LMU_Unlocks", (Action<List<LMUnlockable>, ulong>)null, (Action<List<LMUnlockable>>)ClientReceiveUnlockables, (Action<List<LMUnlockable>, ulong>)null);
			BuyMoonMessage = LNetworkMessage<string>.Connect("LMU_BuyMoonMessage", (Action<string, ulong>)ServerReceiveBuyMoon, (Action<string>)null, (Action<string, ulong>)null);
			RequestSyncEvent = LNetworkEvent.Connect("LMU_RequestSyncEvent", (Action<ulong>)ServerReceiveRequestSyncEvent, (Action)null, (Action<ulong>)null);
			AlertMessage = LNetworkMessage<Notification>.Connect("LMU_AlertMessage", (Action<Notification, ulong>)null, (Action<Notification>)ClientReceiveAlertMessage, (Action<Notification, ulong>)null);
			SendAlertQueueEvent = LNetworkEvent.Connect("LMU_SendAlertQueueEvent", (Action<ulong>)null, (Action)ClientReceiveSendAlertQueueEvent, (Action<ulong>)null);
			Logger.LogInfo("NetworkManager created.");
		}

		internal bool IsServer()
		{
			return NetworkManager.Singleton.IsServer;
		}

		internal void ServerSendUnlockables(List<LMUnlockable> unlockables, ulong client_id = 0uL)
		{
			if (IsServer())
			{
				if (client_id != 0)
				{
					Logger.LogInfo($"Syncing unlockables to client with id {client_id}");
					UnlockablesMessage.SendClient(unlockables, client_id);
				}
				else
				{
					Logger.LogInfo("Syncing unlockables to all clients..");
					UnlockablesMessage.SendClients(unlockables);
				}
			}
		}

		internal void ServerSendAlertMessage(Notification alert)
		{
			if (IsServer())
			{
				AlertMessage.SendClients(alert);
			}
		}

		internal void ServerSendAlertQueueEvent()
		{
			if (IsServer())
			{
				SendAlertQueueEvent.InvokeClients();
			}
		}

		internal void ClientBuyMoon(string moon)
		{
			if (!IsServer())
			{
				Logger.LogInfo("Sending buy message to host..");
				BuyMoonMessage.SendServer(moon);
			}
		}

		internal void ClientRequestSync()
		{
			if (!IsServer())
			{
				Logger.LogInfo("Requesting sync from host..");
				RequestSyncEvent.InvokeServer();
			}
		}

		private void ClientReceiveUnlockables(List<LMUnlockable> payload)
		{
			if (!IsServer())
			{
				Logger.LogInfo("Receiving LMU data..");
				UnlockManager.Instance.ImportUnlockableData(payload);
			}
			UnlockManager.Instance.ApplyUnlocks();
		}

		private void ClientReceiveAlertMessage(Notification alert)
		{
			if (ConfigManager.ShowAlerts)
			{
				Logger.LogDebug("Receiving alert message..");
				NotificationHelper.AddNotificationToQueue(alert);
			}
		}

		private void ClientReceiveSendAlertQueueEvent()
		{
			if (ConfigManager.ShowAlerts)
			{
				((MonoBehaviour)DelayHelper.Instance).StartCoroutine(NotificationHelper.SendQueuedNotifications());
			}
		}

		private void ServerReceiveBuyMoon(string moon, ulong id)
		{
			if (IsServer())
			{
				Logger.LogInfo($"Received buy message for moon {moon} from client with id {id}.");
				UnlockManager.Instance.BuyMoon(moon);
			}
		}

		private void ServerReceiveRequestSyncEvent(ulong client_id)
		{
			Logger.LogInfo($"Received sync request from client with id {client_id}..");
			ServerSendUnlockables(UnlockManager.Instance.Unlocks, client_id);
		}
	}
	[BepInPlugin("com.xmods.lethalmoonunlocks", "LethalMoonUnlocks", "2.2.3")]
	[BepInDependency("imabatby.lethallevelloader", "1.4.8")]
	[BepInDependency("LethalNetworkAPI", "3.3.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("LethalMoonUnlocks");

		internal static bool LQPresent;

		internal static bool LethalConstellationsPresent;

		internal static bool darmuhsTerminalStuffPresent;

		internal static bool WeatherTweaksPresent;

		private bool _loaded;

		internal static Plugin Instance { get; private set; }

		internal static LethalConstellationsExtension LethalConstellationsExtension { get; private set; }

		internal NetworkManager NetworkManager { get; private set; }

		internal UnlockManager UnlockManager { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			Logger.Initialize(Logger.CreateLogSource("LethalMoonUnlocks"));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hello world (:");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Applying patches..");
			_harmony.PatchAll(typeof(GameNetworkManagerPatch));
			_harmony.PatchAll(typeof(RoundManagerPatch));
			_harmony.PatchAll(typeof(StartOfRoundPatch));
			_harmony.PatchAll(typeof(TerminalPatch));
			_harmony.PatchAll(typeof(TimeOfDayPatch));
			_harmony.PatchAll(typeof(HUDManagerPatch));
			_harmony.PatchAll(typeof(LLLSaveManagerInitPatch));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Patching complete.");
			if (!_loaded)
			{
				Initialize();
			}
		}

		public void Start()
		{
			if (!_loaded)
			{
				Initialize();
			}
		}

		public void OnDestroy()
		{
			if (!_loaded)
			{
				Initialize();
			}
		}

		public void Initialize()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing..");
			GameObject val = new GameObject("DelayHelper");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<DelayHelper>();
			SceneManager.sceneUnloaded += AfterGameInit;
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalMoonUnlocks 2.2.3 initialized!");
			_loaded = true;
		}

		private void AfterGameInit(Scene scene)
		{
			if (!(((Scene)(ref scene)).name != "InitScene") || !(((Scene)(ref scene)).name != "InitSceneLANMode"))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Checking for compatible mods..");
				if (Chainloader.PluginInfos.ContainsKey("LethalQuantities"))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Lethal Quantities found! Enabling compatibility..");
					LQPresent = true;
				}
				if (Chainloader.PluginInfos.ContainsKey("com.zealsprince.malfunctions"))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Malfunctions found! Enabling compatibility..");
					_harmony.PatchAll(typeof(MalfunctionsCompatibility));
				}
				if (Chainloader.PluginInfos.ContainsKey("com.github.darmuh.LethalConstellations"))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalConstellations found! Enabling compatibility..");
					LethalConstellationsPresent = true;
					LethalConstellationsExtension = new LethalConstellationsExtension();
				}
				if (Chainloader.PluginInfos.ContainsKey("darmuh.TerminalStuff"))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"darmuhsTerminalStuff found! Enabling compatibility..");
					darmuhsTerminalStuffPresent = true;
				}
				if (Chainloader.PluginInfos.ContainsKey("WeatherTweaks"))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"WeatherTweaks found! Enabling compatibility..");
					WeatherTweaksPresent = true;
					_harmony.PatchAll(typeof(WTCompatibility));
				}
				ConfigManager.RefreshConfig();
				if (ConfigManager.TerminalScrollAmount > 0)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"TerminalScrollAmount is set to a positive value! Patching scroll amount..");
					_harmony.PatchAll(typeof(PlayerControllerBPatch));
				}
				NetworkManager = new NetworkManager();
				UnlockManager = new UnlockManager();
				SceneManager.sceneUnloaded -= AfterGameInit;
			}
		}

		internal static float GetDiscountRate(int discount_number)
		{
			List<int> list = new List<int>();
			foreach (int discount in ConfigManager.Discounts)
			{
				list.Add(100 - Mathf.Clamp(discount, 0, 100));
			}
			if (discount_number > list.Count)
			{
				discount_number = list.Count;
			}
			return (float)list[discount_number - 1] / 100f;
		}
	}
	public static class PluginMetadata
	{
		public const string PLUGIN_GUID = "com.xmods.lethalmoonunlocks";

		public const string PLUGIN_NAME = "LethalMoonUnlocks";

		public const string PLUGIN_VERSION = "2.2.3";
	}
	internal static class RandomSelector
	{
		internal static List<T> Get<T>(List<T> objects, int amount)
		{
			if (objects.Count < amount || objects.Count == 0 || objects == null)
			{
				return objects;
			}
			List<T> list = new List<T>(objects);
			List<T> list2 = new List<T>();
			while (list2.Count < amount)
			{
				list2.Add(list[Random.Range(0, list.Count)]);
				list.Remove(list2.Last());
			}
			CheckResult(list2, amount);
			return list2;
		}

		internal static List<T> GetWeighted<T>(Dictionary<T, int> objects, int amount)
		{
			if (objects.Count < amount || objects.Count == 0 || objects == null)
			{
				return new List<T>(objects.Keys);
			}
			Dictionary<T, int> dictionary = new Dictionary<T, int>(objects);
			List<T> list = new List<T>();
			while (list.Count < amount)
			{
				int num = dictionary.Sum((KeyValuePair<T, int> entry) => entry.Value);
				int num2 = Random.Range(0, num);
				T val = default(T);
				foreach (KeyValuePair<T, int> item in dictionary)
				{
					if (item.Value != 0)
					{
						if (num2 < item.Value)
						{
							val = item.Key;
							break;
						}
						num2 -= item.Value;
					}
				}
				if (val == null)
				{
					break;
				}
				list.Add(val);
				dictionary.Remove(val);
			}
			CheckResult(list, amount);
			return list;
		}

		internal static Dictionary<LMUnlockable, int> CalculateBiasedWeights(List<LMUnlockable> unlocks, float bias)
		{
			Dictionary<LMUnlockable, int> dictionary = new Dictionary<LMUnlockable, int>();
			if (unlocks.Count < 1)
			{
				return dictionary;
			}
			int num = unlocks.Sum((LMUnlockable unlock) => unlock.OriginalPrice);
			foreach (LMUnlockable unlock in unlocks)
			{
				int num2 = ((!ConfigManager.CheapMoonBiasIgnorePriceChanges) ? Math.Clamp(unlock.ExtendedLevel.RoutePrice, 1, int.MaxValue) : Math.Clamp(unlock.OriginalPrice, 1, int.MaxValue));
				long num3 = Math.Clamp((long)Math.Pow((float)(num / num2) * bias, bias), 1L, int.MaxValue / (unlocks.Count + 1));
				dictionary[unlock] = (int)num3;
			}
			Logger.LogDebug("Cheap moon bias: Assigned the following weights: [ " + string.Join(", ", dictionary.Select((KeyValuePair<LMUnlockable, int> weight) => weight.Key.Name + ":" + weight.Value)) + " ]");
			return dictionary;
		}

		private static bool CheckResult<T>(List<T> result, int goal)
		{
			if (result.Count < goal)
			{
				Logger.LogWarning("Couldn't select the desired amount of elements!");
				return false;
			}
			return true;
		}
	}
	internal static class SaveManager
	{
		internal static Dictionary<string, object> Savedata => Load();

		private static Dictionary<string, object> Load()
		{
			Logger.LogInfo("Loading save data..");
			string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName;
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			if (ES3.KeyExists("LMU_Unlockables", currentSaveFileName))
			{
				List<LMUnlockable> list = ES3.Load<List<LMUnlockable>>("LMU_Unlockables", currentSaveFileName);
				dictionary.Add("LMU_Unlockables", list);
				Logger.LogInfo("Found LMU_Unlockables: " + string.Join(", ", list.Select((LMUnlockable unlock) => unlock.Name)));
				if (ES3.KeyExists("LMU_QuotaCount", currentSaveFileName))
				{
					int num = ES3.Load<int>("LMU_QuotaCount", currentSaveFileName);
					dictionary.Add("LMU_QuotaCount", num);
					Logger.LogInfo($"Found LMU_QuotaCount: {num}");
				}
				if (ES3.KeyExists("LMU_DayCount", currentSaveFileName))
				{
					int num2 = ES3.Load<int>("LMU_DayCount", currentSaveFileName);
					dictionary.Add("LMU_DayCount", num2);
					Logger.LogInfo($"Found LMU_DayCount: {num2}");
				}
				if (ES3.KeyExists("LMU_QuotaUnlocksCount", currentSaveFileName))
				{
					int num3 = ES3.Load<int>("LMU_QuotaUnlocksCount", currentSaveFileName);
					dictionary.Add("LMU_QuotaUnlocksCount", num3);
					Logger.LogInfo($"Found LMU_QuotaUnlocksCount: {num3}");
				}
				if (ES3.KeyExists("LMU_QuotaFullDiscountsCount", currentSaveFileName))
				{
					int num4 = ES3.Load<int>("LMU_QuotaFullDiscountsCount", currentSaveFileName);
					dictionary.Add("LMU_QuotaFullDiscountsCount", num4);
					Logger.LogInfo($"Found LMU_QuotaFullDiscountsCount: {num4}");
				}
				if (ConfigManager.GroupCreditsSavingBandAid && ES3.KeyExists("GroupCredits", currentSaveFileName))
				{
					dictionary.Add("GroupCredits", ES3.Load<int>("GroupCredits", currentSaveFileName));
				}
				return dictionary;
			}
			if (ES3.KeyExists("LMU_UnlockedMoons", currentSaveFileName))
			{
				Dictionary<string, int> dictionary2 = ES3.Load<Dictionary<string, int>>("LMU_UnlockedMoons", currentSaveFileName);
				dictionary.Add("LMU_UnlockedMoons", dictionary2);
				Logger.LogInfo("Found deprecated LMU_UnlockedMoons: " + string.Join(", ", dictionary2));
			}
			if (ES3.KeyExists("LMU_OriginalMoonPrices", currentSaveFileName))
			{
				Dictionary<string, int> dictionary3 = ES3.Load<Dictionary<string, int>>("LMU_OriginalMoonPrices", currentSaveFileName);
				dictionary.Add("LMU_OriginalMoonPrices", dictionary3);
				Logger.LogInfo("Found deprecated LMU_OriginalMoonPrices: " + string.Join(", ", dictionary3));
			}
			if (ES3.KeyExists("UnlockedMoons", currentSaveFileName))
			{
				List<string> list2 = ES3.Load<List<string>>("UnlockedMoons", currentSaveFileName);
				dictionary.Add("UnlockedMoons", list2);
				Logger.LogInfo("Found Permanent Moons data UnlockedMoons: " + string.Join(", ", list2));
			}
			if (ES3.KeyExists("MoonQuotaNum", currentSaveFileName))
			{
				int num5 = ES3.Load<int>("MoonQuotaNum", currentSaveFileName);
				dictionary.Add("MoonQuotaNum", num5);
				Logger.LogInfo($"Found Permanent Moons data MoonQuotaNum: {num5}");
			}
			return dictionary;
		}

		internal static void StoreSaveData()
		{
			Logger.LogInfo("Saving data..");
			string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName;
			if (UnlockManager.Instance.Unlocks.Count != 0)
			{
				ES3.Save<List<LMUnlockable>>("LMU_Unlockables", UnlockManager.Instance.Unlocks, currentSaveFileName);
				Logger.LogInfo("Saving LMU_Unlockables..");
				UnlockManager.Instance.LogUnlockables();
			}
			else if (ES3.KeyExists("LMU_Unlockables", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_Unlockables", currentSaveFileName);
			}
			if (UnlockManager.Instance.QuotaCount > 0)
			{
				ES3.Save<int>("LMU_QuotaCount", UnlockManager.Instance.QuotaCount, currentSaveFileName);
				Logger.LogInfo("Saving LMU_QuotaCount: " + string.Join(", ", UnlockManager.Instance.QuotaCount));
			}
			else if (ES3.KeyExists("LMU_QuotaCount", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_QuotaCount", currentSaveFileName);
			}
			if (UnlockManager.Instance.DayCount > 0)
			{
				ES3.Save<int>("LMU_DayCount", UnlockManager.Instance.DayCount, currentSaveFileName);
				Logger.LogInfo("Saving LMU_DayCount: " + string.Join(", ", UnlockManager.Instance.DayCount));
			}
			else if (ES3.KeyExists("LMU_DayCount", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_DayCount", currentSaveFileName);
			}
			if (UnlockManager.Instance.QuotaUnlocksCount > 0)
			{
				ES3.Save<int>("LMU_QuotaUnlocksCount", UnlockManager.Instance.QuotaUnlocksCount, currentSaveFileName);
				Logger.LogInfo("Saving LMU_QuotaUnlocksCount: " + string.Join(", ", UnlockManager.Instance.QuotaUnlocksCount));
			}
			else if (ES3.KeyExists("LMU_QuotaUnlocksCount", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_QuotaUnlocksCount", currentSaveFileName);
			}
			if (UnlockManager.Instance.QuotaDiscountsCount > 0)
			{
				ES3.Save<int>("LMU_QuotaDiscountsCount", UnlockManager.Instance.QuotaDiscountsCount, currentSaveFileName);
				Logger.LogInfo("Saving LMU_QuotaDiscountsCount: " + string.Join(", ", UnlockManager.Instance.QuotaDiscountsCount));
			}
			else if (ES3.KeyExists("LMU_QuotaDiscountsCount", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_QuotaDiscountsCount", currentSaveFileName);
			}
			if (UnlockManager.Instance.QuotaFullDiscountsCount > 0)
			{
				ES3.Save<int>("LMU_QuotaFullDiscountsCount", UnlockManager.Instance.QuotaCount, currentSaveFileName);
				Logger.LogInfo("Saving LMU_QuotaFullDiscountsCount: " + string.Join(", ", UnlockManager.Instance.QuotaFullDiscountsCount));
			}
			else if (ES3.KeyExists("LMU_QuotaFullDiscountsCount", currentSaveFileName))
			{
				ES3.DeleteKey("LMU_QuotaFullDiscountsCount", currentSaveFileName);
			}
			if (ConfigManager.GroupCreditsSavingBandAid)
			{
				int groupCredits = UnlockManager.Instance.Terminal.groupCredits;
				Logger.LogInfo($"BAND-AID: Saving group credits ({groupCredits})..");
				ES3.Save<int>("GroupCredits", groupCredits, currentSaveFileName);
			}
			if (ES3.KeyExists("LMU_UnlockedMoons", currentSaveFileName))
			{
				Logger.LogInfo("Deleting deprecated save field: LMU_UnlockedMoons");
				ES3.DeleteKey("LMU_UnlockedMoons", currentSaveFileName);
			}
			if (ES3.KeyExists("LMU_OriginalMoonPrices", currentSaveFileName))
			{
				Logger.LogInfo("Deleting deprecated save field: LMU_OriginalMoonPrices");
				ES3.DeleteKey("LMU_OriginalMoonPrices", currentSaveFileName);
			}
		}
	}
	public class UnlockManager
	{
		public delegate List<string> DesignateStoryUnlocks();

		private int _discoveredFreeCount;

		private int _discoveredDynamicFreeCount;

		private int _discoveredPaidCount;

		internal static UnlockManager Instance { get; private set; }

		internal static string LogFormatString { get; } = "| {0, -20} | {1, 6} | {2, 7} | {3, 7} | {4, 8} | {5, 11} | {6, 5} | {7, 12} | {8, 12} | {9, 11} |";


		internal static List<string> LogHeader { get; } = new List<string>(10) { "Name", "Price", "Bought", "Visits", "Catalog", "Discovered", "Sale", "Orig. Price", "Orig. State", "Story Lock" };


		internal Terminal Terminal { get; set; }

		internal List<ExtendedLevel> AllLevels { get; private set; } = PatchedContent.ExtendedLevels;


		internal List<LMUnlockable> Unlocks { get; set; } = new List<LMUnlockable>();


		internal int QuotaCount { get; set; }

		internal int DayCount { get; set; }

		internal int QuotaUnlocksCount { get; set; }

		internal int QuotaDiscountsCount { get; set; }

		internal int QuotaFullDiscountsCount { get; set; }

		internal int DiscoveredFreeCount
		{
			get
			{
				if (_discoveredFreeCount <= DiscoveryFreeCandidates.Count)
				{
					return _discoveredFreeCount;
				}
				return DiscoveryFreeCandidates.Count;
			}
			set
			{
				_discoveredFreeCount = value;
			}
		}

		internal int DiscoveredDynamicFreeCount
		{
			get
			{
				if (_discoveredDynamicFreeCount <= DiscoveryDynamicFreeCandidates.Count)
				{
					return _discoveredDynamicFreeCount;
				}
				return DiscoveryDynamicFreeCandidates.Count;
			}
			set
			{
				_discoveredDynamicFreeCount = value;
			}
		}

		internal int DiscoveredPaidCount
		{
			get
			{
				if (_discoveredPaidCount <= DiscoveryPaidCandidates.Count)
				{
					return _discoveredPaidCount;
				}
				return DiscoveryPaidCandidates.Count;
			}
			set
			{
				_discoveredPaidCount = value;
			}
		}

		internal List<LMUnlockable> FreeMoons => Unlocks.Where((LMUnlockable unlock) => unlock.OriginalPrice == 0).ToList();

		internal List<LMUnlockable> DynamicFreeMoons => Unlocks.Where((LMUnlockable unlock) => unlock.ExtendedLevel.RoutePrice == 0).ToList();

		internal List<LMUnlockable> PaidMoons => Unlocks.Where((LMUnlockable unlock) => unlock.ExtendedLevel.RoutePrice > 0).ToList();

		internal List<LMUnlockable> DiscoveryCandidates => Unlocks.Where((LMUnlockable unlock) => ((!unlock.OriginallyLocked && !unlock.OriginallyHidden && !unlock.StoryUnlock) || (unlock.StoryUnlock && unlock.StoryIsUnlocked)) && !unlock.Discovered && !unlock.PermanentlyDiscovered).ToList();

		internal List<LMUnlockable> DiscoveryFreeCandidates => DiscoveryCandidates.Where((LMUnlockable candidate) => candidate.OriginalPrice == 0).ToList();

		internal List<LMUnlockable> DiscoveryDynamicFreeCandidates => DiscoveryCandidates.Where((LMUnlockable candidate) => candidate.ExtendedLevel.RoutePrice == 0).ToList();

		internal List<LMUnlockable> DiscoveryPaidCandidates => DiscoveryCandidates.Where((LMUnlockable candidate) => candidate.ExtendedLevel.RoutePrice > 0).ToList();

		public static event DesignateStoryUnlocks OnCollectStoryLockedMoons;

		internal UnlockManager()
		{
			if (Instance == null)
			{
				Instance = this;
			}
		}

		public static bool TryReleaseStoryLock(string numberlessPlanetName)
		{
			if (!ConfigManager.EnableStoryProgression)
			{
				Logger.LogInfo("Received request to release story lock but story locks are ignored by user config.");
				return false;
			}
			LMUnlockable lMUnlockable = Instance?.Unlocks.FirstOrDefault((LMUnlockable u) => u.Name == numberlessPlanetName);
			if (lMUnlockable == null)
			{
				Logger.LogWarning("Received request to release story lock but the LMUnlockable associated with the level name was not found!");
				return false;
			}
			if (!lMUnlockable.StoryUnlock)
			{
				Logger.LogWarning("Received request to release story lock but the LMUnlockable associated with the level name is not desiganted as story lock!");
				return false;
			}
			lMUnlockable.StoryIsUnlocked = true;
			Logger.LogInfo(lMUnlockable.Name + ": Request to release story lock received! Releasing lock.. " + lMUnlockable.Name + " now available (for discovery).");
			Instance?.IterateUnlocks();
			NetworkManager.Instance?.ServerSendUnlockables(Instance?.Unlocks, 0uL);
			return true;
		}

		public static bool TryReleaseStoryLockShowAlert(string numberlessPlanetName)
		{
			if (!ConfigManager.EnableStoryProgression)
			{
				Logger.LogInfo("Received request to release story lock but story locks are ignored by user config.");
				return false;
			}
			LMUnlockable lMUnlockable = Instance?.Unlocks.FirstOrDefault((LMUnlockable u) => u.Name == numberlessPlanetName);
			if (lMUnlockable == null)
			{
				Logger.LogWarning("Received request to release story lock but the LMUnlockable associated with the level name was not found!");
				return false;
			}
			if (!lMUnlockable.StoryUnlock)
			{
				Logger.LogWarning("Received request to release story lock but the LMUnlockable associated with the level name is not desiganted as story lock!");
				return false;
			}
			lMUnlockable.StoryIsUnlocked = true;
			Logger.LogInfo(lMUnlockable.Name + ": Request to release story lock received! Releasing lock.. " + lMUnlockable.Name + " now available (for discovery).");
			Instance?.IterateUnlocks();
			NetworkManager.Instance?.ServerSendUnlockables(Instance?.Unlocks, 0uL);
			NetworkManager.Instance?.ServerSendAlertMessage(new Notification
			{
				Header = "Autopilot",
				Text = "Incoming transmission! Decoding location data...",
				Key = "LMU_StoryLockReleasedGeneric"
			});
			NetworkManager.Instance?.ServerSendAlertQueueEvent();
			return true;
		}

		internal void InitializeUnlocks()
		{
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			if (AllLevels == null || AllLevels.Count == 0)
			{
				Logger.LogFatal("Unable to find levels!");
			}
			Logger.LogInfo("Initializing LMUnlockables from Extended levels..");
			foreach (ExtendedLevel level in AllLevels)
			{
				if ((Object)(object)level == (Object)null || (Object)(object)level.SelectableLevel == (Object)null || level.NumberlessPlanetName == "Liquidation" || level.NumberlessPlanetName == "Gordion" || Unlocks.Any((LMUnlockable unlock) => unlock.Name == level.NumberlessPlanetName))
				{
					string text = string.Empty;
					if ((Object)(object)level != (Object)null && (Object)(object)level.SelectableLevel != (Object)null)
					{
						text = ": " + level.NumberlessPlanetName;
					}
					Logger.LogDebug("Skipping level" + text + "..");
				}
				else
				{
					Unlocks.Add(new LMUnlockable(level));
				}
			}
			Unlocks = Unlocks.OrderBy((LMUnlockable unlock) => unlock.OriginalPrice).ToList();
			LogUnlockables();
			if (ConfigManager.DisplayTerminalTags || ConfigManager.TerminalFontSizeOverride)
			{
				TerminalManager.onBeforePreviewInfoTextAdded -= new PreviewInfoText(ReplaceTerminalPreview);
				TerminalManager.onBeforePreviewInfoTextAdded += new PreviewInfoText(ReplaceTerminalPreview);
			}
			else
			{
				TerminalManager.onBeforePreviewInfoTextAdded -= new PreviewInfoText(ReplaceTerminalPreview);
			}
		}

		internal void ImportUnlockableData(List<LMUnlockable> newData)
		{
			Logger.LogInfo("Importing LMU_Unlockable data..");
			foreach (LMUnlockable newDatum in newData)
			{
				foreach (LMUnlockable unlock in Unlocks)
				{
					if (unlock.Name == newDatum.Name)
					{
						unlock.OverrideData(newDatum);
					}
				}
			}
		}

		internal void CollectStoryLockedMoons()
		{
			List<string> list = new List<string>();
			if (UnlockManager.OnCollectStoryLockedMoons == null)
			{
				return;
			}
			Delegate[] invocationList = UnlockManager.OnCollectStoryLockedMoons.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				DesignateStoryUnlocks designateStoryUnlocks = (DesignateStoryUnlocks)invocationList[i];
				try
				{
					List<string> collection = designateStoryUnlocks();
					list.AddRange(collection);
					Logger.LogInfo("Collected the following story locked moons: " + string.Join(", ", list));
				}
				catch (Exception ex)
				{
					Logger.LogError("Couldn't handle subscriber response while collecting s