Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BM MoonCatalogue v0.3.0
BM_MoonCatalogue.dll
Decompiled 7 hours agousing 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); } }