Decompiled source of Ember v0.5.10

plugins/Ember/Ember.Core.dll

Decompiled 13 hours ago
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using ExtraPotions.Ember.Config;
using ExtraPotions.Ember.Doors;
using ExtraPotions.Ember.Sleep;
using ExtraPotions.Ember.Torches;
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.5.10.0")]
[assembly: AssemblyInformationalVersion("0.5.10+fatal: not a git repository (or any of the parent directories): .git")]
[assembly: AssemblyProduct("Ember.Core")]
[assembly: AssemblyTitle("Ember.Core")]
[assembly: AssemblyVersion("0.5.10.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.Ember
{
	public static class EmberCore
	{
		public const string PluginGuid = "com.meadworks.ember";

		public const string ProductName = "Ember";

		public const string ProductVersion = "0.5.10";

		public const string Family = "Hearth";

		public const string TokenPrefix = "meadworks_ember_";
	}
}
namespace ExtraPotions.Ember.Torches
{
	public static class FireComfort
	{
		private static readonly HashSet<string> placed = new HashSet<string>(FirePieceList.Parse("piece_groundtorch,piece_groundtorch_wood,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch"), StringComparer.Ordinal) { "fire_pit" };

		public static bool Placed(string? prefab)
		{
			if (prefab != null)
			{
				return placed.Contains(prefab);
			}
			return false;
		}

		public static bool Held(string? prefab)
		{
			return prefab == "Torch";
		}
	}
	public static class FirePieceList
	{
		public const string Default = "piece_groundtorch,piece_groundtorch_wood,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch";

		public static IReadOnlyList<string> Parse(string? text)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			List<string> list = new List<string>();
			string[] array = (text ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length > 0 && hashSet.Add(text2))
				{
					list.Add(text2);
				}
			}
			return list;
		}

		public static string Normalize(string? text)
		{
			return string.Join(",", Parse(text));
		}

		public static IReadOnlyList<string> Unknown(IEnumerable<string> names, Func<string, bool> known)
		{
			if (names == null)
			{
				throw new ArgumentNullException("names");
			}
			if (known == null)
			{
				throw new ArgumentNullException("known");
			}
			return names.Where((string n) => !known(n)).Distinct<string>(StringComparer.Ordinal).ToList();
		}
	}
	public readonly struct TorchBurnSettings
	{
		private static readonly IReadOnlyList<string> NoWeathers = Array.Empty<string>();

		public bool EnvironmentAffects { get; }

		public bool ColdBiomeAffects { get; }

		public float BurnRate { get; }

		public IReadOnlyList<string> ExtraWeathers { get; }

		public TorchBurnSettings(bool environmentAffects, bool coldBiomeAffects, float burnRate, IReadOnlyList<string>? extraWeathers = null)
		{
			EnvironmentAffects = environmentAffects;
			ColdBiomeAffects = coldBiomeAffects;
			BurnRate = ((float.IsNaN(burnRate) || float.IsInfinity(burnRate)) ? 0.5f : Math.Max(0f, Math.Min(1f, burnRate)));
			ExtraWeathers = extraWeathers ?? NoWeathers;
		}
	}
	public static class TorchBurn
	{
		public const float DefaultBurnRate = 0.5f;

		public static IReadOnlyList<string> BurnWeathers { get; } = Array.AsReadOnly(new string[5] { "LightRain", "SwampRain", "ThunderStorm", "SnowStorm", "Twilight_SnowStorm" });

		public static IReadOnlyList<string> ColdBiomes { get; } = Array.AsReadOnly(new string[2] { "Mountain", "DeepNorth" });

		public static bool IsBurnWeather(string? environment, TorchBurnSettings settings)
		{
			if (environment != null)
			{
				if (!BurnWeathers.Contains<string>(environment, StringComparer.Ordinal))
				{
					return settings.ExtraWeathers.Contains<string>(environment, StringComparer.Ordinal);
				}
				return true;
			}
			return false;
		}

		public static bool IsColdBiome(string? biome)
		{
			if (biome != null)
			{
				return ColdBiomes.Contains<string>(biome, StringComparer.Ordinal);
			}
			return false;
		}

		public static float Drain(float vanillaDrain, TorchBurnSettings settings, string? environment, string? biome, bool endless)
		{
			if (endless)
			{
				return 0f;
			}
			if ((settings.EnvironmentAffects && IsBurnWeather(environment, settings)) || (settings.ColdBiomeAffects && IsColdBiome(biome)))
			{
				return settings.BurnRate;
			}
			if (!float.IsNaN(vanillaDrain) && !(vanillaDrain < 0f))
			{
				return vanillaDrain;
			}
			return 0f;
		}

		public static bool UsesFuel(bool endless)
		{
			return !endless;
		}
	}
	public enum CarriedHand
	{
		None,
		Right,
		Left
	}
	public static class TorchFire
	{
		public const float AreaRadius = 6f;

		public const float CarriedAreaRadius = 2f;

		public static CarriedHand HandFor(bool rightHoldsTorch, bool leftHoldsTorch)
		{
			if (!rightHoldsTorch)
			{
				if (!leftHoldsTorch)
				{
					return CarriedHand.None;
				}
				return CarriedHand.Left;
			}
			return CarriedHand.Right;
		}

		public static bool PieceIsFire(bool moduleActive, bool listed, bool burning)
		{
			return moduleActive && listed && burning;
		}

		public static bool HeldWarms(bool moduleActive, bool heldTorchWarms, bool torchEquipped, float durability)
		{
			if (moduleActive && heldTorchWarms && torchEquipped)
			{
				return durability > 0f;
			}
			return false;
		}
	}
	public readonly struct TorchChange
	{
		public readonly bool Write;

		public readonly int Phase;

		public readonly bool? On;

		internal TorchChange(int phase, bool? on)
		{
			Write = true;
			Phase = phase;
			On = on;
		}
	}
	public static class TorchSchedule
	{
		public const int Day = 1;

		public const int Night = 2;

		public const float MaxMargin = 4f;

		public static TorchChange Step(bool enabled, bool listed, bool kept, int applied, bool? night)
		{
			if (!enabled || !listed || kept)
			{
				if (applied != 0)
				{
					return new TorchChange(0, (applied == 1) ? new bool?(true) : ((bool?)null));
				}
				return default(TorchChange);
			}
			if (!night.HasValue)
			{
				return default(TorchChange);
			}
			int num = ((!night.Value) ? 1 : 2);
			if (num != applied)
			{
				return new TorchChange(num, night.Value);
			}
			return default(TorchChange);
		}

		public static bool? IsNight(float fraction, float lightsOut, float lightsOn)
		{
			if (!Finite(fraction) || !Finite(lightsOut) || !Finite(lightsOn))
			{
				return null;
			}
			return fraction <= lightsOut || fraction >= lightsOn;
		}

		public static bool Settled(float fraction, float target)
		{
			if (!Finite(fraction) || !Finite(target))
			{
				return false;
			}
			double num = ((double)(fraction - target) * 360.0 + 180.0) % 360.0;
			if (num < 0.0)
			{
				num += 360.0;
			}
			return Math.Abs(num - 180.0) < 10.0;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
}
namespace ExtraPotions.Ember.Sleep
{
	public static class OceanRules
	{
		public const float CalmSpeed = 0.5f;

		private static bool Boosting(double extraRate)
		{
			if (extraRate > 0.0 && !double.IsNaN(extraRate))
			{
				return !double.IsInfinity(extraRate);
			}
			return false;
		}

		public static bool DecoupleWaves(bool enabled, double extraRate)
		{
			if (enabled)
			{
				return Boosting(extraRate);
			}
			return false;
		}

		public static float WaveClockStep(bool fixedStep, int frame, ref int lastFrame, float dt)
		{
			if (fixedStep || frame == lastFrame)
			{
				return 0f;
			}
			lastFrame = frame;
			return Math.Max(0f, dt);
		}

		public static float CalmTarget(bool enabled, double extraRate, float multiplier)
		{
			if (!enabled || !Boosting(extraRate))
			{
				return 1f;
			}
			return Math.Max(0f, Math.Min(1f, float.IsNaN(multiplier) ? 1f : multiplier));
		}

		public static float Approach(float current, float target, float seconds)
		{
			float num = 0.5f * Math.Max(0f, seconds);
			if (target > current)
			{
				return Math.Min(target, current + num);
			}
			return Math.Max(target, current - num);
		}

		public static bool SkipShipWaterForce(bool preventDamage, bool suppressShake, double extraRate)
		{
			if (preventDamage || suppressShake)
			{
				return Boosting(extraRate);
			}
			return false;
		}
	}
	public readonly struct BoostStep
	{
		public double NewTime { get; }

		public double Applied { get; }

		public bool Advanced => Applied > 0.0;

		public BoostStep(double newTime, double applied)
		{
			NewTime = newTime;
			Applied = applied;
		}
	}
	public static class SleepBoost
	{
		public const double MaxFadeRealSeconds = 30.0;

		public static double EffectiveExtraRate(double extraRate, double remainingToMorning, double fadeRealSeconds)
		{
			if (extraRate <= 0.0)
			{
				return 0.0;
			}
			double num = (double.IsNaN(fadeRealSeconds) ? 0.0 : Math.Max(0.0, Math.Min(30.0, fadeRealSeconds)));
			if (num <= 0.0)
			{
				return extraRate;
			}
			double num2 = num * (1.0 + extraRate);
			if (remainingToMorning < num2)
			{
				return extraRate * (Math.Max(0.0, remainingToMorning) / num2);
			}
			return extraRate;
		}

		public static BoostStep Step(double time, double dt, double extraRate, double morningCap, double fadeRealSeconds)
		{
			if (extraRate <= 0.0 || double.IsNaN(extraRate) || dt <= 0.0 || double.IsNaN(dt))
			{
				return new BoostStep(time, 0.0);
			}
			if (double.IsPositiveInfinity(morningCap))
			{
				return new BoostStep(time + dt * extraRate, dt * extraRate);
			}
			if (time >= morningCap)
			{
				return new BoostStep(time, 0.0);
			}
			double num = morningCap - time;
			double num2 = EffectiveExtraRate(extraRate, num, fadeRealSeconds);
			double num3 = Math.Min(dt * num2, num);
			if (num3 <= 0.0)
			{
				return new BoostStep(time, 0.0);
			}
			return new BoostStep((num3 >= num) ? morningCap : (time + num3), num3);
		}
	}
	public sealed class TimeSyncTimer
	{
		public const double IntervalSeconds = 0.25;

		private double elapsed;

		public bool Tick(double dt)
		{
			if (dt > 0.0 && !double.IsNaN(dt))
			{
				elapsed += dt;
			}
			if (elapsed + 1E-09 < 0.25)
			{
				return false;
			}
			elapsed = 0.0;
			return true;
		}

		public void Reset()
		{
			elapsed = 0.0;
		}
	}
	public static class SleepCommands
	{
		public const string BedChangedName = "ember.sleep.bedChanged";

		public const string StateName = "ember.sleep.state";

		public const string BedChangedId = "meadworks.ember.sleep.bed_changed";

		public const string StateId = "meadworks.ember.sleep.state";

		public const byte Version = 2;

		public const int MaxPlayers = 1024;

		public const double MaxExtraRate = 30.0;

		public const int StateLength = 26;

		public const int StationStateLength = 50;

		private const byte FlagWindowOpen = 1;

		private const byte FlagBoosting = 2;

		private const byte FlagFullSkip = 4;

		private const byte KnownFlags = 7;

		public static byte[] EncodeBedChanged()
		{
			return EncodeBedChanged(stationTotals: false);
		}

		public static byte[] EncodeBedChanged(bool stationTotals)
		{
			if (stationTotals)
			{
				return new byte[2] { 2, 1 };
			}
			return new byte[1] { 2 };
		}

		public static bool TryDecodeBedChanged(byte[]? payload, out string? error)
		{
			return VersionOnly(payload, out error);
		}

		public static byte[] EncodeStateRequest()
		{
			return EncodeStateRequest(stationTotals: false);
		}

		public static byte[] EncodeStateRequest(bool stationTotals)
		{
			if (stationTotals)
			{
				return new byte[2] { 2, 1 };
			}
			return new byte[1] { 2 };
		}

		public static bool TryDecodeStateRequest(byte[]? payload, out string? error)
		{
			return VersionOnly(payload, out error);
		}

		public static byte[] EncodeState(SleepState state)
		{
			string text = Validate(state);
			if (text != null)
			{
				throw new ArgumentException("Invalid sleep state: " + text, "state");
			}
			byte[] array = new byte[state.StationTotals.HasValue ? 50 : 26];
			array[0] = 2;
			array[1] = (byte)((state.WindowOpen ? 1u : 0u) | (uint)(state.Boosting ? 2 : 0) | (uint)(state.FullSkip ? 4 : 0));
			BinaryPrimitives.WriteInt32LittleEndian(array.AsSpan(2, 4), state.Players);
			BinaryPrimitives.WriteInt32LittleEndian(array.AsSpan(6, 4), state.Sleeping);
			BinaryPrimitives.WriteInt64LittleEndian(array.AsSpan(10, 8), BitConverter.DoubleToInt64Bits(state.ExtraRate));
			BinaryPrimitives.WriteInt64LittleEndian(array.AsSpan(18, 8), BitConverter.DoubleToInt64Bits(state.BoostTotal));
			if (state.StationTotals.HasValue)
			{
				StationBoostTotals value = state.StationTotals.Value;
				BinaryPrimitives.WriteInt64LittleEndian(array.AsSpan(26, 8), BitConverter.DoubleToInt64Bits(value.Mead));
				BinaryPrimitives.WriteInt64LittleEndian(array.AsSpan(34, 8), BitConverter.DoubleToInt64Bits(value.Crops));
				BinaryPrimitives.WriteInt64LittleEndian(array.AsSpan(42, 8), BitConverter.DoubleToInt64Bits(value.Pickables));
			}
			return array;
		}

		public static bool TryDecodeState(byte[]? payload, out SleepState state, out string? error)
		{
			state = default(SleepState);
			if (payload == null || payload.Length == 0)
			{
				error = "empty payload";
				return false;
			}
			if (payload[0] != 2)
			{
				error = "unknown version " + payload[0];
				return false;
			}
			if (payload.Length != 26 && payload.Length != 50)
			{
				error = "expected " + 26 + " bytes, got " + payload.Length;
				return false;
			}
			byte b = payload[1];
			if ((b & -8) != 0)
			{
				error = "unknown flags";
				return false;
			}
			int players = BinaryPrimitives.ReadInt32LittleEndian(payload.AsSpan(2, 4));
			int sleeping = BinaryPrimitives.ReadInt32LittleEndian(payload.AsSpan(6, 4));
			double extraRate = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload.AsSpan(10, 8)));
			double boostTotal = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload.AsSpan(18, 8)));
			StationBoostTotals? stationTotals = ((payload.Length == 50) ? new StationBoostTotals?(new StationBoostTotals(BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload.AsSpan(26, 8))), BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload.AsSpan(34, 8))), BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload.AsSpan(42, 8))))) : ((StationBoostTotals?)null));
			SleepState sleepState = new SleepState((b & 1) != 0, players, sleeping, extraRate, (b & 2) != 0, (b & 4) != 0, boostTotal, stationTotals);
			error = Validate(sleepState);
			if (error != null)
			{
				return false;
			}
			state = sleepState;
			return true;
		}

		private static string? Validate(SleepState s)
		{
			if (s.Players < 0 || s.Players > 1024)
			{
				return "players out of range";
			}
			if (s.Sleeping < 0 || s.Sleeping > s.Players)
			{
				return "sleeping out of range";
			}
			if (double.IsNaN(s.ExtraRate) || double.IsInfinity(s.ExtraRate) || s.ExtraRate < 0.0 || s.ExtraRate > 30.0)
			{
				return "extra rate out of range";
			}
			if (s.Boosting != s.ExtraRate > 0.0)
			{
				return "boosting must match a positive extra rate";
			}
			if (double.IsNaN(s.BoostTotal) || double.IsInfinity(s.BoostTotal) || s.BoostTotal < 0.0 || s.BoostTotal > 1000000000000.0)
			{
				return "boost total out of range";
			}
			if (s.StationTotals.HasValue)
			{
				StationBoostTotals value = s.StationTotals.Value;
				if (!ValidStation(value.Mead, s.BoostTotal) || !ValidStation(value.Crops, s.BoostTotal) || !ValidStation(value.Pickables, s.BoostTotal))
				{
					return "station boost total out of range";
				}
			}
			return null;
		}

		private static bool ValidStation(double value, double total)
		{
			if (!double.IsNaN(value) && value >= 0.0)
			{
				return value <= total;
			}
			return false;
		}

		private static bool VersionOnly(byte[]? payload, out string? error)
		{
			if (payload == null || payload.Length == 0)
			{
				error = "empty payload";
				return false;
			}
			if (payload[0] != 2)
			{
				error = "unknown version " + payload[0];
				return false;
			}
			if (payload.Length != 1 && (payload.Length != 2 || payload[1] != 1))
			{
				error = "unexpected data after the version";
				return false;
			}
			error = null;
			return true;
		}
	}
	public static class SleepHud
	{
		public const int DefaultCompactAbove = 5;

		public const int MinCompactAbove = 1;

		public const int MaxCompactAbove = 20;

		public const double SlowestChevronStepSeconds = 0.35;

		public const double FastestChevronStepSeconds = 0.08;

		public static double LabelPercent(double extraRate, double maxExtraRate)
		{
			return 100.0 * BoostFraction(extraRate, maxExtraRate);
		}

		public static double BoostFraction(double extraRate, double maxExtraRate)
		{
			if (!(maxExtraRate > 0.0) || !(extraRate > 0.0))
			{
				return 0.0;
			}
			return Math.Min(1.0, extraRate / maxExtraRate);
		}

		public static string Label(double percent)
		{
			if (!(percent > 0.0001))
			{
				return "+0%";
			}
			return "+" + percent.ToString("F0", CultureInfo.InvariantCulture) + "%";
		}

		public static double ChevronStepSeconds(double boostFraction)
		{
			double num = (double.IsNaN(boostFraction) ? 0.0 : Math.Max(0.0, Math.Min(1.0, boostFraction)));
			return 0.35 + -0.26999999999999996 * num;
		}

		public static int ChevronStep(double elapsedSeconds, double stepSeconds)
		{
			if (!(stepSeconds > 0.0) || !(elapsedSeconds > 0.0))
			{
				return 0;
			}
			return (int)(Math.Floor(elapsedSeconds / stepSeconds) % 4.0);
		}

		public static bool[] ChevronLit(int step)
		{
			return new bool[3]
			{
				step >= 0 && step <= 2,
				step >= 1 && step <= 2,
				step == 2
			};
		}

		public static bool ChevronLit(int step, int index)
		{
			if (index >= 0 && index < 3 && step >= index)
			{
				return step <= 2;
			}
			return false;
		}

		public static bool Compact(int players, int compactAbove)
		{
			return players > Math.Max(1, Math.Min(20, compactAbove));
		}

		public static string Ratio(int sleeping, int players)
		{
			return sleeping.ToString(CultureInfo.InvariantCulture) + "/" + players.ToString(CultureInfo.InvariantCulture);
		}

		public static int LitPillows(int sleeping, int pillows)
		{
			return Math.Max(0, Math.Min(pillows, sleeping));
		}
	}
	public sealed class SleepHudCaption
	{
		private string? template;

		private string? text;

		private int sleeping;

		private int players;

		private double percent;

		public string Text(string format, int sleeping, int players, double percent)
		{
			double num = ((!(percent > 0.0001)) ? 0.0 : Math.Round(percent, MidpointRounding.ToEven));
			if (text != null && template == format && this.sleeping == sleeping && this.players == players && this.percent == num)
			{
				return text;
			}
			template = format;
			this.sleeping = sleeping;
			this.players = players;
			this.percent = num;
			return text = string.Format(CultureInfo.InvariantCulture, format, sleeping, players) + "   " + SleepHud.Label(num);
		}
	}
	public readonly struct SleepCounts : IEquatable<SleepCounts>
	{
		public int Players { get; }

		public int Sleeping { get; }

		public bool AllInBed
		{
			get
			{
				if (Players > 0)
				{
					return Sleeping >= Players;
				}
				return false;
			}
		}

		public SleepCounts(int players, int sleeping)
		{
			Players = Math.Max(0, players);
			Sleeping = Math.Max(0, Math.Min(Players, sleeping));
		}

		public bool Equals(SleepCounts other)
		{
			if (Players == other.Players)
			{
				return Sleeping == other.Sleeping;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is SleepCounts other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (Players * 397) ^ Sleeping;
		}

		public override string ToString()
		{
			return Sleeping + "/" + Players;
		}
	}
	public readonly struct TestingOffsets
	{
		public const int MaxFake = 80;

		public const int DefaultFakeTotalPlayers = 10;

		public const int DefaultSimulatePlayersInBed = 1;

		public static TestingOffsets Off => new TestingOffsets(enabled: false, 0, 0);

		public bool Enabled { get; }

		public int FakeTotalPlayers { get; }

		public int SimulatePlayersInBed { get; }

		public TestingOffsets(bool enabled, int fakeTotalPlayers, int simulatePlayersInBed)
		{
			Enabled = enabled;
			FakeTotalPlayers = Clamp(fakeTotalPlayers, 0, 80);
			SimulatePlayersInBed = Clamp(simulatePlayersInBed, 0, FakeTotalPlayers);
		}

		internal static int Clamp(int value, int min, int max)
		{
			if (value >= min)
			{
				if (value <= max)
				{
					return value;
				}
				return max;
			}
			return min;
		}
	}
	public readonly struct BoostSettings
	{
		public const float DefaultBonusMultiplier = 0.6f;

		public const float DefaultBonusIncrementScale = 20f;

		public const float MaxBonusMultiplier = 1f;

		public const float MaxBonusIncrementScale = 30f;

		public static BoostSettings Default => new BoostSettings(partialBoost: true, 0.6f, 20f);

		public bool PartialBoost { get; }

		public float BonusMultiplier { get; }

		public float BonusIncrementScale { get; }

		public BoostSettings(bool partialBoost, float bonusMultiplier, float bonusIncrementScale)
		{
			PartialBoost = partialBoost;
			BonusMultiplier = Range(bonusMultiplier, 1f, 0.6f);
			BonusIncrementScale = Range(bonusIncrementScale, 30f, 20f);
		}

		private static float Range(float value, float max, float fallback)
		{
			if (!float.IsNaN(value))
			{
				return Math.Max(0f, Math.Min(max, value));
			}
			return fallback;
		}
	}
	public static class SleepRate
	{
		public static SleepCounts Count(int characterZdos, int sessionPlayers, int realSleeping, TestingOffsets testing)
		{
			int num = Math.Max(Math.Max(0, characterZdos), Math.Max(0, sessionPlayers));
			int num2 = Math.Max(0, realSleeping);
			if (testing.Enabled)
			{
				num += testing.FakeTotalPlayers;
				num2 += testing.SimulatePlayersInBed;
			}
			return new SleepCounts(num, num2);
		}

		public static double ExtraRate(SleepCounts counts, BoostSettings settings, bool windowOpen)
		{
			if (!windowOpen || !settings.PartialBoost)
			{
				return 0.0;
			}
			int players = counts.Players;
			int sleeping = counts.Sleeping;
			if (players <= 1)
			{
				return 0.0;
			}
			if (sleeping <= 0 || sleeping >= players)
			{
				return 0.0;
			}
			double num = (double)sleeping / (double)(players - 1);
			return (double)settings.BonusMultiplier * num * (double)settings.BonusIncrementScale;
		}

		public static double MaxExtraRate(BoostSettings settings)
		{
			if (!settings.PartialBoost)
			{
				return 0.0;
			}
			return (double)settings.BonusMultiplier * (double)settings.BonusIncrementScale;
		}
	}
	public readonly struct SleepReading
	{
		public const int MaximumPlayers = 1024;

		public const double MaximumExtraRate = 30.0;

		public static SleepReading Closed => default(SleepReading);

		public bool WindowOpen { get; }

		public int Players { get; }

		public int Sleeping { get; }

		public int Required { get; }

		public double ExtraRate { get; }

		public bool SkipRunning { get; }

		public float SkipProgress { get; }

		private SleepReading(bool windowOpen, int players, int sleeping, int required, double extraRate, bool skipRunning, float skipProgress)
		{
			WindowOpen = windowOpen;
			Players = players;
			Sleeping = sleeping;
			Required = required;
			ExtraRate = extraRate;
			SkipRunning = skipRunning;
			SkipProgress = skipProgress;
		}

		public static int RequiredFor(int players)
		{
			return Math.Max(0, Math.Min(1024, players));
		}

		public static float ProgressBetween(double startSeconds, double targetSeconds, double nowSeconds)
		{
			if (!Finite(startSeconds) || !Finite(targetSeconds) || !Finite(nowSeconds))
			{
				return 0f;
			}
			if (!(targetSeconds > startSeconds))
			{
				return 1f;
			}
			return (float)Math.Max(0.0, Math.Min(1.0, (nowSeconds - startSeconds) / (targetSeconds - startSeconds)));
		}

		public static SleepReading From(SleepState state, bool skipRunning, float skipProgress)
		{
			int num = Math.Max(0, Math.Min(1024, state.Players));
			int sleeping = Math.Max(0, Math.Min(num, state.Sleeping));
			double extraRate = (Finite(state.ExtraRate) ? Math.Max(0.0, Math.Min(30.0, state.ExtraRate)) : 0.0);
			bool flag = state.FullSkip || skipRunning;
			float skipProgress2 = ((flag && !float.IsNaN(skipProgress)) ? Math.Max(0f, Math.Min(1f, skipProgress)) : 0f);
			return new SleepReading(state.WindowOpen, num, sleeping, RequiredFor(num), extraRate, flag, skipProgress2);
		}

		private static bool Finite(double value)
		{
			if (!double.IsNaN(value))
			{
				return !double.IsInfinity(value);
			}
			return false;
		}

		public override string ToString()
		{
			return (WindowOpen ? "open " : "closed ") + Sleeping + "/" + Players + " need " + Required + " rate " + ExtraRate + (SkipRunning ? (" skip " + SkipProgress) : "");
		}
	}
	public sealed class SkipProgressTracker
	{
		private double start;

		private double target;

		public bool Running { get; private set; }

		public float Update(bool skipRunning, double nowSeconds, double targetSeconds)
		{
			if (!skipRunning)
			{
				Running = false;
				return 0f;
			}
			if (!Running)
			{
				Running = true;
				start = nowSeconds;
				target = targetSeconds;
			}
			return SleepReading.ProgressBetween(start, target, nowSeconds);
		}

		public void Reset()
		{
			Running = false;
		}
	}
	public readonly struct SleepState : IEquatable<SleepState>
	{
		public bool WindowOpen { get; }

		public int Players { get; }

		public int Sleeping { get; }

		public double ExtraRate { get; }

		public bool Boosting { get; }

		public bool FullSkip { get; }

		public double BoostTotal { get; }

		public StationBoostTotals? StationTotals { get; }

		public SleepState(bool windowOpen, int players, int sleeping, double extraRate, bool boosting, bool fullSkip, double boostTotal = 0.0)
			: this(windowOpen, players, sleeping, extraRate, boosting, fullSkip, boostTotal, null)
		{
		}

		public SleepState(bool windowOpen, int players, int sleeping, double extraRate, bool boosting, bool fullSkip, double boostTotal, StationBoostTotals? stationTotals)
		{
			WindowOpen = windowOpen;
			Players = players;
			Sleeping = sleeping;
			ExtraRate = extraRate;
			Boosting = boosting;
			FullSkip = fullSkip;
			BoostTotal = boostTotal;
			StationTotals = stationTotals;
		}

		public SleepState WithBoostTotal(double boostTotal)
		{
			return new SleepState(WindowOpen, Players, Sleeping, ExtraRate, Boosting, FullSkip, boostTotal);
		}

		public SleepState WithStationTotals(double boostTotal, StationBoostTotals totals)
		{
			return new SleepState(WindowOpen, Players, Sleeping, ExtraRate, Boosting, FullSkip, boostTotal, totals);
		}

		public bool Equals(SleepState o)
		{
			if (WindowOpen == o.WindowOpen && Players == o.Players && Sleeping == o.Sleeping && ExtraRate.Equals(o.ExtraRate) && Boosting == o.Boosting && FullSkip == o.FullSkip && BoostTotal.Equals(o.BoostTotal))
			{
				return Nullable.Equals(StationTotals, o.StationTotals);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is SleepState o)
			{
				return Equals(o);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (int)((uint)((Players * 397) ^ (Sleeping * 31) ^ ExtraRate.GetHashCode()) ^ (WindowOpen ? 1u : 0u) ^ (uint)(Boosting ? 2 : 0) ^ (uint)(FullSkip ? 4 : 0)) ^ BoostTotal.GetHashCode();
		}

		public override string ToString()
		{
			return $"window={WindowOpen} {Sleeping}/{Players} rate={ExtraRate} boosting={Boosting} skip={FullSkip} boostTotal={BoostTotal}";
		}
	}
	public sealed class RecountThrottle
	{
		public const double IntervalSeconds = 0.25;

		public const int MaxPeers = 256;

		private readonly Dictionary<string, double> last = new Dictionary<string, double>(StringComparer.Ordinal);

		public int TrackedPeers => last.Count;

		public bool Admit(string peerId, bool verified, double now)
		{
			if (!verified || string.IsNullOrEmpty(peerId))
			{
				return false;
			}
			if (last.TryGetValue(peerId, out var value) && now - value + 1E-09 < 0.25)
			{
				return false;
			}
			if (!last.ContainsKey(peerId) && last.Count >= 256)
			{
				Prune(now);
			}
			if (!last.ContainsKey(peerId) && last.Count >= 256)
			{
				return false;
			}
			last[peerId] = now;
			return true;
		}

		public void Forget(string peerId)
		{
			if (peerId != null)
			{
				last.Remove(peerId);
			}
		}

		public void Reset()
		{
			last.Clear();
		}

		private void Prune(double now)
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, double> item in last)
			{
				if (now - item.Value >= 0.25)
				{
					list.Add(item.Key);
				}
			}
			foreach (string item2 in list)
			{
				last.Remove(item2);
			}
		}
	}
	public sealed class Heartbeat
	{
		public const double TickSeconds = 1.0;

		public const int ForceEveryTicks = 3;

		private int counter;

		public void Reset()
		{
			counter = 0;
		}

		public bool Tick(bool windowOpen)
		{
			if (!windowOpen)
			{
				return false;
			}
			counter++;
			if (counter < 3)
			{
				return false;
			}
			counter = 0;
			return true;
		}
	}
	public enum WindowEdge
	{
		None,
		Opened,
		ClosedWake,
		ClosedDuringSkip
	}
	public sealed class WindowEdges
	{
		private bool wasOpen;

		public bool IsOpen => wasOpen;

		public void Reset()
		{
			wasOpen = false;
		}

		public WindowEdge Update(bool windowOpen, bool timeSkipping)
		{
			WindowEdge result = WindowEdge.None;
			if (windowOpen && !wasOpen)
			{
				result = WindowEdge.Opened;
			}
			else if (!windowOpen && wasOpen)
			{
				result = (timeSkipping ? WindowEdge.ClosedDuringSkip : WindowEdge.ClosedWake);
			}
			wasOpen = windowOpen;
			return result;
		}
	}
	public sealed class PublishGate
	{
		public const double RateEpsilon = 0.001;

		private bool any;

		private int players;

		private int sleeping;

		private double rate;

		public bool ShouldPublish(SleepState state, bool force)
		{
			if (!force && any && state.Players == players && state.Sleeping == sleeping && !(Math.Abs(state.ExtraRate - rate) > 0.001))
			{
				return false;
			}
			any = true;
			players = state.Players;
			sleeping = state.Sleeping;
			rate = state.ExtraRate;
			return true;
		}

		public void Reset()
		{
			any = false;
		}
	}
	public enum SkipAction
	{
		None,
		Vanilla,
		StartSkip,
		StopSkip
	}
	public static class SleepSkip
	{
		public static SkipAction Decide(bool moduleActive, bool isServer, bool gameSleeping, bool timeSkipping, bool windowOpen, bool allInBed)
		{
			if (!moduleActive)
			{
				return SkipAction.Vanilla;
			}
			if (!isServer)
			{
				return SkipAction.None;
			}
			if (gameSleeping)
			{
				if (!timeSkipping)
				{
					return SkipAction.StopSkip;
				}
				return SkipAction.None;
			}
			if (timeSkipping || !windowOpen)
			{
				return SkipAction.None;
			}
			if (!allInBed)
			{
				return SkipAction.None;
			}
			return SkipAction.StartSkip;
		}
	}
	public static class SleepWindow
	{
		public const float MorningFraction = 0.25f;

		public const float DefaultSleepStart = 0.5f;

		public const float MinSleepStart = 0f;

		public const float MaxSleepStart = 0.99f;

		public static float ClampSleepStart(float sleepStart)
		{
			if (float.IsNaN(sleepStart))
			{
				return 0.5f;
			}
			return Math.Max(0f, Math.Min(0.99f, sleepStart));
		}

		public static bool IsOpen(float dayFraction, float sleepStart, bool timeSkipping, bool sleepAnyTime = false)
		{
			if (timeSkipping)
			{
				return false;
			}
			if (sleepAnyTime)
			{
				return true;
			}
			float num = ClampSleepStart(sleepStart);
			if (num < 0.25f)
			{
				if (dayFraction < 0.25f)
				{
					return dayFraction >= num;
				}
				return false;
			}
			if (!(dayFraction >= num))
			{
				return dayFraction < 0.25f;
			}
			return true;
		}

		public static bool CanSleep(bool windowOpen, double nowSeconds, double? wakeupTime, double cooldownSeconds)
		{
			if (!windowOpen)
			{
				return false;
			}
			if (wakeupTime.HasValue && nowSeconds <= wakeupTime.Value + cooldownSeconds)
			{
				return false;
			}
			return true;
		}
	}
	public readonly struct StationBoostTotals : IEquatable<StationBoostTotals>
	{
		public double Mead { get; }

		public double Crops { get; }

		public double Pickables { get; }

		public StationBoostTotals(double mead, double crops, double pickables)
		{
			Mead = mead;
			Crops = crops;
			Pickables = pickables;
		}

		public StationBoostTotals Add(double applied, bool mead, bool crops, bool pickables)
		{
			return new StationBoostTotals(StationTimers.AddBoost(Mead, mead ? applied : 0.0), StationTimers.AddBoost(Crops, crops ? applied : 0.0), StationTimers.AddBoost(Pickables, pickables ? applied : 0.0));
		}

		public bool Equals(StationBoostTotals other)
		{
			if (Mead == other.Mead && Crops == other.Crops)
			{
				return Pickables == other.Pickables;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is StationBoostTotals other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return Mead.GetHashCode() ^ Crops.GetHashCode() ^ Pickables.GetHashCode();
		}

		public string Save(double total)
		{
			return StationTimers.FormatTotal(total) + "|" + StationTimers.FormatTotal(Mead) + "|" + StationTimers.FormatTotal(Crops) + "|" + StationTimers.FormatTotal(Pickables);
		}

		public static bool TryLoad(string? text, double total, out StationBoostTotals totals)
		{
			totals = default(StationBoostTotals);
			string[] array = text?.Split('|');
			if (array == null || array.Length != 4 || !StationTimers.TryParseTotal(array[0], out var total2) || total2 != total || !StationTimers.TryParseTotal(array[1], out var total3) || total3 > total || !StationTimers.TryParseTotal(array[2], out var total4) || total4 > total || !StationTimers.TryParseTotal(array[3], out var total5) || total5 > total)
			{
				return false;
			}
			totals = new StationBoostTotals(total3, total4, total5);
			return true;
		}
	}
	public readonly struct TimerShift
	{
		public bool Mark { get; }

		public long Marker { get; }

		public long Shift { get; }

		public TimerShift(bool mark, long marker, long shift)
		{
			Mark = mark;
			Marker = marker;
			Shift = shift;
		}
	}
	public static class StationTimers
	{
		public const double TicksPerSecond = 10000000.0;

		public const double MaxBoostTotal = 1000000000000.0;

		public static double ScaleDelta(double dt, double extraRate)
		{
			if (!(extraRate > 0.0) || double.IsInfinity(extraRate))
			{
				return dt;
			}
			return dt / (1.0 + extraRate);
		}

		public static long ToTicks(double seconds)
		{
			if (double.IsNaN(seconds) || seconds <= 0.0)
			{
				return 0L;
			}
			return (long)(Math.Min(seconds, 1000000000000.0) * 10000000.0);
		}

		public static TimerShift Shift(long totalTicks, long lastChecked)
		{
			if (lastChecked < 0)
			{
				return new TimerShift(mark: true, totalTicks, 0L);
			}
			if (totalTicks > lastChecked)
			{
				return new TimerShift(mark: true, totalTicks, totalTicks - lastChecked);
			}
			if (lastChecked > totalTicks)
			{
				return new TimerShift(mark: true, totalTicks, 0L);
			}
			return new TimerShift(mark: false, lastChecked, 0L);
		}

		public static double AddBoost(double total, double applied)
		{
			if (double.IsNaN(total) || double.IsInfinity(total) || total < 0.0)
			{
				total = 0.0;
			}
			if (double.IsNaN(applied) || double.IsInfinity(applied) || applied <= 0.0)
			{
				return Math.Min(total, 1000000000000.0);
			}
			return Math.Min(total + applied, 1000000000000.0);
		}

		public static string FormatTotal(double total)
		{
			return AddBoost(total, 0.0).ToString("R", CultureInfo.InvariantCulture);
		}

		public static bool TryParseTotal(string? text, out double total)
		{
			total = 0.0;
			if (text == null)
			{
				return false;
			}
			if (!double.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result) || result < 0.0)
			{
				return false;
			}
			total = Math.Min(result, 1000000000000.0);
			return true;
		}

		public static string FileName(long worldId)
		{
			return "sleep-boost-" + worldId.ToString(CultureInfo.InvariantCulture) + ".txt";
		}
	}
}
namespace ExtraPotions.Ember.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 EmberTextKeys
	{
		public const string StepAsideSleep = "step_aside_sleep";

		public const string StepAsideTorches = "step_aside_torches";

		public const string StepAsideDoors = "step_aside_doors";

		public const string OffNoCrucible = "off_no_crucible";

		public const string OffNoEmber = "off_no_ember";

		public const string DoorsOn = "doors_on";

		public const string DoorsOff = "doors_off";

		public const string DoorsNotAllowed = "doors_not_allowed";

		public const string PresetLocked = "preset_locked";

		public const string HudSleeping = "hud_sleeping";

		public const string HudBoost = "hud_boost";

		public const string ImportDone = "import_done";

		public const string CheatSleepAnyTime = "cheat_sleep_any_time";

		public const string CheatTestingMode = "cheat_testing_mode";

		public const string CheatEndlessTorches = "cheat_endless_torches";

		public const string TorchNightfall = "torch_nightfall";

		public const string TorchKeep = "torch_keep";

		public const string TorchSchedule = "torch_schedule";

		public const string TorchKept = "torch_kept";

		public const string TorchScheduled = "torch_scheduled";

		public const string TorchRefused = "torch_refused";

		public const string BedChoiceCountdown = "bed_choice_countdown";

		public const string BedChoiceNearest = "bed_choice_nearest";

		public const string BedChoiceClick = "bed_choice_click";

		public const string BedChoiceKeys = "bed_choice_keys";

		public static IReadOnlyList<TextKey> All { get; } = Array.AsReadOnly(new TextKey[25]
		{
			new TextKey("step_aside_sleep", 1),
			new TextKey("step_aside_torches", 1),
			new TextKey("step_aside_doors", 1),
			new TextKey("off_no_crucible", 0),
			new TextKey("off_no_ember", 0),
			new TextKey("doors_on", 0),
			new TextKey("doors_off", 0),
			new TextKey("doors_not_allowed", 0),
			new TextKey("preset_locked", 0),
			new TextKey("hud_sleeping", 2),
			new TextKey("hud_boost", 1),
			new TextKey("import_done", 2),
			new TextKey("cheat_sleep_any_time", 0),
			new TextKey("cheat_testing_mode", 0),
			new TextKey("cheat_endless_torches", 0),
			new TextKey("torch_nightfall", 0),
			new TextKey("torch_keep", 0),
			new TextKey("torch_schedule", 0),
			new TextKey("torch_kept", 0),
			new TextKey("torch_scheduled", 0),
			new TextKey("torch_refused", 0),
			new TextKey("bed_choice_countdown", 1),
			new TextKey("bed_choice_nearest", 0),
			new TextKey("bed_choice_click", 0),
			new TextKey("bed_choice_keys", 0)
		});
	}
}
namespace ExtraPotions.Ember.Integration
{
	public enum EmberModule
	{
		Sleep,
		Torches,
		Doors
	}
	public sealed class ModuleSource
	{
		public string Guid { get; }

		public string Name { get; }

		public EmberModule Module { get; }

		internal ModuleSource(string guid, string name, EmberModule module)
		{
			Guid = guid;
			Name = name;
			Module = module;
		}
	}
	public readonly struct StepAsideNotice
	{
		public EmberModule Module { get; }

		public string SourceName { get; }

		public string Key => "step_aside_" + Module.ToString().ToLowerInvariant();

		public StepAsideNotice(EmberModule module, string sourceName)
		{
			Module = module;
			SourceName = sourceName;
		}
	}
	public sealed class ModuleStepAsideDecision
	{
		private readonly HashSet<EmberModule> 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<EmberModule>(present.Select((ModuleSource p) => p.Module));
			Notices = (from p in present
				orderby p.Module
				select new StepAsideNotice(p.Module, p.Name)).ToList();
		}

		public bool SteppedAside(EmberModule module)
		{
			return aside.Contains(module);
		}
	}
	public static class ModuleStepAside
	{
		public static IReadOnlyList<ModuleSource> Sources { get; } = Array.AsReadOnly(new ModuleSource[3]
		{
			new ModuleSource("vapok.mods.BetterSleepBruh", "BetterSleepBruh", EmberModule.Sleep),
			new ModuleSource("blacks7ar.TorchesAreFires", "TorchesAreFires", EmberModule.Torches),
			new ModuleSource("vapok.mods.dooropenerbruh", "DoorOpenerBruh", EmberModule.Doors)
		});

		public static ModuleStepAsideDecision Decide(IEnumerable<string> installedGuids)
		{
			if (installedGuids == null)
			{
				throw new ArgumentNullException("installedGuids");
			}
			HashSet<string> installed = new HashSet<string>(installedGuids.Where((string g) => g != null), StringComparer.Ordinal);
			return new ModuleStepAsideDecision(Sources.Where((ModuleSource s) => installed.Contains(s.Guid)).ToList());
		}
	}
	public readonly struct SeasonReading
	{
		public bool Active { get; }

		public int NightPercent { get; }

		public float Dawn
		{
			get
			{
				if (!Active)
				{
					return 0.15f;
				}
				return (float)NightPercent / 200f;
			}
		}

		public bool TorchHeat { get; }

		public float TorchDrain { get; }

		public SeasonReading(int nightPercent, bool torchHeat, float torchDrain)
		{
			Active = true;
			NightPercent = Math.Max(10, Math.Min(50, nightPercent));
			TorchHeat = torchHeat;
			TorchDrain = Math.Max(0.1f, Math.Min(5f, torchDrain));
		}
	}
	public static class SeasonRules
	{
		public const string Id = "meadworks.seasons.facts";

		public static readonly Version Version = new Version(1, 0);

		public static SeasonReading Read(Func<string>? provider)
		{
			try
			{
				SeasonReading reading;
				return (provider != null && TryParse(provider(), out reading)) ? reading : default(SeasonReading);
			}
			catch (Exception)
			{
				return default(SeasonReading);
			}
		}

		public static bool TryParse(string? text, out SeasonReading reading)
		{
			reading = default(SeasonReading);
			if (string.IsNullOrEmpty(text) || text.Length > 8192)
			{
				return false;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string[] array = text.Split(';');
			foreach (string text2 in array)
			{
				int num = text2.IndexOf('=');
				if (num <= 0)
				{
					continue;
				}
				string text3 = text2.Substring(0, num);
				if (!(text3 != "effects") || !(text3 != "season") || !(text3 != "day") || !(text3 != "length") || !(text3 != "night") || !(text3 != "daystart") || !(text3 != "torch") || !(text3 != "torchdrain"))
				{
					if (dictionary.ContainsKey(text3))
					{
						return false;
					}
					dictionary.Add(text3, text2.Substring(num + 1));
				}
			}
			if (!Integer(dictionary, "effects", out var value) || value != 1 || !Integer(dictionary, "season", out var value2) || value2 < 0 || value2 > 3 || !Integer(dictionary, "day", out var value3) || !Integer(dictionary, "length", out var value4) || value4 < 1 || value3 < 0 || value3 > value4 || !Integer(dictionary, "night", out var value5) || value5 <= 0 || value5 >= 100 || !Number(dictionary, "daystart", out var value6) || Math.Abs(value6 - (float)value5 / 200f) > 0.0011f || !Integer(dictionary, "torch", out var value7) || (value7 != 0 && value7 != 1) || !Number(dictionary, "torchdrain", out var value8) || value8 < 0f)
			{
				return false;
			}
			reading = new SeasonReading(value5, value7 == 1, value8);
			return true;
		}

		private static bool Integer(Dictionary<string, string> fields, string key, out int value)
		{
			value = 0;
			if (fields.TryGetValue(key, out string value2))
			{
				return int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
			}
			return false;
		}

		private static bool Number(Dictionary<string, string> fields, string key, out float value)
		{
			value = 0f;
			if (fields.TryGetValue(key, out string value2) && float.TryParse(value2, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		public static float Rescale(float fraction, float dawn)
		{
			if (fraction < dawn)
			{
				return fraction / dawn * 0.25f;
			}
			if (fraction <= 1f - dawn)
			{
				return 0.25f + (fraction - dawn) / (1f - 2f * dawn) * 0.5f;
			}
			return 0.75f + (fraction - (1f - dawn)) / dawn * 0.25f;
		}

		public static double NextMorning(double time, double dayLength, float dawn)
		{
			return (Math.Floor(time / dayLength - (double)dawn) + 1.0 + (double)dawn) * dayLength;
		}

		public static float Drain(float currentDrain, SeasonReading reading, bool enabled, bool fuelFree)
		{
			if (!fuelFree)
			{
				if (!enabled || !reading.Active)
				{
					return currentDrain;
				}
				return reading.TorchDrain;
			}
			return 0f;
		}

		public static bool Heat(bool currentHeat, SeasonReading reading, bool enabled)
		{
			if (currentHeat)
			{
				if (enabled && reading.Active)
				{
					return reading.TorchHeat;
				}
				return true;
			}
			return false;
		}
	}
}
namespace ExtraPotions.Ember.Doors
{
	public readonly struct Vec3
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public Vec3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public static double Distance(Vec3 a, Vec3 b)
		{
			double num = a.X - b.X;
			double num2 = a.Y - b.Y;
			double num3 = a.Z - b.Z;
			return Math.Sqrt(num * num + num2 * num2 + num3 * num3);
		}

		public override string ToString()
		{
			return $"({X}, {Y}, {Z})";
		}
	}
	public readonly struct Box
	{
		public Vec3 Min { get; }

		public Vec3 Max { get; }

		public Box(Vec3 min, Vec3 max)
		{
			Min = new Vec3(Math.Min(min.X, max.X), Math.Min(min.Y, max.Y), Math.Min(min.Z, max.Z));
			Max = new Vec3(Math.Max(min.X, max.X), Math.Max(min.Y, max.Y), Math.Max(min.Z, max.Z));
		}

		public Vec3 ClosestPoint(Vec3 p)
		{
			return new Vec3(Clamp(p.X, Min.X, Max.X), Clamp(p.Y, Min.Y, Max.Y), Clamp(p.Z, Min.Z, Max.Z));
		}

		private static float Clamp(float v, float min, float max)
		{
			if (!(v < min))
			{
				if (!(v > max))
				{
					return v;
				}
				return max;
			}
			return min;
		}
	}
	public static class DoorDistance
	{
		public static double ToPlayer(Vec3 doorOrigin, IEnumerable<Box>? colliderBounds, Vec3 player)
		{
			double num = Vec3.Distance(doorOrigin, player);
			if (colliderBounds == null)
			{
				return num;
			}
			foreach (Box colliderBound in colliderBounds)
			{
				double num2 = Vec3.Distance(colliderBound.ClosestPoint(player), player);
				if (num2 < num)
				{
					num = num2;
				}
			}
			return num;
		}
	}
	public static class DoorDistances
	{
		public const float Min = 0.25f;

		public const float Max = 20f;

		public const float DefaultScale = 0.5f;

		public static (float Open, float Close) Normalize(float open, float close)
		{
			float num = Clamp(open);
			float val = Clamp(close);
			return (Open: num, Close: Math.Max(num, val));
		}

		private static float Clamp(float value)
		{
			if (!float.IsNaN(value))
			{
				return Math.Max(0.25f, Math.Min(20f, value));
			}
			return 0.25f;
		}
	}
	public static class DoorDistanceDefaults
	{
		public readonly struct Saved
		{
			public string Id { get; }

			public float Value { get; }

			public float OldDefault { get; }

			public float NewDefault { get; }

			public Saved(string id, float value, float oldDefault, float newDefault)
			{
				Id = id;
				Value = value;
				OldDefault = oldDefault;
				NewDefault = newDefault;
			}
		}

		public const int Current = 1;

		public const string MarkerSection = "Doors";

		public const string MarkerKey = "Distance Defaults Version";

		public static List<Saved> Plan(int marker, IEnumerable<Saved>? entries)
		{
			List<Saved> list = new List<Saved>();
			if (marker >= 1 || entries == null)
			{
				return list;
			}
			foreach (Saved entry in entries)
			{
				if (entry.Value == entry.OldDefault && entry.Value != entry.NewDefault)
				{
					list.Add(entry);
				}
			}
			return list;
		}

		public static IEnumerable<Saved> Entries(Func<string, float> value)
		{
			foreach (DoorKind kind in DoorKinds.All)
			{
				DoorKindSettings settings = EmberSettings.Door(kind);
				yield return new Saved(settings.OpenDistance.Id, value(settings.OpenDistance.Id), kind.PreviousOpenDistance, kind.DefaultOpenDistance);
				yield return new Saved(settings.CloseDistance.Id, value(settings.CloseDistance.Id), kind.PreviousCloseDistance, kind.DefaultCloseDistance);
			}
		}
	}
	public enum AutomationMechanic
	{
		OnlyOpenSelfmadeDoors,
		OnlyOpenAllPlayerMadeDoors,
		OpenAllDoors,
		DoNotOpenAutomatically
	}
	public sealed class DoorKind
	{
		public string Section { get; }

		public string Prefab { get; }

		public string PieceToken { get; }

		public AutomationMechanic DefaultMechanic { get; }

		public float DefaultOpenDistance => PreviousOpenDistance * 0.5f;

		public float DefaultCloseDistance => PreviousCloseDistance * 0.5f;

		public float PreviousOpenDistance { get; }

		public float PreviousCloseDistance { get; }

		public bool HasCheckForKey { get; }

		public bool DefaultCheckForKey => false;

		public string? GameName { get; }

		internal DoorKind(string section, string prefab, string pieceToken, AutomationMechanic mechanic, float previousOpen, float previousClose, bool hasCheckForKey, string? gameName = null)
		{
			Section = section;
			Prefab = prefab;
			PieceToken = pieceToken;
			DefaultMechanic = mechanic;
			PreviousOpenDistance = previousOpen;
			PreviousCloseDistance = previousClose;
			HasCheckForKey = hasCheckForKey;
			GameName = gameName;
		}

		public override string ToString()
		{
			return Section;
		}
	}
	public static class DoorKinds
	{
		public const string OtherKey = "other";

		private const AutomationMechanic All_ = AutomationMechanic.OpenAllDoors;

		private const AutomationMechanic Never = AutomationMechanic.DoNotOpenAutomatically;

		private static readonly DoorKind[] kinds = new DoorKind[24]
		{
			new DoorKind("Buildable: Wood Door", "wood_door", "$piece_wooddoor", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Wood Gate", "wood_gate", "$piece_woodgate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Wood Fence Gate", "wood_fence_gate", "$piece_woodfencegate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false, "Roundpole Gate"),
			new DoorKind("Buildable: Stave Gate", "stave_gate", "$piece_stavegate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false, "Timberwood Gate"),
			new DoorKind("Buildable: Wood Shutter", "wood_window", "$piece_woodwindowshutter", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Iron Gate", "iron_grate", "$piece_irongate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Darkwood Gate", "darkwood_gate", "$piece_darkwoodgate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Hexagonal Gate", "piece_hexagonal_door", "$piece_hexagonalgate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Ashwood Door", "ashwood_door", "$piece_ashwood_door", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Flametal Gate", "flametal_gate", "$piece_flametal_gate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Grausten Door", "piece_grausten_door", "$piece_grausten_door", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Grausten Gate", "piece_grausten_gate", "$piece_grausten_gate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Buildable: Drawbridge", "piece_drawbridge", "$piece_drawbridge_dn", AutomationMechanic.OpenAllDoors, 6f, 8f, hasCheckForKey: false, "Timberwood Drawbridge"),
			new DoorKind("Buildable: Rustic Drawbridge", "piece_drawbridge_log", "$piece_drawbridge_log", AutomationMechanic.OpenAllDoors, 6f, 8f, hasCheckForKey: false),
			new DoorKind("World: Mountain Wood Gate", "MountainKit_wood_gate", "$piece_woodgate", AutomationMechanic.OpenAllDoors, 3f, 4.5f, hasCheckForKey: false, "Wood Gate, placed in mountain locations"),
			new DoorKind("World: Dvergr Wood Door", "piece_dvergr_wood_door", "$piece_dvergr_door", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("World: Mistlands Queen Door", "dungeon_queen_door", "$piece_queendoor", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: true),
			new DoorKind("World: Sunken Crypt Gate", "sunken_crypt_gate", "$piece_irongate", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: true),
			new DoorKind("World: Morkhalla Drawbridge", "Morkhalla_Drawbridge", "$morkhalla_drawbridge", AutomationMechanic.DoNotOpenAutomatically, 6f, 8f, hasCheckForKey: false, "Drawbridge, placed in Deep North Morkhalla locations"),
			new DoorKind("Dungeon: Burial Chamber Door", "dungeon_forestcrypt_door", "$piece_wooddoor", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Dungeon: Sunken Crypt Iron Gate", "dungeon_sunkencrypt_irongate", "$piece_irongate", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Dungeon: Dvergr Town Secret Door", "dvergrtown_secretdoor", "$piece_secretdoor", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Dungeon: Dvergr Town Sliding Door", "dvergrtown_slidingdoor", "$piece_dv_gate", AutomationMechanic.DoNotOpenAutomatically, 3f, 4.5f, hasCheckForKey: false),
			new DoorKind("Other: Other and Custom Doors", "other", "other", AutomationMechanic.OnlyOpenAllPlayerMadeDoors, 3f, 4.5f, hasCheckForKey: true)
		};

		private static readonly Dictionary<string, DoorKind> byPrefab = Index();

		public static IReadOnlyList<DoorKind> All { get; } = Array.AsReadOnly(kinds);

		public static DoorKind Other => kinds[kinds.Length - 1];

		public static DoorKind ForPrefab(string? prefab)
		{
			if (prefab == null || !byPrefab.TryGetValue(prefab, out DoorKind value))
			{
				return Other;
			}
			return value;
		}

		private static Dictionary<string, DoorKind> Index()
		{
			Dictionary<string, DoorKind> dictionary = new Dictionary<string, DoorKind>(StringComparer.Ordinal);
			DoorKind[] array = kinds;
			foreach (DoorKind doorKind in array)
			{
				dictionary.Add(doorKind.Prefab, doorKind);
			}
			return dictionary;
		}
	}
	public readonly struct DoorFacts
	{
		public bool MasterEnabled { get; }

		public bool IsGhost { get; }

		public bool CanInteract { get; }

		public long CreatorId { get; }

		public long LocalPlayerId { get; }

		public bool HasKeyItem { get; }

		public bool PlayerHasKey { get; }

		public DoorFacts(bool masterEnabled, bool isGhost, bool canInteract, long creatorId, long localPlayerId, bool hasKeyItem, bool playerHasKey)
		{
			MasterEnabled = masterEnabled;
			IsGhost = isGhost;
			CanInteract = canInteract;
			CreatorId = creatorId;
			LocalPlayerId = localPlayerId;
			HasKeyItem = hasKeyItem;
			PlayerHasKey = playerHasKey;
		}
	}
	public static class DoorRules
	{
		public static bool KeyAllows(bool hasKeyItem, bool kindHasCheckForKey, bool checkForKey, bool playerHasKey)
		{
			if (!hasKeyItem)
			{
				return true;
			}
			return kindHasCheckForKey && checkForKey && playerHasKey;
		}

		public static bool AutomationEnabled(AutomationMechanic mechanic, bool kindHasCheckForKey, bool checkForKey, DoorFacts facts)
		{
			if (!facts.MasterEnabled || facts.IsGhost || !facts.CanInteract || !KeyAllows(facts.HasKeyItem, kindHasCheckForKey, checkForKey, facts.PlayerHasKey))
			{
				return false;
			}
			switch (mechanic)
			{
			case AutomationMechanic.OpenAllDoors:
				return true;
			case AutomationMechanic.OnlyOpenSelfmadeDoors:
				if (facts.CreatorId != 0L)
				{
					return facts.CreatorId == facts.LocalPlayerId;
				}
				return false;
			case AutomationMechanic.OnlyOpenAllPlayerMadeDoors:
				return facts.CreatorId != 0;
			default:
				return false;
			}
		}

		public static bool TryParseMechanic(string? text, out AutomationMechanic mechanic)
		{
			mechanic = AutomationMechanic.OnlyOpenSelfmadeDoors;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string b = text.Trim();
			foreach (AutomationMechanic value in Enum.GetValues(typeof(AutomationMechanic)))
			{
				if (string.Equals(value.ToString(), b, StringComparison.OrdinalIgnoreCase))
				{
					mechanic = value;
					return true;
				}
			}
			return false;
		}
	}
	public enum DoorAction
	{
		None,
		Open,
		Close
	}
	public static class DoorState
	{
		public static bool IsOpen(int state, bool invertedText)
		{
			if (!invertedText)
			{
				return state != 0;
			}
			return state == 0;
		}

		public static int ClosedState(bool invertedText)
		{
			return invertedText ? 1 : 0;
		}
	}
	public readonly struct DoorTick
	{
		public double Distance { get; }

		public float OpenDistance { get; }

		public float CloseDistance { get; }

		public bool AutomationEnabled { get; }

		public int State { get; }

		public bool InvertedText { get; }

		public bool KeepOpenWhileOthersNear { get; }

		public bool OtherPlayerWithinClose { get; }

		public DoorTick(double distance, float openDistance, float closeDistance, bool automationEnabled, int state, bool invertedText, bool keepOpenWhileOthersNear, bool otherPlayerWithinClose)
		{
			Distance = distance;
			OpenDistance = openDistance;
			CloseDistance = closeDistance;
			AutomationEnabled = automationEnabled;
			State = state;
			InvertedText = invertedText;
			KeepOpenWhileOthersNear = keepOpenWhileOthersNear;
			OtherPlayerWithinClose = otherPlayerWithinClose;
		}
	}
	public sealed class DoorTracker
	{
		public bool InRange { get; private set; }

		public bool AutoOpened { get; private set; }

		public int? ExpectedState { get; private set; }

		public bool ForeignChange { get; private set; }

		public static int CloseState(bool invertedText)
		{
			return DoorState.ClosedState(invertedText);
		}

		public DoorAction Step(DoorTick tick)
		{
			var (num, num2) = DoorDistances.Normalize(tick.OpenDistance, tick.CloseDistance);
			if (InRange)
			{
				if (tick.Distance > (double)num2)
				{
					InRange = false;
				}
			}
			else if (tick.Distance <= (double)num)
			{
				InRange = true;
			}
			bool flag = DoorState.IsOpen(tick.State, tick.InvertedText);
			if (AutoOpened)
			{
				if (!ExpectedState.HasValue)
				{
					if (flag)
					{
						ExpectedState = tick.State;
					}
				}
				else if (tick.State != ExpectedState.Value)
				{
					ForeignChange = true;
				}
			}
			if (InRange)
			{
				if (tick.AutomationEnabled && !flag)
				{
					if (AutoOpened)
					{
						return DoorAction.None;
					}
					Take(null);
					return DoorAction.Open;
				}
				if (!AutoOpened && flag)
				{
					Take(tick.State);
				}
				return DoorAction.None;
			}
			if (!AutoOpened)
			{
				return DoorAction.None;
			}
			bool keepOpenWhileOthersNear = tick.KeepOpenWhileOthersNear;
			if (!tick.AutomationEnabled || !flag || (keepOpenWhileOthersNear && ForeignChange))
			{
				Release();
				return DoorAction.None;
			}
			if (keepOpenWhileOthersNear && tick.OtherPlayerWithinClose)
			{
				return DoorAction.None;
			}
			Release();
			return DoorAction.Close;
		}

		public void Reset()
		{
			InRange = false;
			Release();
		}

		private void Take(int? expected)
		{
			AutoOpened = true;
			ExpectedState = expected;
			ForeignChange = false;
		}

		private void Release()
		{
			AutoOpened = false;
			ExpectedState = null;
			ForeignChange = false;
		}
	}
}
namespace ExtraPotions.Ember.Conversion
{
	public enum ImportSource
	{
		BetterSleepBruh,
		TorchesAreFires,
		DoorOpenerBruh
	}
	public enum ImportNoteKind
	{
		Skipped,
		Clamped,
		Fallback
	}
	public sealed class ImportNote
	{
		public ImportNoteKind Kind { get; }

		public string Source { get; }

		public string Text { get; }

		internal ImportNote(ImportNoteKind kind, string source, string text)
		{
			Kind = kind;
			Source = source;
			Text = text;
		}

		public override string ToString()
		{
			return Kind.ToString() + " " + Source + ": " + Text;
		}
	}
	public sealed class ImportValue
	{
		public string SettingId { get; }

		public object Value { get; }

		public string Source { get; }

		internal ImportValue(string settingId, object value, string source)
		{
			SettingId = settingId;
			Value = value;
			Source = source;
		}
	}
	public sealed class ImportPlan
	{
		public ImportSource Source { get; }

		public IReadOnlyList<ImportValue> Values { get; }

		public IReadOnlyList<ImportNote> Notes { get; }

		internal ImportPlan(ImportSource source, IReadOnlyList<ImportValue> values, IReadOnlyList<ImportNote> notes)
		{
			Source = source;
			Values = Array.AsReadOnly(values.ToArray());
			Notes = Array.AsReadOnly(notes.ToArray());
		}

		public string Report()
		{
			return string.Join("\n", new string[1] { "Source: " + Source }.Concat(Values.Select((ImportValue v) => "Set " + v.SettingId + " = " + Format(v.Value) + " (from " + v.Source + ")")).Concat(Notes.Select((ImportNote n) => n.ToString())));
		}

		internal static string Format(object value)
		{
			string text;
			if (!(value is float num))
			{
				if (value is bool)
				{
					if (!(bool)value)
					{
						return "false";
					}
					return "true";
				}
				text = Convert.ToString(value, CultureInfo.InvariantCulture);
				if (text == null)
				{
					return "";
				}
			}
			else
			{
				text = num.ToString("R", CultureInfo.InvariantCulture);
			}
			return text;
		}
	}
	public readonly struct SourceEntry
	{
		public string Section { get; }

		public string Key { get; }

		public string Value { get; }

		public SourceEntry(string section, string key, string value)
		{
			Section = section;
			Key = key;
			Value = value;
		}
	}
	public static class SourceConfig
	{
		public static IReadOnlyList<SourceEntry> Parse(string? text)
		{
			List<SourceEntry> list = new List<SourceEntry>();
			string section = "";
			string[] array = (text ?? "").Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				if (text2[0] == '[' && text2[text2.Length - 1] == ']')
				{
					section = text2.Substring(1, text2.Length - 2).Trim();
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					string text3 = text2.Substring(0, num).Trim();
					if (text3.Length != 0)
					{
						list.Add(new SourceEntry(section, text3, text2.Substring(num + 1).Trim()));
					}
				}
			}
			return list;
		}
	}
	public static class SourceImport
	{
		private static readonly Dictionary<string, EmberSetting> Bsb = new Dictionary<string, EmberSetting>(StringComparer.Ordinal)
		{
			{
				"Server Settings.Use Vanilla Sleep Start",
				EmberSettings.Sleep.UseVanillaSleepWindow
			},
			{
				"Server Settings.Sleep Start",
				EmberSettings.Sleep.SleepStart
			},
			{
				"Server Settings.Bonus Multiplier",
				EmberSettings.Sleep.BonusMultiplier
			},
			{
				"Server Settings.Bonus Increment Scale",
				EmberSettings.Sleep.BonusIncrementScale
			},
			{
				"Server Settings.Boost Fade (Real Seconds)",
				EmberSettings.Sleep.BoostFadeSeconds
			},
			{
				"Testing Mode.Enable Testing Mode",
				EmberSettings.Sleep.TestingMode
			},
			{
				"Testing Mode.Fake Total Players",
				EmberSettings.Sleep.FakeTotalPlayers
			},
			{
				"Testing Mode.Simulate Players In Bed",
				EmberSettings.Sleep.SimulatePlayersInBed
			},
			{
				"Station Timers.Protect Cooking Timers",
				EmberSettings.Stations.ProtectCookingTimers
			},
			{
				"Station Timers.Protect Smelter Timers",
				EmberSettings.Stations.ProtectSmelterTimers
			},
			{
				"Station Timers.Protect Crop Timers",
				EmberSettings.Stations.ProtectCropTimers
			},
			{
				"Station Timers.Protect Mead Timers",
				EmberSettings.Stations.ProtectMeadTimers
			},
			{
				"Station Timers.Protect Pickable Timers",
				EmberSettings.Stations.ProtectPickableTimers
			},
			{
				"Ocean & Boat Physics.Decouple Water Wave Speed",
				EmberSettings.Ocean.DecoupleWaterWaveSpeed
			},
			{
				"Ocean & Boat Physics.Calm Ocean During Sleep",
				EmberSettings.Ocean.CalmOceanDuringSleep
			},
			{
				"Ocean & Boat Physics.Ocean Calm Multiplier",
				EmberSettings.Ocean.OceanCalmMultiplier
			},
			{
				"Ocean & Boat Physics.Prevent Boat Sleep Impact Damage",
				EmberSettings.Ocean.PreventBoatSleepImpactDamage
			},
			{
				"Ocean & Boat Physics.Suppress Boat Impact Screen Shake",
				EmberSettings.Ocean.SuppressBoatImpactScreenShake
			}
		};

		private static readonly Dictionary<string, EmberSetting> Taf = new Dictionary<string, EmberSetting>(StringComparer.Ordinal)
		{
			{
				"2- General.Environment Affects Torch",
				EmberSettings.Torches.EnvironmentAffectsTorch
			},
			{
				"2- General.Cold Biome Affects Torch",
				EmberSettings.Torches.ColdBiomeAffectsTorch
			},
			{
				"2- General.Burn Rate",
				EmberSettings.Torches.BurnRate
			}
		};

		public static string FileName(ImportSource source)
		{
			return source switch
			{
				ImportSource.BetterSleepBruh => "vapok.mods.BetterSleepBruh.cfg", 
				ImportSource.TorchesAreFires => "blacks7ar.TorchesAreFires.cfg", 
				ImportSource.DoorOpenerBruh => "vapok.mods.dooropenerbruh.cfg", 
				_ => throw new ArgumentException("Unknown source.", "source"), 
			};
		}

		public static ImportPlan Plan(ImportSource source, string? text)
		{
			List<ImportValue> list = new List<ImportValue>();
			List<ImportNote> list2 = new List<ImportNote>();
			foreach (SourceEntry item in SourceConfig.Parse(text))
			{
				string text2 = item.Section + "." + item.Key;
				EmberSetting target = Map(source, item);
				if (target == null)
				{
					list2.Add(new ImportNote(ImportNoteKind.Skipped, text2, SkipReason(source, item)));
				}
				else if (!list.Any((ImportValue v) => v.SettingId == target.Id))
				{
					list.Add(new ImportValue(target.Id, Convert(target, item.Value, text2, list2), text2));
				}
			}
			return new ImportPlan(source, list, list2);
		}

		private static string SkipReason(ImportSource source, SourceEntry entry)
		{
			return "not imported";
		}

		private static EmberSetting? Map(ImportSource source, SourceEntry entry)
		{
			string text = entry.Section + "." + entry.Key;
			switch (source)
			{
			case ImportSource.BetterSleepBruh:
			{
				if (!Bsb.TryGetValue(text, out EmberSetting value2))
				{
					return null;
				}
				return value2;
			}
			case ImportSource.TorchesAreFires:
			{
				if (!Taf.TryGetValue(text, out EmberSetting value))
				{
					return null;
				}
				return value;
			}
			case ImportSource.DoorOpenerBruh:
			{
				if (text == "Local Settings.Enable Auto Door")
				{
					return EmberSettings.Doors.EnableAutoDoor;
				}
				DoorKind doorKind = DoorKinds.All.FirstOrDefault((DoorKind k) => k.Section == entry.Section);
				if (doorKind == null)
				{
					return null;
				}
				DoorKindSettings doorKindSettings = EmberSettings.Door(doorKind);
				return entry.Key switch
				{
					"Automation Mechanic" => doorKindSettings.Mechanic, 
					"Open Distance" => doorKindSettings.OpenDistance, 
					"Close Distance" => doorKindSettings.CloseDistance, 
					"Check for Key" => doorKindSettings.CheckForKey, 
					_ => null, 
				};
			}
			default:
				return null;
			}
		}

		private static object Convert(EmberSetting target, string text, string from, List<ImportNote> notes)
		{
			if (!(target is EmberSetting<bool>))
			{
				if (!(target is EmberSetting<float>))
				{
					if (!(target is EmberSetting<int>))
					{
						if (target is EmberSetting<AutomationMechanic>)
						{
							if (!DoorRules.TryParseMechanic(text, out var mechanic))
							{
								return Fallback("unknown mechanic \"" + text + "\"");
							}
							return mechanic;
						}
						return Fallback("unsupported type");
					}
					if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
					{
						return Fallback("unreadable \"" + text + "\"");
					}
					return Checked(result);
				}
				if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result2) || float.IsInfinity(result2))
				{
					return Fallback("unreadable \"" + text + "\"");
				}
				return Checked(result2);
			}
			if (string.Equals(text, "true", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "On", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (string.Equals(text, "false", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "Off", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return Fallback("unreadable \"" + text + "\"");
			object Checked(object value)
			{
				object obj = target.NormalizeBoxed(value);
				if (!object.Equals(obj, value))
				{
					notes.Add(new ImportNote(ImportNoteKind.Clamped, from, ImportPlan.Format(value) + " clamped to " + ImportPlan.Format(obj)));
				}
				return obj;
			}
			object Fallback(string why)
			{
				notes.Add(new ImportNote(ImportNoteKind.Fallback, from, why + "; Ember default " + ImportPlan.Format(target.DefaultValue)));
				return target.DefaultValue;
			}
		}
	}
}
namespace ExtraPotions.Ember.Config
{
	public enum SettingsPreset
	{
		MeadWorksDefault,
		Vanilla,
		Relaxed,
		Custom
	}
	public sealed class EmberPresetGroup
	{
		private readonly IReadOnlyDictionary<SettingsPreset, IReadOnlyDictionary<EmberSetting, object>> choices;

		public EmberSetting<SettingsPreset> Selector { get; }

		public IReadOnlyList<EmberSetting> Members { get; }

		internal EmberPresetGroup(EmberSetting<SettingsPreset> selector, IReadOnlyDictionary<SettingsPreset, IReadOnlyDictionary<EmberSetting, object>> choices)
		{
			Selector = selector;
			this.choices = choices;
			Members = choices[SettingsPreset.MeadWorksDefault].Keys.ToArray();
		}

		public IReadOnlyDictionary<EmberSetting, object> Values(SettingsPreset preset)
		{
			if (!choices.TryGetValue(preset, out IReadOnlyDictionary<EmberSetting, object> value))
			{
				return new Dictionary<EmberSetting, object>();
			}
			return value;
		}

		public SettingsPreset Reconcile(SettingsPreset saved, Func<EmberSetting, object> read)
		{
			if (saved == SettingsPreset.Custom || Values(saved).Count <= 0 || !Values(saved).All<KeyValuePair<EmberSetting, object>>((KeyValuePair<EmberSetting, object> p) => object.Equals(p.Value, read(p.Key))))
			{
				return SettingsPreset.Custom;
			}
			return saved;
		}
	}
	public sealed class PresetEdits
	{
		private bool selectorChanged;

		private bool memberChanged;

		public bool Pending
		{
			get
			{
				if (!selectorChanged)
				{
					return memberChanged;
				}
				return true;
			}
		}

		public bool ApplySelection
		{
			get
			{
				if (selectorChanged)
				{
					return !memberChanged;
				}
				return false;
			}
		}

		public void Observe(bool selector)
		{
			if (selector)
			{
				selectorChanged = true;
			}
			else
			{
				memberChanged = true;
			}
		}

		public void Clear()
		{
			selectorChanged = false;
			memberChanged = false;
		}
	}
	public static class EmberPresets
	{
		private static readonly Lazy<IReadOnlyList<EmberPresetGroup>> groups = new Lazy<IReadOnlyList<EmberPresetGroup>>(Create);

		public static IReadOnlyList<EmberPresetGroup> All => groups.Value;

		private static IReadOnlyList<EmberPresetGroup> Create()
		{
			EmberSetting[] members = new EmberSetting[7]
			{
				EmberSettings.Sleep.UseVanillaSleepWindow,
				EmberSettings.Sleep.SleepStart,
				EmberSettings.Sleep.PartialSleepBoost,
				EmberSettings.Sleep.BonusMultiplier,
				EmberSettings.Sleep.BonusIncrementScale,
				EmberSettings.Sleep.BoostFadeSeconds,
				EmberSettings.Sleep.WakeAtWindowEnd
			};
			Dictionary<EmberSetting, object> dictionary = Defaults(members);
			dictionary[EmberSettings.Sleep.UseVanillaSleepWindow] = true;
			dictionary[EmberSettings.Sleep.PartialSleepBoost] = false;
			Dictionary<EmberSetting, object> dictionary2 = Defaults(members);
			dictionary2[EmberSettings.Sleep.BonusMultiplier] = 1f;
			dictionary2[EmberSettings.Sleep.BonusIncrementScale] = 30f;
			dictionary2[EmberSettings.Sleep.BoostFadeSeconds] = 5f;
			EmberSetting[] members2 = new EmberSetting[1] { EmberSettings.Doors.EnableAutoDoor }.Concat(DoorKinds.All.SelectMany((DoorKind k) => new EmberSetting[2]
			{
				EmberSettings.Door(k).OpenDistance,
				EmberSettings.Door(k).CloseDistance
			})).ToArray();
			Dictionary<EmberSetting, object> dictionary3 = Defaults(members2);
			dictionary3[EmberSettings.Doors.EnableAutoDoor] = false;
			Dictionary<EmberSetting, object> dictionary4 = Defaults(members2);
			foreach (DoorKind item in DoorKinds.All)
			{
				dictionary4[EmberSettings.Door(item).OpenDistance] = item.PreviousOpenDistance;
				dictionary4[EmberSettings.Door(item).CloseDistance] = item.PreviousCloseDistance;
			}
			return new EmberPresetGroup[2]
			{
				Group(EmberSettings.Sleep.Preset, members, dictionary, dictionary2),
				Group(EmberSettings.Doors.Preset, members2, dictionary3, dictionary4)
			};
		}

		private static Dictionary<EmberSetting, object> Defaults(IEnumerable<EmberSetting> members)
		{
			return members.ToDictionary((EmberSetting s) => s, (EmberSetting s) => s.DefaultValue);
		}

		private static EmberPresetGroup Group(EmberSetting<SettingsPreset> selector, EmberSetting[] members, Dictionary<EmberSetting, object> vanilla, Dictionary<EmberSetting, object> relaxed)
		{
			return new EmberPresetGroup(selector, new Dictionary<SettingsPreset, IReadOnlyDictionary<EmberSetting, object>>
			{
				[SettingsPreset.MeadWorksDefault] = Defaults(members),
				[SettingsPreset.Vanilla] = vanilla,
				[SettingsPreset.Relaxed] = relaxed
			});
		}
	}
	public enum SettingScope
	{
		Local,
		ServerAuthoritative,
		Cheat
	}
	public abstract class EmberSetting
	{
		private static readonly IReadOnlyList<string> NoChoices = Array.Empty<string>();

		public string Section { get; }

		public string Key { get; }

		public string Id => Section + "." + Key;

		public SettingScope Scope { get; }

		public string Description { get; }

		public bool Lockable => Scope == SettingScope.Local;

		public IReadOnlyList<string> Choices { get; }

		public abstract Type ValueType { get; }

		public abstract object DefaultValue { get; }

		public abstract object? SafeValue { get; }

		public abstract object? Minimum { get; }

		public abstract object? Maximum { get; }

		private protected EmberSetting(string section, string key, SettingScope scope, string description, IReadOnlyList<string>? choices)
		{
			if (string.IsNullOrWhiteSpace(section) || section.Trim() != section)
			{
				throw new ArgumentException("Expected a trimmed section name.", "section");
			}
			if (string.IsNullOrWhiteSpace(key) || key.Trim() != key)
			{
				throw new ArgumentException("Expected a trimmed key.", "key");
			}
			if (!Enum.IsDefined(typeof(SettingScope), scope))
			{
				throw new ArgumentException("Unknown scope.", "scope");
			}
			if (string.IsNullOrWhiteSpace(description))
			{
				throw new ArgumentException("Expected a description.", "description");
			}
			Section = section;
			Key = key;
			Scope = scope;
			Description = description;
			Choices = choices ?? NoChoices;
		}

		public abstract object NormalizeBoxed(object? value);

		public override string ToString()
		{
			return Id + " (" + Scope.ToString() + ")";
		}

		public static EmberSetting<bool> Toggle(string section, string key, bool defaultValue, SettingScope scope, string description, bool? safe = null)
		{
			return new EmberSetting<bool>(section, key, scope, description, defaultValue, safe, null, (bool v) => v);
		}

		public static EmberSetting<int> Integer(string section, string key, int defaultValue, int min, int max, SettingScope scope, string description, int? safe = null)
		{
			if (min > max)
			{
				throw new ArgumentException("Minimum above maximum.", "min");
			}
			return new EmberSetting<int>(section, key, scope, description, defaultValue, safe, (min, max), (int v) => Math.Min(max, Math.Max(min, v)));
		}

		public static EmberSetting<float> Number(string section, string key, float defaultValue, float min, float max, SettingScope scope, string description, float? safe = null)
		{
			if (!(min <= max) || float.IsInfinity(min) || float.IsInfinity(max))
			{
				throw new ArgumentException("Expected a finite range.", "min");
			}
			return new EmberSetting<float>(section, key, scope, description, defaultValue, safe, (min, max), (float v) => (!float.IsNaN(v) && !float.IsInfinity(v)) ? Math.Min(max, Math.Max(min, v)) : defaultValue);
		}

		public static EmberSetting<TEnum> Choice<TEnum>(string section, string key, TEnum defaultValue, SettingScope scope, string description) where TEnum : struct, Enum
		{
			return new EmberSetting<TEnum>(section, key, scope, description, defaultValue, null, null, (TEnum v) => (!Enum.IsDefined(typeof(TEnum), v)) ? defaultValue : v, Enum.GetNames(typeof(TEnum)));
		}

		public static EmberSetting<string> Text(string section, string key, string defaultValue, SettingScope scope, string description, Func<string, string> normalize)
		{
			if (normalize == null)
			{
				throw new ArgumentNullException("normalize");
			}
			return new EmberSetting<string>(section, key, scope, description, defaultValue, null, null, normalize);
		}
	}
	public sealed class EmberSetting<T> : EmberSetting
	{
		private readonly Func<T, T> normalize;

		private readonly bool hasSafe;

		private readonly (T Min, T Max)? range;

		public T Default { get; }

		public T Safe { get; }

		public override Type ValueType => typeof(T);

		public override object DefaultValue => Default;

		public override object? SafeValue
		{
			get
			{
				if (!hasSafe)
				{
					return null;
				}
				return Safe;
			}
		}

		public override object? Minimum
		{
			get
			{
				if (!range.HasValue)
				{
					return null;
				}
				return range.Value.Min;
			}
		}

		public override object? Maximum
		{
			get
			{
				if (!range.HasValue)
				{
					return null;
				}
				return range.Value.Max;
			}
		}

		internal EmberSetting(string section, string key, SettingScope scope, string description, T defaultValue, object? safe, (T, T)? range, Func<T, T> normalize, IReadOnlyList<string>? choices = null)
			: base(section, key, scope, description, choices)
		{
			this.normalize = normalize ?? throw new ArgumentNullException("normalize");
			this.range = range;
			if (!EqualityComparer<T>.Default.Equals(normalize(defaultValue), defaultValue))
			{
				throw new ArgumentException(base.Id + ": the default is outside its own rule.", "defaultValue");
			}
			Default = defaultValue;
			if (scope == SettingScope.Cheat)
			{
				if (safe == null)
				{
					throw new ArgumentException(base.Id + ": a cheat needs a safe value.", "safe");
				}
				if (!EqualityComparer<T>.Default.Equals(defaultValue, (T)safe))
				{
					throw new ArgumentException(base.Id + ": a cheat must default to its safe value.", "defaultValue");
				}
				Safe = (T)safe;
				hasSafe = true;
			}
			else
			{
				if (safe != null)
				{
					throw new ArgumentException(base.Id + ": only cheats have a safe value.", "safe");
				}
				Safe = defaultValue;
			}
		}

		public T Normalize(T value)
		{
			return normalize(value);
		}

		public override object NormalizeBoxed(object? value)
		{
			return (value is T value2) ? Normalize(value2) : Default;
		}
	}
	public sealed class DoorKindSettings
	{
		public DoorKind Kind { get; }

		public EmberSetting<AutomationMechanic> Mechanic { get; }

		public EmberSetting<float> OpenDistance { get; }

		public EmberSetting<float> CloseDistance { get; }

		public EmberSetting<bool>? CheckForKey { get; }

		internal DoorKindSettings(DoorKind kind)
		{
			Kind = kind;
			string section = "Doors: " + kind.Section;
			Mechanic = EmberSetting.Choice(section, "Automation Mechanic", kind.DefaultMechanic, SettingScope.Local, ((kind.GameName == null) ? "" : ("In game: " + kind.GameName + ". ")) + "When doors of this kind open by themselves: OpenAllDoors, OnlyOpenSelfmadeDoors (built by you), OnlyOpenAllPlayerMadeDoors (built by any player) or DoNotOpenAutomatically.");
			OpenDistance = EmberSetting.Number(section, "Open Distance", kind.DefaultOpenDistance, 0.25f, 20f, SettingScope.Local, "Meters from the door at which it opens.");
			CloseDistance = EmberSetting.Number(section, "Close Distance", kind.DefaultCloseDistance, 0.25f, 20f, SettingScope.Local, "Meters from the door beyond which it closes again. Raised to Open Distance when set lower.");
			if (kind.HasCheckForKey)
			{
				CheckForKey = EmberSetting.Toggle(section, "Check for Key", kind.DefaultCheckForKey, SettingScope.Local, "Doors of this kind that need a key open by themselves only when this is on and you carry the key.");
			}
		}

		internal IEnumerable<EmberSetting> Entries()
		{
			yield return Mechanic;
			yield return OpenDistance;
			yield return CloseDistance;
			if (CheckForKey != null)
			{
				yield return CheckForKey;
			}
		}
	}
	public static class EmberSettings
	{
		public static class Sleep
		{
			public const string Section = "Sleep";

			public static readonly EmberSetting<SettingsPreset> Preset = EmberSetting.Choice("Sleep", "Preset", SettingsPreset.MeadWorksDefault, SettingScope.ServerAuthoritative, "Host or server: applies sleep window, partial boost, both rate factors, fade and morning wake rules live. MeadWorksDefault: noon start, 0.6 x 20 rate, 3-second fade. Vanilla: native sleep window and no partial boost. Relaxed: noon start, 1 x 30 rate, 5-second fade. Custom keeps individual values; editing a member selects Custom. Saved values are preserved on load.");

			public static readonly EmberSetting<bool> Enabled = EmberSetting.Toggle("Sleep", "Enabled", defaultValue: true, SettingScope.ServerAuthoritative, "Ember's multiplayer sleep. Off: the game's own sleep and no sleep HUD.");

			public static readonly EmberSetting<bool> UseVanillaSleepWindow = EmberSetting.Toggle("Sleep", "Use Vanilla Sleep Window", defaultValue: false, SettingScope.ServerAuthoritative, "On: the game's sleep window and skip rule, and no partial sleep boost.");

			public static readonly EmberSetting<float> SleepStart = EmberSetting.Number("Sleep", "Sleep Start", 0.5f, 0f, 0.99f, SettingScope.ServerAuthoritative, "Day fraction from which beds can be used (0.5 is noon). Sleep ends at morning (0.25).");

			public static readonly EmberSetting<bool> PartialSleepBoost = EmberSetting.Toggle("Sleep", "Partial Sleep Boost", defaultValue: true, SettingScope.ServerAuthoritative, "While some but not all players are in bed, the night passes faster. Off: only the all-in-bed skip.");

			public static readonly EmberSetting<float> BonusMultiplier = EmberSetting.Number("Sleep", "Bonus Multiplier", 0.6f, 0f, 1f, SettingScope.ServerAuthoritative, "Extra rate = Bonus Multiplier x sleeping / (players - 1) x Bonus Increment Scale. Defaults give 12 (13x speed) with all but one in bed.");

			public static readonly EmberSetting<float> BonusIncrementScale = EmberSetting.Number("Sleep", "Bonus Increment Scale", 20f, 0f, 30f, SettingScope.ServerAuthoritative, "Scales how much each sleeping player speeds up the night while others stay awake. Multiplied with Bonus Multiplier; 0 turns the partial-sleep speedup off.");

			public static readonly EmberSetting<float> BoostFadeSeconds = EmberSetting.Number("Sleep", "Boost Fade Seconds", 3f, 0f, 30f, SettingScope.ServerAuthoritative, "Real seconds before morning over which the boost ramps down to none. 0: hard stop at morning.");

			public static readonly EmberSetting<bool> WakeAtWindowEnd = EmberSetting.Toggle("Sleep", "Wake At Window End", defaultValue: true, SettingScope.ServerAuthoritative, "When morning comes without a full skip, everybody in bed wakes up.");

			public static readonly EmberSetting<bool> SleepInAnyBed = EmberSetting.Toggle("Sleep", "Sleep In Any Bed", defaultValue: true, SettingScope.ServerAuthoritative, "Sleep in a bed another player has claimed. The bed keeps its owner and your spawn point does not change; the game's time, roof, fire, wet and enemy checks still apply. The host decides; on by default.");

			public static readonly EmberSetting<bool> StandUpOnRespawn = EmberSetting.Toggle("Sleep", "Stand Up On Respawn", defaultValue: false, SettingScope.ServerAuthoritative, "After death, skip the getting-up animation when you respawn. Logging in and first-spawn flight keep the game's behavior. Requires Ember's Sleep module; off by default.");

			public static readonly EmberSetting<bool> NearestOwnedBedRespawn = EmberSetting.Toggle("Sleep", "Nearest Owned Bed Respawn", defaultValue: false, SettingScope.ServerAuthoritative, "After death, respawn at your nearest known owned bed. With at least two candidates and maps allowed, choose a bed on the native map for up to 30 seconds; timeout keeps the nearest. Map, Escape, or gamepad B confirms. Destroyed or reclaimed beds fall back to another known bed, then world start. Requires Sleep Enabled. Off by default; normal login is unchanged.");

			public static readonly EmberSetting<bool> QuickRespawn = EmberSetting.Toggle("Sleep", "Quick Respawn", defaultValue: false, SettingScope.ServerAuthoritative, "Shorten death and bed-loading waits near your respawn destination, up to the game's normal wait at 1,000 meters. Confirming a map bed choice skips the remaining waits; native area, bed and remote arrival checks still apply. With this option off, confirmation keeps the remaining native death delay and full loading wait. Requires Sleep Enabled; also works without nearest-bed selection. Disabling preserves pending requests and stops later acceleration. Off by default.");

			public static readonly EmberSetting<bool> OwnedBedMapPins = EmberSetting.Toggle("Sleep", "Show Owned Beds on Map", defaultValue: true, SettingScope.Local, "Requires Nearest Owned Bed Respawn and Sleep Enabled. Show learned owned beds for your character in this world as temporary yellow icons on the minimap and large map. The native bound-bed icon remains and your bed filter is preserved. Death-choice candidates appear even with this display off; temporary choice filters are restored afterward. Does not discover unknown beds.");

			public static readonly EmberSetting<bool> SleepAnyTime = EmberSetting.Toggle("Sleep", "Sleep Any Time", defaultValue: false, SettingScope.Cheat, "Cheat. Beds work at any time of day (the wake-up cooldown still applies). Off whenever the server blocks cheats.", false);

			public static readonly EmberSetting<bool> TestingMode = EmberSetting.Toggle("Sleep", "Testing Mode", defaultValue: false, SettingScope.Cheat, "Cheat. Adds Fake Total Players and Simulate Players In Bed to the real counts. Off whenever the server blocks cheats.", false);

			public static readonly EmberSetting<int> FakeTotalPlayers = EmberSetting.Integer("Sleep", "Fake Total Players", 10, 0, 80, SettingScope.ServerAuthoritative, "Players added to the count while Testing Mode is on.");

			public static readonly EmberSetting<int> SimulatePlayersInBed = EmberSetting.Integer("Sleep", "Simulate Players In Bed", 1, 0, 80, SettingScope.ServerAuthoritative, "Sleepers added while Testing Mode is on (at most Fake Total Players).");
		}

		public static class Hud
		{
			public const string Section = "Sleep HUD";

			public static readonly EmberSetting<bool> Show = EmberSetting.Toggle("Sleep HUD", "Show", defaultValue: true, SettingScope.Local, "Show the sleep panel under the minimap during the sleep window.");

			public static readonly EmberSetting<int> CompactAbove = EmberSetting.Integer("Sleep HUD", "Compact Above", 5, 1, 20, SettingScope.Local, "With more players than this, one pillow and a count are shown.");
		}

		public static class Stations
		{
			public const string Section = "Station Timers";

			public static readonly EmberSetting<bool> ProtectCookingTimers = EmberSetting.Toggle("Station Timers", "Protect Cooking Timers", defaultValue: true, SettingScope.ServerAuthoritative, "During the partial sleep boost, food on cooking stations and in ovens cooks and burns at normal speed.");

			public static readonly EmberSetting<bool> ProtectSmelterTimers = EmberSetting.Toggle("Station Timers", "Protect Smelter Timers", defaultValue: true, SettingScope.ServerAuthoritative, "During the partial sleep boost, smelters, blast furnaces, kilns and similar stations work at normal speed.");

			public static readonly EmberSetting<bool> ProtectCropTimers = EmberSetting.Toggle("Station Timers", "Protect Crop Timers", defaultValue: true, SettingScope.ServerAuthoritative, "During the partial sleep boost, crops and saplings grow at normal speed.");

			public static readonly EmberSetting<bool> ProtectMeadTimers = EmberSetting.Toggle("Station Timers", "Protect Mead Timers", defaultValue: true, SettingScope.ServerAuthoritative, "During the partial sleep boost, fermenters ferment at normal speed.");

			public static readonly EmberSetting<bool> ProtectPickableTimers = EmberSetting.Toggle("Station Timers", "Protect Pickable Timers", defaultValue: true, SettingScope.ServerAuthoritative, "During the partial sleep boost, berry bushes and other pickables regrow at normal speed.");
		}

		public static class Ocean
		{
			public const string Section = "Ocean & Boat Physics";

			public static readonly EmberSetting<bool> DecoupleWaterWaveSpeed = EmberSetting.Toggle("Ocean & Boat Physics", "Decouple Water Wave Speed", defaultValue: true, SettingScope.ServerAuthoritative, "While the night is accelerated, ocean waves move at normal real-time speed instead of boosted time.");

			public static readonly EmberSetting<bool> CalmOceanDuringSleep = EmberSetting.Toggle("Ocean & Boat Physics", "Calm Ocean During Sleep", defaultValue: true, SettingScope.ServerAuthoritative, "Ocean waves are calmed and smoothed while the night is accelerated.");

			public static readonly EmberSetting<float> OceanCalmMultiplier = EmberSetting.Number("Ocean & Boat Physics", "Ocean Calm Multiplier", 0.5f, 0f, 1f, SettingScope.ServerAuthoritative, "Wave height multiplier while calming is active (0 is flat, 1 is normal).");

			public static readonly EmberSetting<bool> PreventBoatSleepImpactDamage = EmberSetting.Toggle("Ocean & Boat Physics", "Prevent Boat Sleep Impact Damage", defaultValue: true, SettingScope.ServerAuthoritative, "Boats take no spurious wave impact damage during accelerated sleep.");

			public static readonly EmberSetting<bool> SuppressBoatImpactScreenShake = EmberSetting.Toggle("Ocean & Boat Physics", "Suppress Boat Impact Screen Shake", defaultValue: true, SettingScope.ServerAuthoritative, "No camera shake from fast wave wakes during accelerated sleep.");
		}

		public static class Torches
		{
			public const string Section = "Torches";

			public static readonly EmberSetting<bool> Enabled = EmberSetting.Toggle("Torches", "Enabled", defaultValue: true, SettingScope.ServerAuthoritative, "Torches act like a campfire: warmth, resting and the bed's fire check.");

			public static readonly EmberSetting<string> FirePieces = EmberSetting.Text("Torches", "Fire Pieces", "piece_groundtorch,piece_groundtorch_wood,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch", SettingScope.ServerAuthoritative, "Comma list of piece prefab names that act as fire while lit.", FirePieceList.Normalize);

			public static readonly EmberSetting<bool> HeldTorchWarms = EmberSetting.Toggle("Torches", "Held Torch Warms", defaultValue: true, SettingScope.ServerAuthoritative, "A lit torch held in hand keeps the player warm like a campfire.");

			public static readonly EmberSetting<bool> EnvironmentAffectsTorch = EmberSetting.Toggle("Torches", "Environment Affects Torch", defaultValue: true, SettingScope.ServerAuthoritative, "Rain and storms make a held torch burn at Burn Rate.");

			public static readonly EmberSetting<bool> ColdBiomeAffectsTorch = EmberSetting.Toggle("Torches", "Cold Biome Affects Torch", defaultValue: true, SettingScope.ServerAuthoritative, "The Mountains and the Deep North make a held torch burn at Burn Rate.");

			public static readonly EmberSetting<float> BurnRate = EmberSetting.Number("Torches", "Burn Rate", 0.5f, 0f, 1f, SettingScope.ServerAuthoritative, "Durability a held torch loses per second under the two rules above.");

			public static readonly EmberSetting<bool> EndlessTorches = EmberSetting.Toggle("Torches", "Endless Torches", defaultValue: false, SettingScope.Cheat, "Cheat. Held torches lose no durability and listed fire pieces use no fuel. Off whenever the server blocks cheats.", false);

			public static readonly EmberSetting<bool> FuelFreeFires = EmberSetting.Toggle("Torches", "Fuel Free Fires", defaultValue: true, SettingScope.ServerAuthoritative, "The five standard placed torches and campfires burn without consuming fuel, including at zero stock; held torches lose no durability. Stored fuel and durability stay unchanged. Requires Ember's Torches module.");

			public static readonly EmberSetting<bool> SmokelessFires = EmberSetting.Toggle("Torches", "Smokeless Fires", defaultValue: true, SettingScope.ServerAuthoritative, "The five standard placed torches, campfires and held torches produce no smoke. Other fires keep their behavior. Requires Ember's Torches module.");

			public static readonly EmberSetting<bool> TorchesNightOnly = EmberSetting.Toggle("Torches", "Torches Night Only", defaultValue: false, SettingScope.ServerAuthoritative, "Player-built Torch Pieces light at night and go out by day, removing their warmth while off. Requires Ember's Torches module and steps aside for OpenKeep. Off relights torches this schedule put out; visit your bases before removing Ember.");

			public static readonly EmberSetting<string> TorchPieces = EmberSetting.Text("Torches", "Torch Pieces", "piece_groundtorch,piece_groundtorch_wood,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch", SettingScope.ServerAuthoritative, "Comma list of placed fire prefab names switched by Torches Night Only. Independent of Fire Pieces warmth and fuel-free fire eligibility.", FirePieceList.Normalize);

			public static readonly EmberSetting<float> TorchMargin = EmberSetting.Number("Torches", "Torch Margin", 1f, 0f, 4f, SettingScope.ServerAuthoritative, "In-game hours to light before nightfall and remain lit after daybreak. Zero follows the native night exactly.");

			public static readonly EmberSetting<string> TorchSwitchKey = EmberSetting.Text("Torches", "Torch Switch Key", "O", SettingScope.Local, "While looking at a scheduled torch, keep it lit or return it to the night schedule. Requires ward access. Empty disables the key.", (string t) => (t ?? "").Trim());
		}

		public static class Seasons
		{
			public const string Section = "Seasons";

			public static readonly EmberSetting<bool> NightLength = EmberSetting.Toggle("Seasons", "Night Length", defaultValue: true, SettingScope.ServerAuthoritative, "Host or server: use Solstice's seasonal dawn and nightfall live, with nights limited to 10-50% of a day. Requires Ember's active Sleep module with Use Vanilla Sleep Window off. World day length and day counting stay unchanged. Off, missing Solstice or disabled seasons keeps Ember's usual timing.");

			public static readonly EmberSetting<bool> FuelBurn = EmberSetting.Toggle("Seasons", "Fuel Burn", defaultValue: true, SettingScope.ServerAuthoritative, "Host or server: use Solstice's held-torch durability drain, limited to 0.1-5 per second. Requires Ember's Torches module; Fuel Free Fires and allowed Endless Torches keep priority. Solstice already controls seasonal placed-fire fuel use. Off, missing Solstice or disabled seasons keeps Ember's configured held-torch rate.");

			public static readonly EmberSetting<bool> TorchHeat = EmberSetting.Toggle("Seasons", "Torch Heat", defaultValue: true, SettingScope.ServerAuthoritative, "Host or server: let Solstice decide whether Ember's torches provide warmth this season. Does not enable the Torches module or Held Torch Warms. Off, missing Solstice or disabled seasons keeps configured warmth.");
		}

		public static class Doors
		{
			public const string Section = "Doors";

			public static readonly EmberSetting<SettingsPreset> Preset = EmberSetting.Choice("Doors", "Preset", SettingsPreset.MeadWorksDefault, SettingScope.Local, "Each player: applies automatic-door enable and all per-kind open/close distances live. MeadWorksDefault: enabled, doors 1.5/2.25 m and drawbridges 3/4 m. Vanilla: automatic doors off. Relaxed: enabled, doors 3/4.5 m and drawbridges 6/8 m. Mechanics and key checks stay as configured. Custom keeps individual values; editing a member selects Custom. Server locks take priority; saved values are preserved on load.");

			public static readonly EmberSetting<bool> AutoDoorsAllowed = EmberSetting.Toggle("Doors", "Auto Doors Allowed", defaultValue: true, SettingScope.ServerAuthoritative, "Off turns automatic doors off for every player.");

			public static readonly EmberSetting<bool> EnableAutoDoor = EmberSetting.Toggle("Doors", "Enable Auto Door", defaultValue: true, SettingScope.Local, "Doors open as you walk up and close behind you.");

			public static readonly EmberSetting<bool> KeepOpenWhileOthersNear = EmberSetting.Toggle("Doors", "Keep Open While Others Near", defaultValue: true, SettingScope.Local, "Never close a door while another player is within its close distance.");

			public static readonly EmberSetting<string> ToggleKey = EmberSetting.Text("Doors", "Toggle Key", "", SettingScope.Local, "Key that turns Enable Auto Door on or off. Empty: no key.", (string t) => (t ?? "").Trim());
		}

		public static class Server
		{
			public const string Section = "Server";

			public static readonly EmberSetting<string> LockedPlayerSettings = EmberSetting.Text("Server", "Locked Player Settings", "", SettingScope.ServerAuthoritative, "Comma list of Ember player settings (Section.Key) the server forces to its own value for every player.", (string t) => (t ?? "").Trim());
		}

		public const string DoorSectionPrefix = "Doors: ";

		private static readonly Lazy<IReadOnlyDictionary<DoorKind, DoorKindSettings>> doors = new Lazy<IReadOnlyDictionary<DoorKind, DoorKindSettings>>(() => DoorKinds.All.ToDictionary((DoorKind k) => k, (DoorKind k) => new DoorKindSettings(k)));

		private static readonly Lazy<EmberSetting[]> all = new Lazy<EmberSetting[]>(() => new EmberSetting[51]
		{
			Sleep.Preset,
			Sleep.Enabled,
			Sleep.UseVanillaSleepWindow,
			Sleep.SleepStart,
			Sleep.PartialSleepBoost,
			Sleep.BonusMultiplier,
			Sleep.BonusIncrementScale,
			Sleep.BoostFadeSeconds,
			Sleep.WakeAtWindowEnd,
			Sleep.SleepInAnyBed,
			Sleep.StandUpOnRespawn,
			Sleep.NearestOwnedBedRespawn,
			Sleep.QuickRespawn,
			Sleep.OwnedBedMapPins,
			Sleep.SleepAnyTime,
			Sleep.TestingMode,
			Sleep.FakeTotalPlayers,
			Sleep.SimulatePlayersInBed,
			Hud.Show,
			Hud.CompactAbove,
			Stations.ProtectCookingTimers,
			Stations.ProtectSmelterTimers,
			Stations.ProtectCropTimers,
			Stations.ProtectMeadTimers,
			Stations.ProtectPickableTimers,
			Ocean.DecoupleWaterWaveSpeed,
			Ocean.CalmOceanDuringSleep,
			Ocean.OceanCalmMultiplier,
			Ocean.PreventBoatSleepImpactDamage,
			Ocean.SuppressBoatImpactScreenShake,
			Torches.Enabled,
			Torches.FirePieces,
			Torches.HeldTorchWarms,
			Torches.EnvironmentAffectsTorch,
			Torches.ColdBiomeAffectsTorch,
			Torches.BurnRate,
			Torches.EndlessTorches,
			Torches.FuelFreeFires,
			Torches.SmokelessFires,
			Torches.TorchesNightOnly,
			Torches.TorchPieces,
			Torches.TorchMargin,
			Torches.TorchSwitchKey,
			Seasons.NightLength,
			Seasons.FuelBurn,
			Seasons.TorchHeat,
			Doors.Preset,

plugins/Ember/Ember.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
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.Ember.Beds;
using ExtraPotions.Ember.Config;
using ExtraPotions.Ember.Conversion;
using ExtraPotions.Ember.Doors;
using ExtraPotions.Ember.Integration;
using ExtraPotions.Ember.Sleep;
using ExtraPotions.Ember.Torches;
using ExtraPotions.Meadworks.Contracts;
using ExtraPotions.Meadworks.Crucible;
using ExtraPotions.Meadworks.Crucible.UI;
using HarmonyLib;
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_guiutils")]
[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.5.10.0")]
[assembly: AssemblyInformationalVersion("0.5.10+fatal: not a git repository (or any of the parent directories): .git")]
[assembly: AssemblyProduct("Ember")]
[assembly: AssemblyTitle("Ember")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.10.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.Ember.Runtime
{
	[HarmonyPatch(typeof(Bed), "CheckEnemies")]
	internal static class BedEnemyWarning
	{
		internal static bool ConfirmedStale(Player human)
		{
			if (!Object.op_Implicit((Object)(object)human) || (Object)(object)human != (Object)(object)Player.m_localPlayer || ((Character)human).IsDead() || !EmberModules.ProductActive(out var _) || !human.IsSensed())
			{
				return false;
			}
			try
			{
				ZNetView component = ((Component)human).GetComponent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
				{
					return false;
				}
				List<BaseAI> allInstances = BaseAI.GetAllInstances();
				if (allInstances == null)
				{
					return false;
				}
				foreach (BaseAI item in allInstances)
				{
					if (!Object.op_Implicit((Object)(object)item))
					{
						continue;
					}
					Character component2 = ((Component)item).GetComponent<Character>();
					if (!Object.op_Implicit((Object)(object)component2))
					{
						return false;
					}
					if (!component2.IsDead() && BaseAI.IsEnemy(component2, (Character)(object)human) && !(((object)item).GetType() == typeof(AnimalAI)))
					{
						ZNetView component3 = ((Component)item).GetComponent<ZNetView>();
						if (!Object.op_Implicit((Object)(object)component3) || !component3.IsValid() || !component3.IsOwner())
						{
							return false;
						}
						if (((object)item).GetType() != typeof(MonsterAI))
						{
							return false;
						}
						if (((Behaviour)item).enabled && ((Component)item).gameObject.activeInHierarchy && (Object)(object)item.GetTargetCreature() == (Object)(object)human && (item.CanSeeTarget((Character)(object)human) || item.CanHearTarget((Character)(object)human)))
						{
							return false;
						}
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		[HarmonyPrefix]
		internal static bool Prefix(Player human, ref bool __result, bool __runOriginal)
		{
			if (!__runOriginal || !ConfirmedStale(human))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	internal static class OwnedBedChoice
	{
		private static readonly List<BedPoint> beds = new List<BedPoint>();

		private static Game? game;

		private static Player? actor;

		private static Minimap? map;

		private static Hud? hud;

		private static bool open;

		private static bool oldBedFilter;

		private static bool oldPingFilter;

		private static bool oldLoadingActive;

		private static bool screenOwned;

		private static float deadline;

		private static float oldZoom;

		private static float oldLastZoom;

		private static float oldLoadingAlpha;

		private static float pendingDue;

		private static Vector3 oldOffset;

		private static Vector3 deathPoint;

		private static BedPoint? pending;

		private static int closeFrame = -1;

		internal static bool Active
		{
			get
			{
				if (open && Object.op_Implicit((Object)(object)game) && game == Game.instance && Object.op_Implicit((Object)(object)actor) && (Object)(object)actor == (Object)(object)Player.m_localPlayer && ((Character)actor).IsDead() && Object.op_Implicit((Object)(object)map) && map == Minimap.instance && !Game.m_noMap && OwnedBedRespawn.Active && QuickRespawn.ChoiceCurrent)
				{
					return !ForeignPicker(map);
				}
				return false;
			}
		}

		internal static IReadOnlyList<BedPoint> Beds => beds;

		internal static Minimap? Map => map;

		internal static int SecondsLeft => Mathf.CeilToInt(Mathf.Max(0f, deadline - Time.time));

		private static bool ForeignPicker(Minimap owner)
		{
			if (!Object.op_Implicit((Object)(object)owner) || !Object.op_Implicit((Object)(object)owner.m_mapImageLarge))
			{
				return false;
			}
			Transform parent = ((Component)owner.m_mapImageLarge).transform.parent;
			Transform val = (Object.op_Implicit((Object)(object)parent) ? parent.Find("Threshold.Destinations") : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return ((Component)val).gameObject.activeInHierarchy;
			}
			return false;
		}

		internal static bool TryOpen(Player dead, IReadOnlyList<BedPoint> candidates, Vector3 at)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00b9: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if (open || !Object.op_Implicit((Object)(object)dead) || (Object)(object)dead != (Object)(object)Player.m_localPlayer || !((Character)dead).IsDead() || !Object.op_Implicit((Object)(object)instance) || !instance.m_hasGenerated || !Object.op_Implicit((Object)(object)instance.m_mapImageLarge) || !Object.op_Implicit((Object)(object)instance.m_biomeNameLarge) || instance.m_visibleIconTypes == null || instance.m_visibleIconTypes.Length <= 12 || Game.m_noMap || ZInput.VirtualKeyboardOpen || !Object.op_Implicit((Object)(object)Utils.GetMainCamera()) || ForeignPicker(instance) || candidates.Count < 2)
			{
				return false;
			}
			List<BedPoint> list = new List<BedPoint>();
			foreach (BedPoint candidate in candidates)
			{
				if (BedPoints.Finite(candidate) && !BedPoints.Contains(list, candidate))
				{
					list.Add(candidate);
				}
			}
			if (list.Count < 2 || !QuickRespawn.TryBeginChoice(out var num))
			{
				return false;
			}
			try
			{
				game = Game.instance;
				actor = dead;
				map = instance;
				deadline = num;
				deathPoint = at;
				oldBedFilter = instance.m_visibleIconTypes[5];
				oldPingFilter = instance.m_visibleIconTypes[12];
				oldZoom = instance.LargeZoom;
				oldLastZoom = instance.m_lastLargeZoom;
				oldOffset = instance.m_mapOffset;
				beds.Clear();
				beds.AddRange(list);
				open = true;
				pending = null;
				closeFrame = -1;
				instance.m_visibleIconTypes[5] = true;
				instance.m_visibleIconTypes[12] = true;
				instance.SetMapMode((MapMode)2);
				instance.m_mapOffset = Vector3.zero;
				double num2 = 0.0;
				BedPoint val = default(BedPoint);
				((BedPoint)(ref val))..ctor(at.x, at.y, at.z);
				foreach (BedPoint bed in beds)
				{
					num2 = Math.Max(num2, BedPoints.MapDistance(val, bed));
				}
				double num3 = (double)instance.m_textureSize * (double)instance.m_pixelSize;
				if (num3 > 0.0)
				{
					instance.LargeZoom = Math.Max(oldZoom, (float)(2.0 * num2 * 1.25 / num3));
				}
				OwnedBedPins.Refresh(instance);
				OwnedBedChoicePresentation.Open(instance, beds[0]);
				return true;
			}
			catch
			{
				Cleanup();
				if (QuickRespawn.ChoiceCurrent)
				{
					CompleteWithCover();
				}
				else
				{
					QuickRespawn.AbandonChoice();
				}
				return true;
			}
		}

		internal static void Tick()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			if (!open)
			{
				return;
			}
			if (!Active)
			{
				Close();
			}
			else if (Time.time >= deadline || (closeFrame >= 0 && Time.frameCount >= closeFrame))
			{
				Confirm();
			}
			else if (pending.HasValue && Time.time >= pendingDue)
			{
				BedPoint value = pending.Value;
				pending = null;
				if (Active && OwnedBedRespawn.Prefer(value))
				{
					Confirm();
				}
			}
		}

		private static bool InputFocus()
		{
			if (Active && !ZInput.VirtualKeyboardOpen && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible())
			{
				return !Menu.IsActive();
			}
			return false;
		}

		internal static bool TakesInput()
		{
			if (InputFocus())
			{
				return !Minimap.InTextInput();
			}
			return false;
		}

		private static void Queue(Vector3 point)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (!TakesInput() || !Object.op_Implicit((Object)(object)map))
			{
				return;
			}
			double num = OwnedBedChoicePresentation.WorldRadius(map);
			if (num <= 0.0)
			{
				num = map.PinInteractRadius;
			}
			int num2 = -1;
			double num3 = num;
			BedPoint val = default(BedPoint);
			((BedPoint)(ref val))..ctor(point.x, point.y, point.z);
			for (int i = 0; i < beds.Count; i++)
			{
				double num4 = BedPoints.MapDistance(beds[i], val);
				if (num4 < num3)
				{
					num3 = num4;
					num2 = i;
				}
			}
			if (num2 >= 0)
			{
				pending = beds[num2];
				pendingDue = Time.time + 0.3f;
			}
		}

		internal static bool LeftClick(Minimap owner)
		{
			//IL_0019: 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)
			if (!Active || map != owner)
			{
				return true;
			}
			if (TakesInput())
			{
				Queue(owner.ScreenToWorldPoint(ZInput.pointerPosition));
			}
			return false;
		}

		internal static void DoubleClick(Minimap owner)
		{
			if (open && map == owner)
			{
				pending = null;
			}
		}

		internal static bool UpdateMap(Minimap owner)
		{
			//IL_018c: 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)
			if (!open)
			{
				return true;
			}
			if (!Active)
			{
				Close();
				return true;
			}
			if (map != owner)
			{
				return true;
			}
			if ((closeFrame >= 0 && Time.frameCount >= closeFrame) || Time.time >= deadline)
			{
				Confirm();
				return false;
			}
			if (ZInput.VirtualKeyboardOpen || !Object.op_Implicit((Object)(object)Utils.GetMainCamera()))
			{
				return false;
			}
			owner.SetMapMode((MapMode)2);
			owner.m_hiddenFrames = 0;
			owner.m_shownFrames++;
			owner.inputDelay = Mathf.Max(0f, owner.inputDelay - Time.deltaTime);
			owner.UpdateNameInput();
			if (InputFocus() && Minimap.InTextInput() && owner.m_namePin != null && (ZInput.GetKeyDown((KeyCode)27, true) || ZInput.GetButton("JoyButtonB")))
			{
				((TMP_InputField)owner.m_nameInput).text = "";
				owner.OnPinTextEntered("");
			}
			bool flag = TakesInput();
			if (flag && owner.m_shownFrames > 1 && (ZInput.GetButtonDown("Map") || ZInput.GetKeyDown((KeyCode)27, true) || (ZInput.GetButtonDown("JoyMap") && (!ZInput.GetButton("JoyLTrigger") || !ZInput.GetButton("JoyLBumper"))) || ZInput.GetButtonDown("JoyButtonB")))
			{
				closeFrame = Time.frameCount + 1;
			}
			bool flag2 = flag && !ZInput.IsMouseActive() && ZInput.GetButtonUp("JoyButtonA") && owner.inputDelay <= 0f;
			if (flag2)
			{
				Queue(owner.ScreenToWorldPoint(new Vector3((float)Screen.width * 0.5f, (float)Screen.height * 0.5f)));
			}
			owner.UpdateMap(actor, Time.deltaTime, flag && !flag2);
			owner.UpdateDynamicPins(Time.deltaTime);
			OwnedBedChoicePresentation.BeforePins(owner);
			owner.m_pinUpdateRequired = false;
			owner.UpdatePins();
			OwnedBedChoicePresentation.AfterPins(owner);
			owner.UpdateBiome(actor);
			OwnedBedChoicePresentation.Label(owner, SecondsLeft);
			return false;
		}

		private static void Confirm()
		{
			if (!Active)
			{
				Close();
				return;
			}
			Cleanup();
			CompleteWithCover();
		}

		private static void CompleteWithCover()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			bool active = QuickRespawn.Active;
			if (!QuickRespawn.CompleteChoice())
			{
				QuickRespawn.AbandonChoice();
				return;
			}
			PlayerProfile val = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.GetPlayerProfile() : null);
			CanvasGroup val2 = (Object.op_Implicit((Object)(object)Hud.instance) ? Hud.instance.m_loadingScreen : null);
			if (active && val != null && val.HaveCustomSpawnPoint() && Object.op_Implicit((Object)(object)ZNetScene.instance) && !ZNetScene.instance.IsAreaReady(val.GetCustomSpawnPoint()) && Object.op_Implicit((Object)(object)val2))
			{
				((Component)val2).gameObject.SetActive(true);
				val2.alpha = 1f;
			}
		}

		internal static void Close()
		{
			Cleanup();
			QuickRespawn.AbandonChoice();
		}

		private static void Cleanup()
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Invalid comparison between Unknown and I4
			if (!open && !Object.op_Implicit((Object)(object)map) && !screenOwned)
			{
				return;
			}
			open = false;
			pending = null;
			closeFrame = -1;
			Minimap val = map;
			try
			{
				OwnedBedChoicePresentation.Close(val);
			}
			finally
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					if (val.m_visibleIconTypes[5])
					{
						val.m_visibleIconTypes[5] = oldBedFilter;
					}
					if (val.m_visibleIconTypes[12])
					{
						val.m_visibleIconTypes[12] = oldPingFilter;
					}
					if (val == Minimap.instance && !ForeignPicker(val))
					{
						if (val.m_namePin != null && Object.op_Implicit((Object)(object)val.m_nameInput))
						{
							val.OnPinTextEntered(((TMP_InputField)val.m_nameInput).text);
						}
						val.LargeZoom = oldZoom;
						val.m_lastLargeZoom = oldLastZoom;
						val.m_mapOffset = oldOffset;
						if ((int)val.m_mode == 2)
						{
							val.SetMapMode((MapMode)0);
						}
						val.m_hiddenFrames = 3;
					}
				}
				if (screenOwned && Object.op_Implicit((Object)(object)hud) && Object.op_Implicit((Object)(object)hud.m_loadingScreen))
				{
					if (hud.m_loadingScreen.alpha == 0f)
					{
						hud.m_loadingScreen.alpha = oldLoadingAlpha;
					}
					if (!((Component)hud.m_loadingScreen).gameObject.activeSelf)
					{
						((Component)hud.m_loadingScreen).gameObject.SetActive(oldLoadingActive);
					}
				}
				screenOwned = false;
				hud = null;
				map = null;
				actor = null;
				game = null;
				beds.Clear();
				if (Object.op_Implicit((Object)(object)val))
				{
					OwnedBedPins.Refresh(val);
				}
			}
		}

		internal static bool BlackScreen(Hud owner)
		{
			if (!Active)
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)owner.m_loadingScreen))
			{
				return true;
			}
			if (!screenOwned || owner != hud)
			{
				hud = owner;
				oldLoadingAlpha = owner.m_loadingScreen.alpha;
				oldLoadingActive = ((Component)owner.m_loadingScreen).gameObject.activeSelf;
				screenOwned = true;
			}
			owner.m_loadingScreen.alpha = 0f;
			((Component)owner.m_loadingScreen).gameObject.SetActive(false);
			return false;
		}

		internal static object Status()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			List<object> list = new List<object>();
			foreach (BedPoint bed in beds)
			{
				list.Add(new
				{
					x = bed.X,
					y = bed.Y,
					z = bed.Z
				});
			}
			Game instance = Game.instance;
			Vector3? obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				PlayerProfile playerProfile = instance.GetPlayerProfile();
				obj = ((playerProfile != null) ? new Vector3?(playerProfile.GetCustomSpawnPoint()) : ((Vector3?)null));
			}
			Vector3 val = (Vector3)(((??)obj) ?? Vector3.zero);
			return new
			{
				active = Active,
				open = open,
				deadline = deadline,
				secondsLeft = SecondsLeft,
				candidates = list,
				selected = new { val.x, val.y, val.z },
				deathPoint = new { deathPoint.x, deathPoint.y, deathPoint.z },
				pendingClick = pending.HasValue,
				pendingDue = pendingDue,
				mapInstance = (Object.op_Implicit((Object)(object)map) ? ((Object)map).GetInstanceID() : 0),
				choiceMapMode = (Object.op_Implicit((Object)(object)map) ? ((object)Unsafe.As<MapMode, MapMode>(ref map.m_mode)/*cast due to .constrained prefix*/).ToString() : "None"),
				labelFontSize = OwnedBedChoicePresentation.FontSize,
				labelScreenFontSize = OwnedBedChoicePresentation.ScreenFontSize
			};
		}
	}
	[HarmonyPatch(typeof(Minimap), "Update")]
	internal static class OwnedBedChoiceMapPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Before(Minimap __instance)
		{
			return OwnedBedChoice.UpdateMap(__instance);
		}

		[HarmonyPostfix]
		private static void After()
		{
			OwnedBedChoice.Tick();
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapLeftClick")]
	internal static class OwnedBedChoiceClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "com.meadworks.wayfarer", "com.meadworks.threshold" })]
		private static bool Before(Minimap __instance)
		{
			return OwnedBedChoice.LeftClick(__instance);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapDblClick")]
	internal static class OwnedBedChoiceDoubleClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Before(Minimap __instance)
		{
			OwnedBedChoice.DoubleClick(__instance);
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateBlackScreen")]
	internal static class OwnedBedChoiceScreenPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Before(Hud __instance)
		{
			return OwnedBedChoice.BlackScreen(__instance);
		}
	}
	internal static class OwnedBedChoicePresentation
	{
		private sealed class PinState
		{
			internal PinData Pin;

			internal bool Double;

			internal bool Animate;

			internal RectTransform? Rect;

			internal Vector2 Size;

			internal Canvas? ForeignCanvas;
		}

		private static readonly Dictionary<PinData, PinState> changed = new Dictionary<PinData, PinState>();

		private static readonly List<Canvas> canvases = new List<Canvas>();

		private static readonly List<RectTransform> bubbles = new List<RectTransform>();

		private static readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

		private static PinData? ping;

		private static TMP_Text? label;

		private static RectTransform? measured;

		private static string lastLabel = "";

		internal static float FontSize
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)label))
				{
					return 0f;
				}
				return label.fontSize;
			}
		}

		internal static float ScreenFontSize
		{
			get
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)label))
				{
					return 0f;
				}
				return label.fontSize * Mathf.Abs(label.transform.lossyScale.y);
			}
		}

		internal static void Open(Minimap map, BedPoint nearest)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			ping = new PinData
			{
				m_type = (PinType)12,
				m_name = "",
				m_pos = new Vector3(nearest.X, nearest.Y, nearest.Z),
				m_icon = map.GetSprite((PinType)12),
				m_save = false,
				m_doubleSize = true,
				m_animate = true
			};
			map.m_pins.Add(ping);
			map.m_pinUpdateRequired = true;
			Label(map, OwnedBedChoice.SecondsLeft);
		}

		private static void Enlarge(PinData? pin)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (pin != null)
			{
				if (!changed.ContainsKey(pin))
				{
					RectTransform uiElement = pin.m_uiElement;
					changed.Add(pin, new PinState
					{
						Pin = pin,
						Double = pin.m_doubleSize,
						Animate = pin.m_animate,
						Rect = uiElement,
						Size = (Object.op_Implicit((Object)(object)uiElement) ? uiElement.sizeDelta : Vector2.zero),
						ForeignCanvas = (Object.op_Implicit((Object)(object)uiElement) ? ((Component)uiElement).GetComponent<Canvas>() : null)
					});
				}
				pin.m_doubleSize = true;
				pin.m_animate = true;
			}
		}

		internal static void BeforePins(Minimap map)
		{
			foreach (PinData item in OwnedBedPins.Drawn)
			{
				Enlarge(item);
			}
			Enlarge(map.m_spawnPointPin);
		}

		private static void AboveMap(PinData? pin, Minimap map)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = pin?.m_uiElement;
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			Canvas component = ((Component)val).GetComponent<Canvas>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				Canvas componentInParent = ((Component)val).GetComponentInParent<Canvas>();
				component = ((Component)val).gameObject.AddComponent<Canvas>();
				component.overrideSorting = true;
				if (Object.op_Implicit((Object)(object)componentInParent))
				{
					component.sortingLayerID = componentInParent.rootCanvas.sortingLayerID;
					component.sortingOrder = componentInParent.rootCanvas.sortingOrder + 1;
				}
				canvases.Add(component);
			}
			if (!Object.op_Implicit((Object)(object)((Transform)val).Find("Ember.BedChoice.ClickArea")))
			{
				RectTransform val2 = (RectTransform)new GameObject("Ember.BedChoice.ClickArea", new Type[1] { typeof(RectTransform) })
				{
					layer = ((Component)val).gameObject.layer
				}.transform;
				((Transform)val2).SetParent((Transform)(object)val, false);
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0.5f, 0.5f);
				val2.pivot = val3;
				Vector2 anchorMin = (val2.anchorMax = val3);
				val2.anchorMin = anchorMin;
				val2.anchoredPosition = Vector2.zero;
				val2.sizeDelta = Vector2.one * (map.m_pinSizeLarge * 2f);
				bubbles.Add(val2);
				measured = val2;
			}
		}

		internal static void AfterPins(Minimap map)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			foreach (PinData item in OwnedBedPins.Drawn)
			{
				AboveMap(item, map);
			}
			AboveMap(map.m_spawnPointPin, map);
			if (Object.op_Implicit((Object)(object)ping?.m_iconElement))
			{
				((Graphic)ping.m_iconElement).color = new Color(1f, 0.76f, 0.2f, 1f);
			}
		}

		internal static double WorldRadius(Minimap map)
		{
			//IL_003c: 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_004c: 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_005b: 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_008e: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)measured) || !((Component)measured).gameObject.activeInHierarchy)
			{
				return 0.0;
			}
			measured.GetWorldCorners(corners);
			Vector3 val = (corners[0] + corners[2]) * 0.5f;
			Vector3 val2 = val + new Vector3((corners[2].x - corners[0].x) * 0.5f, 0f, 0f);
			Vector3 val3 = map.ScreenToWorldPoint(val);
			Vector3 val4 = map.ScreenToWorldPoint(val2);
			return BedPoints.MapDistance(new BedPoint(val3.x, val3.y, val3.z), new BedPoint(val4.x, val4.y, val4.z));
		}

		internal static void Label(Minimap map, int seconds)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: 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_00fe: 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)
			Transform transform = ((Component)map.m_mapImageLarge).transform;
			if (!Object.op_Implicit((Object)(object)label) || (Object)(object)label.transform.parent != (Object)(object)transform)
			{
				if (Object.op_Implicit((Object)(object)label))
				{
					Object.Destroy((Object)(object)((Component)label).gameObject);
				}
				GameObject val = new GameObject("Ember.BedChoice.Label", new Type[1] { typeof(RectTransform) });
				RectTransform val2 = (RectTransform)val.transform;
				((Transform)val2).SetParent(transform, false);
				((Transform)val2).SetAsLastSibling();
				Vector2 val3 = (val2.pivot = Vector2.up);
				Vector2 anchorMin = (val2.anchorMax = val3);
				val2.anchorMin = anchorMin;
				val2.anchoredPosition = new Vector2(18f, -18f);
				Rect rect = ((Graphic)map.m_mapImageLarge).rectTransform.rect;
				val2.sizeDelta = new Vector2(Mathf.Max(200f, Mathf.Min(560f, ((Rect)(ref rect)).width - 36f)), 220f);
				val.SetActive(false);
				label = (TMP_Text?)(object)val.AddComponent<TextMeshProUGUI>();
				label.font = map.m_biomeNameLarge.font;
				label.fontSharedMaterial = map.m_biomeNameLarge.fontSharedMaterial;
				((Graphic)label).color = ((Graphic)map.m_biomeNameLarge).color;
				label.fontSize = 24f;
				label.enableAutoSizing = false;
				label.alignment = (TextAlignmentOptions)257;
				label.textWrappingMode = (TextWrappingModes)1;
				((Graphic)label).raycastTarget = false;
				val.SetActive(true);
				lastLabel = "";
			}
			string text = "<size=180%>" + EmberText.Format("bed_choice_countdown", seconds) + "</size>\n" + EmberText.Text("bed_choice_nearest") + "\n" + EmberText.Text("bed_choice_click") + "\n" + EmberText.Text("bed_choice_keys");
			if (text != lastLabel)
			{
				label.text = text;
				lastLabel = text;
			}
		}

		internal static void Close(Minimap? map)
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)label))
			{
				Object.Destroy((Object)(object)((Component)label).gameObject);
			}
			label = null;
			lastLabel = "";
			if (Object.op_Implicit((Object)(object)map) && ping != null && map.m_pins.Contains(ping))
			{
				map.RemovePin(ping);
			}
			ping = null;
			foreach (PinState value in changed.Values)
			{
				bool doubleSize = value.Pin.m_doubleSize;
				if (doubleSize)
				{
					value.Pin.m_doubleSize = value.Double;
				}
				if (value.Pin.m_animate)
				{
					value.Pin.m_animate = value.Animate;
				}
				RectTransform uiElement = value.Pin.m_uiElement;
				bool flag = false;
				if (Object.op_Implicit((Object)(object)uiElement))
				{
					Canvas[] components = ((Component)uiElement).GetComponents<Canvas>();
					foreach (Canvas val in components)
					{
						if (Object.op_Implicit((Object)(object)val) && !canvases.Contains(val))
						{
							flag = true;
						}
					}
				}
				if (flag)
				{
					if (doubleSize && Object.op_Implicit((Object)(object)value.Rect) && value.Rect == uiElement)
					{
						value.Rect.sizeDelta = value.Size;
					}
				}
				else if (Object.op_Implicit((Object)(object)map))
				{
					map.DestroyPinMarker(value.Pin);
				}
			}
			changed.Clear();
			foreach (Canvas canvase in canvases)
			{
				if (Object.op_Implicit((Object)(object)canvase))
				{
					Object.Destroy((Object)(object)canvase);
				}
			}
			foreach (RectTransform bubble in bubbles)
			{
				if (Object.op_Implicit((Object)(object)bubble))
				{
					Object.Destroy((Object)(object)((Component)bubble).gameObject);
				}
			}
			canvases.Clear();
			bubbles.Clear();
			measured = null;
		}
	}
	internal static class OwnedBedPins
	{
		private static readonly Color Yellow = new Color(1f, 0.85f, 0.1f);

		private static readonly List<PinData> pins = new List<PinData>();

		private static readonly Dictionary<Image, Color> colors = new Dictionary<Image, Color>();

		private static readonly HashSet<Image> desiredImages = new HashSet<Image>();

		private static readonly List<Image> staleImages = new List<Image>();

		private static Minimap? owner;

		private static string? scope;

		private static long generation = -1L;

		private static float nextCheck;

		private static Vector3? shownSpawn;

		private static bool showingChoice;

		internal static bool Active
		{
			get
			{
				if (!OwnedBedChoice.Active)
				{
					if (OwnedBedRespawn.Active)
					{
						return Plugin.Settings?.Get<bool>(Sleep.OwnedBedMapPins) ?? false;
					}
					return false;
				}
				return true;
			}
		}

		internal static IReadOnlyList<PinData> Drawn => pins;

		private static BedPoint Point(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new BedPoint(p.x, p.y, p.z);
		}

		private static Vector3 Position(BedPoint p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(p.X, p.Y, p.Z);
		}

		private static bool SameSpawn(Vector3? a, Vector3? b)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (a.HasValue == b.HasValue)
			{
				if (a.HasValue)
				{
					if (a.Value.x == b.Value.x && a.Value.y == b.Value.y)
					{
						return a.Value.z == b.Value.z;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private static Vector3? Spawn()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile val = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.GetPlayerProfile() : null);
			if (val == null || !val.HaveCustomSpawnPoint())
			{
				return null;
			}
			return val.GetCustomSpawnPoint();
		}

		internal static void Tick()
		{
			Refresh(Minimap.instance);
		}

		internal static void Refresh(Minimap map)
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: 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_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)map) && map != Minimap.instance)
			{
				if (owner == map)
				{
					Clear();
				}
				return;
			}
			string text = OwnedBedRespawn.Scope();
			if (!Object.op_Implicit((Object)(object)map) || !Active || text == null || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				Clear();
				return;
			}
			if (map != owner || scope != text || generation != Plugin.Session.Generation)
			{
				Clear();
				owner = map;
				scope = text;
				generation = Plugin.Session.Generation;
			}
			Vector3? a = Spawn();
			bool active = OwnedBedChoice.Active;
			if (Time.time < nextCheck && SameSpawn(a, shownSpawn) && active == showingChoice)
			{
				return;
			}
			nextCheck = Time.time + 1f;
			shownSpawn = a;
			showingChoice = active;
			List<BedPoint> list = new List<BedPoint>();
			IReadOnlyList<BedPoint> readOnlyList2;
			if (!OwnedBedChoice.Active)
			{
				IReadOnlyList<BedPoint> readOnlyList = OwnedBedRespawn.Known();
				readOnlyList2 = readOnlyList;
			}
			else
			{
				readOnlyList2 = OwnedBedChoice.Beds;
			}
			foreach (BedPoint item2 in readOnlyList2)
			{
				if ((!a.HasValue || !BedPoints.Same(item2, Point(a.Value))) && !BedPoints.Contains(list, item2))
				{
					list.Add(item2);
				}
			}
			bool flag = list.Count != pins.Count;
			int num = 0;
			while (!flag && num < pins.Count)
			{
				flag = !map.m_pins.Contains(pins[num]) || !BedPoints.Same(Point(pins[num].m_pos), list[num]);
				num++;
			}
			if (!flag)
			{
				return;
			}
			RemovePins();
			foreach (BedPoint item3 in list)
			{
				PinData item = new PinData
				{
					m_type = (PinType)5,
					m_name = "",
					m_pos = Position(item3),
					m_icon = map.GetSprite((PinType)5),
					m_save = false
				};
				map.m_pins.Add(item);
				pins.Add(item);
				map.m_pinUpdateRequired = true;
			}
		}

		private static void RemovePins()
		{
			if (Object.op_Implicit((Object)(object)owner))
			{
				foreach (PinData pin in pins)
				{
					if (owner.m_pins.Contains(pin))
					{
						owner.RemovePin(pin);
					}
				}
			}
			pins.Clear();
		}

		private static void Restore(Image image)
		{
			//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)
			//IL_0020: 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)
			if (Object.op_Implicit((Object)(object)image) && colors.TryGetValue(image, out var value))
			{
				Color color = ((Graphic)image).color;
				if (((Color)(ref color)).Equals(Yellow))
				{
					((Graphic)image).color = value;
				}
			}
		}

		internal static void Tint(Minimap map)
		{
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: 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)
			Refresh(map);
			if (!Object.op_Implicit((Object)(object)owner) || owner != map)
			{
				return;
			}
			desiredImages.Clear();
			foreach (PinData pin in pins)
			{
				if (Object.op_Implicit((Object)(object)pin.m_iconElement))
				{
					desiredImages.Add(pin.m_iconElement);
				}
			}
			if (map.m_spawnPointPin != null && Object.op_Implicit((Object)(object)map.m_spawnPointPin.m_iconElement))
			{
				desiredImages.Add(map.m_spawnPointPin.m_iconElement);
			}
			staleImages.Clear();
			foreach (KeyValuePair<Image, Color> color2 in colors)
			{
				if (!Object.op_Implicit((Object)(object)color2.Key) || !desiredImages.Contains(color2.Key))
				{
					staleImages.Add(color2.Key);
				}
			}
			foreach (Image staleImage in staleImages)
			{
				Restore(staleImage);
				colors.Remove(staleImage);
			}
			foreach (Image desiredImage in desiredImages)
			{
				Color color = ((Graphic)desiredImage).color;
				if (((Color)(ref color)).Equals(Color.white))
				{
					if (!colors.ContainsKey(desiredImage))
					{
						colors.Add(desiredImage, color);
					}
					((Graphic)desiredImage).color = Yellow;
				}
				else if (colors.ContainsKey(desiredImage) && !((Color)(ref color)).Equals(Yellow))
				{
					colors.Remove(desiredImage);
				}
			}
		}

		internal static void Clear()
		{
			foreach (KeyValuePair<Image, Color> color in colors)
			{
				Restore(color.Key);
			}
			colors.Clear();
			desiredImages.Clear();
			staleImages.Clear();
			RemovePins();
			owner = null;
			scope = null;
			generation = -1L;
			nextCheck = 0f;
			shownSpawn = null;
			showingChoice = false;
		}

		internal static object Status()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			foreach (PinData pin in pins)
			{
				list.Add(pin.m_pos);
			}
			return new
			{
				active = Active,
				scope = scope,
				ownedCount = pins.Count,
				mapInstance = (Object.op_Implicit((Object)(object)owner) ? ((Object)owner).GetInstanceID() : 0),
				ownedPositions = list.ToArray(),
				tintedImages = colors.Count
			};
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdateProfilePins")]
	internal static class OwnedBedPinsPatch
	{
		[HarmonyPostfix]
		private static void After(Minimap __instance, bool __runOriginal)
		{
			if (__runOriginal)
			{
				OwnedBedPins.Refresh(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdatePins")]
	internal static class OwnedBedPinTintPatch
	{
		[HarmonyPostfix]
		private static void After(Minimap __instance, bool __runOriginal)
		{
			if (__runOriginal)
			{
				OwnedBedPins.Tint(__instance);
			}
		}
	}
	internal static class OwnedBedRespawn
	{
		private struct Change
		{
			internal string Scope;

			internal BedPoint Point;

			internal bool Add;
		}

		internal const string KeyPrefix = "meadworks.ember.beds.";

		private static readonly List<Change> pending = new List<Change>();

		private static readonly List<BedPoint> ordered = new List<BedPoint>();

		private static readonly List<BedPoint> fallback = new List<BedPoint>();

		private static readonly Dictionary<Bed, float> claims = new Dictionary<Bed, float>();

		private static string? currentScope;

		private static string? fallbackScope;

		private static bool seeded;

		private static long generation = -1L;

		internal static bool Active
		{
			get
			{
				if (Plugin.AllowsUi && EmberModules.ProductActive(out var _))
				{
					EmberConfig? settings = Plugin.Settings;
					if (settings != null && settings.Get<bool>(Sleep.Enabled) && Plugin.Settings.Get<bool>(Sleep.NearestOwnedBedRespawn))
					{
						return SourceAllows();
					}
				}
				return false;
			}
		}

		private static string Key => "meadworks.ember.beds." + ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture);

		private static bool Writable
		{
			get
			{
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)Game.instance) && Game.instance.GetPlayerProfile() != null)
				{
					return Player.m_localPlayer.GetPlayerID() == Game.instance.GetPlayerProfile().GetPlayerID();
				}
				return false;
			}
		}

		private static bool SourceAllows()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if (!Chainloader.PluginInfos.TryGetValue("milkyteam.openkeep", out var value))
			{
				return true;
			}
			BaseUnityPlugin instance = value.Instance;
			ConfigEntry<bool> val = default(ConfigEntry<bool>);
			if ((Object)(object)instance != (Object)null && instance.Config.TryGetEntry<bool>(new ConfigDefinition("8. Homestead", "Nearest Bed Respawn"), ref val))
			{
				return !val.Value;
			}
			return false;
		}

		internal static string? Scope()
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.GetWorld() == null || ZNet.instance.IsDedicated() || !Object.op_Implicit((Object)(object)Game.instance))
			{
				return null;
			}
			long worldUID = ZNet.instance.GetWorldUID();
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			if (worldUID == 0L || playerProfile == null || playerProfile.GetPlayerID() == 0L || !Plugin.Session.AuthorityReady || Plugin.Session.WorldId != worldUID)
			{
				return null;
			}
			return playerProfile.GetPlayerID().ToString(CultureInfo.InvariantCulture) + "@" + worldUID.ToString(CultureInfo.InvariantCulture);
		}

		private static BedPoint Point(Vector3 v)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new BedPoint(v.x, v.y, v.z);
		}

		private static Vector3 Position(BedPoint p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(p.X, p.Y, p.Z);
		}

		private static bool BindScope()
		{
			string text = Scope();
			if (!Active || text == null)
			{
				Reset();
				return false;
			}
			if (currentScope != text || generation != Plugin.Session.Generation)
			{
				Reset();
				currentScope = text;
				generation = Plugin.Session.Generation;
			}
			return true;
		}

		internal static List<BedPoint> Known()
		{
			if (!Writable || currentScope == null || Scope() != currentScope)
			{
				return new List<BedPoint>();
			}
			if (!Player.m_localPlayer.m_customData.TryGetValue(Key, out var value))
			{
				return new List<BedPoint>();
			}
			return BedPoints.Decode(value);
		}

		private static void Record(BedPoint point, bool add)
		{
			//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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			if (!BindScope() || !BedPoints.Finite(point))
			{
				return;
			}
			if (!Writable)
			{
				pending.RemoveAll((Change c) => c.Scope == currentScope && BedPoints.Same(c.Point, point));
				pending.Add(new Change
				{
					Scope = currentScope,
					Point = point,
					Add = add
				});
				return;
			}
			List<BedPoint> list = Known();
			if (add == BedPoints.Contains(list, point))
			{
				return;
			}
			if (add)
			{
				list.Add(point);
			}
			else
			{
				list.RemoveAll((BedPoint p) => BedPoints.Same(p, point));
			}
			Player.m_localPlayer.m_customData[Key] = BedPoints.Encode((IEnumerable<BedPoint>)list);
		}

		private static bool Live(Bed bed)
		{
			if (Object.op_Implicit((Object)(object)bed) && Object.op_Implicit((Object)(object)bed.m_spawnPoint) && Object.op_Implicit((Object)(object)bed.m_nview) && bed.m_nview.IsValid())
			{
				return bed.m_nview.GetZDO() != null;
			}
			return false;
		}

		internal static void Observe(Bed bed)
		{
			//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 (BindScope() && Live(bed))
			{
				long num = bed.m_nview.GetZDO().GetLong(ZDOVars.s_owner, 0L);
				Record(Point(bed.GetSpawnPoint()), num != 0L && num == Game.instance.GetPlayerProfile().GetPlayerID());
			}
		}

		internal static void ClaimMayHaveChanged(Bed bed)
		{
			if (BindScope() && Live(bed))
			{
				claims[bed] = Time.unscaledTime + 5f;
			}
		}

		internal static void Tick()
		{
			if (!BindScope())
			{
				return;
			}
			if (Writable && !seeded)
			{
				seeded = true;
				Bed[] array = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0);
				for (int i = 0; i < array.Length; i++)
				{
					Observe(array[i]);
				}
			}
			KeyValuePair<Bed, float>[] array2 = claims.ToArray();
			for (int i = 0; i < array2.Length; i++)
			{
				KeyValuePair<Bed, float> keyValuePair = array2[i];
				Bed key = keyValuePair.Key;
				if (!Live(key))
				{
					claims.Remove(key);
				}
				else if (key.m_nview.GetZDO().GetLong(ZDOVars.s_owner, 0L) == Game.instance.GetPlayerProfile().GetPlayerID() || Time.unscaledTime >= keyValuePair.Value)
				{
					Observe(key);
					claims.Remove(key);
				}
			}
		}

		internal static void Died(Player player)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			if (!BindScope() || !Writable || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)((Character)player).m_nview) || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner() || !((Character)player).IsDead())
			{
				return;
			}
			ordered.Clear();
			fallback.Clear();
			fallbackScope = null;
			List<BedPoint> list = Known();
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			if (playerProfile.HaveCustomSpawnPoint())
			{
				BedPoint val = Point(playerProfile.GetCustomSpawnPoint());
				if (BedPoints.Finite(val) && !BedPoints.Contains(list, val))
				{
					list.Add(val);
				}
			}
			BedPoints.SortByDistance(list, Point(((Component)player).transform.position));
			if (list.Count != 0)
			{
				fallbackScope = currentScope;
				ordered.AddRange(list);
				playerProfile.SetCustomSpawnPoint(Position(list[0]));
				fallback.AddRange(list.Skip(1));
			}
		}

		internal static IReadOnlyList<BedPoint> Choices()
		{
			if (fallbackScope == null || !(fallbackScope == Scope()))
			{
				return Array.Empty<BedPoint>();
			}
			return ordered.ToArray();
		}

		internal static bool Prefer(BedPoint point)
		{
			//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_0059: 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_006e: 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)
			if (!QuickRespawn.ChoiceCurrent || !BindScope() || !Writable || fallbackScope != currentScope)
			{
				return false;
			}
			int num = ordered.FindIndex((BedPoint p) => BedPoints.Same(p, point));
			if (num < 0)
			{
				return false;
			}
			BedPoint actual = ordered[num];
			Game.instance.GetPlayerProfile().SetCustomSpawnPoint(Position(actual));
			fallback.Clear();
			fallback.AddRange(ordered.Where((BedPoint p) => !BedPoints.Same(p, actual)));
			return true;
		}

		internal static void FailedSpawn(BedPoint gone)
		{
			//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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (BindScope() && !(fallbackScope != currentScope) && Game.instance.m_respawnAfterDeath)
			{
				Record(gone, add: false);
				ordered.RemoveAll((BedPoint val) => BedPoints.Same(val, gone));
				fallback.RemoveAll((BedPoint val) => BedPoints.Same(val, gone));
				if (fallback.Count != 0)
				{
					BedPoint p = fallback[0];
					fallback.RemoveAt(0);
					Game.instance.GetPlayerProfile().SetCustomSpawnPoint(Position(p));
				}
			}
		}

		internal static void Gone(Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Record(Point(point), add: false);
		}

		internal static void Spawned(Player player)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !BindScope())
			{
				return;
			}
			ordered.Clear();
			fallback.Clear();
			fallbackScope = null;
			Change[] array = pending.ToArray();
			pending.Clear();
			Change[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Change change = array2[i];
				if (change.Scope == currentScope)
				{
					Record(change.Point, change.Add);
				}
			}
		}

		internal static void Reset()
		{
			pending.Clear();
			ordered.Clear();
			fallback.Clear();
			claims.Clear();
			currentScope = null;
			fallbackScope = null;
			seeded = false;
			generation = -1L;
		}

		internal static object Status()
		{
			return new
			{
				active = Active,
				scope = Scope(),
				key = ((Scope() != null) ? Key : null),
				known = Known().Select(Position).ToArray(),
				fallback = fallback.Select(Position).ToArray(),
				pendingChanges = pending.Count,
				pendingClaims = claims.Count
			};
		}
	}
	[HarmonyPatch(typeof(Bed), "Awake")]
	internal static class OwnedBedSeenPatch
	{
		[HarmonyPostfix]
		private static void After(Bed __instance, bool __runOriginal)
		{
			if (__runOriginal)
			{
				OwnedBedRespawn.Observe(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Bed), "Interact")]
	internal static class OwnedBedClaimPatch
	{
		[HarmonyPostfix]
		private static void After(Bed __instance, Humanoid __0, bool __runOriginal)
		{
			if (__runOriginal && (Object)(object)__0 == (Object)(object)Player.m_localPlayer)
			{
				OwnedBedRespawn.ClaimMayHaveChanged(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class OwnedBedDeathPatch
	{
		[HarmonyPrefix]
		private static void Before(Player __instance, out QuickRespawn.DeathCall? __state)
		{
			__state = QuickRespawn.BeforeDeath(__instance);
		}

		[HarmonyPostfix]
		private static void After(Player __instance, bool __runOriginal, QuickRespawn.DeathCall? __state)
		{
			if (__runOriginal)
			{
				OwnedBedRespawn.Died(__instance);
			}
			QuickRespawn.AfterDeath(__instance, __state, __runOriginal);
		}
	}
	[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
	internal static class OwnedBedFallbackPatch
	{
		[HarmonyPrefix]
		private static void Before(Game __instance, out Vector3? __state)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile playerProfile = __instance.GetPlayerProfile();
			__state = ((OwnedBedRespawn.Active && __instance.m_respawnAfterDeath && playerProfile != null && playerProfile.HaveCustomSpawnPoint()) ? new Vector3?(playerProfile.GetCustomSpawnPoint()) : ((Vector3?)null));
		}

		[HarmonyPostfix]
		private static void After(Game __instance, bool __result, bool __runOriginal, Vector3? __state)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile playerProfile = __instance.GetPlayerProfile();
			if (!(!__runOriginal || __result) && __state.HasValue && playerProfile != null && !playerProfile.HaveCustomSpawnPoint())
			{
				Vector3 value = __state.Value;
				OwnedBedRespawn.FailedSpawn(new BedPoint(value.x, value.y, value.z));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "RemoveCustomSpawnPoint")]
	internal static class OwnedBedGonePatch
	{
		[HarmonyPostfix]
		private static void After(Vector3 point, bool __runOriginal)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			if (__runOriginal)
			{
				OwnedBedRespawn.Gone(point);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class OwnedBedSpawnedPatch
	{
		[HarmonyPostfix]
		private static void After(Player __instance, bool __runOriginal)
		{
			if (__runOriginal)
			{
				OwnedBedRespawn.Spawned(__instance);
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					QuickRespawn.Reset();
				}
			}
		}
	}
	internal static class QuickRespawn
	{
		internal sealed class DeathCall
		{
			internal Player Actor;

			internal Game Game;

			internal long Sequence;

			internal long Attempts;

			internal string Scope;

			internal long Generation;
		}

		internal sealed class LoadCall
		{
			internal Game Game;

			internal float Before;

			internal float Applied;

			internal bool Changed;
		}

		private static long sequence;

		private static long attempts;

		private static long ownedAttempt;

		private static long lastRequestFrame;

		private static Player? actor;

		private static bool choicePending;

		private static bool chosenLoad;

		private static bool currentBedObserved;

		private static float choiceDeadline;

		private static Player? lastActor;

		private static Game? lastGame;

		private static float lastDelay;

		private static float lastTime;

		private static bool lastAfterDeath;

		private static bool selfSchedule;

		private static bool captured;

		private static bool requestFired;

		private static bool segmentStarted;

		private static bool segmentDisabled;

		private static bool holding;

		private static string? scope;

		private static long generation;

		private static Game? game;

		private static BedPoint deathPoint;

		private static BedPoint target;

		private static float deathTime;

		private static float scheduledDelay;

		private static float scheduledAt;

		private static float loadStartedAt;

		private static float baseDuration;

		private static float lastWait;

		private static int sampleCount = -1;

		private static int sampleReads;

		private static int sampleChanges;

		private static long sampleIds;

		private static long sampleRevisions;

		private static float sampleTime;

		private static float sampleChangedAt;

		private static float lastBaseThreshold;

		private static float lastEffectiveThreshold;

		private static float lastWaitBefore;

		private static float lastWaitAfter;

		private static int loadCalls;

		private static int appliedCalls;

		private static int restoredCalls;

		private static int skippedCalls;

		private static int thrownCalls;

		private static int foreignValueCalls;

		private static string holdReason = "none";

		private static readonly AreaQuiet quiet = new AreaQuiet();

		private static readonly List<ZDO> found = new List<ZDO>();

		private static readonly SimulationDistance around = new SimulationDistance(1, 0, false);

		internal static bool Active
		{
			get
			{
				if (Plugin.AllowsUi && EmberModules.ProductActive(out var _))
				{
					EmberConfig? settings = Plugin.Settings;
					if (settings != null && settings.Get<bool>(Sleep.Enabled) && Plugin.Settings.Get<bool>(Sleep.QuickRespawn))
					{
						return SourceAllows();
					}
				}
				return false;
			}
		}

		internal static bool ChoiceCurrent
		{
			get
			{
				if (choicePending && CurrentScope() && OwnedBedRespawn.Active && Object.op_Implicit((Object)(object)actor) && (Object)(object)actor == (Object)(object)Player.m_localPlayer && ((Character)actor).IsDead() && Object.op_Implicit((Object)(object)((Character)actor).m_nview) && ((Character)actor).m_nview.IsValid() && ((Character)actor).m_nview.IsOwner() && attempts == ownedAttempt && ((MonoBehaviour)game).IsInvoking("_RequestRespawn"))
				{
					return !game.WaitingForRespawn();
				}
				return false;
			}
		}

		private static bool SourceAllows()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if (!Chainloader.PluginInfos.TryGetValue("milkyteam.openkeep", out var value))
			{
				return true;
			}
			BaseUnityPlugin instance = value.Instance;
			ConfigEntry<bool> val = default(ConfigEntry<bool>);
			if ((Object)(object)instance != (Object)null && instance.Config.TryGetEntry<bool>(new ConfigDefinition("8. Homestead", "Quick Respawn"), ref val))
			{
				return !val.Value;
			}
			return false;
		}

		private static BedPoint Point(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new BedPoint(p.x, p.y, p.z);
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static bool CurrentScope()
		{
			if (captured && Object.op_Implicit((Object)(object)game) && game == Game.instance && scope == OwnedBedRespawn.Scope())
			{
				return generation == Plugin.Session.Generation;
			}
			return false;
		}

		private static bool Current()
		{
			if (CurrentScope())
			{
				return Active;
			}
			return false;
		}

		private static bool PendingOwned()
		{
			if (CurrentScope() && Object.op_Implicit((Object)(object)actor) && (Object)(object)actor == (Object)(object)Player.m_localPlayer && ((Character)actor).IsDead() && Object.op_Implicit((Object)(object)((Character)actor).m_nview) && ((Character)actor).m_nview.IsValid() && ((Character)actor).m_nview.IsOwner() && attempts == ownedAttempt && ((MonoBehaviour)game).IsInvoking("_RequestRespawn"))
			{
				return !game.WaitingForRespawn();
			}
			return false;
		}

		internal static DeathCall? BeforeDeath(Player player)
		{
			string text = OwnedBedRespawn.Scope();
			if ((!Active && !OwnedBedRespawn.Active) || text == null || !Object.op_Implicit((Object)(object)Game.instance) || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || !Object.op_Implicit((Object)(object)((Character)player).m_nview) || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner())
			{
				return null;
			}
			return new DeathCall
			{
				Actor = player,
				Game = Game.instance,
				Sequence = sequence,
				Attempts = attempts,
				Scope = text,
				Generation = Plugin.Session.Generation
			};
		}

		internal static void RequestBeginning(Game instance)
		{
			attempts++;
			if (!selfSchedule && captured)
			{
				Reset();
			}
		}

		internal static void RequestFailed(Game instance)
		{
			if (captured && game == instance)
			{
				Reset();
			}
		}

		internal static void Requested(Game instance, float delay, bool afterDeath, bool original)
		{
			if (original)
			{
				sequence++;
				lastGame = instance;
				lastActor = Player.m_localPlayer;
				lastDelay = delay;
				lastAfterDeath = afterDeath;
				lastRequestFrame = Time.frameCount;
				lastTime = Time.time;
				if (!selfSchedule && captured)
				{
					Reset();
				}
			}
		}

		internal static void AfterDeath(Player player, DeathCall? call, bool original)
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: 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_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			if (call == null || !original)
			{
				return;
			}
			Reset();
			if ((!Active && !OwnedBedRespawn.Active) || (Object)(object)player != (Object)(object)call.Actor || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsDead() || call.Game != Game.instance || call.Scope != OwnedBedRespawn.Scope() || call.Generation != Plugin.Session.Generation || sequence != call.Sequence + 1 || attempts != call.Attempts + 1 || (Object)(object)lastActor != (Object)(object)player || lastGame != call.Game || !lastAfterDeath || lastDelay != 10f || lastRequestFrame != Time.frameCount || !((MonoBehaviour)call.Game).IsInvoking("_RequestRespawn") || call.Game.WaitingForRespawn())
			{
				return;
			}
			BedPoint val = Point(((Component)player).transform.position);
			float respawnLoadDuration = call.Game.m_respawnLoadDuration;
			if (!BedPoints.Finite(val) || !Finite(respawnLoadDuration) || respawnLoadDuration < 0f || !TryTarget(call.Game, out var destination))
			{
				return;
			}
			captured = true;
			game = call.Game;
			actor = player;
			scope = call.Scope;
			generation = call.Generation;
			deathPoint = val;
			deathTime = lastTime;
			ownedAttempt = attempts;
			long num = attempts;
			if (!OwnedBedChoice.TryOpen(player, OwnedBedRespawn.Choices(), ((Component)player).transform.position) && attempts == num && PendingOwned())
			{
				if (!Active)
				{
					Reset();
				}
				else
				{
					Schedule(RespawnWait.DeathDelay(BedPoints.MapDistance(val, destination), respawnLoadDuration, (double)(Time.time - deathTime)));
				}
			}
		}

		private static bool Schedule(float delay)
		{
			if (!PendingOwned() || !Finite(delay) || delay < 0f)
			{
				return false;
			}
			Game val = game;
			Player val2 = actor;
			long num = sequence;
			long num2 = attempts;
			selfSchedule = true;
			try
			{
				val.RequestRespawn(delay, true);
			}
			catch
			{
				Reset();
				throw;
			}
			finally
			{
				selfSchedule = false;
			}
			ownedAttempt = attempts;
			if (sequence != num + 1 || attempts != num2 + 1 || lastGame != val || (Object)(object)lastActor != (Object)(object)val2 || lastDelay != delay || !lastAfterDeath || lastRequestFrame != Time.frameCount || !PendingOwned())
			{
				Reset();
				return false;
			}
			scheduledDelay = delay;
			scheduledAt = Time.time;
			return true;
		}

		internal static bool TryBeginChoice(out float deadline)
		{
			deadline = 0f;
			if (choicePending || !OwnedBedRespawn.Active || !PendingOwned())
			{
				return false;
			}
			if (!Schedule(30f))
			{
				return false;
			}
			choicePending = true;
			choiceDeadline = (deadline = scheduledAt + scheduledDelay);
			return true;
		}

		internal static bool CompleteChoice()
		{
			if (!ChoiceCurrent)
			{
				return false;
			}
			bool active = Active;
			if (!Schedule(active ? 0f : Math.Max(0f, deathTime + 10f - Time.time)))
			{
				return false;
			}
			choicePending = false;
			chosenLoad = active;
			if (!active)
			{
				Reset();
			}
			return true;
		}

		internal static void AbandonChoice()
		{
			if (choicePending)
			{
				Reset();
			}
		}

		private static bool TryTarget(Game instance, out BedPoint destination)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			destination = default(BedPoint);
			PlayerProfile playerProfile = instance.GetPlayerProfile();
			if (playerProfile != null && playerProfile.HaveCustomSpawnPoint())
			{
				destination = Point(playerProfile.GetCustomSpawnPoint());
				return BedPoints.Finite(destination);
			}
			Vector3 p = default(Vector3);
			if (!Object.op_Implicit((Object)(object)ZoneSystem.instance) || !ZoneSystem.instance.GetLocationIcon(instance.m_StartLocation, ref p))
			{
				return false;
			}
			destination = Point(p);
			return BedPoints.Finite(destination);
		}

		internal static void RequestFired(Game instance, bool original)
		{
			if (original && CurrentScope() && instance == game && attempts == ownedAttempt)
			{
				if (choicePending)
				{
					choicePending = false;
					chosenLoad = Active;
				}
				requestFired = true;
				if (!Active)
				{
					Reset();
				}
			}
		}

		internal static void Tick()
		{
			if (captured && (!CurrentScope() || (choicePending ? (!ChoiceCurrent) : (!Active))))
			{
				Reset();
			}
		}

		internal static LoadCall? BeforeLoad(Game instance, float dt, bool original)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			if (!original || !Current() || !requestFired || instance != game || !instance.m_respawnAfterDeath || !instance.WaitingForRespawn() || !Finite(dt) || dt <= 0f)
			{
				return null;
			}
			PlayerProfile playerProfile = instance.GetPlayerProfile();
			if (playerProfile == null || !playerProfile.HaveCustomSpawnPoint())
			{
				return null;
			}
			BedPoint val = Point(playerProfile.GetCustomSpawnPoint());
			float respawnLoadDuration = instance.m_respawnLoadDuration;
			float respawnWait = instance.m_respawnWait;
			if (!BedPoints.Finite(val) || !Finite(respawnLoadDuration) || respawnLoadDuration < 0f || !Finite(respawnWait))
			{
				segmentDisabled = true;
				return null;
			}
			bool num = !segmentStarted || respawnWait <= 0f;
			if (segmentStarted && respawnWait > 0f && respawnWait < lastWait)
			{
				segmentDisabled = true;
				quiet.Reset();
			}
			if (num)
			{
				target = val;
				baseDuration = respawnLoadDuration;
				loadStartedAt = Time.time;
				segmentStarted = true;
				ResetQuiet();
			}
			else if (target.X != val.X || target.Y != val.Y || target.Z != val.Z || respawnLoadDuration != baseDuration)
			{
				segmentDisabled = true;
				quiet.Reset();
			}
			if (segmentDisabled)
			{
				return null;
			}
			loadCalls++;
			lastWaitBefore = respawnWait;
			lastBaseThreshold = respawnLoadDuration;
			float num2 = (chosenLoad ? 0f : RespawnWait.LoadDuration(BedPoints.MapDistance(deathPoint, val), respawnLoadDuration));
			holding = false;
			holdReason = "local-authority";
			if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer())
			{
				if (ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZoneSystem.instance))
				{
					segmentDisabled = true;
					holdReason = "unavailable";
					return null;
				}
				bool flag;
				try
				{
					flag = Settled(val);
				}
				catch (Exception)
				{
					segmentDisabled = true;
					holdReason = "sample-failed";
					quiet.Reset();
					return null;
				}
				bool num3 = flag && (!chosenLoad || currentBedObserved);
				bool flag2 = Time.time - loadStartedAt >= baseDuration;
				holdReason = (num3 ? "settled" : (flag2 ? "native-deadline" : ((chosenLoad && !currentBedObserved) ? "bed-arrival" : "quiet")));
				if (!num3 && !flag2)
				{
					num2 = Math.Max(num2, respawnWait + dt);
					holding = true;
				}
			}
			lastEffectiveThreshold = num2;
			LoadCall obj = new LoadCall
			{
				Game = instance,
				Before = respawnLoadDuration,
				Applied = num2,
				Changed = (num2 != respawnLoadDuration)
			};
			if (obj.Changed)
			{
				instance.m_respawnLoadDuration = num2;
				appliedCalls++;
			}
			return obj;
		}

		private static bool Settled(BedPoint at)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!quiet.Due((double)Time.time))
			{
				return quiet.Settled;
			}
			found.Clear();
			try
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(at.X, at.Y, at.Z);
				ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(val), around, found, (List<ZDO>)null);
				if (chosenLoad)
				{
					currentBedObserved = (Object)(object)game.FindBedNearby(val, 5f) != (Object)null;
				}
				long num = 0L;
				long num2 = 0L;
				foreach (ZDO item in found)
				{
					num ^= ((ZDOID)(ref item.m_uid)).UserID * 31 + ((ZDOID)(ref item.m_uid)).ID;
					num2 += item.DataRevision;
				}
				sampleReads++;
				if (sampleCount != found.Count || sampleIds != num || sampleRevisions != num2)
				{
					sampleChanges++;
					sampleChangedAt = Time.time;
				}
				sampleCount = found.Count;
				sampleIds = num;
				sampleRevisions = num2;
				sampleTime = Time.time;
				return quiet.Observe((double)Time.time, found.Count, num, num2);
			}
			finally
			{
				found.Clear();
			}
		}

		internal static void AfterLoad(LoadCall? call, bool original, bool threw = false)
		{
			if (call == null)
			{
				return;
			}
			if (call.Changed && Object.op_Implicit((Object)(object)call.Game))
			{
				if (call.Game.m_respawnLoadDuration == call.Applied)
				{
					call.Game.m_respawnLoadDuration = call.Before;
					restoredCalls++;
				}
				else
				{
					foreignValueCalls++;
				}
			}
			call.Changed = false;
			if (!original || threw)
			{
				segmentDisabled = true;
				if (threw)
				{
					thrownCalls++;
				}
				else
				{
					skippedCalls++;
				}
				quiet.Reset();
			}
			else
			{
				lastWait = (lastWaitAfter = call.Game.m_respawnWait);
			}
		}

		private static void ResetQuiet()
		{
			quiet.Reset();
			currentBedObserved = false;
			sampleCount = -1;
			sampleIds = (sampleRevisions = 0L);
			sampleTime = (sampleChangedAt = 0f);
		}

		internal static void Reset()
		{
			//IL_0056: 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_005d: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			captured = (requestFired = (segmentStarted = (segmentDisabled = (holding = (choicePending = (chosenLoad = false))))));
			actor = null;
			ownedAttempt = 0L;
			choiceDeadline = 0f;
			scope = null;
			game = null;
			generation = 0L;
			deathPoint = (target = default(BedPoint));
			deathTime = (scheduledDelay = (scheduledAt = (loadStartedAt = (baseDuration = (lastWait = 0f)))));
			ResetQuiet();
			found.Clear();
			holdReason = "none";
		}

		internal static object Status()
		{
			return new
			{
				active = Active,
				captured = captured,
				scope = scope,
				requestSequence = sequence,
				requestAttempts = attempts,
				ownedRequestAttempt = ownedAttempt,
				choicePending = choicePending,
				choiceDeadline = choiceDeadline,
				choiceOwned = ChoiceCurrent,
				choiceActor = (Object.op_Implicit((Object)(object)actor) ? actor.GetPlayerID().ToString() : null),
				chosenLoad = chosenLoad,
				scheduledDelay = scheduledDelay,
				deathTime = deathTime,
				scheduledAt = scheduledAt,
				deathDeadline = scheduledAt + scheduledDelay,
				requestFired = requestFired,
				loadStartedAt = loadStartedAt,
				nativeDeadline = loadStartedAt + baseDuration,
				deathPoint = new
				{
					x = deathPoint.X,
					y = deathPoint.Y,
					z = deathPoint.Z
				},
				target = new
				{
					x = target.X,
					y = target.Y,
					z = target.Z
				},
				baseDuration = baseDuration,
				nativeWait = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.m_respawnWait : 0f),
				nativeDuration = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.m_respawnLoadDuration : 0f),
				lastBaseThreshold = lastBaseThreshold,
				lastEffectiveThreshold = lastEffectiveThreshold,
				lastWaitBefore = lastWaitBefore,
				lastWaitAfter = lastWaitAfter,
				segmentDisabled = segmentDisabled,
				holding = holding,
				holdReason = holdReason,
				currentBedObserved = currentBedObserved,
				quiet = quiet.Settled,
				sampleCount = sampleCount,
				sampleIds = sampleIds,
				sampleRevisions = sampleRevisions,
				sampleTime = sampleTime,
				sampleChangedAt = sampleChangedAt,
				sampleReads = sampleReads,
				sampleChanges = sampleChanges,
				loadCalls = loadCalls,
				appliedCalls = appliedCalls,
				restoredCalls = restoredCalls,
				skippedCalls = skippedCalls,
				thrownCalls = thrownCalls,
				foreignValueCalls = foreignValueCalls
			};
		}
	}
	[HarmonyPatch(typeof(Game), "RequestRespawn")]
	internal static class QuickRespawnRequestPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Before(Game __instance)
		{
			QuickRespawn.RequestBeginning(__instance);
		}

		[HarmonyFinalizer]
		private static void Finally(Game __instance, Exception? __exception)
		{
			if (__exception != null)
			{
				QuickRespawn.RequestFailed(__instance);
			}
		}

		[HarmonyPostfix]
		private static void After(Game __instance, float delay, bool afterDeath, bool __runOriginal)
		{
			QuickRespawn.Requested(__instance, delay, afterDeath, __runOriginal);
		}
	}
	[HarmonyPatch(typeof(Game), "_RequestRespawn")]
	internal static class QuickRespawnRequestFiredPatch
	{
		[HarmonyPostfix]
		private static void After(Game __instance, bool __runOriginal)
		{
			QuickRespawn.RequestFired(__instance, __runOriginal);
		}
	}
	[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
	internal static class QuickRespawnLoadPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Before(Game __instance, float dt, bool __runOriginal, out QuickRespawn.LoadCall? __state)
		{
			__state = QuickRespawn.BeforeLoad(__instance, dt, __runOriginal);
		}

		[HarmonyPostfix]
		private static void After(QuickRespawn.LoadCall? __state, bool __runOriginal)
		{
			QuickRespawn.AfterLoad(__state, __runOriginal);
		}

		[HarmonyFinalizer]
		private static void Finally(QuickRespawn.LoadCall? __state, Exception? __exception)
		{
			if (__exception != null)
			{
				QuickRespawn.AfterLoad(__state, original: false, threw: true);
			}
		}
	}
	[HarmonyPatch(typeof(Bed))]
	internal static class SleepInAnyBed
	{
		private static bool warned;

		internal static bool Allowed()
		{
			if (!EmberModules.ProductActive(out var _) || EmberModules.StepAside.SteppedAside((EmberModule)0) || Plugin.Settings == null)
			{
				return false;
			}
			return Plugin.Settings.Get<bool>(Sleep.SleepInAnyBed);
		}

		private static bool OthersBed(Bed bed)
		{
			if (!Object.op_Implicit((Object)(object)bed) || !Object.op_Implicit((Object)(object)bed.m_nview) || !bed.m_nview.IsValid() || (Object)(object)Game.instance == (Object)null)
			{
				return false;
			}
			if (bed.GetOwner() != 0L && !bed.IsMine())
			{
				return Allowed();
			}
			return false;
		}

		[HarmonyPatch("Interact")]
		[HarmonyPrefix]
		[HarmonyPriority(200)]
		private static bool Interact(Bed __instance, Humanoid human, bool repeat, ref bool __result, bool __runOriginal)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			if (!__runOriginal || repeat)
			{
				return true;
			}
			try
			{
				Player val = (Player)(object)((human is Player) ? human : null);
				if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !OthersBed(__instance))
				{
					return true;
				}
				__result = false;
				if (!EnvMan.CanSleep())
				{
					((Character)val).Message((MessageType)2, "$msg_cantsleep", 0, (Sprite)null, false);
					return false;
				}
				if (!__instance.CheckEnemies(val) || !__instance.CheckExposure(val) || !__instance.CheckFire(val) || !__instance.CheckWet(val))
				{
					return false;
				}
				((Character)val).AttachStart(__instance.m_spawnPoint, ((Component)__instance).gameObject, true, true, false, "attach_bed", new Vector3(0f, 0.5f, 0f), (Transform)null);
				return false;
			}
			catch (Exception ex)
			{
				if (!warned)
				{
					warned = true;
					Debug.LogWarning((object)("[Ember] Sleep In Any Bed skipped: " + ex.GetType().Name + ": " + ex.Message));
				}
				return true;
			}
		}

		[HarmonyPatch("GetHoverText")]
		[HarmonyPostfix]
		private static void HoverText(Bed __instance, ref string __result)
		{
			try
			{
				if (!string.IsNullOrEmpty(__result) && OthersBed(__instance))
				{
					__result += Localization.instance.Localize("\n[<color=yellow><b>$KEY_Use</b></color>] $piece_bed_sleep");
				}
			}
			catch (Exception)
			{
			}
		}
	}
	public sealed class EmberConfig
	{
		public const string LockedValuesKey = "Locked Player Values";

		private const string Product = "com.meadworks.ember";

		private const float RefreshSeconds = 0.5f;

		private static readonly HashSet<EmberSetting> Shortcuts = BuildShortcuts();

		private readonly ConfigFile file;

		private readonly MeadworksLog log;

		private readonly Dictionary<EmberSetting, ConfigEntryBase> entries = new Dictionary<EmberSetting, ConfigEntryBase>();

		private readonly Dictionary<ConfigEntryBase, EmberSetting> settingsByEntry = new Dictionary<ConfigEntryBase, EmberSetting>();

		private readonly HashSet<EmberSetting> trackedLocks = new HashSet<EmberSetting>();

		private readonly ConfigEntry<string> lockedValues;

		private readonly EmberPresetRuntime presets;

		private string? lockListText;

		private LockList lockList = LockList.Empty;

		private string? valuesText;

		private IReadOnlyDictionary<string, string> values = new Dictionary<string, string>();

		private long syncedGeneration = -1L;

		private readonly EmberSyncReceipt syncReceipt = new EmberSyncReceipt();

		private float nextRefresh;

		public bool SleepAnyTime => Get<bool>(Sleep.SleepAnyTime);

		public bool EndlessTorches => Get<bool>(Torches.EndlessTorches);

		public TestingOffsets Testing => EmberCheats.Testing(CheatLocked(), Get<bool>(Sleep.TestingMode), Get<int>(Sleep.FakeTotalPlayers), Get<int>(Sleep.SimulatePlayersInBed));

		public LockList Locks => lockList;

		public event EventHandler? Changed;

		private static HashSet<EmberSetting> BuildShortcuts()
		{
			_ = EmberSettings.All;
			return new HashSet<EmberSetting>
			{
				(EmberSetting)(object)Doors.ToggleKey,
				(EmberSetting)(object)Torches.TorchSwitchKey
			};
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			this.Changed?.Invoke(this, EventArgs.Empty);
		}

		private EmberConfig(ConfigFile file, MeadworksLog log)
		{
			this.file = file;
			this.log = log;
			bool saveOnConfigSet = file.SaveOnConfigSet;
			file.SaveOnConfigSet = false;
			try
			{
				foreach (EmberSetting item in EmberSettings.All)
				{
					BindSetting(item);
				}
				lockedValues = MeadworksConfig.BindHidden<string>(file, "Server", "Locked Player Values", "", "Managed by Ember: the server's values for Locked Player Settings. Do not edit.", (ConfigScope)1, (AcceptableValueBase)null);
				MoveDoorDistanceDefaults();
				presets = new EmberPresetRuntime(file, this, log);
			}
			finally
			{
				file.SaveOnConfigSet = saveOnConfigSet;
			}
			file.Save();
			file.SettingChanged += OnSettingChanged;
			SynchronizationManager.OnConfigurationSynchronized += OnSynchronized;
		}

		public static EmberConfig Bind(ConfigFile file, MeadworksLog log)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (file == null)
			{
				throw new ArgumentNullException("file");
			}
			if (log == null)
			{
				throw new ArgumentNullException("log");
			}
			EmberConfig emberConfig = new EmberConfig(file, log);
			foreach (KeyValuePair<EmberSetting, ConfigEntryBase> item in emberConfig.entries.Where<KeyValuePair<EmberSetting, ConfigEntryBase>>((KeyValuePair<EmberSetting, ConfigEntryBase> p) => (int)p.Key.Scope > 0))
			{
				MeadworksBlockedOptions.Track("com.meadworks.ember", item.Value, Map(item.Key.Scope), true);
			}
			emberConfig.Refresh(force: true);
			log.Verbose("Configuration bound: " + Count((SettingScope)0) + " Local, " + Count((SettingScope)1) + " ServerAuthoritative, " + Count((SettingScope)2) + " Cheat.");
			return emberConfig;
		}

		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 ConfigEntryBase Entry(EmberSetting setting)
		{
			if (!entries.TryGetValue(setting ?? throw new ArgumentNullException("setting"), out ConfigEntryBase value))
			{
				throw new ArgumentException("Not an Ember setting.", "setting");
			}
			return value;
		}

		public T Get<T>(EmberSetting<T> setting)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if (Shortcuts.Contains((EmberSetting)(object)setting))
			{
				throw new ArgumentException(((EmberSetting)setting).Id + " is a keyboard shortcut; use Shortcut.", "setting");
			}
			ConfigEntry<T> val = (ConfigEntry<T>)(object)Entry((EmberSetting)(object)setting);
			SettingScope scope = ((EmberSetting)setting).Scope;
			if ((int)scope != 1)
			{
				if ((int)scope == 2)
				{
					return EmberCheats.Effective<T>(setting, CheatLocked(), val.Value);
				}
				PlayerLock val2 = Lock((EmberSetting)(object)setting);
				if (((PlayerLock)(ref val2)).Locked && TryConvert<T>(((PlayerLock)(ref val2)).ServerText, out var value))
				{
					return setting.Normalize(value);
				}
				return setting.Normalize(val.Value);
			}
			return setting.Normalize(MeadworksConfig.Effective<T>(val, "com.meadworks.ember", (ConfigScope)1, setting.Default, true));
		}

		public KeyboardShortcut Shortcut(EmberSetting<string> setting)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (!Shortcuts.Contains((EmberSetting)(object)setting))
			{
				throw new ArgumentException(((EmberSetting)setting).Id + " is not a keyboard shortcut.", "setting");
			}
			ConfigEntry<KeyboardShortcut> val = (ConfigEntry<KeyboardShortcut>)(object)Entry((EmberSetting)(object)setting);
			PlayerLock val2 = Lock((EmberSetting)(object)setting);
			if (((PlayerLock)(ref val2)).Locked)
			{
				try
				{
					return KeyboardShortcut.Deserialize(((PlayerLock)(ref val2)).ServerText);
				}
				catch (Exception)
				{
				}
			}
			return val.Value;
		}

		public (AutomationMechanic Mechanic, float Open, float Close, bool CheckForKey) Door(DoorKind kind)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			DoorKindSettings val = EmberSettings.Door(kind);
			var (item, item2) = DoorDistances.Normalize(Get<float>(val.OpenDistance), Get<float>(val.CloseDistance));
			return (Mechanic: Get<AutomationMechanic>(val.Mechanic), Open: item, Close: item2, CheckForKey: val.CheckForKey != null && Get<bool>(val.CheckForKey));
		}

		public bool IsLocked(EmberSetting setting)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (setting != null && setting.Lockable)
			{
				PlayerLock val = Lock(setting);
				return ((PlayerLock)(ref val)).Locked;
			}
			return false;
		}

		public bool TryUseCheat(EmberSetting 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)cheat.Scope != 2)
			{
				throw new ArgumentException("Expected an Ember cheat.", "cheat");
			}
			return MeadworksBlockedOptions.TryUse("com.meadworks.ember", cheat.Id, (ConfigScope)2, label, true);
		}

		public bool TryChange(EmberSetting setting, string? label = null)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (setting == null)
			{
				throw new ArgumentNullException("setting");
			}
			if ((int)setting.Scope != 0)
			{
				return MeadworksBlockedOptions.TryEdit("com.meadworks.ember", setting.Id, Map(setting.Scope), label, true);
			}
			if (!IsLocked(setting))
			{
				return true;
			}
			BlockedOption obj = BlockedOption.From("com.meadworks.ember", setting.Id, MeadworksAuthority.Decide("com.meadworks.ember", (ConfigScope)1, true));
			MeadworksBlockedOptions.Notify(obj, label);
			return obj.Allowed;
		}

		public void Tick(float now)
		{
			presets.Tick();
			if (!(now < nextRefresh))
			{
				nextRefresh = now + 0.5f;
				Refresh(force: false);
			}
		}

		public void Shutdown()
		{
			presets.Dispose();
			file.SettingChanged -= OnSettingChanged;
			this.Changed = null;
			SynchronizationManager.OnConfigurationSynchronized -= OnSynchronized;
			foreach (EmberSetting trackedLock in trackedLocks)
			{
				MeadworksBlockedOptions.Untrack(entries[trackedLock]);
			}
			trackedLocks.Clear();
			foreach (ConfigEntryBase item in entries.Values.Where((ConfigEntryBase e) => !settingsByEntry[e].Lockable))
			{
				MeadworksBlockedOptions.Untrack(item);
			}
		}

		private void Refresh(bool force)
		{
			try
			{
				PublishLockedValues();
				ReadLocks(force);
				foreach (EmberSetting item in EmberSettings.All.Where((EmberSetting s) => s.Lockable))
				{
					bool flag = IsLocked(item);
					if (flag && trackedLocks.Add(item))
					{
						MeadworksBlockedOptions.Track("com.meadworks.ember", entries[item], (ConfigScope)1, true);
					}
					else if (!flag && trackedLocks.Remove(item))
					{
						MeadworksBlockedOptions.Untrack(entries[item]);
					}
				}
			}
			catch (Exception ex)
			{
				log.Warning("Configuration refresh failed: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private void PublishLockedValues()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if (Plugin.IsReady && (int)Plugin.Session.Role != 3)
			{
				string text = LockedValues.Compose(LockList.Parse(((ConfigEntry<string>)(object)entries[(EmberSetting)(object)Server.LockedPlayerSettings]).Value), (Func<string, string>)delegate(string id)
				{
					EmberSetting val2 = EmberSettings.Find(id);
					ConfigEntryBase value;
					return (val2 == null || !entries.TryGetValue(val2, out value)) ? null : value.GetSerializedValue();
				});
				if (!(text == lockedValues.Value))
				{
					ConfigAmendResult val = MeadworksConfig.Amend<string>(lockedValues, "com.meadworks.ember", (ConfigScope)1, text, (string)null, true);
					log.Verbose("Locked player values: " + (object)val);
				}
			}
		}

		private void ReadLocks(bool force)
		{
			string text = Get<string>(Server.LockedPlayerSettings);
			if (force || text != lockListText)
			{
				lockListText = text;
				lockList = LockList.Parse(text);
				if (lockList.Unknown.Count