using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ExtendedRandomMoons")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ExtendedRandomMoons")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("832d2f86-4282-4d2b-8a6a-b706dcac09b0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace HoppinHauler.ExtendedRandomMoons;
internal sealed class ConfigExt
{
private readonly ConfigFile _cfg;
public ConfigEntry<bool> DeductCredits;
public ConfigEntry<bool> SkipConfirmation;
public ConfigEntry<bool> DifferentPlanetEachTime;
public ConfigEntry<int> AvoidRepeatCount;
public ConfigEntry<string> Blacklist;
public ConfigEntry<bool> ExcludeCompanyMoons;
public ConfigEntry<string> DisallowedWeathers;
public ConfigEntry<bool> DebugLogging;
public readonly HashSet<string> BlacklistSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
public readonly HashSet<string> DisallowedWeatherSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
public ConfigExt(ConfigFile cfg)
{
_cfg = cfg;
_cfg.SaveOnConfigSet = false;
DeductCredits = _cfg.Bind<bool>("General", "DeductCredits", true, "If false, routing via 'route random' will not deduct credits (cost forced to 0).");
SkipConfirmation = _cfg.Bind<bool>("General", "SkipConfirmation", false, "If true, 'route random' will skip the confirmation node and route immediately.");
DifferentPlanetEachTime = _cfg.Bind<bool>("General", "DifferentPlanetEachTime", true, "If true, excludes the currently orbited moon.");
AvoidRepeatCount = _cfg.Bind<int>("General", "AvoidRepeatCount", 3, "Avoids choosing any of the last N selected moons (best-effort).");
Blacklist = _cfg.Bind<string>("Moons", "Blacklist", "Gordion,Liquidation", "Comma-separated list of moons that will never be selected (e.g. Gordion,Liquidation).");
ExcludeCompanyMoons = _cfg.Bind<bool>("Moons", "ExcludeCompanyMoons", true, "If true, excludes company moons (best-effort heuristic).");
DisallowedWeathers = _cfg.Bind<string>("Weather", "DisallowedWeathers", "Eclipsed", "Comma-separated list of weather keys/names to exclude. Vanilla examples: Mild,DustClouds,Rainy,Stormy,Foggy,Flooded,Eclipsed");
DebugLogging = _cfg.Bind<bool>("Debug", "DebugLogging", false, "If true, enables verbose [ERM] debug logs.");
_cfg.Save();
_cfg.SaveOnConfigSet = true;
}
public void ReloadDerived()
{
Util.ParseCsvToNormalizedSet(Blacklist.Value, BlacklistSet);
Util.ParseCsvToNormalizedSet(DisallowedWeathers.Value, DisallowedWeatherSet);
Util.ExpandWeatherAliases(DisallowedWeatherSet);
}
}
internal static class ERMLog
{
public static void Debug(string message)
{
if (Plugin.Log != null && Plugin.Cfg != null && Plugin.Cfg.DebugLogging != null && Plugin.Cfg.DebugLogging.Value)
{
Plugin.Log.LogInfo((object)message);
}
}
public static void Info(string message)
{
if (Plugin.Log != null)
{
Plugin.Log.LogInfo((object)message);
}
}
public static void Warn(string message)
{
if (Plugin.Log != null)
{
Plugin.Log.LogWarning((object)message);
}
}
public static void Error(string message)
{
if (Plugin.Log != null)
{
Plugin.Log.LogError((object)message);
}
}
}
internal static class MoonSelector
{
private static readonly Random Rand = new Random();
private static readonly Queue<string> LastKeys = new Queue<string>();
public static bool TryPickMoon(object terminalInstance, ConfigExt cfg, out MoonCandidate chosen, out string failureReason)
{
chosen = default(MoonCandidate);
failureReason = null;
object startOfRoundInstance = ReflectionCache.GetStartOfRoundInstance();
if (startOfRoundInstance == null)
{
failureReason = "StartOfRound.Instance not available.";
return false;
}
object[] array = Util.TryGetLevelsArray(startOfRoundInstance);
if (array == null || array.Length == 0)
{
failureReason = "No levels found on StartOfRound.";
return false;
}
object obj = Util.TryGetCurrentLevel(startOfRoundInstance);
int num = Util.TryGetGroupCredits(terminalInstance, int.MaxValue);
string text = null;
int num2 = int.MinValue;
if (obj != null)
{
string text2 = Util.TryGetPlanetName(obj);
text = (string.IsNullOrEmpty(text2) ? null : Util.NormalizeMoonName(text2));
num2 = Util.TryGetLevelId(obj, int.MinValue);
}
ERMLog.Debug("[ERM] TryPickMoon: levels=" + array.Length + ", current='" + (text ?? "(null)") + "', credits=" + ((num == int.MaxValue) ? (-1) : num) + ", deduct=" + cfg.DeductCredits.Value + ", diffEachTime=" + cfg.DifferentPlanetEachTime.Value + ", excludeCompany=" + cfg.ExcludeCompanyMoons.Value + ", avoidN=" + cfg.AvoidRepeatCount.Value);
List<MoonCandidate> list = new List<MoonCandidate>(array.Length);
for (int i = 0; i < array.Length; i++)
{
object obj2 = array[i];
if (obj2 == null)
{
continue;
}
string text3 = Util.TryGetPlanetName(obj2);
if (string.IsNullOrEmpty(text3))
{
continue;
}
string text4 = Util.NormalizeMoonName(text3);
int num3 = Util.TryGetLevelId(obj2, i);
if (cfg.DifferentPlanetEachTime.Value)
{
bool flag = num2 != int.MinValue && num3 == num2;
bool flag2 = !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text4) && string.Equals(text4, text, StringComparison.InvariantCultureIgnoreCase);
if (flag || flag2)
{
ERMLog.Debug("[ERM] Filtered (current moon): " + text3);
continue;
}
}
if (cfg.BlacklistSet.Contains(text4))
{
ERMLog.Debug("[ERM] Filtered (blacklist): " + text3);
continue;
}
if (cfg.ExcludeCompanyMoons.Value && Util.IsCompanyMoonHeuristic(obj2))
{
ERMLog.Debug("[ERM] Filtered (company heuristic): " + text3);
continue;
}
string weatherKey = WeatherResolver.GetWeatherKey(obj2);
if (!string.IsNullOrEmpty(weatherKey) && cfg.DisallowedWeatherSet.Contains(weatherKey))
{
ERMLog.Debug("[ERM] Filtered (weather '" + weatherKey + "'): " + text3);
continue;
}
int cost = -1;
if (TerminalIntegration.TryGetCachedRouteCost(num3, out var cost2))
{
cost = cost2;
}
MoonCandidate moonCandidate = default(MoonCandidate);
moonCandidate.PlanetName = text3;
moonCandidate.NormalizedName = text4;
moonCandidate.LevelId = num3;
moonCandidate.Cost = cost;
moonCandidate.WeatherKey = weatherKey;
MoonCandidate item = moonCandidate;
ERMLog.Debug("[ERM] Candidate: planet='" + item.PlanetName + "', levelId=" + item.LevelId + ", cost=" + ((item.Cost == 0) ? "FREE" : item.Cost.ToString()) + ", weather=" + (item.WeatherKey ?? "(null)"));
if (cfg.DeductCredits.Value && item.Cost > 0 && num != int.MaxValue && num < item.Cost)
{
ERMLog.Debug("[ERM] Filtered (cannot afford): " + text3 + " cost=" + item.Cost + " credits=" + num);
}
else
{
list.Add(item);
}
}
ERMLog.Debug("[ERM] Candidates after filtering: " + list.Count);
if (list.Count == 0)
{
failureReason = "All moons were filtered out.";
return false;
}
int num4 = cfg.AvoidRepeatCount.Value;
if (num4 > 0)
{
if (num4 > 50)
{
num4 = 50;
}
List<MoonCandidate> list2 = new List<MoonCandidate>(list.Count);
for (int j = 0; j < list.Count; j++)
{
if (!LastKeys.Contains(list[j].RepeatKey))
{
list2.Add(list[j]);
}
}
if (list2.Count > 0)
{
list = list2;
}
}
chosen = list[Rand.Next(list.Count)];
ERMLog.Debug("[ERM] Chosen: '" + chosen.PlanetName + "' levelId=" + chosen.LevelId + " cost=" + ((chosen.Cost == 0) ? "FREE" : chosen.Cost.ToString()) + " weather=" + (string.IsNullOrEmpty(chosen.WeatherKey) ? "(null)" : chosen.WeatherKey));
if (num4 > 0)
{
LastKeys.Enqueue(chosen.RepeatKey);
while (LastKeys.Count > num4)
{
LastKeys.Dequeue();
}
}
return true;
}
}
internal struct MoonCandidate
{
public string PlanetName;
public string NormalizedName;
public int Cost;
public int LevelId;
public string WeatherKey;
public string RepeatKey => string.IsNullOrEmpty(NormalizedName) ? ("id:" + LevelId) : (NormalizedName + "|id:" + LevelId);
}
[BepInPlugin("HoppinHauler.extendedrandommoons", "ExtendedRandomMoons", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "HoppinHauler.extendedrandommoons";
public const string PluginName = "ExtendedRandomMoons";
public const string PluginVersion = "1.0.0";
internal static ManualLogSource Log;
internal static ConfigExt Cfg;
private Harmony _harmony;
private void Awake()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
try
{
Cfg = new ConfigExt(((BaseUnityPlugin)this).Config);
Cfg.ReloadDerived();
ReflectionCache.Warmup(Log);
_harmony = new Harmony("HoppinHauler.extendedrandommoons");
TerminalIntegration.ApplyPatches(_harmony, Log);
Log.LogInfo((object)"ExtendedRandomMoons loaded.");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failed to initialize ExtendedRandomMoons");
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
}
internal static class ReflectionCache
{
internal static Type TerminalType;
internal static Type TerminalNodeType;
internal static Type TerminalKeywordType;
internal static Type CompatibleNounType;
internal static Type StartOfRoundType;
internal static Type SelectableLevelType;
internal static void Warmup(ManualLogSource log)
{
TerminalType = AccessTools.TypeByName("Terminal");
TerminalNodeType = AccessTools.TypeByName("TerminalNode");
TerminalKeywordType = AccessTools.TypeByName("TerminalKeyword");
CompatibleNounType = AccessTools.TypeByName("CompatibleNoun");
StartOfRoundType = AccessTools.TypeByName("StartOfRound");
SelectableLevelType = AccessTools.TypeByName("SelectableLevel");
if (TerminalType == null || TerminalNodeType == null || TerminalKeywordType == null || CompatibleNounType == null)
{
log.LogWarning((object)"Some terminal types could not be resolved by name. Terminal integration may fail.");
}
if (StartOfRoundType == null || SelectableLevelType == null)
{
log.LogWarning((object)"Some gameplay types could not be resolved by name. Moon selection may fail.");
}
}
internal static object GetStartOfRoundInstance()
{
if (StartOfRoundType == null)
{
return null;
}
PropertyInfo property = StartOfRoundType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null)
{
return property.GetValue(null, null);
}
FieldInfo field = StartOfRoundType.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field.GetValue(null);
}
return null;
}
}
internal static class TerminalIntegration
{
private const string NodeNameRouteRandom = "erm_routeRandom";
private const string NodeNameNoSuitable = "erm_noSuitable";
private static ManualLogSource _log;
private static object _routeKeyword;
private static object _sampleDenyCompatibleNoun;
private static object _sampleConfirmKeyword;
private static readonly Dictionary<int, int> _routeCostByLevelId = new Dictionary<int, int>();
public static bool TryGetCachedRouteCost(int levelId, out int cost)
{
return _routeCostByLevelId.TryGetValue(levelId, out cost);
}
public static void ApplyPatches(Harmony harmony, ManualLogSource log)
{
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Expected O, but got Unknown
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Expected O, but got Unknown
_log = log;
MethodInfo methodInfo = AccessTools.Method(ReflectionCache.TerminalType, "Awake", (Type[])null, (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(ReflectionCache.TerminalType, "ParsePlayerSentence", (Type[])null, (Type[])null);
if (methodInfo != null)
{
harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(TerminalIntegration), "TerminalAwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
if (methodInfo2 != null)
{
harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(TerminalIntegration), "ParsePlayerSentencePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
}
public static void TerminalAwakePostfix(object __instance)
{
try
{
if (__instance == null)
{
return;
}
Plugin.Cfg.ReloadDerived();
_routeKeyword = TerminalNodeFactory.GetKeywordByName(__instance, "Route");
if (_routeKeyword == null)
{
_log.LogWarning((object)"Failed to find Route keyword.");
return;
}
BuildRouteCostCacheFromRouteKeyword(_routeKeyword);
if (TerminalNodeFactory.HasRouteRandomAlready(__instance))
{
ERMLog.Debug("[ERM] route random already registered, skipping.");
TerminalNodeFactory.TryCacheConfirmDenySamples(__instance, _routeKeyword, out _sampleConfirmKeyword, out _sampleDenyCompatibleNoun);
return;
}
object templateKeyword = null;
object templateCompatibleNoun = null;
if (Util.TryGetMemberValue(_routeKeyword, "compatibleNouns") is Array array)
{
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value != null)
{
object obj = Util.TryGetMemberValue(value, "noun");
if (obj != null)
{
templateCompatibleNoun = value;
templateKeyword = obj;
break;
}
}
}
}
object obj2 = TerminalNodeFactory.CreateTerminalKeywordFromTemplate(templateKeyword, "random", "ExtendedRandom", _routeKeyword);
object resultNode = TerminalNodeFactory.CreateEmptyResultNode("erm_routeRandom");
object newCompatibleNoun = TerminalNodeFactory.CreateCompatibleNounFromTemplate(templateCompatibleNoun, obj2, resultNode);
TerminalNodeFactory.AddKeyword(__instance, obj2);
TerminalNodeFactory.AddCompatibleNounToKeyword(__instance, "Route", newCompatibleNoun);
TerminalNodeFactory.AppendMoonsHelp(__instance, "* Random // Routes you to a random moon (uses config filters)\n");
TerminalNodeFactory.TryCacheConfirmDenySamples(__instance, _routeKeyword, out _sampleConfirmKeyword, out _sampleDenyCompatibleNoun);
_log.LogInfo((object)"Registered terminal command: route random");
}
catch (Exception ex)
{
_log.LogError((object)"Failed to install terminal keywords.");
_log.LogError((object)ex);
}
}
public static void ParsePlayerSentencePostfix(ref object __result, object __instance)
{
try
{
if (__result == null || __instance == null)
{
return;
}
string a = TerminalNodeFactory.TryGetNodeName(__result);
if (string.Equals(a, "erm_routeRandom", StringComparison.InvariantCultureIgnoreCase))
{
Plugin.Cfg.ReloadDerived();
if (!MoonSelector.TryPickMoon(__instance, Plugin.Cfg, out var chosen, out var failureReason))
{
__result = TerminalNodeFactory.CreateSimpleNode("erm_noSuitable", "\nNo suitable moons found.\nCheck your ExtendedRandomMoons config (blacklist/weather/cost).\n" + (string.IsNullOrEmpty(failureReason) ? "\n\n\n" : ("\n" + failureReason + "\n\n\n")), clearPreviousText: true);
return;
}
bool free = !Plugin.Cfg.DeductCredits.Value;
object obj = TerminalNodeFactory.CreateRouteNode(__instance, chosen, free, Plugin.Cfg.SkipConfirmation.Value, _sampleConfirmKeyword, _sampleDenyCompatibleNoun);
__result = obj;
}
}
catch (Exception ex)
{
_log.LogError((object)"Failed handling route random.");
_log.LogError((object)ex);
}
}
private static void BuildRouteCostCacheFromRouteKeyword(object routeKeyword)
{
_routeCostByLevelId.Clear();
object startOfRoundInstance = ReflectionCache.GetStartOfRoundInstance();
object[] levels = ((startOfRoundInstance != null) ? Util.TryGetLevelsArray(startOfRoundInstance) : null);
if (!(Util.TryGetMemberValue(routeKeyword, "compatibleNouns") is Array array) || array.Length == 0)
{
ERMLog.Debug("[ERM] Route cost cache: route keyword has no compatibleNouns.");
return;
}
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value == null)
{
continue;
}
object obj = Util.TryGetMemberValue(value, "noun");
object obj2 = Util.TryGetMemberValue(value, "result");
if (obj == null || obj2 == null)
{
continue;
}
int num = Util.TryGetIntMember(obj2, "itemCost", int.MinValue);
if (num == int.MinValue)
{
num = Util.TryGetIntMember(obj2, "ItemCost", int.MinValue);
}
if (num == int.MinValue)
{
continue;
}
string text = Util.TryGetStringMember(obj, "name") ?? Util.TryGetStringMember(obj, "word");
if (string.IsNullOrEmpty(text))
{
continue;
}
int num2 = FindLevelIdByName(levels, text);
if (num2 < 0)
{
int num3 = Util.TryGetIntMember(obj2, "buyRerouteToMoon", int.MinValue);
if (num3 != int.MinValue && num3 >= 0)
{
num2 = num3;
}
}
if (num2 >= 0)
{
_routeCostByLevelId[num2] = num;
}
}
ERMLog.Debug("[ERM] Route cost cache built: entries=" + _routeCostByLevelId.Count);
}
private static int FindLevelIdByName(object[] levels, string nounName)
{
if (levels == null || levels.Length == 0)
{
return -1;
}
string text = Util.NormalizeMoonName(nounName);
for (int i = 0; i < levels.Length; i++)
{
object obj = levels[i];
if (obj == null)
{
continue;
}
string text2 = Util.TryGetPlanetName(obj);
if (!string.IsNullOrEmpty(text2))
{
string text3 = Util.NormalizeMoonName(text2);
if (string.Equals(text3, text, StringComparison.InvariantCultureIgnoreCase) || text3.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) >= 0 || text.IndexOf(text3, StringComparison.InvariantCultureIgnoreCase) >= 0)
{
return Util.TryGetLevelId(obj, i);
}
}
}
return -1;
}
}
internal static class TerminalNodeFactory
{
private static object SmartCreateInstance(Type t)
{
if (t == null)
{
return null;
}
if (typeof(ScriptableObject).IsAssignableFrom(t))
{
return ScriptableObject.CreateInstance(t);
}
return Activator.CreateInstance(t);
}
private static object CloneScriptableObject(object template)
{
if (template == null)
{
return null;
}
ScriptableObject val = (ScriptableObject)((template is ScriptableObject) ? template : null);
if ((Object)(object)val == (Object)null)
{
return null;
}
return Object.Instantiate<ScriptableObject>(val);
}
public static object GetKeywordByName(object terminalInstance, string keywordName)
{
if (terminalInstance == null)
{
return null;
}
object obj = Util.TryGetMemberValue(terminalInstance, "terminalNodes");
if (obj == null)
{
return null;
}
if (!(Util.TryGetMemberValue(obj, "allKeywords") is Array array))
{
return null;
}
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value != null)
{
string a = Util.TryGetStringMember(value, "name");
if (string.Equals(a, keywordName, StringComparison.InvariantCultureIgnoreCase))
{
return value;
}
}
}
return null;
}
public static object GetKeywordByWord(object terminalInstance, string word)
{
if (terminalInstance == null)
{
return null;
}
object obj = Util.TryGetMemberValue(terminalInstance, "terminalNodes");
if (obj == null)
{
return null;
}
if (!(Util.TryGetMemberValue(obj, "allKeywords") is Array array))
{
return null;
}
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value != null)
{
string a = Util.TryGetStringMember(value, "word");
if (string.Equals(a, word, StringComparison.InvariantCultureIgnoreCase))
{
return value;
}
}
}
return null;
}
public static bool HasRouteRandomAlready(object terminalInstance)
{
object keywordByWord = GetKeywordByWord(terminalInstance, "random");
if (keywordByWord == null)
{
return false;
}
object keywordByName = GetKeywordByName(terminalInstance, "Route");
if (keywordByName == null)
{
return false;
}
if (!(Util.TryGetMemberValue(keywordByName, "compatibleNouns") is Array array))
{
return false;
}
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value == null)
{
continue;
}
object obj = Util.TryGetMemberValue(value, "noun");
if (obj != null)
{
if (obj == keywordByWord)
{
return true;
}
string a = Util.TryGetStringMember(obj, "word");
if (string.Equals(a, "random", StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
}
return false;
}
public static void AddKeyword(object terminalInstance, object newKeyword)
{
object obj = Util.TryGetMemberValue(terminalInstance, "terminalNodes");
if (obj != null && Util.TryGetMemberValue(obj, "allKeywords") is Array array)
{
Array value = Util.AppendToArray(array, newKeyword);
Util.TrySetMemberValue(obj, "allKeywords", value);
}
}
public static void AddCompatibleNounToKeyword(object terminalInstance, string keywordName, object newCompatibleNoun)
{
object keywordByName = GetKeywordByName(terminalInstance, keywordName);
if (keywordByName != null)
{
Array array = Util.TryGetMemberValue(keywordByName, "compatibleNouns") as Array;
if (array == null)
{
array = Array.CreateInstance(ReflectionCache.CompatibleNounType, 0);
}
Array value = Util.AppendToArray(array, newCompatibleNoun);
Util.TrySetMemberValue(keywordByName, "compatibleNouns", value);
}
}
public static void AppendMoonsHelp(object terminalInstance, string extraText)
{
object keywordByName = GetKeywordByName(terminalInstance, "Moons");
if (keywordByName == null)
{
return;
}
object obj = Util.TryGetMemberValue(keywordByName, "specialKeywordResult");
if (obj != null)
{
string text = Util.TryGetStringMember(obj, "displayText") ?? "";
if (text.IndexOf(extraText, StringComparison.InvariantCultureIgnoreCase) < 0 && (extraText.IndexOf("* Random", StringComparison.InvariantCultureIgnoreCase) < 0 || text.IndexOf("* Random", StringComparison.InvariantCultureIgnoreCase) < 0))
{
text += extraText;
Util.TrySetMemberValue(obj, "displayText", text);
}
}
}
public static object CreateTerminalKeywordFromTemplate(object templateKeyword, string word, string name, object defaultVerbKeyword)
{
object obj = CloneScriptableObject(templateKeyword) ?? SmartCreateInstance(ReflectionCache.TerminalKeywordType);
Util.TrySetMemberValue(obj, "word", word);
Util.TrySetMemberValue(obj, "name", name);
Util.TrySetMemberValue(obj, "defaultVerb", defaultVerbKeyword);
Util.TrySetMemberValue(obj, "compatibleNouns", Array.CreateInstance(ReflectionCache.CompatibleNounType, 0));
Util.TrySetMemberValue(obj, "isVerb", false);
Util.TrySetMemberValue(obj, "IsVerb", false);
return obj;
}
public static object CreateCompatibleNounFromTemplate(object templateCompatibleNoun, object nounKeyword, object resultNode)
{
object obj = CloneScriptableObject(templateCompatibleNoun) ?? SmartCreateInstance(ReflectionCache.CompatibleNounType);
Util.TrySetMemberValue(obj, "noun", nounKeyword);
Util.TrySetMemberValue(obj, "result", resultNode);
return obj;
}
public static object CreateEmptyResultNode(string name)
{
object obj = SmartCreateInstance(ReflectionCache.TerminalNodeType);
Util.TrySetMemberValue(obj, "name", name);
Util.TrySetMemberValue(obj, "buyRerouteToMoon", -1);
Util.TrySetMemberValue(obj, "terminalOptions", Array.CreateInstance(ReflectionCache.CompatibleNounType, 0));
return obj;
}
public static object CreateSimpleNode(string name, string displayText, bool clearPreviousText)
{
object obj = SmartCreateInstance(ReflectionCache.TerminalNodeType);
Util.TrySetMemberValue(obj, "name", name);
Util.TrySetMemberValue(obj, "displayText", displayText);
Util.TrySetMemberValue(obj, "clearPreviousText", clearPreviousText);
return obj;
}
public static string TryGetNodeName(object terminalNode)
{
return Util.TryGetStringMember(terminalNode, "name");
}
public static void TryCacheConfirmDenySamples(object terminalInstance, object routeKeyword, out object confirmKeyword, out object denyCompatibleNoun)
{
confirmKeyword = GetKeywordByName(terminalInstance, "Confirm");
denyCompatibleNoun = null;
if (!(Util.TryGetMemberValue(routeKeyword, "compatibleNouns") is Array array))
{
return;
}
for (int i = 0; i < array.Length; i++)
{
object value = array.GetValue(i);
if (value == null)
{
continue;
}
object obj = Util.TryGetMemberValue(value, "result");
if (obj == null)
{
continue;
}
int num = Util.TryGetIntMember(obj, "buyRerouteToMoon", int.MinValue);
if (num != -2 || !(Util.TryGetMemberValue(obj, "terminalOptions") is Array array2))
{
continue;
}
for (int j = 0; j < array2.Length; j++)
{
object value2 = array2.GetValue(j);
if (value2 == null)
{
continue;
}
object obj2 = Util.TryGetMemberValue(value2, "noun");
if (obj2 != null)
{
string a = Util.TryGetStringMember(obj2, "name");
if (string.Equals(a, "Deny", StringComparison.InvariantCultureIgnoreCase))
{
denyCompatibleNoun = value2;
return;
}
}
}
}
}
public static object CreateRouteNode(object terminalInstance, MoonCandidate moon, bool free, bool skipConfirmation, object sampleConfirmKeyword, object sampleDenyCompatibleNoun)
{
object obj = SmartCreateInstance(ReflectionCache.TerminalNodeType);
Util.TrySetMemberValue(obj, "name", "erm_confirmRoute_" + Util.SanitizeId(moon.NormalizedName));
Util.TrySetMemberValue(obj, "buyRerouteToMoon", moon.LevelId);
Util.TrySetMemberValue(obj, "clearPreviousText", true);
int num = ((!free) ? ((moon.Cost >= 0) ? moon.Cost : 0) : 0);
string text = moon.PlanetName ?? moon.NormalizedName ?? "UNKNOWN";
string text2 = ((num == 0) ? "FREE" : (num + " credits"));
ERMLog.Debug("[ERM] CreateRouteNode: planet='" + text + "' levelId=" + moon.LevelId + " cost=" + ((moon.Cost == 0) ? "FREE" : moon.Cost.ToString()) + " freeFlag=" + free + " itemCost=" + ((num == 0) ? "FREE" : num.ToString()) + " skipConfirm=" + skipConfirmation);
Util.TrySetMemberValue(obj, "itemCost", num);
string value = "\nRouting autopilot to " + text + " (" + text2 + ").\nYour new balance is [playerCredits].\n\nPlease enjoy your flight.\n\n\n";
Util.TrySetMemberValue(obj, "displayText", value);
if (skipConfirmation)
{
return obj;
}
object obj2 = SmartCreateInstance(ReflectionCache.TerminalNodeType);
Util.TrySetMemberValue(obj2, "name", "erm_routeNode_" + Util.SanitizeId(moon.NormalizedName));
Util.TrySetMemberValue(obj2, "buyRerouteToMoon", -2);
Util.TrySetMemberValue(obj2, "clearPreviousText", true);
Util.TrySetMemberValue(obj2, "overrideOptions", true);
Util.TrySetMemberValue(obj2, "displayPlanetInfo", true);
string value2 = "\nRoute autopilot to " + text + " (" + text2 + ")?\nType 'c' to confirm or 'd' to cancel.\n\n";
Util.TrySetMemberValue(obj2, "displayText", value2);
object value3 = CreateCompatibleNounFromTemplate(sampleDenyCompatibleNoun, sampleConfirmKeyword, obj);
object obj3 = sampleDenyCompatibleNoun;
if (obj3 == null)
{
object resultNode = CreateSimpleNode("erm_deny", "\nCancelled.\n\n\n", clearPreviousText: true);
object keywordByName = GetKeywordByName(terminalInstance, "Deny");
obj3 = CreateCompatibleNounFromTemplate(null, keywordByName, resultNode);
}
Array array = Array.CreateInstance(ReflectionCache.CompatibleNounType, 2);
array.SetValue(obj3, 0);
array.SetValue(value3, 1);
Util.TrySetMemberValue(obj2, "terminalOptions", array);
return obj2;
}
}
internal static class Util
{
private static readonly Regex LeadingDigits = new Regex("^[0-9]+", RegexOptions.Compiled);
public static void ParseCsvToNormalizedSet(string csv, HashSet<string> set)
{
set.Clear();
if (string.IsNullOrWhiteSpace(csv))
{
return;
}
string[] array = csv.Split(',');
for (int i = 0; i < array.Length; i++)
{
string text = ((array[i] == null) ? null : array[i].Trim());
if (!string.IsNullOrWhiteSpace(text))
{
set.Add(NormalizeMoonName(text));
}
}
}
public static string NormalizeMoonName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return string.Empty;
}
string text = LeadingDigits.Replace(name.Trim(), string.Empty);
text = text.Trim();
if (text.StartsWith("-", StringComparison.InvariantCulture))
{
text = text.Substring(1).Trim();
}
return text;
}
public static string NormalizeWeatherToken(string token)
{
if (string.IsNullOrWhiteSpace(token))
{
return null;
}
string text = token.Trim().ToLowerInvariant();
text = text.Replace(" ", "");
text = text.Replace("_", "");
text = text.Replace("-", "");
if (text == "dustcloud" || text == "dustclouds")
{
return "dustclouds";
}
if (text == "none")
{
return "mild";
}
return text;
}
public static void ExpandWeatherAliases(HashSet<string> set)
{
List<string> list = new List<string>(set);
set.Clear();
for (int i = 0; i < list.Count; i++)
{
string text = NormalizeWeatherToken(list[i]);
if (!string.IsNullOrEmpty(text))
{
set.Add(text);
}
}
if (set.Contains("none"))
{
set.Remove("none");
set.Add("mild");
}
}
public static object[] TryGetLevelsArray(object startOfRoundInstance)
{
object obj = TryGetMemberValue(startOfRoundInstance, "levels");
if (!(obj is Array array))
{
return null;
}
object[] array2 = new object[array.Length];
for (int i = 0; i < array.Length; i++)
{
array2[i] = array.GetValue(i);
}
return array2;
}
public static object TryGetCurrentLevel(object startOfRoundInstance)
{
return TryGetMemberValue(startOfRoundInstance, "currentLevel");
}
public static string TryGetPlanetName(object selectableLevel)
{
object obj = TryGetMemberValue(selectableLevel, "PlanetName") ?? TryGetMemberValue(selectableLevel, "planetName") ?? TryGetMemberValue(selectableLevel, "name");
return obj as string;
}
public static int TryGetLevelId(object selectableLevel, int fallbackIndex)
{
if ((TryGetMemberValue(selectableLevel, "levelID") ?? TryGetMemberValue(selectableLevel, "LevelID")) is int result)
{
return result;
}
return fallbackIndex;
}
public static bool IsCompanyMoonHeuristic(object selectableLevel)
{
object obj = TryGetMemberValue(selectableLevel, "planetHasTime");
object obj2 = TryGetMemberValue(selectableLevel, "spawnEnemiesAndScrap");
bool? flag = obj as bool?;
bool? flag2 = obj2 as bool?;
if (flag.HasValue && flag2.HasValue)
{
return !flag.Value && !flag2.Value;
}
return false;
}
public static object TryGetMemberValue(object instance, string name)
{
if (instance == null || string.IsNullOrEmpty(name))
{
return null;
}
Type type = instance.GetType();
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field.GetValue(instance);
}
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.GetIndexParameters().Length == 0)
{
return property.GetValue(instance, null);
}
return null;
}
public static bool TrySetMemberValue(object instance, string name, object value)
{
if (instance == null || string.IsNullOrEmpty(name))
{
return false;
}
Type type = instance.GetType();
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
try
{
field.SetValue(instance, value);
return true;
}
catch
{
return false;
}
}
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.GetIndexParameters().Length == 0 && property.CanWrite)
{
try
{
property.SetValue(instance, value, null);
return true;
}
catch
{
return false;
}
}
return false;
}
public static string TryGetStringMember(object instance, string name)
{
object obj = TryGetMemberValue(instance, name);
return obj as string;
}
public static int TryGetIntMember(object instance, string name, int fallback)
{
return (TryGetMemberValue(instance, name) is int num) ? num : fallback;
}
public static Array AppendToArray(Array array, object item)
{
if (array == null)
{
Array array2 = Array.CreateInstance(item.GetType(), 1);
array2.SetValue(item, 0);
return array2;
}
Type elementType = array.GetType().GetElementType();
Array array3 = Array.CreateInstance(elementType, array.Length + 1);
Array.Copy(array, array3, array.Length);
array3.SetValue(item, array.Length);
return array3;
}
public static string SanitizeId(string s)
{
if (string.IsNullOrEmpty(s))
{
return "unknown";
}
s = s.Trim().ToLowerInvariant();
s = Regex.Replace(s, "[^a-z0-9]+", "_");
return s.Trim('_');
}
public static int TryGetGroupCredits(object terminalInstance, int fallback)
{
if (TryGetMemberValue(terminalInstance, "groupCredits") is int result)
{
return result;
}
return fallback;
}
}
internal static class WeatherResolver
{
public static string GetWeatherKey(object selectableLevel)
{
if (selectableLevel == null)
{
return null;
}
object obj = Util.TryGetMemberValue(selectableLevel, "currentWeather");
if (obj != null)
{
string text = NormalizeWeather(obj);
if (!string.IsNullOrEmpty(text))
{
return text;
}
}
object obj2 = Util.TryGetMemberValue(selectableLevel, "currentWeatherString") ?? Util.TryGetMemberValue(selectableLevel, "weatherString") ?? Util.TryGetMemberValue(selectableLevel, "Weather") ?? Util.TryGetMemberValue(selectableLevel, "weather");
if (obj2 is string text2 && !string.IsNullOrWhiteSpace(text2))
{
return Util.NormalizeWeatherToken(text2);
}
return null;
}
private static string NormalizeWeather(object weatherValue)
{
string text = weatherValue.ToString();
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
if (string.Equals(text, "None", StringComparison.InvariantCultureIgnoreCase))
{
return "mild";
}
return Util.NormalizeWeatherToken(text);
}
}