Decompiled source of MissionSelectCleanup v1.0.0

BepInEx/plugins/MissionSelectCleanup.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Math;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5")]
[assembly: AssemblyProduct("MissionSelectCleanup")]
[assembly: AssemblyTitle("MissionSelectCleanup")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.5.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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;
		}
	}
}
public static class ConfigManager
{
	private const float DebounceSeconds = 0.25f;

	private const int SettingsSchemaVersion = 5;

	private static ConfigFile config;

	private static ManualLogSource logger;

	private static FileSystemWatcher configWatcher;

	private static volatile bool reloadPending;

	private static float lastReloadTime;

	public static ConfigEntry<bool> EnableFeature { get; private set; }

	public static ConfigEntry<float> Tightness { get; private set; }

	public static ConfigEntry<float> NeighborAngle { get; private set; }

	public static ConfigEntry<float> CoreSearchRadius { get; private set; }

	public static ConfigEntry<int> MinTrianglesPerRegion { get; private set; }

	public static ConfigEntry<int> MaxTrianglesPerRegion { get; private set; }

	public static ConfigEntry<string> FallbackMode { get; private set; }

	public static ConfigEntry<bool> HideSpecialMissionsFromMap { get; private set; }

	public static ConfigEntry<string> AlwaysHideNameFilters { get; private set; }

	public static ConfigEntry<string> HideWhenCompletedNameFilters { get; private set; }

	public static ConfigEntry<string> SpecialMissionNameFilters { get; private set; }

	public static ConfigEntry<bool> DebugLog { get; private set; }

	private static ConfigEntry<int> SchemaVersion { get; set; }

	public static bool Enabled
	{
		get
		{
			if (EnableFeature != null)
			{
				return EnableFeature.Value;
			}
			return true;
		}
	}

	public static void Initialize(ConfigFile configFile, ManualLogSource log)
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Expected O, but got Unknown
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Expected O, but got Unknown
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Expected O, but got Unknown
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Expected O, but got Unknown
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Expected O, but got Unknown
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Expected O, but got Unknown
		config = configFile;
		logger = log;
		SchemaVersion = config.Bind<int>("Internal", "Settings Schema Version", 0, "Internal. Bumped when defaults change so old cfg values are migrated.");
		EnableFeature = config.Bind<bool>("General", "Enable Feature", true, "When enabled, mission icons cluster in the densest pocket of each region mask.");
		Tightness = config.Bind<float>("General", "Tightness", 0.92f, new ConfigDescription("How small the dense core is. 0 = use most of the region mask, 1 = Max Triangles only (squared curve).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		NeighborAngle = config.Bind<float>("General", "Neighbor Angle", 22f, new ConfigDescription("Degrees between triangle centers to count as connected (diagnostics / seed blob). Lower splits long chains.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 60f), Array.Empty<object>()));
		CoreSearchRadius = config.Bind<float>("General", "Core Search Radius", 28f, new ConfigDescription("Degrees used when scoring local density to find the best pocket center.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 90f), Array.Empty<object>()));
		MinTrianglesPerRegion = config.Bind<int>("General", "Min Triangles Per Region", 10, new ConfigDescription("Never keep fewer than this many core triangles (8 missions + specials).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 64), Array.Empty<object>()));
		MaxTrianglesPerRegion = config.Bind<int>("General", "Max Triangles Per Region", 10, new ConfigDescription("At high tightness, core size caps here. Lower = denser piles (10 ≈ just enough for a full roll).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 64), Array.Empty<object>()));
		FallbackMode = config.Bind<string>("General", "Fallback Mode", "Region", new ConfigDescription("If the dense core is full: Region = stay in core only (no spill). Planet = vanilla any free globe triangle. Expand is treated like Region (no multi-patch spill).", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Region", "Planet", "Expand" }), Array.Empty<object>()));
		HideSpecialMissionsFromMap = config.Bind<bool>("General", "Hide Special Missions From Map", true, "When enabled, filtered specials are removed from the 3D globe (side panel unchanged). Always-hide + hide-when-completed lists apply.");
		AlwaysHideNameFilters = config.Bind<string>("General", "Always Hide Name Filters", "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation", "Comma-separated substrings (mission ID/name/type). Always removed from the globe when Hide Special Missions From Map is on.");
		HideWhenCompletedNameFilters = config.Bind<string>("General", "Hide When Completed Name Filters", "containment,ground zero,oxythane breach escalation,oxythane breach", "Comma-separated substrings. Removed from the globe only after PlayerData.HasCompletedMission is true (e.g. Ground Zero, Containment).");
		SpecialMissionNameFilters = config.Bind<string>("General", "Special Mission Name Filters", "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation", "Legacy always-hide list. Prefer Always Hide Name Filters; still used as fallback if that is empty.");
		DebugLog = config.Bind<bool>("General", "Debug Log", false, "Extra cluster-build and hide diagnostics.");
		MigrateDefaultsIfNeeded();
		try
		{
			SetupFileWatcher();
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
		}
	}

	private static void MigrateDefaultsIfNeeded()
	{
		if (SchemaVersion.Value < 5)
		{
			logger.LogInfo((object)$"Migrating MissionSelectCleanup settings schema {SchemaVersion.Value} → {5}.");
			if (SchemaVersion.Value < 3)
			{
				Tightness.Value = 0.92f;
				NeighborAngle.Value = 22f;
				CoreSearchRadius.Value = 28f;
				MinTrianglesPerRegion.Value = 10;
				MaxTrianglesPerRegion.Value = 10;
				FallbackMode.Value = "Region";
			}
			if (SchemaVersion.Value < 4)
			{
				AlwaysHideNameFilters.Value = "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation";
				HideWhenCompletedNameFilters.Value = "containment,ground zero,oxythane breach escalation,oxythane breach";
				SpecialMissionNameFilters.Value = "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation";
				HideSpecialMissionsFromMap.Value = true;
			}
			SchemaVersion.Value = 5;
			try
			{
				config.Save();
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not save migrated config: " + ex.Message));
			}
			RegionClusterCache.Invalidate();
			SpecialMissionFilter.InvalidateCache();
		}
	}

	public static void Tick()
	{
		if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f)
		{
			return;
		}
		reloadPending = false;
		lastReloadTime = Time.unscaledTime;
		try
		{
			config.Reload();
			RegionClusterCache.Invalidate();
			SpecialMissionFilter.InvalidateCache();
			logger.LogInfo((object)"Config reloaded from disk.");
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error reloading config: " + ex.Message));
		}
	}

	public static void Dispose()
	{
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Changed -= OnConfigFileChanged;
			configWatcher.Created -= OnConfigFileChanged;
			configWatcher.Renamed -= OnConfigFileChanged;
			configWatcher.Dispose();
			configWatcher = null;
		}
	}

	private static void SetupFileWatcher()
	{
		configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.missionselectcleanup.cfg");
		configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
		configWatcher.Changed += OnConfigFileChanged;
		configWatcher.Created += OnConfigFileChanged;
		configWatcher.Renamed += OnConfigFileChanged;
		configWatcher.EnableRaisingEvents = true;
	}

	private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		reloadPending = true;
	}
}
[HarmonyPatch(typeof(MissionSelectWindow))]
internal static class FindUnusedMissionIndexPatch
{
	[HarmonyPrefix]
	[HarmonyPatch("FindUnusedMissionIndex")]
	private static bool FindUnusedMissionIndex_Prefix(MissionSelectWindow __instance, PlanetData planet, ref Random rand, WorldRegion region, ref int __result)
	{
		if (!ConfigManager.Enabled)
		{
			return true;
		}
		if (planet == null || region == null || planet.activeMissions == null)
		{
			return true;
		}
		if (RegionClusterCache.TryPickTriangle(planet, region, ref rand, out var triangleIndex) && triangleIndex >= 0)
		{
			__result = triangleIndex;
			return false;
		}
		if (string.Equals(ConfigManager.FallbackMode?.Value ?? "Region", "Planet", StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		__result = FindAnyFreeTriangle(planet, ref rand);
		ConfigEntry<bool> debugLog = ConfigManager.DebugLog;
		if (debugLog != null && debugLog.Value)
		{
			ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)$"Dense core full for region '{((region != null) ? region.NameID : null)}'; emergency free tri={__result} (set Fallback Mode=Planet to make this intentional).");
			}
		}
		return false;
	}

	private static int FindAnyFreeTriangle(PlanetData planet, ref Random rand)
	{
		int triangleCount = planet.activeMissions.TriangleCount;
		Span<int> span = ((triangleCount > 512) ? ((Span<int>)new int[triangleCount]) : stackalloc int[triangleCount]);
		Span<int> span2 = span;
		int num = span2.Length;
		for (int i = 0; i < span2.Length; i++)
		{
			span2[i] = i;
		}
		while (num > 0)
		{
			int index = ((Random)(ref rand)).Next(num);
			int num2 = span2[index];
			ActiveMission data = planet.activeMissions.GetData(num2);
			if (data != null && (Object)(object)data.Button == (Object)null)
			{
				return num2;
			}
			span2[index] = span2[num - 1];
			num--;
		}
		return -1;
	}
}
internal static class HideSpecialMissionsPatch
{
	private static MethodInfo clearMissionMethod;

	private static FieldInfo planetsField;

	private static bool loggedMissingClear;

	public static void Apply(Harmony harmony)
	{
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Expected O, but got Unknown
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01de: Expected O, but got Unknown
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Expected O, but got Unknown
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_0275: Expected O, but got Unknown
		clearMissionMethod = AccessTools.Method(typeof(MissionSelectWindow), "ClearMission", new Type[2]
		{
			typeof(PlanetData),
			typeof(int)
		}, (Type[])null);
		planetsField = AccessTools.Field(typeof(MissionSelectWindow), "planets");
		MethodInfo methodInfo = AccessTools.Method(typeof(MissionSelectWindow), "AddMission", new Type[8]
		{
			typeof(Mission),
			typeof(int),
			typeof(WorldRegion),
			typeof(Transform),
			typeof(object),
			typeof(bool).MakeByRefType(),
			typeof(bool),
			typeof(bool)
		}, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(MissionSelectWindow), "AddMission", new Type[3]
		{
			typeof(MissionData),
			typeof(object),
			typeof(bool)
		}, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(MissionSelectWindow), "SetupMissions", (Type[])null, (Type[])null);
		MethodInfo methodInfo4 = AccessTools.Method(typeof(MissionSelectWindow), "Setup", new Type[1] { typeof(bool) }, (Type[])null);
		if (methodInfo != null)
		{
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(HideSpecialMissionsPatch), "AddMission_Mission_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)"Patched AddMission(Mission, ...)");
			}
		}
		else
		{
			ManualLogSource logger2 = MissionSelectCleanupPlugin.Logger;
			if (logger2 != null)
			{
				logger2.LogError((object)"Could not find AddMission(Mission, ...).");
			}
		}
		if (methodInfo2 != null)
		{
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(HideSpecialMissionsPatch), "AddMission_MissionData_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			ManualLogSource logger3 = MissionSelectCleanupPlugin.Logger;
			if (logger3 != null)
			{
				logger3.LogInfo((object)"Patched AddMission(MissionData, ...)");
			}
		}
		else
		{
			ManualLogSource logger4 = MissionSelectCleanupPlugin.Logger;
			if (logger4 != null)
			{
				logger4.LogError((object)"Could not find AddMission(MissionData, ...).");
			}
		}
		if (methodInfo4 != null)
		{
			harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(HideSpecialMissionsPatch), "Setup_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			ManualLogSource logger5 = MissionSelectCleanupPlugin.Logger;
			if (logger5 != null)
			{
				logger5.LogInfo((object)"Patched Setup(bool) for map cleanup");
			}
		}
		else if (methodInfo3 != null)
		{
			harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(HideSpecialMissionsPatch), "SetupMissions_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			ManualLogSource logger6 = MissionSelectCleanupPlugin.Logger;
			if (logger6 != null)
			{
				logger6.LogInfo((object)"Patched SetupMissions for map cleanup");
			}
		}
		else
		{
			ManualLogSource logger7 = MissionSelectCleanupPlugin.Logger;
			if (logger7 != null)
			{
				logger7.LogError((object)"Could not find Setup/SetupMissions for map cleanup.");
			}
		}
		if (clearMissionMethod == null)
		{
			ManualLogSource logger8 = MissionSelectCleanupPlugin.Logger;
			if (logger8 != null)
			{
				logger8.LogError((object)"Could not find ClearMission(PlanetData, int).");
			}
		}
	}

	private static bool AddMission_Mission_Prefix(Mission mission)
	{
		if (!ConfigManager.Enabled)
		{
			return true;
		}
		SpecialMissionFilter.HideReason hideReason = SpecialMissionFilter.GetHideReason(mission);
		if (hideReason == SpecialMissionFilter.HideReason.None)
		{
			return true;
		}
		ConfigEntry<bool> debugLog = ConfigManager.DebugLog;
		if (debugLog != null && debugLog.Value)
		{
			ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[MapHide] Blocked AddMission {SpecialMissionFilter.Describe(mission)} ({hideReason})");
			}
		}
		return false;
	}

	private static bool AddMission_MissionData_Prefix(MissionData mission)
	{
		if (!ConfigManager.Enabled)
		{
			return true;
		}
		Mission mission2 = ((MissionData)(ref mission)).Mission;
		SpecialMissionFilter.HideReason hideReason = SpecialMissionFilter.GetHideReason(mission2);
		if (hideReason == SpecialMissionFilter.HideReason.None)
		{
			return true;
		}
		ConfigEntry<bool> debugLog = ConfigManager.DebugLog;
		if (debugLog != null && debugLog.Value)
		{
			ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[MapHide] Blocked AddMission(Data) {SpecialMissionFilter.Describe(mission2)} ({hideReason})");
			}
		}
		return false;
	}

	private static void Setup_Postfix(MissionSelectWindow __instance)
	{
		CleanupFilteredPins(__instance);
	}

	private static void SetupMissions_Postfix(MissionSelectWindow __instance)
	{
		CleanupFilteredPins(__instance);
	}

	private static void CleanupFilteredPins(MissionSelectWindow window)
	{
		if (!ConfigManager.Enabled || (Object)(object)window == (Object)null)
		{
			return;
		}
		ConfigEntry<bool> hideSpecialMissionsFromMap = ConfigManager.HideSpecialMissionsFromMap;
		if ((hideSpecialMissionsFromMap != null && !hideSpecialMissionsFromMap.Value) || planetsField == null || !(planetsField.GetValue(window) is PlanetData[] array))
		{
			return;
		}
		int num = 0;
		foreach (PlanetData val in array)
		{
			if (val == null || val.activeMissions == null)
			{
				continue;
			}
			int triangleCount = val.activeMissions.TriangleCount;
			for (int j = 0; j < triangleCount; j++)
			{
				ActiveMission data = val.activeMissions.GetData(j);
				if (data != null && !((Object)(object)data.Button == (Object)null))
				{
					Mission val2 = null;
					try
					{
						val2 = ((MissionData)(ref data.Button.Mission)).Mission;
					}
					catch
					{
						continue;
					}
					if (val2 != null && SpecialMissionFilter.GetHideReason(val2) != SpecialMissionFilter.HideReason.None && TryClearMission(window, val, j))
					{
						num++;
					}
				}
			}
		}
		if (num <= 0)
		{
			return;
		}
		ConfigEntry<bool> debugLog = ConfigManager.DebugLog;
		if (debugLog != null && debugLog.Value)
		{
			ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[MapHide] Cleared {num} filtered globe pin(s).");
			}
		}
	}

	private static bool TryClearMission(MissionSelectWindow window, PlanetData planet, int index)
	{
		if (clearMissionMethod == null)
		{
			if (!loggedMissingClear)
			{
				loggedMissingClear = true;
				ManualLogSource logger = MissionSelectCleanupPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)"ClearMission method missing; cannot free globe slots.");
				}
			}
			try
			{
				ActiveMission data = planet.activeMissions.GetData(index);
				if ((Object)(object)data?.Button != (Object)null)
				{
					((Component)data.Button).gameObject.SetActive(false);
				}
			}
			catch
			{
				return false;
			}
			return true;
		}
		try
		{
			clearMissionMethod.Invoke(window, new object[2] { planet, index });
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource logger2 = MissionSelectCleanupPlugin.Logger;
			if (logger2 != null)
			{
				logger2.LogWarning((object)("ClearMission failed: " + ex.Message));
			}
			return false;
		}
	}
}
[BepInPlugin("sparroh.missionselectcleanup", "MissionSelectCleanup", "1.0.5")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class MissionSelectCleanupPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.missionselectcleanup";

	public const string PluginName = "MissionSelectCleanup";

	public const string PluginVersion = "1.0.5";

	internal static ManualLogSource Logger;

	private Harmony harmony;

	private void Awake()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
		try
		{
			harmony = new Harmony("sparroh.missionselectcleanup");
			harmony.PatchAll(typeof(FindUnusedMissionIndexPatch));
			HideSpecialMissionsPatch.Apply(harmony);
			Logger.LogInfo((object)(string.Format("{0} v{1} loaded. Enabled={2}, ", "MissionSelectCleanup", "1.0.5", ConfigManager.Enabled) + $"Tightness={ConfigManager.Tightness.Value:0.##}, MaxTris={ConfigManager.MaxTrianglesPerRegion.Value}, " + $"HideSpecials={ConfigManager.HideSpecialMissionsFromMap.Value}, " + "Fallback=" + ConfigManager.FallbackMode.Value));
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"Error applying patches: {arg}");
		}
	}

	private void Update()
	{
		ConfigManager.Tick();
	}

	private void OnDestroy()
	{
		ConfigManager.Dispose();
		RegionClusterCache.Invalidate();
		SpecialMissionFilter.InvalidateCache();
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}
}
internal static class RegionClusterCache
{
	private sealed class PlanetCache
	{
		public int TriangleCount;

		public int BiomeDataHash;

		public readonly Dictionary<int, RegionSlots> Regions = new Dictionary<int, RegionSlots>(8);
	}

	internal sealed class RegionSlots
	{
		public int[] SortedTriangles;

		public float[] SortedDots;

		public Vector3 Center;

		public int BiomeTriangleCount;

		public int ComponentCount;

		public int BlobTriangleCount;

		public float CoreRadiusDegrees;
	}

	private static readonly Dictionary<int, PlanetCache> CacheByPlanet = new Dictionary<int, PlanetCache>(4);

	private static bool loggedProof;

	private static ManualLogSource Log => MissionSelectCleanupPlugin.Logger;

	public static void Invalidate()
	{
		CacheByPlanet.Clear();
		loggedProof = false;
	}

	public static bool TryPickTriangle(PlanetData planet, WorldRegion region, ref Random rand, out int triangleIndex)
	{
		triangleIndex = -1;
		if (planet == null || planet.activeMissions == null || region == null)
		{
			return false;
		}
		if (!TryGetRegionSlots(planet, region, out var slots) || slots.SortedTriangles == null || slots.SortedTriangles.Length == 0)
		{
			return false;
		}
		if (TryPickFree(planet, slots.SortedTriangles, slots.SortedTriangles.Length, ref rand, out triangleIndex))
		{
			LogProofOnce(region, slots, slots.SortedTriangles.Length);
			return true;
		}
		string.Equals(ConfigManager.FallbackMode?.Value ?? "Expand", "Planet", StringComparison.OrdinalIgnoreCase);
		return false;
	}

	private static void LogProofOnce(WorldRegion region, RegionSlots slots, int keep)
	{
		if (!loggedProof)
		{
			loggedProof = true;
			string arg = ((region != null) ? region.NameID : "?");
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)($"Tight placement active: region '{arg}' core {keep}/{slots.BiomeTriangleCount} biome tris " + $"(components={slots.ComponentCount}, blob={slots.BlobTriangleCount}, " + $"coreRadius~{slots.CoreRadiusDegrees:0.#}°, tightness={ConfigManager.Tightness?.Value ?? 0f:0.##})."));
			}
		}
	}

	private static bool TryPickFree(PlanetData planet, int[] sorted, int count, ref Random rand, out int triangleIndex)
	{
		triangleIndex = -1;
		count = Mathf.Clamp(count, 0, sorted.Length);
		if (count <= 0)
		{
			return false;
		}
		Span<int> span = ((count > 256) ? ((Span<int>)new int[count]) : stackalloc int[count]);
		Span<int> span2 = span;
		int num = 0;
		for (int i = 0; i < count; i++)
		{
			int num2 = sorted[i];
			if (IsTriangleFree(planet, num2))
			{
				span2[num++] = num2;
			}
		}
		if (num <= 0)
		{
			return false;
		}
		triangleIndex = span2[((Random)(ref rand)).Next(num)];
		return true;
	}

	private static bool IsTriangleFree(PlanetData planet, int tri)
	{
		if (tri < 0 || tri >= planet.activeMissions.TriangleCount)
		{
			return false;
		}
		ActiveMission data = planet.activeMissions.GetData(tri);
		if (data != null)
		{
			return (Object)(object)data.Button == (Object)null;
		}
		return false;
	}

	private static bool TryGetRegionSlots(PlanetData planet, WorldRegion region, out RegionSlots slots)
	{
		slots = null;
		Planet planetReference = planet.planetReference;
		if (planetReference == null)
		{
			return false;
		}
		if ((Object)(object)Global.Instance == (Object)null || Global.Instance.Regions == null)
		{
			return false;
		}
		int instanceID = ((Object)planetReference).GetInstanceID();
		int triangleCount = planet.activeMissions.TriangleCount;
		int[] planetBiomeData = planetReference.PlanetBiomeData;
		int num = ((planetBiomeData != null) ? planetBiomeData.Length : 0);
		int num2 = HashSettings();
		if (!CacheByPlanet.TryGetValue(instanceID, out var value) || value.TriangleCount != triangleCount || value.BiomeDataHash != (num ^ num2))
		{
			value = BuildPlanetCache(planet, planetReference, triangleCount, planetBiomeData, num ^ num2);
			CacheByPlanet[instanceID] = value;
		}
		int num3 = Array.IndexOf(Global.Instance.Regions, region) + 1;
		if (num3 <= 0)
		{
			return false;
		}
		return value.Regions.TryGetValue(num3, out slots);
	}

	private static int HashSettings()
	{
		return ((((17 * 31 + (ConfigManager.Tightness?.Value ?? 0.9f).GetHashCode()) * 31 + (ConfigManager.MinTrianglesPerRegion?.Value ?? 10)) * 31 + (ConfigManager.MaxTrianglesPerRegion?.Value ?? 10)) * 31 + (ConfigManager.NeighborAngle?.Value ?? 22f).GetHashCode()) * 31 + (ConfigManager.CoreSearchRadius?.Value ?? 28f).GetHashCode();
	}

	private static PlanetCache BuildPlanetCache(PlanetData planet, Planet planetAsset, int triCount, int[] biomeData, int biomeHash)
	{
		//IL_0244: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: Unknown result type (might be due to invalid IL or missing references)
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		//IL_032e: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
		PlanetCache planetCache = new PlanetCache
		{
			TriangleCount = triCount,
			BiomeDataHash = biomeHash
		};
		Dictionary<int, List<int>> dictionary = new Dictionary<int, List<int>>(8);
		int num = ((biomeData != null) ? biomeData.Length : 0);
		if (num > 0)
		{
			int num2 = Math.Min(triCount, num);
			for (int i = 0; i < num2; i++)
			{
				int num3 = biomeData[i];
				if (num3 > 0)
				{
					if (!dictionary.TryGetValue(num3, out var value))
					{
						value = (dictionary[num3] = new List<int>(32));
					}
					value.Add(i);
				}
			}
		}
		if (dictionary.Count == 0 && Global.Instance?.Regions != null)
		{
			BuildSpatialPartitionBuckets(planet, planetAsset, triCount, dictionary);
		}
		else if (num > 0 && num < triCount)
		{
			ConfigEntry<bool> debugLog = ConfigManager.DebugLog;
			if (debugLog != null && debugLog.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)$"PlanetBiomeData length ({num}) < triangle count ({triCount}); only tagged tris used.");
				}
			}
		}
		float neighborDot = Mathf.Cos((ConfigManager.NeighborAngle?.Value ?? 22f) * (MathF.PI / 180f));
		float coreSearchDot = Mathf.Cos((ConfigManager.CoreSearchRadius?.Value ?? 28f) * (MathF.PI / 180f));
		int minKeep = ConfigManager.MinTrianglesPerRegion?.Value ?? 10;
		int maxKeep = ConfigManager.MaxTrianglesPerRegion?.Value ?? 10;
		float num4 = Mathf.Clamp01(ConfigManager.Tightness?.Value ?? 0.9f);
		float tightnessSquared = num4 * num4;
		foreach (KeyValuePair<int, List<int>> item2 in dictionary)
		{
			List<int> value2 = item2.Value;
			if (value2.Count == 0)
			{
				continue;
			}
			List<List<int>> list2 = BuildComponents(planet, value2, neighborDot);
			int count = list2.Count;
			List<int> list3 = ((list2.Count > 0) ? list2[0] : value2);
			if (list2.Count > 1)
			{
				list2.Sort((List<int> a, List<int> b) => b.Count.CompareTo(a.Count));
				list3 = list2[0];
			}
			Vector3 center;
			float coreRadiusDeg;
			List<int> list4 = SelectDenseCore(planet, value2, list3, coreSearchDot, minKeep, maxKeep, tightnessSquared, out center, out coreRadiusDeg);
			if (list4.Count == 0)
			{
				list4 = new List<int>(list3);
			}
			List<(int, float)> list5 = new List<(int, float)>(list4.Count);
			for (int num5 = 0; num5 < list4.Count; num5++)
			{
				Vector3 center2 = planet.activeMissions.GetCenter(list4[num5]);
				float item = ((((Vector3)(ref center2)).sqrMagnitude > 1E-08f) ? Vector3.Dot(center, ((Vector3)(ref center2)).normalized) : (-1f));
				list5.Add((list4[num5], item));
			}
			list5.Sort(((int tri, float dot) a, (int tri, float dot) b) => b.dot.CompareTo(a.dot));
			int[] array = new int[list5.Count];
			float[] array2 = new float[list5.Count];
			for (int num6 = 0; num6 < list5.Count; num6++)
			{
				array[num6] = list5[num6].Item1;
				array2[num6] = list5[num6].Item2;
			}
			RegionSlots value3 = new RegionSlots
			{
				SortedTriangles = array,
				SortedDots = array2,
				Center = center,
				BiomeTriangleCount = value2.Count,
				ComponentCount = count,
				BlobTriangleCount = list3.Count,
				CoreRadiusDegrees = coreRadiusDeg
			};
			planetCache.Regions[item2.Key] = value3;
			ConfigEntry<bool> debugLog2 = ConfigManager.DebugLog;
			if (debugLog2 != null && debugLog2.Value)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)($"Region id {item2.Key}: biome={value2.Count}, components={count}, " + $"seedBlob={list3.Count}, core={array.Length}, coreRadius~{coreRadiusDeg:0.#}°, center={center}"));
				}
			}
		}
		if (planetCache.Regions.Count == 0)
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogWarning((object)$"Built empty region cluster cache for planet '{((Object)planetAsset).name}' (triCount={triCount}, biomeLen={num}).");
			}
		}
		return planetCache;
	}

	private static List<int> SelectDenseCore(PlanetData planet, List<int> biomeTris, List<int> seedPool, float coreSearchDot, int minKeep, int maxKeep, float tightnessSquared, out Vector3 center, out float coreRadiusDeg)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_0276: Unknown result type (might be due to invalid IL or missing references)
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		center = Vector3.forward;
		coreRadiusDeg = 0f;
		int count = biomeTris.Count;
		if (count == 0)
		{
			return new List<int>();
		}
		Vector3[] array = (Vector3[])(object)new Vector3[count];
		Dictionary<int, int> dictionary = new Dictionary<int, int>(count);
		for (int i = 0; i < count; i++)
		{
			Vector3 center2 = planet.activeMissions.GetCenter(biomeTris[i]);
			array[i] = ((((Vector3)(ref center2)).sqrMagnitude > 1E-08f) ? ((Vector3)(ref center2)).normalized : Vector3.forward);
			dictionary[biomeTris[i]] = i;
		}
		int num = 0;
		int num2 = -1;
		float num3 = -1f;
		for (int j = 0; j < count; j++)
		{
			int num4 = 0;
			for (int k = 0; k < count; k++)
			{
				if (Vector3.Dot(array[j], array[k]) >= coreSearchDot)
				{
					num4++;
				}
			}
			bool flag = false;
			for (int l = 0; l < seedPool.Count; l++)
			{
				if (seedPool[l] == biomeTris[j])
				{
					flag = true;
					break;
				}
			}
			int num5 = num4 * 2 + (flag ? 1 : 0);
			float num6 = 0f;
			for (int m = 0; m < count; m++)
			{
				num6 += Vector3.Dot(array[j], array[m]);
			}
			num6 /= (float)count;
			if (num5 > num2 || (num5 == num2 && num6 > num3))
			{
				num2 = num5;
				num3 = num6;
				num = j;
			}
		}
		center = array[num];
		maxKeep = Mathf.Clamp(maxKeep, 1, count);
		minKeep = Mathf.Clamp(minKeep, 1, count);
		int num7 = Mathf.RoundToInt(Mathf.Lerp((float)count, (float)maxKeep, tightnessSquared));
		num7 = Mathf.Clamp(num7, minKeep, count);
		if (tightnessSquared >= 0.5f)
		{
			num7 = Mathf.Min(num7, maxKeep);
		}
		List<(int, float)> list = new List<(int, float)>(count);
		for (int n = 0; n < count; n++)
		{
			list.Add((n, Vector3.Dot(center, array[n])));
		}
		list.Sort(((int local, float dot) a, (int local, float dot) b) => b.dot.CompareTo(a.dot));
		List<int> list2 = new List<int>(num7);
		float num8 = 1f;
		for (int num9 = 0; num9 < num7; num9++)
		{
			list2.Add(biomeTris[list[num9].Item1]);
			num8 = list[num9].Item2;
		}
		Vector3 val = Vector3.zero;
		for (int num10 = 0; num10 < list2.Count; num10++)
		{
			int num11 = dictionary[list2[num10]];
			val += array[num11];
		}
		if (((Vector3)(ref val)).sqrMagnitude > 1E-08f)
		{
			center = ((Vector3)(ref val)).normalized;
		}
		num8 = Mathf.Clamp(num8, -1f, 1f);
		coreRadiusDeg = Mathf.Acos(num8) * 57.29578f;
		return list2;
	}

	private static List<List<int>> BuildComponents(PlanetData planet, List<int> biomeTris, float neighborDot)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		int count = biomeTris.Count;
		Vector3[] array = (Vector3[])(object)new Vector3[count];
		for (int i = 0; i < count; i++)
		{
			Vector3 center = planet.activeMissions.GetCenter(biomeTris[i]);
			array[i] = ((((Vector3)(ref center)).sqrMagnitude > 1E-08f) ? ((Vector3)(ref center)).normalized : Vector3.forward);
		}
		List<int>[] array2 = new List<int>[count];
		for (int j = 0; j < count; j++)
		{
			array2[j] = new List<int>(6);
		}
		for (int k = 0; k < count; k++)
		{
			for (int l = k + 1; l < count; l++)
			{
				if (Vector3.Dot(array[k], array[l]) >= neighborDot)
				{
					array2[k].Add(l);
					array2[l].Add(k);
				}
			}
		}
		bool[] array3 = new bool[count];
		List<List<int>> list = new List<List<int>>(8);
		Queue<int> queue = new Queue<int>(count);
		for (int m = 0; m < count; m++)
		{
			if (array3[m])
			{
				continue;
			}
			List<int> list2 = new List<int>(8);
			array3[m] = true;
			queue.Enqueue(m);
			while (queue.Count > 0)
			{
				int num = queue.Dequeue();
				list2.Add(biomeTris[num]);
				List<int> list3 = array2[num];
				for (int n = 0; n < list3.Count; n++)
				{
					int num2 = list3[n];
					if (!array3[num2])
					{
						array3[num2] = true;
						queue.Enqueue(num2);
					}
				}
			}
			list.Add(list2);
		}
		list.Sort((List<int> a, List<int> b) => b.Count.CompareTo(a.Count));
		return list;
	}

	private static void BuildSpatialPartitionBuckets(PlanetData planet, Planet planetAsset, int triCount, Dictionary<int, List<int>> buckets)
	{
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		WorldRegion[] regions = Global.Instance.Regions;
		for (int i = 0; i < regions.Length; i++)
		{
			if (regions[i] == null)
			{
				continue;
			}
			Planet planet2 = regions[i].Planet;
			if ((planet2 == null || planet2 == planetAsset) && (planet2 != null || planetAsset == GetDefaultPlanet()))
			{
				int key = i + 1;
				if (!buckets.ContainsKey(key))
				{
					buckets[key] = new List<int>(triCount / Math.Max(1, regions.Length) + 8);
				}
			}
		}
		if (buckets.Count == 0)
		{
			return;
		}
		int[] array = new int[buckets.Count];
		buckets.Keys.CopyTo(array, 0);
		Array.Sort(array);
		Vector3[] array2 = (Vector3[])(object)new Vector3[array.Length];
		for (int j = 0; j < array2.Length; j++)
		{
			float num = ((float)j + 0.5f) / (float)array2.Length * MathF.PI * 2f;
			float num2 = ((j % 2 == 0) ? 0.35f : (-0.35f));
			int num3 = j;
			Vector3 val = new Vector3(Mathf.Cos(num2) * Mathf.Cos(num), Mathf.Sin(num2), Mathf.Cos(num2) * Mathf.Sin(num));
			array2[num3] = ((Vector3)(ref val)).normalized;
		}
		for (int k = 0; k < triCount; k++)
		{
			Vector3 center = planet.activeMissions.GetCenter(k);
			if (((Vector3)(ref center)).sqrMagnitude < 1E-08f)
			{
				continue;
			}
			((Vector3)(ref center)).Normalize();
			int num4 = 0;
			float num5 = float.NegativeInfinity;
			for (int l = 0; l < array2.Length; l++)
			{
				float num6 = Vector3.Dot(center, array2[l]);
				if (num6 > num5)
				{
					num5 = num6;
					num4 = l;
				}
			}
			buckets[array[num4]].Add(k);
		}
		ManualLogSource log = Log;
		if (log != null)
		{
			log.LogWarning((object)$"Planet '{((Object)planetAsset).name}' has no usable PlanetBiomeData; using spatial partition across {array.Length} regions.");
		}
	}

	private static Planet GetDefaultPlanet()
	{
		Global instance = Global.Instance;
		if (instance?.Planets != null && instance.Planets.Length != 0)
		{
			return instance.Planets[0];
		}
		return null;
	}
}
internal static class SpecialMissionFilter
{
	public enum HideReason
	{
		None,
		Always,
		Completed
	}

	public const string DefaultAlwaysHideFilters = "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation";

	public const string DefaultHideWhenCompletedFilters = "containment,ground zero,oxythane breach escalation,oxythane breach";

	public const string DefaultFilters = "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation";

	private static string[] alwaysFilters = Array.Empty<string>();

	private static string[] completedFilters = Array.Empty<string>();

	private static string alwaysRaw;

	private static string completedRaw;

	public static bool ShouldHideFromGlobe(Mission mission)
	{
		return GetHideReason(mission) != HideReason.None;
	}

	public static bool ShouldHideFromGlobe(ref MissionData data)
	{
		return ShouldHideFromGlobe(((MissionData)(ref data)).Mission);
	}

	public static HideReason GetHideReason(Mission mission)
	{
		if (mission == null)
		{
			return HideReason.None;
		}
		ConfigEntry<bool> hideSpecialMissionsFromMap = ConfigManager.HideSpecialMissionsFromMap;
		if (hideSpecialMissionsFromMap != null && !hideSpecialMissionsFromMap.Value)
		{
			return HideReason.None;
		}
		EnsureCaches();
		string name = ((object)mission).GetType().Name;
		string id = null;
		string displayName = null;
		try
		{
			id = mission.ID;
		}
		catch
		{
		}
		try
		{
			displayName = mission.MissionName;
		}
		catch
		{
		}
		if (!string.IsNullOrEmpty(name) && name.IndexOf("Amalgamation", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return HideReason.Always;
		}
		if (MatchesAny(alwaysFilters, id, displayName, name))
		{
			return HideReason.Always;
		}
		if (MatchesAny(completedFilters, id, displayName, name) && HasCompleted(mission))
		{
			return HideReason.Completed;
		}
		return HideReason.None;
	}

	public static string Describe(Mission mission)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		if (mission == null)
		{
			return "(null)";
		}
		string text = "?";
		string text2 = "?";
		try
		{
			text = mission.ID ?? "?";
		}
		catch
		{
		}
		try
		{
			text2 = mission.MissionName ?? "?";
		}
		catch
		{
		}
		return $"id='{text}' name='{text2}' type={((object)mission).GetType().Name} flags={mission.MissionFlags}";
	}

	private static bool HasCompleted(Mission mission)
	{
		try
		{
			PlayerData instance = PlayerData.Instance;
			if (instance == null)
			{
				return false;
			}
			return instance.HasCompletedMission(mission);
		}
		catch
		{
			return false;
		}
	}

	private static bool MatchesAny(string[] filters, string id, string displayName, string typeName)
	{
		if (filters == null || filters.Length == 0)
		{
			return false;
		}
		foreach (string text in filters)
		{
			if (text.Length != 0)
			{
				if (!string.IsNullOrEmpty(id) && id.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
				if (!string.IsNullOrEmpty(displayName) && displayName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
				if (!string.IsNullOrEmpty(typeName) && typeName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
		}
		return false;
	}

	private static void EnsureCaches()
	{
		string text = ConfigManager.AlwaysHideNameFilters?.Value ?? ConfigManager.SpecialMissionNameFilters?.Value ?? "ouroboros,ouroboros blitz,incursion,weekly overtime,overtime,amalgamation hunt,amalgamation";
		string text2 = ConfigManager.HideWhenCompletedNameFilters?.Value ?? "containment,ground zero,oxythane breach escalation,oxythane breach";
		if (text != alwaysRaw)
		{
			alwaysRaw = text;
			alwaysFilters = ParseFilters(text);
		}
		if (text2 != completedRaw)
		{
			completedRaw = text2;
			completedFilters = ParseFilters(text2);
		}
	}

	private static string[] ParseFilters(string raw)
	{
		if (string.IsNullOrWhiteSpace(raw))
		{
			return Array.Empty<string>();
		}
		string[] array = raw.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
		List<string> list = new List<string>(array.Length);
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		list.Sort((string x, string y) => y.Length.CompareTo(x.Length));
		return list.ToArray();
	}

	public static void InvalidateCache()
	{
		alwaysRaw = null;
		completedRaw = null;
		alwaysFilters = Array.Empty<string>();
		completedFilters = Array.Empty<string>();
	}
}
namespace MissionSelectCleanup
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MissionSelectCleanup";

		public const string PLUGIN_NAME = "MissionSelectCleanup";

		public const string PLUGIN_VERSION = "1.0.5";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}