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 WeatherGordion v1.4.0
WeatherGordion.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using MrovLib; using MrovLib.Events; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using WeatherGordion.Compat; using WeatherGordion.Patches; using WeatherRegistry; using WeatherRegistry.Definitions; using WeatherRegistry.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WeatherGordion")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0+ff21221130f3d2b28e4045635126b336cf91d094")] [assembly: AssemblyProduct("WeatherGordion")] [assembly: AssemblyTitle("WeatherGordion")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WeatherGordion { internal static class ClockDiagnostics { private const float SampleLogInterval = 5f; private const int MaxSampleLines = 20; private static float _lastAlpha = float.NaN; private static float _lastTargetAlpha = float.NaN; private static float _nextSampleLog; private static int _sampleLines; private static bool _patchesReported; private static bool Enabled { get { if (Plugin.Cfg != null) { return Plugin.Cfg.ClockDiagnostics.Value; } return false; } } public static void Reset() { _lastAlpha = float.NaN; _lastTargetAlpha = float.NaN; _nextSampleLog = 0f; _sampleLines = 0; } public static void Report(string reason) { if (!Enabled) { return; } try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Clock chain (" + reason + "):"); HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { stringBuilder.AppendLine(" HUDManager.Instance is null — nothing to draw on."); Plugin.Log.LogInfo((object)stringBuilder.ToString()); return; } HUDElement clock = instance.Clock; if (clock == null) { stringBuilder.AppendLine(" HUDManager.Clock is null — the vanilla element does not exist."); AppendTime(stringBuilder); Plugin.Log.LogInfo((object)stringBuilder.ToString()); return; } CanvasGroup canvasGroup = clock.canvasGroup; stringBuilder.AppendLine($" targetAlpha = {clock.targetAlpha:0.###}"); if ((Object)(object)canvasGroup == (Object)null) { stringBuilder.AppendLine(" Clock.canvasGroup is null — the element has no canvas group."); } else { GameObject gameObject = ((Component)canvasGroup).gameObject; stringBuilder.AppendLine($" canvasGroup on '{((Object)gameObject).name}': alpha {canvasGroup.alpha:0.###}, activeSelf {gameObject.activeSelf}, " + $"activeInHierarchy {gameObject.activeInHierarchy}, interactable {canvasGroup.interactable}, " + $"blocksRaycasts {canvasGroup.blocksRaycasts}, ignoreParentGroups {canvasGroup.ignoreParentGroups}"); AppendAncestors(stringBuilder, gameObject.transform); Transform transform = gameObject.transform; AppendRect(stringBuilder, (RectTransform)(object)((transform is RectTransform) ? transform : null)); AppendCanvas(stringBuilder, gameObject.transform); } AppendText(stringBuilder, "clockNumber", instance.clockNumber); AppendImage(stringBuilder, "clockIcon", instance.clockIcon); AppendTime(stringBuilder); Plugin.Log.LogInfo((object)stringBuilder.ToString().TrimEnd()); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"Clock diagnostics failed: {arg}"); } } public static void ReportPatches() { if (!Enabled || _patchesReported) { return; } _patchesReported = true; try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Clock patches:"); string[] array = new string[4] { "SetClockVisible", "SetClock", "SetClockIcon", "Update" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(typeof(HUDManager), text, (Type[])null, (Type[])null); if (methodInfo == null) { stringBuilder.AppendLine(" HUDManager." + text + ": not found"); continue; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null) { stringBuilder.AppendLine(" HUDManager." + text + ": unpatched"); continue; } stringBuilder.AppendLine(" HUDManager." + text + ": prefixes [" + Owners(patchInfo.Prefixes) + "], postfixes [" + Owners(patchInfo.Postfixes) + "], transpilers [" + Owners(patchInfo.Transpilers) + "], finalizers [" + Owners(patchInfo.Finalizers) + "]"); } MethodInfo methodInfo2 = AccessTools.Method(typeof(TimeOfDay), "SetInsideLightingDimness", (Type[])null, (Type[])null); if (methodInfo2 != null) { Patches patchInfo2 = Harmony.GetPatchInfo((MethodBase)methodInfo2); stringBuilder.AppendLine(" TimeOfDay.SetInsideLightingDimness: " + ((patchInfo2 == null) ? "unpatched" : ("prefixes [" + Owners(patchInfo2.Prefixes) + "], postfixes [" + Owners(patchInfo2.Postfixes) + "], transpilers [" + Owners(patchInfo2.Transpilers) + "]"))); } Plugin.Log.LogInfo((object)stringBuilder.ToString().TrimEnd()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not list clock patches: " + ex.Message)); } } public static void Sample() { if (!Enabled || _sampleLines >= 20) { return; } try { HUDElement val = HUDManager.Instance?.Clock; CanvasGroup val2 = val?.canvasGroup; if (!((Object)(object)val2 == (Object)null)) { float alpha = val2.alpha; float targetAlpha = val.targetAlpha; bool num = !Mathf.Approximately(alpha, _lastAlpha) || !Mathf.Approximately(targetAlpha, _lastTargetAlpha); bool flag = Time.unscaledTime >= _nextSampleLog; if (num || flag) { _lastAlpha = alpha; _lastTargetAlpha = targetAlpha; _nextSampleLog = Time.unscaledTime + 5f; _sampleLines++; Plugin.Log.LogInfo((object)($"Clock alpha at frame start: {alpha:0.###} (targetAlpha {targetAlpha:0.###}, " + $"visible in hierarchy {((Component)val2).gameObject.activeInHierarchy}).")); } } } catch (Exception ex) { Plugin.DebugLog("Clock sample failed: " + ex.Message); } } private static string Owners(ICollection<Patch> patches) { if (patches == null || patches.Count == 0) { return "none"; } List<string> list = new List<string>(); foreach (Patch patch in patches) { list.Add(patch.owner); } return string.Join(", ", list); } private static void AppendAncestors(StringBuilder text, Transform start) { text.AppendLine(" ancestors (nearest first):"); Transform parent = start.parent; int num = 0; while ((Object)(object)parent != (Object)null && num < 12) { text.AppendLine($" '{((Object)parent).name}' activeSelf {((Component)parent).gameObject.activeSelf}"); parent = parent.parent; num++; } if (num == 0) { text.AppendLine(" (none — the clock sits at the scene root)"); } } private static void AppendRect(StringBuilder text, RectTransform rect) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rect == (Object)null) { text.AppendLine(" rect: not a RectTransform"); } else { text.AppendLine($" rect: anchoredPosition {rect.anchoredPosition}, sizeDelta {rect.sizeDelta}, " + $"localScale {((Transform)rect).localScale}, lossyScale {((Transform)rect).lossyScale}"); } } private static void AppendCanvas(StringBuilder text, Transform start) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) Canvas componentInParent = ((Component)start).GetComponentInParent<Canvas>(); if ((Object)(object)componentInParent == (Object)null) { text.AppendLine(" canvas: none found above the clock"); } else { text.AppendLine($" canvas '{((Object)componentInParent).name}': enabled {((Behaviour)componentInParent).enabled}, " + $"activeInHierarchy {((Component)componentInParent).gameObject.activeInHierarchy}, renderMode {componentInParent.renderMode}, " + $"sortingOrder {componentInParent.sortingOrder}"); } } private static void AppendText(StringBuilder text, string label, TextMeshProUGUI field) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)field == (Object)null) { text.AppendLine(" " + label + ": null"); } else { text.AppendLine($" {label}: '{((TMP_Text)field).text}', enabled {((Behaviour)field).enabled}, " + $"activeInHierarchy {((Component)field).gameObject.activeInHierarchy}, colorAlpha {((Graphic)field).color.a:0.###}, " + $"fontSize {((TMP_Text)field).fontSize:0.#}"); } } private static void AppendImage(StringBuilder text, string label, Image field) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)field == (Object)null) { text.AppendLine(" " + label + ": null"); } else { text.AppendLine($" {label}: enabled {((Behaviour)field).enabled}, activeInHierarchy {((Component)field).gameObject.activeInHierarchy}, " + string.Format("sprite {0}, colorAlpha {1:0.###}", ((Object)(object)field.sprite != (Object)null) ? ((Object)field.sprite).name : "null", ((Graphic)field).color.a)); } } private static void AppendTime(StringBuilder text) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null) { text.AppendLine(" TimeOfDay.Instance is null"); } else { text.AppendLine($" time: globalTime {instance.globalTime:0.##}, currentDayTime {instance.currentDayTime:0.##}, " + $"normalized {instance.normalizedTimeOfDay:0.###}, endOfDay {instance.globalTimeAtEndOfDay:0.##}, " + $"dayMode {instance.dayMode}, started {instance.currentDayTimeStarted}, " + $"movingForward {instance.movingGlobalTimeForward}, numberOfHours {instance.numberOfHours}"); } } } internal static class ClockOverlay { private static bool _loggedVisibleFailure; private static bool _loggedTextFailure; private static bool _loggedIconFailure; private static bool _loggedAlphaFailure; private static bool _loggedClusterFailure; private static bool _loggedMissingGroup; private static bool _loggedShown; public static void Reset() { _loggedVisibleFailure = false; _loggedTextFailure = false; _loggedIconFailure = false; _loggedAlphaFailure = false; _loggedClusterFailure = false; _loggedMissingGroup = false; _loggedShown = false; ClockDiagnostics.Reset(); } public static void Draw(float normalizedTimeOfDay) { if (!Plugin.Cfg.ShowClock.Value) { return; } HUDManager hud = HUDManager.Instance; TimeOfDay time = TimeOfDay.Instance; if ((Object)(object)hud == (Object)null || (Object)(object)time == (Object)null) { return; } ClockDiagnostics.Sample(); Step("quotaAnimator", ref _loggedClusterFailure, delegate { ShowCluster(hud); }); Step("SetClockVisible", ref _loggedVisibleFailure, delegate { hud.SetClockVisible(true); }); Step("SetClock", ref _loggedTextFailure, delegate { hud.SetClock(normalizedTimeOfDay, (float)time.numberOfHours, true); }); Step("SetClockIcon", ref _loggedIconFailure, delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) hud.SetClockIcon(time.dayMode); }); Step("canvasGroup", ref _loggedAlphaFailure, delegate { CanvasGroup val = hud.Clock?.canvasGroup; if ((Object)(object)val == (Object)null) { if (!_loggedMissingGroup) { _loggedMissingGroup = true; Plugin.Log.LogWarning((object)"The vanilla clock has no canvas group, so it cannot be shown on Gordion."); } } else { if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } val.alpha = 1f; } }); } private static void ShowCluster(HUDManager hud) { Animator quotaAnimator = hud.quotaAnimator; if (!((Object)(object)quotaAnimator == (Object)null) && ((Behaviour)quotaAnimator).isActiveAndEnabled && !quotaAnimator.GetBool("visible")) { quotaAnimator.SetBool("visible", true); if (!_loggedShown) { _loggedShown = true; Plugin.Log.LogInfo((object)"Showed the Gordion clock: its HUD cluster is driven by quotaAnimator, which vanilla only reveals on moons with both spawnEnemiesAndScrap and planetHasTime."); } } } public static void Hide() { if (!Plugin.Cfg.ShowClock.Value) { return; } HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null) { return; } try { instance.SetClockVisible(false); if ((Object)(object)instance.quotaAnimator != (Object)null && ((Behaviour)instance.quotaAnimator).isActiveAndEnabled) { instance.quotaAnimator.SetBool("visible", false); } } catch { } } private static void Step(string what, ref bool alreadyLogged, Action action) { try { action(); } catch (Exception arg) { if (!alreadyLogged) { alreadyLogged = true; Plugin.Log.LogWarning((object)$"Drawing the Gordion clock failed at {what}: {arg}"); } } } } internal static class GordionLevel { private static readonly string[] CompanyAliases = new string[4] { "gordion", "71gordion", "companybuilding", "company" }; private static SelectableLevel _level; public static SelectableLevel Level => _level; public static bool OriginalPlanetHasTime { get; private set; } public static void Resolve(StartOfRound round) { if ((Object)(object)_level != (Object)null) { return; } SelectableLevel[] array = round?.levels; if (array != null && array.Length != 0) { SelectableLevel val = FindByName(array) ?? FindByShape(array); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Could not find the Company building among the moons — weather on Gordion is disabled for this session. If a moon loader renamed it, that is the likely cause."); return; } _level = val; OriginalPlanetHasTime = val.planetHasTime; string text = $"Company moon resolved: '{val.PlanetName}' (scene '{val.sceneName}', levelID {val.levelID}, "; object arg = val.planetHasTime; RandomWeatherWithVariables[] randomWeathers = val.randomWeathers; Plugin.DebugLog(text + $"planetHasTime {arg}, randomWeathers {((randomWeathers != null) ? randomWeathers.Length : 0)})."); } } public static void Reset() { _level = null; } public static bool Is(SelectableLevel level) { if ((Object)(object)level != (Object)null) { return level == _level; } return false; } public static bool IsCurrent() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { return Is(instance.currentLevel); } return false; } private static SelectableLevel FindByName(SelectableLevel[] levels) { string[] companyAliases = CompanyAliases; foreach (string text in companyAliases) { foreach (SelectableLevel val in levels) { if (!((Object)(object)val == (Object)null) && (Normalize(val.PlanetName) == text || Normalize(val.sceneName) == text || Normalize(((Object)val).name) == text)) { return val; } } } return null; } private static SelectableLevel FindByShape(SelectableLevel[] levels) { foreach (SelectableLevel val in levels) { if ((Object)(object)val != (Object)null && !val.planetHasTime && !val.spawnEnemiesAndScrap) { Plugin.Log.LogInfo((object)("No moon matched a Company name; using '" + val.PlanetName + "' (no day cycle, nothing spawns) as the Company building.")); return val; } } return null; } public static string Normalize(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } int i; for (i = 0; i < name.Length && char.IsDigit(name[i]); i++) { } if (i >= name.Length) { i = 0; } StringBuilder stringBuilder = new StringBuilder(name.Length - i); for (int j = i; j < name.Length; j++) { char c = name[j]; if (c != ' ' && c != '-' && c != '_') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } public static string ConfigKey(SelectableLevel level) { if ((Object)(object)level == (Object)null) { return string.Empty; } try { string alphanumericName = LevelHelper.GetAlphanumericName(level); if (!string.IsNullOrEmpty(alphanumericName)) { return alphanumericName; } } catch (Exception ex) { Plugin.DebugLog("MrovLib.LevelHelper.GetAlphanumericName failed (" + ex.Message + "); falling back."); } string text = level.PlanetName ?? ((Object)level).name ?? string.Empty; StringBuilder stringBuilder = new StringBuilder(text.Length); int i; for (i = 0; i < text.Length && char.IsDigit(text[i]); i++) { } if (i >= text.Length) { i = 0; } for (int j = i; j < text.Length; j++) { char c = text[j]; if (c != ' ' && c != '-' && c != '_' && c != '/' && c != '\\') { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } internal sealed class LandingWatcher : MonoBehaviour { private const float PollInterval = 0.5f; private const float FallbackLandedAfter = 20f; private float _nextPoll; private bool _landedHandled; private float _fallbackTimer; internal static void Create() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("WeatherGordion_LandingWatcher"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent<LandingWatcher>(); } private void Update() { if (Time.unscaledTime < _nextPoll) { return; } _nextPoll = Time.unscaledTime + 0.5f; try { Poll(); } catch (Exception ex) { Plugin.DebugLog("LandingWatcher poll failed: " + ex.Message); } } private void Poll() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { if (_landedHandled) { _landedHandled = false; TimeController.OnLeftGordion(); } _fallbackTimer = 0f; return; } if (!GordionLevel.IsCurrent()) { if (_landedHandled) { _landedHandled = false; TimeController.OnLeftGordion(); } _fallbackTimer = 0f; return; } bool flag = instance.shipHasLanded && !instance.shipIsLeaving; if (!flag && !instance.shipIsLeaving && !instance.inShipPhase && IsCompanySceneLoaded()) { _fallbackTimer += 0.5f; if (_fallbackTimer >= 20f && !_landedHandled) { Plugin.Log.LogWarning((object)"shipHasLanded was never set (another mod likely altered the landing sequence) — starting the Gordion clock via fallback detection."); flag = true; } } else { _fallbackTimer = 0f; } if (flag && !_landedHandled) { _landedHandled = true; TimeController.OnLandedOnGordion(); } else if (!flag && _landedHandled && (instance.shipIsLeaving || !instance.shipHasLanded)) { _landedHandled = false; _fallbackTimer = 0f; TimeController.OnLeftGordion(); } } private static bool IsCompanySceneLoaded() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) SelectableLevel level = GordionLevel.Level; if ((Object)(object)level == (Object)null || string.IsNullOrEmpty(level.sceneName)) { return false; } Scene sceneByName = SceneManager.GetSceneByName(level.sceneName); if (((Scene)(ref sceneByName)).IsValid()) { return ((Scene)(ref sceneByName)).isLoaded; } return false; } } [BepInPlugin("Timofey.WeatherGordion", "WeatherGordion", "1.4.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static PluginConfig Cfg { get; private set; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg = new PluginConfig(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Timofey.WeatherGordion"); _harmony.PatchAll(typeof(StartOfRoundPatches)); _harmony.PatchAll(typeof(SetPlanetsWeatherPatch)); _harmony.PatchAll(typeof(RoundManagerPatches)); _harmony.PatchAll(typeof(TimeOfDayPatches)); _harmony.PatchAll(typeof(SpectatorVotePatches)); WeatherUnlocker.Subscribe(); LandingWatcher.Create(); Log.LogInfo((object)("WeatherGordion v1.4.0 loaded. Weather on 71-Gordion: " + (Cfg.Enabled.Value ? "on" : "off") + ", " + $"time mode: {Cfg.TimeMode.Value}.")); } internal static void DebugLog(string message) { if (Cfg != null && Cfg.DebugMode.Value) { Log.LogInfo((object)("[Debug] " + message)); } } private void OnDestroy() { TimeController.RestoreImmediately(); WeatherUnlocker.Unsubscribe(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal enum GordionTimeMode { Off, RealTime, Simulated } internal sealed class WeatherSettings { public ConfigEntry<bool> Enabled; public ConfigEntry<int> Weight; public int EffectiveWeight { get { if (!Enabled.Value) { return 0; } return Weight.Value; } } } internal sealed class PluginConfig { private static readonly Dictionary<string, int> DefaultWeights = new Dictionary<string, int> { ["rainy"] = 120, ["foggy"] = 100, ["stormy"] = 60, ["eclipsed"] = 50, ["flooded"] = 40, ["dustclouds"] = 60 }; private static readonly HashSet<string> EnabledByDefault = new HashSet<string> { "rainy", "foggy", "stormy", "eclipsed", "flooded" }; private readonly ConfigFile _file; private readonly Dictionary<string, WeatherSettings> _weatherSettings = new Dictionary<string, WeatherSettings>(StringComparer.Ordinal); public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> DebugMode; public readonly ConfigEntry<bool> ClockDiagnostics; public readonly ConfigEntry<int> ClearWeatherWeight; public readonly ConfigEntry<bool> RespectExistingConfig; public readonly ConfigEntry<string> BannedComponents; private HashSet<string> _banned; private string _bannedSource; public readonly ConfigEntry<GordionTimeMode> TimeMode; public readonly ConfigEntry<bool> FreezeDeadline; public readonly ConfigEntry<bool> ShipLeavesAtEndOfDay; public readonly ConfigEntry<bool> SpectatorVote; public readonly ConfigEntry<bool> ShowClock; public readonly ConfigEntry<float> DayLengthSeconds; public PluginConfig(ConfigFile cfg) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown _file = cfg; Enabled = cfg.Bind<bool>("1. General", "Enabled", true, "Master switch. When off the plugin changes nothing and Gordion stays clear."); DebugMode = cfg.Bind<bool>("1. General", "DebugMode", false, "Verbose logging: which weathers were unlocked, weights written, time transitions."); ClockDiagnostics = cfg.Bind<bool>("1. General", "Clock diagnostics", false, "Report why the HUD clock is or is not on screen: the state of the vanilla element and everything above it, which mods have patched the clock methods, and whether its alpha is being cleared between frames. Off now that the cause is known — turn it on if the clock ever goes missing again, and the log will say which link in the chain broke."); ClearWeatherWeight = cfg.Bind<int>("2. Weathers", "Clear weather weight", 200, new ConfigDescription("Weight of clear weather (None) on Gordion, i.e. how often the moon stays as it is in vanilla. Set to 0 to guarantee some weather on every visit.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>())); BannedComponents = cfg.Bind<string>("2. Weathers", "Never allow, even in combinations", "", "Semicolon-separated weathers that must never occur on Gordion, including as part of a combined or progressing weather. This is stronger than switching a weather off in its own section: '[Weather.Rainy] Enabled = false' only removes plain rain, while 'Stormy + Rainy' is a separate weather that turns the rain on as well. Listing 'Rainy' here refuses both, and every other combination containing rain. Written for exactly that case — rain's puddles do not render correctly at the Company. Example: Rainy; Flooded"); RespectExistingConfig = cfg.Bind<bool>("2. Weathers", "Respect existing config", true, "Leave a weather alone if it already has a Gordion weight in mrov.WeatherRegistry.cfg. Keeps hand-tuned entries (for example an Eclipsed you unblocked yourself) from being overwritten by this mod's defaults."); TimeMode = cfg.Bind<GordionTimeMode>("3. Time", "Gordion time mode", GordionTimeMode.RealTime, "Off - no day cycle; weather is fixed for the whole visit (vanilla behaviour).\nRealTime - start the game's own clock on Gordion while landed, so progressing weather, the rising Flooded water and the HUD clock all work. planetHasTime is deliberately left alone, so the ship still never leaves on its own, no end-of-round stats screen appears, and landing late in the day is not blocked.\nSimulated - never touch the game clock at all; drive weather stages and the HUD clock from this mod's own timer instead. Use it if RealTime upsets another mod."); FreezeDeadline = cfg.Bind<bool>("3. Time", "Freeze deadline on Gordion", true, "RealTime only. The vanilla clock drains the quota deadline as it runs, which on Gordion would both cost you days and move the company buying rate — neither happens in vanilla, where the clock is stopped. Leave this on to hold the deadline still for the visit."); ShipLeavesAtEndOfDay = cfg.Bind<bool>("3. Time", "Ship leaves at end of day", true, "RealTime only. Let the ship fly off on its own when the Gordion day runs out, exactly as it does on every other moon, with the usual warning at 90% of the day. Vanilla cannot do this at the Company because the whole midnight-departure branch sits behind planetHasTime, so the mod calls the game's own networked departure itself. Turn it off to keep leaving manual and never risk being cut off mid-sale."); SpectatorVote = cfg.Bind<bool>("3. Time", "Spectators can vote to leave", true, "RealTime only. Give dead players the vanilla 'Tell autopilot ship to leave early' prompt on Gordion, so the last one alive is warned and the round can end sooner. The vote, the count and the warning are the game's own; vanilla only hides the prompt because the Company has no day. A passed vote sends the ship off even when 'Ship leaves at end of day' is turned off."); ShowClock = cfg.Bind<bool>("3. Time", "Show clock on Gordion", true, "Show the HUD clock while on Gordion, indoors included. Vanilla hides the clock whenever you count as inside, which at the Company is most of the visit, so the mod asserts it instead. Works in both RealTime and Simulated."); DayLengthSeconds = cfg.Bind<float>("3. Time", "Day length seconds", 1200f, new ConfigDescription("Simulated only: real seconds for a full Gordion day, i.e. how long progressing weather takes to run through all of its stages.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 7200f), Array.Empty<object>())); } public HashSet<string> BannedComponentNames() { string text = BannedComponents.Value ?? string.Empty; if (_banned != null && _bannedSource == text) { return _banned; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = text.Split(';'); for (int i = 0; i < array.Length; i++) { string text2 = NormalizeName(array[i]); if (text2.Length > 0) { hashSet.Add(text2); } } _banned = hashSet; _bannedSource = text; return hashSet; } public WeatherSettings SettingsFor(string weatherName) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown string text = NormalizeName(weatherName); if (_weatherSettings.TryGetValue(text, out var value)) { return value; } string text2 = "Weather." + SanitizeForConfig(weatherName); bool flag = EnabledByDefault.Contains(text); int value2; int num = (DefaultWeights.TryGetValue(text, out value2) ? value2 : 100); WeatherSettings weatherSettings = new WeatherSettings { Enabled = _file.Bind<bool>(text2, "Enabled", flag, "Whether '" + weatherName + "' can happen on Gordion at all. Turning it off takes it back out of the moon's weather pool; nothing else on the moon is affected."), Weight = _file.Bind<int>(text2, "Weight", num, new ConfigDescription("How likely '" + weatherName + "' is on Gordion, relative to the other weathers here and to 'Clear weather weight'. Ignored while Enabled is false.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>())) }; _weatherSettings[text] = weatherSettings; return weatherSettings; } private static string SanitizeForConfig(string name) { if (string.IsNullOrEmpty(name)) { return "Unnamed"; } StringBuilder stringBuilder = new StringBuilder(name.Length); foreach (char c in name) { if (c != '=' && c != '\n' && c != '\t' && c != '\\' && c != '"' && c != '\'' && c != '[' && c != ']') { stringBuilder.Append(c); } } string text = stringBuilder.ToString().Trim(); if (text.Length != 0) { return text; } return "Unnamed"; } public static string NormalizeName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(name.Length); foreach (char c in name) { if (c != ' ' && c != '\t' && c != '-' && c != '_') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } internal static class ProgressionDriver { private static List<ProgressionStage> _stages; private static int _nextStage; private static bool _active; public static void Begin() { _stages = null; _nextStage = 0; _active = false; if (!IsHost()) { return; } SelectableLevel level = GordionLevel.Level; if ((Object)(object)level == (Object)null) { return; } try { Weather currentWeather = WeatherManager.GetCurrentWeather(level); if (currentWeather == null) { return; } List<ProgressionStage> stages = WeatherTweaksCompat.GetStages(currentWeather); if (stages.Count == 0) { Plugin.DebugLog("'" + currentWeather.Name + "' has no progression stages — the weather stays put for this visit."); return; } _stages = stages; _active = true; List<string> list = new List<string>(stages.Count); foreach (ProgressionStage item in stages) { list.Add($"{item.Name}@{item.DayTime:0.00}"); } Plugin.Log.LogInfo((object)("Gordion progression for '" + currentWeather.Name + "': " + string.Join(" -> ", list) + ".")); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not set up Gordion weather progression: {arg}"); _active = false; } } public static void Stop() { _stages = null; _nextStage = 0; _active = false; } public static void Tick(float normalizedTimeOfDay) { if (!_active || _stages == null) { return; } while (_nextStage < _stages.Count && _stages[_nextStage].DayTime <= normalizedTimeOfDay) { ProgressionStage progressionStage = _stages[_nextStage]; _nextStage++; try { WeatherController.ChangeCurrentWeather(progressionStage.Weather); Plugin.Log.LogInfo((object)$"Gordion weather progressed to '{progressionStage.Name}' at {normalizedTimeOfDay:0.00} of the day."); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not apply progression stage '" + progressionStage.Name + "': " + ex.Message)); } } if (_nextStage >= _stages.Count) { _active = false; } } private static bool IsHost() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { return ((NetworkBehaviour)instance).IsHost; } return false; } } internal sealed class RealTimeDriver : MonoBehaviour { private static RealTimeDriver _instance; private bool _departureCalled; private bool _warningCalled; private bool _pushedEndOfDay; private bool _loggedTickFailure; private int _ticks; private float _started; private float _nextHeartbeat; private bool _reportedEarly; private bool _reportedSettled; public static void Begin() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("WeatherGordion_RealTimeDriver"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; _instance = val.AddComponent<RealTimeDriver>(); ClockOverlay.Reset(); SpectatorVote.Reset(); ClockDiagnostics.ReportPatches(); } } public static void Stop() { if (!((Object)(object)_instance == (Object)null)) { ClockOverlay.Hide(); Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; } } private void Awake() { _started = Time.unscaledTime; } private void LateUpdate() { try { Tick(); } catch (Exception arg) { if (!_loggedTickFailure) { _loggedTickFailure = true; Plugin.Log.LogWarning((object)$"The Gordion time driver threw and will keep retrying: {arg}"); } } } private void Tick() { TimeOfDay instance = TimeOfDay.Instance; StartOfRound instance2 = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null) && TimeController.ClockRunning && instance.currentDayTimeStarted && !(instance.totalTime <= 0f)) { Heartbeat(); KeepDayFromFreezing(instance); float num = (instance.normalizedTimeOfDay = Mathf.Clamp01(instance.currentDayTime / instance.totalTime)); ClockOverlay.Draw(num); HandleEndOfDay(instance, instance2, num); } } private void Heartbeat() { _ticks++; float num = Time.unscaledTime - _started; if (!_reportedEarly && num >= 1f) { _reportedEarly = true; ClockDiagnostics.Report("1 s after the day started"); } if (!_reportedSettled && num >= 8f) { _reportedSettled = true; ClockDiagnostics.Report("8 s after the day started"); } if (Plugin.Cfg.ClockDiagnostics.Value && !(Time.unscaledTime < _nextHeartbeat)) { _nextHeartbeat = Time.unscaledTime + ((num < 10f) ? 2f : 30f); TimeOfDay instance = TimeOfDay.Instance; Plugin.Log.LogInfo((object)($"Clock tick #{_ticks} at {num:0.#} s: globalTime {instance.globalTime:0.##}, " + $"currentDayTime {instance.currentDayTime:0.##}, normalized {instance.normalizedTimeOfDay:0.###}.")); } } private void KeepDayFromFreezing(TimeOfDay time) { if (!(time.globalTimeAtEndOfDay > time.globalTime + 0.01f) && !(time.currentDayTime >= time.totalTime)) { SelectableLevel level = GordionLevel.Level; float num = (((Object)(object)level != (Object)null) ? Mathf.Max(0.01f, level.DaySpeedMultiplier) : 1f); time.globalTimeAtEndOfDay = time.globalTime + (time.totalTime - time.currentDayTime) / num; if (!_pushedEndOfDay) { _pushedEndOfDay = true; Plugin.Log.LogWarning((object)("Gordion's end of day had fallen behind the clock and the day would have frozen; pushed it " + $"to {time.globalTimeAtEndOfDay:0.##}.")); } } } private void HandleEndOfDay(TimeOfDay time, StartOfRound round, float normalized) { if (round.shipIsLeaving) { return; } bool value = Plugin.Cfg.ShipLeavesAtEndOfDay.Value; if (!value && !SpectatorVote.Passed) { return; } if (value && !_warningCalled && !time.shipLeavingAlertCalled && normalized > 0.9f) { _warningCalled = true; try { HUDManager instance = HUDManager.Instance; if ((Object)(object)instance != (Object)null) { instance.ReadDialogue(time.shipLeavingSoonDialogue); ((Behaviour)instance.shipLeavingEarlyIcon).enabled = true; } time.shipLeavingAlertCalled = true; } catch (Exception ex) { Plugin.DebugLog("Ship-leaving warning failed: " + ex.Message); } } float num = time.shipLeaveAutomaticallyTime; if (SpectatorVote.Passed) { num = Mathf.Min(num, SpectatorVote.LeaveAt); } else if (!value) { return; } if (!_departureCalled && ((NetworkBehaviour)round).IsServer && !(normalized < num)) { _departureCalled = true; time.SetShipToLeaveOnMidnightClientRpc(); Plugin.Log.LogInfo((object)(SpectatorVote.Passed ? "The spectators' vote came due — the ship is leaving Gordion early." : "Gordion reached the end of the day — the ship is leaving on its own.")); } } } internal sealed class SimulatedClock : MonoBehaviour { private static SimulatedClock _instance; private float _elapsed; private float _dayLength; public static float Normalized { get { if (!((Object)(object)_instance != (Object)null)) { return 0f; } return _instance.Current; } } public static bool Running => (Object)(object)_instance != (Object)null; private float Current { get { if (!(_dayLength <= 0f)) { return Mathf.Clamp01(_elapsed / _dayLength); } return 0f; } } public static void Begin() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("WeatherGordion_SimulatedClock"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; _instance = val.AddComponent<SimulatedClock>(); _instance._dayLength = Mathf.Max(1f, Plugin.Cfg.DayLengthSeconds.Value); ClockOverlay.Reset(); ClockDiagnostics.ReportPatches(); ProgressionDriver.Begin(); Plugin.Log.LogInfo((object)$"Simulated Gordion day started ({_instance._dayLength:0} s). The game clock is untouched."); } } public static void Stop() { if (!((Object)(object)_instance == (Object)null)) { ProgressionDriver.Stop(); ClockOverlay.Hide(); Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; Plugin.DebugLog("Simulated Gordion day stopped."); } } private void Update() { try { Tick(); } catch (Exception ex) { Plugin.DebugLog("Simulated clock tick failed: " + ex.Message); } } private void Tick() { if (_elapsed < _dayLength) { _elapsed += Time.deltaTime; } float current = Current; TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance != (Object)null) { instance.normalizedTimeOfDay = current; instance.currentDayTime = current * instance.totalTime; ClockOverlay.Draw(current); } ProgressionDriver.Tick(current); } } internal static class SpectatorVote { public static bool Passed { get; private set; } public static float LeaveAt { get; private set; } public static void Reset() { Passed = false; LeaveAt = 1f; } public static bool LevelAllowsVote(SelectableLevel level) { if ((Object)(object)level == (Object)null) { return false; } if (level.planetHasTime) { return true; } if (Plugin.Cfg != null && Plugin.Cfg.Enabled.Value && Plugin.Cfg.SpectatorVote.Value && TimeController.ClockRunning) { return GordionLevel.Is(level); } return false; } public static void OnVotePassed(float leaveAt) { if (GordionLevel.IsCurrent()) { bool num = !Passed; Passed = true; LeaveAt = leaveAt; if (num) { Plugin.Log.LogInfo((object)$"Spectators voted for the ship to leave Gordion early; departure at {leaveAt:0.###} of the day."); } } } } internal sealed class StormyMetal : MonoBehaviour { private const float RescanInterval = 6f; private static StormyMetal _instance; private static FieldInfo _metalObjectsField; private static bool _fieldMissingLogged; private float _nextScan; private int _lastCount = -1; public static void Begin() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("WeatherGordion_StormyMetal"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; _instance = val.AddComponent<StormyMetal>(); } } public static void Stop() { if (!((Object)(object)_instance == (Object)null)) { Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; } } private void Update() { if (Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + 6f; try { Rescan(); } catch (Exception ex) { Plugin.DebugLog("Storm metal rescan failed: " + ex.Message); } } private void Rescan() { if (!GordionLevel.IsCurrent()) { return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || !((NetworkBehaviour)instance).IsServer) { return; } StormyWeather val = Object.FindObjectOfType<StormyWeather>(); if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { return; } if ((object)_metalObjectsField == null) { _metalObjectsField = AccessTools.Field(typeof(StormyWeather), "metalObjects"); } if (_metalObjectsField == null) { if (!_fieldMissingLogged) { _fieldMissingLogged = true; Plugin.Log.LogWarning((object)"StormyWeather.metalObjects was not found, so lightning targets cannot be refreshed on Gordion. Storms still work; they just keep whatever targets they found at first."); } } else { if (!(_metalObjectsField.GetValue(val) is List<GrabbableObject> list)) { return; } list.Clear(); GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); foreach (GrabbableObject val2 in array) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.itemProperties != (Object)null && val2.itemProperties.isConductiveMetal) { list.Add(val2); } } if (list.Count != _lastCount) { _lastCount = list.Count; Plugin.DebugLog($"Storm lightning targets on Gordion: {list.Count} conductive item(s)."); } } } } internal static class TimeController { private static bool _clockStarted; private static float _deadlineSnapshot; private static bool _hasDeadlineSnapshot; private static SelectableLevel _speedPatchedLevel; private static float _originalDaySpeed; private static SelectableLevel _offsetPatchedLevel; private static float _originalOffset; public static bool ClockRunning => _clockStarted; public static bool TryGetFrozenDeadline(out float value) { value = _deadlineSnapshot; if (_clockStarted && _hasDeadlineSnapshot) { return Plugin.Cfg.FreezeDeadline.Value; } return false; } public static void OnLandedOnGordion() { if (Plugin.Cfg != null && Plugin.Cfg.Enabled.Value) { switch (Plugin.Cfg.TimeMode.Value) { case GordionTimeMode.RealTime: StartVanillaClock(); StormyMetal.Begin(); break; case GordionTimeMode.Simulated: SimulatedClock.Begin(); StormyMetal.Begin(); break; case GordionTimeMode.Off: break; } } } public static void OnLeftGordion() { SimulatedClock.Stop(); RestoreImmediately(); } public static void RestoreImmediately() { RealTimeDriver.Stop(); StormyMetal.Stop(); RestoreDeadline(); RestoreDaySpeed(); if (_clockStarted) { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance != (Object)null) { instance.currentDayTimeStarted = false; instance.movingGlobalTimeForward = false; } _clockStarted = false; Plugin.DebugLog("Gordion clock stopped."); } _hasDeadlineSnapshot = false; } private static void StartVanillaClock() { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)"Landed on Gordion but TimeOfDay is missing — the clock stays stopped."); return; } if (_clockStarted || instance.currentDayTimeStarted) { SnapshotDeadline(instance); _clockStarted = true; RealTimeDriver.Begin(); return; } EnsureDaySpeed(); ZeroDayOffset(instance); SnapshotDeadline(instance); instance.currentDayTimeStarted = true; instance.movingGlobalTimeForward = true; _clockStarted = true; RealTimeDriver.Begin(); SelectableLevel level = GordionLevel.Level; Plugin.Log.LogInfo((object)($"Gordion clock started (globalTime {instance.globalTime:0.##}, totalTime {instance.totalTime:0.##}, " + $"daySpeed {level?.DaySpeedMultiplier:0.###}, offset {level?.OffsetFromGlobalTime:0.##}, " + $"deadline held: {Plugin.Cfg.FreezeDeadline.Value}).")); } private static void ZeroDayOffset(TimeOfDay time) { SelectableLevel level = GordionLevel.Level; if (!((Object)(object)level == (Object)null)) { _offsetPatchedLevel = level; _originalOffset = level.OffsetFromGlobalTime; level.OffsetFromGlobalTime = 0f; Plugin.DebugLog($"Gordion OffsetFromGlobalTime {_originalOffset:0.##} -> 0 so the day can run " + $"(globalTime {time.globalTime:0.##} is server-synced, so every player gets the same time)."); } } private static void EnsureDaySpeed() { SelectableLevel level = GordionLevel.Level; if (!((Object)(object)level == (Object)null)) { Plugin.DebugLog($"Gordion DaySpeedMultiplier = {level.DaySpeedMultiplier}, " + $"OffsetFromGlobalTime = {level.OffsetFromGlobalTime}."); if (!(level.DaySpeedMultiplier > 0f)) { _speedPatchedLevel = level; _originalDaySpeed = level.DaySpeedMultiplier; level.DaySpeedMultiplier = 1f; Plugin.Log.LogInfo((object)($"Gordion had DaySpeedMultiplier = {_originalDaySpeed}, which would freeze the day and " + "divide by zero at the start of it; using 1 for this visit.")); } } } private static void RestoreDaySpeed() { if ((Object)(object)_speedPatchedLevel != (Object)null) { _speedPatchedLevel.DaySpeedMultiplier = _originalDaySpeed; _speedPatchedLevel = null; } if ((Object)(object)_offsetPatchedLevel != (Object)null) { _offsetPatchedLevel.OffsetFromGlobalTime = _originalOffset; _offsetPatchedLevel = null; } } private static void SnapshotDeadline(TimeOfDay time) { if (!_hasDeadlineSnapshot) { _deadlineSnapshot = time.timeUntilDeadline; _hasDeadlineSnapshot = true; Plugin.DebugLog($"Deadline snapshot: timeUntilDeadline = {_deadlineSnapshot:0.##}."); } } private static void RestoreDeadline() { if (!_hasDeadlineSnapshot || !_clockStarted) { return; } TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null || !Plugin.Cfg.FreezeDeadline.Value) { return; } try { instance.timeUntilDeadline = _deadlineSnapshot; instance.UpdateProfitQuotaCurrentTime(); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not restore the deadline after a Gordion visit: " + ex.Message)); } } } internal static class WeatherUnlocker { [CompilerGenerated] private static class <>O { public static Event <0>__OnSetupFinished; public static ParameterEvent<int> <1>__OnDayChanged; public static Func<SelectableLevel, string> <2>__ConfigKey; } private static readonly HashSet<LevelWeatherType> Added = new HashSet<LevelWeatherType>(); private static readonly HashSet<string> PreExistingWeights = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> _loggedComponents = new HashSet<string>(StringComparer.Ordinal); private static bool _snapshotTaken; private static bool _subscribed; private static bool _loggedSummary; public static void Subscribe() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (_subscribed) { return; } try { WeatherRegistryEvent setupFinished = EventManager.SetupFinished; object obj = <>O.<0>__OnSetupFinished; if (obj == null) { Event val = OnSetupFinished; <>O.<0>__OnSetupFinished = val; obj = (object)val; } ((CustomEvent)setupFinished).AddListener((Event)obj); ((CustomEvent<int>)(object)EventManager.DayChanged).AddListener((ParameterEvent<int>)OnDayChanged); _subscribed = true; } catch (Exception ex) { Plugin.Log.LogError((object)("Could not subscribe to WeatherRegistry events: " + ex.Message + ". Gordion weather will still be applied from the SetPlanetsWeather patch.")); } } public static void Unsubscribe() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown if (!_subscribed) { return; } try { WeatherRegistryEvent setupFinished = EventManager.SetupFinished; object obj = <>O.<0>__OnSetupFinished; if (obj == null) { Event val = OnSetupFinished; <>O.<0>__OnSetupFinished = val; obj = (object)val; } ((CustomEvent)setupFinished).RemoveListener((Event)obj); ((CustomEvent<int>)(object)EventManager.DayChanged).RemoveListener((ParameterEvent<int>)OnDayChanged); } catch { } _subscribed = false; } private static void OnSetupFinished() { Apply("WeatherRegistry setup finished"); } private static void OnDayChanged(int day) { Apply($"day {day} started"); } public static void Reset() { Added.Clear(); WeatherVariables.Reset(); _loggedComponents.Clear(); _loggedSummary = false; } public static void Apply(string reason) { if (Plugin.Cfg == null || !Plugin.Cfg.Enabled.Value) { return; } SelectableLevel level = GordionLevel.Level; if ((Object)(object)level == (Object)null) { return; } try { ApplyCore(level, reason); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to unlock weather on Gordion ({reason}): {arg}"); } } private static void ApplyCore(SelectableLevel gordion, string reason) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) List<Weather> weathers = WeatherManager.Weathers; if (weathers == null || weathers.Count == 0) { Plugin.DebugLog("No weathers registered yet (" + reason + "); nothing to unlock."); return; } TakeSnapshot(gordion, weathers); List<string> list = new List<string>(); List<string> list2 = new List<string>(); List<string> list3 = new List<string>(); foreach (Weather item in weathers) { if (item == null) { continue; } if ((int)item.Type == 0) { SetLevelWeight(item, gordion, Plugin.Cfg.ClearWeatherWeight.Value); continue; } int effectiveWeight = Plugin.Cfg.SettingsFor(item.Name).EffectiveWeight; if (IsBanned(item)) { RemoveFromPool(gordion, item); list3.Add(item.Name); } else if (effectiveWeight == 0) { RemoveFromPool(gordion, item); list2.Add(item.Name); } else if (Plugin.Cfg.RespectExistingConfig.Value && HasHandTunedWeight(item)) { Plugin.DebugLog("'" + item.Name + "' already has a Gordion weight in mrov.WeatherRegistry.cfg — left alone."); AddToPool(gordion, item); list.Add(item.Name + " (kept existing weight)"); } else { AllowOnLevel(item, gordion); SetLevelWeight(item, gordion, effectiveWeight); AddToPool(gordion, item); list.Add($"{item.Name}@{effectiveWeight}"); } } if (!_loggedSummary || Plugin.Cfg.DebugMode.Value) { _loggedSummary = true; Plugin.Log.LogInfo((object)("Gordion weather pool (" + reason + "): " + ((list.Count > 0) ? string.Join(", ", list) : "nothing") + $"; clear weight {Plugin.Cfg.ClearWeatherWeight.Value}.")); if (list3.Count > 0) { Plugin.Log.LogInfo((object)("Refused by 'Never allow, even in combinations' (" + Plugin.Cfg.BannedComponents.Value + "): " + string.Join(", ", list3) + ".")); } if (list2.Count > 0) { Plugin.DebugLog("Switched off for Gordion: " + string.Join(", ", list2) + "."); } } } private static bool IsBanned(Weather weather) { HashSet<string> hashSet = Plugin.Cfg.BannedComponentNames(); if (hashSet.Count == 0) { return false; } List<string> componentNames = WeatherTweaksCompat.GetComponentNames(weather); if (Plugin.Cfg.DebugMode.Value && !_loggedComponents.Contains(weather.Name ?? string.Empty)) { _loggedComponents.Add(weather.Name ?? string.Empty); Plugin.DebugLog("'" + weather.Name + "' is made of: " + string.Join(", ", componentNames.Distinct()) + "."); } foreach (string item in componentNames) { if (hashSet.Contains(PluginConfig.NormalizeName(item))) { return true; } if (MatchesAnyPart(item, hashSet)) { return true; } } return MatchesAnyPart(weather.Name, hashSet); } private static bool MatchesAnyPart(string name, HashSet<string> banned) { if (string.IsNullOrEmpty(name)) { return false; } string[] array = name.Split('+', '>', ','); foreach (string name2 in array) { if (banned.Contains(PluginConfig.NormalizeName(name2))) { return true; } } return false; } private static void AllowOnLevel(Weather weather, SelectableLevel level) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 LevelListConfigHandler val = weather.Config?.LevelFilters; if (val == null) { return; } bool flag = (int)weather.LevelFilteringOption == 0; SelectableLevel[] array = ((ConfigHandler<SelectableLevel[], string>)(object)val).Value ?? Array.Empty<SelectableLevel>(); if (flag && array.Length == 0) { return; } bool flag2 = array.Contains(level); if (flag != flag2) { string text = SerialiseLevels(flag ? array.Concat((IEnumerable<SelectableLevel>)(object)new SelectableLevel[1] { level }) : array.Where((SelectableLevel l) => (Object)(object)l != (Object)(object)level)); if (WriteHandler<SelectableLevel[]>((ConfigHandler<SelectableLevel[], string>)(object)val, text)) { Plugin.DebugLog("'" + weather.Name + "': " + (flag ? "added Gordion to" : "removed Gordion from") + " its level filter -> '" + text + "'."); } } } private static void TakeSnapshot(SelectableLevel gordion, List<Weather> weathers) { if (_snapshotTaken) { return; } _snapshotTaken = true; foreach (Weather weather in weathers) { if (weather != null && CarriesWeightFor(weather, gordion)) { PreExistingWeights.Add(weather.Name ?? string.Empty); } } if (PreExistingWeights.Count > 0) { Plugin.DebugLog("Already had a Gordion weight in mrov.WeatherRegistry.cfg: " + string.Join(", ", PreExistingWeights) + "."); } } private static bool HasHandTunedWeight(Weather weather) { return PreExistingWeights.Contains(weather.Name ?? string.Empty); } private static bool CarriesWeightFor(Weather weather, SelectableLevel level) { LevelRarity[] array = ((ConfigHandler<LevelRarity[], string>)(object)weather.Config?.LevelWeights)?.Value; if (array == null) { return false; } for (int i = 0; i < array.Length; i++) { if (array[i] != null && (Object)(object)array[i].Level == (Object)(object)level) { return true; } } return false; } private static void SetLevelWeight(Weather weather, SelectableLevel level, int weight) { LevelWeightsConfigHandler val = weather.Config?.LevelWeights; if (val == null || (Plugin.Cfg.RespectExistingConfig.Value && HasHandTunedWeight(weather))) { return; } LevelRarity[] obj = ((ConfigHandler<LevelRarity[], string>)(object)val).Value ?? Array.Empty<LevelRarity>(); string arg = GordionLevel.ConfigKey(level); List<string> list = new List<string>(); bool flag = false; LevelRarity[] array = obj; foreach (LevelRarity val2 in array) { if ((Object)(object)((val2 != null) ? val2.Level : null) == (Object)null) { continue; } if ((Object)(object)val2.Level == (Object)(object)level) { if (!flag) { list.Add($"{arg}@{weight}"); flag = true; } } else { list.Add($"{GordionLevel.ConfigKey(val2.Level)}@{((Rarity)val2).Weight}"); } } if (!flag) { list.Add($"{arg}@{weight}"); } string text = string.Join(";", list) + ";"; if (WriteHandler<LevelRarity[]>((ConfigHandler<LevelRarity[], string>)(object)val, text)) { Plugin.DebugLog("'" + weather.Name + "': level weights -> '" + text + "'."); } } private static void AddToPool(SelectableLevel level, Weather weather) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) RandomWeatherWithVariables val = FindInPool(level, weather.VanillaWeatherType); if (val != null) { if (Added.Contains(weather.VanillaWeatherType) && WeatherVariables.IsUnset(val)) { WeatherVariables.Apply(val, weather, level); } return; } RandomWeatherWithVariables val2 = new RandomWeatherWithVariables { weatherType = weather.VanillaWeatherType }; WeatherVariables.Apply(val2, weather, level); WeatherController.AddRandomWeather(level, val2); Added.Add(val2.weatherType); Plugin.DebugLog("'" + weather.Name + "' added to Gordion's weather pool " + $"(variables {val2.weatherVariable}/{val2.weatherVariable2})."); } private static void RemoveFromPool(SelectableLevel level, Weather weather) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) LevelWeatherType vanillaWeatherType = weather.VanillaWeatherType; if (Added.Contains(vanillaWeatherType) && IsInPool(level, vanillaWeatherType)) { WeatherController.RemoveRandomWeather(level, vanillaWeatherType); Added.Remove(vanillaWeatherType); Plugin.DebugLog("'" + weather.Name + "' removed from Gordion's weather pool (configured weight 0)."); } } private static bool IsInPool(SelectableLevel level, LevelWeatherType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return FindInPool(level, type) != null; } private static RandomWeatherWithVariables FindInPool(SelectableLevel level, LevelWeatherType type) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) RandomWeatherWithVariables[] randomWeathers = level.randomWeathers; if (randomWeathers == null) { return null; } for (int i = 0; i < randomWeathers.Length; i++) { if (randomWeathers[i] != null && randomWeathers[i].weatherType == type) { return randomWeathers[i]; } } return null; } private static bool WriteHandler<TValue>(ConfigHandler<TValue, string> handler, string raw) { try { if (!handler.ConfigEntryActive) { if (((ConfigHandler<TValue, string>)(object)handler).DefaultValue == raw) { return false; } ((ConfigHandler<TValue, string>)(object)handler).DefaultValue = raw; return true; } ConfigEntry<string> configEntry = ((ConfigHandler<TValue, string>)(object)handler).ConfigEntry; if (configEntry.Value == raw) { return false; } ConfigFile configFile = ((ConfigEntryBase)configEntry).ConfigFile; bool saveOnConfigSet = configFile != null && configFile.SaveOnConfigSet; if (configFile != null) { configFile.SaveOnConfigSet = false; } try { configEntry.Value = raw; } finally { if (configFile != null) { configFile.SaveOnConfigSet = saveOnConfigSet; } } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Could not write a WeatherRegistry config handler: " + ex.Message)); return false; } } private static string SerialiseLevels(IEnumerable<SelectableLevel> levels) { List<string> list = (from n in levels.Where((SelectableLevel l) => (Object)(object)l != (Object)null).Select(GordionLevel.ConfigKey) where n.Length > 0 select n).Distinct().ToList(); if (list.Count != 0) { return string.Join(";", list) + ";"; } return string.Empty; } } internal static class WeatherVariables { private struct Variables { public int Variable1; public int Variable2; public Color Color; public string Source; } private static readonly Dictionary<LevelWeatherType, Variables> Cache = new Dictionary<LevelWeatherType, Variables>(); public static void Reset() { Cache.Clear(); } public static void Apply(RandomWeatherWithVariables entry, Weather weather, SelectableLevel exclude) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (entry != null) { if (TryFindDonor(entry.weatherType, exclude, out var donor)) { entry.weatherVariable = donor.Variable1; entry.weatherVariable2 = donor.Variable2; entry.weatherVariableColor = donor.Color; Plugin.DebugLog($"'{((weather != null) ? weather.Name : null)}' variables {donor.Variable1}/{donor.Variable2} borrowed from {donor.Source}."); } else { entry.weatherVariable = (((Object)(object)weather?.Effect != (Object)null) ? weather.Effect.DefaultVariable1 : 0); entry.weatherVariable2 = (((Object)(object)weather?.Effect != (Object)null) ? weather.Effect.DefaultVariable2 : 0); Plugin.DebugLog("'" + ((weather != null) ? weather.Name : null) + "': no moon defines this weather, using the effect's own defaults " + $"{entry.weatherVariable}/{entry.weatherVariable2}."); } } } public static bool IsUnset(RandomWeatherWithVariables entry) { if (entry != null && entry.weatherVariable == 0) { return entry.weatherVariable2 == 0; } return false; } private static bool TryFindDonor(LevelWeatherType type, SelectableLevel exclude, out Variables donor) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (Cache.TryGetValue(type, out donor)) { return donor.Source != null; } donor = default(Variables); SelectableLevel[] array = StartOfRound.Instance?.levels; if (array == null) { return false; } if (TryScan(array, type, exclude, vanillaOnly: true, out donor) || TryScan(array, type, exclude, vanillaOnly: false, out donor)) { Cache[type] = donor; return true; } Cache[type] = default(Variables); return false; } private static bool TryScan(SelectableLevel[] levels, LevelWeatherType type, SelectableLevel exclude, bool vanillaOnly, out Variables donor) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) donor = default(Variables); foreach (SelectableLevel val in levels) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)exclude || val.randomWeathers == null || (vanillaOnly && !IsVanilla(val))) { continue; } RandomWeatherWithVariables[] randomWeathers = val.randomWeathers; foreach (RandomWeatherWithVariables val2 in randomWeathers) { if (val2 != null && val2.weatherType == type && (val2.weatherVariable != 0 || val2.weatherVariable2 != 0)) { donor = new Variables { Variable1 = val2.weatherVariable, Variable2 = val2.weatherVariable2, Color = val2.weatherVariableColor, Source = (val.PlanetName ?? ((Object)val).name) }; return true; } } } return false; } private static bool IsVanilla(SelectableLevel level) { try { return LevelHelper.IsVanillaLevel(level); } catch { return false; } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Timofey.WeatherGordion"; public const string PLUGIN_NAME = "WeatherGordion"; public const string PLUGIN_VERSION = "1.4.0"; } } namespace WeatherGordion.Patches { [HarmonyPatch] internal static class RoundManagerPatches { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(RoundManager), "SetToCurrentLevelWeather")] private static void SetToCurrentLevelWeather_Prefix() { try { WeatherUnlocker.Apply("the level is applying its weather"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not prepare Gordion's weather variables: {arg}"); } } } [HarmonyPatch] internal static class SetPlanetsWeatherPatch { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch(typeof(StartOfRound), "SetPlanetsWeather")] private static void SetPlanetsWeather_Prefix(StartOfRound __instance) { try { GordionLevel.Resolve(__instance); WeatherUnlocker.Apply("weather is being selected"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not prepare Gordion before weather selection: {arg}"); } } } [HarmonyPatch] internal static class SpectatorVotePatches { [HarmonyTranspiler] [HarmonyPatch(typeof(HUDManager), "Update")] private static IEnumerable<CodeInstruction> HUDManagerUpdate_Transpiler(IEnumerable<CodeInstruction> instructions) { FieldInfo fieldInfo = AccessTools.Field(typeof(SelectableLevel), "planetHasTime"); MethodInfo methodInfo = AccessTools.Method(typeof(SpectatorVote), "LevelAllowsVote", (Type[])null, (Type[])null); int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if (fieldInfo != null && methodInfo != null && CodeInstructionExtensions.LoadsField(instruction, fieldInfo, false)) { instruction.opcode = OpCodes.Call; instruction.operand = methodInfo; num++; } list.Add(instruction); } switch (num) { case 1: Plugin.DebugLog("Spectator vote enabled for Gordion (HUDManager.Update patched)."); break; case 0: Plugin.Log.LogWarning((object)"Could not find the spectator vote check in HUDManager.Update; dead players will not get the leave-early prompt on Gordion. The game or another mod has likely changed that method."); break; default: Plugin.Log.LogInfo((object)($"HUDManager.Update read planetHasTime {num} times (vanilla has 1); all were routed " + "through the Gordion check, which only answers differently on Gordion with its day running.")); break; } return list; } [HarmonyPostfix] [HarmonyPatch(typeof(TimeOfDay), "SetShipLeaveEarlyClientRpc")] private static void SetShipLeaveEarlyClientRpc_Postfix(float timeToLeaveEarly) { try { SpectatorVote.OnVotePassed(timeToLeaveEarly); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not record the leave-early vote: {arg}"); } } } [HarmonyPatch] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Awake")] private static void Awake_Postfix(StartOfRound __instance) { try { GordionLevel.Reset(); WeatherUnlocker.Reset(); GordionLevel.Resolve(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"StartOfRound.Awake handler failed: {arg}"); } } [HarmonyPrefix] [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] private static void ShipLeave_Prefix() { try { TimeController.OnLeftGordion(); } catch (Exception arg) { Plugin.Log.LogError((object)$"ShipLeave handler failed: {arg}"); } } [HarmonyPrefix] [HarmonyPatch(typeof(StartOfRound), "PassTimeToNextDay")] private static void PassTimeToNextDay_Prefix() { try { TimeController.RestoreImmediately(); } catch (Exception arg) { Plugin.Log.LogError((object)$"PassTimeToNextDay handler failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "OnDestroy")] private static void OnDestroy_Postfix() { try { TimeController.RestoreImmediately(); SimulatedClock.Stop(); GordionLevel.Reset(); WeatherUnlocker.Reset(); } catch { } } } [HarmonyPatch] internal static class TimeOfDayPatches { [HarmonyPostfix] [HarmonyPatch(typeof(TimeOfDay), "MoveGlobalTime")] private static void MoveGlobalTime_Postfix(TimeOfDay __instance) { try { if (TimeController.TryGetFrozenDeadline(out var value)) { __instance.timeUntilDeadline = value; } } catch (Exception ex) { Plugin.DebugLog("Deadline hold failed: " + ex.Message); } } } } namespace WeatherGordion.Compat { internal sealed class ProgressionStage { public float DayTime; public Weather Weather; public string Name; } internal static class WeatherTweaksCompat { private static bool _probed; private static bool _available; private static MethodInfo _getFullWeatherType; private static Type _progressingType; private static FieldInfo _weatherEntries; private static FieldInfo _entryDayTime; private static MethodInfo _entryGetWeather; private static PropertyInfo _weatherTypes; public static bool Available { get { Probe(); return _available; } } public static List<ProgressionStage> GetStages(Weather weather) { List<ProgressionStage> list = new List<ProgressionStage>(); if (weather == null || !Available) { return list; } try { object obj = _getFullWeatherType.Invoke(null, new object[1] { weather }); if (obj == null || !_progressingType.IsInstanceOfType(obj)) { return list; } if (!(_weatherEntries.GetValue(obj) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { if (item != null) { float dayTime = (float)_entryDayTime.GetValue(item); object? obj2 = _entryGetWeather.Invoke(item, null); Weather val = (Weather)((obj2 is Weather) ? obj2 : null); if (val != null) { list.Add(new ProgressionStage { DayTime = dayTime, Weather = val, Name = val.Name }); } } } list.Sort((ProgressionStage a, ProgressionStage b) => a.DayTime.CompareTo(b.DayTime)); } catch (Exception ex) { Plugin.DebugLog("Could not read progression stages for '" + weather.Name + "': " + ex.Message); list.Clear(); } return list; } public static List<string> GetComponentNames(Weather weather) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); if (weather == null) { return list; } list.Add(((object)weather.VanillaWeatherType/*cast due to .constrained prefix*/).ToString()); list.Add(weather.Name); foreach (WeatherResolvable item in ReadResolvables(weather)) { try { list.Add(((object)item.WeatherType/*cast due to .constrained prefix*/).ToString()); } catch { } try { if (!string.IsNullOrEmpty(item.WeatherName)) { list.Add(item.WeatherName); } } catch { } } return list; } private static IEnumerable<WeatherResolvable> ReadResolvables(Weather weather) { if (!Available) { yield break; } object obj = null; PropertyInfo property = ((object)weather).GetType().GetProperty("WeatherTypes", BindingFlags.Instance | BindingFlags.Public); if (property != null) { obj = SafeGet(property, weather); } else if (_getFullWeatherType != null && _weatherTypes != null) { object obj2 = SafeInvoke(_getFullWeatherType, new object[1] { weather }); if (obj2 != null) { obj = SafeGet(_weatherTypes, obj2); } } if (!(obj is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { WeatherResolvable val = (WeatherResolvable)((item is WeatherResolvable) ? item : null); if (val != null) { yield return val; } } } private static object SafeGet(PropertyInfo property, object target) { try { return property.GetValue(target); } catch (Exception ex) { Plugin.DebugLog("Reading " + property.Name + " failed: " + ex.Message); return null; } } private static object SafeInvoke(MethodInfo method, object[] args) { try { return method.Invoke(null, args); } catch (Exception ex) { Plugin.DebugLog("Calling " + method.Name + " failed: " + ex.Message); return null; } } private static void Probe() { if (_probed) { return; } _probed = true; try { Type type = FindType("WeatherTweaks.Variables"); _progressingType = FindType("WeatherTweaks.Definitions.ProgressingWeatherType"); Type type2 = FindType("WeatherTweaks.Definitions.ProgressingWeatherEntry"); _weatherTypes = FindType("WeatherTweaks.Definitions.WeatherTweaksWeather")?.GetProperty("WeatherTypes", BindingFlags.Instance | BindingFlags.Public); if (type == null || _progressingType == null || type2 == null) { Plugin.DebugLog("WeatherTweaks not found — progressing weather stages are unavailable."); return; } _getFullWeatherType = type.GetMethod("GetFullWeatherType", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Weather) }, null); _weatherEntries = _progressingType.GetField("WeatherEntries", BindingFlags.Instance | BindingFlags.Public); _entryDayTime = type2.GetField("DayTime", BindingFlags.Instance | BindingFlags.Public); _entryGetWeather = type2.GetMethod("GetWeather", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); _available = _getFullWeatherType != null && _weatherEntries != null && _entryDayTime != null && _entryGetWeather != null; if (_available) { Plugin.DebugLog("WeatherTweaks progressing-weather support is available."); } else { Plugin.Log.LogWarning((object)"WeatherTweaks is installed but its progressing-weather API did not match what this mod expects; stages will not be stepped through in Simulated mode."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("WeatherTweaks compatibility probe failed: " + ex.Message)); _available = false; } } private static Type FindType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(fullName, throwOnError: false); if (type != null) { return type; } } catch { } } return null; } } }