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 Tempest v0.1.15
plugins/Tempest/Tempest.Core.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using ExtraPotions.Tempest.Config; using ExtraPotions.Tempest.Events; using ExtraPotions.Tempest.Localization; using ExtraPotions.Tempest.Weather; using Microsoft.CodeAnalysis; [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("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.1.15.0")] [assembly: AssemblyInformationalVersion("0.1.15+570907621cf8cf31bf960deb7d50bfbe8f5ca425")] [assembly: AssemblyProduct("Tempest.Core")] [assembly: AssemblyTitle("Tempest.Core")] [assembly: AssemblyVersion("0.1.15.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 ExtraPotions.Tempest { public static class TempestCore { public const string PluginGuid = "com.meadworks.tempest"; public const string ProductName = "Tempest"; public const string ProductVersion = "0.1.15"; public const string Family = "Runestone"; public const string TokenNamespace = "tempest"; public const string TokenPrefix = "meadworks_tempest_"; public const string WorldKey = "mw.tempest.event"; public const string DryWorldKey = "mw.tempest.lightningstorm"; public const string ExtendedWorldKey = "mw.tempest.extended"; public const string LegacyWorldKey = "tempest.event"; public const string SupportedGameVersion = "1.0.17"; } } namespace ExtraPotions.Tempest.Weather { public enum FreezeLevel { Freezing, Cold, None } public static class EnvironmentPlan { public enum CloneStyle { Copy, Dry, DenseFog, VisualOnly, Whiteout } public const string BlizzardEnvironment = "Tempest_Blizzard"; public const string StormEnvironment = "Tempest_Storm"; public const string DryStormEnvironment = "Tempest_LightningStorm"; public const string FogEnvironment = "Tempest_Fog"; public const string AshStormEnvironment = "Tempest_AshStorm"; public const string MeteorShowerEnvironment = "Tempest_MeteorShower"; public const string WhiteoutEnvironment = "Tempest_Whiteout"; public const string Haze = "Misty"; public const string LightSnow = "Snow"; public const string LightRain = "LightRain"; public const string AshHaze = "Ashlands_misty"; public const string AshRain = "Ashlands_ashrain"; public const string NorthSnow = "Twilight_Snow"; public const int BuildUpSeconds = 60; public const float DenseFogDensity = 0.16f; public const float WhiteoutFogDensity = 0.1f; public const float WhiteoutWind = 0.9f; public static IReadOnlyList<string> Names { get; } = Array.AsReadOnly(new string[13] { "Tempest_Blizzard", "Tempest_Storm", "Tempest_LightningStorm", "Tempest_Fog", "Tempest_AshStorm", "Tempest_MeteorShower", "Tempest_Whiteout", "Misty", "Snow", "LightRain", "Ashlands_misty", "Ashlands_ashrain", "Twilight_Snow" }); public static IReadOnlyList<string> EventEnvironments { get; } = Array.AsReadOnly(new string[7] { "Tempest_Blizzard", "Tempest_Storm", "Tempest_LightningStorm", "Tempest_Fog", "Tempest_AshStorm", "Tempest_MeteorShower", "Tempest_Whiteout" }); public static IReadOnlyList<string> FreezingEnvironments { get; } = Array.AsReadOnly(new string[2] { "Tempest_Blizzard", "Tempest_Whiteout" }); public static IReadOnlyList<(string Name, string Source, bool Dry)> Clones { get; } = Array.AsReadOnly(new(string, string, bool)[7] { ("Tempest_Blizzard", "SnowStorm", false), ("Tempest_Storm", "ThunderStorm", false), ("Tempest_LightningStorm", "ThunderStorm", true), ("Tempest_Fog", "Misty", false), ("Tempest_AshStorm", "Ashlands_storm", false), ("Tempest_MeteorShower", "Ashlands_meteorshower", false), ("Tempest_Whiteout", "Twilight_SnowStorm", false) }); public static bool Whiteout(EventKind kind, WeatherBiome biome) { if (kind == EventKind.Blizzard) { return biome == WeatherBiome.DeepNorth; } return false; } public static string EventEnvironment(EventKind kind) { return kind switch { EventKind.Blizzard => "Tempest_Blizzard", EventKind.LightningStorm => "Tempest_LightningStorm", EventKind.DenseFog => "Tempest_Fog", EventKind.AshStorm => "Tempest_AshStorm", EventKind.MeteorShower => "Tempest_MeteorShower", _ => "Tempest_Storm", }; } public static string EventEnvironment(TempestEvent e) { return EventEnvironment(e.Kind); } public static string EventEnvironment(EventKind kind, WeatherBiome biome) { if (!Whiteout(kind, biome)) { return EventEnvironment(kind); } return "Tempest_Whiteout"; } public static IReadOnlyList<CloneStyle> Styles(string name) { switch (name) { case "Tempest_LightningStorm": return new CloneStyle[1] { CloneStyle.Dry }; case "Tempest_Fog": return new CloneStyle[2] { CloneStyle.DenseFog, CloneStyle.VisualOnly }; case "Tempest_AshStorm": case "Tempest_MeteorShower": return new CloneStyle[1] { CloneStyle.VisualOnly }; case "Tempest_Whiteout": return new CloneStyle[2] { CloneStyle.Whiteout, CloneStyle.VisualOnly }; default: return new CloneStyle[1]; } } public static IReadOnlyList<string> SourceNames(string clone) { foreach (var clone2 in Clones) { if (clone2.Name == clone) { return new string[1] { clone2.Source }; } } return Array.Empty<string>(); } public static bool IsEventEnvironment(string? name) { if (name == null) { return false; } foreach (string eventEnvironment in EventEnvironments) { if (string.Equals(eventEnvironment, name, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static bool IsRainEffect(string? name) { if (name != null) { return name.IndexOf("rain", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } public static string Source(EventKind kind) { string text = EventEnvironment(kind); foreach (var clone in Clones) { if (clone.Name == text) { return clone.Source; } } throw new ArgumentException("Unknown kind.", "kind"); } public static string? Light(EventKind kind) { return kind switch { EventKind.Blizzard => "Snow", EventKind.Thunderstorm => "LightRain", EventKind.AshStorm => "Ashlands_ashrain", EventKind.MeteorShower => null, _ => "Misty", }; } public static string? Light(EventKind kind, WeatherBiome biome) { if (!Whiteout(kind, biome)) { return Light(kind); } return "Twilight_Snow"; } public static string? Warning(EventKind kind) { return kind switch { EventKind.AshStorm => "Ashlands_misty", EventKind.MeteorShower => null, _ => "Misty", }; } public static string? For(TempestEvent? e, double now, WeatherBiome biome, bool gameForced) { if (e == null || gameForced || !e.Affects(biome)) { return null; } PhaseState phaseState = Timeline.At(e, now); switch (phaseState.Phase) { case EventPhase.Warning: if (!(phaseState.SecondsLeft > 60.0)) { return Light(e.Kind, biome); } return Warning(e.Kind); case EventPhase.Active: return EventEnvironment(e.Kind, biome); case EventPhase.Clearing: return Light(e.Kind, biome); default: return null; } } public static (bool Freezing, bool Cold) Flags(FreezeLevel level) { return level switch { FreezeLevel.Freezing => (Freezing: true, Cold: true), FreezeLevel.Cold => (Freezing: false, Cold: true), _ => (Freezing: false, Cold: false), }; } public static bool ClearsPersonalOverride(TempestEvent? e, double now, WeatherBiome biome) { if (e != null && e.Affects(biome)) { return Timeline.At(e, now).Phase != EventPhase.Idle; } return false; } } public static class SeasonalEnvironments { private static readonly Dictionary<string, string> Bases = Build(); private static readonly string[] Suffixes = new string[4] { "", " Summer", " Fall", " Winter" }; private static readonly Dictionary<string, string?[]> Variants = BuildVariants(); private static Dictionary<string, string?[]> BuildVariants() { Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, string> basis in Bases) { if (!dictionary.TryGetValue(basis.Value, out var value)) { dictionary.Add(basis.Value, value = new string[4]); } for (int i = 1; i <= 3; i++) { if (basis.Key.EndsWith(Suffixes[i], StringComparison.Ordinal)) { value[i] = basis.Key; } } } return dictionary; } private static Dictionary<string, string> Build() { Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Add(" Summer", new string[7] { "Clear", "Misty", "DeepForest Mist", "Mistlands_clear", "Heath clear", "SwampRain", "Swamp" }); Add(" Fall", new string[2] { "ThunderStorm", "SwampRain" }); Add(" Winter", new string[14] { "Rain", "LightRain", "ThunderStorm", "Clear", "Misty", "DeepForest Mist", "SwampRain", "Mistlands_clear", "Mistlands_rain", "Mistlands_thunder", "Heath clear", "Snow", "SnowStorm", "Darklands_dark" }); return result; void Add(string suffix, params string[] names) { foreach (string text in names) { result.Add(text + suffix, text); } } } public static string BaseName(string name) { if (name == null || !Bases.TryGetValue(name, out string value)) { return name; } return value; } public static string? ForSeason(string? name, int season, Func<string, bool> exists) { if (string.IsNullOrEmpty(name) || season < 1 || season > 3 || EnvironmentPlan.IsEventEnvironment(name)) { return name; } if (Variants.TryGetValue(BaseName(name), out string[] value)) { string text = value[season]; if (text != null && exists(text)) { return text; } } return name; } } public readonly struct SeasonReading { public int Season { get; } public bool Effects { get; } public bool Available { get; } private SeasonReading(int season, bool effects) { Season = season; Effects = effects; Available = true; } public static bool TryRead(string? facts, out SeasonReading reading) { reading = default(SeasonReading); if (string.IsNullOrEmpty(facts) || facts.Length > 16384) { return false; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); string[] array = facts.Split(';'); foreach (string text in array) { int num = text.IndexOf('='); if (num <= 0) { continue; } string text2 = text.Substring(0, num); if (!(text2 != "season") || !(text2 != "effects") || !(text2 != "day") || !(text2 != "length") || !(text2 != "doy") || !(text2 != "year")) { if (dictionary.ContainsKey(text2) || !int.TryParse(text.Substring(num + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } dictionary.Add(text2, result); } } if (!dictionary.TryGetValue("season", out var value) || value < 0 || value > 3 || !dictionary.TryGetValue("effects", out var value2) || value2 < 0 || value2 > 1 || !dictionary.TryGetValue("length", out var value3) || value3 <= 0 || !dictionary.TryGetValue("day", out var value4) || value4 < 0 || value4 > value3 || !dictionary.TryGetValue("year", out var value5) || value5 < value3 || !dictionary.TryGetValue("doy", out var value6) || value6 < 0 || value6 > value5) { return false; } reading = new SeasonReading(value, value2 == 1); return true; } } public sealed class SeasonFactsReader { private readonly Func<Func<string>?> resolve; private double nextRead = double.NegativeInfinity; public SeasonReading Reading { get; private set; } public SeasonFactsReader(Func<Func<string>?> resolver) { resolve = resolver ?? throw new ArgumentNullException("resolver"); } public bool Poll(double now) { if (double.IsNaN(now) || double.IsInfinity(now) || now < nextRead) { return false; } nextRead = now + 1.0; SeasonReading reading = default(SeasonReading); try { SeasonReading.TryRead(resolve()?.Invoke(), out reading); } catch (Exception) { reading = default(SeasonReading); } bool result = Reading.Available != reading.Available || Reading.Season != reading.Season || Reading.Effects != reading.Effects; Reading = reading; return result; } public void Reset() { Reading = default(SeasonReading); nextRead = double.NegativeInfinity; } } public sealed class SeasonalWeights { private readonly Dictionary<string, float> values; public SeasonalWeights(string? text) { values = Parse(text); } private static Dictionary<string, float> Parse(string? text) { Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); string[] array = (text ?? "").Split(','); foreach (string text2 in array) { int num = text2.IndexOf('='); if (num >= 1) { string text3 = text2.Substring(0, num).Trim(); if (text3.Length != 0 && text3.IndexOf(';') < 0 && text3.IndexOf('\n') < 0 && text3.IndexOf('\r') < 0 && float.TryParse(text2.Substring(num + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result) && !float.IsInfinity(result) && !(result < 0f) && !(result > 4f)) { dictionary[text3] = result; } } } return dictionary; } public static string Normalize(string? text) { return string.Join(", ", from p in Parse(text) select p.Key + "=" + p.Value.ToString("R", CultureInfo.InvariantCulture)); } public float Multiplier(string? name) { if (string.IsNullOrEmpty(name) || name.IndexOf("clear", StringComparison.OrdinalIgnoreCase) >= 0 || EnvironmentPlan.IsEventEnvironment(name)) { return 1f; } string text = SeasonalEnvironments.BaseName(name); WeatherSwitch weatherSwitch = WeatherSwitches.Find(text); if (weatherSwitch != null && weatherSwitch.Group != SwitchGroup.World) { return 1f; } if (values.TryGetValue(name, out var value)) { return value; } if (!values.TryGetValue(text, out var value2)) { return 1f; } return value2; } } [Flags] public enum HazardEffects { None = 0, Shock = 1, Burning = 2 } public static class StormHazards { public const double RefreshSeconds = 1.0; public const string SerialKey = "Tempest_HazardSerial"; public const string UntilKey = "Tempest_HazardUntil"; public const double AshSeconds = 5.0; public static bool Live(int strikeSerial, double until, TempestEvent? current, double now) { if (current != null && EventKinds.Lightning(current.Kind) && strikeSerial == current.Serial && now >= current.ActiveStart && now < current.ClearingStart) { return now < until; } return false; } public static bool Inside(float dx, float dy, float dz, float radius) { if (radius > 0f && !float.IsNaN(dx + dy + dz) && dx * dx + dz * dz <= radius * radius) { return Math.Abs(dy) <= radius; } return false; } public static bool Due(double last, double now) { if (!(last < 0.0) && !(now < last)) { return now - last >= 1.0; } return true; } public static bool Affects(bool enabled, bool dead, bool godMode, bool ghostMode) { if (enabled && !dead && !godMode) { return !ghostMode; } return false; } public static HazardEffects Effects(bool startsFires, bool raining) { return (HazardEffects)(1 | ((startsFires && !raining) ? 2 : 0)); } public static (float Lightning, float Fire) Damage(HazardEffects effects, float damage) { if (float.IsNaN(damage) || damage <= 0f) { return (Lightning: 0f, Fire: 0f); } if ((effects & HazardEffects.Burning) == 0) { return (Lightning: damage, Fire: 0f); } return (Lightning: damage / 2f, Fire: damage / 2f); } public static bool AddBurning(HazardEffects effects, bool hasBurning, bool dead) { if ((effects & HazardEffects.Burning) != HazardEffects.None && !hasBurning) { return !dead; } return false; } public static bool AshBurnEnded(int burnedSerial, TempestEvent? current, double now, bool enabled) { if (burnedSerial != 0) { if (enabled && current != null && current.Serial == burnedSerial && !(now < current.ActiveStart)) { return now >= current.ClearingStart; } return true; } return false; } public static bool AshAddBurning(bool hasBurning, bool dead) { return AddBurning(HazardEffects.Burning, hasBurning, dead); } public static bool AshBurns(TempestEvent? current, double now, WeatherBiome biome, bool forced, bool enabled, bool dead, bool godMode, bool ghostMode, bool interior, bool underRoof) { if (current != null && current.Kind == EventKind.AshStorm && !forced && current.Affects(biome) && Timeline.At(current, now).Phase == EventPhase.Active && Affects(enabled, dead, godMode, ghostMode) && !interior) { return !underRoof; } return false; } public static bool AshDue(double last, double now) { if (!(last < 0.0) && !(now < last)) { return now - last >= 5.0; } return true; } public static float AshDamage(float damage) { if (!float.IsNaN(damage) && !(damage <= 0f)) { return damage; } return 0f; } public static long UntilMilliseconds(double now, float lingerSeconds) { return (long)Math.Round((now + (double)Math.Max(0f, float.IsNaN(lingerSeconds) ? 0f : lingerSeconds)) * 1000.0); } } [Flags] public enum StrikeEffects { None = 0, HurtsPlayers = 1, StartsFires = 2, HitsTrees = 4 } public readonly struct StrikeOrder : IEquatable<StrikeOrder> { public const string Prefix = "s1"; public const int MaximumLength = 256; public const float MaximumCoordinate = 100000f; public const float MaximumDamage = 200f; private const double MaximumTime = 1000000000000.0; private const int FieldCount = 8; public int Serial { get; } public long PlayerId { get; } public double Time { get; } public float X { get; } public float Z { get; } public float Damage { get; } public StrikeEffects Effects { get; } public string Id => Serial.ToString(CultureInfo.InvariantCulture) + ":" + PlayerId.ToString(CultureInfo.InvariantCulture) + ":" + Num(Time); public StrikeOrder(int serial, long playerId, double time, float x, float z, float damage, StrikeEffects effects) { Serial = serial; PlayerId = playerId; Time = time; X = x; Z = z; Damage = damage; Effects = effects; } public string Encode() { return string.Join(";", "s1", Serial.ToString(CultureInfo.InvariantCulture), PlayerId.ToString(CultureInfo.InvariantCulture), Num(Time), Num(X), Num(Z), Num(Damage), ((int)Effects).ToString(CultureInfo.InvariantCulture)); } public static bool TryDecode(string? text, out StrikeOrder order) { order = default(StrikeOrder); if (string.IsNullOrEmpty(text) || text.Length > 256) { return false; } string[] array = text.Split(';'); if (array.Length != 8 || array[0] != "s1") { return false; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (!int.TryParse(array[1], NumberStyles.AllowLeadingSign, invariantCulture, out var result) || result <= 0) { return false; } if (!long.TryParse(array[2], NumberStyles.AllowLeadingSign, invariantCulture, out var result2) || result2 == 0L) { return false; } if (!double.TryParse(array[3], NumberStyles.Float, invariantCulture, out var result3) || !Finite(result3) || result3 < 0.0 || result3 > 1000000000000.0) { return false; } if (!float.TryParse(array[4], NumberStyles.Float, invariantCulture, out var result4) || !Finite(result4) || Math.Abs(result4) > 100000f) { return false; } if (!float.TryParse(array[5], NumberStyles.Float, invariantCulture, out var result5) || !Finite(result5) || Math.Abs(result5) > 100000f) { return false; } if (!float.TryParse(array[6], NumberStyles.Float, invariantCulture, out var result6) || !Finite(result6) || result6 < 0f || result6 > 200f) { return false; } if (!int.TryParse(array[7], NumberStyles.AllowLeadingSign, invariantCulture, out var result7) || result7 < 0 || result7 > 7) { return false; } order = new StrikeOrder(result, result2, result3, result4, result5, result6, (StrikeEffects)result7); return true; } public bool Equals(StrikeOrder other) { if (Serial == other.Serial && PlayerId == other.PlayerId && Time.Equals(other.Time) && X.Equals(other.X) && Z.Equals(other.Z) && Damage.Equals(other.Damage)) { return Effects == other.Effects; } return false; } public override bool Equals(object? obj) { if (obj is StrikeOrder other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((Serial * 397) ^ PlayerId.GetHashCode()) * 397) ^ Time.GetHashCode()) * 397) ^ (int)Effects; } public override string ToString() { return Encode(); } private static bool Finite(double v) { if (!double.IsNaN(v)) { return !double.IsInfinity(v); } return false; } private static string Num(double v) { return v.ToString("R", CultureInfo.InvariantCulture).ToLowerInvariant(); } private static string Num(float v) { return v.ToString("R", CultureInfo.InvariantCulture).ToLowerInvariant(); } } public enum StrikeVerdict { Accepted, OtherPlayer, NoEvent, OtherEvent, NotActive, Stale, Forced } public static class StrikeOrders { public const double AcceptWindowSeconds = 10.0; public const double TelegraphSeconds = 1.0; public static IReadOnlyList<StrikeOrder> Plan(TempestEvent e, long playerId, float playerX, float playerZ, int perMinute, float damage, StrikeEffects effects, double from, double to, Func<float, float, bool> spared) { if (e == null) { throw new ArgumentNullException("e"); } if (spared == null) { throw new ArgumentNullException("spared"); } float damage2 = (float.IsNaN(damage) ? 0f : Math.Max(0f, Math.Min(200f, damage))); List<StrikeOrder> list = new List<StrikeOrder>(); foreach (PlannedStrike item in StrikePlanner.Due(e, playerId, perMinute, from, to)) { float num = playerX + item.X; float num2 = playerZ + item.Z; if (!(Math.Abs(num) > 100000f) && !(Math.Abs(num2) > 100000f) && !spared(num, num2)) { list.Add(new StrikeOrder(e.Serial, playerId, item.Time, num, num2, damage2, effects)); } } return list; } public static StrikeVerdict Accept(StrikeOrder order, TempestEvent? current, long localPlayerId, double now, bool forced) { if (order.PlayerId != localPlayerId) { return StrikeVerdict.OtherPlayer; } if (current == null) { return StrikeVerdict.NoEvent; } if (current.Serial != order.Serial) { return StrikeVerdict.OtherEvent; } if (!EventKinds.Strikes(current.Kind) || order.Time < current.ActiveStart - 1.0 || order.Time >= current.ClearingStart) { return StrikeVerdict.NotActive; } if (Math.Abs(now - order.Time) > 10.0) { return StrikeVerdict.Stale; } if (now < current.ActiveStart || now >= current.ClearingStart) { return StrikeVerdict.NotActive; } if (forced) { return StrikeVerdict.Forced; } return StrikeVerdict.Accepted; } } public sealed class StrikeLedger { private readonly int capacity; private readonly Queue<string> order = new Queue<string>(); private readonly HashSet<string> seen = new HashSet<string>(StringComparer.Ordinal); public int Count => seen.Count; public StrikeLedger(int capacity) { if (capacity <= 0) { throw new ArgumentOutOfRangeException("capacity"); } this.capacity = capacity; } public bool Admit(string id) { if (id == null || seen.Contains(id)) { return false; } if (order.Count >= capacity) { seen.Remove(order.Dequeue()); } order.Enqueue(id); seen.Add(id); return true; } public void Clear() { order.Clear(); seen.Clear(); } } public enum StrikeTarget { Player, Creature, Tree, Piece, Other } public static class StrikeTargets { public static bool Allow(StrikeEffects effects, StrikeTarget target) { return target switch { StrikeTarget.Player => (effects & StrikeEffects.HurtsPlayers) != 0, StrikeTarget.Tree => (effects & StrikeEffects.HitsTrees) != 0, StrikeTarget.Piece => false, _ => true, }; } public static StrikeEffects InWeather(StrikeEffects effects, bool raining) { if (!raining) { return effects; } return effects & ~StrikeEffects.StartsFires; } public static (float Lightning, float Fire) Damage(float damage, StrikeEffects effects) { if (float.IsNaN(damage) || damage <= 0f) { return (Lightning: 0f, Fire: 0f); } if ((effects & StrikeEffects.StartsFires) == 0) { return (Lightning: damage, Fire: 0f); } return (Lightning: damage / 2f, Fire: damage / 2f); } public static bool CanIgnite(StrikeEffects effects, StrikeTarget target, bool insideWard, bool insideShield) { if ((effects & StrikeEffects.StartsFires) == 0 || insideWard || insideShield) { return false; } return target switch { StrikeTarget.Tree => (effects & StrikeEffects.HitsTrees) != 0, StrikeTarget.Other => true, _ => false, }; } public static StrikeEffects Effects(bool hurtsPlayers, bool startsFires, bool hitsTrees) { return (StrikeEffects)((hurtsPlayers ? 1 : 0) | (startsFires ? 2 : 0) | (hitsTrees ? 4 : 0)); } } public readonly struct PlannedStrike { public double Second { get; } public float X { get; } public float Z { get; } public double Time { get; } public PlannedStrike(double second, float x, float z, double time) { Second = second; X = x; Z = z; Time = time; } } public static class StrikePlanner { public const float MinimumDistance = 15f; public const float MaximumDistance = 60f; public const int MaximumPerMinute = 30; public static IReadOnlyList<PlannedStrike> ForMinute(int eventSeed, long minute, long playerId, int perMinute) { int num = Math.Max(0, Math.Min(30, perMinute)); List<PlannedStrike> list = new List<PlannedStrike>(num); ulong a = (uint)eventSeed; ulong b = (ulong)((minute * 1099511628211L) ^ playerId); for (int i = 0; i < num; i++) { double second = SeededRandom.Unit(a, b, (ulong)(i * 3 + 1)) * 60.0; double num2 = SeededRandom.Unit(a, b, (ulong)(i * 3 + 2)) * 2.0 * Math.PI; double num3 = SeededRandom.Unit(a, b, (ulong)(i * 3 + 3)); double num4 = Math.Sqrt(225.0 + num3 * 3375.0); list.Add(new PlannedStrike(second, (float)(Math.Cos(num2) * num4), (float)(Math.Sin(num2) * num4), 0.0)); } return list.OrderBy((PlannedStrike s) => s.Second).ToList(); } public static int Rate(TempestEvent e, int perMinute, float lightningMultiplier) { if (perMinute <= 0) { return 0; } if (e == null || !e.Dry || float.IsNaN(lightningMultiplier)) { return Math.Min(30, perMinute); } float num = Math.Max(1f, Math.Min(5f, lightningMultiplier)); return (int)Math.Min(30.0, Math.Round((float)perMinute * num, MidpointRounding.AwayFromZero)); } public static IReadOnlyList<PlannedStrike> Due(TempestEvent e, long playerId, int perMinute, double from, double to) { if (e == null) { throw new ArgumentNullException("e"); } List<PlannedStrike> list = new List<PlannedStrike>(); if (!EventKinds.Strikes(e.Kind) || !(to > from)) { return list; } double num = Math.Max(from, e.ActiveStart - 1.0); double num2 = Math.Min(to, e.ClearingStart); if (!(num2 > num)) { return list; } long val = (long)Math.Floor((num - e.ActiveStart) / 60.0); long num3 = (long)Math.Floor((num2 - e.ActiveStart) / 60.0); for (long num4 = Math.Max(0L, val); num4 <= num3; num4++) { foreach (PlannedStrike item in ForMinute(e.Seed, num4, playerId, perMinute)) { double num5 = e.ActiveStart + (double)(num4 * 60) + item.Second; if (num5 > from && num5 <= to && num5 < e.ClearingStart) { list.Add(new PlannedStrike(item.Second, item.X, item.Z, num5)); } } } return list; } } public readonly struct SparedArea { public float X { get; } public float Z { get; } public float Radius { get; } public SparedArea(float x, float z, float radius) { X = x; Z = z; Radius = radius; } } public readonly struct PiecePoint { public float X { get; } public float Z { get; } public PiecePoint(float x, float z) { X = x; Z = z; } } public static class BaseExclusion { public static bool Spared(float x, float z, IEnumerable<SparedArea> areas, IEnumerable<PiecePoint> pieces, float clearance) { foreach (SparedArea item in areas ?? Enumerable.Empty<SparedArea>()) { if (Within(x, z, item.X, item.Z, item.Radius)) { return true; } } if (!(clearance > 0f)) { return false; } foreach (PiecePoint item2 in pieces ?? Enumerable.Empty<PiecePoint>()) { if (Within(x, z, item2.X, item2.Z, clearance)) { return true; } } return false; } private static bool Within(float x, float z, float cx, float cz, float r) { float num = x - cx; float num2 = z - cz; return num * num + num2 * num2 <= r * r; } } public enum SwitchGroup { World, Boss, Dungeon, Raid } public sealed class WeatherSwitch { public SwitchGroup Group { get; } public string Environment { get; } public string Key { get; } public string Description { get; } public string Section => "Weather Filter - " + Group; public string SourceSection => "Weather Events - " + Group; internal WeatherSwitch(SwitchGroup group, string environment, string key, string description) { Group = group; Environment = environment; Key = key; Description = description; } } public static class WeatherSwitches { private static readonly Dictionary<string, WeatherSwitch> ByEnvironment = All.ToDictionary<WeatherSwitch, string>((WeatherSwitch s) => s.Environment, StringComparer.OrdinalIgnoreCase); public static IReadOnlyList<WeatherSwitch> All { get; } = Array.AsReadOnly(new WeatherSwitch[45] { new WeatherSwitch(SwitchGroup.World, "Misty", "Enable Misty Weather", "Enables dense fog weather in Meadows, Black Forest, Plains, and Ocean."), new WeatherSwitch(SwitchGroup.World, "DeepForest Mist", "Enable Deep Forest Mist Weather", "Enables mist and fog weather in Black Forest."), new WeatherSwitch(SwitchGroup.World, "Rain", "Enable Rain Weather", "Enables standard rain in Meadows, Black Forest, Plains, and Ocean."), new WeatherSwitch(SwitchGroup.World, "LightRain", "Enable Light Rain Weather", "Enables light rain and drizzle in Meadows, Plains, and Ocean."), new WeatherSwitch(SwitchGroup.World, "ThunderStorm", "Enable Thunderstorm Weather", "Enables heavy thunderstorms and sea storms in Meadows, Plains, and Ocean."), new WeatherSwitch(SwitchGroup.World, "SwampRain", "Enable Swamp Rain Weather", "Enables continuous rain and gloom in Swamp."), new WeatherSwitch(SwitchGroup.World, "Snow", "Enable Snow Weather", "Enables snowfall in Mountain and Deep North."), new WeatherSwitch(SwitchGroup.World, "SnowStorm", "Enable Snowstorm Weather", "Enables snowstorms and blizzards in Mountain and Deep North."), new WeatherSwitch(SwitchGroup.World, "Twilight_Snow", "Enable Twilight Snow Weather", "Enables twilight snowfall in the Deep North."), new WeatherSwitch(SwitchGroup.World, "Twilight_SnowStorm", "Enable Twilight Snowstorm Weather", "Enables twilight snowstorms in the Deep North."), new WeatherSwitch(SwitchGroup.World, "Mistlands_rain", "Enable Mistlands Rain Weather", "Enables rainfall in Mistlands."), new WeatherSwitch(SwitchGroup.World, "Mistlands_thunder", "Enable Mistlands Thunder Weather", "Enables thunderstorms in Mistlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_misty", "Enable Ashlands Mist Weather", "Enables foggy mist weather in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_ashrain", "Enable Ashlands Ash Rain Weather", "Enables ash rain in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_CinderRain", "Enable Ashlands Cinder Rain Weather", "Enables cinder rain in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_storm", "Enable Ashlands Storm Weather", "Enables heavy storms in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_meteorshower", "Enable Ashlands Meteor Shower Weather", "Enables meteor showers in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashlands_SeaStorm", "Enable Ashlands Sea Storm Weather", "Enables boiling sea storms in Ashlands."), new WeatherSwitch(SwitchGroup.World, "Ashrain", "Enable Legacy Ash Rain Weather", "Enables the older ash rain weather in Ashlands. Off: Ashlands never picks it."), new WeatherSwitch(SwitchGroup.World, "Darklands_dark", "Enable Darklands Dark Weather", "Enables darklands environmental darkness."), new WeatherSwitch(SwitchGroup.World, "nofogts", "Enable Internal NoFog Weather", "Enables the game's fog-free thunderstorm variant. Off: the game never picks it."), new WeatherSwitch(SwitchGroup.Boss, "Eikthyr", "Enable Eikthyr Weather", "Enables storm and lightning weather during Eikthyr boss fight."), new WeatherSwitch(SwitchGroup.Boss, "Eikthyr_no_lightning", "Enable Eikthyr No Lightning Weather", "Enables storm without lightning during Eikthyr boss fight."), new WeatherSwitch(SwitchGroup.Boss, "GDKing", "Enable The Elder Weather", "Enables overcast root weather during The Elder boss fight."), new WeatherSwitch(SwitchGroup.Boss, "Bonemass", "Enable Bonemass Weather", "Enables murky swamp weather during Bonemass boss fight."), new WeatherSwitch(SwitchGroup.Boss, "Moder", "Enable Moder Weather", "Enables freezing blizzard weather during Moder boss fight."), new WeatherSwitch(SwitchGroup.Boss, "GoblinKing", "Enable Yagluth Weather", "Enables fiery atmospheric weather during Yagluth boss fight."), new WeatherSwitch(SwitchGroup.Boss, "Queen", "Enable The Queen Weather", "Enables infested chamber weather during The Queen boss fight."), new WeatherSwitch(SwitchGroup.Boss, "Fader", "Enable Fader Weather", "Enables ash vortex weather during Fader boss fight."), new WeatherSwitch(SwitchGroup.Boss, "DN_Bossroom", "Enable Deep North Boss Room Weather", "Enables Deep North boss arena weather."), new WeatherSwitch(SwitchGroup.Dungeon, "Crypt", "Enable Burial Chambers Weather", "Enables interior crypt gloom in Black Forest Burial Chambers."), new WeatherSwitch(SwitchGroup.Dungeon, "SunkenCrypt", "Enable Sunken Crypt Weather", "Enables interior wet gloom in Swamp Sunken Crypts."), new WeatherSwitch(SwitchGroup.Dungeon, "Caves", "Enable Mountain Caves Weather", "Enables interior mist and cold in Mountain Frost Caves."), new WeatherSwitch(SwitchGroup.Dungeon, "CavesHildir", "Enable Hildir Frost Caves Weather", "Enables interior mist in Hildir Mountain Caves."), new WeatherSwitch(SwitchGroup.Dungeon, "CryptHildir", "Enable Hildir Sunken Crypt Weather", "Enables interior mist in Hildir Sunken Crypts."), new WeatherSwitch(SwitchGroup.Dungeon, "InfectedMine", "Enable Infected Mine Weather", "Enables interior mist and spores in Mistlands Infected Mines."), new WeatherSwitch(SwitchGroup.Raid, "Ghosts", "Enable Ghosts Raid Weather", "Enables ghostly mist during ghost raid encounters."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_meadows", "Enable Meadows Invasion Raid Weather", "Enables atmospheric distortion during Meadows Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_blackforest", "Enable Black Forest Invasion Raid Weather", "Enables atmospheric distortion during Black Forest Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_swamp", "Enable Swamp Invasion Raid Weather", "Enables atmospheric distortion during Swamp Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_mountain", "Enable Mountain Invasion Raid Weather", "Enables atmospheric distortion during Mountain Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_plains", "Enable Plains Invasion Raid Weather", "Enables atmospheric distortion during Plains Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "JotunInvasion_mistlands", "Enable Mistlands Invasion Raid Weather", "Enables atmospheric distortion during Mistlands Jotun invasions."), new WeatherSwitch(SwitchGroup.Raid, "Morkhalla", "Enable Morkhalla Raid Weather", "Enables Morkhalla event weather."), new WeatherSwitch(SwitchGroup.Raid, "TheHollow", "Enable The Hollow Raid Weather", "Enables The Hollow event weather.") }); public static WeatherSwitch? Find(string? environment) { if (environment == null || !ByEnvironment.TryGetValue(environment, out WeatherSwitch value)) { return null; } return value; } } public sealed class WeatherFilter { private readonly Func<WeatherSwitch, bool> isOn; private readonly HashSet<string> others; private readonly SeasonalWeights? seasonal; public WeatherFilter(Func<WeatherSwitch, bool> isOn, string? otherDisabled) : this(isOn, otherDisabled, null) { } public WeatherFilter(Func<WeatherSwitch, bool> isOn, string? otherDisabled, SeasonalWeights? seasonal) { this.isOn = isOn ?? throw new ArgumentNullException("isOn"); others = new HashSet<string>(SplitList(otherDisabled), StringComparer.OrdinalIgnoreCase); this.seasonal = seasonal; } public bool Allows(string? environment) { if (string.IsNullOrEmpty(environment) || IsClear(environment) || IsTempest(environment)) { return true; } string text = SeasonalEnvironments.BaseName(environment); if (others.Contains(environment) || others.Contains(text)) { return false; } WeatherSwitch weatherSwitch = WeatherSwitches.Find(text); if (weatherSwitch == null || isOn(weatherSwitch)) { return Weight(environment) > 0f; } return false; } public float Weight(string? environment) { return seasonal?.Multiplier(environment) ?? 1f; } public IReadOnlyList<string> Available(IEnumerable<string> environments, string? fallback) { List<string> list = (environments ?? Enumerable.Empty<string>()).Where(Allows).ToList(); if (list.Count == 0 && !string.IsNullOrEmpty(fallback)) { list.Add(fallback); } return list; } public string? ForcedOrNull(string? environment) { if (!string.IsNullOrEmpty(environment) && Allows(environment)) { return environment; } return null; } public static string? Fallback(IEnumerable<string>? biomeEnvironments, Func<string, bool> exists, string? defaultEnvironment) { foreach (string item in biomeEnvironments ?? Enumerable.Empty<string>()) { if (!string.IsNullOrEmpty(item) && IsClear(item) && exists(item)) { return item; } } if (!exists("Clear")) { return defaultEnvironment; } return "Clear"; } public static string NormalizeList(string? text) { return string.Join(", ", SplitList(text)); } private static IEnumerable<string> SplitList(string? text) { HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = (text ?? "").Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0 && !IsClear(text2) && !IsTempest(text2) && seen.Add(text2)) { yield return text2; } } } private static bool IsClear(string name) { return name.IndexOf("clear", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsTempest(string name) { return EnvironmentPlan.IsEventEnvironment(name); } } } namespace ExtraPotions.Tempest.Localization { public readonly struct TextKey { public string Key { get; } public int Arguments { get; } public TextKey(string key, int arguments) { Key = key; Arguments = arguments; } } public static class TempestTextKeys { public const string OffNoCrucible = "off_no_crucible"; public const string OffNoTempest = "off_no_tempest"; public const string ImportDone = "import_done"; public const string ImportPreview = "import_preview"; public const string ImportUnavailable = "import_unavailable"; public const string ImportHostOnly = "import_host_only"; public const string WarningStarted = "warning_started"; public const string EventStarted = "event_started"; public const string EventEnded = "event_ended"; public const string PhaseWarning = "phase_warning"; public const string PhaseActive = "phase_active"; public const string PhaseClearing = "phase_clearing"; public const string BadgeEtaWarning = "badge_eta_warning"; public const string BadgeEtaActive = "badge_eta_active"; public const string BadgeEtaClearing = "badge_eta_clearing"; public const string CommandStatusIdle = "cmd_status_idle"; public const string CommandStatusEvent = "cmd_status_event"; public const string CommandStarted = "cmd_started"; public const string CommandStopped = "cmd_stopped"; public const string CommandBusy = "cmd_busy"; public const string CommandNothing = "cmd_nothing"; public const string CommandNoBiome = "cmd_no_biome"; public const string CommandCheatOff = "cmd_cheat_off"; public const string CommandNotAdmin = "cmd_not_admin"; public const string CommandMischiefDenied = "cmd_mischief_denied"; public const string CommandRemoteFailed = "cmd_remote_failed"; public const string CommandSent = "cmd_sent"; public const string CommandHeadless = "cmd_headless"; public const string CommandInactive = "cmd_inactive"; public const string CommandHelp = "cmd_help"; public const string CommandDryUnsupported = "cmd_dry_unsupported"; public const string OverrideSet = "override_set"; public const string OverrideReset = "override_reset"; public const string OverrideUnknown = "override_unknown"; public const string OverrideCleared = "override_cleared"; public const string KindLightningStorm = "kind_lightning_storm"; public static IReadOnlyList<TextKey> All { get; } = Array.AsReadOnly(new TextKey[46] { new TextKey("step_aside_events", 1), new TextKey("step_aside_filter", 1), new TextKey("step_aside_override", 1), new TextKey("off_no_crucible", 0), new TextKey("off_no_tempest", 0), new TextKey("import_done", 2), new TextKey("import_preview", 4), new TextKey("import_unavailable", 0), new TextKey("import_host_only", 0), new TextKey("cheat_weather_commands", 0), new TextKey("cheat_personal_override", 0), new TextKey("kind_blizzard", 0), new TextKey("kind_thunderstorm", 0), new TextKey("kind_lightning_storm", 0), new TextKey("kind_dense_fog", 0), new TextKey("kind_ash_storm", 0), new TextKey("kind_meteor_shower", 0), new TextKey("warning_started", 2), new TextKey("event_started", 1), new TextKey("event_ended", 1), new TextKey("phase_warning", 0), new TextKey("phase_active", 0), new TextKey("phase_clearing", 0), new TextKey("badge_eta_warning", 1), new TextKey("badge_eta_active", 1), new TextKey("badge_eta_clearing", 1), new TextKey("cmd_status_idle", 0), new TextKey("cmd_status_event", 3), new TextKey("cmd_started", 1), new TextKey("cmd_stopped", 1), new TextKey("cmd_busy", 0), new TextKey("cmd_nothing", 0), new TextKey("cmd_no_biome", 1), new TextKey("cmd_cheat_off", 0), new TextKey("cmd_not_admin", 0), new TextKey("cmd_mischief_denied", 0), new TextKey("cmd_remote_failed", 1), new TextKey("cmd_sent", 0), new TextKey("cmd_headless", 0), new TextKey("cmd_inactive", 0), new TextKey("cmd_help", 0), new TextKey("cmd_dry_unsupported", 0), new TextKey("override_set", 1), new TextKey("override_reset", 0), new TextKey("override_unknown", 1), new TextKey("override_cleared", 0) }); public static string Kind(EventKind kind) { return "kind_" + EventKinds.Token(kind); } public static string Kind(EventKind kind, bool dry) { if (!dry || kind != EventKind.Thunderstorm) { return Kind(kind); } return "kind_lightning_storm"; } public static string Kind(TempestEvent e) { return Kind(e.Kind, e.Dry); } public static string Phase(EventPhase phase) { return phase switch { EventPhase.Warning => "phase_warning", EventPhase.Active => "phase_active", EventPhase.Clearing => "phase_clearing", _ => throw new ArgumentException("Idle has no phase text.", "phase"), }; } public static string BadgeEta(EventPhase phase) { return phase switch { EventPhase.Warning => "badge_eta_warning", EventPhase.Active => "badge_eta_active", EventPhase.Clearing => "badge_eta_clearing", _ => throw new ArgumentException("Idle has no badge countdown.", "phase"), }; } public static long WholeSeconds(double seconds) { if (double.IsNaN(seconds) || double.IsInfinity(seconds) || seconds < 0.0) { seconds = 0.0; } return (long)Math.Ceiling(seconds - 1E-09); } public static string Countdown(double seconds) { long num = WholeSeconds(seconds); long num2 = num / 3600; long num3 = num / 60 % 60; long num4 = num % 60; if (num2 <= 0) { return num3 + ":" + num4.ToString("00", CultureInfo.InvariantCulture); } return num2 + ":" + num3.ToString("00", CultureInfo.InvariantCulture) + ":" + num4.ToString("00", CultureInfo.InvariantCulture); } } } namespace ExtraPotions.Tempest.Integration { public readonly struct BadgeState { public static BadgeState Hidden => default(BadgeState); public bool Visible { get; } public EventKind Kind { get; } public bool Dry { get; } public EventPhase Phase { get; } public double SecondsLeft { get; } public double Fraction { get; } public BadgeState(bool visible, EventKind kind, EventPhase phase, double secondsLeft, double fraction, bool dry = false) { Dry = dry; Visible = visible; Kind = kind; Phase = phase; SecondsLeft = secondsLeft; Fraction = fraction; } } public static class Badge { public static BadgeState For(TempestEvent? e, double now, WeatherBiome localBiome, bool gameForced, bool badgeOn, bool otherHudShows) { if (e == null || !badgeOn || otherHudShows || gameForced || !e.Affects(localBiome)) { return BadgeState.Hidden; } PhaseState phaseState = Timeline.At(e, now); if (phaseState.Phase == EventPhase.Idle) { return BadgeState.Hidden; } int num = ((phaseState.Phase == EventPhase.Warning) ? e.WarningSeconds : ((phaseState.Phase == EventPhase.Active) ? e.ActiveSeconds : e.ClearingSeconds)); double num2 = Math.Max(0.0, phaseState.SecondsLeft); double fraction = ((num > 0) ? Math.Max(0.0, Math.Min(1.0, num2 / (double)num)) : 0.0); return new BadgeState(visible: true, e.Kind, phaseState.Phase, num2, fraction, e.Dry); } public static string Title(BadgeState state, Func<string, string> text) { if (!state.Visible) { return ""; } if (text == null) { throw new ArgumentNullException("text"); } return text(TempestTextKeys.Kind(state.Kind, state.Dry)); } public static string Detail(BadgeState state, Func<string, string, string> format) { if (!state.Visible) { return ""; } if (format == null) { throw new ArgumentNullException("format"); } return format(TempestTextKeys.BadgeEta(state.Phase), TempestTextKeys.Countdown(state.SecondsLeft)); } public static string Text(BadgeState state, Func<string, string> text) { if (!state.Visible) { return ""; } if (text == null) { throw new ArgumentNullException("text"); } return text(TempestTextKeys.Kind(state.Kind, state.Dry)) + " " + text(TempestTextKeys.Phase(state.Phase)) + " " + TempestTextKeys.Countdown(state.SecondsLeft); } } public static class CenterNotice { public static bool Show(bool warningMessageOn, bool otherHudShows) { if (warningMessageOn) { return !otherHudShows; } return false; } } public sealed class WarningCue { private int played; public bool Observe(TempestEvent? e, double now, WeatherBiome localBiome, bool gameForced) { if (e == null || e.Serial == played || gameForced || !e.Affects(localBiome)) { return false; } if (Timeline.At(e, now).Phase != EventPhase.Warning) { return false; } played = e.Serial; return true; } } public static class WarningSound { public const string Storm = "sfx_thunder"; public const string Blizzard = "sfx_Frost_Start"; public static string For(EventKind kind) { if (kind != EventKind.Blizzard) { return "sfx_thunder"; } return "sfx_Frost_Start"; } public static bool Matches(string? prefabName, string wanted) { if (prefabName != null) { return string.Equals(prefabName, wanted, StringComparison.Ordinal); } return false; } } public enum TempestModule { Events, Filter, Override } public readonly struct InstalledPlugin { public string Guid { get; } public string Name { get; } public InstalledPlugin(string guid, string name) { Guid = guid ?? ""; Name = name ?? ""; } } public sealed class ModuleSource { public string Guid { get; } public string Name { get; } public TempestModule Module { get; } public IReadOnlyList<string> PluginNames { get; } public string ModuleId => Module.ToString().ToLowerInvariant(); internal ModuleSource(string guid, string name, TempestModule module, params string[] pluginNames) { Guid = guid; Name = name; Module = module; PluginNames = pluginNames; } public bool Matches(InstalledPlugin plugin) { if (!string.Equals(plugin.Guid, Guid, StringComparison.Ordinal)) { return PluginNames.Any((string n) => string.Equals(n, plugin.Name, StringComparison.OrdinalIgnoreCase)); } return true; } } public readonly struct StepAsideNotice { public TempestModule Module { get; } public string SourceName { get; } public string SourceGuid { get; } public string Key => KeyOf(Module); public StepAsideNotice(TempestModule module, string sourceName, string sourceGuid) { Module = module; SourceName = sourceName; SourceGuid = sourceGuid; } public static string KeyOf(TempestModule module) { return "step_aside_" + module.ToString().ToLowerInvariant(); } } public sealed class ModuleStepAsideDecision { private readonly HashSet<TempestModule> aside; public IReadOnlyList<ModuleSource> Present { get; } public bool Any => aside.Count > 0; public IReadOnlyList<StepAsideNotice> Notices { get; } internal ModuleStepAsideDecision(IReadOnlyList<ModuleSource> present) { Present = present; aside = new HashSet<TempestModule>(present.Select((ModuleSource p) => p.Module)); Notices = (from p in present orderby p.Module select new StepAsideNotice(p.Module, p.Name, p.Guid)).ToList(); } public bool SteppedAside(TempestModule module) { return aside.Contains(module); } } public static class ModuleStepAside { public static readonly ModuleSource WildWeather = new ModuleSource("com.burks.wildweather", "WildWeather", TempestModule.Events, "Wild Weather", "WildWeather"); public static readonly ModuleSource NoFogBruh = new ModuleSource("vapok.mods.nofogbruh", "NoFogBruh", TempestModule.Filter); public static readonly ModuleSource AdminTool = new ModuleSource("com.icecub.valheimadmintool", "Valheim_Admin_Tool", TempestModule.Override); public static IReadOnlyList<ModuleSource> Sources { get; } = Array.AsReadOnly(new ModuleSource[3] { WildWeather, NoFogBruh, AdminTool }); public static ModuleStepAsideDecision Decide(IEnumerable<InstalledPlugin> installed) { if (installed == null) { throw new ArgumentNullException("installed"); } List<InstalledPlugin> list = installed.ToList(); return new ModuleStepAsideDecision(Sources.Where((ModuleSource s) => list.Any(s.Matches)).ToList()); } } public enum NoticeRoute { Log, Journal, Kit, Native } public static class NoticeRoutes { public static NoticeRoute For(bool gameUi, bool kitActive, bool playerWindowOpen) { if (!gameUi) { return NoticeRoute.Log; } if (playerWindowOpen) { return NoticeRoute.Journal; } if (!kitActive) { return NoticeRoute.Native; } return NoticeRoute.Kit; } } public readonly struct WeatherEventsView : IEquatable<WeatherEventsView> { public const string Id = "meadworks.weather.events"; public static readonly Version ContractVersion = new Version(1, 0); public const float MaximumSecondsLeft = 3600f; public int Kind { get; } public int Phase { get; } public float SecondsLeft { get; } public int Biomes { get; } public bool LocalAffected { get; } public int Revision { get; } public WeatherEventsView(int kind, int phase, float secondsLeft, int biomes, bool localAffected, int revision) { Kind = kind; Phase = phase; SecondsLeft = secondsLeft; Biomes = biomes; LocalAffected = localAffected; Revision = revision; } public WeatherEventsView WithRevision(int revision) { return new WeatherEventsView(Kind, Phase, SecondsLeft, Biomes, LocalAffected, revision); } public static WeatherEventsView Read(TempestEvent? e, double now, WeatherBiome localBiome, bool gameForced) { if (e == null) { return default(WeatherEventsView); } PhaseState phaseState = Timeline.At(e, now); if (phaseState.Phase == EventPhase.Idle) { return default(WeatherEventsView); } float secondsLeft = (float)Math.Max(0.0, Math.Min(3600.0, phaseState.SecondsLeft)); if (EventKinds.Extended(e.Kind)) { return default(WeatherEventsView); } return new WeatherEventsView((e.Kind == EventKind.Blizzard) ? 1 : 2, (int)phaseState.Phase, secondsLeft, (int)e.Biomes, !gameForced && e.Affects(localBiome), 0); } public static (WeatherEventsView View, string Token) ReadDetail(TempestEvent? e, double now, WeatherBiome localBiome, bool gameForced) { if (e == null) { return (View: default(WeatherEventsView), Token: ""); } PhaseState phaseState = Timeline.At(e, now); if (phaseState.Phase == EventPhase.Idle) { return (View: default(WeatherEventsView), Token: ""); } float secondsLeft = (float)Math.Max(0.0, Math.Min(3600.0, phaseState.SecondsLeft)); return (View: new WeatherEventsView(WeatherEventsDetail.Number(e.Kind), (int)phaseState.Phase, secondsLeft, (int)e.Biomes, !gameForced && e.Affects(localBiome), 0), Token: EventKinds.Token(e.Kind)); } public bool Equals(WeatherEventsView other) { if (Kind == other.Kind && Phase == other.Phase && SecondsLeft.Equals(other.SecondsLeft) && Biomes == other.Biomes && LocalAffected == other.LocalAffected) { return Revision == other.Revision; } return false; } public override bool Equals(object? obj) { if (obj is WeatherEventsView other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((Kind * 397) ^ Phase) * 397) ^ Biomes) * 397) ^ Revision ^ (LocalAffected ? 7919 : 0); } } public static class WeatherEventsDetail { public const string Id = "meadworks.weather.events.detail"; public static readonly Version ContractVersion = new Version(1, 0); public static int Number(EventKind kind) { return (int)(kind + 1); } } public sealed class RevisionCounter { private int revision; private int kind = -1; private int phase = -1; private int biomes = -1; public int Observe(WeatherEventsView view) { if (view.Kind != kind || view.Phase != phase || view.Biomes != biomes) { kind = view.Kind; phase = view.Phase; biomes = view.Biomes; revision++; } return revision; } } } namespace ExtraPotions.Tempest.Events { public static class EventRecord { public const string Version = "t1"; public const int MaximumLength = 256; public static string Encode(ScheduleState state) { if (state == null) { throw new ArgumentNullException("state"); } TempestEvent current = state.Current; string text = ((current == null || EventKinds.Extended(current.Kind)) ? "-" : Fields(current, (current.Kind == EventKind.Blizzard) ? "b" : "t")); return string.Join(";", "t1", I(state.NextSerial), state.LastRolledDay.ToString(CultureInfo.InvariantCulture), state.LastStartDay.ToString(CultureInfo.InvariantCulture), text).ToLowerInvariant(); } public static bool TryDecode(string? text, out ScheduleState state) { state = ScheduleState.Empty; if (string.IsNullOrEmpty(text) || text.Length > 256) { return false; } foreach (char c in text) { if ((c < '0' || c > '9') && c != '.' && c != '+' && c != '-' && c != ';' && c != ',' && c != 'b' && c != 't' && c != 'e') { return false; } } string[] array = text.Split(';'); if (array.Length != 5 || array[0] != "t1") { return false; } if (!int.TryParse(array[1], NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result)) { return false; } if (!long.TryParse(array[2], NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!long.TryParse(array[3], NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result3)) { return false; } TempestEvent e = null; if (array[4] != "-" && !TryEvent(array[4], out e)) { return false; } try { state = new ScheduleState(e, result2, result3, result); return true; } catch (ArgumentException) { state = ScheduleState.Empty; return false; } } public static string? Canonical(string? text) { if (!TryDecode(text, out ScheduleState state)) { return null; } return Encode(state); } private static bool TryEvent(string text, out TempestEvent? e) { return TryFields(text, (string letter) => (!(letter == "b")) ? ((!(letter == "t")) ? ((EventKind?)null) : new EventKind?(EventKind.Thunderstorm)) : new EventKind?(EventKind.Blizzard), out e); } internal static string Fields(TempestEvent e, string letter) { return string.Join(",", e.Serial.ToString(CultureInfo.InvariantCulture), letter, e.Start.ToString("R", CultureInfo.InvariantCulture), I(e.WarningSeconds), I(e.ActiveSeconds), I(e.ClearingSeconds), I(e.Seed), I((int)e.Biomes)); } internal static bool TryFields(string text, Func<string, EventKind?> kindOf, out TempestEvent? e) { e = null; string[] array = text.Split(','); if (array.Length != 8) { return false; } EventKind? eventKind = kindOf(array[1]); if (!eventKind.HasValue) { return false; } if (!Int(array[0], out var value) || !Int(array[3], out var value2) || !Int(array[4], out var value3) || !Int(array[5], out var value4) || !Int(array[6], out var value5) || !Int(array[7], out var value6)) { return false; } if (!double.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result)) { return false; } try { e = new TempestEvent(value, eventKind.Value, result, value2, value3, value4, value5, (WeatherBiome)value6); return true; } catch (ArgumentException) { return false; } } private static bool Int(string s, out int value) { return int.TryParse(s, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out value); } private static string I(int value) { return value.ToString(CultureInfo.InvariantCulture); } } public static class DryMark { public const string None = "0"; public static string Encode(TempestEvent? e) { if (e == null || !e.Dry) { return "0"; } return e.Serial.ToString(CultureInfo.InvariantCulture); } public static string? Canonical(string? text) { if (string.IsNullOrEmpty(text) || text.Length > 10) { return null; } int i; for (i = 0; i < text.Length; i++) { char c = text[i]; if (c < '0' || c > '9') { return null; } } if (text.Length > 1 && text[0] == '0') { return null; } if (!int.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out i)) { return null; } return text; } public static ScheduleState Apply(ScheduleState state, string? mark) { TempestEvent current = state.Current; if (current == null || current.Kind != EventKind.Thunderstorm || Canonical(mark) != current.Serial.ToString(CultureInfo.InvariantCulture)) { return state; } if (current.Dry) { return state; } return state.With(new TempestEvent(current.Serial, current.Kind, current.Start, current.WarningSeconds, current.ActiveSeconds, current.ClearingSeconds, current.Seed, current.Biomes, dry: true)); } } public static class ExtendedRecord { public const string None = "0"; public const string Version = "x1"; public static string Encode(TempestEvent? e) { if (e != null && EventKinds.Extended(e.Kind)) { return "x1;" + EventRecord.Fields(e, Letter(e.Kind)).ToLowerInvariant(); } return "0"; } public static string? Canonical(string? text) { if (text == "0") { return "0"; } if (!TryDecode(text, out TempestEvent e)) { return null; } return Encode(e); } public static bool TryDecode(string? text, out TempestEvent? e) { e = null; if (string.IsNullOrEmpty(text) || text.Length > 256 || !text.StartsWith("x1;", StringComparison.Ordinal)) { return false; } return EventRecord.TryFields(text.Substring("x1".Length + 1), Kind, out e); } public static ScheduleState Apply(ScheduleState state, string? text) { if (state.Current != null || !TryDecode(text, out TempestEvent e) || e.Serial != state.NextSerial - 1) { return state; } return state.With(e); } private static string Letter(EventKind kind) { return kind switch { EventKind.AshStorm => "a", EventKind.DenseFog => "f", _ => "m", }; } private static EventKind? Kind(string letter) { return letter switch { "m" => EventKind.MeteorShower, "a" => EventKind.AshStorm, "f" => EventKind.DenseFog, _ => null, }; } } public static class RecordMigration { public readonly struct Choice { public string? Text { get; } public bool Migrate { get; } public Choice(string? text, bool migrate) { Text = text; Migrate = migrate; } } public static Choice Choose(string? current, string? legacy) { string text = EventRecord.Canonical(current); if (text != null) { return new Choice(text, migrate: false); } string text2 = EventRecord.Canonical(legacy); if (text2 == null) { return new Choice(null, migrate: false); } return new Choice(text2, migrate: true); } } public sealed class ScheduleRules { public bool Enabled { get; } public float BlizzardChance { get; } public float StormChance { get; } public int MinimumDaysBetween { get; } public int WarningSeconds { get; } public int ActiveMinSeconds { get; } public int ActiveMaxSeconds { get; } public int ClearingSeconds { get; } public WeatherBiome BlizzardBiomes { get; } public WeatherBiome StormBiomes { get; } public float DryStormChance { get; } public float FogChance { get; } public WeatherBiome FogBiomes { get; } public float AshStormChance { get; } public WeatherBiome AshStormBiomes { get; } public float MeteorShowerChance { get; } public WeatherBiome MeteorShowerBiomes { get; } public ScheduleRules(bool enabled, float blizzardChance, float stormChance, int minimumDaysBetween, int warningSeconds, int activeMinSeconds, int activeMaxSeconds, int clearingSeconds, WeatherBiome blizzardBiomes, WeatherBiome stormBiomes, float dryStormChance = 0f, float fogChance = 0f, WeatherBiome fogBiomes = WeatherBiome.None, float ashStormChance = 0f, WeatherBiome ashStormBiomes = WeatherBiome.None, float meteorShowerChance = 0f, WeatherBiome meteorShowerBiomes = WeatherBiome.None) { Enabled = enabled; BlizzardChance = Clamp01(blizzardChance); StormChance = Clamp01(stormChance); MinimumDaysBetween = Math.Max(0, minimumDaysBetween); WarningSeconds = Math.Max(0, Math.Min(3600, warningSeconds)); ActiveMinSeconds = Math.Max(1, Math.Min(3600, activeMinSeconds)); ActiveMaxSeconds = Math.Max(ActiveMinSeconds, Math.Min(3600, activeMaxSeconds)); ClearingSeconds = Math.Max(0, Math.Min(3600, clearingSeconds)); BlizzardBiomes = blizzardBiomes & BiomeSets.Allowed(EventKind.Blizzard); StormBiomes = stormBiomes & BiomeSets.Allowed(EventKind.Thunderstorm); DryStormChance = Clamp01(dryStormChance); FogChance = Clamp01(fogChance); FogBiomes = fogBiomes & BiomeSets.Allowed(EventKind.DenseFog); AshStormChance = Clamp01(ashStormChance); AshStormBiomes = ashStormBiomes & BiomeSets.Allowed(EventKind.AshStorm); MeteorShowerChance = Clamp01(meteorShowerChance); MeteorShowerBiomes = meteorShowerBiomes & BiomeSets.Allowed(EventKind.MeteorShower); } public float Chance(EventKind kind) { return kind switch { EventKind.Blizzard => BlizzardChance, EventKind.Thunderstorm => StormChance, EventKind.LightningStorm => StormChance * DryStormChance, EventKind.DenseFog => FogChance, EventKind.AshStorm => AshStormChance, EventKind.MeteorShower => MeteorShowerChance, _ => 0f, }; } public WeatherBiome Biomes(EventKind kind) { switch (kind) { case EventKind.Blizzard: return BlizzardBiomes; case EventKind.Thunderstorm: case EventKind.LightningStorm: return StormBiomes; case EventKind.DenseFog: return FogBiomes; case EventKind.AshStorm: return AshStormBiomes; case EventKind.MeteorShower: return MeteorShowerBiomes; default: return WeatherBiome.None; } } private static float Clamp01(float v) { if (!float.IsNaN(v)) { return Math.Max(0f, Math.Min(1f, v)); } return 0f; } } public sealed class ScheduleState : IEquatable<ScheduleState> { public const int MaximumSerial = 1000000000; public const long NoStart = -2305843009213693952L; public static ScheduleState Empty => new ScheduleState(null, -1L, -2305843009213693952L, 1); public TempestEvent? Current { get; } public long LastRolledDay { get; } public long LastStartDay { get; } public int NextSerial { get; } public ScheduleState(TempestEvent? current, long lastRolledDay, long lastStartDay, int nextSerial) { if (lastRolledDay < -1) { throw new ArgumentException("Expected -1 or a day.", "lastRolledDay"); } if (nextSerial < 1 || nextSerial > 1000000000) { throw new ArgumentException("Serial out of range.", "nextSerial"); } if (current != null && current.Serial >= nextSerial) { throw new ArgumentException("The next serial must follow the current event.", "nextSerial"); } Current = current; LastRolledDay = lastRolledDay; LastStartDay = lastStartDay; NextSerial = nextSerial; } public ScheduleState With(TempestEvent? current) { return new ScheduleState(current, LastRolledDay, LastStartDay, NextSerial); } public bool Equals(ScheduleState? other) { if (other != null && object.Equals(Current, other.Current) && LastRolledDay == other.LastRolledDay && LastStartDay == other.LastStartDay) { return NextSerial == other.NextSerial; } return false; } public override bool Equals(object? obj) { if (obj is ScheduleState other) { return Equals(other); } return false; } public override int GetHashCode() { return (((LastRolledDay.GetHashCode() * 397) ^ LastStartDay.GetHashCode()) * 397) ^ NextSerial; } } public enum SchedulerOutcome { None, Started, Finished, Stopped, Busy, Refused } public readonly struct SchedulerResult { public ScheduleState State { get; } public SchedulerOutcome Outcome { get; } public SchedulerResult(ScheduleState state, SchedulerOutcome outcome) { State = state; Outcome = outcome; } } public static class Scheduler { private const ulong RollSalt = 8387230210553246721uL; private const ulong LengthSalt = 8387230210553246724uL; private const ulong SeedSalt = 8387230210553246725uL; private const ulong DrySalt = 8387230210553246726uL; private const ulong PickSalt = 8387230210553246727uL; public static long DayOf(double now, double dayLength) { if (!(dayLength > 0.0) || !(now >= 0.0)) { return 0L; } return (long)Math.Floor(now / dayLength); } public static double RollTime(long worldSeed, long day, double dayLength) { return (double)day * dayLength + (0.05 + 0.9 * SeededRandom.Unit((ulong)worldSeed, (ulong)day, 8387230210553246721uL)) * dayLength; } public static SchedulerResult Tick(ScheduleState state, ScheduleRules rules, long worldSeed, double now, double dayLength, WeatherBiome occupied, WeatherBiome raided = WeatherBiome.None) { if (state == null) { throw new ArgumentNullException("state"); } if (rules == null) { throw new ArgumentNullException("rules"); } if (state.Current != null && !rules.Enabled) { return new SchedulerResult(state.With(null), SchedulerOutcome.Stopped); } if (state.Current != null && Timeline.Finished(state.Current, now)) { return new SchedulerResult(state.With(null), SchedulerOutcome.Finished); } long num = DayOf(now, dayLength); if (num <= state.LastRolledDay || now < RollTime(worldSeed, num, dayLength)) { return new SchedulerResult(state, SchedulerOutcome.None); } ScheduleState state2 = new ScheduleState(state.Current, num, state.LastStartDay, state.NextSerial); if (!rules.Enabled || state.Current != null || num - state.LastStartDay < rules.MinimumDaysBetween || state.NextSerial >= 1000000000) { return new SchedulerResult(state2, SchedulerOutcome.None); } EventKind? eventKind = Pick(rules, SeededRandom.Unit((ulong)worldSeed, (ulong)num, 8387230210553246727uL)); if (!eventKind.HasValue || (rules.Biomes(eventKind.Value) & occupied) == 0) { return new SchedulerResult(state2, SchedulerOutcome.None); } if ((rules.Biomes(eventKind.Value) & raided) != WeatherBiome.None) { return new SchedulerResult(state, SchedulerOutcome.None); } int val = rules.ActiveMinSeconds + (int)Math.Floor(SeededRandom.Unit((ulong)worldSeed, (ulong)num, 8387230210553246724uL) * (double)(rules.ActiveMaxSeconds - rules.ActiveMinSeconds + 1)); int seed = (int)SeededRandom.Next((ulong)worldSeed, (ulong)num, 8387230210553246725uL); SchedulerResult result = Start(state2, rules, eventKind.Value, now, rules.WarningSeconds, Math.Min(val, rules.ActiveMaxSeconds), seed, dayLength, null, nightRule: true); if (result.Outcome != SchedulerOutcome.Started) { return new SchedulerResult(state2, SchedulerOutcome.None); } return result; } public static EventKind? Pick(ScheduleRules rules, double roll) { if (rules == null) { throw new ArgumentNullException("rules"); } double num = 0.0; foreach (EventKind item in EventKinds.Rolled) { if (Enabled(rules, item)) { num += (double)rules.Chance(item); } } if (!(num > 0.0)) { return null; } double num2 = ((num > 1.0) ? (1.0 / num) : 1.0); double num3 = 0.0; foreach (EventKind item2 in EventKinds.Rolled) { if (Enabled(rules, item2)) { num3 += (double)rules.Chance(item2) * num2; if (roll < num3) { return item2; } } } return null; } private static bool Enabled(ScheduleRules rules, EventKind kind) { if (rules.Chance(kind) > 0f) { return rules.Biomes(kind) != WeatherBiome.None; } return false; } public static SchedulerResult Summon(ScheduleState state, ScheduleRules rules, EventKind kind, double now, bool skipWarning, int seed, double dayLength, bool? dry = null) { if (state == null) { throw new ArgumentNullException("state"); } if (rules == null) { throw new ArgumentNullException("rules"); } if (state.Current != null && !Timeline.Finished(state.Current, now)) { return new SchedulerResult(state, SchedulerOutcome.Busy); } if (rules.Biomes(kind) == WeatherBiome.None || state.NextSerial >= 1000000000) { return new SchedulerResult(state, SchedulerOutcome.Refused); } int val = rules.ActiveMinSeconds + (int)Math.Floor(SeededRandom.Unit((ulong)seed, (ulong)now, 8387230210553246724uL) * (double)(rules.ActiveMaxSeconds - rules.ActiveMinSeconds + 1)); bool flag = kind == EventKind.Thunderstorm || kind == EventKind.LightningStorm; bool? dry2 = ((kind == EventKind.LightningStorm) ? new bool?(true) : (dry.HasValue ? new bool?(dry.Value && flag) : ((bool?)null))); return Start(state, rules, flag ? EventKind.Thunderstorm : kind, now, (!skipWarning) ? rules.WarningSeconds : 0, Math.Min(val, rules.ActiveMaxSeconds), seed, dayLength, dry2, !skipWarning); } public static EventKind SummonedKind(SchedulerResult result, EventKind requested) { if (result.Outcome != SchedulerOutcome.Started || result.State.Current == null) { return requested; } return result.State.Current.Kind; } public static SchedulerResult Stop(ScheduleState state, double now) { if (state == null) { throw new ArgumentNullException("state"); } TempestEvent current = state.Current; if (current == null || Timeline.Finished(current, now)) { return new SchedulerResult(state.With(null), SchedulerOutcome.None); } double start = Math.Max(0.0, now); return new SchedulerResult(state.With(new TempestEvent(current.Serial, current.Kind, start, 0, 0, current.ClearingSeconds, current.Seed, current.Biomes)), SchedulerOutcome.Stopped); } public static bool RollDry(EventKind kind, int seed, int serial, float chance) { if (kind == EventKind.Thunderstorm && chance > 0f) { return SeededRandom.Unit((ulong)seed, (ulong)serial, 8387230210553246726uL) < (double)chance; } return false; } private static SchedulerResult Start(ScheduleState state, ScheduleRules rules, EventKind kind, double now, int warning, int active, int seed, double dayLength, bool? dry, bool nightRule) { if (nightRule && EventKinds.NightOnly(kind) && !NightWindow.Fit(Math.Max(0.0, now), dayLength, warning, active, rules.ActiveMinSeconds, out warning, out active)) { return new SchedulerResult(state, SchedulerOutcome.Refused); } int nextSerial = state.NextSerial; bool dry2 = dry ?? RollDry(kind, seed, nextSerial, rules.DryStormChance); TempestEvent tempestEvent = new TempestEvent(nextSerial, kind, Math.Max(0.0, now), warning, active, rules.ClearingSeconds, seed, rules.Biomes(kind), dry2); return new SchedulerResult(new ScheduleState(tempestEvent, state.LastRolledDay, DayOf(now, dayLength), tempestEvent.Serial + 1), SchedulerOutcome.Started); } } public static class NightWindow { public const double Dusk = 0.85; public const double Dawn = 0.15; public static double Length(double dayLength) { return 0.30000000000000004 * dayLength; } public static bool IsNight(double now, double dayLength) { if (!(dayLength > 0.0) || now < 0.0) { return false; } double num = now / dayLength - Math.Floor(now / dayLength); if (!(num >= 0.85)) { return num < 0.15; } return true; } public static (double Start, double End) Next(double time, double dayLength) { double num = Math.Floor(time / dayLength); double num2 = time / dayLength - num; if (num2 < 0.15) { return (Start: time, End: (num + 0.15) * dayLength); } if (num2 >= 0.85) { return (Start: time, End: (num + 1.0 + 0.15) * dayLength); } return (Start: (num + 0.85) * dayLength, End: (num + 1.0 + 0.15) * dayLength); } public static bool Fit(double now, double dayLength, int warning, int active, int activeMin, out int fitWarning, out int fitActive) { fitWarning = warning; fitActive = active; if (!(dayLength > 0.0) || double.IsInfinity(dayLength) || active < 1) { return false; } double d = Length(dayLength); int num = Math.Max(1, Math.Min(activeMin, (int)Math.Floor(d))); double num2 = now + (double)warning; (double, double) tuple = Next(num2, dayLength); if (tuple.Item1 > num2 || tuple.Item2 - num2 < (double)num) { double time; if (tuple.Item1 > num2) { (time, _) = tuple; } else { time = tuple.Item2; } tuple = Next(time, dayLength); fitWarning = (int)Math.Ceiling(tuple.Item1 - now); num2 = now + (double)fitWarning; } if (fitWarning > 3600) { return false; } fitActive = (int)Math.Max(1.0, Math.Min(active, Math.Floor(tuple.Item2 - num2))); return true; } } public static class SeededRandom { public static ulong Next(ulong a, ulong b, ulong c) { return Mix(Mix(Mix(a ^ 0x9E3779B97F4A7C15uL) ^ b) ^ c); } public static double Unit(ulong a, ulong b, ulong c) { return (double)(Next(a, b, c) >> 11) * 1.1102230246251565E-16; } private static ulong Mix(ulong z) { z += 11400714819323198485uL; z = (z ^ (z >> 30)) * 13787848793156543929uL; z = (z ^ (z >> 27)) * 10723151780598845931uL; return z ^ (z >> 31); } } public enum EventKind { Blizzard, Thunderstorm, LightningStorm, DenseFog, AshStorm, MeteorShower } public static class EventKinds { public static IReadOnlyList<EventKind> All { get; } = Array.AsReadOnly((EventKind[])Enum.GetValues(typeof(EventKind))); public static IReadOnlyList<EventKind> Rolled { get; } = Array.AsReadOnly(new EventKind[5] { EventKind.Blizzard, EventKind.Thunderstorm, EventKind.DenseFog, EventKind.AshStorm, EventKind.MeteorShower }); public static string Token(EventKind kind) { return kind switch { EventKind.Blizzard => "blizzard", EventKind.Thunderstorm => "thunderstorm", EventKind.LightningStorm => "lightning_storm", EventKind.DenseFog => "dense_fog", EventKind.AshStorm => "ash_storm", EventKind.MeteorShower => "meteor_shower", _ => throw new ArgumentException("Unknown kind.", "kind"), }; } public static bool Strikes(EventKind kind) { if (kind != EventKind.Thunderstorm && kind != EventKind.LightningStorm) { return kind == EventKind.MeteorShower; } return true; } public static bool Lightning(EventKind kind) { if (kind != EventKind.Thunderstorm) { return kind == EventKind.LightningStorm; } return true; } public static bool NightOnly(EventKind kind) { return kind == EventKind.MeteorShower; } public static bool Extended(EventKind kind) { if (kind != EventKind.DenseFog && kind != EventKind.AshStorm) { return kind == EventKind.MeteorShower; } return true; } } public enum EventPhase { Idle, Warning, Active, Clearing } [Flags] public enum WeatherBiome { None = 0, Meadows = 1, Swamp = 2, Mountain = 4, BlackForest = 8, Plains = 0x10, AshLands = 0x20, DeepNorth = 0x40, Ocean = 0x100, Mistlands = 0x200 } public static class BiomeSets { private static readonly WeatherBiome[] Order = new WeatherBiome[9] { WeatherBiome.Meadows, WeatherBiome.BlackForest, WeatherBiome.Swamp, WeatherBiome.Mountain, WeatherBiome.Plains, WeatherBiome.Ocean, WeatherBiome.Mistlands, WeatherBiome.AshLands, WeatherBiome.DeepNorth }; private const WeatherBiome Lowlands = WeatherBiome.Meadows | WeatherBiome.Swamp | WeatherBiome.BlackForest | WeatherBiome.Plains; public static IEnumerable<WeatherBiome> All => Order; public static WeatherBiome Allowed(EventKind kind) { return kind switch { EventKind.Blizzard => WeatherBiome.Meadows | WeatherBiome.Swamp | WeatherBiome.BlackForest | WeatherBiome.Plains | WeatherBiome.DeepNorth, EventKind.AshStorm => WeatherBiome.AshLands, EventKind.MeteorShower => WeatherBiome.Plains | WeatherBiome.AshLands, _ => WeatherBiome.Meadows | WeatherBiome.Swamp | WeatherBiome.BlackForest | WeatherBiome.Plains | WeatherBiome.Ocean, }; } public static WeatherBiome Default(EventKind kind) { if (kind != EventKind.Blizzard) { return Allowed(kind); } return WeatherBiome.Meadows | WeatherBiome.BlackForest | WeatherBiome.DeepNorth; } public static WeatherBiome Parse(EventKind kind, string? text) { WeatherBiome weatherBiome = WeatherBiome.None; string[] array = (text ?? "").Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { continue; } WeatherBiome[] order = Order; for (int j = 0; j < order.Length; j++) { WeatherBiome weatherBiome2 = order[j]; if (string.Equals(weatherBiome2.ToString(), text2, StringComparison.OrdinalIgnoreCase)) { weatherBiome |= weatherBiome2; } } } return weatherBiome & Allowed(kind); } public static string Format(WeatherBiome biomes) { return string.Join(", ", from b in Order where (biomes & b) != 0 select b.ToString()); } public static string Normalize(EventKind kind, string? text) { return Format(Parse(kind, text)); } public static IReadOnlyList<string> Dropped(EventKind kind, string? text) { List<string> list = new List<string>(); WeatherBiome allowed = Allowed(kind); string[] array = (text ?? "").Split(','); foreach (string text2 in array) { string name = text2.Trim(); if (name.Length != 0 && !Order.Any((WeatherBiome b) => (allowed & b) != WeatherBiome.None && string.Equals(b.ToString(), name, StringComparison.OrdinalIgnoreCase))) { list.Add(name); } } return list; } public static bool IsSingle(WeatherBiome biome) { if (biome != WeatherBiome.None) { return (biome & (biome - 1)) == 0; } return false; } } public sealed class TempestEvent : IEquatable<TempestEvent> { public const int MaximumPhaseSeconds = 3600; public int Serial { get; } public EventKind Kind { get; } public double Start { get; } public int WarningSeconds { get; } public int ActiveSeconds { get; } public int ClearingSeconds { get; } public int Seed { get; } public WeatherBiome Biomes { get; } public bool Dry => Kind == EventKind.LightningStorm; public double ActiveStart => Start + (double)WarningSeconds; public double ClearingStart => ActiveStart + (double)ActiveSeconds; public double End => ClearingStart + (double)ClearingSeconds; public TempestEvent(int serial, EventKind kind, double start, int warningSeconds, int activeSeconds, int clearingSeconds, int seed, WeatherBiome biomes, bool dry = false) { if (serial < 1) { throw new ArgumentException("Serial starts at 1.", "serial"); } if (!Enum.IsDefined(typeof(EventKind), kind)) { throw new ArgumentException("Unknown kind.", "kind"); } if (double.IsNaN(start) || double.IsInfinity(start) || start < 0.0) { throw new ArgumentException("Expected a finite world time.", "start"); } Check(warningSeconds, "warningSeconds"); Check(activeSeconds, "activeSeconds"); Check(clearingSeconds, "clearingSeconds"); if (biomes == WeatherBiome.None || (biomes & ~BiomeSets.Allowed(kind)) != WeatherBiome.None) { throw new ArgumentException("Biomes must be a non-empty set this kind allows.", "biomes"); } if (dry && kind != EventKind.Thunderstorm && kind != EventKind.LightningStorm) { throw new ArgumentException("Only a thunderstorm can be dry.", "dry"); } Serial = serial; Kind = (dry ? EventKind.LightningStorm : kind); Start = start; WarningSeconds = warningSeconds; ActiveSeconds = activeSeconds; ClearingSeconds = clearingSeconds; Seed = seed; Biomes = biomes; } public bool Affects(WeatherBiome biome) { if (biome != WeatherBiome.None) { return (Biomes & biome) == biome; } return false; } public bool Equals(TempestEvent? other) { if (other != null && Serial == other.Serial && Kind == other.Kind && Start.Equals(other.Start) && WarningSeconds == other.WarningSeconds && ActiveSeconds == other.ActiveSeconds && ClearingSeconds == other.ClearingSeconds && Seed == other.Seed) { return Biomes == other.Biomes; } return false; } public override bool Equals(object? obj) { if (obj is TempestEvent other) { return Equals(other); } return false; } public override int GetHashCode() { return (int)(((((uint)(Serial * 397) ^ (uint)Kind) * 397) ^ (uint)Start.GetHashCode()) * 397) ^ Seed; } public override string ToString() { return Kind.ToString() + " #" + Serial + " at " + Start.ToString("0.#", CultureInfo.InvariantCulture) + " (" + WarningSeconds + "/" + ActiveSeconds + "/" + ClearingSeconds + " s, " + BiomeSets.Format(Biomes) + ")"; } private static void Check(int seconds, string name) { if (seconds < 0 || seconds > 3600) { throw new ArgumentException("Phase length out of range.", name); } } } public readonly struct PhaseState { public static PhaseState Idle => new PhaseState(EventPhase.Idle, 0.0, 0.0, 0.0); public EventPhase Phase { get; } public double SecondsLeft { get; } public double Progress { get; } public double SecondsUntilActive { get; } public PhaseState(EventPhase phase, double secondsLeft, double progress, double secondsUntilActive) { Phase = phase; SecondsLeft = secondsLeft; Progress = progress; SecondsUntilActive = secondsUntilActive; } } public static class Timeline { public static PhaseState At(TempestEvent e, double now) { if (e == null) { throw new ArgumentNullException("e"); } if (double.IsNaN(now) || now < e.Start || now >= e.End) { return PhaseState.Idle; } if (now < e.ActiveStart) { return Make(EventPhase.Warning, now, e.Start, e.WarningSeconds, e.ActiveStart - now); } if (now < e.ClearingStart) { return Make(EventPhase.Active, now, e.ActiveStart, e.ActiveSeconds, 0.0); } return Make(EventPhase.Clearing, now, e.ClearingStart, e.ClearingSeconds, 0.0); } public static bool Finished(TempestEvent e, double now) { if (e != null) { return now >= e.End; } return false; } private static PhaseState Make(EventPhase phase, double now, double from, int length, double untilActive) { double num = now - from; return new PhaseState(phase, (double)length - num, (length <= 0) ? 0.0 : (num / (double)length), untilActive); } } public readonly struct VanillaEventArea { public float X { get; } public float Z { get; } public float Range { get; } public VanillaEventArea(float x, float z, float range) { X = x; Z = z; Range = range; } } public static class VanillaEvents { public const float CeilingY = 3000f; public static bool Inside(VanillaEventArea? area, float x, float y, float z) { if (!area.HasValue || y > 3000f) { return false; } VanillaEventArea value = area.Value; float num = x - value.X; float num2 = z - value.Z; return Math.Sqrt(num * num + num2 * num2) < (double)value.Range; } public static bool Covers(bool activeForPlayer, VanillaEventArea? area, float x, float y, float z) { if (!activeForPlayer) { return Inside(area, x, y, z); } return true; } } } namespace ExtraPotions.Tempest.Conversion { public readonly struct ImportValue { public string SettingId { get; } public object Value { get; } public string Source { get; } public ImportValue(string settingId, object value, string source) { SettingId = settingId; Value = value; Source = source; } } public sealed class ImportPlan { public IReadOnlyList<ImportValue> Values { get; } public IReadOnlyList<string> OtherDisabled { get; } public IReadOnlyList<string> Notes { get; } internal ImportPlan(IReadOnlyList<ImportValue> values, IReadOnlyList<string> otherDisabled, IReadOnlyList<string> notes) { Values = values; OtherDisabled = otherDisabled; Notes = notes; } public string Report() { IEnumerable<string> first = new string[1] { "Source: NoFogBruh" }.Concat(Values.Select((ImportValue v) => "Set " + v.SettingId + " = " + (((bool)v.Value) ? "true" : "false") + " (from " + v.Source + ")")); IEnumerable<string> second; if (OtherDisabled.Count != 0) { IEnumerable<string> enumerable = new string[1] { "Set " + TempestSettings.Filter.OtherDisabled.Id + " = " + string.Join(", ", OtherDisabled) + " (merged with the current list)" }; second = enumerable; } else { second = Enumerable.Empty<string>(); } return string.Join("\n", first.Concat(second).Concat(Notes.Select((string n) => "Note: " + n))); } } public readonly struct SourceEntry { public string Section { get; } public string Key { get; } public string Value { get; } public SourceEntry(string section, string key, string value) { Section = section; Key = key; Value = value; } } public static class SourceConfig { public static IReadOnlyList<SourceEntry> Parse(string? text) { List<SourceEntry> list = new List<SourceEntry>(); string section = ""; string[] array = (text ?? "").Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } if (text2[0] == '[' && text2[text2.Length - 1] == ']') { section = text2.Substring(1, text2.Length - 2).Trim(); continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); if (text3.Length != 0) { list.Add(new SourceEntry(section, text3, text2.Substring(num + 1).Trim())); } } } return list; } } public static class SourceImport { public const string NoFogBruhFile = "vapok.mods.nofogbruh.cfg"; private const string SectionPrefix = "Weather Events - "; public static ImportPlan Plan(string? text) { List<ImportValue> list = new List<ImportValue>(); List<string> list2 = new List<string>(); List<string> list3 = new List<string>(); Dictionary<string, WeatherSwitch> dictionary = WeatherSwitches.All.ToDictionary<WeatherSwitch, string>((WeatherSwitch s) => s.SourceSection + "\n" + s.Key, StringComparer.Ordinal); foreach (SourceEntry item in SourceConfig.Parse(text)) { if (!item.Section.StartsWith("Weather Events - ", StringComparison.Ordinal)) { continue; } string text2 = "[" + item.Section + "] " + item.Key; if (!bool.TryParse(item.Value, out var result)) { list3.Add(text2 + " = " + item.Value + ": not true or false, skipped"); continue; } if (dictionary.TryGetValue(item.Section + "\n" + item.Key, out var value)) { list.Add(new ImportValue(TempestSettings.Filter.Switches[value].Id, result, text2)); continue; } string text3 = DynamicEnvironment(item.Key); if (text3 == null) { list3.Add(text2 + ": unknown key, skipped"); } else if (result) { list3.Add(text2 + ": other environment " + text3 + " stays allowed (nothing to import)"); } else { list2.Add(text3); } } return new ImportPlan(list, list2.Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(), list3); } public static string MergeOther(string? current, IEnumerable<string> imported) { return WeatherFilter.NormalizeList(string.Join(",", new string[1] { current ?? "" }.Concat(imported ?? Enumerable.Empty<string>()))); } private static string? DynamicEnvironment(string key) { if (!key.StartsWith("Enable ", StringComparison.Ordinal) || !key.EndsWith(" Weather", StringComparison.Ordinal) || key.Length <= "Enable ".Length + " Weather".Length) { return null; } string text = key.Substring("Enable ".Length, key.Length - "Enable ".Length - " Weather".Length).Trim(); if (text.Length != 0 && text.IndexOf(' ') < 0) { return text; } return null; } } } namespace ExtraPotions.Tempest.Config { public static class DefaultUpgrades { public const string BlizzardBiomesBeforeDeepNorth = "Meadows, BlackForest"; private static readonly Dictionary<TempestSetting<string>, string> earlier = new Dictionary<TempestSetting<string>, string> { [TempestSettings.Schedule.BlizzardBiomes] = "Meadows, BlackForest" }; public static string? Upgrade(TempestSetting setting, string? saved) { if (!(setting is TempestSetting<string> tempestSetting) || saved == null || !earlier.TryGetValue(tempestSetting, out string value) || !string.Equals(saved, value, StringComparison.Ordinal)) { return null; } return tempestSetting.Default; } public static bool MissesDeepNorth(TempestSetting setting, string? saved) { if (setting != TempestSettings.Schedule.BlizzardBiomes || saved == null) { return false; } WeatherBiome weatherBiome = BiomeSets.Parse(EventKind.Blizzard, saved); if (weatherBiome != WeatherBiome.None) { return (weatherBiome & WeatherBiome.DeepNorth) == 0; } return false; } } public enum SessionKind { SinglePlayer, Host, DedicatedServer, RemoteClient } public enum ServerKind { Unknown, Vanilla, CrucibleWithoutTempest, Tempest } public enum BlockReason { None, SetByServer, VanillaServer, ProductDisabled, NotAdmin, AuthorityPending } public readonly struct SessionFacts { public static SessionFacts SinglePlayer { get; } = new SessionFacts(SessionKind.SinglePlayer, ServerKind.Tempest, admin: true); public SessionKind Kind { get; } public ServerKind Server { get; } public bool Admin { get; } public bool IsAuthority => Kind != SessionKind.RemoteClient; public SessionFacts(SessionKind kind, ServerKind server, bool admin) { Kind = kind; Server = server; Admin = admin; } } public static class SettingAccess { public static BlockReason Decide(SettingScope scope, SessionFacts facts) { if (facts.IsAuthority || scope == SettingScope.Local) { return BlockReason.None; } switch (facts.Server) { case ServerKind.Unknown: return BlockReason.AuthorityPending; case ServerKind.Vanilla: return BlockReason.VanillaServer; case ServerKind.CrucibleWithoutTempest: return BlockReason.ProductDisabled; default: if (facts.Admin) { return BlockReason.None; } if (scope != SettingScope.Cheat) { return BlockReason.SetByServer; } return BlockReason.NotAdmin; } } public static bool CheatLocked(SessionFacts facts) { if (!facts.IsAuthority) { return facts.Server != ServerKind.Tempest; } return false; } } public enum SettingScope { Local, ServerAuthoritative, Cheat } public abstract class TempestSetting { private static readonly IReadOnlyList<string> NoChoices = Array.Empty<string>(); public string Section { get; } public string Key { get; } public string Id => Section + "." + Key; public SettingScope Scope { get; } public string Description { get; } public IReadOnlyList<string> Choices { get; } public bool Hidden { get; } public abstract Type ValueType { get; } public abstract object DefaultValue { get; } public abstract object? SafeValue { get; } public abstract object? Minimum { get; } public abstract object? Maximum { get; } private protected TempestSetting(string section, string key, SettingScope scope, string description, IReadOnlyList<string>? choices, bool hidden) { if (string.IsNullOrWhiteSpace(section) || section.Trim() != section) { throw new ArgumentException("Expected a trimmed section name.", "section"); } if (string.IsNullOrWhiteSpace(key) || key.Trim() != key) { throw new ArgumentException("Expected a trimmed key.", "key"); } if (!Enum.IsDefined(typeof(SettingScope), scope)) { throw new ArgumentException("Unknown scope.", "scope"); } if (string.IsNullOrWhiteSpace(description)) { throw new ArgumentException("Expected a description.", "description"); } if (hidden && scope == SettingScope.Cheat) { throw new ArgumentException(section + "." + key + ": a cheat cannot be hidden; cheats stay visible for review.", "hidden"); } Section = section; Key = key; Scope = scope; Description = description; Choices = choices ?? NoChoices; Hidden = hidden; } public abstract object NormalizeBoxed(object? value); public override string ToString() { return Id + " (" + Scope.ToString() + (Hidden ? ", hidden" : "") + ")"; } public static TempestSetting<bool> Toggle(string section, string key, bool defaultValue, SettingScope scope, string description, bool? safe = null, bool hidden = false) { return new TempestSetting<bool>(section, key, scope, description, defaultValue, safe, null, (bool v) => v, null, hidden); } public static TempestSetting<int> Integer(string section, string key, int defaultValue, int min, int max, SettingScope scope, string description) { if (min > max) { throw new ArgumentException("Minimum above maximum.", "min"); } return new TempestSetting<int>(section, key, scope, description, defaultValue, null, (min, max), (int v) => Math.Min(max, Math.Max(min, v))); } public static TempestSetting<float> Number(string section, string key, float defaultValue, float min, float max, SettingScope scope, string description) { if (!(min <= max) || float.IsInfinity(min) || float.IsInfinity(max)) { throw new ArgumentException("Expected a finite range.", "min"); } return new TempestSetting<float>(section, key, scope, description, defaultValue, null, (min, max), (float v) => (!float.IsNaN(v) && !float.IsInfinity(v)) ? Math.Min(max, Math.Max(min, v)) : defaultValue); } public static TempestSetting<TEnum> Choice<TEnum>(string section, string key, TEnum defaultValue, SettingScope scope, string description) where TEnum : struct, Enum { return new TempestSetting<TEnum>(section, key, scope, description, defaultValue, null, null, (TEnum v) => (!Enum.IsDefined(typeof(TEnum), v)) ? defaultValue : v, Enum.GetNames(typeof(TEnum))); } public static TempestSetting<string> Text(string section, string key, string defaultValue, SettingScope scope, string description, Func<string, string> normalize, bool hidden = false) { if (normalize == null) { throw new ArgumentNullException("normalize"); } return new TempestSetting<string>(section, key, scope, description, defaultValue, null, null, normalize, null, hidden); } } public sealed class TempestSetting<T> : TempestSetting { private readonly Func<T, T> normalize; private readonly bool hasSafe; private readonly (T Min, T Max)? range; public T Default { get; } public T Safe { get; } public override Type ValueType => typeof(T); public override object DefaultValue => Default; public override object? SafeValue { get { if (!hasSafe) { return null; } return Safe; } } public override object? Minimum { get { if (!range.HasValue) { return null; } return range.Value.Min; } } public override object? Maximum { get { if (!range.HasValue) { return null; } return range.Value.Max; } } internal TempestSetting(string section, string key, SettingScope scope, string description, T defaultValue, object? safe, (T, T)? range, Func<T, T> nor
plugins/Tempest/Tempest.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExtraPotions.Meadworks.Crucible; using ExtraPotions.Meadworks.Crucible.UI; using ExtraPotions.Tempest.Commands; using ExtraPotions.Tempest.Config; using ExtraPotions.Tempest.Conversion; using ExtraPotions.Tempest.Events; using ExtraPotions.Tempest.Integration; using ExtraPotions.Tempest.Localization; using ExtraPotions.Tempest.Weather; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.1.15.0")] [assembly: AssemblyInformationalVersion("0.1.15+570907621cf8cf31bf960deb7d50bfbe8f5ca425")] [assembly: AssemblyProduct("Tempest")] [assembly: AssemblyTitle("Tempest")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.15.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 ExtraPotions.Tempest.Runtime { public sealed class TempestCommand : ConsoleCommand { private readonly string name; public override string Name => name; public override string Help => "Tempest weather events: status, blizzard [now], storm [now] [lightning], lightningstorm [now], fog [now], ashstorm [now], meteorshower [now], stop, override <environment> [freeze], override reset, import [apply]."; public TempestCommand(string name) { this.name = name; } public override void Run(string[] args) { foreach (string item in Execute(args)) { Print(item); } } private static void Print(string line) { Console instance = Console.instance; if (instance != null) { instance.Print(line); } } private static IEnumerable<string> Execute(string[] args) { ParsedCommand val = CommandParser.Parse(args); TempestConfig settings = Plugin.Settings; if ((int)val.Verb == 0) { yield return TempestText.Text("cmd_help"); yield break; } if ((int)val.Verb == 10 || (int)val.Verb == 11) { TempestImport import = Plugin.Import; yield return (import == null) ? TempestText.Text("cmd_inactive") : import.Run((int)val.Verb == 11); yield break; } if (settings == null || (Object)(object)ZNet.instance == (Object)null || !TempestModules.ProductActive(out var _)) { yield return TempestText.Text("cmd_inactive"); yield break; } if (!TempestModules.Active((TempestModule)(((int)val.Verb == 5 || (int)val.Verb == 6) ? 2 : 0))) { yield return TempestText.Text("cmd_inactive"); yield break; } CommandRefusal val2 = CommandRules.Decide(val.Verb, Facts(), settings.Get<bool>(TempestSettings.Cheats.WeatherCommands), settings.Get<bool>(TempestSettings.Cheats.PersonalOverride)); if ((int)val2 != 0) { yield return TempestText.Text(((int)val2 == 2) ? "cmd_headless" : "cmd_cheat_off"); yield break; } if ((int)CommandRules.Route(val.Verb, Facts()) == 1) { TempestRemoteCommands.Send(val.Verb, val.Now, val.Dry); yield return TempestText.Text("cmd_sent"); yield break; } CommandVerb verb = val.Verb; switch (verb - 1) { case 0: { TempestEvent current = TempestWorld.Current; PhaseState val3 = ((current == null) ? PhaseState.Idle : Timeline.At(current, TempestWorld.Now)); if (current == null || (int)((PhaseState)(ref val3)).Phase == 0) { yield return TempestText.Text("cmd_status_idle"); break; } yield return TempestText.Format("cmd_status_event", TempestText.Text(TempestTextKeys.Kind(current)), TempestText.Text(TempestTextKeys.Phase(((PhaseState)(ref val3)).Phase)), TempestTextKeys.Countdown(((PhaseState)(ref val3)).SecondsLeft)); break; } case 1: case 2: case 6: case 7: case 8: { EventKind value = val.Kind.Value; if (!settings.TryUseCheat(TempestSettings.Cheats.WeatherCommands)) { yield return TempestText.Text("cmd_cheat_off"); break; } EventKind started; SchedulerOutcome outcome2 = TempestWorld.Summon(value, val.Now, val.Dry, out started); yield return OutcomeText(outcome2, started); break; } case 3: { if (!settings.TryUseCheat(TempestSettings.Cheats.WeatherCommands)) { yield return TempestText.Text("cmd_cheat_off"); break; } EventKind kind; bool dry; SchedulerOutcome outcome = TempestWorld.Stop(out kind, out dry); yield return OutcomeText(outcome, kind, dry); break; } case 4: if (!settings.TryUseCheat(TempestSettings.Cheats.PersonalOverride)) { yield return TempestText.Text("cmd_cheat_off"); break; } yield return TempestSky.SetPersonal(val.Environment, val.Freeze) ? TempestText.Format("override_set", val.Environment) : TempestText.Format("override_unknown", val.Environment); break; case 5: TempestSky.ResetPersonal(); yield return TempestText.Text("override_reset"); break; } } public static string OutcomeText(SchedulerOutcome outcome, EventKind kind, bool dry = false) { //IL_0008: 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_0024: Expected I4, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) return (outcome - 1) switch { 0 => TempestText.Format("cmd_started", Kind(kind)), 2 => TempestText.Format("cmd_stopped", Kind(kind)), 3 => TempestText.Text("cmd_busy"), 4 => TempestText.Format("cmd_no_biome", Kind(kind)), _ => TempestText.Text("cmd_nothing"), }; string Kind(EventKind k) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return TempestText.Text(TempestTextKeys.Kind(k, dry)); } } private static SessionFacts Facts() { //IL_004c: 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) ZNet instance = ZNet.instance; if (instance.IsServer()) { return new SessionFacts((SessionKind)(instance.IsDedicated() ? 2 : (((Object)(object)Player.m_localPlayer != (Object)null && instance.GetPeers().Count > 0) ? 1 : 0)), (ServerKind)3, true); } return new SessionFacts((SessionKind)3, (ServerKind)3, Plugin.Session.IsAdmin); } } public static class TempestRemoteCommands { private sealed class Pending { internal Task<OperationResult<Reply>> Task; internal Request Request; internal int? SentSerial; internal float WaitUntil = -1f; } private sealed class Codec : IOperationCodec<Request, Reply> { public byte[] EncodeRequest(Request request) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return RemoteCommand.EncodeRequest(request); } public Request DecodeRequest(byte[] bytes) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return RemoteCommand.DecodeRequest(bytes); } public byte[] EncodeReply(Reply reply) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return RemoteCommand.EncodeReply(reply); } public Reply DecodeReply(byte[] bytes) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return RemoteCommand.DecodeReply(bytes); } } private static IDisposable? operation; private const float RecordWait = 3f; private static readonly List<Pending> pending = new List<Pending>(); private static MeadworksLog? log; public static void Initialize(ProductRegistration registration, MeadworksLog logger) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown log = logger; operation = Plugin.Services.Operations.Register<Request, Reply>(registration, new OperationDescriptor("meadworks.tempest.weather_command", RemoteCommand.OperationVersion), (IOperationCodec<Request, Reply>)new Codec(), (Func<AuthorityContext, Request, OperationExecution<Reply>>)Apply, (Func<RequestIdentity, OperationExecution<Reply>>)null); } public static void Send(CommandVerb verb, bool now, bool dry = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) Send(new Request(verb, now, dry)); } private static void Send(Request request) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) List<Pending> list = pending; Pending obj = new Pending { Task = Plugin.RequestClient.InvokeAsync<Request, Reply>("meadworks.tempest.weather_command", request, CancellationToken.None), Request = request }; TempestEvent? current = TempestWorld.Current; obj.SentSerial = ((current != null) ? new int?(current.Serial) : ((int?)null)); list.Add(obj); } public static void Tick() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) Pending[] array = TempestRemoteCommands.pending.ToArray(); foreach (Pending pending in array) { Task<OperationResult<Reply>> task = pending.Task; if (!task.IsCompleted) { continue; } TempestRemoteCommands.pending.Remove(pending); string text; if (task.Status != TaskStatus.RanToCompletion) { text = TempestText.Format("cmd_remote_failed", "Faulted"); } else { OperationResult<Reply> result = task.Result; if (RemoteCommand.DryUnsupported(pending.Request, (int)result.State == 3, result.Code)) { Console instance = Console.instance; if (instance != null) { instance.Print(TempestText.Text("cmd_dry_unsupported")); } Send(new Request(((Request)(ref pending.Request)).Verb, ((Request)(ref pending.Request)).Now, false)); continue; } TempestEvent? current = TempestWorld.Current; EventKind? val = ((current != null) ? new EventKind?(current.Kind) : ((EventKind?)null)); if ((int)result.State == 2 && RemoteCommand.AwaitsRecord(pending.Request, result.Reply)) { TempestEvent current2 = TempestWorld.Current; bool flag = current2 != null && current2.Serial != pending.SentSerial; if (!flag) { if (pending.WaitUntil < 0f) { pending.WaitUntil = Time.unscaledTime + 3f; } if (Time.unscaledTime < pending.WaitUntil) { TempestRemoteCommands.pending.Add(pending); continue; } } val = (flag ? new EventKind?(current2.Kind) : ((EventKind?)null)); } object obj; if ((int)result.State != 2) { obj = ((RemoteCommand.TextKey(result.Code) == "cmd_remote_failed") ? TempestText.Format("cmd_remote_failed", result.Code) : TempestText.Text(RemoteCommand.TextKey(result.Code))); } else { Reply reply = result.Reply; obj = TempestCommand.OutcomeText(((Reply)(ref reply)).Outcome, RemoteCommand.Shown(pending.Request, result.Reply, val)); } text = (string)obj; } Console instance2 = Console.instance; if (instance2 != null) { instance2.Print(text); } } } public static void Shutdown() { operation?.Dispose(); operation = null; pending.Clear(); log = null; } private static OperationExecution<Reply> Apply(AuthorityContext context, Request request) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) TempestConfig settings = Plugin.Settings; bool num = settings != null && TempestWorld.IsAuthority && TempestModules.Active((TempestModule)0); bool flag = settings != null && ((ConfigEntry<bool>)(object)settings.Entry((TempestSetting)(object)TempestSettings.Cheats.WeatherCommands)).Value; bool flag2 = (context.IsLocal ? MeadworksMischief.Decide("com.meadworks.tempest", (MischiefCategory)2).Allowed : MeadworksMischief.DecideForPeer(PeerRpc(context.SenderId), (MischiefCategory)2).Allowed); string text = RemoteCommand.Refusal(num, flag, context.IsAdmin, flag2); if (text != null) { MeadworksLog? obj = log; if (obj != null) { obj.Info("Weather command from " + context.SenderId + " refused: " + text + "."); } return OperationExecution<Reply>.Rejected(text); } context.MarkDecisionMade(); EventKind kind; bool dry; if ((int)((Request)(ref request)).Verb == 4) { return OperationExecution<Reply>.Success(new Reply(TempestWorld.Stop(out kind, out dry), kind)); } EventKind value = CommandParser.KindOf(((Request)(ref request)).Verb, ((Request)(ref request)).Dry).Value; MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Info("Weather command from " + context.SenderId + ": " + ((object)((Request)(ref request)).Verb/*cast due to .constrained prefix*/).ToString() + (((Request)(ref request)).Now ? " now" : "") + (((Request)(ref request)).Dry ? " lightning" : "") + "."); } EventKind started; return OperationExecution<Reply>.Success(new Reply(TempestWorld.Summon(value, ((Request)(ref request)).Now, ((Request)(ref request)).Dry, out started), started)); } private static ZRpc? PeerRpc(string senderId) { if ((Object)(object)ZNet.instance == (Object)null || !long.TryParse(senderId, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var uid)) { return null; } return ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p != null && p.m_uid == uid))?.m_rpc; } } public sealed class TempestConfig { private const string Product = "com.meadworks.tempest"; private readonly ConfigFile file; private readonly Dictionary<TempestSetting, ConfigEntryBase> entries = new Dictionary<TempestSetting, ConfigEntryBase>(); private readonly HashSet<string> droppedLogged = new HashSet<string>(StringComparer.Ordinal); private MeadworksLog? log; private readonly List<string> upgradeNotes = new List<string>(); public ConfigFile File => file; public event Action? Changed; private TempestConfig(ConfigFile file) { this.file = file; bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { foreach (TempestSetting item in TempestSettings.All) { BindSetting(item); } } finally { file.SaveOnConfigSet = saveOnConfigSet; } file.Save(); } public static TempestConfig Bind(ConfigFile file, MeadworksLog log) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (file == null) { throw new ArgumentNullException("file"); } if (log == null) { throw new ArgumentNullException("log"); } TempestConfig tempestConfig = new TempestConfig(file) { log = log }; foreach (string upgradeNote in tempestConfig.upgradeNotes) { log.Info(upgradeNote); } foreach (KeyValuePair<TempestSetting, ConfigEntryBase> item in tempestConfig.entries.Where<KeyValuePair<TempestSetting, ConfigEntryBase>>((KeyValuePair<TempestSetting, ConfigEntryBase> p) => (int)p.Key.Scope != 0 && !p.Key.Hidden)) { MeadworksBlockedOptions.Track("com.meadworks.tempest", item.Value, Map(item.Key.Scope), true); } file.SettingChanged += tempestConfig.OnSettingChanged; file.ConfigReloaded += tempestConfig.OnReloaded; log.Verbose("Configuration bound: " + Count((SettingScope)0) + " Local, " + Count((SettingScope)1) + " ServerAuthoritative, " + Count((SettingScope)2) + " Cheat."); return tempestConfig; } public static ConfigScope Map(SettingScope scope) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown return (ConfigScope)((int)scope switch { 0 => 0, 1 => 1, 2 => 2, _ => throw new ArgumentException("Unknown scope.", "scope"), }); } public static MischiefCategory CategoryOf(TempestSetting cheat) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)TempestSettings.CategoryOf(cheat) == 1) { return (MischiefCategory)2; } return (MischiefCategory)0; } public ConfigEntryBase Entry(TempestSetting setting) { if (!entries.TryGetValue(setting ?? throw new ArgumentNullException("setting"), out ConfigEntryBase value)) { throw new ArgumentException("Not a Tempest setting.", "setting"); } return value; } public T Get<T>(TempestSetting<T> setting) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 ConfigEntry<T> val = (ConfigEntry<T>)(object)Entry((TempestSetting)(object)setting); SettingScope scope = ((TempestSetting)setting).Scope; if ((int)scope != 1) { if ((int)scope == 2) { return TempestCheats.Effective<T>(setting, CheatLocked(), val.Value); } return setting.Normalize(val.Value); } return setting.Normalize(MeadworksConfig.Effective<T>(val, "com.meadworks.tempest", (ConfigScope)1, setting.Default, true)); } public ScheduleRules Rules() { ScheduleSettings schedule = TempestSettings.Schedule; return TempestSettings.Rules(Get<bool>(schedule.Enabled), Get<float>(schedule.BlizzardChance), Get<float>(schedule.StormChance), Get<int>(schedule.MinimumDays), Get<int>(schedule.WarningSeconds), Get<int>(schedule.ActiveMinSeconds), Get<int>(schedule.ActiveMaxSeconds), Biomes(schedule.BlizzardBiomes, (EventKind)0), Biomes(schedule.StormBiomes, (EventKind)1), Get<float>(schedule.DryStormChance), Get<float>(schedule.FogChance), Biomes(schedule.FogBiomes, (EventKind)3), Get<float>(schedule.AshStormChance), Biomes(schedule.AshStormBiomes, (EventKind)4), Get<float>(schedule.MeteorShowerChance), Biomes(schedule.MeteorShowerBiomes, (EventKind)5)); } private string Biomes(TempestSetting<string> setting, EventKind kind) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) string text = MeadworksConfig.Effective<string>((ConfigEntry<string>)(object)Entry((TempestSetting)(object)setting), "com.meadworks.tempest", (ConfigScope)1, setting.Default, true); IReadOnlyList<string> readOnlyList = BiomeSets.Dropped(kind, text); if (readOnlyList.Count > 0 && droppedLogged.Add(((TempestSetting)setting).Key + "=" + text)) { MeadworksLog? obj = log; if (obj != null) { obj.Warning(((TempestSetting)setting).Key + ": dropped " + string.Join(", ", readOnlyList) + ". Tempest events never occur in the Mountain or Mistlands, and only a blizzard reaches the Deep North; this kind covers only " + BiomeSets.Format(BiomeSets.Allowed(kind)) + "."); } } return setting.Normalize(text); } public WeatherFilter Filter() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown FilterSettings f = TempestSettings.Filter; return new WeatherFilter((Func<WeatherSwitch, bool>)((WeatherSwitch s) => Get<bool>(f.Switches[s])), Get<string>(f.OtherDisabled), TempestSeasons.Weights(this)); } public bool TryUseCheat(TempestSetting<bool> cheat, string? label = null) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (cheat == null || (int)((TempestSetting)cheat).Scope != 2) { throw new ArgumentException("Expected a Tempest cheat.", "cheat"); } if (Get<bool>(cheat)) { return MeadworksBlockedOptions.TryUse("com.meadworks.tempest", ((TempestSetting)cheat).Id, (ConfigScope)2, label, true); } return false; } public void Shutdown() { file.SettingChanged -= OnSettingChanged; file.ConfigReloaded -= OnReloaded; foreach (ConfigEntryBase value in entries.Values) { MeadworksBlockedOptions.Untrack(value); } entries.Clear(); this.Changed = null; } private void OnSettingChanged(object sender, SettingChangedEventArgs e) { this.Changed?.Invoke(); } private void OnReloaded(object sender, EventArgs e) { this.Changed?.Invoke(); } private static bool CheatLocked() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 return (int)MeadworksAuthority.Decide("com.meadworks.tempest", (ConfigScope)2, true).Source == 2; } private void BindSetting(TempestSetting setting) { ConfigEntryBase value; if (!(setting is TempestSetting<bool> setting2)) { if (!(setting is TempestSetting<int> val)) { if (!(setting is TempestSetting<float> val2)) { if (!(setting is TempestSetting<string> setting3)) { if (!(setting is TempestSetting<FreezeLevel> setting4)) { throw new NotSupportedException(setting.Id + ": unsupported value type " + setting.ValueType.Name + "."); } value = (ConfigEntryBase)(object)Bind<FreezeLevel>(setting4, null); } else { value = (ConfigEntryBase)(object)Upgrade(setting3, Bind<string>(setting3, null)); } } else { value = (ConfigEntryBase)(object)Bind<float>(val2, (AcceptableValueBase?)(object)new AcceptableValueRange<float>((float)((TempestSetting)val2).Minimum, (float)((TempestSetting)val2).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<int>(val, (AcceptableValueBase?)(object)new AcceptableValueRange<int>((int)((TempestSetting)val).Minimum, (int)((TempestSetting)val).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<bool>(setting2, null); } entries.Add(setting, value); } private ConfigEntry<string> Upgrade(TempestSetting<string> setting, ConfigEntry<string> entry) { string text = DefaultUpgrades.Upgrade((TempestSetting)(object)setting, entry.Value); if (text != null) { entry.Value = text; upgradeNotes.Add(((TempestSetting)setting).Section + " / " + ((TempestSetting)setting).Key + ": saved earlier default updated to \"" + text + "\"."); } else if (DefaultUpgrades.MissesDeepNorth((TempestSetting)(object)setting, entry.Value)) { upgradeNotes.Add(((TempestSetting)setting).Section + " / " + ((TempestSetting)setting).Key + ": customized list kept; add DeepNorth to let blizzards reach the Deep North as a whiteout."); } return entry; } private ConfigDescription Describe(TempestSetting setting, AcceptableValueBase? acceptable) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown ConfigDependency val = null; if ((object)setting != TempestSettings.Schedule.Enabled && TempestSettings.Schedule.Entries().Contains(setting)) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(TempestSettings.Schedule.Enabled)), (Func<string>)(() => "Requires " + ((TempestSetting)TempestSettings.Schedule.Enabled).Section + ": " + ((TempestSetting)TempestSettings.Schedule.Enabled).Key + "."), (Func<bool>)null); } TempestSetting<bool> hazards = TempestSettings.Effects.HazardsAffectPlayers; if ((object)setting == TempestSettings.Effects.HazardDamage) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(hazards)), (Func<string>)(() => "Requires " + ((TempestSetting)hazards).Section + ": " + ((TempestSetting)hazards).Key + "."), (Func<bool>)null); } return new ConfigDescription(setting.Description, acceptable, (val == null) ? Array.Empty<object>() : new object[1] { val }); } private ConfigEntry<T> Bind<T>(TempestSetting<T> setting, AcceptableValueBase? acceptable) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if ((int)((TempestSetting)setting).Scope == 2) { return MeadworksConfig.BindCheat<T>(file, ((TempestSetting)setting).Section, ((TempestSetting)setting).Key, setting.Safe, ((TempestSetting)setting).Description, CategoryOf((TempestSetting)(object)setting), acceptable); } if (((TempestSetting)setting).Hidden) { return MeadworksConfig.BindHidden<T>(file, ((TempestSetting)setting).Section, ((TempestSetting)setting).Key, setting.Default, ((TempestSetting)setting).Description, Map(((TempestSetting)setting).Scope), acceptable); } return MeadworksConfig.Bind<T>(file, ((TempestSetting)setting).Section, ((TempestSetting)setting).Key, setting.Default, Describe((TempestSetting)(object)setting, acceptable), Map(((TempestSetting)setting).Scope)); } private static int Count(SettingScope scope) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return TempestSettings.All.Count((TempestSetting s) => s.Scope == scope && !s.Hidden); } } public static class TempestWorld { private const float TickSeconds = 1f; private const double DefaultDayLength = 1800.0; private static ScheduleState state = ScheduleState.Empty; private static string? lastText; private static string? lastMark; private static string? lastExtended; private static float sinceTick = 1f; private static int seenSerial; private static EventPhase seenPhase = (EventPhase)0; private static bool seenAffected; private static long world; private static MeadworksLog? log; private static ConfigEntry<string>? fallback; private static WorldSettingBinding? binding; private static ConfigEntry<string>? dryFallback; private static WorldSettingBinding? dryBinding; private static ConfigEntry<string>? extendedFallback; private static WorldSettingBinding? extendedBinding; public static ScheduleState State => state; public static TempestEvent? Current => state.Current; public static double Now { get { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0.0; } return ZNet.instance.GetTimeSeconds(); } } public static double DayLength { get { if (!((Object)(object)EnvMan.instance != (Object)null) || EnvMan.instance.m_dayLengthSec <= 0) { return 1800.0; } return EnvMan.instance.m_dayLengthSec; } } public static bool IsAuthority { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public static WeatherBiome LocalBiome { get { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return (WeatherBiome)0; } return Mask((int)Player.m_localPlayer.GetCurrentBiome()); } } public static VanillaEventArea? VanillaArea { get { //IL_0038: Unknown result type (might be due to invalid IL or missing references) RandEventSystem instance = RandEventSystem.instance; RandomEvent val = (((Object)(object)instance != (Object)null) ? instance.GetCurrentRandomEvent() : null); if (val != null) { return new VanillaEventArea(val.m_pos.x, val.m_pos.z, val.m_eventRange); } return null; } } public static bool LocalInVanillaEvent { get { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } RandEventSystem instance = RandEventSystem.instance; Vector3 position = ((Component)localPlayer).transform.position; return VanillaEvents.Covers((Object)(object)instance != (Object)null && instance.GetActiveEvent() != null, VanillaArea, position.x, position.y, position.z); } } private static string LastKind { get; set; } = TempestTextKeys.Kind((EventKind)0); public static bool InVanillaEvent(Vector3 at) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) return VanillaEvents.Inside(VanillaArea, at.x, at.y, at.z); } public static void Initialize(MeadworksLog logger, ConfigFile config) { log = logger; fallback = MeadworksConfig.BindHidden<string>(config, "World", "Event Record", "", "Managed by Tempest: required fallback of the guarded world key mw.tempest.event; the shared event lives in the world.", (ConfigScope)1, (AcceptableValueBase)null); binding = MeadworksWorldSettings.Register("com.meadworks.tempest", "mw.tempest.event", (ConfigEntryBase)(object)fallback, (Func<string, string>)EventRecord.Canonical); dryFallback = MeadworksConfig.BindHidden<string>(config, "World", "Lightning Storm Mark", "", "Managed by Tempest: required fallback of the guarded world key mw.tempest.lightningstorm; the lightning storm flag lives in the world.", (ConfigScope)1, (AcceptableValueBase)null); dryBinding = MeadworksWorldSettings.Register("com.meadworks.tempest", "mw.tempest.lightningstorm", (ConfigEntryBase)(object)dryFallback, (Func<string, string>)DryMark.Canonical); extendedFallback = MeadworksConfig.BindHidden<string>(config, "World", "Extended Event Record", "", "Managed by Tempest: required fallback of the guarded world key mw.tempest.extended; Dense Fog, Ash Storm and Meteor Shower live in the world.", (ConfigScope)1, (AcceptableValueBase)null); extendedBinding = MeadworksWorldSettings.Register("com.meadworks.tempest", "mw.tempest.extended", (ConfigEntryBase)(object)extendedFallback, (Func<string, string>)ExtendedRecord.Canonical); } public static void Tick(float deltaTime) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Invalid comparison between Unknown and I4 //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Invalid comparison between Unknown and I4 //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Invalid comparison between Unknown and I4 //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) sinceTick += deltaTime; if (sinceTick < 1f) { return; } sinceTick = 0f; TempestConfig settings = Plugin.Settings; if (settings == null || (Object)(object)ZNet.instance == (Object)null || ZNet.World == null || (Object)(object)ZoneSystem.instance == (Object)null || !TempestModules.Active((TempestModule)0)) { Clear(); return; } if (ZNet.instance.GetWorldUID() != world) { Clear(); world = ZNet.instance.GetWorldUID(); } Read(); if (IsAuthority) { SchedulerResult val = Scheduler.Tick(state, settings.Rules(), ZNet.instance.GetWorldUID(), Now, DayLength, Occupied(), Raided()); Write(((SchedulerResult)(ref val)).State); if ((int)((SchedulerResult)(ref val)).Outcome == 1) { MeadworksLog? obj = log; if (obj != null) { obj.Info("Weather event started: " + (object)((SchedulerResult)(ref val)).State.Current); } } else if ((int)((SchedulerResult)(ref val)).Outcome == 2 || (int)((SchedulerResult)(ref val)).Outcome == 3) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Info("Weather event ended (" + ((object)((SchedulerResult)(ref val)).Outcome/*cast due to .constrained prefix*/).ToString() + ")."); } } } Notices(settings); } public static SchedulerOutcome Summon(EventKind kind, bool skipWarning, bool dry = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) EventKind started; return Summon(kind, skipWarning, dry, out started); } public static SchedulerOutcome Summon(EventKind kind, bool skipWarning, bool dry, out EventKind started) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Expected I4, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected I4, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Invalid comparison between Unknown and I4 //IL_00ce: Unknown result type (might be due to invalid IL or missing references) started = (EventKind)(int)kind; TempestConfig settings = Plugin.Settings; if (settings == null || !IsAuthority || (Object)(object)ZoneSystem.instance == (Object)null) { return (SchedulerOutcome)5; } Read(); int num = (int)DateTime.UtcNow.Ticks ^ (int)(DateTime.UtcNow.Ticks >> 32); SchedulerResult val = Scheduler.Summon(state, settings.Rules(), kind, Now, skipWarning, num, DayLength, dry ? new bool?(true) : ((bool?)null)); started = (EventKind)(int)Scheduler.SummonedKind(val, kind); if ((int)((SchedulerResult)(ref val)).Outcome == 1) { Write(((SchedulerResult)(ref val)).State); MeadworksLog? obj = log; if (obj != null) { obj.Info("Weather event summoned: " + (object)((SchedulerResult)(ref val)).State.Current); } } return ((SchedulerResult)(ref val)).Outcome; } public static SchedulerOutcome Stop(out EventKind kind, out bool dry) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected I4, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) kind = (EventKind)0; dry = false; if (Plugin.Settings == null || !IsAuthority || (Object)(object)ZoneSystem.instance == (Object)null) { return (SchedulerOutcome)5; } Read(); if (state.Current != null) { kind = (EventKind)(int)state.Current.Kind; dry = state.Current.Dry; } SchedulerResult val = Scheduler.Stop(state, Now); if ((int)((SchedulerResult)(ref val)).Outcome == 3) { Write(((SchedulerResult)(ref val)).State); MeadworksLog? obj = log; if (obj != null) { obj.Info("Weather event stopped by command."); } } return ((SchedulerResult)(ref val)).Outcome; } public static void Shutdown() { Clear(); WorldSettingBinding? obj = binding; if (obj != null) { obj.Dispose(); } binding = null; fallback = null; WorldSettingBinding? obj2 = dryBinding; if (obj2 != null) { obj2.Dispose(); } dryBinding = null; dryFallback = null; WorldSettingBinding? obj3 = extendedBinding; if (obj3 != null) { obj3.Dispose(); } extendedBinding = null; extendedFallback = null; CairnWeather.Shutdown(); log = null; } private static void Clear() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) state = ScheduleState.Empty; lastText = null; lastMark = null; lastExtended = null; seenSerial = 0; seenPhase = (EventPhase)0; seenAffected = false; world = 0L; } private static void Read() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) string text2 = default(string); string text = (ZoneSystem.instance.GetGlobalKey("tempest.event", ref text2) ? text2 : null); WorldSettingBinding? obj = binding; Choice val = RecordMigration.Choose((obj != null) ? obj.WorldText : null, text); if (((Choice)(ref val)).Text == null) { if (!IsAuthority) { state = ScheduleState.Empty; lastText = null; lastMark = null; lastExtended = null; } return; } if (((Choice)(ref val)).Migrate && IsAuthority && binding != null && (int)binding.Set(((Choice)(ref val)).Text).Status != 0) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Info("Weather event record moved from tempest.event to mw.tempest.event (the old key is left as it was)."); } } WorldSettingBinding? obj3 = dryBinding; string text3 = DryMark.Canonical((obj3 != null) ? obj3.WorldText : null); WorldSettingBinding? obj4 = extendedBinding; string text4 = ExtendedRecord.Canonical((obj4 != null) ? obj4.WorldText : null); if ((!(((Choice)(ref val)).Text == lastText) || !(text3 == lastMark) || !(text4 == lastExtended)) && (!IsAuthority || !(((Choice)(ref val)).Text == lastText) || (!(text3 != DryMark.Encode(state.Current)) && !((text4 ?? "0") != ExtendedRecord.Encode(state.Current))))) { lastText = ((Choice)(ref val)).Text; lastMark = text3; lastExtended = text4; ScheduleState val2 = default(ScheduleState); state = (EventRecord.TryDecode(((Choice)(ref val)).Text, ref val2) ? ExtendedRecord.Apply(DryMark.Apply(val2, text3), text4) : ScheduleState.Empty); } } private static void Write(ScheduleState next) { //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Invalid comparison between Unknown and I4 //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Invalid comparison between Unknown and I4 //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Invalid comparison between Unknown and I4 //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Invalid comparison between Unknown and I4 //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Invalid comparison between Unknown and I4 //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if ((next.Equals(state) && lastText != null) || binding == null) { return; } string text = DryMark.Encode(next.Current); if (dryBinding != null && DryMark.Canonical(dryBinding.WorldText) != text && (!(text == "0") || dryBinding.WorldText != null)) { WorldSettingWriteResult val = dryBinding.Set(text); if ((int)val.Status != 4 && (int)val.Status != 3) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("Lightning storm mark not written: " + ((object)val.Status/*cast due to .constrained prefix*/).ToString() + "."); } } } string text2 = ExtendedRecord.Encode(next.Current); if (extendedBinding != null && ExtendedRecord.Canonical(extendedBinding.WorldText) != text2 && (!(text2 == "0") || extendedBinding.WorldText != null)) { WorldSettingWriteResult val2 = extendedBinding.Set(text2); if ((int)val2.Status != 4 && (int)val2.Status != 3) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Extended weather event not written: " + ((object)val2.Status/*cast due to .constrained prefix*/).ToString() + "."); } } } string text3 = EventRecord.Encode(next); WorldSettingWriteResult val3 = binding.Set(text3); if ((int)val3.Status != 4 && (int)val3.Status != 3) { MeadworksLog? obj3 = log; if (obj3 != null) { obj3.Warning("Weather event record not written: " + ((object)val3.Status/*cast due to .constrained prefix*/).ToString() + "."); } } else { state = next; lastText = text3; lastMark = text; lastExtended = text2; } } private static WeatherBiome Occupied() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected I4, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) WeatherBiome val = LocalBiome; WorldGenerator instance = WorldGenerator.instance; if (instance != null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.IsReady()) { val |= Mask((int)instance.GetBiome(peer.m_refPos)); } } } return val; } private static WeatherBiome Raided() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected I4, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) WeatherBiome val = (WeatherBiome)(LocalInVanillaEvent ? ((int)LocalBiome) : 0); WorldGenerator instance = WorldGenerator.instance; VanillaEventArea? vanillaArea = VanillaArea; if (instance != null && vanillaArea.HasValue) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.IsReady() && VanillaEvents.Inside(vanillaArea, peer.m_refPos.x, peer.m_refPos.y, peer.m_refPos.z)) { val |= Mask((int)instance.GetBiome(peer.m_refPos)); } } } return val; } private static WeatherBiome Mask(int biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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_0017: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) WeatherBiome val = (WeatherBiome)biome; WeatherBiome val2 = (WeatherBiome)0; foreach (WeatherBiome item in BiomeSets.All) { val2 |= item; } return (WeatherBiome)(val & val2); } private static void Notices(TempestConfig settings) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Invalid comparison between Unknown and I4 //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Invalid comparison between Unknown and I4 //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Invalid comparison between Unknown and I4 //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Invalid comparison between Unknown and I4 //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.AllowsUi || (Object)(object)Player.m_localPlayer == (Object)null) { return; } TempestEvent current = state.Current; double now = Now; PhaseState val; int num; if (current != null) { val = Timeline.At(current, now); num = (int)((PhaseState)(ref val)).Phase; } else { num = 0; } EventPhase val2 = (EventPhase)num; int num2 = ((current != null) ? current.Serial : 0); bool localInVanillaEvent = LocalInVanillaEvent; bool flag = current != null && (int)val2 != 0 && !localInVanillaEvent && current.Affects(LocalBiome); if (num2 == seenSerial && val2 == seenPhase) { seenAffected |= flag; return; } bool flag2 = seenAffected; string text = null; if (seenSerial != 0 && (int)seenPhase != 0 && (num2 != seenSerial || (int)val2 == 0) && flag2) { text = LastKind; } seenSerial = num2; seenPhase = val2; seenAffected = flag; if (current != null) { LastKind = TempestTextKeys.Kind(current); } if (localInVanillaEvent || !settings.Get<bool>(TempestSettings.Notices.WarningMessage) || (text == null && (!flag || current == null || (int)val2 == 3)) || !CenterNotice.Show(true, CairnWeather.DisplaysWeather(log))) { return; } if (text != null && (int)val2 == 0) { TempestText.Centre(TempestText.Format("event_ended", TempestText.Text(text)), log); } else if (flag && current != null) { string text2 = TempestText.Text(TempestTextKeys.Kind(current)); if ((int)val2 == 1) { object[] obj = new object[2] { text2, null }; val = Timeline.At(current, now); obj[1] = TempestTextKeys.Countdown(((PhaseState)(ref val)).SecondsUntilActive); TempestText.Centre(TempestText.Format("warning_started", obj), log); } else if ((int)val2 == 2) { TempestText.Centre(TempestText.Format("event_started", text2), log); } } } } public static class CairnWeather { private static bool warned; public static bool DisplaysWeather(MeadworksLog? log) { try { Func<bool> func = null; return Plugin.Services.Capabilities.TryResolve<Func<bool>>("meadworks.cairn.weather_display", new Version(1, 0), ref func) && func != null && func(); } catch (Exception ex) { if (!warned && log != null) { log.Warning("Cairn weather display was unavailable; Tempest keeps its own messages: " + ex.GetType().Name); } warned = true; return false; } } public static void Shutdown() { warned = false; } } public static class TempestBadge { private const float Width = 240f; private const float MaxWidth = 440f; private const float Pad = 12f; private const float Glyph = 22f; private const float Gap = 6f; private const float Height = 82f; private const float DefaultTop = 96f; private static readonly WarningCue Cue = new WarningCue(); private static KitHudPanel? panel; private static Transform? owner; private static TMP_Text? label; private static TMP_Text? detail; private static KitMeter? meter; private static Image? icon; private static bool warnedDisplay; private static string? shownTitle; private static string? shownLine; private static EventPhase shownPhase; private static long shownWhole = long.MinValue; private static bool warnedSound; private static MeadworksLog? log; public static void Initialize(MeadworksLog logger) { log = logger; } public static void Tick() { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Invalid comparison between Unknown and I4 if (!Plugin.AllowsUi) { return; } TempestConfig settings = Plugin.Settings; Player localPlayer = Player.m_localPlayer; if (settings == null || (Object)(object)localPlayer == (Object)null || !TempestModules.Active((TempestModule)0)) { Hide(); return; } TempestEvent current = TempestWorld.Current; double now = TempestWorld.Now; WeatherBiome localBiome = TempestWorld.LocalBiome; bool flag = (Object)(object)EnvMan.instance != (Object)null && !string.IsNullOrEmpty(EnvMan.instance.m_forceEnv); NoticeSettings notices = TempestSettings.Notices; if (Cue.Observe(current, now, localBiome, flag) && settings.Get<bool>(notices.WarningSound) && current != null) { PlaySound(current.Kind, ((Component)localPlayer).transform.position); } BadgeState val = Badge.For(current, now, localBiome, flag, settings.Get<bool>(notices.HudBadge), CairnDisplaysWeather()); Transform val2 = (((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_rootObject != (Object)null) ? Hud.instance.m_rootObject.transform : null); if (!((BadgeState)(ref val)).Visible || !CrucibleUiPlugin.Active || !((Object)(object)val2 != (Object)null) || !((Object)(object)KitCanvas.Font != (Object)null) || Hud.IsUserHidden() || TempestWindows.Open()) { Hide(); return; } if (panel != null && ((Object)(object)panel.Root == (Object)null || (Object)(object)owner != (Object)(object)val2)) { Reset(); } if (panel == null && !Build(val2)) { return; } string text = Badge.Title(val, (Func<string, string>)TempestText.Text); long num = TempestTextKeys.WholeSeconds(((BadgeState)(ref val)).SecondsLeft); if (shownTitle == null || text != shownTitle || ((BadgeState)(ref val)).Phase != shownPhase || num != shownWhole) { string text2 = Badge.Detail(val, (Func<string, string, string>)((string key, string value) => TempestText.Format(key, value))); if (text != shownTitle || text2 != shownLine) { label.text = text; detail.text = text2; Fit(); } shownTitle = text; shownLine = text2; shownPhase = ((BadgeState)(ref val)).Phase; shownWhole = num; } meter.Set((float)((BadgeState)(ref val)).Fraction, 1f, "", (Color?)null); icon.sprite = KitGlyphSprites.Of(GlyphOf(((BadgeState)(ref val)).Kind)); bool flag2 = (int)ZCursor.LockState != 1 && ZCursor.IsVisible; panel.Update(true, flag2, 1f, 1f); } public static void Shutdown() { Reset(); log = null; warnedDisplay = false; } private static bool CairnDisplaysWeather() { try { Func<bool> func = null; return Plugin.Services.Capabilities.TryResolve<Func<bool>>("meadworks.cairn.weather_display", new Version(1, 0), ref func) && func != null && func(); } catch (Exception ex) { if (!warnedDisplay) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("Cairn weather display was unavailable; Tempest keeps its badge: " + ex.GetType().Name); } } warnedDisplay = true; return false; } } private static void Fit() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) KitHudPanel? obj = panel; if ((Object)(object)((obj != null) ? obj.Root : null) == (Object)null || (Object)(object)label == (Object)null || (Object)(object)detail == (Object)null) { return; } KitMeter? obj2 = meter; if ((Object)(object)((obj2 != null) ? obj2.Root : null) == (Object)null || (Object)(object)icon == (Object)null) { return; } float x = label.GetPreferredValues(label.text, 10000f, 0f).x; float num = Mathf.Clamp(Mathf.Max(x + 22f + 6f + 24f, detail.GetPreferredValues(detail.text, 10000f, 0f).x + 24f), 240f, 440f); x = Mathf.Min(x, num - 24f - 22f - 6f); ((RectTransform)((Component)icon).transform).anchoredPosition = new Vector2((num - 22f - 6f - x) * 0.5f, -10f); RectTransform root = panel.Root; if (!(Mathf.Abs(num - root.sizeDelta.x) < 0.5f)) { Transform parent = ((Transform)root).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); float num2; if (val == null) { num2 = Screen.width; } else { Rect rect = val.rect; num2 = ((Rect)(ref rect)).width; } float num3 = num2; panel.SetPosition(new Vector2(Mathf.Max(0f, num3 * 0.5f - num * 0.5f), 96f)); root.sizeDelta = new Vector2(num, 82f); meter.Root.sizeDelta = new Vector2(num - 24f, meter.Root.sizeDelta.y); } } private static bool Build(Transform root) { //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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)(object)((root is RectTransform) ? root : null); float num; if (val == null) { num = Screen.width; } else { Rect rect = val.rect; num = ((Rect)(ref rect)).width; } float num2 = num; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(Mathf.Max(0f, num2 * 0.5f - 120f), 96f); KitHudPanel val3 = KitHudPanel.Create(root, "Tempest_Badge", new Vector2(240f, 82f), val2, "com.meadworks.tempest", "Tempest_Badge"); if (val3 == null) { return false; } panel = val3; owner = root; GameObject val4 = new GameObject("Tempest_BadgeGlyph", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val5 = (RectTransform)val4.transform; ((Transform)val5).SetParent((Transform)(object)val3.Content, false); Vector2 val6 = default(Vector2); ((Vector2)(ref val6))..ctor(0f, 1f); val5.pivot = val6; Vector2 anchorMin = (val5.anchorMax = val6); val5.anchorMin = anchorMin; val5.anchoredPosition = new Vector2(12f, -10f); val5.sizeDelta = new Vector2(22f, 22f); icon = val4.GetComponent<Image>(); icon.preserveAspect = true; ((Graphic)icon).raycastTarget = false; ((Graphic)icon).color = KitUnity.Colour(KitPalette.TabLitText); label = Line(val3.Content, "Tempest_BadgeText", 19f, 40f, -8f, -34f); detail = Line(val3.Content, "Tempest_BadgeTime", 16f, 12f, -36f, -56f); meter = KitMeter.Create(val3.Content, "Tempest_BadgeMeter", new Vector2(12f, -62f), new Vector2(216f, 12f)); if (meter == null) { Reset(); return false; } shownTitle = (shownLine = null); shownWhole = long.MinValue; return true; } private static TMP_Text Line(RectTransform parent, string name, float size, float left, float top, float bottom) { //IL_0009: 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) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = KitCanvas.Text((Transform)(object)parent, name, "", size, KitAppearance.Color((KitColorRole)6), (TextAlignmentOptions)4098, false); RectTransform rectTransform = obj.rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.offsetMin = new Vector2(left, bottom); rectTransform.offsetMax = new Vector2(-12f, top); obj.enableAutoSizing = true; obj.fontSizeMin = 12f; obj.fontSizeMax = size; return obj; } internal static KitGlyph GlyphOf(EventKind kind) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown switch ((int)kind) { case 0: return (KitGlyph)14; case 3: return (KitGlyph)21; case 4: case 5: return (KitGlyph)10; default: return (KitGlyph)15; } } private static void PlaySound(EventKind kind, Vector3 at) { //IL_0000: 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_0049: Unknown result type (might be due to invalid IL or missing references) GameObject val = FindSound(WarningSound.For(kind)) ?? FindSound("sfx_thunder"); if ((Object)(object)val == (Object)null) { if (!warnedSound) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("The game's warning sound was not found; the warning stays silent."); } } warnedSound = true; return; } try { TempestEffects.Track(Object.Instantiate<GameObject>(val, at, Quaternion.identity), activeOnly: false); } catch (Exception ex) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Warning sound failed: " + ex.GetType().Name); } } } private static GameObject? FindSound(string name) { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(name) : null); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)ObjectDB.instance != (Object)null) { foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects) { if ((Object)(object)statusEffect != (Object)null && (Object)(object)(val = InEffects(statusEffect.m_startEffects, name)) != (Object)null) { return val; } } } if ((Object)(object)EnvMan.instance != (Object)null) { foreach (EnvSetup environment in EnvMan.instance.m_environments) { if (environment == null) { continue; } if ((Object)(object)(val = InThunder(environment.m_envObject, name)) != (Object)null) { return val; } if (environment.m_psystems == null) { continue; } GameObject[] psystems = environment.m_psystems; for (int i = 0; i < psystems.Length; i++) { if ((Object)(object)(val = InThunder(psystems[i], name)) != (Object)null) { return val; } } } } return null; } private static GameObject? InThunder(GameObject? root, string name) { if ((Object)(object)root == (Object)null) { return null; } Thunder[] componentsInChildren = root.GetComponentsInChildren<Thunder>(true); for (int i = 0; i < componentsInChildren.Length; i++) { GameObject val = InEffects(componentsInChildren[i].m_thunderEffect, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static GameObject? InEffects(EffectList? effects, string name) { if (effects?.m_effectPrefabs == null) { return null; } EffectData[] effectPrefabs = effects.m_effectPrefabs; foreach (EffectData val in effectPrefabs) { if ((Object)(object)val?.m_prefab != (Object)null && WarningSound.Matches(((Object)val.m_prefab).name, name)) { return val.m_prefab; } } return null; } private static void Hide() { KitHudPanel? obj = panel; if (obj != null) { obj.Update(false, false, 1f, 1f); } } private static void Reset() { KitMeter? obj = meter; if (obj != null) { obj.Dispose(); } KitHudPanel? obj2 = panel; if (obj2 != null) { obj2.Dispose(); } meter = null; panel = null; owner = null; label = null; detail = null; icon = null; shownTitle = (shownLine = null); shownWhole = long.MinValue; } } public sealed class TempestImport : IDisposable { public const string Section = "Import"; private const string SourceName = "NoFogBruh"; private readonly ConfigFile config; private readonly TempestConfig settings; private readonly MeadworksLog log; private readonly ConfigEntry<string> marker; private readonly IDisposable registration; private bool disposed; private static string SourcePath => Path.Combine(Paths.ConfigPath, "vapok.mods.nofogbruh.cfg"); private static string ReportPath => Path.Combine(Paths.ConfigPath, "Meadworks", "tempest", "import-nofogbruh.txt"); private bool Imported => marker.Value.Split(',').Contains("NoFogBruh"); public bool Available { get { if (!disposed && File.Exists(SourcePath)) { return !Imported; } return false; } } public TempestImport(ConfigFile config, TempestConfig settings, MeadworksLog log) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown this.config = config; this.settings = settings; this.log = log; marker = MeadworksConfig.BindHidden<string>(config, "Import", "Imported Sources", "", "Managed by Tempest: sources already imported.", (ConfigScope)0, (AcceptableValueBase)null); ModuleSource noFogBruh = ModuleStepAside.NoFogBruh; registration = MeadworksConversion.Register(new ConversionRegistration("tempest.nofogbruh", "com.meadworks.tempest", noFogBruh.Guid, noFogBruh.Name, (Func<bool>)(() => Available), (Func<object>)(() => Preview()), (Func<object, IReadOnlyDictionary<string, string>, bool, object>)((object document, IReadOnlyDictionary<string, string> resolutions, bool backup) => Apply((ImportPlan)(((document is ImportPlan) ? document : null) ?? throw new ArgumentException("Wrong Tempest import preview.")), backup)))); } public string Run(bool apply) { if (!Available) { return TempestText.Text("import_unavailable"); } if (!MeadworksConfig.CanEdit((ConfigScope)1)) { return TempestText.Text("import_host_only"); } ImportPlan val = Preview(); if (!apply) { return TempestText.Format("import_preview", "NoFogBruh", val.Values.Count, val.OtherDisabled.Count, ReportPath); } return Apply(val, backup: true); } public ImportPlan Preview() { ImportPlan val = SourceImport.Plan(File.ReadAllText(SourcePath)); Directory.CreateDirectory(Path.GetDirectoryName(ReportPath)); File.WriteAllText(ReportPath, val.Report()); log.Info("Import preview for NoFogBruh: " + val.Values.Count + " switches, " + val.OtherDisabled.Count + " other environments, " + val.Notes.Count + " notes (" + ReportPath + ")."); return val; } private string Apply(ImportPlan plan, bool backup) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!backup) { throw new InvalidOperationException("Tempest import requires a backup."); } if (!MeadworksConfig.CanEdit((ConfigScope)1)) { throw new InvalidOperationException("The weather filter is set by the server."); } MeadworksConversion.Target(config).Backup(); bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; int num = 0; try { foreach (ImportValue value in plan.Values) { ImportValue current = value; TempestSetting val = TempestSettings.Find(((ImportValue)(ref current)).SettingId); if (val != null) { settings.Entry(val).BoxedValue = ((ImportValue)(ref current)).Value; num++; } } if (plan.OtherDisabled.Count > 0) { ConfigEntry<string> obj = (ConfigEntry<string>)(object)settings.Entry((TempestSetting)(object)TempestSettings.Filter.OtherDisabled); obj.Value = SourceImport.MergeOther(obj.Value, (IEnumerable<string>)plan.OtherDisabled); num += plan.OtherDisabled.Count; } marker.Value = string.Join(",", (from p in marker.Value.Split(',') where p.Length > 0 select p).Append("NoFogBruh").Distinct()); } finally { config.SaveOnConfigSet = saveOnConfigSet; } config.Save(); string text = TempestText.Format("import_done", num, "NoFogBruh"); log.Info(text); return text; } public void Dispose() { if (!disposed) { disposed = true; registration.Dispose(); } } } public static class TempestModules { private static ModuleStepAsideDecision? stepAside; private static long offNoticedSession = -1L; public static ModuleStepAsideDecision StepAside => stepAside ?? ModuleStepAside.Decide(Enumerable.Empty<InstalledPlugin>()); public static void Decide(MeadworksLog log) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown if (!Plugin.Services.StepAside.Declared("com.meadworks.tempest").Any()) { MeadworksStepAside.Declare("com.meadworks.tempest", "Tempest", ((IEnumerable<ModuleSource>)ModuleStepAside.Sources).Select((Func<ModuleSource, SourceMod>)((ModuleSource s) => new SourceMod(s.Guid, s.Name)))); } stepAside = ModuleStepAside.Decide(((IEnumerable<PluginInfo>)Chainloader.PluginInfos.Values).Select((Func<PluginInfo, InstalledPlugin>)((PluginInfo p) => new InstalledPlugin(p.Metadata.GUID, p.Metadata.Name)))); if (!stepAside.Any) { return; } foreach (StepAsideNotice notice in stepAside.Notices) { StepAsideNotice current = notice; Plugin.Services.StepAside.ReportModule(new ModuleStepAside("com.meadworks.tempest", ((object)((StepAsideNotice)(ref current)).Module/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(), new SourceMod(((StepAsideNotice)(ref current)).SourceGuid, ((StepAsideNotice)(ref current)).SourceName), ((StepAsideNotice)(ref current)).Key)); } log.Info(string.Join(" ", stepAside.Notices.Select((StepAsideNotice n) => TempestText.Format(((StepAsideNotice)(ref n)).Key, ((StepAsideNotice)(ref n)).SourceName)))); } public static bool ProductActive(out ConfigBlockReason reason) { reason = (ConfigBlockReason)0; if (Plugin.Ready) { return MeadworksAuthority.ProductActive("com.meadworks.tempest", ref reason); } return false; } public static bool Active(TempestModule module) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings != null && ProductActive(out var _)) { return !StepAside.SteppedAside(module); } return false; } public static void Tick(MeadworksLog log) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 if (Plugin.Ready && !((Object)(object)Player.m_localPlayer == (Object)null)) { SessionSnapshot session = Plugin.Session; if (offNoticedSession != session.Generation && session.AuthorityReady && !ProductActive(out var reason) && ((int)reason == 4 || (int)reason == 5)) { offNoticedSession = session.Generation; string text = TempestText.Text(((int)reason == 4) ? "off_no_crucible" : "off_no_tempest"); TempestText.Centre(text, log); log.Info(text); } } } public static void Reset() { //IL_001a: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (stepAside != null) { foreach (StepAsideNotice notice in stepAside.Notices) { StepAsideNotice current = notice; Plugin.Services.StepAside.WithdrawModule("com.meadworks.tempest", ((object)((StepAsideNotice)(ref current)).Module/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()); } } stepAside = null; offNoticedSession = -1L; } } public static class TempestSeasons { private static readonly Version ContractVersion = new Version(1, 0); private static readonly SeasonFactsReader Reader = new SeasonFactsReader((Func<Func<string>>)Resolve); private static Func<string>? Resolve() { Func<string> result = default(Func<string>); Plugin.Services.Capabilities.TryResolve<Func<string>>("meadworks.seasons.facts", ContractVersion, ref result); return result; } public static void Tick() { //IL_0005: 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) int num = EffectiveSeason(Reader.Reading); if (Reader.Poll(Time.unscaledTimeAsDouble) && num != EffectiveSeason(Reader.Reading)) { TempestFilter.Invalidate(); TempestFilter.Reapply(); } } private static int EffectiveSeason(SeasonReading reading) { if (!((SeasonReading)(ref reading)).Available || !((SeasonReading)(ref reading)).Effects) { return -1; } return ((SeasonReading)(ref reading)).Season; } public static SeasonalWeights? Weights(TempestConfig settings) { //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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown SeasonReading reading = Reader.Reading; if (!((SeasonReading)(ref reading)).Available || !((SeasonReading)(ref reading)).Effects) { return null; } return new SeasonalWeights(settings.Get<string>(TempestSettings.SeasonalWeather.Weights[((SeasonReading)(ref reading)).Season])); } internal static string FilterStamp(TempestConfig settings) { //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) SeasonReading reading = Reader.Reading; IReadOnlyList<TempestSetting<string>> weights = TempestSettings.SeasonalWeather.Weights; if (!((SeasonReading)(ref reading)).Available || !((SeasonReading)(ref reading)).Effects || (uint)((SeasonReading)(ref reading)).Season >= (uint)weights.Count) { return "off"; } return ((SeasonReading)(ref reading)).Season.ToString(CultureInfo.InvariantCulture) + ":" + settings.Get<string>(weights[((SeasonReading)(ref reading)).Season]); } public static string? EventEnvironment(string? name, Func<string, bool> exists) { //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) SeasonReading reading = Reader.Reading; if (!((SeasonReading)(ref reading)).Available || !((SeasonReading)(ref reading)).Effects) { return name; } return SeasonalEnvironments.ForSeason(name, ((SeasonReading)(ref reading)).Season, exists); } public static void Shutdown() { Reader.Reset(); } } public static class TempestText { private static MeadworksTranslations? catalog; public static ProductTranslations? Translations { get; private set; } public static void Initialize(BepInPlugin metadata, ManualLogSource log) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Assembly assembly = typeof(TempestText).Assembly; foreach (string item in MeadworksLanguages.Supported) { using Stream stream = assembly.GetManifestResourceStream("Tempest.localization." + item + ".json"); if (stream != null) { using StreamReader streamReader = new StreamReader(stream); dictionary.Add(item, streamReader.ReadToEnd()); } } Translations = new ProductTranslations("tempest", (IReadOnlyDictionary<string, string>)dictionary); catalog = MeadworksTranslations.Register(metadata, Translations, (string)null, log); } public static string Text(string key) { MeadworksTranslations? obj = catalog; return ((obj != null) ? obj.Text(key) : null) ?? key; } public static string Format(string key, params object[] arguments) { MeadworksTranslations? obj = catalog; return ((obj != null) ? obj.Format(key, arguments) : null) ?? key; } public static void Tick() { MeadworksTranslations? obj = catalog; if (obj != null) { obj.Update(); } } public static void Shutdown() { MeadworksTranslations? obj = catalog; if (obj != null) { obj.Dispose(); } catalog = null; Translations = null; } public static void Centre(string text, MeadworksLog log) { //IL_0043: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected I4, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown bool flag = MeadworksHeadless.AllowsUi && (Object)(object)MessageHud.instance != (Object)null; ProductRegistration val = (flag ? Plugin.Source : null); bool flag2 = val != null && val.IsActive && CrucibleUiPlugin.Active; NoticeRoute val2 = NoticeRoutes.For(flag, flag2, flag && TempestWindows.Open()); switch ((int)val2) { case 0: log.Info(text); break; case 1: MessageHud.instance.AddLog(text); break; case 2: KitNotices.Show(val, new KitNotice(text, (KitNoticeKind)1, "", 0, (Sprite)null, true, false, true)); break; default: MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); break; } } } public static class TempestWindows { public static bool Open() { if (!KitWindows.HudBlocked && !Minimap.IsOpen() && !InventoryGui.IsVisible() && !Compendium() && !Menu.IsVisible() && !StoreGui.IsVisible() && !TextInput.IsVisible()) { if ((Object)(object)TextViewer.instance != (Object)null) { return TextViewer.instance.IsVisible(); } return false; } return true; } private static bool Compendium() { TextsDialog val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_textsDialog : null); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject.activeInHierarchy; } return false; } } public static class WeatherCapability { private static readonly RevisionCounter Revisions = new RevisionCounter(); private static readonly RevisionCounter DetailRevisions = new RevisionCounter(); public static void Register(ProductRegistration registration) { registration.AddCapability<Func<Tuple<int, int, float, int, bool, int>>>("meadworks.weather.events", WeatherEventsView.ContractVersion, (Func<Tuple<int, int, float, int, bool, int>>)Provide); registration.AddCapability<Func<Tuple<int, int, float, int, bool, int, string>>>("meadworks.weather.events.detail", WeatherEventsDetail.ContractVersion, (Func<Tuple<int, int, float, int, bool, int, string>>)ProvideDetail); registration.AddCapability<Func<IReadOnlyList<string>>>("meadworks.diagnostics.com.meadworks.tempest", new Version(1, 0), (Func<IReadOnlyList<string>>)Describe); } public static WeatherEventsView Read() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!TempestModules.Active((TempestModule)0)) { return default(WeatherEventsView); } return WeatherEventsView.Read(TempestWorld.Current, TempestWorld.Now, TempestWorld.LocalBiome, Forced()); } private static bool Forced() { EnvMan instance = EnvMan.instance; if ((Object)(object)instance != (Object)null) { if (string.IsNullOrEmpty(instance.m_forceEnv)) { return !string.IsNullOrEmpty(instance.m_debugEnv); } return true; } return false; } private static Tuple<int, int, float, int, bool, int, string> ProvideDetail() { //IL_0022: 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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) WeatherEventsView val; string item; if (TempestModules.Active((TempestModule)0)) { (val, item) = WeatherEventsView.ReadDetail(TempestWorld.Current, TempestWorld.Now, TempestWorld.LocalBiome, Forced()); } else { val = default(WeatherEventsView); item = ""; } int item2 = DetailRevisions.Observe(val); return Tuple.Create(((WeatherEventsView)(ref val)).Kind, ((WeatherEventsView)(ref val)).Phase, ((WeatherEventsView)(ref val)).SecondsLeft, ((WeatherEventsView)(ref val)).Biomes, ((WeatherEventsView)(ref val)).LocalAffected, item2, item); } private static Tuple<int, int, float, int, bool, int> Provide() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) WeatherEventsView val = Read(); int item = Revisions.Observe(val); return Tuple.Create(((WeatherEventsView)(ref val)).Kind, ((WeatherEventsView)(ref val)).Phase, ((WeatherEventsView)(ref val)).SecondsLeft, ((WeatherEventsView)(ref val)).Biomes, ((WeatherEventsView)(ref val)).LocalAffected, item); } private static IReadOnlyList<string> Describe() { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); ModuleStepAsideDecision stepAside = TempestModules.StepAside; list.Add("modules: events " + (TempestModules.Active((TempestModule)0) ? "on" : "off") + ", filter " + (TempestModules.Active((TempestModule)1) ? "on" : "off") + ", override " + (TempestModules.Active((TempestModule)2) ? "on" : "off") + (stepAside.Any ? (" (steps aside: " + string.Join(", ", stepAside.Notices.Select((StepAsideNotice n) => ((StepAsideNotice)(ref n)).SourceName)) + ")") : "")); ScheduleState state = TempestWorld.State; TempestEvent current = state.Current; object item; if (current != null) { string? obj = ((object)current)?.ToString(); PhaseState val = Timeline.At(current, TempestWorld.Now); item = "event: " + obj + ", " + ((object)((PhaseState)(ref val)).Phase/*cast due to .constrained prefix*/).ToString(); } else { item = "event: none"; } list.Add((string)item); list.Add("last rolled day " + state.LastRolledDay + ", next serial " + state.NextSerial + ", applying " + (TempestSky.Applying ?? "-")); return list; } } [BepInPlugin("com.meadworks.tempest", "Tempest", "0.1.15")] [BepInDependency("com.meadworks.crucible", "0.3.30")] [BepInDependency("com.meadworks.crucible.ui", "0.3.21")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.meadworks.tempest"; public const string Name = "Tempest"; public const string Version = "0.1.15"; public const string Family = "Runestone"; private ProductRegistration? registration; private MeadworksLog? log; private static TempestImport? import; private Harmony? harmony; private bool started; internal static ProductRegistration? Source { get { if (!Chainloader.PluginInfos.TryGetValue("com.meadworks.tempest", out var value)) { return null; } return (value.Instance as Plugin)?.registration; } } internal static TempestImport? Import => import; public static bool Ready { get; private set; } public static bool AllowsUi { get; private set; } public static TempestConfig? Settings { get; private set; } private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown log = MeadworksLog.For(((BaseUnityPlugin)this).Logger, "Tempest"); try { if (!Plugin.IsReady) { throw new InvalidOperationException("Required Crucible is not ready"); } registration = Plugin.Services.Register(new ProductMetadata("com.meadworks.tempest", "Tempest", new Version("0.1.15"), new Version(0, 1), new Version(0, 1), (MultiplayerPolicy)2, "Runestone")); AllowsUi = MeadworksHeadless.AllowsUi; Settings = TempestConfig.Bind(((BaseUnityPlugin)this).Config, log); Settings.Changed += OnSettingsChanged; TempestText.Initialize(((BaseUnityPlugin)this).Info.Metadata, ((BaseUnityPlugin)this).Logger); Ready = true; log.Info(AllowsUi ? "Tempest registered; modules start after plugin discovery." : "Tempest registered on a headless process; no UI is created."); } catch (Exception ex) { log.Error((object)("Tempest initialization rejected: " + ex.GetType().Name + ": " + ex.Message)); Shutdown(); } } private void Start() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if (!Ready || registration == null || log == null) { return; } try { TempestModules.Decide(log); import = new TempestImport(((BaseUnityPlugin)this).Config, Settings, log); TempestWorld.Initialize(log, ((BaseUnityPlugin)this).Config); TempestSky.Initialize(log); TempestStrikes.Initialize(log); TempestBadge.Initialize(log); WeatherCapability.Register(registration); TempestRemoteCommands.Initialize(registration, log); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TempestCommand("tempest")); harmony = new Harmony("com.meadworks.tempest"); harmony.PatchAll(typeof(Plugin).Assembly); started = true; } catch (Exception ex) { log.Error((object)("Tempest modules rejected: " + ex.GetType().Name + ": " + ex.Message)); Shutdown(); } } private void Update() { if (Ready) { TempestText.Tick(); if (started && log != null) { TempestModules.Tick(log); TempestSeasons.Tick(); TempestWorld.Tick(Time.deltaTime); TempestSky.Tick(); TempestStrikes.Tick(); TempestBadge.Tick(); TempestRemoteCommands.Tick(); } } } private static void OnSettingsChanged() { TempestFilter.Invalidate(); TempestSky.UpdateFlags(); TempestFilter.Reapply(); } private void OnDestroy() { Shutdown(); } private void Shutdown() { Ready = false; started = false; try { Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } catch (Exception ex) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Tempest unpatch: " + ex.GetType().Name); } } harmony = null; Action[] array = new Action[10] { TempestRemoteCommands.Shutdown, TempestStrikes.Shutdown, TempestBadge.Shutdown, TempestSky.Shutdown, TempestSeasons.Shutdown, TempestFilter.Shutdown, TempestWorld.Shutdown, delegate { import?.Dispose(); }, TempestModules.Reset, TempestText.Shutdown }; foreach (Action action in array) { try { action(); } catch (Exception ex2) { MeadworksLog? obj3 = log; if (obj3 != null) { obj3.Warning("Tempest teardown: " + ex2.GetType().Name); } } } import = null; Settings?.Shutdown(); Settings = null; ProductRegistration? obj4 = registration; if (obj4 != null) { obj4.Dispose(); } registration = null; } } internal static class TempestEffects { private static readonly List<(GameObject Root, int Serial, bool ActiveOnly)> Owned = new List<(GameObject, int, bool)>(); internal static void Track(GameObject root, bool activeOnly) { TempestEvent current = TempestWorld.Current; if ((Object)(object)root != (Object)null && current != null) { Owned.Add((root, current.Serial, activeOnly)); } } internal static void Tick() { TempestEvent current = TempestWorld.Current; double now = TempestWorld.Now; bool flag = TempestModules.Active((TempestModule)0); for (int num = Owned.Count - 1; num >= 0; num--) { (GameObject, int, bool) tuple = Owned[num]; if ((Object)(object)tuple.Item1 == (Object)null) { Owned.RemoveAt(num); } else if (!flag || current == null || current.Serial != tuple.Item2 || !(now < (tuple.Item3 ? current.ClearingStart : current.End))) { Release(tuple.Item1); Owned.RemoveAt(num); } } } internal static void Clear() { foreach (var item in Owned) { if ((Object)(object)item.Root != (Object)null) { Release(item.Root); } } Owned.Clear(); } private static void Release(GameObject root) { ZSFX[] componentsInChildren = root.GetComponentsInChildren<ZSFX>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].Stop(); } AudioSource[] componentsInChildren2 = root.GetComponentsInChildren<AudioSource>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].Stop(); } root.SetActive(false); ZNetView component = root.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { if (component.IsOwner() && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(root); } } else { Object.Destroy((Object)(object)root); } } } public static class TempestFilter { [HarmonyPatch(typeof(EnvMan), "GetAvailableEnvironments")] private static class GetAvailableEnvironmentsPatch { [HarmonyAfter(new string[] { "com.meadworks.solstice", "com.extrapotions.solstice" })] private static void Postfix(EnvMan __instance, BiomeSector biome, ref List<EnvEntry> __result) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown WeatherFilter filter = Current; if (__result == null || __result.Count == 0 || filter == null || __result.All((EnvEntry e) => e == null || (Allows(e.m_environment) && filter.Weight(e.m_environment) == 1f))) { return; } List<EnvEntry> list = new List<EnvEntry>(__result.Count); foreach (EnvEntry item in __result) { if (item != null && Allows(item.m_environment)) { float num = filter.Weight(item.m_environment); list.Add((EnvEntry)((num == 1f) ? ((object)item) : ((object)new EnvEntry { m_environment = item.m_environment, m_env = item.m_env, m_weigh