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 Solstice v0.1.5
plugins/Solstice/Solstice.Core.dll
Decompiled 2 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.Text; using ExtraPotions.Solstice.Calendar; using ExtraPotions.Solstice.Effects; 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.5.0")] [assembly: AssemblyInformationalVersion("0.1.5")] [assembly: AssemblyProduct("Solstice.Core")] [assembly: AssemblyTitle("Solstice.Core")] [assembly: AssemblyVersion("0.1.5.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.Solstice { public enum LegacyFileOutcome { None, Migrated, KeptNew, Failed } public sealed class LegacyFileResult { public LegacyFileOutcome Outcome { get; } public string? Backup { get; } public string? Problem { get; } internal LegacyFileResult(LegacyFileOutcome outcome, string? backup = null, string? problem = null) { Outcome = outcome; Backup = backup; Problem = problem; } } public static class LegacyFileMigration { public static LegacyFileResult PrepareSettings(string directory, string legacyName, string currentName) { LegacyFileResult legacyFileResult = Run(directory, legacyName, currentName); if (legacyFileResult.Outcome == LegacyFileOutcome.Failed) { throw new IOException("Legacy settings could not be carried over; initialization stopped before defaults were saved. " + legacyFileResult.Problem); } return legacyFileResult; } public static LegacyFileResult Run(string directory, string legacyName, string currentName) { if (string.IsNullOrEmpty(directory)) { throw new ArgumentException("A config directory is required.", "directory"); } if (string.IsNullOrEmpty(legacyName) || string.IsNullOrEmpty(currentName) || string.Equals(legacyName, currentName, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Two different file names are required."); } string text = Path.Combine(directory, legacyName); string text2 = Path.Combine(directory, currentName); if (!File.Exists(text)) { return new LegacyFileResult(LegacyFileOutcome.None); } if (File.Exists(text2)) { return new LegacyFileResult(LegacyFileOutcome.KeptNew); } string text3 = text2 + ".migrate-" + Guid.NewGuid().ToString("N"); try { byte[] array = File.ReadAllBytes(text); File.WriteAllBytes(text3, array); if (!((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)File.ReadAllBytes(text3))) { throw new IOException("The copied settings failed readback."); } if (File.Exists(text2)) { return new LegacyFileResult(LegacyFileOutcome.KeptNew); } File.Move(text3, text2); } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { return File.Exists(text2) ? new LegacyFileResult(LegacyFileOutcome.KeptNew) : new LegacyFileResult(LegacyFileOutcome.Failed, null, ex.Message); } finally { try { if (File.Exists(text3)) { File.Delete(text3); } } catch (IOException) { } catch (UnauthorizedAccessException) { } } try { string text4 = FreeBackupName(text); File.Move(text, text4); return new LegacyFileResult(LegacyFileOutcome.Migrated, text4); } catch (Exception ex4) when (ex4 is IOException || ex4 is UnauthorizedAccessException) { return new LegacyFileResult(LegacyFileOutcome.Migrated, null, "The old file kept its name: " + ex4.Message); } } private static string FreeBackupName(string path) { string text = path + ".bak"; if (!File.Exists(text)) { return text; } for (int i = 1; i < 1000; i++) { string text2 = path + "." + i + ".bak"; if (!File.Exists(text2)) { return text2; } } throw new IOException("No free backup name for " + Path.GetFileName(path) + "."); } } [Flags] public enum SeasonFrameSystem { None = 0, WeatherListSwap = 1, EnvironmentVariants = 2, MusicInvalidate = 4, VisualsRepaint = 8, MinimapTint = 0x10 } public static class SeasonFrame { private static readonly Stopwatch clock = Stopwatch.StartNew(); public static SeasonFrameSystem Ran { get; private set; } public static bool DayMoved { get; set; } public static bool EnabledToggled { get; set; } public static long WeatherSwapTicks { get; private set; } public static long MusicTicks { get; private set; } public static void Mark(SeasonFrameSystem system) { Ran |= system; } public static long Stamp() { return clock.ElapsedTicks; } public static void Add(SeasonFrameSystem system, long startedAt) { Ran |= system; long num = clock.ElapsedTicks - startedAt; if (num < 0) { num = 0L; } switch (system) { case SeasonFrameSystem.WeatherListSwap: WeatherSwapTicks += num; break; case SeasonFrameSystem.MusicInvalidate: MusicTicks += num; break; } } public static void ClearWork() { Ran = SeasonFrameSystem.None; WeatherSwapTicks = 0L; MusicTicks = 0L; } public static int Milliseconds(long ticks) { if (ticks <= 0) { return 0; } long frequency = Stopwatch.Frequency; if (frequency <= 0) { return 0; } return (int)(ticks * 1000 / frequency); } public static string Describe(SeasonFrameSystem systems) { if (systems == SeasonFrameSystem.None) { return "none"; } string text = ""; if ((systems & SeasonFrameSystem.WeatherListSwap) != SeasonFrameSystem.None) { text = Join(text, "weather list swap"); } if ((systems & SeasonFrameSystem.EnvironmentVariants) != SeasonFrameSystem.None) { text = Join(text, "environment variants"); } if ((systems & SeasonFrameSystem.MusicInvalidate) != SeasonFrameSystem.None) { text = Join(text, "music invalidate"); } if ((systems & SeasonFrameSystem.VisualsRepaint) != SeasonFrameSystem.None) { text = Join(text, "visuals repaint"); } if ((systems & SeasonFrameSystem.MinimapTint) != SeasonFrameSystem.None) { text = Join(text, "minimap tint"); } if (text.Length != 0) { return text; } return "none"; } private static string Join(string text, string name) { if (text.Length != 0) { return text + ", " + name; } return name; } } public static class SolsticeCore { public const string PluginGuid = "com.meadworks.solstice"; public const string LegacyPluginGuid = "com.extrapotions.solstice"; public const string ProductName = "Solstice"; public const string ProductVersion = "0.1.5"; public const string Family = "Runestone"; public const string TokenNamespace = "solstice"; public const string TokenPrefix = "meadworks_solstice_"; public const string SupportedGameVersion = "1.0.17"; } public static class TickGuard { public const double WindowSeconds = 60.0; public static bool Report(ref double nextReportAt, double nowSeconds) { if (nowSeconds < nextReportAt) { return false; } nextReportAt = nowSeconds + 60.0; return true; } public static void Run(Action tick, ref double nextReportAt, double nowSeconds, Action<Exception> report) { if (tick == null) { return; } try { tick(); } catch (Exception obj) { if (Report(ref nextReportAt, nowSeconds)) { report?.Invoke(obj); } } } } } namespace ExtraPotions.Solstice.Weather { public sealed class EnvironmentWeight { public string Name { get; } public float Weight { get; } public object? Source { get; } public EnvironmentWeight(string name, float weight, object? source = null) { Name = name ?? ""; Weight = weight; Source = source; } } public sealed class EnvironmentAdd { public string Biome { get; } public string Environment { get; } public float Weight { get; } public EnvironmentAdd(string biome, string environment, float weight) { Biome = biome; Environment = environment; Weight = weight; } } public sealed class EnvironmentReplace { public string Environment { get; } public string ReplaceTo { get; } public EnvironmentReplace(string environment, string replaceTo) { Environment = environment; ReplaceTo = replaceTo; } } public sealed class EnvironmentRemove { public string Biome { get; } public string Environment { get; } public EnvironmentRemove(string biome, string environment) { Biome = biome; Environment = environment; } } public sealed class SeasonEnvironmentRules { public List<EnvironmentAdd> Add { get; } = new List<EnvironmentAdd>(); public List<EnvironmentReplace> Replace { get; } = new List<EnvironmentReplace>(); public List<EnvironmentRemove> Remove { get; } = new List<EnvironmentRemove>(); public int Count => Add.Count + Replace.Count + Remove.Count; } public static class SeasonBiomeEnvironments { public static SeasonEnvironmentRules For(Season season) { SeasonEnvironmentRules seasonEnvironmentRules = new SeasonEnvironmentRules(); switch (season) { case Season.Summer: seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Clear", "Clear Summer")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Misty", "Misty Summer")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("DeepForest Mist", "DeepForest Mist Summer")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Mistlands_clear", "Mistlands_clear Summer")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Heath clear", "Heath clear Summer")); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Meadows", "Heath clear", 2f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Black forest", "LightRain", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Black forest", "Clear", 0.2f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Swamp", "SwampRain Summer", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Swamp", "Swamp Summer", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mountain", "Twilight_Clear", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mountain", "Twilight_Snow", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "ThunderStorm", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Ocean", "Heath clear", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mistlands", "Heath clear", 0.5f)); break; case Season.Fall: seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("ThunderStorm", "ThunderStorm Fall")); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Meadows", "DeepForest Mist", 0.2f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Meadows", "SwampRain Fall", 0.2f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Black forest", "LightRain", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Black forest", "SwampRain Fall", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Swamp", "ThunderStorm", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mountain", "Twilight_SnowStorm", 0.5f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "Rain", 0.4f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "ThunderStorm", 0.2f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "SwampRain Fall", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Ocean", "SwampRain Fall", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Ocean", "DeepForest Mist", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mistlands", "SwampRain Fall", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mistlands", "DeepForest Mist", 0.1f)); break; case Season.Winter: seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Rain", "Rain Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("LightRain", "LightRain Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("ThunderStorm", "ThunderStorm Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Clear", "Clear Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Misty", "Misty Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("DeepForest Mist", "DeepForest Mist Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("SwampRain", "SwampRain Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Mistlands_clear", "Mistlands_clear Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Mistlands_rain", "Mistlands_rain Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Mistlands_thunder", "Mistlands_thunder Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Heath clear", "Heath clear Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("Snow", "Snow Winter")); seasonEnvironmentRules.Replace.Add(new EnvironmentReplace("SnowStorm", "SnowStorm Winter")); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Meadows", "Snow", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Black forest", "Snow", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mountain", "Twilight_Snow", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mountain", "Twilight_SnowStorm", 1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "Snow", 0.5f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Plains", "Twilight_SnowStorm", 0.3f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Ocean", "Darklands_dark Winter", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mistlands", "Twilight_Snow", 0.1f)); seasonEnvironmentRules.Add.Add(new EnvironmentAdd("Mistlands", "Twilight_SnowStorm", 0.1f)); seasonEnvironmentRules.Remove.Add(new EnvironmentRemove("Meadows", "Rain Winter")); seasonEnvironmentRules.Remove.Add(new EnvironmentRemove("Meadows", "LightRain Winter")); seasonEnvironmentRules.Remove.Add(new EnvironmentRemove("Meadows", "ThunderStorm Winter")); break; default: throw new ArgumentOutOfRangeException("season"); case Season.Spring: break; } return seasonEnvironmentRules; } } public static class EnvironmentRules { public const string TempestPrefix = "Tempest_"; public static bool IsForeign(string? environment) { return environment?.StartsWith("Tempest_", StringComparison.Ordinal) ?? false; } public static List<EnvironmentWeight> Apply(SeasonEnvironmentRules rules, string setupName, string enumName, IEnumerable<EnvironmentWeight> baseline, Func<string, bool> exists, Action<string>? onSkipped = null) { if (rules == null) { throw new ArgumentNullException("rules"); } if (baseline == null) { throw new ArgumentNullException("baseline"); } if (exists == null) { throw new ArgumentNullException("exists"); } List<EnvironmentWeight> list = (from w in baseline where w != null select new EnvironmentWeight(w.Name, w.Weight, w.Source)).ToList(); foreach (EnvironmentAdd item in rules.Add) { if (item != null && !string.IsNullOrWhiteSpace(item.Environment) && !IsForeign(item.Environment) && BiomeMatches(item.Biome, setupName, enumName)) { if (!exists(item.Environment)) { onSkipped?.Invoke(item.Environment); } else { list.Add(new EnvironmentWeight(item.Environment, item.Weight)); } } } foreach (EnvironmentReplace item2 in rules.Replace) { if (item2 == null || string.IsNullOrWhiteSpace(item2.Environment) || string.IsNullOrWhiteSpace(item2.ReplaceTo) || IsForeign(item2.Environment) || IsForeign(item2.ReplaceTo)) { continue; } bool flag = false; for (int num = 0; num < list.Count; num++) { if (!string.Equals(list[num].Name, item2.Environment, StringComparison.Ordinal)) { continue; } if (!exists(item2.ReplaceTo)) { if (!flag) { onSkipped?.Invoke(item2.ReplaceTo); } flag = true; } else { list[num] = new EnvironmentWeight(item2.ReplaceTo, list[num].Weight); } } } foreach (EnvironmentRemove remove in rules.Remove) { if (remove != null && !string.IsNullOrWhiteSpace(remove.Environment) && !IsForeign(remove.Environment) && BiomeMatches(remove.Biome, setupName, enumName)) { list.RemoveAll((EnvironmentWeight w) => string.Equals(w.Name, remove.Environment, StringComparison.Ordinal)); } } return list; } public static bool BiomeMatches(string? configuredName, string? setupName, string? enumName) { if (string.IsNullOrWhiteSpace(configuredName)) { return false; } string a = Normalize(configuredName); if (!string.IsNullOrWhiteSpace(setupName) && string.Equals(a, Normalize(setupName), StringComparison.OrdinalIgnoreCase)) { return true; } if (!string.IsNullOrWhiteSpace(enumName)) { return string.Equals(a, Normalize(enumName), StringComparison.OrdinalIgnoreCase); } return false; } private static string Normalize(string? name) { if (!string.IsNullOrWhiteSpace(name)) { return name.Replace(" ", "").Replace("_", "").Replace("-", ""); } return ""; } } public readonly struct EnvironmentVariant { public string Name { get; } public string Source { get; } public byte Red { get; } public byte Green { get; } public byte Blue { get; } public EnvironmentVariant(string name, string source, byte red, byte green, byte blue) { Name = name; Source = source; Red = red; Green = green; Blue = blue; } } public static class EnvironmentVariants { private static readonly string[] Suffixes = new string[3] { " Summer", " Fall", " Winter" }; public static IReadOnlyList<EnvironmentVariant> All { get; } = Build(); public static bool IsVariant(string? name) { return Suffix(name) != null; } private static List<EnvironmentVariant> Build() { SortedDictionary<string, EnvironmentVariant> sortedDictionary = new SortedDictionary<string, EnvironmentVariant>(StringComparer.Ordinal); Season[] all = Seasons.All; for (int i = 0; i < all.Length; i++) { SeasonEnvironmentRules seasonEnvironmentRules = SeasonBiomeEnvironments.For(all[i]); foreach (EnvironmentReplace item in seasonEnvironmentRules.Replace) { Add(sortedDictionary, item.ReplaceTo); } foreach (EnvironmentAdd item2 in seasonEnvironmentRules.Add) { Add(sortedDictionary, item2.Environment); } } return sortedDictionary.Values.ToList(); } private static void Add(SortedDictionary<string, EnvironmentVariant> map, string name) { string text = Suffix(name); if (text != null && !map.ContainsKey(name)) { string source = name.Substring(0, name.Length - text.Length); if (name == "Swamp Summer") { source = "Darklands_dark"; } byte red; byte green; byte blue; if (text == " Summer") { red = byte.MaxValue; green = 214; blue = 140; } else if (text == " Fall") { red = 196; green = 112; blue = 48; } else { red = 186; green = 206; blue = 214; } map.Add(name, new EnvironmentVariant(name, source, red, green, blue)); } } private static string? Suffix(string? name) { if (string.IsNullOrEmpty(name)) { return null; } string[] suffixes = Suffixes; foreach (string text in suffixes) { if (name.EndsWith(text, StringComparison.Ordinal) && name.Length > text.Length) { return text; } } return null; } } public static class SeasonSky { public const float ClearDaySun = 0.84f; public const float ClearDayAmbient = 0.88f; public const float ClearDayIntensity = 0.82f; public const float ClearDayFogSun = 0.9f; public static bool DimsLowlandClear(string? name) { if (!string.Equals(name, "Clear Winter", StringComparison.Ordinal)) { return string.Equals(name, "Heath clear Winter", StringComparison.Ordinal); } return true; } public static float Scale(float value, float factor) { if (float.IsNaN(value) || float.IsInfinity(value)) { return value; } if (float.IsNaN(factor) || factor < 0f) { return value; } return value * factor; } } } namespace ExtraPotions.Solstice.Visuals { public static class HeatMeter { public const float Partial = 0.4f; public const float FullSeconds = 8f; public const float ReliefSeconds = 4f; public const float LabelWide = 1280f; public const float LabelMinPixels = 8f; public const float LabelSizeAtWide = 14f; public static float LabelFontSize(float barHeight, float canvasScale, float screenWidth, float hudWidth) { float num = ((screenWidth > 1f) ? screenWidth : hudWidth); if (!float.IsNaN(num) && !float.IsInfinity(num) && num >= 1280f) { return 14f; } float num2 = canvasScale; if (float.IsNaN(num2) || num2 < 0.05f) { num2 = 1f; } if (14f * num2 >= 8f) { return 14f; } float num3 = 8f / num2; float num4 = barHeight - 2f; if (num4 >= 1f && num3 >= 1f && num3 <= num4) { return num3; } return 0f; } public static float Target(bool settingOn, bool seasonOverheats, bool shelteredOrCold, int warmClothes, bool coolingFood) { if (!settingOn || !seasonOverheats || shelteredOrCold || coolingFood || warmClothes <= 0) { return 0f; } if (warmClothes != 1) { return 1f; } return 0.4f; } public static float Advance(float current, float target, float seconds) { if (float.IsNaN(current) || float.IsInfinity(current) || current < 0f) { current = 0f; } if (current > 1f) { current = 1f; } if (float.IsNaN(target) || float.IsInfinity(target) || target < 0f) { target = 0f; } if (target > 1f) { target = 1f; } if (float.IsNaN(seconds) || float.IsInfinity(seconds) || seconds <= 0f || current == target) { return current; } float num = ((target > current) ? 0.125f : 0.25f); float num2 = seconds * num; if (num2 > 1f) { num2 = 1f; } float num3 = ((target > current) ? (current + num2) : (current - num2)); if (target > current) { if (!(num3 > target)) { return num3; } return target; } if (!(num3 < target)) { return num3; } return target; } } public static class HeatMeterLayout { public const float Width = 168f; public const float Height = 18f; public static HudBox Union(HudBox a, HudBox b) { if (!a.Shown) { return b; } if (!b.Shown) { return a; } float num = ((a.Left < b.Left) ? a.Left : b.Left); float num2 = ((a.Top < b.Top) ? a.Top : b.Top); float num3 = ((a.Right > b.Right) ? a.Right : b.Right); float num4 = ((a.Bottom > b.Bottom) ? a.Bottom : b.Bottom); return new HudBox(num, num2, num3 - num, num4 - num2); } public static HudBox Place(float parentWidth, float parentHeight, HudBox above, float occupiedBottom, HudBox status, HudBox notices, HudBox hotbar) { float num = 168f; float num2 = 18f; if (parentWidth > 0f && num > parentWidth) { num = parentWidth; } float left = SeasonHudLayout.Left(parentWidth, num); float num3 = 14f; if (above.Shown && num3 < above.Bottom + 6f) { num3 = above.Bottom + 6f; } if (occupiedBottom >= 0f && num3 < occupiedBottom + 6f) { num3 = occupiedBottom + 6f; } if (Hits(left, num3, num, num2, status)) { num3 = status.Bottom + 6f; } Shift(ref left, num3, num, num2, notices, parentWidth); if (Hits(left, num3, num, num2, hotbar)) { float num4 = hotbar.Top - 6f - num2; if (num4 >= 0f && !Hits(left, num4, num, num2, hotbar) && !Hits(left, num4, num, num2, above) && !Hits(left, num4, num, num2, status)) { num3 = num4; } } if (parentHeight > 0f && num3 + num2 > parentHeight) { num3 = parentHeight - num2; } if (num3 < 0f) { num3 = 0f; } if (occupiedBottom >= 0f && num3 < occupiedBottom) { return default(HudBox); } if (Hits(left, num3, num, num2, above) || Hits(left, num3, num, num2, status) || Hits(left, num3, num, num2, notices) || Hits(left, num3, num, num2, hotbar)) { return default(HudBox); } return new HudBox(left, num3, num, num2); } private static void Shift(ref float left, float top, float width, float height, HudBox block, float parentWidth) { if (Hits(left, top, width, height, block)) { float num = block.Left - 6f - width; float num2 = block.Right + 6f; bool flag = num >= 0f && !Hits(num, top, width, height, block); if ((parentWidth <= 0f || num2 + width <= parentWidth) && !Hits(num2, top, width, height, block) && (!flag || block.Left < left)) { left = num2; } else if (flag) { left = num; } } } private static bool Hits(float left, float top, float width, float height, HudBox other) { return SeasonHudLayout.Overlaps(left, top, width, height, other); } } public static class SeasonGround { public const int Meadows = 1; public const int Swamp = 2; public const int BlackForest = 8; public const int Plains = 16; public const int SnowBiomes = 27; public const float MaskCap = 0.8f; public const int FrostSteps = 4; public const double PixelBudgetMs = 2.0; public const int ChunksPerFrame = 3; public const double ChunkSliceMs = 12.0; public static bool Paints(int biome) { if (biome != 1 && biome != 2 && biome != 8) { return biome == 16; } return true; } public static void Paint(int biome, ref byte r, ref byte g, ref byte b, ref byte a, float amount) { if (Paints(biome) && !float.IsNaN(amount) && !(amount <= 0f)) { if (amount > 0.8f) { amount = 0.8f; } float num = 1f - amount; r = Unit((float)(int)r * num); g = Unit((float)(int)g * num + 255f * amount); b = Unit((float)(int)b * num); a = Unit((float)(int)a * num); } } public static bool IsGrass(string name) { if (!string.IsNullOrEmpty(name)) { return name.IndexOf("grass", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } public static bool HideBloom(string name, float amount) { if (string.IsNullOrEmpty(name) || float.IsNaN(amount) || amount < 0.2f) { return false; } if (name.IndexOf("flower", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("bloom", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } public static int GrassCount(int original, float amount) { if (original <= 0 || float.IsNaN(amount) || amount <= 0f) { return original; } if (amount > 1f) { amount = 1f; } int num = (int)Math.Round((double)original * (1.0 - 0.55 * (double)amount)); if (num < 1) { num = 1; } if (num <= original) { return num; } return original; } public static float GrassScale(float original, float amount) { if (float.IsNaN(original) || float.IsNaN(amount) || amount <= 0f) { return original; } if (amount > 1f) { amount = 1f; } return original * (1f - 0.45f * amount); } public static int FrostStep(float amount) { if (float.IsNaN(amount) || amount <= 0f) { return 0; } if (amount > 1f) { amount = 1f; } int num = (int)Math.Ceiling((double)(amount * 4f) - 0.0001); if (num < 1) { num = 1; } if (num > 4) { num = 4; } return num; } public static float FrostAmount(int step) { if (step <= 0) { return 0f; } if (step > 4) { step = 4; } return (float)step / 4f; } public static int NextChunk(int doneThisFrame, double elapsedMs, int remaining) { if (remaining <= 0 || doneThisFrame >= 3) { return 0; } if (doneThisFrame > 0 && elapsedMs >= 12.0) { return 0; } return 1; } public static void Frost(byte[] rgba, float amount, bool leaves) { if (rgba == null || rgba.Length < 4 || float.IsNaN(amount) || amount <= 0f) { return; } if (amount > 1f) { amount = 1f; } int num = rgba.Length - rgba.Length % 4; for (int i = 0; i < num; i += 4) { byte b = rgba[i + 3]; if (b != 0) { float r = (float)(int)rgba[i] / 255f; float g = (float)(int)rgba[i + 1] / 255f; float b2 = (float)(int)rgba[i + 2] / 255f; if (leaves) { DustLeaf(ref r, ref g, ref b2, amount); } else { Whiten(ref r, ref g, ref b2, amount * 0.9f, 0.91f, 0.95f, 0.98f); } rgba[i] = Unit(r * 255f); rgba[i + 1] = Unit(g * 255f); rgba[i + 2] = Unit(b2 * 255f); rgba[i + 3] = b; } } } private static void Whiten(ref float r, ref float g, ref float b, float blend, float targetR, float targetG, float targetB) { r += (targetR - r) * blend; g += (targetG - g) * blend; b += (targetB - b) * blend; } private static void DustLeaf(ref float r, ref float g, ref float b, float amount) { float num = ((r > g) ? r : g); if (b > num) { num = b; } float num2 = ((r < g) ? r : g); if (b < num2) { num2 = b; } float num3 = ((num <= 0.001f) ? 0f : ((num - num2) / num)); float num4 = 0.2126f * r + 0.7152f * g + 0.0722f * b; if (g >= r + 0.05f && g >= b + 0.02f && num3 >= 0.28f && num4 >= 0.22f && g >= 0.25f) { float num5 = amount * 0.48f; r += (0.93f - r) * num5; g += (0.96f - g) * num5; b += (0.9f - b) * num5; return; } float num6 = amount * 0.1f; if (num4 < 0.18f) { num6 *= 0.35f; } r += (0.78f - r) * num6; g += (0.74f - g) * num6; b += (0.68f - b) * num6; } private static byte Unit(float value) { if (value <= 0f) { return 0; } if (value >= 255f) { return byte.MaxValue; } return (byte)Math.Round(value); } } public readonly struct HudBox { public float Left { get; } public float Top { get; } public float Width { get; } public float Height { get; } public float Right => Left + Width; public float Bottom => Top + Height; public bool Shown { get { if (Width > 0f) { return Height > 0f; } return false; } } public HudBox(float left, float top, float width, float height) { Left = left; Top = top; Width = width; Height = height; } } public static class SeasonHudLayout { public const float Gap = 6f; public const float TopGap = 14f; public const float ChipWidth = 188f; public const float ChipHeight = 24f; public static float Left(float parentWidth, float chipWidth) { if (parentWidth > 0f && chipWidth > parentWidth) { chipWidth = parentWidth; } if (!(parentWidth > chipWidth)) { return 0f; } return (parentWidth - chipWidth) * 0.5f; } public static HudBox Place(float parentWidth, float chipWidth, float chipHeight, float occupiedBottom, HudBox status) { if (chipWidth < 1f) { chipWidth = 188f; } if (chipHeight < 1f) { chipHeight = 24f; } if (parentWidth > 0f && chipWidth > parentWidth) { chipWidth = parentWidth; } float left = Left(parentWidth, chipWidth); float num = 14f; if (occupiedBottom >= 0f && num < occupiedBottom + 6f) { num = occupiedBottom + 6f; } if (Overlaps(left, num, chipWidth, chipHeight, status)) { num = status.Bottom + 6f; } return new HudBox(left, num, chipWidth, chipHeight); } public static bool Overlaps(float left, float top, float width, float height, HudBox other) { if (!other.Shown || width <= 0f || height <= 0f) { return false; } if (left < other.Right && left + width > other.Left && top < other.Bottom) { return top + height > other.Top; } return false; } } public static class SeasonIcon { public const int Size = 32; public const byte AmberR = 217; public const byte AmberG = 148; public const byte AmberB = 54; public const byte BrassR = 168; public const byte BrassG = 121; public const byte BrassB = 56; public const byte CreamR = 226; public const byte CreamG = 208; public const byte CreamB = 168; public const byte CharcoalR = 38; public const byte CharcoalG = 37; public const byte CharcoalB = 34; public static byte[] DrawHeat() { byte[] array = new byte[4096]; Flame(array); Outline(array); return array; } public static byte[] Draw(Season season) { byte[] array = new byte[4096]; switch (season) { case Season.Spring: Sprout(array); break; case Season.Summer: Sun(array); break; case Season.Fall: Leaf(array); break; default: Crystal(array); break; } Outline(array); return array; } public static bool IsPalette(byte r, byte g, byte b) { if ((r != 217 || g != 148 || b != 54) && (r != 168 || g != 121 || b != 56) && (r != 226 || g != 208 || b != 168)) { if (r == 38 && g == 37) { return b == 34; } return false; } return true; } private static void Flame(byte[] pixels) { for (int i = 4; i <= 27; i++) { int num = FlameHalf(i - 4); int num2 = ((i >= 18) ? 17 : 16); for (int j = num2 - num; j <= num2 + num; j++) { int num3 = j - num2; if (num3 < 0) { num3 = -num3; } bool flag = i >= 6 && i <= 16 && num3 <= ((i > 12) ? 1 : 2); if (flag && i <= 11) { Plot(pixels, j, i, 226, 208, 168); } else if (flag) { Plot(pixels, j, i, 168, 121, 56); } else { Plot(pixels, j, i, 217, 148, 54); } } } for (int k = 5; k <= 11; k++) { Plot(pixels, 11, k, 217, 148, 54); Plot(pixels, 10, k, 168, 121, 56); } } private static int FlameHalf(int rise) { return rise switch { 0 => 3, 1 => 4, 2 => 5, 3 => 6, 4 => 7, 5 => 7, 6 => 6, 7 => 6, 8 => 5, 9 => 5, 10 => 4, 11 => 4, 12 => 3, 13 => 3, 14 => 3, 15 => 2, 16 => 2, 17 => 2, 18 => 2, 19 => 1, 20 => 1, 21 => 1, 22 => 1, _ => 0, }; } private static void Sun(byte[] pixels) { int[] array = new int[8] { 1, 1, 0, -1, -1, -1, 0, 1 }; int[] array2 = new int[8] { 0, 1, 1, 1, 0, -1, -1, -1 }; for (int i = 0; i < 8; i++) { for (int j = 9; j <= 13; j++) { Plot(pixels, 16 + array[i] * j, 16 + array2[i] * j, 168, 121, 56); } } Disc(pixels, 16, 16, 7, 217, 148, 54); Disc(pixels, 16, 16, 3, 226, 208, 168); } private static void Sprout(byte[] pixels) { for (int i = 2; i <= 14; i++) { Plot(pixels, 15, i, 226, 208, 168); Plot(pixels, 16, i, 226, 208, 168); } Ellipse(pixels, 9, 16, 6, 4, 217, 148, 54); Ellipse(pixels, 22, 17, 6, 4, 168, 121, 56); Disc(pixels, 16, 21, 2, 226, 208, 168); } private static void Leaf(byte[] pixels) { for (int i = 8; i <= 26; i++) { int num = 9 - Abs(i - 18) * 9 / 10; if (num < 1) { num = 1; } for (int j = 16 - num; j <= 16 + num; j++) { Plot(pixels, j, i, 217, 148, 54); } } for (int k = 8; k <= 26; k++) { Plot(pixels, 16, k, 226, 208, 168); } for (int l = 2; l <= 8; l++) { Plot(pixels, 16, l, 168, 121, 56); } } private static void Crystal(byte[] pixels) { for (int i = 4; i <= 28; i++) { Plot(pixels, i, 16, 226, 208, 168); Plot(pixels, 16, i, 226, 208, 168); } for (int j = 1; j <= 8; j++) { Plot(pixels, 16 + j, 16 + j, 168, 121, 56); Plot(pixels, 16 - j, 16 + j, 168, 121, 56); Plot(pixels, 16 + j, 16 - j, 168, 121, 56); Plot(pixels, 16 - j, 16 - j, 168, 121, 56); } Disc(pixels, 16, 16, 2, 168, 121, 56); } private static void Outline(byte[] pixels) { byte[] array = new byte[pixels.Length]; Buffer.BlockCopy(pixels, 0, array, 0, pixels.Length); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { if (Alpha(array, j, i) == 0 && (Alpha(array, j - 1, i) != 0 || Alpha(array, j + 1, i) != 0 || Alpha(array, j, i - 1) != 0 || Alpha(array, j, i + 1) != 0)) { Plot(pixels, j, i, 38, 37, 34); } } } } private static int Alpha(byte[] pixels, int x, int y) { if ((uint)x >= 32u || (uint)y >= 32u) { return 0; } return pixels[(y * 32 + x) * 4 + 3]; } private static void Disc(byte[] pixels, int cx, int cy, int radius, byte r, byte g, byte b) { int num = radius * radius; for (int i = cy - radius; i <= cy + radius; i++) { for (int j = cx - radius; j <= cx + radius; j++) { int num2 = j - cx; int num3 = i - cy; if (num2 * num2 + num3 * num3 <= num) { Plot(pixels, j, i, r, g, b); } } } } private static void Ellipse(byte[] pixels, int cx, int cy, int rx, int ry, byte r, byte g, byte b) { if (rx < 1 || ry < 1) { return; } int num = rx * rx; int num2 = ry * ry; for (int i = cy - ry; i <= cy + ry; i++) { for (int j = cx - rx; j <= cx + rx; j++) { int num3 = j - cx; int num4 = i - cy; if (num3 * num3 * num2 + num4 * num4 * num <= num * num2) { Plot(pixels, j, i, r, g, b); } } } } private static void Plot(byte[] pixels, int x, int y, byte r, byte g, byte b) { if ((uint)x < 32u && (uint)y < 32u) { int num = (y * 32 + x) * 4; pixels[num] = r; pixels[num + 1] = g; pixels[num + 2] = b; pixels[num + 3] = byte.MaxValue; } } private static int Abs(int value) { if (value >= 0) { return value; } return -value; } } public static class SeasonRebuild { public static bool Text(bool sameObject, string? text, string? cached, int day, int cachedDay, int length, int cachedLength) { if (!sameObject) { return true; } if (!string.Equals(text, cached, StringComparison.Ordinal)) { return true; } if (day == cachedDay) { return length != cachedLength; } return true; } } public enum TintChannel { Grass, Leaf, Terrain, Map, ShallowWater } public readonly struct Rgb { public float R { get; } public float G { get; } public float B { get; } public Rgb(float r, float g, float b) { R = r; G = g; B = b; } public static float Clamp(float value) { if (!(value < 0f)) { if (!(value > 1f)) { return value; } return 1f; } return 0f; } public Rgb Clamped() { return new Rgb(Clamp(R), Clamp(G), Clamp(B)); } } public static class SeasonTint { public const float SnowShowsFrom = 0.25f; public const float IceBlend = 0.55f; public const float SurfaceIceBlend = 0.34f; public const float FoamIceBlend = 0.3f; public const float RippleCalm = 1.45f; public const float RippleCalmCap = 12f; public static readonly Rgb Ice = new Rgb(0.45f, 0.64f, 0.8f); public static readonly Rgb SnowWhite = new Rgb(0.93f, 0.95f, 0.97f); public static Rgb Apply(TintChannel channel, Season season, Rgb color, bool frozen) { if (channel == TintChannel.ShallowWater) { return Freeze(color, frozen); } Rgb color2 = Multiply(color, Multiplier(channel, season)); if (season != Season.Winter) { return color2.Clamped(); } return Desaturate(color2, WinterFade(channel)).Clamped(); } public static Rgb Multiplier(TintChannel channel, Season season) { return channel switch { TintChannel.Grass => Grass(season), TintChannel.Leaf => Leaf(season), TintChannel.Terrain => Terrain(season), TintChannel.Map => Map(season), _ => new Rgb(1f, 1f, 1f), }; } public static float WinterFade(TintChannel channel) { return channel switch { TintChannel.Grass => 0.9f, TintChannel.Leaf => 0.94f, TintChannel.Terrain => 0.4f, TintChannel.Map => 0.35f, _ => 0f, }; } public static Rgb Multiply(Rgb color, Rgb multiplier) { return new Rgb(color.R * multiplier.R, color.G * multiplier.G, color.B * multiplier.B); } public static Rgb Desaturate(Rgb color, float amount) { if (amount <= 0f) { return color; } if (amount > 1f) { amount = 1f; } float num = 0.2126f * color.R + 0.7152f * color.G + 0.0722f * color.B; float num2 = 1f - amount; return new Rgb(color.R * num2 + num * amount, color.G * num2 + num * amount, color.B * num2 + num * amount); } public static float Saturation(Rgb color) { float num = ((color.R > color.G) ? color.R : color.G); if (color.B > num) { num = color.B; } float num2 = ((color.R < color.G) ? color.R : color.G); if (color.B < num2) { num2 = color.B; } if (!(num <= 0f)) { return (num - num2) / num; } return 0f; } public static Rgb Freeze(Rgb color, bool frozen) { return FreezeAmount(color, frozen, 0.55f); } public static Rgb FreezeAmount(Rgb color, bool frozen, float blend) { if (!frozen || blend <= 0f) { return color; } if (blend > 1f) { blend = 1f; } return new Rgb(color.R + (Ice.R - color.R) * blend, color.G + (Ice.G - color.G) * blend, color.B + (Ice.B - color.B) * blend).Clamped(); } public static float CalmRipple(float original, bool frozen) { if (!frozen || float.IsNaN(original) || float.IsInfinity(original) || original < 0f) { return original; } float num = original * 1.45f; float num2 = original + 12f; if (!(num > num2)) { return num; } return num2; } public static Rgb Whiten(Rgb color, float cover) { float num = SnowLevel(cover); if (num <= 0f) { return color; } return new Rgb(color.R + (SnowWhite.R - color.R) * num, color.G + (SnowWhite.G - color.G) * num, color.B + (SnowWhite.B - color.B) * num).Clamped(); } public static float SnowLevel(float cover) { if (float.IsNaN(cover) || cover <= 0.25f) { return 0f; } float num = (cover - 0.25f) / 0.75f; if (num < 0f) { return 0f; } if (!(num > 1f)) { return num; } return 1f; } public static float SnowApply(float original, float cover) { if (float.IsNaN(original)) { original = 0f; } float num = SnowLevel(cover); if (!(original > num)) { return num; } return original; } private static Rgb Grass(Season season) { return season switch { Season.Spring => new Rgb(0.9f, 1.12f, 0.86f), Season.Summer => new Rgb(0.82f, 1f, 0.7f), Season.Fall => new Rgb(1.15f, 0.92f, 0.58f), _ => new Rgb(0.7f, 0.74f, 1.08f), }; } private static Rgb Leaf(Season season) { return season switch { Season.Spring => new Rgb(0.86f, 1.16f, 0.78f), Season.Summer => new Rgb(0.8f, 1.02f, 0.68f), Season.Fall => new Rgb(1.4f, 0.58f, 0.26f), _ => new Rgb(0.72f, 0.76f, 1.06f), }; } private static Rgb Terrain(Season season) { return season switch { Season.Spring => new Rgb(0.97f, 1.05f, 0.95f), Season.Summer => new Rgb(0.95f, 1.02f, 0.9f), Season.Fall => new Rgb(1.08f, 0.97f, 0.84f), _ => new Rgb(0.96f, 0.98f, 1.02f), }; } private static Rgb Map(Season season) { return season switch { Season.Spring => new Rgb(0.93f, 1.05f, 0.92f), Season.Summer => new Rgb(0.9f, 1.02f, 0.86f), Season.Fall => new Rgb(1.1f, 0.95f, 0.8f), _ => new Rgb(0.88f, 0.92f, 1f), }; } } public static class SeasonShader { public const string Color = "_Color"; public const string SnowLevel = "_SnowLevel"; public const string Shallow = "_ColorBottomShallow"; public const string Top = "_ColorTop"; public const string Foam = "_FoamColor"; public const string Ripple = "_RippleDistance"; } [Flags] public enum VisualTarget { None = 0, Grass = 1, Leaf = 2, Terrain = 4, Snow = 8, ShallowWater = 0x10, WaterTop = 0x20, WaterFoam = 0x40, WaterRipple = 0x80, WaterSurface = 0x100 } public static class SeasonVisualSelection { public static VisualTarget Select(string? shaderName, string? materialName, bool color, bool snowLevel, bool shallow, bool foam = false, bool ripple = false, bool top = false) { string text = shaderName ?? ""; string text2 = materialName ?? ""; if (text.Length == 0 && text2.Length == 0) { return VisualTarget.None; } if (Excluded(text)) { return VisualTarget.None; } VisualTarget visualTarget = VisualTarget.None; if (Has(text, "water")) { if (shallow) { visualTarget |= VisualTarget.ShallowWater; } if (top) { visualTarget |= VisualTarget.WaterTop; } if (foam) { visualTarget |= VisualTarget.WaterFoam; } if (ripple) { visualTarget |= VisualTarget.WaterRipple; } return visualTarget; } if (snowLevel) { visualTarget |= VisualTarget.Snow; } if (!color) { return visualTarget; } if (Has(text, "grass")) { visualTarget |= VisualTarget.Grass; } else if (Has(text, "vegetation") || Has(text, "foliage") || Has(text, "leaf")) { visualTarget |= VisualTarget.Leaf; } else if (Has(text, "terrain") || Has(text, "heightmap")) { visualTarget |= VisualTarget.Terrain; } else if (Has(text2, "grass")) { visualTarget |= VisualTarget.Grass; } else if (Has(text2, "leaf") || Has(text2, "bush") || Has(text2, "shrub") || Has(text2, "foliage")) { visualTarget |= VisualTarget.Leaf; } return visualTarget; } public static bool BarkOnly(string? name) { if (string.IsNullOrEmpty(name)) { return false; } if (!Has(name, "bark") && !Has(name, "trunk") && !Has(name, "log")) { return Has(name, "stem"); } return true; } public static bool FrostTexture(string? shaderName, string? materialName, out bool leaves) { return FrostTexture(shaderName, materialName, null, out leaves); } public static bool FrostTexture(string? shaderName, string? materialName, string? textureName, out bool leaves) { leaves = false; string text = shaderName ?? ""; string text2 = materialName ?? ""; if (text.Length == 0 && text2.Length == 0 && string.IsNullOrEmpty(textureName)) { return false; } if (Excluded(text)) { return false; } if (Has(text, "water") || Has(text, "terrain") || Has(text, "heightmap")) { return false; } if (BarkOnly(text2) || BarkOnly(text)) { return false; } if (Has(text, "grass")) { leaves = GroundClutter(text2) || GroundClutter(textureName); return true; } if (Has(text, "vegetation") || Has(text, "foliage") || Has(text, "leaf")) { leaves = true; return true; } return false; } public static bool GroundClutter(string? name) { if (string.IsNullOrEmpty(name)) { return false; } if (!Has(name, "groundcover") && !Has(name, "ormbunke") && !Has(name, "fern") && !Has(name, "shrub") && !Has(name, "bush") && !Has(name, "waterlil") && !Has(name, "clutter") && !Has(name, "grasscross_forest")) { return Has(name, "vass"); } return true; } private static bool Excluded(string shader) { if (!Has(shader, "creature") && !Has(shader, "character") && !Has(shader, "player") && !Has(shader, "ui/") && !Has(shader, "gui/") && !Has(shader, "textmesh") && !Has(shader, "particle") && !Has(shader, "sprite") && !Has(shader, "font")) { return Has(shader, "decal"); } return true; } private static bool Has(string text, string part) { return text.IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0; } } } namespace ExtraPotions.Solstice.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 SolsticeTextKeys { public const string BlockedSetByServer = "blocked_set_by_server"; public const string BlockedVanillaServer = "blocked_vanilla_server"; public const string BlockedProductDisabled = "blocked_product_disabled"; public const string BlockedNotAdmin = "blocked_not_admin"; public const string BlockedAuthorityPending = "blocked_authority_pending"; public const string OffNoCrucible = "off_no_crucible"; public const string OffNoSolstice = "off_no_solstice"; public const string SeasonStarted = "season_started"; public const string CommandStatus = "cmd_status"; public const string CommandStatusOverride = "cmd_status_override"; public const string CommandInactive = "cmd_inactive"; public const string CommandHelp = "cmd_help"; public const string CommandWaiting = "cmd_waiting"; public const string PlantFrostResistant = "plant_frost_resistant"; public const string PlantHeatProtected = "plant_heat_protected"; public const string PlantSurvivedWinter = "plant_survived_winter"; public const string PlantFreezing = "plant_freezing"; public const string PlantWillPerish = "plant_will_perish"; public const string PlantExposed = "plant_exposed"; public const string StatusOverheat = "status_overheat"; public const string StatusOverheatTip = "status_overheat_tip"; public const string StatusSeasonTip = "status_season_tip"; public const string HudSeason = "hud_season"; public const string HudHeat = "hud_heat"; public static IReadOnlyList<TextKey> All { get; } = Array.AsReadOnly(new TextKey[168] { new TextKey("step_aside_seasons", 1), new TextKey("off_no_crucible", 0), new TextKey("off_no_solstice", 0), new TextKey("blocked_set_by_server", 0), new TextKey("blocked_vanilla_server", 0), new TextKey("blocked_product_disabled", 0), new TextKey("blocked_not_admin", 0), new TextKey("blocked_authority_pending", 0), new TextKey("cheat_season_override", 0), new TextKey("season_spring", 0), new TextKey("season_summer", 0), new TextKey("season_fall", 0), new TextKey("season_winter", 0), new TextKey("season_started", 1), new TextKey("cmd_status", 3), new TextKey("cmd_status_override", 3), new TextKey("cmd_inactive", 0), new TextKey("cmd_help", 0), new TextKey("cmd_waiting", 0), new TextKey("plant_frost_resistant", 0), new TextKey("plant_heat_protected", 0), new TextKey("plant_survived_winter", 0), new TextKey("plant_freezing", 1), new TextKey("plant_will_perish", 0), new TextKey("plant_exposed", 0), new TextKey("status_overheat", 0), new TextKey("status_overheat_tip", 0), new TextKey("status_season_tip", 2), new TextKey("hud_season", 2), new TextKey("hud_heat", 0), new TextKey("cfg_visuals_heading", 0), new TextKey("cfg_visuals__world_tint_name", 0), new TextKey("cfg_visuals__world_tint_help", 0), new TextKey("cfg_visuals__snow_cover_name", 0), new TextKey("cfg_visuals__snow_cover_help", 0), new TextKey("cfg_visuals__frozen_water_name", 0), new TextKey("cfg_visuals__frozen_water_help", 0), new TextKey("cfg_visuals__minimap_tint_name", 0), new TextKey("cfg_visuals__minimap_tint_help", 0), new TextKey("cfg_visuals__season_indicator_name", 0), new TextKey("cfg_visuals__season_indicator_help", 0), new TextKey("cfg_visuals__season_icons_name", 0), new TextKey("cfg_visuals__season_icons_help", 0), new TextKey("cfg_visuals__heat_meter_name", 0), new TextKey("cfg_visuals__heat_meter_help", 0), new TextKey("cfg_music_heading", 0), new TextKey("cfg_music__seasonal_music_name", 0), new TextKey("cfg_music__seasonal_music_help", 0), new TextKey("cfg_general_heading", 0), new TextKey("cfg_calendar_heading", 0), new TextKey("cfg_spring_heading", 0), new TextKey("cfg_summer_heading", 0), new TextKey("cfg_fall_heading", 0), new TextKey("cfg_winter_heading", 0), new TextKey("cfg_protection_heading", 0), new TextKey("cfg_climate_heading", 0), new TextKey("cfg_notices_heading", 0), new TextKey("cfg_cheats_heading", 0), new TextKey("cfg_general__enabled_name", 0), new TextKey("cfg_general__enabled_help", 0), new TextKey("cfg_protection__heat_protects_crops_name", 0), new TextKey("cfg_protection__heat_protects_crops_help", 0), new TextKey("cfg_protection__shield_generator_protects_name", 0), new TextKey("cfg_protection__shield_generator_protects_help", 0), new TextKey("cfg_protection__shield_generator_protects_in_winter_only_name", 0), new TextKey("cfg_protection__shield_generator_protects_in_winter_only_help", 0), new TextKey("cfg_protection__winter_hardy_crops_name", 0), new TextKey("cfg_protection__winter_hardy_crops_help", 0), new TextKey("cfg_protection__crops_perish_name", 0), new TextKey("cfg_protection__crops_perish_help", 0), new TextKey("cfg_protection__perish_from_day_name", 0), new TextKey("cfg_protection__perish_from_day_help", 0), new TextKey("cfg_protection__freeze_seconds_name", 0), new TextKey("cfg_protection__freeze_seconds_help", 0), new TextKey("cfg_protection__winter_harvest_chance_name", 0), new TextKey("cfg_protection__winter_harvest_chance_help", 0), new TextKey("cfg_protection__crops_to_control_name", 0), new TextKey("cfg_protection__crops_to_control_help", 0), new TextKey("cfg_climate__season_status_name", 0), new TextKey("cfg_climate__season_status_help", 0), new TextKey("cfg_climate__warm_clothes_overheat_name", 0), new TextKey("cfg_climate__warm_clothes_overheat_help", 0), new TextKey("cfg_climate__cooling_foods_name", 0), new TextKey("cfg_climate__cooling_foods_help", 0), new TextKey("cfg_notices__season_message_name", 0), new TextKey("cfg_notices__season_message_help", 0), new TextKey("cfg_cheats__season_override_name", 0), new TextKey("cfg_cheats__season_override_help", 0), new TextKey("cfg_calendar__days_in_spring_name", 0), new TextKey("cfg_calendar__days_in_spring_help", 0), new TextKey("cfg_spring__plants_growth_multiplier_name", 0), new TextKey("cfg_spring__plants_growth_multiplier_help", 0), new TextKey("cfg_spring__beehive_production_multiplier_name", 0), new TextKey("cfg_spring__beehive_production_multiplier_help", 0), new TextKey("cfg_spring__food_drain_multiplier_name", 0), new TextKey("cfg_spring__food_drain_multiplier_help", 0), new TextKey("cfg_spring__stamina_drain_multiplier_name", 0), new TextKey("cfg_spring__stamina_drain_multiplier_help", 0), new TextKey("cfg_spring__fireplace_drain_multiplier_name", 0), new TextKey("cfg_spring__fireplace_drain_multiplier_help", 0), new TextKey("cfg_spring__sap_collecting_speed_multiplier_name", 0), new TextKey("cfg_spring__sap_collecting_speed_multiplier_help", 0), new TextKey("cfg_spring__rested_buff_duration_multiplier_name", 0), new TextKey("cfg_spring__rested_buff_duration_multiplier_help", 0), new TextKey("cfg_spring__wind_intensity_multiplier_name", 0), new TextKey("cfg_spring__wind_intensity_multiplier_help", 0), new TextKey("cfg_spring__rain_protection_name", 0), new TextKey("cfg_spring__rain_protection_help", 0), new TextKey("cfg_calendar__days_in_summer_name", 0), new TextKey("cfg_calendar__days_in_summer_help", 0), new TextKey("cfg_summer__plants_growth_multiplier_name", 0), new TextKey("cfg_summer__plants_growth_multiplier_help", 0), new TextKey("cfg_summer__beehive_production_multiplier_name", 0), new TextKey("cfg_summer__beehive_production_multiplier_help", 0), new TextKey("cfg_summer__food_drain_multiplier_name", 0), new TextKey("cfg_summer__food_drain_multiplier_help", 0), new TextKey("cfg_summer__stamina_drain_multiplier_name", 0), new TextKey("cfg_summer__stamina_drain_multiplier_help", 0), new TextKey("cfg_summer__fireplace_drain_multiplier_name", 0), new TextKey("cfg_summer__fireplace_drain_multiplier_help", 0), new TextKey("cfg_summer__sap_collecting_speed_multiplier_name", 0), new TextKey("cfg_summer__sap_collecting_speed_multiplier_help", 0), new TextKey("cfg_summer__rested_buff_duration_multiplier_name", 0), new TextKey("cfg_summer__rested_buff_duration_multiplier_help", 0), new TextKey("cfg_summer__wind_intensity_multiplier_name", 0), new TextKey("cfg_summer__wind_intensity_multiplier_help", 0), new TextKey("cfg_summer__rain_protection_name", 0), new TextKey("cfg_summer__rain_protection_help", 0), new TextKey("cfg_calendar__days_in_fall_name", 0), new TextKey("cfg_calendar__days_in_fall_help", 0), new TextKey("cfg_fall__plants_growth_multiplier_name", 0), new TextKey("cfg_fall__plants_growth_multiplier_help", 0), new TextKey("cfg_fall__beehive_production_multiplier_name", 0), new TextKey("cfg_fall__beehive_production_multiplier_help", 0), new TextKey("cfg_fall__food_drain_multiplier_name", 0), new TextKey("cfg_fall__food_drain_multiplier_help", 0), new TextKey("cfg_fall__stamina_drain_multiplier_name", 0), new TextKey("cfg_fall__stamina_drain_multiplier_help", 0), new TextKey("cfg_fall__fireplace_drain_multiplier_name", 0), new TextKey("cfg_fall__fireplace_drain_multiplier_help", 0), new TextKey("cfg_fall__sap_collecting_speed_multiplier_name", 0), new TextKey("cfg_fall__sap_collecting_speed_multiplier_help", 0), new TextKey("cfg_fall__rested_buff_duration_multiplier_name", 0), new TextKey("cfg_fall__rested_buff_duration_multiplier_help", 0), new TextKey("cfg_fall__wind_intensity_multiplier_name", 0), new TextKey("cfg_fall__wind_intensity_multiplier_help", 0), new TextKey("cfg_fall__rain_protection_name", 0), new TextKey("cfg_fall__rain_protection_help", 0), new TextKey("cfg_calendar__days_in_winter_name", 0), new TextKey("cfg_calendar__days_in_winter_help", 0), new TextKey("cfg_winter__plants_growth_multiplier_name", 0), new TextKey("cfg_winter__plants_growth_multiplier_help", 0), new TextKey("cfg_winter__beehive_production_multiplier_name", 0), new TextKey("cfg_winter__beehive_production_multiplier_help", 0), new TextKey("cfg_winter__food_drain_multiplier_name", 0), new TextKey("cfg_winter__food_drain_multiplier_help", 0), new TextKey("cfg_winter__stamina_drain_multiplier_name", 0), new TextKey("cfg_winter__stamina_drain_multiplier_help", 0), new TextKey("cfg_winter__fireplace_drain_multiplier_name", 0), new TextKey("cfg_winter__fireplace_drain_multiplier_help", 0), new TextKey("cfg_winter__sap_collecting_speed_multiplier_name", 0), new TextKey("cfg_winter__sap_collecting_speed_multiplier_help", 0), new TextKey("cfg_winter__rested_buff_duration_multiplier_name", 0), new TextKey("cfg_winter__rested_buff_duration_multiplier_help", 0), new TextKey("cfg_winter__wind_intensity_multiplier_name", 0), new TextKey("cfg_winter__wind_intensity_multiplier_help", 0), new TextKey("cfg_winter__rain_protection_name", 0), new TextKey("cfg_winter__rain_protection_help", 0) }); public static string SeasonName(Season season) { return "season_" + season.ToString().ToLowerInvariant(); } } } namespace ExtraPotions.Solstice.Integration { public enum SolsticeModule { Seasons } 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 SolsticeModule Module { get; } internal ModuleSource(string guid, string name, SolsticeModule module) { Guid = guid; Name = name; Module = module; } public bool Matches(InstalledPlugin plugin) { return string.Equals(plugin.Guid, Guid, StringComparison.Ordinal); } } public readonly struct StepAsideNotice { public SolsticeModule Module { get; } public string SourceName { get; } public string SourceGuid { get; } public string Key => KeyOf(Module); public StepAsideNotice(SolsticeModule module, string sourceName, string sourceGuid) { Module = module; SourceName = sourceName; SourceGuid = sourceGuid; } public static string KeyOf(SolsticeModule module) { return "step_aside_" + module.ToString().ToLowerInvariant(); } } public sealed class ModuleStepAsideDecision { private readonly HashSet<SolsticeModule> 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<SolsticeModule>(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(SolsticeModule module) { return aside.Contains(module); } } public static class ModuleStepAside { public static readonly ModuleSource Seasons = new ModuleSource("shudnal.Seasons", "Seasons", SolsticeModule.Seasons); public static readonly ModuleSource Seasonality = new ModuleSource("RustyMods.Seasonality", "Seasonality", SolsticeModule.Seasons); public static IReadOnlyList<ModuleSource> Sources { get; } = Array.AsReadOnly(new ModuleSource[2] { Seasons, Seasonality }); 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 readonly struct SeasonClockView : IEquatable<SeasonClockView> { public const string Id = "meadworks.seasons.clock"; public static readonly Version ContractVersion = new Version(1, 0); public int Season { get; } public int DayInSeason { get; } public int DaysInSeason { get; } public int DayOfYear { get; } public int YearLength { get; } public int WorldDay { get; } public int AnchorDay { get; } public int Revision { get; } public bool Available => DaysInSeason > 0; public SeasonClockView(int season, int dayInSeason, int daysInSeason, int dayOfYear, int yearLength, int worldDay, int anchorDay, int revision) { Season = season; DayInSeason = dayInSeason; DaysInSeason = daysInSeason; DayOfYear = dayOfYear; YearLength = yearLength; WorldDay = worldDay; AnchorDay = anchorDay; Revision = revision; } public static SeasonClockView FromClock(SeasonClock clock) { return new SeasonClockView((int)clock.Season, clock.DayInSeason, clock.DaysIn(clock.Season), clock.DayOfYear, clock.YearLength, clock.WorldDay, clock.AnchorDay, clock.Revision); } public (int, int, int, int, int, int, int, int) ToTuple() { return (Season, DayInSeason, DaysInSeason, DayOfYear, YearLength, WorldDay, AnchorDay, Revision); } public static bool TryFromTuple((int, int, int, int, int, int, int, int) tuple, out SeasonClockView view) { view = default(SeasonClockView); int item = tuple.Item1; int item2 = tuple.Item2; int item3 = tuple.Item3; int item4 = tuple.Item4; int item5 = tuple.Item5; int item6 = tuple.Item6; int item7 = tuple.Item7; int item8 = tuple.Rest.Item1; if (item3 == 0 && item2 == 0 && item5 == 0 && item6 == 0 && item7 == 0 && item8 == 0 && item == 0 && item4 == 0) { view = default(SeasonClockView); return true; } if (item < 0 || item >= 4) { return false; } if (item3 < 1 || item5 < item3 || item2 < 0 || item2 > item3 || item4 < 0 || item4 > item5) { return false; } if (item6 < item7 || item6 - item7 != item2) { return false; } view = new SeasonClockView(item, item2, item3, item4, item5, item6, item7, item8); return true; } public bool Equals(SeasonClockView other) { if (Season == other.Season && DayInSeason == other.DayInSeason && DaysInSeason == other.DaysInSeason && DayOfYear == other.DayOfYear && YearLength == other.YearLength && WorldDay == other.WorldDay && AnchorDay == other.AnchorDay) { return Revision == other.Revision; } return false; } public override bool Equals(object? obj) { if (obj is SeasonClockView other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((((((((((Season * 397) ^ DayInSeason) * 397) ^ DaysInSeason) * 397) ^ DayOfYear) * 397) ^ YearLength) * 397) ^ WorldDay) * 397) ^ AnchorDay) * 397) ^ Revision; } } public readonly struct SeasonFactValues { public bool Available { get; } public int Season { get; } public int CalendarSeason { get; } public int Day { get; } public int Length { get; } public int DayOfYear { get; } public int Year { get; } public int WorldDay { get; } public int Anchor { get; } public int Revision { get; } public bool Held { get; } public bool Effects { get; } public int NightPercent { get; } public float DayStart { get; } public float Wood { get; } public float Meat { get; } public float Breed { get; } public bool Torch { get; } public float TorchDrain { get; } public bool Overheat { get; } public float Growth { get; } public float Snow { get; } public bool Ice { get; } public SeasonFactValues(bool available, int season, int calendarSeason, int day, int length, int dayOfYear, int year, int worldDay, int anchor, int revision, bool held, bool effects, int nightPercent, float dayStart, float wood, float meat, float breed, bool torch, float torchDrain, bool overheat, float growth, float snow, bool ice) { Available = available; Season = season; CalendarSeason = calendarSeason; Day = day; Length = length; DayOfYear = dayOfYear; Year = year; WorldDay = worldDay; Anchor = anchor; Revision = revision; Held = held; Effects = effects; NightPercent = nightPercent; DayStart = dayStart; Wood = wood; Meat = meat; Breed = breed; Torch = torch; TorchDrain = torchDrain; Overheat = overheat; Growth = growth; Snow = snow; Ice = ice; } } public static class SeasonFacts { public const string Id = "meadworks.seasons.facts"; public static readonly Version ContractVersion = new Version(1, 0); public static string Format(SeasonFactValues facts) { if (!facts.Available) { return ""; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; return string.Join(";", "season=" + facts.Season.ToString(invariantCulture), "calendar=" + facts.CalendarSeason.ToString(invariantCulture), "day=" + facts.Day.ToString(invariantCulture), "length=" + facts.Length.ToString(invariantCulture), "doy=" + facts.DayOfYear.ToString(invariantCulture), "year=" + facts.Year.ToString(invariantCulture), "world=" + facts.WorldDay.ToString(invariantCulture), "anchor=" + facts.Anchor.ToString(invariantCulture), "rev=" + facts.Revision.ToString(invariantCulture), "held=" + (facts.Held ? "1" : "0"), "effects=" + (facts.Effects ? "1" : "0"), "night=" + facts.NightPercent.ToString(invariantCulture), "daystart=" + facts.DayStart.ToString("0.###", invariantCulture), "wood=" + facts.Wood.ToString("0.###", invariantCulture), "meat=" + facts.Meat.ToString("0.###", invariantCulture), "breed=" + facts.Breed.ToString("0.###", invariantCulture), "torch=" + (facts.Torch ? "1" : "0"), "torchdrain=" + facts.TorchDrain.ToString("0.###", invariantCulture), "overheat=" + (facts.Overheat ? "1" : "0"), "growth=" + facts.Growth.ToString("0.###", invariantCulture), "snow=" + facts.Snow.ToString("0.###", invariantCulture), "ice=" + (facts.Ice ? "1" : "0")); } public static bool TryGet(string? text, string key, out string value) { value = ""; if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(key)) { return false; } string[] array = text.Split(';'); foreach (string text2 in array) { int num = text2.IndexOf('='); if (num > 0 && string.Equals(text2.Substring(0, num), key, StringComparison.Ordinal)) { value = text2.Substring(num + 1); return true; } } return false; } } public readonly struct SeasonView : IEquatable<SeasonView> { public const string Id = "meadworks.seasons.view"; public static readonly Version ContractVersion = new Version(1, 0); public int Season { get; } public int DayInSeason { get; } public int DaysInSeason { get; } public int DayOfYear { get; } public int YearLength { get; } public int Revision { get; } public bool Available => DaysInSeason > 0; public SeasonView(int season, int dayInSeason, int daysInSeason, int dayOfYear, int yearLength, int revision) { Season = season; DayInSeason = dayInSeason; DaysInSeason = daysInSeason; DayOfYear = dayOfYear; YearLength = yearLength; Revision = revision; } public static SeasonView Read(SeasonCalendar calendar, int day, Season? forced = null) { if (calendar == null) { throw new ArgumentNullException("calendar"); } SeasonPosition seasonPosition = calendar.At(day); Season season = forced ?? seasonPosition.Season; int num = calendar.DaysIn(season); return new SeasonView((int)season, Math.Min(seasonPosition.DayInSeason, num), num, seasonPosition.DayOfYear, calendar.YearLength, 0); } public static SeasonView FromClock(SeasonClock clock, Season? forced = null) { Season season = forced ?? clock.Season; int num = clock.DaysIn(season); int dayInSeason = Math.Min(Math.Max(0, clock.DayInSeason), num); return new SeasonView((int)season, dayInSeason, num, clock.DayOfYear, clock.YearLength, 0); } public SeasonView WithRevision(int revision) { return new SeasonView(Season, DayInSeason, DaysInSeason, DayOfYear, YearLength, revision); } public Tuple<int, int, int, int, int, int> ToTuple() { return Tuple.Create(Season, DayInSeason, DaysInSeason, DayOfYear, YearLength, Revision); } public static SeasonView FromTuple(Tuple<int, int, int, int, int, int> tuple) { if (!TryFromTuple(tuple, out var view)) { throw new ArgumentException("Invalid season reading.", "tuple"); } return view; } public static bool TryFromTuple(Tuple<int, int, int, int, int, int>? tuple, out SeasonView view) { view = default(SeasonView); if (tuple == null) { return false; } int item = tuple.Item1; int item2 = tuple.Item2; int item3 = tuple.Item3; int item4 = tuple.Item4; int item5 = tuple.Item5; if (item < 0 || item >= 4) { return false; } if (item3 < 0 || item5 < item3 || item2 < 0 || item2 > item3 || item4 < 0 || item4 > item5) { return false; } view = new SeasonView(item, item2, item3, item4, item5, tuple.Item6); return true; } public bool Equals(SeasonView other) { if (Season == other.Season && DayInSeason == other.DayInSeason && DaysInSeason == other.DaysInSeason && DayOfYear == other.DayOfYear && YearLength == other.YearLength) { return Revision == other.Revision; } return false; } public override bool Equals(object? obj) { if (obj is SeasonView other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((((((Season * 397) ^ DayInSeason) * 397) ^ DaysInSeason) * 397) ^ DayOfYear) * 397) ^ YearLength) * 397) ^ Revision; } } public sealed class SeasonViewRevision { private int revision; private bool seen; private SeasonView last; public int Observe(SeasonView view) { SeasonView seasonView = view.WithRevision(0); if (!seen || !seasonView.Equals(last)) { seen = true; last = seasonView; revision++; } return revision; } public SeasonView Stamp(SeasonView view) { return view.WithRevision(Observe(view)); } } public enum YieldReason { None, TempestEvent, ForcedEnvironment } public readonly struct WeatherYieldDecision { public bool Yield { get; } public YieldReason Reason { get; } public WeatherYieldDecision(bool yield, YieldReason reason) { Yield = yield; Reason = reason; } } public static class WeatherYield { public const float MaxDeferSeconds = 3600f; public static WeatherYieldDecision Decide(bool tempestRunning, bool tempestLocalAffected, bool gameForced) { if (gameForced) { return new WeatherYieldDecision(yield: true, YieldReason.ForcedEnvironment); } if (tempestRunning && tempestLocalAffected) { return new WeatherYieldDecision(yield: true, YieldReason.TempestEvent); } return new WeatherYieldDecision(yield: false, YieldReason.None); } } public sealed class EnvironmentRefresh { private double since; public bool Pending { get; private set; } public void Request(double now) { if (!Pending) { Pending = true; since = now; } } public bool Tick(double now, WeatherYieldDecision decision) { if (!Pending) { return false; } if (decision.Yield && now - since < 3600.0) { return false; } Pending = false; return true; } public void Cancel() { Pending = false; } } } namespace ExtraPotions.Solstice.Effects { public static class ClimateRules { public const float OverheatRegen = 0.8f; public const int WaterFreezesFromDay = 6; public const int WaterFreezesUntilDay = 9; public static bool StatusApplies(bool enabled, bool interior, bool ashlandsOrDeepNorth) { if (enabled && !interior) { return !ashlandsOrDeepNorth; } return false; } public static bool Overheating(bool settingOn, bool seasonOverheats, bool shelteredOrCold, int warmClothes, bool coolingFood) { if (settingOn && seasonOverheats && !shelteredOrCold && !coolingFood) { return warmClothes > 1; } return false; } public static float SnowCover(Season season, int dayInSeason, int daysInSeason, bool placeProtected, bool heatProtected) { if (season != Season.Winter || placeProtected || heatProtected || daysInSeason < 1 || dayInSeason < 1) { return 0f; } float num = (float)dayInSeason / (float)daysInSeason; if (!(num > 1f)) { return num; } return 1f; } public static bool WaterFrozen(Season season, int dayInSeason) { if (season == Season.Winter && dayInSeason >= 6) { return dayInSeason <= 9; } return false; } } public sealed class CropFreezeTimer { private bool observing; private long previousBudget; private double previousTime; public long Advance(long budget, bool owner, double now) { if (!owner) { observing = false; return budget; } if (!observing || budget != previousBudget) { observing = true; previousBudget = budget; previousTime = now; return budget; } double num = Math.Max(0.0, now - previousTime); long num2 = ((double.IsNaN(num) || double.IsInfinity(num)) ? 0 : ((long)(num * 1000.0))); long result = (previousBudget = ((budget > 0) ? Math.Max(1L, budget - num2) : 0)); previousTime = now; return result; } } public enum CropWinterAction { None, ClearFreeze, BeginFreeze, StayFreezing, Survive, Perish } public enum CropHover { None, FrostResistant, HeatProtected, Survived, Freezing, WillPerish, Exposed } public readonly struct CropWinterDecision { public CropWinterAction Action { get; } public CropHover Hover { get; } public int HoverSeconds { get; } public CropWinterDecision(CropWinterAction action, CropHover hover, int hoverSeconds) { Action = action; Hover = hover; HoverSeconds = hoverSeconds; } } public static class CropWinter { public const int DefaultPerishDay = 3; public const float DefaultFreezeSeconds = 120f; public const float DefaultHarvestChance = 0.33f; public static float FreezeWindow(float seconds) { if (!(seconds < 0f)) { return seconds; } return 0f; } public static string PrefabName(string? name) { if (string.IsNullOrEmpty(name)) { return ""; } int num = name.IndexOf("(Clone)", StringComparison.Ordinal); if (num < 0) { return name; } if (num > 0 && name[num - 1] == ' ') { num--; } return name.Substring(0, num); } public static bool SurvivedWinter(int savedDay, int anchorDay, int length) { if (savedDay > anchorDay) { return savedDay <= (long)anchorDay + (long)length; } return false; } public static CropWinterDecision Decide(bool perishEnabled, bool winterStopped, bool canBePicked, bool alreadyPicked, bool controlled, bool placeProtected, bool hardy, bool heatProtected, bool alreadySurvived, int dayInSeason, int perishFromDay, float freezeSeconds, float harvestChance, bool freezeStarted, double secondsRemaining, double roll) { if (!(perishEnabled && winterStopped && controlled && canBePicked) || alreadyPicked) { if (!freezeStarted) { return None(CropHover.None); } return Clear(CropHover.None); } if (placeProtected) { if (!freezeStarted) { return None(CropHover.None); } return Clear(CropHover.None); } if (hardy) { if (!freezeStarted) { return None(CropHover.FrostResistant); } return Clear(CropHover.FrostResistant); } if (heatProtected) { if (!freezeStarted) { return None(CropHover.HeatProtected); } return Clear(CropHover.HeatProtected); } if (alreadySurvived) { if (!freezeStarted) { return None(CropHover.Survived); } return Clear(CropHover.Survived); } if (dayInSeason < perishFromDay) { if (!freezeStarted) { return None(CropHover.Exposed); } return Clear(CropHover.Exposed); } float num = FreezeWindow(freezeSeconds); if (num > 0f) { if (!freezeStarted) { return new CropWinterDecision(CropWinterAction.BeginFreeze, CropHover.Freezing, WholeSeconds(num)); } if (secondsRemaining > 0.0) { return new CropWinterDecision(CropWinterAction.StayFreezing, CropHover.Freezing, WholeSeconds(secondsRemaining)); } } if (KeepsCrop(roll, harvestChance)) { return new CropWinterDecision(CropWinterAction.Survive, CropHover.Survived, 0); } return new CropWinterDecision(CropWinterAction.Perish, CropHover.WillPerish, 0); } public static double Roll(long id, int worldDay) { long num = (id * -7046029254386353131L) ^ (uint)worldDay; long num2 = (num ^ (num >>> 30)) * -4658895280553007687L; long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L; return (double)(ulong)((num3 ^ (num3 >>> 31)) & 0xFFFFFF) / 16777216.0; } private static bool KeepsCrop(double roll, float harvestChance) { if (double.IsNaN(roll) || roll < 0.0 || roll >= 1.0) { return true; } if (harvestChance <= 0f) { return false; } if (harvestChance >= 1f) { return true; } return roll < (double)harvestChance; } private static int WholeSeconds(double seconds) { return (int)Math.Ceiling((seconds < 1.0) ? 1.0 : seconds); } private static CropWinterDecision None(CropHover hover) { return new CropWinterDecision(CropWinterAction.None, hover, 0); } private static CropWinterDecision Clear(CropHover hover) { return new CropWinterDecision(CropWinterAction.ClearFreeze, hover, 0); } } public enum PositionProtection { None, Interior, Terrain, Shield } public readonly struct PositionFacts { public bool Interior { get; } public bool AshlandsOrDeepNorth { get; } public bool HighMountain { get; } public bool InsideShield { get; } public PositionFacts(bool interior, bool ashlandsOrDeepNorth, bool highMountain, bool insideShield) { Interior = interior; AshlandsOrDeepNorth = ashlandsOrDeepNorth; HighMountain = highMountain; InsideShield = insideShield; } } public sealed class ProtectionRules { private readonly bool shield; private readonly bool winterOnly; public ProtectionRules(bool shieldProtects, bool shieldWinterOnly) { shield = shieldProtects; winterOnly = shieldWinterOnly; } public bool ShieldActive(Season season) { if (shield) { if (winterOnly) { return season == Season.Winter; } return true; } return false; } public PositionProtection Decide(PositionFacts facts, Season season) { if (facts.Interior) { return PositionProtection.Interior; } if (facts.AshlandsOrDeepNorth || facts.HighMountain) { return PositionProtection.Terrain; } if (ShieldActive(season) && facts.InsideShield) { return PositionProtection.Shield; } return PositionProtection.None; } } public sealed class WinterCrops { private readonly HashSet<string> names; private WinterCrops(HashSet<string> names) { this.names = names; } public static WinterCrops Parse(string? list) { return new WinterCrops(new HashSet<string>(from n in (list ?? "").Split(',') select n.Trim().ToLowerInvariant() into n where n.Length > 0 select n, StringComparer.Ordinal)); } public static string Normalize(string? list) { return string.Join(",", from n in (list ?? "").Split(',') select n.Trim() into n where n.Length > 0 select n); } public bool Survives(bool toleratesCold, IEnumerable<string> grownPrefabNames) { if (!toleratesCold) { return grownPrefabNames.Any((string n) => names.Contains((n ?? "").ToLowerInvariant())); } return true; } } public static class ColdPlants { public static bool IsTooCold(bool healthy, Season season, float growthMultiplier, bool hardy, bool heatProtected) { if (healthy && season == Season.Winter && growthMultiplier == 0f && !hardy) { return !heatProtected; } return false; } public static double AdjustedAge(double secondsSincePlanted, Season season, float growthMultiplier) { if (secondsSincePlanted != 0.0 || growthMultiplier != 0f || season != Season.Winter) { return secondsSincePlanted; } return 11.0; } } public sealed class SeasonEffects { public static readonly IReadOnlyList<string> Wired = Array.AsReadOnly(new string[9] { "PlantsGrowthMultiplier", "BeehiveProductionMultiplier", "FoodDrainMultiplier", "StaminaDrainMultiplier", "FireplaceDrainMultiplier", "SapCollectingSpeedMultiplier", "RestedBuffDurationMultiplier", "WindIntensityMultiplier", "RainProtection" }); public int DaysInSeason { get; set; } = 10; public int NightLength { get; set; } = 30; public bool TorchAsFiresource { get; set; } public float TorchDurabilityDrain { get; set; } = 0.1f; public float PlantsGrowthMultiplier { get; set; } = 1f; public float BeehiveProductionMultiplier { get; set; } = 1f; public float FoodDrainMultiplier { get; set; } = 1f; public float StaminaDrainMultiplier { get; set; } = 1f; public float FireplaceDrainMultiplier { get; set; } = 1f; public float SapCollectingSpeedMultiplier { get; set; } = 1f; public bool RainProtection { get; set; } public float WoodFromTreesMultiplier { get; set; } = 1f; public float WindIntensityMultiplier { get; set; } = 1f; public float RestedBuffDurationMultiplier { get; set; } = 1f; public float LivestockProcreationMultiplier { get; set; } = 1f; public bool OverheatIn2WarmClothes { get; set; } public float MeatFromAnimalsMultiplier { get; set; } = 1f; public float TreesRegrowthChance { get; set; } public SeasonEffects Clone() { return (SeasonEffects)MemberwiseClone(); } public static SeasonEffects Defaults(Season season) { SeasonEffects seasonEffects = new SeasonEffects(); switch (season) { case Season.Spring: seasonEffects.PlantsGrowthMultiplier = 2f; seasonEffects.BeehiveProductionMultiplier = 0.5f; seasonEffects.FireplaceDrainMultiplier = 0.75f; seasonEffects.SapCollectingSpeedMultiplier = 2f; seasonEffects.WoodFromTreesMultiplier = 0.75f; seasonEffects.WindIntensityMultiplier = 0.9f; seasonEffects.RestedBuffDurationMultiplier = 1.25f; seasonEffects.LivestockProcreationMultiplier = 1.5f; seasonEffects.MeatFromAnimalsMultiplier = 0.5f; seasonEffects.TreesRegrowthChance = 0.9f; break; case Season.Summer: seasonEffects.PlantsGrowthMultiplier = 1.5f; seasonEffects.BeehiveProductionMultiplier = 2f; seasonEffects.FoodDrainMultiplier = 0.75f; seasonEffects.NightLength = 15; seasonEffects.StaminaDrainMultiplier = 0.8f; seasonEffects.FireplaceDrainMultiplier = 0.25f; seasonEffects.SapCollectingSpeedMultiplier = 1.25f; seasonEffects.WoodFromTreesMultiplier = 0.75f; seasonEffects.WindIntensityMultiplier = 1.1f; seasonEffects.RestedBuffDurationMultiplier = 1.5f; seasonEffects.LivestockProcreationMultiplier = 1.25f; seasonEffects.OverheatIn2WarmClothes = true; seasonEffects.MeatFromAnimalsMultiplier = 0.75f; seasonEffects.TreesRegrowthChance = 0.75f; break; case Season.Fall: seasonEffects.PlantsGrowthMultiplier = 0.5f; seasonEffects.BeehiveProductionMultiplier = 1.5f; seasonEffects.FireplaceDrainMultiplier = 1f; seasonEffects.TorchAsFiresource = true; seasonEffects.SapCollectingSpeedMultiplier = 0.5f; seasonEffects.WoodFromTreesMultiplier = 1.25f; seasonEffects.WindIntensityMultiplier = 1.2f; seasonEffects.RestedBuffDurationMultiplier = 0.85f; seasonEffects.LivestockProcreationMultiplier = 0.75f; seasonEffects.MeatFromAnimalsMultiplier = 1.25f; seasonEffects.TreesRegrowthChance = 0.25f; break; case Season.Winter: seasonEffects.PlantsGrowthMultiplier = 0f; seasonEffects.BeehiveProductionMultiplier = 0f; seasonEffects.FoodDrainMultiplier = 1.25f; seasonEffects.NightLength = 45; seasonEffects.TorchAsFiresource = true; seasonEffects.StaminaDrainMultiplier = 1.2f; seasonEffects.FireplaceDrainMultiplier = 2f; seasonEffects.SapCollectingSpeedMultiplier = 0.25f; seasonEffects.RainProtection = true; seasonEffects.WoodFromTreesMultiplier = 1.5f; seasonEffects.WindIntensityMultiplier = 0.9f; seasonEffects.RestedBuffDurationMultiplier = 0.75f; seasonEffects.LivestockProcreationMultiplier = 0.5f; seasonEffects.MeatFromAnimalsMultiplier = 1.5f; seasonEffects.TreesRegrowthChance = 0f; break; default: throw new ArgumentOutOfRangeException("season"); } return seasonEffects; } public static SeasonCalendar CalendarOf(Func<Season, int> daysIn) { return new SeasonCalendar(daysIn(Season.Spring), daysIn(Season.Summer), daysIn(Season.Fall), daysIn(Season.Winter)); } } public sealed class SeasonStatModifiers { public float TickInterval { get; set; } public float HealthPerTickMinHealthPercentage { get; set; } public float HealthPerTick { get; set; } public string HealthHitType { get; set; } = ""; public float RunStaminaDrainModifier { get; set; } public float JumpStaminaUseModifier { get; set; } public float AttackStaminaUseModifier { get; set; } public float BlockStaminaUseModifier { get; set; } public float BlockStaminaUseFlatValue { get; set; } public float DodgeStaminaUseModifier { get; set; } public float SwimStaminaUseModifier { get; set; } public float HomeItemStaminaUseModifier { get; set; } public float SneakStaminaUseModifier { get; set; } public float RunStaminaUseModifier { get; set; } public float HealthRegenMultiplier { get; set; } = 1f; public float StaminaRegenMultiplier { get; set; } = 1f; public float EitrRegenMultiplier { get; set; } = 1f; public Dictionary<string, float> RaiseSkills { get; } = new Dictionary<string, float>(); public Dictionary<string, float> SkillLevels { get; } = new Dictionary<string, float>(); public Dictionary<string, float> ModifyAttackSkills { get; } = new Dictionary<string, float>(); public Dictionary<string, string> DamageModifiers { get; } = new Dictionary<string, string>(); public float NoiseModifier { get; set; } public float StealthModifier { get; set; } public float AddMaxCarryWeight { get; set; } public float SpeedModifier { get; set; } public float SwimSpeedModifier { get; set; } public float MaxMaxFallSpeed { get; set; } public float FallDamageModifier { get; set; } public float AdrenalineModifier { get; set; } public float StaggerModifier { get; set; } public float TimedBlockBonus { get; set; } } public static class SeasonStatTable { public static SeasonStatModifiers Defaults(Season season) { SeasonStatModifiers seasonStatModifiers = new SeasonStatModifiers(); switch (season) { case Season.Spring: { seasonStatModifiers.TickInterval = 5f; seasonStatModifiers.HealthPerTick = 1f; seasonStatModifiers.DamageModifiers["Poison"] = "Resistant"; string[] array = new string[4] { "Jump", "Sneak", "Run", "Swim" }; foreach (string key4 in array) { seasonStatModifiers.RaiseSkills[key4] = 1.2f; seasonStatModifiers.SkillLevels[key4] = 15f; } break; } case Season.Summer: { seasonStatModifiers.RunStaminaDrainModifier = -0.1f; seasonStatModifiers.JumpStaminaUseModifier = -0.1f; seasonStatModifiers.HealthRegenMultiplier = 1.1f; seasonStatModifiers.NoiseModifier = -0.2f; seasonStatModifiers.StealthModifier = 0.2f; seasonStatModifiers.SpeedModifier = 0.05f; string[] array = new string[4] { "Jump", "Sneak", "Run", "Swim" }; foreach (string key2 in array) { seasonStatModifiers.RaiseSkills[key2] = 1.1f; seasonStatModifiers.SkillLevels[key2] = 10f; } break; } case Season.Fall: { seasonStatModifiers.EitrRegenMultiplier = 1.1f; string[] array = new string[3] { "WoodCutting", "Fishing", "Pickaxes" }; foreach (string key3 in array) { seasonStatModifiers.RaiseSkills[key3] = 1.2f; seasonStatModifiers.SkillLevels[key3] = 15f; } break; } case Season.Winter: { seasonStatModifiers.StaminaRegenMultiplier = 1.1f; seasonStatModifiers.NoiseModifier = 0.2f; seasonStatModifiers.StealthModifier = -0.2f; seasonStatModifiers.SpeedModifier = -0.05f; seasonStatModifiers.FallDamageModifier = -0.3f; seasonStatModifiers.DamageModifiers["Fire"] = "Resistant"; string[] array = new string[3] { "WoodCutting", "Fishing", "Pickaxes" }; foreach (string key in array) { seasonStatModifiers.RaiseSkills[key] = 1.1f; seasonStatModifiers.SkillLevels[key] = 10f; } break; } default: throw new ArgumentOutOfRangeException("season"); } return seasonStatModifiers; } } } namespace ExtraPotions.Solstice.Config { public readonly struct ImportChange { public string Id { get; } public string Text { get; } public ImportChange(string id, string text) { Id = id; Text = text; } } public sealed class ImportPlan { public bool Ok { get; set; } public string Error { get; set; } = ""; public List<ImportChange> Changes { get; } = new List<ImportChange>(); public List<string> Unmapped { get; } = new List<string>(); public List<string> Unknown { get; } = new List<string>(); } public static class SeasonConfigImport { private static readonly Dictionary<string, string> Fields = new Dictionary<string, string>(StringComparer.Ordinal) { ["daysInSeason"] = "Calendar", ["plantsGrowthMultiplier"] = "Plants Growth Multiplier", ["beehiveProductionMultiplier"] = "Beehive Production Multiplier", ["foodDrainMultiplier"] = "Food Drain Multiplier", ["staminaDrainMultiplier"] = "Stamina Drain Multiplier", ["fireplaceDrainMultiplier"] = "Fireplace Drain Multiplier", ["sapCollectingSpeedMultiplier"] = "Sap Collecting Speed Multiplier", ["rainProtection"] = "Rain Protection", ["windIntensityMultiplier"] = "Wind Intensity Multiplier", ["restedBuffDurationMultiplier"] = "Rested Buff Duration Multiplier" }; private static readonly HashSet<string> OwnedElsewhere = new HashSet<string>(StringComparer.Ordinal) { "nightLength", "torchAsFiresource", "torchDurabilityDrain", "woodFromTreesMultiplier", "livestockProcreationMultiplier", "meatFromAnimalsMultiplier", "treesRegrowthChance", "overheatIn2WarmClothes" }; public static bool ShouldApply(string? current, string? factoryDefault, string? proposed) { if (current != null && factoryDefault != null && proposed != null && string.Equals(current, factoryDefault, StringComparison.Ordinal)) { return !string.Equals(proposed, factoryDefault, StringComparison.Ordinal); } return false; } public static ImportPlan Plan(string? json) { ImportPlan importPlan = new ImportPlan(); if (string.IsNullOrWhiteSpace(json)) { importPlan.Error = "Import file is empty."; return importPlan; } JsonValue jsonValue; try { jsonValue = JsonReader.Parse(json); } catch (FormatException ex) { importPlan.Error = ex.Message; return importPlan; } if (jsonValue.Kind != JsonKind.Object) { importPlan.Error = "Import file must be a JSON object."; return importPlan; } importPlan.Ok = true; if (jsonValue.Object.ContainsKey("spring") || jsonValue.Object.ContainsKey("summer") || jsonValue.Object.ContainsKey("fall") || jsonValue.Object.ContainsKey("winter")) { Season[] all = Seasons.All; for (int i = 0; i < all.Length; i++) { Season season = all[i]; ReadSeason(importPlan, season, Child(jsonValue, season.ToString().ToLowerInvariant())); } return importPlan; } if (!TrySeason(Child(jsonValue, "season"), out var season2)) { importPlan.Ok = false; importPlan.Error = "Import file needs spring, summer, fall and winter objects, or a season field."; return importPlan; } ReadSeason(importPlan, season2, jsonValue); return importPlan; } private static void ReadSeason(ImportPlan plan, Season season, JsonValue? body) { if (body == null) { return; } if (body.Kind != JsonKind.Object) { plan.Unknown.Add(season.ToString().ToLowerInvariant()); return; } foreach (KeyValuePair<string, JsonValue> item in body.Object) { if (!(item.Key == "season")) { if (OwnedElsewhere.Contains(item.Key)) { plan.Unmapped.Add(season.ToString().ToLowerInvariant() + "." + item.Key); continue; } if (!Fields.TryGetValue(item.Key, out string value)) { plan.Unknown.Add(season.ToString().ToLowerInvariant() + "." + item.Key); continue; } if (!TryText(item.Value, out string text)) { plan.Unknown.Add(season.ToString().ToLowerInvariant() + "." + item.Key); continue; } string id = ((value == "Calendar") ? ("Calendar.Days In " + season) : (season.ToString() + "." + value)); plan.Changes.Add(new ImportChange(id, text)); } } } private static bool TryText(JsonValue value, out string text) { text = ""; switch (value.Kind) { case JsonKind.Bool: text = (value.Bool ? "true" : "false"); return true; case JsonKind.Number: text = value.Number.ToString("0.###", CultureInfo.InvariantCulture); return true; case JsonKind.String: text = value.Text ?? ""; return true; default: return false; } } private static JsonValue? Child(JsonValue root, string name) { if (root.Object == null || !root.Object.TryGetValue(name, out JsonValue value)) { return null; } return value; } private static bool TrySeason(JsonValue? value, out Season season) { season = Season.Spring; if (value == null || value.Kind != JsonKind.String || value.Text == null) { return false; } if (Enum.TryParse<Season>(value.Text, ignoreCase: true, out season)) { return Enum.IsDefined(typeof(Season), season); } return false; } } internal enum JsonKind { Null, Bool, Number, String, Object, Array } internal sealed class JsonValue { public JsonKind Kind; public bool Bool; public double Number; public string? Text; public Dictionary<string, JsonValue>? Object; } internal sealed class JsonReader { private readonly string text; private int index; private JsonReader(string text) { this.text = text; } public static JsonValue Parse(string text) { JsonReader jsonReader = new JsonReader(text); JsonValue result = jsonReader.ReadValue(); jsonReader.Skip(); if (jsonReader.index != jsonReader.text.Length) { throw new FormatException("Import file has trailing text."); } return result; } private JsonValue ReadValue() { Skip(); if (index >= text.Length) { throw new FormatException("Import file ended early."); } switch (text[index]) { case '{': return ReadObject(); case '[': return ReadArray(); case '"': return new JsonValue { Kind = JsonKind.String, Text = ReadString() }; case 'f': case 't': return ReadBool(); case 'n': return ReadNull(); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return ReadNumber(); default: throw new FormatException("Import file has a value Solstice does not read."); } } private JsonValue ReadObject() { index++; Dictionary<string, JsonValue> dictionary = new Dictionary<string, JsonValue>(StringComparer.Ordinal); Skip(); if (index < text.Length && text[index] == '}') { index++; return new JsonValue { Kind = JsonKind.Object, Object = dictionary }; } while (true) { Skip(); if (index >= text.Length || text[index] != '"') { throw new FormatException("Import file has an object key that is not text."); } string key = ReadString(); Skip(); if (index >= text.Length || text[index] != ':') { throw new FormatException("Import file is missing a colon."); } index++; dictionary[key] = ReadValue(); Skip(); if (index >= text.Length) { throw new FormatException("Import file ended inside an object."); } if (text[index] != ',') { break; } index++; } if (text[index] == '}') { index++; return new JsonValue { Kind = JsonKind.Object, Object = dictionary }; } throw new FormatException("Import file has a broken object."); } private JsonValue ReadArray() { index++; Skip(); if (index < text.Length && text[index] == ']') { index++; return new JsonValue { Kind = JsonKind.Array }; } while (true) { ReadValue(); Skip(); if (index >= text.Length) { throw new FormatException("Import file ended inside a list."); } if (text[index] != ',') { break; } index++; } if (text[index] == ']') { index++; return new JsonValue { Kind = JsonKind.Array }; } throw new FormatException("Import file has a broken list."); } private JsonValue ReadBool() { if (Match("true")) { return new JsonValue { Kind = JsonKind.Bool, Bool = true }; } if (Match("false")) { return new JsonValue { Kind = JsonKind.Bool, Bool = false }; } throw new FormatException("Import file has a broken boolean."); } private JsonValue ReadNull() { if (!Match("null")) { throw new FormatException("Import file has a broken null."); } return new JsonValue { Kind = JsonKind.Null }; } private JsonValue ReadNumber() { int num = index; if (text[index] == '-') { index++; } while (index < text.Length && text[index] >= '0' && text[index] <= '9') { index++; } if (index < text.Length && text[index] == '.') { index++; while (index < text.Length && text[index] >= '0' && text[index] <= '9') { index++; } } if (index < text.Length && (text[index] == 'e' || text[index] == 'E')) {
plugins/Solstice/Solstice.dll
Decompiled 2 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.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExtraPotions.Meadworks.Contracts; using ExtraPotions.Meadworks.Crucible; using ExtraPotions.Meadworks.Crucible.UI; using ExtraPotions.Solstice.Audio; using ExtraPotions.Solstice.Calendar; using ExtraPotions.Solstice.Config; using ExtraPotions.Solstice.Effects; using ExtraPotions.Solstice.Integration; using ExtraPotions.Solstice.Localization; using ExtraPotions.Solstice.Visuals; using ExtraPotions.Solstice.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.5.0")] [assembly: AssemblyInformationalVersion("0.1.5")] [assembly: AssemblyProduct("Solstice")] [assembly: AssemblyTitle("Solstice")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.5.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.Solstice.Runtime { public static class SeasonMusicRuntime { private static MusicTrack[] buffer = Array.Empty<MusicTrack>(); private static int[] source = Array.Empty<int>(); private static int cachedSeason = int.MinValue; private static int cachedCount = -1; private static string? cachedAmbient; private static NamedMusic? chosen; private static string? playing; public static void Invalidate() { long num = SeasonFrame.Stamp(); cachedSeason = int.MinValue; if (!Wanted()) { Release(); } SeasonFrame.Add((SeasonFrameSystem)4, num); } public static void Shutdown() { Release(); cachedSeason = int.MinValue; cachedAmbient = null; chosen = null; buffer = Array.Empty<MusicTrack>(); source = Array.Empty<int>(); } public static string Describe() { if (playing == null) { return ""; } return " music " + playing; } public static void Apply(MusicMan music, ref NamedMusic result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!MeadworksHeadless.AllowsUi || (Object)(object)music == (Object)null || result == null || !Wanted()) { return; } SeasonView view = SolsticeSeasons.View; if (!((SeasonView)(ref view)).Available || ((SeasonView)(ref view)).Season < 0 || ((SeasonView)(ref view)).Season >= 4) { return; } EnvMan instance = EnvMan.instance; string text = (((Object)(object)instance != (Object)null) ? instance.GetAmbientMusic() : null); if (!string.IsNullOrEmpty(text) && string.Equals(result.m_name, text, StringComparison.Ordinal) && !SeasonMusic.IsReserved(text)) { NamedMusic val = Choose(music, (Season)((SeasonView)(ref view)).Season, text); if (val != null && val != result) { result = val; playing = val.m_name; } } } private static bool Wanted() { return Plugin.Settings?.Get<bool>(SolsticeSettings.Music.SeasonalMusic) ?? false; } private static NamedMusic? Choose(MusicMan music, Season season, string ambient) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between I4 and Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected I4, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) List<NamedMusic> music2 = music.m_music; int num = music2?.Count ?? 0; if (cachedSeason == (int)season && cachedCount == num && string.Equals(cachedAmbient, ambient, StringComparison.Ordinal)) { return chosen; } cachedSeason = (int)season; cachedCount = num; cachedAmbient = ambient; chosen = null; if (music2 == null || num == 0) { return null; } if (buffer.Length < num) { int num2; for (num2 = ((buffer.Length == 0) ? 16 : buffer.Length); num2 < num; num2 *= 2) { } buffer = (MusicTrack[])(object)new MusicTrack[num2]; source = new int[num2]; } int num3 = 0; for (int i = 0; i < num; i++) { NamedMusic val = music2[i]; if (val != null && !string.IsNullOrEmpty(val.m_name)) { bool flag = val.m_enabled && val.m_clips != null && val.m_clips.Length != 0; buffer[num3] = new MusicTrack(val.m_name, val.m_ambientMusic, flag); source[num3] = i; num3++; } } int num4 = SeasonMusic.Select(season, ambient, buffer, num3); if (num4 < 0) { return null; } chosen = music2[source[num4]]; return chosen; } private static void Release() { string text = playing; playing = null; if (MeadworksHeadless.AllowsUi && text != null) { MusicMan instance = MusicMan.instance; if ((Object)(object)instance != (Object)null && instance.GetCurrentMusic() == text) { instance.StopMusic(); } } } } [HarmonyPatch(typeof(MusicMan), "GetEnvironmentMusic")] internal static class SeasonMusicPatch { private static void Postfix(MusicMan __instance, ref NamedMusic __result) { SeasonMusicRuntime.Apply(__instance, ref __result); } } public sealed class SolsticeCommand : ConsoleCommand { public const string CommandName = "solstice"; public override string Name => "solstice"; public override string Help => "Solstice seasons: status."; public override void Run(string[] args) { foreach (string item in Execute(args)) { Console instance = Console.instance; if (instance != null) { instance.Print(item); } } } private static IEnumerable<string> Execute(string[] args) { if (args.Length != 0 && !string.Equals(args[0], "status", StringComparison.OrdinalIgnoreCase)) { yield return SolsticeText.Text("cmd_help"); yield break; } SeasonView view = SolsticeSeasons.View; if (Plugin.Settings == null || !SolsticeModules.Active((SolsticeModule)0)) { yield return SolsticeText.Text("cmd_inactive"); yield break; } if (!((SeasonView)(ref view)).Available) { yield return SolsticeText.Text("cmd_waiting"); yield break; } string text = SolsticeText.Text(SolsticeTextKeys.SeasonName((Season)((SeasonView)(ref view)).Season)); yield return SolsticeText.Format(SolsticeSeasons.Held.HasValue ? "cmd_status_override" : "cmd_status", text, ((SeasonView)(ref view)).DayInSeason, ((SeasonView)(ref view)).DaysInSeason); } } public static class SeasonImport { private const string FileName = "Solstice.seasons.json"; public const string MarkerName = "com.meadworks.solstice.seasons-import"; public const string LegacyMarkerName = "com.extrapotions.solstice.seasons-import"; public static void Run(SolsticeConfig settings, MeadworksLog log) { if (settings == null || log == null) { return; } string path = Path.Combine(Paths.ConfigPath, "Solstice.seasons.json"); if (!File.Exists(path)) { return; } byte[] array; try { array = File.ReadAllBytes(path); } catch (Exception ex) { log.Warning("Seasons import was not read: " + ex.GetType().Name + "."); return; } string text; using (SHA256 sHA = SHA256.Create()) { text = BitConverter.ToString(sHA.ComputeHash(array)).Replace("-", "").ToLowerInvariant(); } string path2 = Path.Combine(Paths.ConfigPath, "com.meadworks.solstice.seasons-import"); if (File.Exists(path2) && string.Equals(ReadMarker(path2), text, StringComparison.Ordinal)) { return; } ImportPlan val = SeasonConfigImport.Plan(Encoding.UTF8.GetString(array)); if (!val.Ok) { log.Warning("Seasons import was not applied: " + val.Error); return; } int num = settings.ApplyImport(val.Changes); try { File.WriteAllText(path2, text); } catch (Exception ex2) { log.Warning("Seasons import marker was not saved: " + ex2.GetType().Name + "."); } log.Info("Seasons import applied " + num + " default setting" + ((num == 1) ? "" : "s") + ". Left unmapped: " + val.Unmapped.Count + ". Unknown fields: " + val.Unknown.Count + "."); } private static string ReadMarker(string path) { try { return File.ReadAllText(path).Trim(); } catch (Exception) { return ""; } } } public sealed class SolsticeConfig { private const string Product = "com.meadworks.solstice"; private readonly ConfigFile file; private readonly Dictionary<SolsticeSetting, ConfigEntryBase> entries = new Dictionary<SolsticeSetting, ConfigEntryBase>(); private SeasonCalendar? cachedCalendar; private int cachedSpring; private int cachedSummer; private int cachedFall; private int cachedWinter; public ConfigFile File => file; public event Action? Changed; private SolsticeConfig(ConfigFile file) { this.file = file; bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { foreach (SolsticeSetting item in SolsticeSettings.All) { BindSetting(item); } } finally { file.SaveOnConfigSet = saveOnConfigSet; } file.Save(); } public static SolsticeConfig Bind(ConfigFile file, MeadworksLog log) { //IL_006f: 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) if (file == null) { throw new ArgumentNullException("file"); } if (log == null) { throw new ArgumentNullException("log"); } SolsticeConfig solsticeConfig = new SolsticeConfig(file); foreach (KeyValuePair<SolsticeSetting, ConfigEntryBase> item in solsticeConfig.entries.Where<KeyValuePair<SolsticeSetting, ConfigEntryBase>>((KeyValuePair<SolsticeSetting, ConfigEntryBase> p) => (int)p.Key.Scope != 0 && !p.Key.Hidden)) { MeadworksBlockedOptions.Track("com.meadworks.solstice", item.Value, Map(item.Key.Scope), true); } file.SettingChanged += solsticeConfig.OnSettingChanged; file.ConfigReloaded += solsticeConfig.OnReloaded; log.Verbose("Configuration bound: " + Count((SettingScope)0) + " Local, " + Count((SettingScope)1) + " ServerAuthoritative, " + Count((SettingScope)2) + " Cheat."); return solsticeConfig; } 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(SolsticeSetting 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)SolsticeSettings.CategoryOf(cheat) == 1) { return (MischiefCategory)2; } return (MischiefCategory)0; } public ConfigEntryBase Entry(SolsticeSetting setting) { if (!entries.TryGetValue(setting ?? throw new ArgumentNullException("setting"), out ConfigEntryBase value)) { throw new ArgumentException("Not a Solstice setting.", "setting"); } return value; } public T Get<T>(SolsticeSetting<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((SolsticeSetting)(object)setting); SettingScope scope = ((SolsticeSetting)setting).Scope; if ((int)scope != 1) { if ((int)scope == 2) { return SolsticeCheats.Effective<T>(setting, CheatLocked(), val.Value); } return setting.Normalize(val.Value); } return setting.Normalize(MeadworksConfig.Effective<T>(val, "com.meadworks.solstice", (ConfigScope)1, setting.Default, true)); } public SeasonCalendar Calendar() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown int num = Get<int>(SolsticeSettings.Calendar.DaysIn((Season)0)); int num2 = Get<int>(SolsticeSettings.Calendar.DaysIn((Season)1)); int num3 = Get<int>(SolsticeSettings.Calendar.DaysIn((Season)2)); int num4 = Get<int>(SolsticeSettings.Calendar.DaysIn((Season)3)); if (cachedCalendar != null && cachedSpring == num && cachedSummer == num2 && cachedFall == num3 && cachedWinter == num4) { return cachedCalendar; } SeasonCalendar result = new SeasonCalendar(num, num2, num3, num4); cachedSpring = num; cachedSummer = num2; cachedFall = num3; cachedWinter = num4; cachedCalendar = result; return result; } public ProtectionRules Protection() { return SolsticeSettings.RulesFrom((Func<SolsticeSetting<bool>, bool>)Get<bool>); } public SeasonEffects Effects(Season season) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return SolsticeSettings.Seasons.For(season).Resolve((Func<SolsticeSetting<float>, float>)Get<float>, (Func<SolsticeSetting<bool>, bool>)Get<bool>); } public Season? HeldSeason(out bool blocked) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) SolsticeSetting<SeasonOverride> seasonOverride = SolsticeSettings.Cheats.SeasonOverride; SeasonOverride value = ((ConfigEntry<SeasonOverride>)(object)Entry((SolsticeSetting)(object)seasonOverride)).Value; blocked = (int)value != 0 && CheatLocked(); return SeasonOverrides.ToSeason(Get<SeasonOverride>(seasonOverride)); } public void ReportBlockedCheat(SolsticeSetting<SeasonOverride> 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)((SolsticeSetting)cheat).Scope != 2) { throw new ArgumentException("Expected a Solstice cheat.", "cheat"); } MeadworksBlockedOptions.TryUse("com.meadworks.solstice", ((SolsticeSetting)cheat).Id, (ConfigScope)2, label, true); } public void Shutdown() { file.SettingChanged -= OnSettingChanged; file.ConfigReloaded -= OnReloaded; foreach (ConfigEntryBase value in entries.Values) { MeadworksBlockedOptions.Untrack(value); } entries.Clear(); this.Changed = null; } public int ApplyImport(IReadOnlyList<ImportChange> changes) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (changes == null) { return 0; } int num = 0; bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { foreach (ImportChange change in changes) { ImportChange current = change; SolsticeSetting val = SolsticeSettings.Find(((ImportChange)(ref current)).Id); if (val != null && entries.TryGetValue(val, out ConfigEntryBase value) && ApplyOne(val, value, ((ImportChange)(ref current)).Text)) { num++; } } } finally { file.SaveOnConfigSet = saveOnConfigSet; if (num > 0) { file.Save(); } } return num; } 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.solstice", (ConfigScope)2, true).Source == 2; } private void BindSetting(SolsticeSetting setting) { ConfigEntryBase value; if (!(setting is SolsticeSetting<bool> setting2)) { if (!(setting is SolsticeSetting<int> val)) { if (!(setting is SolsticeSetting<float> val2)) { if (!(setting is SolsticeSetting<string> setting3)) { if (!(setting is SolsticeSetting<SeasonOverride> setting4)) { throw new NotSupportedException(setting.Id + ": unsupported value type " + setting.ValueType.Name + "."); } value = (ConfigEntryBase)(object)Bind<SeasonOverride>(setting4, null); } else { value = (ConfigEntryBase)(object)Bind<string>(setting3, null); } } else { value = (ConfigEntryBase)(object)Bind<float>(val2, (AcceptableValueBase?)(object)new AcceptableValueRange<float>((float)((SolsticeSetting)val2).Minimum, (float)((SolsticeSetting)val2).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<int>(val, (AcceptableValueBase?)(object)new AcceptableValueRange<int>((int)((SolsticeSetting)val).Minimum, (int)((SolsticeSetting)val).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<bool>(setting2, null); } entries.Add(setting, value); } private ConfigEntry<T> Bind<T>(SolsticeSetting<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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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)((SolsticeSetting)setting).Scope == 2) { return MeadworksConfig.BindCheat<T>(file, ((SolsticeSetting)setting).Section, ((SolsticeSetting)setting).Key, setting.Safe, ((SolsticeSetting)setting).Description, CategoryOf((SolsticeSetting)(object)setting), acceptable); } if (((SolsticeSetting)setting).Hidden) { return MeadworksConfig.BindHidden<T>(file, ((SolsticeSetting)setting).Section, ((SolsticeSetting)setting).Key, setting.Default, ((SolsticeSetting)setting).Description, Map(((SolsticeSetting)setting).Scope), acceptable); } return MeadworksConfig.Bind<T>(file, ((SolsticeSetting)setting).Section, ((SolsticeSetting)setting).Key, setting.Default, ((SolsticeSetting)setting).Description, Map(((SolsticeSetting)setting).Scope), acceptable); } 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 SolsticeSettings.All.Count((SolsticeSetting s) => s.Scope == scope && !s.Hidden); } private static bool ApplyOne(SolsticeSetting setting, ConfigEntryBase entry, string text) { if (!(setting is SolsticeSetting<bool> val)) { if (!(setting is SolsticeSetting<int> val2)) { if (setting is SolsticeSetting<float> val3) { if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } result = val3.Normalize(result); ConfigEntry<float> val4 = (ConfigEntry<float>)(object)entry; if (!SeasonConfigImport.ShouldApply(Format(val4.Value), Format(val3.Default), Format(result))) { return false; } val4.Value = result; return true; } return false; } if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } result2 = val2.Normalize(result2); ConfigEntry<int> val5 = (ConfigEntry<int>)(object)entry; if (!SeasonConfigImport.ShouldApply(val5.Value.ToString(CultureInfo.InvariantCulture), val2.Default.ToString(CultureInfo.InvariantCulture), result2.ToString(CultureInfo.InvariantCulture))) { return false; } val5.Value = result2; return true; } if (!bool.TryParse(text, out var result3)) { return false; } ConfigEntry<bool> val6 = (ConfigEntry<bool>)(object)entry; if (!SeasonConfigImport.ShouldApply(val6.Value ? "true" : "false", val.Default ? "true" : "false", result3 ? "true" : "false")) { return false; } val6.Value = val.Normalize(result3); return true; } private static string Format(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } } public static class SeasonCrops { [HarmonyPatch(typeof(Pickable), "Awake")] private static class Awake { private static void Postfix(Pickable __instance) { try { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && Controlled(NameOf((Component)(object)__instance)) && (Object)(object)((Component)__instance).GetComponent<CropWatch>() == (Object)null) { CropWatch cropWatch = ((Component)__instance).gameObject.AddComponent<CropWatch>(); cropWatch.Crop = __instance; watches.Add(cropWatch); ((MonoBehaviour)cropWatch).InvokeRepeating("Check", Random.Range(1f, 5f), 1f); } } catch (Exception ex) { SolsticeSeasons.LogWarning("Crop watch: " + ex.GetType().Name); } } } [HarmonyPatch(typeof(Pickable), "UpdateRespawn")] private static class Respawn { private static bool Prefix(Pickable __instance) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 try { Check(__instance); if (!TryDecide(__instance, out CropWinterDecision decision, out ZNetView _, out bool _)) { return true; } return (int)((CropWinterDecision)(ref decision)).Action != 2 && (int)((CropWinterDecision)(ref decision)).Action != 3 && (int)((CropWinterDecision)(ref decision)).Action != 4 && (int)((CropWinterDecision)(ref decision)).Action != 5; } catch (Exception ex) { SolsticeSeasons.LogWarning("Crop winter: " + ex.GetType().Name); return true; } } } public sealed class CropWatch : MonoBehaviour { public Pickable Crop; public void Check() { if ((Object)(object)Crop != (Object)null) { SeasonCrops.Check(Crop); } } private void OnDestroy() { watches.Remove(this); } } [HarmonyPatch(typeof(Pickable), "GetHoverText")] private static class Hover { private static void Postfix(Pickable __instance, ref string __result) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) try { if (TryDecide(__instance, out CropWinterDecision decision, out ZNetView _, out bool _)) { string text = Line(decision); if (text.Length != 0) { __result = (string.IsNullOrEmpty(__result) ? text : (__result + "\n" + text)); } } } catch (Exception ex) { SolsticeSeasons.LogWarning("Crop hover: " + ex.GetType().Name); } } } private static readonly int FreezeKey = StringExtensionMethods.GetStableHashCode("solstice_freeze"); private static readonly int RealFreezeKey = StringExtensionMethods.GetStableHashCode("solstice_freeze_remaining"); private static readonly int WindowKey = StringExtensionMethods.GetStableHashCode("solstice_freeze_window"); private static readonly int SurvivedKey = StringExtensionMethods.GetStableHashCode("solstice_survived"); private static ConditionalWeakTable<Pickable, CropFreezeTimer> timers = new ConditionalWeakTable<Pickable, CropFreezeTimer>(); private static readonly HashSet<CropWatch> watches = new HashSet<CropWatch>(); public static void Shutdown() { foreach (CropWatch watch in watches) { if (!((Object)(object)watch == (Object)null)) { ((MonoBehaviour)watch).CancelInvoke(); Object.Destroy((Object)(object)watch); } } watches.Clear(); timers = new ConditionalWeakTable<Pickable, CropFreezeTimer>(); } private static void Check(Pickable crop) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) try { ZNetView nview = crop.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO() == null) { return; } CropFreezeTimer orCreateValue = timers.GetOrCreateValue(crop); ZDO zDO = nview.GetZDO(); long num = zDO.GetLong(RealFreezeKey, 0L); bool flag = nview.IsOwner(); bool flag2 = SolsticeSeasons.Effects != null && Controlled(NameOf((Component)(object)crop)); if (flag && flag2 && num > 0) { long num2 = FreezeMilliseconds(); long num3 = zDO.GetLong(WindowKey, num2); if (num3 != num2) { num = Math.Max(1L, Math.Min(num2 + 1, num + num2 - num3)); zDO.Set(RealFreezeKey, num); zDO.Set(WindowKey, num2); } } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; long num4 = orCreateValue.Advance(num, flag && flag2, realtimeSinceStartupAsDouble); if (flag && num != num4) { zDO.Set(RealFreezeKey, num4); } if (TryDecide(crop, out CropWinterDecision decision, out ZNetView _, out bool _) && flag) { Apply(crop, nview, decision); } orCreateValue.Advance(zDO.GetLong(RealFreezeKey, 0L), flag && flag2, realtimeSinceStartupAsDouble); } catch (Exception ex) { SolsticeSeasons.LogWarning("Crop winter: " + ex.GetType().Name); } } private static bool TryDecide(Pickable pickable, out CropWinterDecision decision, out ZNetView view, out bool owner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_0111: Invalid comparison between Unknown and I4 //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_0166: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: 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) decision = default(CropWinterDecision); owner = false; view = pickable.m_nview; SeasonClock? clock = SolsticeSeasons.Clock; SeasonEffects effects = SolsticeSeasons.Effects; if (!clock.HasValue || effects == null || (Object)(object)view == (Object)null || !view.IsValid() || view.GetZDO() == null) { return false; } if (!Controlled(NameOf((Component)(object)pickable))) { return false; } owner = view.IsOwner(); ZDO zDO = view.GetZDO(); long num = zDO.GetLong(RealFreezeKey, 0L); int num2 = zDO.GetInt(SurvivedKey, 0); SeasonClock value = clock.Value; int anchorDay = ((SeasonClock)(ref value)).AnchorDay; value = clock.Value; bool flag = CropWinter.SurvivedWinter(num2, anchorDay, ((SeasonClock)(ref value)).DaysIn((Season)3)); double num3 = ((num > 0) ? ((double)(num - 1) / 1000.0) : 0.0); Plant component = ((Component)pickable).GetComponent<Plant>(); bool flag2 = SolsticeSeasons.HardyCrops.Survives((Object)(object)component != (Object)null && component.m_tolerateCold, (IEnumerable<string>)new string[1] { NameOf((Component)(object)pickable) }); bool flag3 = (int)SolsticeSeasons.Current.GetValueOrDefault() == 3 && effects.PlantsGrowthMultiplier <= 0f; bool cropsPerish = SolsticeSeasons.CropsPerish; bool num4 = pickable.CanBePicked(); bool picked = pickable.m_picked; bool num5 = SeasonProtection.IsProtected(((Component)pickable).transform.position); bool num6 = SeasonProtection.ProtectedWithHeat(((Component)pickable).transform.position); SeasonView view2 = SolsticeSeasons.View; int dayInSeason = ((SeasonView)(ref view2)).DayInSeason; int perishFromDay = SolsticeSeasons.PerishFromDay; float freezeSeconds = SolsticeSeasons.FreezeSeconds; float harvestChance = SolsticeSeasons.HarvestChance; bool num7 = num > 0; long num8 = IdOf(zDO); value = clock.Value; decision = CropWinter.Decide(cropsPerish, flag3, num4, picked, true, num5, flag2, num6, flag, dayInSeason, perishFromDay, freezeSeconds, harvestChance, num7, num3, CropWinter.Roll(num8, ((SeasonClock)(ref value)).WorldDay)); return true; } private static void Apply(Pickable pickable, ZNetView view, CropWinterDecision decision) { //IL_000f: 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_0015: 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_0031: Expected I4, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = view.GetZDO(); SeasonClock? clock = SolsticeSeasons.Clock; CropWinterAction action = ((CropWinterDecision)(ref decision)).Action; switch (action - 1) { case 1: if (zDO.GetLong(RealFreezeKey, 0L) == 0L) { zDO.Set(RealFreezeKey, FreezeMilliseconds() + 1); } zDO.Set(WindowKey, FreezeMilliseconds()); zDO.Set(FreezeKey, 0L); break; case 3: if (clock.HasValue) { int survivedKey = SurvivedKey; SeasonClock value = clock.Value; zDO.Set(survivedKey, ((SeasonClock)(ref value)).WorldDay, false); } zDO.Set(RealFreezeKey, 0L); zDO.Set(FreezeKey, 0L); break; case 4: zDO.Set(RealFreezeKey, 0L); zDO.Set(FreezeKey, 0L); Harvest(pickable, view); break; case 0: if (zDO.GetLong(RealFreezeKey, 0L) != 0L) { zDO.Set(RealFreezeKey, 0L); } if (zDO.GetLong(FreezeKey, 0L) != 0L) { zDO.Set(FreezeKey, 0L); } break; case 2: break; } } private static void Harvest(Pickable pickable, ZNetView view) { if (!view.IsOwner() || pickable.m_picked || (Object)(object)pickable.m_itemPrefab == (Object)null) { return; } pickable.m_picked = true; view.InvokeRPC(ZNetView.Everybody, "RPC_SetPicked", new object[1] { true }); int num = (pickable.m_dontScale ? pickable.m_amount : Math.Max(pickable.m_minAmountScaled, Game.instance.ScaleDrops(pickable.m_itemPrefab, pickable.m_amount))); int num2 = 0; for (int i = 0; i < num; i++) { pickable.Drop(pickable.m_itemPrefab, num2++, 1); } if (pickable.m_extraDrops.IsEmpty()) { return; } foreach (ItemData dropListItem in pickable.m_extraDrops.GetDropListItems()) { pickable.Drop(dropListItem.m_dropPrefab, num2++, dropListItem.m_stack); } } private static string Line(CropWinterDecision decision) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //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) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected I4, but got Unknown CropHover hover = ((CropWinterDecision)(ref decision)).Hover; return (hover - 1) switch { 0 => SolsticeText.Text("plant_frost_resistant"), 1 => SolsticeText.Text("plant_heat_protected"), 2 => SolsticeText.Text("plant_survived_winter"), 3 => SolsticeText.Format("plant_freezing", ((CropWinterDecision)(ref decision)).HoverSeconds), 4 => SolsticeText.Text("plant_will_perish"), 5 => SolsticeText.Text("plant_exposed"), _ => "", }; } private static bool Controlled(string name) { return SolsticeSeasons.ControlledCrops.Survives(false, (IEnumerable<string>)new string[1] { name }); } private static long FreezeMilliseconds() { return (long)Math.Ceiling((double)CropWinter.FreezeWindow(SolsticeSeasons.FreezeSeconds) * 1000.0); } private static string NameOf(Component component) { return CropWinter.PrefabName(((Object)(object)component != (Object)null) ? ((Object)component).name : null); } private static long IdOf(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = zdo.m_uid; return ((ZDOID)(ref uid)).UserID ^ (long)((ulong)((ZDOID)(ref uid)).ID << 17); } } public static class SeasonPatches { [HarmonyPatch(typeof(Plant), "TimeSincePlanted")] private static class PlantTimeSincePlanted { private static void Postfix(Plant __instance, ref double __result) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (Now != null && !((Object)(object)ZNet.instance == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null) && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { __result = ScaleElapsed(__result, (SeasonEffects effects) => effects.PlantsGrowthMultiplier); } } } [HarmonyPatch(typeof(Plant), "UpdateHealth")] private static class PlantCold { private static void Prefix(Plant __instance, ref double timeSincePlanted, out bool __state) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) __state = Now == null || SeasonProtection.IsProtected(((Component)__instance).transform.position); if (!__state) { timeSincePlanted = ColdPlants.AdjustedAge(timeSincePlanted, SolsticeSeasons.Current.GetValueOrDefault(), Now.PlantsGrowthMultiplier); } } private static void Postfix(Plant __instance, bool __state) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Invalid comparison between Unknown and I4 //IL_0090: 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) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) SeasonEffects now = Now; if (!__state && now != null && SolsticeSeasons.Current.HasValue) { string[] array = ((__instance.m_grownPrefabs == null) ? Array.Empty<string>() : (from g in __instance.m_grownPrefabs where (Object)(object)g != (Object)null select ((Object)g).name).ToArray()); if (ColdPlants.IsTooCold((int)__instance.m_status == 0, SolsticeSeasons.Current.Value, now.PlantsGrowthMultiplier, SolsticeSeasons.HardyCrops.Survives(__instance.m_tolerateCold, (IEnumerable<string>)array), SeasonProtection.ProtectedWithHeat(((Component)__instance).transform.position))) { __instance.m_status = (Status)7; } } } } [HarmonyPatch(typeof(Beehive), "GetTimeSinceLastUpdate")] private static class BeehiveTime { private static void Postfix(Beehive __instance, ref float __result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Now != null && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { __result = (float)ScaleElapsed(__result, (SeasonEffects effects) => effects.BeehiveProductionMultiplier); } } } [HarmonyPatch(typeof(Fireplace), "GetTimeSinceLastUpdate")] private static class FireplaceTime { private static void Postfix(Fireplace __instance, ref double __result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Now != null && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { __result = ScaleElapsed(__result, (SeasonEffects effects) => effects.FireplaceDrainMultiplier); } } } [HarmonyPatch(typeof(CookingStation), "UpdateFuel")] private static class CookingStationFuel { private static void Prefix(CookingStation __instance, ref float dt) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Now != null && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { dt *= Math.Max(0f, Now.FireplaceDrainMultiplier); } } } [HarmonyPatch(typeof(SapCollector), "GetTimeSinceLastUpdate")] private static class SapTime { private static void Postfix(SapCollector __instance, ref float __result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Now != null && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { __result = (float)ScaleElapsed(__result, (SeasonEffects effects) => effects.SapCollectingSpeedMultiplier); } } } [HarmonyPatch(typeof(Player), "UpdateFood")] private static class FoodDrain { private static void Prefix(Player __instance, float dt, bool forceUpdate) { SeasonEffects now = Now; if (now == null || (Object)(object)__instance == (Object)null || Math.Abs(now.FoodDrainMultiplier - 1f) < 1E-06f || Sheltered(__instance) || !(dt * Game.m_foodRate + __instance.m_foodUpdateTimer >= 1f || forceUpdate)) { return; } foreach (Food food in __instance.m_foods) { food.m_time += 1f - Math.Max(0f, now.FoodDrainMultiplier); } } } [HarmonyPatch(typeof(Player), "UseStamina")] private static class StaminaDrain { private static void Prefix(Player __instance, ref float v) { SeasonEffects now = Now; if (now != null && !((Object)(object)__instance == (Object)null) && !Sheltered(__instance)) { v *= Math.Max(0f, now.StaminaDrainMultiplier); } } } [HarmonyPatch(typeof(SE_Rested), "UpdateTTL")] private static class RestedDuration { private static void Prefix(SE_Rested __instance, out Tuple<float, float>? __state) { __state = null; SeasonEffects now = Now; if (now != null && !(Math.Abs(now.RestedBuffDurationMultiplier - 1f) < 1E-06f)) { __state = Tuple.Create(__instance.m_baseTTL, __instance.m_TTLPerComfortLevel); __instance.m_baseTTL *= now.RestedBuffDurationMultiplier; __instance.m_TTLPerComfortLevel *= now.RestedBuffDurationMultiplier; } } private static void Finalizer(SE_Rested __instance, Tuple<float, float>? __state) { if (__state != null) { __instance.m_baseTTL = __state.Item1; __instance.m_TTLPerComfortLevel = __state.Item2; } } } [HarmonyPatch(typeof(EnvMan), "GetWindForce")] private static class WindForce { private static void Postfix(ref Vector3 __result) { //IL_0009: 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_001d: Unknown result type (might be due to invalid IL or missing references) if (Now != null) { __result *= Now.WindIntensityMultiplier; } } } [HarmonyPatch(typeof(EnvMan), "GetWindIntensity")] private static class WindIntensity { private static void Postfix(ref float __result) { if (Now != null) { __result *= Now.WindIntensityMultiplier; } } } [HarmonyPatch(typeof(WearNTear), "UpdateWear")] private static class RainProtection { private static void Prefix(WearNTear __instance, out bool? __state) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) __state = null; if (Now != null && Now.RainProtection && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && !SeasonProtection.IsProtected(((Component)__instance).transform.position)) { __state = __instance.m_noRoofWear; __instance.m_noRoofWear = false; } } private static void Finalizer(WearNTear __instance, bool? __state) { if (__state.HasValue) { __instance.m_noRoofWear = __state.Value; } } } private static SeasonEffects? Now => SolsticeSeasons.Effects; private static bool Sheltered(Player player) { if (!((Character)player).InInterior()) { return player.InShelter(); } return true; } private static double ScaleElapsed(double elapsed, Func<SeasonEffects, float> rate) { //IL_0067: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (Now == null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null) { return elapsed; } SeasonClock? clock = SolsticeSeasons.Clock; Func<Season, double> func = delegate(Season season) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) SeasonEffects val2 = SolsticeSeasons.EffectsOf(season); return Math.Max(0f, (val2 != null) ? rate(val2) : 1f); }; if (clock.HasValue) { double timeSeconds = ZNet.instance.GetTimeSeconds(); double num = EnvMan.instance.m_dayLengthSec; SeasonClock value = clock.Value; SeasonCalendar calendar = ((SeasonClock)(ref value)).Calendar; Season? held = SolsticeSeasons.Held; Season val; if (!held.HasValue) { value = clock.Value; val = ((SeasonClock)(ref value)).Season; } else { val = held.GetValueOrDefault(); } int dayInSeason; if (!SolsticeSeasons.Held.HasValue) { value = clock.Value; dayInSeason = ((SeasonClock)(ref value)).DayInSeason; } else { SeasonView view = SolsticeSeasons.View; dayInSeason = ((SeasonView)(ref view)).DayInSeason; } return SeasonTime.ScaleAt(elapsed, timeSeconds, num, calendar, func, val, dayInSeason); } return SeasonTime.Scale(elapsed, ZNet.instance.GetTimeSeconds(), (double)EnvMan.instance.m_dayLengthSec, SolsticeSeasons.Calendar, func); } } public static class SeasonProtection { private const int CacheLimit = 15000; private static readonly Dictionary<Vector2, bool> IgnoredTerrain = new Dictionary<Vector2, bool>(); private static readonly Dictionary<Vector3, bool> ShieldedPlaces = new Dictionary<Vector3, bool>(); private static int shieldChangeId; private static int shieldGeneratorCache; public static bool IsProtected(Vector3 position) { //IL_0011: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) Season? current = SolsticeSeasons.Current; if (!current.HasValue) { return false; } bool flag = Character.InInterior(position); bool ashlandsOrDeepNorth = false; bool highMountain = false; if (!flag) { Terrain(position, out ashlandsOrDeepNorth, out highMountain); } bool flag2 = !flag && !ashlandsOrDeepNorth && !highMountain && IsShielded(position); return (int)SolsticeSeasons.Protection.Decide(new PositionFacts(flag, ashlandsOrDeepNorth, highMountain, flag2), current.Value) > 0; } public static bool ProtectedWithHeat(Vector3 position) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (SolsticeSeasons.HeatProtectsCrops) { return Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(position, (Type)1, 0f)); } return false; } private static void Terrain(Vector3 position, out bool ashlandsOrDeepNorth, out bool highMountain) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_0092: 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_006a: Invalid comparison between Unknown and I4 //IL_006d: 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) ashlandsOrDeepNorth = (highMountain = false); WorldGenerator instance = WorldGenerator.instance; if (instance == null) { return; } Vector2 key = default(Vector2); ((Vector2)(ref key))..ctor(position.x, position.z); if (IgnoredTerrain.TryGetValue(key, out var value)) { ashlandsOrDeepNorth = value; return; } if (IgnoredTerrain.Count > 15000) { IgnoredTerrain.Clear(); } Biome biome = instance.GetBiome(position); ashlandsOrDeepNorth = (int)biome == 32 || (int)biome == 64 || ((int)biome == 4 && instance.GetBaseHeight(position.x, position.z, false) > 0.45000002f); IgnoredTerrain[key] = ashlandsOrDeepNorth; } private static bool IsShielded(Vector3 position) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) Season? current = SolsticeSeasons.Current; if (current.HasValue) { Season valueOrDefault = current.GetValueOrDefault(); if (!SolsticeSeasons.Protection.ShieldActive(valueOrDefault)) { return false; } } int instanceChangeID = ShieldGenerator.m_instanceChangeID; if (shieldChangeId != instanceChangeID) { ShieldedPlaces.Clear(); shieldChangeId = instanceChangeID; } if (ShieldedPlaces.TryGetValue(position, out var value)) { return value; } if (ShieldedPlaces.Count > 15000) { ShieldedPlaces.Clear(); } value = ShieldGenerator.IsInsideShieldCached(position, ref shieldGeneratorCache); ShieldedPlaces[position] = value; return value; } public static void Reset() { IgnoredTerrain.Clear(); ShieldedPlaces.Clear(); shieldChangeId = 0; shieldGeneratorCache = 0; } } public sealed class SolsticeSeasonStatus : SE_Stats { public readonly Dictionary<SkillType, float> Raise = new Dictionary<SkillType, float>(); public readonly Dictionary<SkillType, float> Levels = new Dictionary<SkillType, float>(); public override void ModifyRaiseSkill(SkillType skill, ref float value) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (Raise.TryGetValue(skill, out var value2)) { value += value2; } } public override void ModifySkillLevel(SkillType skill, ref float level) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (Levels.TryGetValue(skill, out var value)) { level += value; } } } public static class SeasonStatus { private const string SeasonKey = "SolsticeSeason"; private const string OverheatKey = "SolsticeOverheat"; private static SolsticeSeasonStatus? seasonTemplate; private static SE_Stats? overheatTemplate; private static SolsticeSeasonStatus? seasonLive; private static SE_Stats? overheatLive; private static Season? cachedSeason; private static SolsticeSeasonStatus? cachedEffect; private static string cachedSeasonName = ""; private static int cachedDay = -1; private static int cachedLength = -1; private static string coolingText = ""; private static WinterCrops? coolingFoods; public static float ShownHeat { get; private set; } public static void Tick() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || localPlayer.m_isLoading || (Object)(object)((Character)localPlayer).m_nview == (Object)null || !((Character)localPlayer).m_nview.IsValid()) { ShownHeat = 0f; return; } SEMan sEMan = ((Character)localPlayer).GetSEMan(); if (sEMan != null) { int num; if (SolsticeSeasons.Effects != null && SolsticeSeasons.Current.HasValue) { SeasonView view = SolsticeSeasons.View; num = (((SeasonView)(ref view)).Available ? 1 : 0); } else { num = 0; } bool effects = (byte)num != 0; HoldSeason(localPlayer, sEMan, effects); HoldOverheat(localPlayer, sEMan, effects); } } public static void Shutdown() { ReleaseLive(); if ((Object)(object)seasonTemplate != (Object)null) { Object.Destroy((Object)(object)seasonTemplate); } if ((Object)(object)overheatTemplate != (Object)null) { Object.Destroy((Object)(object)overheatTemplate); } seasonTemplate = null; overheatTemplate = null; } public static void ReleaseLive() { Player localPlayer = Player.m_localPlayer; SEMan val = (((Object)(object)localPlayer != (Object)null) ? ((Character)localPlayer).GetSEMan() : null); if (val != null) { if ((Object)(object)seasonLive != (Object)null) { val.RemoveStatusEffect(((StatusEffect)seasonLive).NameHash(), false); } if ((Object)(object)overheatLive != (Object)null) { val.RemoveStatusEffect(((StatusEffect)overheatLive).NameHash(), false); } } seasonLive = null; overheatLive = null; cachedSeason = null; cachedEffect = null; cachedSeasonName = ""; cachedDay = -1; cachedLength = -1; ShownHeat = 0f; } private static void HoldSeason(Player player, SEMan seman, bool effects) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Invalid comparison between Unknown and I4 Season? current = SolsticeSeasons.Current; if (!effects || !current.HasValue || !SolsticeSeasons.SeasonStatusEnabled || !ClimateRules.StatusApplies(true, ((Character)player).InInterior(), AshOrDeep(player))) { Drop<SolsticeSeasonStatus>(seman, ref seasonLive); cachedSeason = null; cachedEffect = null; return; } SolsticeSeasonStatus solsticeSeasonStatus = SeasonTemplate(); if (!Worn(seman, (StatusEffect?)(object)seasonLive)) { seasonLive = seman.AddStatusEffect((StatusEffect)(object)solsticeSeasonStatus, false, 0, 0f, (short)(-1)) as SolsticeSeasonStatus; } if (!((Object)(object)seasonLive == (Object)null)) { bool num = cachedEffect == seasonLive; if (!num || cachedSeason != current) { Apply(seasonLive, SeasonStatTable.Defaults(current.Value)); cachedEffect = seasonLive; cachedSeason = current; } SeasonView view = SolsticeSeasons.View; string text = SolsticeText.Text(SolsticeTextKeys.SeasonName(current.Value)); if (SeasonRebuild.Text(num, text, cachedSeasonName, ((SeasonView)(ref view)).DayInSeason, cachedDay, ((SeasonView)(ref view)).DaysInSeason, cachedLength)) { ((StatusEffect)seasonLive).m_name = text; ((StatusEffect)seasonLive).m_tooltip = SolsticeText.Format("status_season_tip", ((SeasonView)(ref view)).DayInSeason, ((SeasonView)(ref view)).DaysInSeason); cachedSeasonName = text; cachedDay = ((SeasonView)(ref view)).DayInSeason; cachedLength = ((SeasonView)(ref view)).DaysInSeason; } ((StatusEffect)seasonLive).m_ttl = 0f; Sprite val = SeasonMarks.For(current.Value); if ((Object)(object)val == (Object)null && (int)current.Value == 3) { val = SeasonMarks.Mark((Season)3); } if ((Object)(object)val != (Object)null) { ((StatusEffect)seasonLive).m_icon = val; } else if ((Object)(object)((StatusEffect)seasonLive).m_icon == (Object)null || SeasonMarks.Owns(((StatusEffect)seasonLive).m_icon)) { ((StatusEffect)seasonLive).m_icon = Borrow("Rested", "Cold", "Freezing"); } } } private static void HoldOverheat(Player player, SEMan seman, bool effects) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) Season? current = SolsticeSeasons.Current; SeasonEffects val = (current.HasValue ? SolsticeSeasons.EffectsOf(current.Value) : null); bool flag = ((Character)player).InInterior() || player.InShelter() || EnvMan.IsCold() || EnvMan.IsFreezing(); int num = WarmClothes(player); bool flag2 = Cooling(player); bool flag3 = val != null && val.OverheatIn2WarmClothes; ShownHeat = HeatMeter.Target((Plugin.Settings?.Get<bool>(SolsticeSettings.Visuals.HeatMeter) ?? false) && effects && SolsticeSeasons.WarmOverheat, flag3, flag, num, flag2); if (!effects || val == null || !SolsticeSeasons.WarmOverheat || !ClimateRules.Overheating(true, flag3, flag, num, flag2)) { Drop<SE_Stats>(seman, ref overheatLive); return; } if (!Worn(seman, (StatusEffect?)(object)overheatLive)) { StatusEffect obj = seman.AddStatusEffect((StatusEffect)(object)OverheatTemplate(), false, 0, 0f, (short)(-1)); overheatLive = (SE_Stats?)(object)((obj is SE_Stats) ? obj : null); } if (!((Object)(object)overheatLive == (Object)null)) { overheatLive.m_staminaRegenMultiplier = 0.8f; overheatLive.m_eitrRegenMultiplier = 0.8f; ((StatusEffect)overheatLive).m_name = SolsticeText.Text("status_overheat"); ((StatusEffect)overheatLive).m_tooltip = SolsticeText.Text("status_overheat_tip"); ((StatusEffect)overheatLive).m_ttl = 0f; Sprite val2 = SeasonMarks.Heat(); if ((Object)(object)val2 != (Object)null) { ((StatusEffect)overheatLive).m_icon = val2; } } } private static void Apply(SolsticeSeasonStatus effect, SeasonStatModifiers table) { //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)effect).m_tickInterval = table.TickInterval; ((SE_Stats)effect).m_healthPerTickMinHealthPercentage = table.HealthPerTickMinHealthPercentage; ((SE_Stats)effect).m_healthPerTick = table.HealthPerTick; ((SE_Stats)effect).m_runStaminaDrainModifier = table.RunStaminaDrainModifier; ((SE_Stats)effect).m_jumpStaminaUseModifier = table.JumpStaminaUseModifier; ((SE_Stats)effect).m_attackStaminaUseModifier = table.AttackStaminaUseModifier; ((SE_Stats)effect).m_blockStaminaUseModifier = table.BlockStaminaUseModifier; ((SE_Stats)effect).m_blockStaminaUseFlatValue = table.BlockStaminaUseFlatValue; ((SE_Stats)effect).m_dodgeStaminaUseModifier = table.DodgeStaminaUseModifier; ((SE_Stats)effect).m_swimStaminaUseModifier = table.SwimStaminaUseModifier; ((SE_Stats)effect).m_homeItemStaminaUseModifier = table.HomeItemStaminaUseModifier; ((SE_Stats)effect).m_sneakStaminaUseModifier = table.SneakStaminaUseModifier; ((SE_Stats)effect).m_runStaminaUseModifier = table.RunStaminaUseModifier; ((SE_Stats)effect).m_healthRegenMultiplier = table.HealthRegenMultiplier; ((SE_Stats)effect).m_staminaRegenMultiplier = table.StaminaRegenMultiplier; ((SE_Stats)effect).m_eitrRegenMultiplier = table.EitrRegenMultiplier; ((SE_Stats)effect).m_noiseModifier = table.NoiseModifier; ((SE_Stats)effect).m_stealthModifier = table.StealthModifier; ((SE_Stats)effect).m_addMaxCarryWeight = table.AddMaxCarryWeight; ((SE_Stats)effect).m_speedModifier = table.SpeedModifier; ((SE_Stats)effect).m_swimSpeedModifier = table.SwimSpeedModifier; ((SE_Stats)effect).m_maxMaxFallSpeed = table.MaxMaxFallSpeed; ((SE_Stats)effect).m_fallDamageModifier = table.FallDamageModifier; ((SE_Stats)effect).m_adrenalineModifier = table.AdrenalineModifier; ((SE_Stats)effect).m_staggerModifier = table.StaggerModifier; ((SE_Stats)effect).m_timedBlockBonus = table.TimedBlockBonus; ((SE_Stats)effect).m_mods = new List<DamageModPair>(); foreach (KeyValuePair<string, string> damageModifier in table.DamageModifiers) { if (Enum.TryParse<DamageType>(damageModifier.Key, out DamageType result) && Enum.TryParse<DamageModifier>(damageModifier.Value, out DamageModifier result2)) { ((SE_Stats)effect).m_mods.Add(new DamageModPair { m_type = result, m_modifier = result2 }); } } effect.Raise.Clear(); effect.Levels.Clear(); foreach (KeyValuePair<string, float> raiseSkill in table.RaiseSkills) { if (Enum.TryParse<SkillType>(raiseSkill.Key, out SkillType result3)) { effect.Raise[result3] = raiseSkill.Value; } } foreach (KeyValuePair<string, float> skillLevel in table.SkillLevels) { if (Enum.TryParse<SkillType>(skillLevel.Key, out SkillType result4)) { effect.Levels[result4] = skillLevel.Value; } } } private static bool AshOrDeep(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 Biome currentBiome = player.GetCurrentBiome(); if ((int)currentBiome != 32) { return (int)currentBiome == 64; } return true; } private static int WarmClothes(Player player) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return 0; } List<ItemData> inventory2 = inventory.m_inventory; if (inventory2 == null) { return 0; } int num = 0; for (int i = 0; i < inventory2.Count; i++) { ItemData val = inventory2[i]; if (val == null || !val.m_equipped) { continue; } List<DamageModPair> list = val?.m_shared?.m_damageModifiers; if (list == null) { continue; } foreach (DamageModPair item in list) { if ((int)item.m_type == 64 && Warm(item.m_modifier)) { num++; break; } } } return num; } private static bool Warm(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if ((int)modifier != 7 && (int)modifier != 1 && (int)modifier != 5) { return (int)modifier == 3; } return true; } private static bool Cooling(Player player) { if (player.m_foods == null) { return false; } string text = SolsticeSeasons.CoolingFoods ?? ""; if (coolingFoods == null || !string.Equals(text, coolingText, StringComparison.Ordinal)) { coolingText = text; coolingFoods = WinterCrops.Parse(text); } WinterCrops val = coolingFoods; foreach (Food food in player.m_foods) { ItemData val2 = food?.m_item; if (val2 != null && val.Survives(false, (IEnumerable<string>)new string[2] { val2.m_shared?.m_name, ((Object)(object)val2.m_dropPrefab != (Object)null) ? ((Object)val2.m_dropPrefab).name : null })) { return true; } } return false; } private static SolsticeSeasonStatus SeasonTemplate() { if ((Object)(object)seasonTemplate == (Object)null) { seasonTemplate = ScriptableObject.CreateInstance<SolsticeSeasonStatus>(); ((Object)seasonTemplate).name = "SolsticeSeason"; ((StatusEffect)seasonTemplate).m_name = "SolsticeSeason"; ((StatusEffect)seasonTemplate).m_ttl = 0f; ((Object)seasonTemplate).hideFlags = (HideFlags)32; } return seasonTemplate; } private static SE_Stats OverheatTemplate() { if ((Object)(object)overheatTemplate == (Object)null) { overheatTemplate = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)overheatTemplate).name = "SolsticeOverheat"; ((StatusEffect)overheatTemplate).m_name = "SolsticeOverheat"; overheatTemplate.m_staminaRegenMultiplier = 0.8f; overheatTemplate.m_eitrRegenMultiplier = 0.8f; ((StatusEffect)overheatTemplate).m_ttl = 0f; ((Object)overheatTemplate).hideFlags = (HideFlags)32; } return overheatTemplate; } private static Sprite? Borrow(params string[] names) { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } foreach (string text in names) { StatusEffect statusEffect = instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(text)); if ((Object)(object)statusEffect != (Object)null && (Object)(object)statusEffect.m_icon != (Object)null) { return statusEffect.m_icon; } } return null; } private static bool Worn(SEMan seman, StatusEffect? effect) { if ((Object)(object)effect == (Object)null) { return false; } List<StatusEffect> statusEffects = seman.GetStatusEffects(); if (statusEffects == null) { return false; } for (int i = 0; i < statusEffects.Count; i++) { if (statusEffects[i] == effect) { return true; } } return false; } private static void Drop<T>(SEMan seman, ref T? live) where T : StatusEffect { if (!((Object)(object)live == (Object)null)) { seman.RemoveStatusEffect(((StatusEffect)live/*cast due to .constrained prefix*/).NameHash(), false); live = default(T); } } } public static class SeasonCapability { public static void Register(ProductRegistration registration) { registration.AddCapability<Func<Tuple<int, int, int, int, int, int>>>("meadworks.seasons.view", SeasonView.ContractVersion, (Func<Tuple<int, int, int, int, int, int>>)Provide); registration.AddCapability<Func<(int, int, int, int, int, int, int, int)>>("meadworks.seasons.clock", SeasonClockView.ContractVersion, (Func<(int, int, int, int, int, int, int, int)>)Clock); registration.AddCapability<Func<string>>("meadworks.seasons.facts", SeasonFacts.ContractVersion, (Func<string>)Facts); registration.AddCapability<Func<Tuple<bool, float, float, float, float>>>("meadworks.hud.occupied", HudOccupiedContract.ContractVersion, (Func<Tuple<bool, float, float, float, float>>)SeasonIndicator.Occupied); registration.AddCapability<Func<IReadOnlyList<string>>>("meadworks.diagnostics.com.meadworks.solstice", new Version(1, 0), (Func<IReadOnlyList<string>>)Describe); } private static Tuple<int, int, int, int, int, 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) SeasonView view = SolsticeSeasons.View; return ((SeasonView)(ref view)).ToTuple(); } private static (int, int, int, int, int, int, int, int) Clock() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) SeasonClock? clock = SolsticeSeasons.Clock; if (clock.HasValue) { SeasonClock valueOrDefault = clock.GetValueOrDefault(); SeasonClockView val = SeasonClockView.FromClock(valueOrDefault); return ((SeasonClockView)(ref val)).ToTuple(); } return (0, 0, 0, 0, 0, 0, 0, 0); } private static string Facts() { return SolsticeSeasons.Facts(); } private static IReadOnlyList<string> Describe() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: 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_0205: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); ModuleStepAsideDecision stepAside = SolsticeModules.StepAside; list.Add("module: seasons " + (SolsticeModules.Active((SolsticeModule)0) ? "on" : "off") + (stepAside.Any ? (" (steps aside: " + string.Join(", ", stepAside.Notices.Select((StepAsideNotice n) => ((StepAsideNotice)(ref n)).SourceName)) + ")") : "")); SeasonView view = SolsticeSeasons.View; object item; if (!((SeasonView)(ref view)).Available) { item = "season: unavailable"; } else { string[] obj = new string[14] { "season: ", ((object)(Season)((SeasonView)(ref view)).Season/*cast due to .constrained prefix*/).ToString(), ", day ", ((SeasonView)(ref view)).DayInSeason.ToString(), " of ", ((SeasonView)(ref view)).DaysInSeason.ToString(), ", day ", ((SeasonView)(ref view)).DayOfYear.ToString(), " of ", ((SeasonView)(ref view)).YearLength.ToString(), SolsticeSeasons.Held.HasValue ? " (held by the Season Override cheat)" : "", ", effects ", (SolsticeSeasons.Effects != null) ? "on" : "off", null }; object obj2; if (!SolsticeSeasons.Clock.HasValue) { obj2 = ""; } else { SeasonClock value = SolsticeSeasons.Clock.Value; obj2 = ", clock revision " + ((SeasonClock)(ref value)).Revision; } obj[13] = (string)obj2; item = string.Concat(obj); } list.Add((string)item); list.Add(SeasonVisuals.Describe() + (SeasonIndicator.Showing ? ", plate" : "") + SeasonMusicRuntime.Describe()); string obj3 = (SolsticeWeather.RefreshPending ? "waiting" : "idle"); WeatherYieldDecision lastDecision = SolsticeWeather.LastDecision; object obj4; if (!((WeatherYieldDecision)(ref lastDecision)).Yield) { obj4 = ""; } else { lastDecision = SolsticeWeather.LastDecision; obj4 = ", yielding to " + ((object)((WeatherYieldDecision)(ref lastDecision)).Reason/*cast due to .constrained prefix*/).ToString(); } list.Add("weather reset " + obj3 + (string?)obj4); return list; } } public static class SolsticeModules { private const string TranslationProduct = "solstice"; private static ModuleStepAsideDecision? stepAside; private static long noticedSession = -1L; private static long offNoticedSession = -1L; public static ModuleStepAsideDecision StepAside => stepAside ?? ModuleStepAside.Decide(Enumerable.Empty<InstalledPlugin>()); public static void Decide(MeadworksLog log) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Services.StepAside.Declared("com.meadworks.solstice").Any()) { MeadworksStepAside.Declare("com.meadworks.solstice", "Solstice", ((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)))); foreach (StepAsideNotice notice in stepAside.Notices) { StepAsideNotice current = notice; log.Info("Solstice " + ((object)((StepAsideNotice)(ref current)).Module/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + " module steps aside: " + ((StepAsideNotice)(ref current)).SourceName + " is installed."); } } public static bool ProductActive(out ConfigBlockReason reason) { reason = (ConfigBlockReason)0; if (Plugin.Ready) { return MeadworksAuthority.ProductActive("com.meadworks.solstice", ref reason); } return false; } public static bool Active(SolsticeModule 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Invalid comparison between Unknown and I4 //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Invalid comparison between Unknown and I4 //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown if (!Plugin.Ready || (Object)(object)Player.m_localPlayer == (Object)null) { return; } SessionSnapshot session = Plugin.Session; if (stepAside != null && stepAside.Any && noticedSession != session.Generation) { noticedSession = session.Generation; foreach (StepAsideNotice notice in stepAside.Notices) { StepAsideNotice current = notice; MeadworksStepAside.ReportModule("com.meadworks.solstice", ((object)((StepAsideNotice)(ref current)).Module/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(), new SourceMod(((StepAsideNotice)(ref current)).SourceGuid, ((StepAsideNotice)(ref current)).SourceName), "solstice", ((StepAsideNotice)(ref current)).Key); } } if (offNoticedSession != session.Generation && session.AuthorityReady && !ProductActive(out var reason) && ((int)reason == 4 || (int)reason == 5)) { offNoticedSession = session.Generation; string text = SolsticeText.Text(((int)reason == 4) ? "off_no_crucible" : "off_no_solstice"); SolsticeText.Centre(text, log); log.Info(text); } } public static void Reset() { stepAside = null; noticedSession = -1L; offNoticedSession = -1L; } } public static class SolsticeText { 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(SolsticeText).Assembly; foreach (string item in MeadworksLanguages.Supported) { using Stream stream = assembly.GetManifestResourceStream("Solstice.localization." + item + ".json"); if (stream != null) { using StreamReader streamReader = new StreamReader(stream); dictionary.Add(item, streamReader.ReadToEnd()); } } Translations = new ProductTranslations("solstice", (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) { if (MeadworksHeadless.AllowsUi && (Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } else { log.Info(text); } } } [BepInPlugin("com.meadworks.solstice", "Solstice", "0.1.5")] [BepInDependency("com.meadworks.crucible", "0.3.28")] [BepInDependency("com.meadworks.crucible.ui", "0.3.20")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.meadworks.solstice"; public const string Name = "Solstice"; public const string Version = "0.1.5"; public const string Family = "Runestone"; private const int TickCount = 10; private static readonly string[] TickNames = new string[10] { "text", "modules", "seasons", "visuals", "winter", "indicator", "status", "heat", "variants", "weather" }; private ProductRegistration? registration; private MeadworksLog? log; private Harmony? harmony; private bool started; private bool generalEnabled; private bool traceEnabled; private bool timingsReady; private long updateTicks; private bool traceFrame; private int traceGc0; private int traceGc1; private int traceGc2; private int traceFrameId; private long traceStart; private readonly Stopwatch clock = new Stopwatch(); private readonly Stopwatch uptime = Stopwatch.StartNew(); private readonly double[] nextFault = new double[10]; private readonly long[] elapsed = new long[10]; private Action[]? ticks; private Action<Exception>[]? reporters; public static bool Ready { get; private set; } public static SolsticeConfig? Settings { get; private set; } private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown log = MeadworksLog.For(((BaseUnityPlugin)this).Logger, "Solstice"); try { if (!Plugin.IsReady) { throw new InvalidOperationException("Required Crucible is not ready"); } CarryOverLegacyFiles(log); registration = Plugin.Services.Register(new ProductMetadata("com.meadworks.solstice", "Solstice", new Version("0.1.5"), new Version(0, 1), new Version(0, 1), (MultiplayerPolicy)2, "Runestone")); Settings = SolsticeConfig.Bind(((BaseUnityPlugin)this).Config, log); generalEnabled = Settings.Get<bool>(SolsticeSettings.General.Enabled); Settings.Changed += OnSettingsChanged; SolsticeText.Initialize(((BaseUnityPlugin)this).Info.Metadata, ((BaseUnityPlugin)this).Logger); SeasonClockSync.Initialize(log, ((BaseUnityPlugin)this).Config); Ready = true; log.Info("Solstice registered; modules start after plugin discovery."); } catch (Exception ex) { log.Error((object)("Solstice initialization rejected: " + ex.GetType().Name + ": " + ex.Message)); Shutdown(); } } private void CarryOverLegacyFiles(MeadworksLog log) { string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath) ?? Paths.ConfigPath; string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath); string text2 = "com.extrapotions.solstice.cfg"; if (Report(log, LegacyFileMigration.PrepareSettings(text, text2, fileName), text2, fileName, "Settings were")) { ((BaseUnityPlugin)this).Config.Reload(); } Report(log, LegacyFileMigration.PrepareSettings(Paths.ConfigPath, "com.extrapotions.solstice.seasons-import", "com.meadworks.solstice.seasons-import"), "com.extrapotions.solstice.seasons-import", "com.meadworks.solstice.seasons-import", "The seasons import marker was"); } private static bool Report(MeadworksLog log, LegacyFileResult result, string legacyName, string currentName, string what) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown LegacyFileOutcome outcome = result.Outcome; switch (outcome - 1) { case 0: log.Info(what + " carried over from " + legacyName + ((result.Backup != null) ? ("; the old file is kept as " + Path.GetFileName(result.Backup) + ".") : (". " + result.Problem))); return true; case 1: log.Info(legacyName + " was left untouched because " + currentName + " already exists."); return false; case 2: log.Warning(what + " not carried over from " + legacyName + " (" + result.Problem + "); the old file is untouched."); return false; default: return false; } } private void Start() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown if (!Ready || registration == null || log == null) { return; } try { SolsticeModules.Decide(log); SeasonImport.Run(Settings, log); SolsticeSeasons.Initialize(log); SeasonVisuals.Initialize(log); SeasonWinterLook.Initialize(log); SolsticeWeather.Initialize(log); SeasonEnvironmentVariants.Initialize(log); SeasonCapability.Register(registration); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SolsticeCommand()); harmony = new Harmony("com.meadworks.solstice"); harmony.PatchAll(typeof(Plugin).Assembly); started = true; } catch (Exception ex) { log.Error((object)("Solstice modules rejected: " + ex.GetType().Name + ": " + ex.Message)); Shutdown(); } } private void Update() { if (!Ready) { return; } EnsureTicks(); traceFrame = log != null && log.IsEnabled((LogSeverity)4); if (traceFrame) { traceStart = Stopwatch.GetTimestamp(); traceFrameId = Time.frameCount; traceGc0 = GC.CollectionCount(0); traceGc1 = GC.CollectionCount(1); traceGc2 = GC.CollectionCount(2); } clock.Restart(); elapsed[0] = Time(0, ticks[0]); if (started && log != null) { if (SeasonFrame.EnabledToggled) { SeasonFrame.EnabledToggled = false; traceEnabled = true; } for (int i = 1; i < 10; i++) { elapsed[i] = Time(i, ticks[i]); } updateTicks = clock.ElapsedTicks; timingsReady = true; } } private void LateUpdate() { //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_00de: Unknown result type (might be due to invalid IL or missing references) if (!Ready) { SeasonFrame.DayMoved = false; SeasonFrame.EnabledToggled = false; SeasonFrame.ClearWork(); traceEnabled = false; timingsReady = false; return; } bool dayMoved = SeasonFrame.DayMoved; bool flag = traceEnabled; bool enabledToggled = SeasonFrame.EnabledToggled; SeasonFrameSystem ran = SeasonFrame.Ran; long weatherSwapTicks = SeasonFrame.WeatherSwapTicks; long musicTicks = SeasonFrame.MusicTicks; bool flag2 = timingsReady && (dayMoved || flag) && log != null; if (flag2 && traceFrame && log.IsEnabled((LogSeverity)4)) { long timestamp = Stopwatch.GetTimestamp(); int num = GC.CollectionCount(0) - traceGc0; int num2 = GC.CollectionCount(1) - traceGc1; int num3 = GC.CollectionCount(2) - traceGc2; try { log.Debug(FrameText(dayMoved, flag, updateTicks, elapsed, weatherSwapTicks, musicTicks, ran) + " frame=" + traceFrameId + " endFrame=" + Time.frameCount + " thread=" + Thread.CurrentThread.ManagedThreadId + " startTicks=" + traceStart + " endTicks=" + timestamp + " gc=" + num + "/" + num2 + "/" + num3); } catch { } } SeasonFrame.DayMoved = false; traceEnabled = false; timingsReady = false; if (flag2 || !enabledToggled) { SeasonFrame.ClearWork(); } } private void EnsureTicks() { if (ticks != null) { return; } reporters = new Action<Exception>[10]; for (int i = 0; i < 10; i++) { int index = i; reporters[i] = delegate(Exception error) { NoteFault(index, error); }; } ticks = new Action[10] { SolsticeText.Tick, delegate { SolsticeModules.Tick(log); }, SolsticeSeasons.Tick, SeasonVisuals.Tick, SeasonWinterLook.Tick, SeasonIndicator.Tick, SeasonStatus.Tick, SeasonHeatHud.Tick, delegate { SeasonEnvironmentVariants.Ensure(EnvMan.instance, countUpdate: true); }, SolsticeWeather.Tick }; } private long Time(int index, Action tick) { long elapsedTicks = clock.ElapsedTicks; TickGuard.Run(tick, ref nextFault[index], uptime.Elapsed.TotalSeconds, reporters[index]); return clock.ElapsedTicks - elapsedTicks; } private void NoteFault(int index, Exception error) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("Solstice " + TickNames[index] + " tick: " + error.GetType().Name + ": " + error.Message); } } private static string FrameText(bool day, bool enabled, long updateTicks, long[] parts, long weatherTicks, long musicTicks, SeasonFrameSystem ran) { //IL_01aa: Unknown result type (might be due to invalid IL or missing references) string text = ((day && enabled) ? "day change and Enabled toggle" : (day ? "day change" : "Enabled toggle")); return "Solstice " + text + " frame: update " + SeasonFrame.Milliseconds(updateTicks) + " ms (text " + SeasonFrame.Milliseconds(parts[0]) + ", modules " + SeasonFrame.Milliseconds(parts[1]) + ", seasons " + SeasonFrame.Milliseconds(parts[2]) + ", visuals " + SeasonFrame.Milliseconds(parts[3]) + ", winter " + SeasonFrame.Milliseconds(parts[4]) + ", indicator " + SeasonFrame.Milliseconds(parts[5]) + ", status " + SeasonFrame.Milliseconds(parts[6]) + ", heat " + SeasonFrame.Milliseconds(parts[7]) + ", variants " + SeasonFrame.Milliseconds(parts[8]) + ", weather " + SeasonFrame.Milliseconds(parts[9]) + "; weather list swap " + SeasonFrame.Milliseconds(weatherTicks) + ", music invalidate " + SeasonFrame.Milliseconds(musicTicks) + "). Ran: " + SeasonFrame.Describe(ran) + "."; } private void OnSettingsChanged() { if (Settings != null) { bool flag = Settings.Get<bool>(SolsticeSettings.General.Enabled); if (flag != generalEnabled) { generalEnabled = flag; SeasonFrame.EnabledToggled = true; } } SolsticeSeasons.Invalidate(); SeasonVisuals.Invalidate(); SeasonWinterLook.Invalidate(); SeasonMusicRuntime.Invalidate(); } private void OnDestroy() { Shutdown(); } private void Shutdown() { Ready = false; started = false; traceEnabled = false; timingsReady = false; SeasonFrame.DayMoved = false; SeasonFrame.EnabledToggled = false; SeasonFrame.ClearWork(); try { SeasonCrops.Shutdown(); } catch (Exception ex) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("Solstice crop watch stop: " + ex.GetType().Name); } } try { SeasonEnvironmentVariants.Shutdown(); } catch (Exception ex2) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Solstice sky restore: " + ex2.GetType().Name); } } try { SeasonWinterLook.Shutdown(); } catch (Exception ex3) { MeadworksLog? obj3 = log; if (obj3 != null) { obj3.Warning("Solstice winter look stop: " + ex3.GetType().Name); } } try { Harmony? obj4 = harmony; if (obj4 != null) { obj4.UnpatchSelf(); } } catch (Exception ex4) { MeadworksLog? obj5 = log; if (obj5 != null) { obj5.Warning("Solstice unpatch: " + ex4.GetType().Name); } } harmony = null; try { SeasonHeatHud.Shutdown(); } catch (Exception ex5) { MeadworksLog? obj6 = log; if (obj6 != null) { obj6.Warning("Solstice heat meter stop: " + ex5.GetType().Name); } } try { SeasonStatus.Shutdown(); } catch (Exception ex6) { MeadworksLog? obj7 = log; if (obj7 != null) { obj7.Warning("Solstice status stop: " + ex6.GetType().Name); } } try { SeasonMarks.Shutdown(); } catch (Exception ex7) { MeadworksLog? obj8 = log; if (obj8 != null) { obj8.Warning("Solstice season marks stop: " + ex7.GetType().Name); } } try { SeasonMusicRuntime.Shutdown(); } catch (Exception ex8) { MeadworksLog? obj9 = log; if (obj9 != null) { obj9.Warning("Solstice music stop: " + ex8.GetType().Name); } } Action[] array = new Action[9] { SeasonIndicator.Shutdown, SeasonVisuals.Shutdown, SeasonStatus.Shutdown, SeasonEnvironmentVariants.Shutdown, SeasonClockSync.Shutdown, SolsticeWeather.Shutdown, SolsticeSeasons.Shutdown, SolsticeModules.Reset, SolsticeText.Shutdown }; foreach (Action action in array) { try { action(); } catch (Exception ex9) { MeadworksLog? obj10 = log; if (obj10 != null) { obj10.Warning("Solstice teardown: " + ex9.GetType().Name); } } } Settings?.Shutdown(); Settings = null; ProductRegistration? obj11 = registration; if (obj11 != null) { obj11.Dispose(); } registration = null; SeasonFrame.ClearWork(); } } public static class SeasonClockSync { private static MeadworksLog? log; private static ConfigEntry<string>? fallback; private static WorldSettingBinding? binding; private static long world; private static SeasonClock? current; private static string? lastWritten; private static bool available; private static WorldSettingWriteStatus lastWrite = (WorldSettingWriteStatus)4; private static bool warnedNewer; private static bool warnedCorrupt; public static SeasonClock? Current { get { if (!available) { return null; } return current; } } public static void Initialize(MeadworksLog logger, ConfigFile config) { log = logger; fallback = MeadworksConfig.BindHidden<string>(config, "World", "Clock", "", "Managed by Solstice: required fallback of the guarded world key mw.solstice.clock. The season clock lives in the world, not in this file.", (ConfigScope)1, (AcceptableValueBase)null); binding = MeadworksWorldSettings.Register("com.meadworks.solstice", "mw.solstice.clock", (ConfigEntryBase)(object)fallback, (Func<string, string>)SeasonClockText.Canonical); } public static bool Advance(int worldDay, SeasonCalendar lengths) { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Invalid comparison between Unknown and I4 //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) SeasonClock? left = Current; if (lengths == null || (Object)(object)ZNet.instance == (Object)null || ZNet.World == null || (Object)(object)ZoneSystem.instance == (Object)null || !SolsticeModules.Active((SolsticeModule)0)) { ClearSession(); return left.HasValue; } long worldUID = ZNet.instance.GetWorldUID(); if (worldUID != world) { ClearSession(); world = worldUID; warnedNewer = false; warnedCorrupt = false; } bool flag = ZNet.instance.IsServer(); WorldSettingBinding? obj = binding; string text = ((obj != null) ? obj.WorldText : null); string text2 = default(string); if (string.IsNullOrEmpty(text) && ZoneSystem.instance.GetGlobalKey("mw.solstice.clock", ref text2) && (!WorldSettingStorage.TryDecode(worldUID, text2, ref text, false) || string.IsNullOrEmpty(text))) { if (!warnedCorrupt) { warnedCorrupt = true; MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Season clock mw.solstice.clock has an unreadable world record. Solstice preserves it and leaves seasonal effects off."); } } available = false; current = null; return !Same(left, Current); } if (string.IsNullOrEmpty(text)) { if (!flag) { available = false; current = null; return !Same(left, Current); } if (current.HasValue && lastWritten != null) { SeasonClock clock = SeasonClocks.Reconcile(current.Value, worldDay, lengths); if (!((SeasonClock)(ref clock)).Equals(current.Value)) { Write(clock); } else { available = true; } return !Same(left, Current); } Write(SeasonClocks.Seed(worldDay, lengths)); return !Same(left, Current); } SeasonClock val = default(SeasonClock); ClockParse val2 = default(ClockParse); if (!SeasonClockText.TryParse(text, ref val, ref val2)) { if ((int)val2 == 3) { if (!warnedNewer) { warnedNewer = true; MeadworksLog? obj3 = log; if (obj3 != null) { obj3.Warning("Season clock mw.solstice.clock is a newer version. Solstice leaves it unread and does not overwrite it."); } } available = false; current = null; return !Same(left, Current); } if (!warnedCorrupt) { warnedCorrupt = true; MeadworksLog? obj4 = log; if (obj4 != null) { obj4.Warning("Season clock mw.solstice.clock could not be read. Solstice preserves it and leaves seasonal effects off."); } } available = false; current = null; return !Same(left, Current); } warnedCorrupt = false; warnedNewer = false; if (!flag) { current = val; available = true; lastWritten = text; return !Same(left, Current); } SeasonClock val3 = SeasonClocks.Reconcile(val, worldDay, lengths); if (!((SeasonClock)(ref val3)).Equals(val)) { Write(val3); } else { current = val3; available = true; lastWritten = SeasonClockText.Format(val3); } return !Same(left, Current); } public static void Shutdown() { ClearSession(); WorldSettingBinding? obj = binding; if (obj != null) { obj.Dispose(); } binding = null; fallback = null; log = null; } public static void ClearSession() { current = null; available = false; lastWritten = null; world = 0L; } private static void Write(SeasonClock clock) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_008b: 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_0088: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0056: 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) if (binding == null) { return; } string text = SeasonClockText.Format(clock); WorldSettingWriteResult val = binding.Set(text); if (val.Status != lastWrite) { lastWrite = val.Status; if ((int)val.Status != 4 && (int)val.Status != 3) { MeadworksLog? obj = log; if (obj != null) { obj.Warning("Season clock not written: " + ((object)val.Status/*cast due to .constrained prefix*/).ToString() + "."); } } } if ((int)val.Status == 4 || (int)val.Status == 3) { current = clock; lastWritten = text; available = true; } } private static bool Same(SeasonClock? left, SeasonClock? right) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references)