Decompiled source of BM MoonCatalogue v0.3.0

BM_MoonCatalogue.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using InteractiveTerminalAPI.UI;
using InteractiveTerminalAPI.UI.Application;
using InteractiveTerminalAPI.UI.Cursor;
using InteractiveTerminalAPI.UI.Screen;
using LethalConfig;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BM_MoonCatalogue")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BM_MoonCatalogue")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b1172a76-4ab4-4be8-9676-d7eb68352d65")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BM_MoonCatalogue;

public enum CatalogueSortMode
{
	Name,
	PriceAscending,
	PriceDescending
}
public enum WeatherDisplayMode
{
	Auto,
	Full,
	Compact
}
internal static class Configuration
{
	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<CatalogueSortMode> DefaultSort;

	internal static ConfigEntry<WeatherDisplayMode> WeatherDisplay;

	internal static ConfigEntry<string> UnknownPriceText;

	internal static void Initialize(ConfigFile config)
	{
		Enabled = config.Bind<bool>("General", "Enabled", true, "Enables BM_MoonCatalogue.");
		DefaultSort = config.Bind<CatalogueSortMode>("Catalogue", "Default Sort", CatalogueSortMode.Name, "Sorting used by the normal 'moons' command.");
		WeatherDisplay = config.Bind<WeatherDisplayMode>("Catalogue", "Weather Display", WeatherDisplayMode.Auto, "Full shows complete weather names. Compact shortens weather names. Auto only shortens long combinations.");
		UnknownPriceText = config.Bind<string>("Catalogue", "Unknown Price Text", "?", "Text shown when the route price cannot be determined reliably.");
		LethalConfigManager.SetModDescription("Provides a cleaner Interactive Terminal moon catalogue with weather, route prices and sorting.");
	}
}
internal sealed class MoonEntry
{
	internal SelectableLevel Level;

	internal int LevelIndex;

	internal string Name;

	internal string Weather;

	internal bool PriceKnown;

	internal int Price;

	internal string PriceSource;
}
internal static class WeatherIconManager
{
	private const int IconCellSize = 32;

	private const int IconCount = 8;

	private const int ExpectedWidth = 256;

	private const int ExpectedHeight = 32;

	private const float TargetIconHeight = 21f;

	private const float TargetBearingY = 16f;

	private const float HorizontalPadding = 2f;

	private static readonly string[] IconNames = new string[8] { "storm", "rain", "flood", "fog", "eclipse", "tornado", "meteor", "dust" };

	private static Texture2D texture;

	private static TMP_SpriteAsset spriteAsset;

	private static Material spriteMaterial;

	private static bool initialized;

	private static bool available;

	internal static bool Available => available;

	internal static void Initialize(string path)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		if (initialized)
		{
			return;
		}
		initialized = true;
		available = false;
		if (string.IsNullOrEmpty(path))
		{
			Plugin.Log.LogWarning((object)"Weather icon path is empty.");
			return;
		}
		if (!File.Exists(path))
		{
			Plugin.Log.LogWarning((object)("Weather icon sheet not found: " + path));
			return;
		}
		try
		{
			byte[] array = File.ReadAllBytes(path);
			texture = new Texture2D(2, 2, (TextureFormat)4, false);
			((Object)texture).name = "BM_WeatherIcons_Texture";
			((Texture)texture).filterMode = (FilterMode)0;
			((Texture)texture).wrapMode = (TextureWrapMode)1;
			if (!ImageConversion.LoadImage(texture, array, false))
			{
				Plugin.Log.LogWarning((object)"Unity could not decode weather_icons.png.");
				return;
			}
			Plugin.Log.LogInfo((object)("Weather icon texture loaded: " + ((Texture)texture).width + "x" + ((Texture)texture).height));
			if (((Texture)texture).width != 256 || ((Texture)texture).height != 32)
			{
				Plugin.Log.LogWarning((object)("weather_icons.png has wrong dimensions. Expected " + 256 + "x" + 32 + ", got " + ((Texture)texture).width + "x" + ((Texture)texture).height + "."));
			}
			else
			{
				spriteAsset = BuildSpriteAsset(texture);
				if ((Object)(object)spriteAsset == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Failed creating TMP weather sprite asset.");
					return;
				}
				available = true;
				Plugin.Log.LogWarning((object)"### BM WEATHER ICONS READY ###");
				Plugin.Log.LogInfo((object)("Loaded weather icon sheet: " + path));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Failed loading weather icons: " + ex));
		}
	}

	private static TMP_SpriteAsset BuildSpriteAsset(Texture2D sourceTexture)
	{
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Expected O, but got Unknown
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0216: Unknown result type (might be due to invalid IL or missing references)
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Expected O, but got Unknown
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0245: Expected O, but got Unknown
		TMP_SpriteAsset val = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
		((Object)val).name = "BM_WeatherIcons";
		try
		{
			FieldInfo field = typeof(TMP_SpriteAsset).GetField("m_Version", BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo field2 = typeof(TMP_SpriteAsset).GetField("m_SpriteGlyphTable", BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo field3 = typeof(TMP_SpriteAsset).GetField("m_SpriteCharacterTable", BindingFlags.Instance | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(val, "1.1.0");
			}
			if (field2 == null)
			{
				Plugin.Log.LogError((object)"TMP field m_SpriteGlyphTable was not found.");
				Object.Destroy((Object)(object)val);
				return null;
			}
			if (field3 == null)
			{
				Plugin.Log.LogError((object)"TMP field m_SpriteCharacterTable was not found.");
				Object.Destroy((Object)(object)val);
				return null;
			}
			List<TMP_SpriteGlyph> list = new List<TMP_SpriteGlyph>();
			List<TMP_SpriteCharacter> list2 = new List<TMP_SpriteCharacter>();
			field2.SetValue(val, list);
			field3.SetValue(val, list2);
			val.spriteSheet = (Texture)(object)sourceTexture;
			Shader val2 = Shader.Find("TextMeshPro/Sprite");
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogError((object)"TextMeshPro/Sprite shader was not found.");
				Object.Destroy((Object)(object)val);
				return null;
			}
			spriteMaterial = new Material(val2);
			((Object)spriteMaterial).name = "BM_WeatherIcons_Material";
			spriteMaterial.SetTexture(ShaderUtilities.ID_MainTex, (Texture)(object)sourceTexture);
			((TMP_Asset)val).material = spriteMaterial;
			Color32[] pixels = sourceTexture.GetPixels32();
			GlyphMetrics val3 = default(GlyphMetrics);
			for (int i = 0; i < 8; i++)
			{
				GlyphRect tightGlyphRect = GetTightGlyphRect(sourceTexture, pixels, i);
				float num = 1f;
				if (((GlyphRect)(ref tightGlyphRect)).height > 0)
				{
					num = (float)((GlyphRect)(ref tightGlyphRect)).width / (float)((GlyphRect)(ref tightGlyphRect)).height;
				}
				float num2 = 21f;
				float num3 = num2 * num;
				if (num3 < 9f)
				{
					num3 = 9f;
				}
				if (num3 > 24f)
				{
					num3 = 24f;
				}
				float num4 = num3 + 2f;
				((GlyphMetrics)(ref val3))..ctor(num3, num2, 0f, 16f, num4);
				TMP_SpriteGlyph val4 = new TMP_SpriteGlyph((uint)i, val3, tightGlyphRect, 1f, 0);
				list.Add(val4);
				uint num5 = (uint)(57344 + i);
				TMP_SpriteCharacter val5 = new TMP_SpriteCharacter(num5, val, val4);
				val5.name = IconNames[i];
				((TMP_TextElement)val5).scale = 1f;
				list2.Add(val5);
				Plugin.Log.LogDebug((object)("Weather icon " + IconNames[i] + " crop=" + ((GlyphRect)(ref tightGlyphRect)).x + "," + ((GlyphRect)(ref tightGlyphRect)).y + " " + ((GlyphRect)(ref tightGlyphRect)).width + "x" + ((GlyphRect)(ref tightGlyphRect)).height + " display=" + num3 + "x" + num2));
			}
			val.SortGlyphTable();
			val.UpdateLookupTables();
			Plugin.Log.LogInfo((object)("Created runtime TMP weather sprite asset with " + list2.Count + " icon(s)."));
			return val;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Failed building TMP weather sprite asset: " + ex));
			Object.Destroy((Object)(object)val);
			return null;
		}
	}

	private static GlyphRect GetTightGlyphRect(Texture2D sourceTexture, Color32[] pixels, int iconIndex)
	{
		//IL_0147: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		int num = iconIndex * 32;
		int num2 = num + 32 - 1;
		int num3 = num2;
		int num4 = num;
		int num5 = 31;
		int num6 = 0;
		bool flag = false;
		for (int i = 0; i < 32; i++)
		{
			for (int j = num; j <= num2; j++)
			{
				int num7 = i * ((Texture)sourceTexture).width + j;
				if (num7 >= 0 && num7 < pixels.Length && pixels[num7].a > 16)
				{
					flag = true;
					if (j < num3)
					{
						num3 = j;
					}
					if (j > num4)
					{
						num4 = j;
					}
					if (i < num5)
					{
						num5 = i;
					}
					if (i > num6)
					{
						num6 = i;
					}
				}
			}
		}
		if (!flag)
		{
			return new GlyphRect(num, 0, 32, 32);
		}
		num3 = Mathf.Max(num, num3 - 1);
		num4 = Mathf.Min(num2, num4 + 1);
		num5 = Mathf.Max(0, num5 - 1);
		num6 = Mathf.Min(31, num6 + 1);
		int num8 = num4 - num3 + 1;
		int num9 = num6 - num5 + 1;
		return new GlyphRect(num3, num5, num8, num9);
	}

	internal static void ApplyToTerminal(Terminal terminal)
	{
		if (!available || (Object)(object)spriteAsset == (Object)null || (Object)(object)terminal == (Object)null || (Object)(object)terminal.screenText == (Object)null || (Object)(object)terminal.screenText.textComponent == (Object)null)
		{
			return;
		}
		TMP_Text textComponent = terminal.screenText.textComponent;
		textComponent.richText = true;
		if ((Object)(object)textComponent.spriteAsset == (Object)null)
		{
			textComponent.spriteAsset = spriteAsset;
			Plugin.Log.LogInfo((object)"BM weather sprite asset assigned directly to terminal TMP.");
		}
		else if ((Object)(object)textComponent.spriteAsset != (Object)(object)spriteAsset)
		{
			if (textComponent.spriteAsset.fallbackSpriteAssets == null)
			{
				textComponent.spriteAsset.fallbackSpriteAssets = new List<TMP_SpriteAsset>();
			}
			if (!textComponent.spriteAsset.fallbackSpriteAssets.Contains(spriteAsset))
			{
				textComponent.spriteAsset.fallbackSpriteAssets.Add(spriteAsset);
				Plugin.Log.LogInfo((object)"BM weather sprite asset added as terminal TMP fallback.");
			}
		}
		((Graphic)textComponent).SetVerticesDirty();
		((Graphic)textComponent).SetLayoutDirty();
	}

	internal static string GetTag(string weatherName)
	{
		if (!available)
		{
			return null;
		}
		string spriteName = GetSpriteName(weatherName);
		if (string.IsNullOrEmpty(spriteName))
		{
			return null;
		}
		return "<sprite name=\"" + spriteName + "\">";
	}

	private static string GetSpriteName(string weatherName)
	{
		if (string.IsNullOrWhiteSpace(weatherName))
		{
			return null;
		}
		switch (weatherName.Trim().ToLowerInvariant())
		{
		case "stormy":
		case "storm":
			return "storm";
		case "rainy":
		case "rain":
			return "rain";
		case "flooded":
		case "flood":
			return "flood";
		case "foggy":
		case "fog":
			return "fog";
		case "eclipsed":
		case "eclipse":
			return "eclipse";
		case "tornado":
		case "torn":
			return "tornado";
		case "meteor shower":
		case "meteorshower":
		case "meteor":
			return "meteor";
		case "dust clouds":
		case "dustclouds":
		case "dust":
			return "dust";
		case "none":
			return null;
		default:
			return null;
		}
	}
}
internal static class MoonCatalogue
{
	private static readonly Regex WeatherParenthesesRegex = new Regex("\\((.*?)\\)", RegexOptions.Compiled);

	private static MethodInfo textPostProcessMethod;

	private static bool searchedForTextPostProcess;

	private static MethodInfo loadNewNodeIfAffordableMethod;

	internal static List<MoonEntry> BuildEntries(Terminal terminal, CatalogueSortMode sortMode)
	{
		List<MoonEntry> list = new List<MoonEntry>();
		if ((Object)(object)terminal == (Object)null)
		{
			return list;
		}
		SelectableLevel[] levels = GetLevels(terminal);
		if (levels == null)
		{
			return list;
		}
		string existingCatalogueText = GetExistingCatalogueText(terminal);
		for (int i = 0; i < levels.Length; i++)
		{
			SelectableLevel val = levels[i];
			if (!((Object)(object)val == (Object)null))
			{
				MoonEntry moonEntry = new MoonEntry();
				moonEntry.Level = val;
				moonEntry.LevelIndex = i;
				moonEntry.Name = GetCleanMoonName(val);
				moonEntry.Weather = ResolveWeather(val, moonEntry.Name, existingCatalogueText);
				MoonPriceResolver.PriceResult priceResult = MoonPriceResolver.ResolvePrice(terminal, val, i);
				moonEntry.PriceKnown = priceResult.Known;
				moonEntry.Price = priceResult.Price;
				moonEntry.PriceSource = priceResult.Source;
				list.Add(moonEntry);
			}
		}
		SortEntries(list, sortMode);
		Plugin.Log.LogInfo((object)("BM catalogue built " + list.Count + " moon entries. Sort=" + sortMode));
		return list;
	}

	private static SelectableLevel[] GetLevels(Terminal terminal)
	{
		if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.levels != null)
		{
			return StartOfRound.Instance.levels;
		}
		if ((Object)(object)terminal != (Object)null && terminal.moonsCatalogueList != null)
		{
			return terminal.moonsCatalogueList;
		}
		return null;
	}

	internal static string FormatRow(MoonEntry entry)
	{
		if (entry == null)
		{
			return string.Empty;
		}
		string text = Truncate(entry.Name, 16);
		string priceText = GetPriceText(entry);
		int visualUnits;
		string text2 = FormatWeatherForRow(entry.Weather, out visualUnits);
		string text3 = priceText.PadLeft(5);
		return "<line-height=78%>" + text + "<pos=47%>" + text2 + "<pos=79%>" + text3;
	}

	internal static string GetHeader()
	{
		return "MOON<pos=47%>WEATHER<pos=79%>PRICE";
	}

	private static string GetPriceText(MoonEntry entry)
	{
		if (entry != null && entry.PriceKnown)
		{
			return entry.Price.ToString();
		}
		string text = Configuration.UnknownPriceText.Value;
		if (string.IsNullOrEmpty(text))
		{
			text = "?";
		}
		return text;
	}

	private static string FormatWeatherForRow(string weather, out int visualUnits)
	{
		visualUnits = 0;
		if (string.IsNullOrWhiteSpace(weather))
		{
			return string.Empty;
		}
		if (weather.Trim().Equals("None", StringComparison.OrdinalIgnoreCase))
		{
			return string.Empty;
		}
		if (WeatherIconManager.Available)
		{
			string[] array = weather.Split(new char[1] { '+' }, StringSplitOptions.RemoveEmptyEntries);
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			string[] array2 = array;
			foreach (string text in array2)
			{
				string text2 = text.Trim();
				string tag = WeatherIconManager.GetTag(text2);
				if (num > 0)
				{
					stringBuilder.Append(' ');
				}
				if (!string.IsNullOrEmpty(tag))
				{
					stringBuilder.Append(tag);
					visualUnits++;
				}
				else
				{
					string unknownWeatherCode = GetUnknownWeatherCode(text2);
					stringBuilder.Append(unknownWeatherCode);
					visualUnits += unknownWeatherCode.Length;
				}
				num++;
			}
			return stringBuilder.ToString();
		}
		string text3 = CompactWeather(weather);
		visualUnits = text3.Length;
		return text3;
	}

	private static string GetUnknownWeatherCode(string weather)
	{
		if (string.IsNullOrWhiteSpace(weather))
		{
			return "?";
		}
		StringBuilder stringBuilder = new StringBuilder();
		foreach (char c in weather)
		{
			if (char.IsLetterOrDigit(c))
			{
				stringBuilder.Append(char.ToUpperInvariant(c));
			}
			if (stringBuilder.Length >= 3)
			{
				break;
			}
		}
		if (stringBuilder.Length == 0)
		{
			return "?";
		}
		return stringBuilder.ToString();
	}

	internal static string GetSortText(CatalogueSortMode sort)
	{
		return sort switch
		{
			CatalogueSortMode.PriceAscending => "PRICE ^", 
			CatalogueSortMode.PriceDescending => "PRICE v", 
			_ => "NAME", 
		};
	}

	internal static void OpenRouteConfirmation(Terminal terminal, MoonEntry entry)
	{
		if ((Object)(object)terminal == (Object)null || entry == null)
		{
			return;
		}
		TerminalNode val = FindRouteNode(terminal, entry);
		if ((Object)(object)val == (Object)null)
		{
			Plugin.Log.LogWarning((object)("Could not find route node for " + entry.Name + "."));
			return;
		}
		try
		{
			if (loadNewNodeIfAffordableMethod == null)
			{
				loadNewNodeIfAffordableMethod = typeof(Terminal).GetMethod("LoadNewNodeIfAffordable", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (loadNewNodeIfAffordableMethod == null)
			{
				Plugin.Log.LogError((object)"Terminal.LoadNewNodeIfAffordable could not be found.");
				return;
			}
			Plugin.Log.LogInfo((object)("Opening vanilla route confirmation for " + entry.Name + ". LevelIndex=" + entry.LevelIndex + ", Price=" + val.itemCost));
			loadNewNodeIfAffordableMethod.Invoke(terminal, new object[1] { val });
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Failed opening route confirmation for " + entry.Name + ": " + ex));
		}
	}

	private static TerminalNode FindRouteNode(Terminal terminal, MoonEntry entry)
	{
		if ((Object)(object)terminal == (Object)null || entry == null || (Object)(object)terminal.terminalNodes == (Object)null)
		{
			return null;
		}
		TerminalKeyword val = FindKeyword(terminal, "route");
		if ((Object)(object)val != (Object)null && val.compatibleNouns != null)
		{
			CompatibleNoun[] compatibleNouns = val.compatibleNouns;
			foreach (CompatibleNoun val2 in compatibleNouns)
			{
				if (val2 != null && !((Object)(object)val2.noun == (Object)null) && !((Object)(object)val2.result == (Object)null) && MoonNamesMatch(entry.Name, val2.noun.word))
				{
					return val2.result;
				}
			}
		}
		TerminalNode val3 = FindRouteNodeInList(terminal.terminalNodes.terminalNodes, entry.LevelIndex);
		if ((Object)(object)val3 != (Object)null)
		{
			return val3;
		}
		return FindRouteNodeInList(terminal.terminalNodes.specialNodes, entry.LevelIndex);
	}

	private static TerminalNode FindRouteNodeInList(List<TerminalNode> nodes, int levelIndex)
	{
		if (nodes == null)
		{
			return null;
		}
		foreach (TerminalNode node in nodes)
		{
			if ((Object)(object)node == (Object)null || node.buyRerouteToMoon != levelIndex)
			{
				continue;
			}
			return node;
		}
		return null;
	}

	private static TerminalKeyword FindKeyword(Terminal terminal, string word)
	{
		if ((Object)(object)terminal == (Object)null || (Object)(object)terminal.terminalNodes == (Object)null || terminal.terminalNodes.allKeywords == null)
		{
			return null;
		}
		TerminalKeyword[] allKeywords = terminal.terminalNodes.allKeywords;
		foreach (TerminalKeyword val in allKeywords)
		{
			if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.word) && val.word.Equals(word, StringComparison.OrdinalIgnoreCase))
			{
				return val;
			}
		}
		return null;
	}

	private static bool MoonNamesMatch(string left, string right)
	{
		if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
		{
			return false;
		}
		return NormalizeMoonName(left) == NormalizeMoonName(right);
	}

	private static string GetExistingCatalogueText(Terminal terminal)
	{
		TerminalNode val = FindOriginalMoonsNode(terminal);
		if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.displayText))
		{
			return null;
		}
		try
		{
			if (!searchedForTextPostProcess)
			{
				searchedForTextPostProcess = true;
				textPostProcessMethod = typeof(Terminal).GetMethod("TextPostProcess", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (textPostProcessMethod == null)
			{
				return val.displayText;
			}
			object obj = textPostProcessMethod.Invoke(terminal, new object[2] { val.displayText, val });
			string text = obj as string;
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogDebug((object)("Could not read original processed moons text: " + ex.Message));
		}
		return val.displayText;
	}

	private static TerminalNode FindOriginalMoonsNode(Terminal terminal)
	{
		if ((Object)(object)terminal == (Object)null || (Object)(object)terminal.terminalNodes == (Object)null || terminal.terminalNodes.allKeywords == null)
		{
			return null;
		}
		TerminalKeyword[] allKeywords = terminal.terminalNodes.allKeywords;
		foreach (TerminalKeyword val in allKeywords)
		{
			if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.word) && val.word.Equals("moons", StringComparison.OrdinalIgnoreCase) && (Object)(object)val.specialKeywordResult != (Object)null)
			{
				return val.specialKeywordResult;
			}
		}
		return null;
	}

	private static string ResolveWeather(SelectableLevel level, string moonName, string originalCatalogue)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		string text = ExtractWeatherFromExistingCatalogue(originalCatalogue, moonName);
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text.Trim();
		}
		if ((int)level.currentWeather == -1)
		{
			return "None";
		}
		return ((object)Unsafe.As<LevelWeatherType, LevelWeatherType>(ref level.currentWeather)/*cast due to .constrained prefix*/).ToString();
	}

	private static string ExtractWeatherFromExistingCatalogue(string catalogue, string moonName)
	{
		if (string.IsNullOrEmpty(catalogue) || string.IsNullOrEmpty(moonName))
		{
			return null;
		}
		string value = NormalizeMoonName(moonName);
		string[] array = catalogue.Replace("\r", string.Empty).Split(new char[1] { '\n' });
		string[] array2 = array;
		foreach (string text in array2)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				continue;
			}
			string text2 = text.Trim();
			string text3 = NormalizeMoonName(text2);
			if (text3.IndexOf(value, StringComparison.OrdinalIgnoreCase) < 0)
			{
				continue;
			}
			Match match = WeatherParenthesesRegex.Match(text2);
			if (match.Success && match.Groups.Count >= 2)
			{
				string text4 = match.Groups[1].Value.Trim();
				if (!string.IsNullOrEmpty(text4))
				{
					return text4;
				}
			}
		}
		return null;
	}

	private static string CompactWeather(string weather)
	{
		if (string.IsNullOrWhiteSpace(weather))
		{
			return string.Empty;
		}
		string[] array = weather.Split(new char[1] { '+' }, StringSplitOptions.RemoveEmptyEntries);
		List<string> list = new List<string>();
		string[] array2 = array;
		foreach (string text in array2)
		{
			string text2 = CompactWeatherName(text.Trim());
			if (!string.IsNullOrEmpty(text2))
			{
				list.Add(text2);
			}
		}
		return string.Join("+", list.ToArray());
	}

	private static string CompactWeatherName(string weather)
	{
		if (string.IsNullOrWhiteSpace(weather))
		{
			return string.Empty;
		}
		return weather.Trim().ToLowerInvariant() switch
		{
			"eclipsed" => "ECL", 
			"flooded" => "FLD", 
			"rainy" => "RAIN", 
			"foggy" => "FOG", 
			"stormy" => "STM", 
			"tornado" => "TOR", 
			"meteor shower" => "MET", 
			"dust clouds" => "DST", 
			"none" => "", 
			_ => GetUnknownWeatherCode(weather), 
		};
	}

	private static void SortEntries(List<MoonEntry> entries, CatalogueSortMode mode)
	{
		switch (mode)
		{
		case CatalogueSortMode.PriceAscending:
			entries.Sort(delegate(MoonEntry left, MoonEntry right)
			{
				int num = CompareKnownPrice(left, right);
				if (num != 0)
				{
					return num;
				}
				if (left.PriceKnown && right.PriceKnown)
				{
					int num2 = left.Price.CompareTo(right.Price);
					if (num2 != 0)
					{
						return num2;
					}
				}
				return CompareName(left, right);
			});
			break;
		case CatalogueSortMode.PriceDescending:
			entries.Sort(delegate(MoonEntry left, MoonEntry right)
			{
				int num = CompareKnownPrice(left, right);
				if (num != 0)
				{
					return num;
				}
				if (left.PriceKnown && right.PriceKnown)
				{
					int num2 = right.Price.CompareTo(left.Price);
					if (num2 != 0)
					{
						return num2;
					}
				}
				return CompareName(left, right);
			});
			break;
		default:
			entries.Sort(CompareName);
			break;
		}
	}

	private static int CompareKnownPrice(MoonEntry left, MoonEntry right)
	{
		if (left.PriceKnown && !right.PriceKnown)
		{
			return -1;
		}
		if (!left.PriceKnown && right.PriceKnown)
		{
			return 1;
		}
		return 0;
	}

	private static int CompareName(MoonEntry left, MoonEntry right)
	{
		return string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase);
	}

	private static string GetCleanMoonName(SelectableLevel level)
	{
		if ((Object)(object)level == (Object)null)
		{
			return "Unknown";
		}
		string text = level.PlanetName;
		if (string.IsNullOrWhiteSpace(text))
		{
			text = ((Object)level).name;
		}
		if (string.IsNullOrWhiteSpace(text))
		{
			return "Unknown";
		}
		text = text.Trim();
		Match match = Regex.Match(text, "^\\d+\\s+(.+)$");
		if (match.Success && match.Groups.Count > 1)
		{
			text = match.Groups[1].Value.Trim();
		}
		return text;
	}

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

	private static string Truncate(string value, int maximum)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		if (value.Length <= maximum)
		{
			return value;
		}
		if (maximum <= 3)
		{
			return value.Substring(0, maximum);
		}
		return value.Substring(0, maximum - 3) + "...";
	}
}
public abstract class BaseMoonCatalogueApplication : InteractiveTerminalApplication<CursorElement>
{
	protected abstract CatalogueSortMode SortMode { get; }

	public override void Initialization()
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Expected O, but got Unknown
		if (!Configuration.Enabled.Value)
		{
			ShowDisabledScreen();
			return;
		}
		Terminal currentTerminal = ((TerminalApplication)this).terminal;
		WeatherIconManager.ApplyToTerminal(currentTerminal);
		List<MoonEntry> list = MoonCatalogue.BuildEntries(currentTerminal, SortMode);
		CursorElement[] array = (CursorElement[])(object)new CursorElement[list.Count];
		for (int i = 0; i < list.Count; i++)
		{
			MoonEntry entry = list[i];
			CursorElement val = new CursorElement();
			val.Name = MoonCatalogue.FormatRow(entry);
			val.Description = string.Empty;
			val.Action = delegate
			{
				((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = null;
				((TerminalApplication)this).currentScreen = null;
				MoonCatalogue.OpenRouteConfirmation(currentTerminal, entry);
			};
			array[i] = val;
		}
		if (array.Length == 0)
		{
			ShowEmptyScreen();
			return;
		}
		CursorMenu<CursorElement> val2 = CursorMenu<CursorElement>.Create(0, '>', array, (Func<CursorElement, CursorElement, int>[])null);
		ITextElement[] array2 = (ITextElement[])(object)new ITextElement[8]
		{
			(ITextElement)TextElement.Create("SORT: " + MoonCatalogue.GetSortText(SortMode)),
			(ITextElement)TextElement.Create(" "),
			(ITextElement)TextElement.Create(MoonCatalogue.GetHeader()),
			(ITextElement)TextElement.Create("---------------------------------------------"),
			(ITextElement)val2,
			(ITextElement)TextElement.Create(" "),
			(ITextElement)TextElement.Create("ENTER: Route to selected moon"),
			(ITextElement)TextElement.Create("moons | moons price | moons price desc")
		};
		IScreen currentScreen = (IScreen)(object)BoxedScreen.Create("BM Moon Catalogue", array2);
		((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = (BaseCursorMenu<CursorElement>)(object)val2;
		((TerminalApplication)this).currentScreen = currentScreen;
		Plugin.Log.LogInfo((object)("Opened BM Moon Catalogue. Entries=" + list.Count + ", sort=" + SortMode.ToString() + ", icons=" + WeatherIconManager.Available));
	}

	private void ShowDisabledScreen()
	{
		IScreen currentScreen = (IScreen)(object)BoxedScreen.Create("BM Moon Catalogue", (ITextElement[])(object)new ITextElement[1] { (ITextElement)TextElement.Create("BM_MoonCatalogue is disabled in config.") });
		((TerminalApplication)this).currentScreen = currentScreen;
	}

	private void ShowEmptyScreen()
	{
		IScreen currentScreen = (IScreen)(object)BoxedScreen.Create("BM Moon Catalogue", (ITextElement[])(object)new ITextElement[1] { (ITextElement)TextElement.Create("No moons were found.") });
		((TerminalApplication)this).currentScreen = currentScreen;
	}
}
public class MoonCatalogueApplication : BaseMoonCatalogueApplication
{
	protected override CatalogueSortMode SortMode => Configuration.DefaultSort.Value;
}
public class MoonCatalogueNameApplication : BaseMoonCatalogueApplication
{
	protected override CatalogueSortMode SortMode => CatalogueSortMode.Name;
}
public class MoonCataloguePriceApplication : BaseMoonCatalogueApplication
{
	protected override CatalogueSortMode SortMode => CatalogueSortMode.PriceAscending;
}
public class MoonCataloguePriceDescendingApplication : BaseMoonCatalogueApplication
{
	protected override CatalogueSortMode SortMode => CatalogueSortMode.PriceDescending;
}
internal static class MoonPriceResolver
{
	internal sealed class PriceResult
	{
		public bool Known;

		public int Price;

		public string Source;
	}

	private const int MaxSearchDepth = 4;

	internal static void Initialize()
	{
	}

	internal static PriceResult ResolvePrice(Terminal terminal, SelectableLevel level, int levelIndex)
	{
		PriceResult priceResult = new PriceResult();
		priceResult.Known = false;
		priceResult.Price = 0;
		priceResult.Source = "Unknown";
		if ((Object)(object)terminal == (Object)null || (Object)(object)level == (Object)null || (Object)(object)terminal.terminalNodes == (Object)null)
		{
			return priceResult;
		}
		if (TryResolveFromRouteKeyword(terminal, level, levelIndex, out var price))
		{
			priceResult.Known = true;
			priceResult.Price = price;
			priceResult.Source = "RouteKeyword";
			return priceResult;
		}
		if (TryResolveFromAllKeywords(terminal, level, levelIndex, out price))
		{
			priceResult.Known = true;
			priceResult.Price = price;
			priceResult.Source = "TerminalKeyword";
			return priceResult;
		}
		if (TryResolveFromNodeLists(terminal, levelIndex, out price))
		{
			priceResult.Known = true;
			priceResult.Price = price;
			priceResult.Source = "TerminalNode";
			return priceResult;
		}
		return priceResult;
	}

	private static bool TryResolveFromRouteKeyword(Terminal terminal, SelectableLevel level, int levelIndex, out int price)
	{
		price = 0;
		TerminalKeyword val = FindKeyword(terminal, "route");
		if ((Object)(object)val == (Object)null || val.compatibleNouns == null)
		{
			return false;
		}
		string left = CleanMoonName(level.PlanetName);
		CompatibleNoun[] compatibleNouns = val.compatibleNouns;
		foreach (CompatibleNoun val2 in compatibleNouns)
		{
			if (val2 == null || (Object)(object)val2.noun == (Object)null)
			{
				continue;
			}
			string word = val2.noun.word;
			if (MoonNamesMatch(left, word))
			{
				HashSet<TerminalNode> visited = new HashSet<TerminalNode>();
				if (TryResolveNodeChain(val2.result, levelIndex, 0, visited, out price))
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool TryResolveFromAllKeywords(Terminal terminal, SelectableLevel level, int levelIndex, out int price)
	{
		price = 0;
		if (terminal.terminalNodes.allKeywords == null)
		{
			return false;
		}
		string left = CleanMoonName(level.PlanetName);
		TerminalKeyword[] allKeywords = terminal.terminalNodes.allKeywords;
		foreach (TerminalKeyword val in allKeywords)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			if (val.compatibleNouns != null)
			{
				CompatibleNoun[] compatibleNouns = val.compatibleNouns;
				foreach (CompatibleNoun val2 in compatibleNouns)
				{
					if (val2 != null && !((Object)(object)val2.noun == (Object)null) && MoonNamesMatch(left, val2.noun.word))
					{
						HashSet<TerminalNode> visited = new HashSet<TerminalNode>();
						if (TryResolveNodeChain(val2.result, levelIndex, 0, visited, out price))
						{
							return true;
						}
					}
				}
			}
			if ((Object)(object)val.specialKeywordResult != (Object)null)
			{
				HashSet<TerminalNode> visited2 = new HashSet<TerminalNode>();
				if (TryResolveNodeChain(val.specialKeywordResult, levelIndex, 0, visited2, out price))
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool TryResolveFromNodeLists(Terminal terminal, int levelIndex, out int price)
	{
		price = 0;
		List<TerminalNode> terminalNodes = terminal.terminalNodes.terminalNodes;
		if (terminalNodes != null)
		{
			foreach (TerminalNode item in terminalNodes)
			{
				HashSet<TerminalNode> visited = new HashSet<TerminalNode>();
				if (TryResolveNodeChain(item, levelIndex, 0, visited, out price))
				{
					return true;
				}
			}
		}
		List<TerminalNode> specialNodes = terminal.terminalNodes.specialNodes;
		if (specialNodes != null)
		{
			foreach (TerminalNode item2 in specialNodes)
			{
				HashSet<TerminalNode> visited2 = new HashSet<TerminalNode>();
				if (TryResolveNodeChain(item2, levelIndex, 0, visited2, out price))
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool TryResolveNodeChain(TerminalNode node, int levelIndex, int depth, HashSet<TerminalNode> visited, out int price)
	{
		price = 0;
		if ((Object)(object)node == (Object)null)
		{
			return false;
		}
		if (depth > 4)
		{
			return false;
		}
		if (visited.Contains(node))
		{
			return false;
		}
		visited.Add(node);
		if (node.buyRerouteToMoon == levelIndex)
		{
			price = node.itemCost;
			return true;
		}
		if (node.isConfirmationNode && node.itemCost >= 0 && node.buyRerouteToMoon == levelIndex)
		{
			price = node.itemCost;
			return true;
		}
		if (node.terminalOptions == null)
		{
			return false;
		}
		CompatibleNoun[] terminalOptions = node.terminalOptions;
		foreach (CompatibleNoun val in terminalOptions)
		{
			if (val != null && !((Object)(object)val.result == (Object)null) && TryResolveNodeChain(val.result, levelIndex, depth + 1, visited, out price))
			{
				return true;
			}
		}
		return false;
	}

	private static TerminalKeyword FindKeyword(Terminal terminal, string word)
	{
		if ((Object)(object)terminal == (Object)null || (Object)(object)terminal.terminalNodes == (Object)null || terminal.terminalNodes.allKeywords == null)
		{
			return null;
		}
		TerminalKeyword[] allKeywords = terminal.terminalNodes.allKeywords;
		foreach (TerminalKeyword val in allKeywords)
		{
			if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.word) && val.word.Equals(word, StringComparison.OrdinalIgnoreCase))
			{
				return val;
			}
		}
		return null;
	}

	private static bool MoonNamesMatch(string left, string right)
	{
		if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
		{
			return false;
		}
		return Normalize(left) == Normalize(right);
	}

	private static string CleanMoonName(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return value;
		}
		value = value.Trim();
		int num = value.IndexOf(' ');
		if (num > 0 && int.TryParse(value.Substring(0, num), out var _))
		{
			value = value.Substring(num + 1);
		}
		return value.Trim();
	}

	private static string Normalize(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		StringBuilder stringBuilder = new StringBuilder();
		foreach (char c in value)
		{
			if (char.IsLetterOrDigit(c))
			{
				stringBuilder.Append(char.ToLowerInvariant(c));
			}
		}
		return stringBuilder.ToString();
	}
}
[BepInPlugin("com.brox.BM_MoonCatalogue", "BM_MoonCatalogue", "0.3.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string ModGuid = "com.brox.BM_MoonCatalogue";

	public const string ModName = "BM_MoonCatalogue";

	public const string ModVersion = "0.3.0";

	internal static ManualLogSource Log;

	private static bool initialized;

	internal static string WeatherIconPath => Path.Combine(Paths.PluginPath, "Brox-BM_MoonCatalogue", "assets", "weather_icons.png");

	private void Awake()
	{
		if (!initialized)
		{
			initialized = true;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"BM_MoonCatalogue v0.3.0 initializing...");
			Configuration.Initialize(((BaseUnityPlugin)this).Config);
			MoonPriceResolver.Initialize();
			WeatherIconManager.Initialize(WeatherIconPath);
			RegisterApplications();
			Log.LogWarning((object)"### BM_MOONCATALOGUE ITAPI APPLICATIONS REGISTERED ###");
			Log.LogInfo((object)"BM_MoonCatalogue loaded.");
		}
	}

	private static void RegisterApplications()
	{
		InteractiveTerminalManager.RegisterApplication<MoonCatalogueApplication>("moons", false);
		InteractiveTerminalManager.RegisterApplication<MoonCatalogueNameApplication>("moons name", false);
		InteractiveTerminalManager.RegisterApplication<MoonCataloguePriceApplication>("moons price", false);
		InteractiveTerminalManager.RegisterApplication<MoonCataloguePriceDescendingApplication>("moons price desc", false);
		InteractiveTerminalManager.RegisterApplication<MoonCatalogueApplication>("bmoons", false);
	}
}