Decompiled source of Rested v0.1.2

plugins/MuckRested.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckRested")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim-style Rested buff: rest sheltered near a furnace or cauldron for stamina and health regen.")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("Rested")]
[assembly: AssemblyTitle("MuckRested")]
[assembly: InternalsVisibleTo("MuckRested.Tests")]
[assembly: AssemblyVersion("0.1.2.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckRested
{
	public readonly struct StationSample
	{
		public StationKind Kind { get; }

		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public int OwnerId { get; }

		public StationSample(StationKind kind, float x, float y, float z, int ownerId)
		{
			Kind = kind;
			X = x;
			Y = y;
			Z = z;
			OwnerId = ownerId;
		}
	}
	public readonly struct Comfort
	{
		public bool HasFire { get; }

		public int DistinctKinds { get; }

		public static Comfort None => new Comfort(hasFire: false, 0);

		public Comfort(bool hasFire, int distinctKinds)
		{
			HasFire = hasFire;
			DistinctKinds = distinctKinds;
		}
	}
	public static class ComfortScan
	{
		public static Comfort Evaluate(IReadOnlyList<StationSample> samples, float px, float py, float pz, float radius, bool ownBuildsOnly, int myId)
		{
			if (float.IsNaN(radius) || radius < 0f)
			{
				radius = 0f;
			}
			float num = radius * radius;
			int num2 = 0;
			bool hasFire = false;
			for (int i = 0; i < samples.Count; i++)
			{
				StationSample stationSample = samples[i];
				if (stationSample.Kind == StationKind.None || (ownBuildsOnly && stationSample.OwnerId != myId))
				{
					continue;
				}
				float num3 = stationSample.X - px;
				float num4 = stationSample.Y - py;
				float num5 = stationSample.Z - pz;
				if (!(num3 * num3 + num4 * num4 + num5 * num5 > num))
				{
					num2 |= StationKinds.Bit(stationSample.Kind);
					if (StationKinds.IsFire(stationSample.Kind))
					{
						hasFire = true;
					}
				}
			}
			return new Comfort(hasFire, CountBits(num2));
		}

		private static int CountBits(int mask)
		{
			int num = 0;
			for (uint num2 = (uint)mask; num2 != 0; num2 >>= 1)
			{
				num += (int)(num2 & 1);
			}
			return num;
		}
	}
	public static class HudGate
	{
		public static bool Hidden(bool notPlaying, bool otherUi, bool inventoryOpen, bool dead)
		{
			return notPlaying || otherUi || inventoryOpen || dead;
		}
	}
	public readonly struct HudBox
	{
		public readonly float PlateX;

		public readonly float PlateY;

		public readonly float PlateWidth;

		public readonly float PlateHeight;

		public readonly float TextX;

		public readonly float TextY;

		public readonly float TextWidth;

		public readonly float TextHeight;

		public readonly float Shadow;

		public HudBox(float plateX, float plateY, float plateWidth, float plateHeight, float pad, float textWidth, float textHeight, float shadow)
		{
			PlateX = plateX;
			PlateY = plateY;
			PlateWidth = plateWidth;
			PlateHeight = plateHeight;
			TextX = plateX + pad;
			TextY = plateY + pad;
			TextWidth = textWidth;
			TextHeight = textHeight;
			Shadow = shadow;
		}
	}
	public static class HudLayout
	{
		public const int BaseFontSize = 20;

		private const float Margin = 12f;

		private const float Pad = 6f;

		public static float Scale(int screenHeight)
		{
			return Math.Max(1f, (float)screenHeight / 1080f);
		}

		public static int FontSize(int screenHeight)
		{
			return (int)Math.Round(20f * Scale(screenHeight), MidpointRounding.AwayFromZero);
		}

		public static HudBox For(int screenWidth, int screenHeight, float textWidth, float textHeight)
		{
			float num = Scale(screenHeight);
			float num2 = (float)Math.Round(6f * num);
			float val = (float)Math.Round(12f * num);
			float num3 = (float)Math.Ceiling(textWidth) + 2f * num2;
			float num4 = (float)Math.Ceiling(textHeight) + 2f * num2;
			return new HudBox(Math.Max(0f, Math.Min(val, (float)screenWidth - num3)), Math.Max(0f, Math.Min(val, (float)screenHeight - num4)), shadow: Math.Max(1f, (float)Math.Round(num)), plateWidth: num3, plateHeight: num4, pad: num2, textWidth: (float)Math.Ceiling(textWidth), textHeight: (float)Math.Ceiling(textHeight));
		}
	}
	public static class RegenMath
	{
		public static float BoostedStamina(float before, float after, float maxStamina, float multiplier)
		{
			if (float.IsNaN(before) || float.IsNaN(after) || float.IsNaN(multiplier) || !(multiplier > 1f) || !(after > before))
			{
				return after;
			}
			float val = after + (after - before) * (multiplier - 1f);
			float val2 = Math.Max(after, maxStamina);
			return Math.Min(val, val2);
		}

		public static float RegenHp(float hp, int maxHp, float hunger, float perSecond, float dt)
		{
			if (!(hp > 0f) || hp >= (float)maxHp || !(hunger > 0f) || !(perSecond > 0f) || !(dt > 0f))
			{
				return hp;
			}
			return Math.Min(hp + perSecond * dt, maxHp);
		}
	}
	public static class RestedText
	{
		public const string Expired = "You are no longer rested.";

		public const string RoofHint = "Near a fire: build a roof to rest";

		public const int ChatMaxLength = 120;

		public static string Clock(float seconds)
		{
			if (float.IsNaN(seconds) || seconds < 0f)
			{
				seconds = 0f;
			}
			int num = (int)Math.Ceiling(seconds);
			int num2 = num / 60;
			int num3 = num % 60;
			return string.Format(CultureInfo.InvariantCulture, "{0}:{1:00}", num2, num3);
		}

		public static string Hud(RestState state, RestedTuning tuning, bool staminaPatched)
		{
			if (state.IsRested)
			{
				string text = "Rested " + Clock(state.RemainingSeconds);
				if (!staminaPatched)
				{
					text += " (no stamina bonus)";
				}
				return text;
			}
			if (state.NeedsRoof)
			{
				return "Near a fire: build a roof to rest";
			}
			if (!state.CanRest)
			{
				return "";
			}
			int num = (int)Math.Floor(state.RestingSeconds);
			int num2 = (int)Math.Ceiling(tuning.RestSeconds);
			return string.Format(CultureInfo.InvariantCulture, "Resting {0}/{1}", num, num2);
		}

		public static float HudAlpha(string text)
		{
			if (!(text == "Near a fire: build a roof to rest"))
			{
				return 1f;
			}
			return 0.7f;
		}

		public static string BecameRested(float seconds, RestedTuning tuning)
		{
			return string.Format(CultureInfo.InvariantCulture, "You feel rested ({0}). Stamina regen x{1:0.##}, +{2:0.##} HP/s.", Clock(seconds), tuning.StaminaRegenMultiplier, tuning.HealthRegenPerSecond);
		}
	}
	public sealed class RestedTuning
	{
		public float RestSeconds { get; }

		public float BaseDurationSeconds { get; }

		public float DurationPerStationSeconds { get; }

		public float MaxDurationSeconds { get; }

		public float StaminaRegenMultiplier { get; }

		public float HealthRegenPerSecond { get; }

		public static RestedTuning Defaults { get; } = new RestedTuning(20f, 120f, 60f, 360f, 2f, 0.5f);

		public RestedTuning(float restSeconds, float baseDurationSeconds, float durationPerStationSeconds, float maxDurationSeconds, float staminaRegenMultiplier, float healthRegenPerSecond)
		{
			RestSeconds = Clamp(restSeconds, 0f, 600f, 20f);
			BaseDurationSeconds = Clamp(baseDurationSeconds, 0f, 3600f, 120f);
			DurationPerStationSeconds = Clamp(durationPerStationSeconds, 0f, 3600f, 60f);
			float val = Clamp(maxDurationSeconds, 0f, 7200f, 360f);
			MaxDurationSeconds = Math.Max(val, BaseDurationSeconds);
			StaminaRegenMultiplier = Clamp(staminaRegenMultiplier, 1f, 10f, 1f);
			HealthRegenPerSecond = Clamp(healthRegenPerSecond, 0f, 20f, 0f);
		}

		public float DurationFor(int stationKinds)
		{
			int num = Math.Max(0, stationKinds);
			return Math.Min(MaxDurationSeconds, BaseDurationSeconds + DurationPerStationSeconds * (float)num);
		}

		private static float Clamp(float value, float lo, float hi, float nan)
		{
			if (float.IsNaN(value))
			{
				return nan;
			}
			return Math.Min(hi, Math.Max(lo, value));
		}
	}
	public readonly struct RestInput
	{
		public bool Alive { get; }

		public bool Sheltered { get; }

		public Comfort Comfort { get; }

		public RestInput(bool alive, bool sheltered, Comfort comfort)
		{
			Alive = alive;
			Sheltered = sheltered;
			Comfort = comfort;
		}
	}
	public enum RestEvent
	{
		None,
		BecameRested,
		Expired
	}
	public sealed class RestState
	{
		public float RestingSeconds { get; private set; }

		public float RemainingSeconds { get; private set; }

		public bool CanRest { get; private set; }

		public bool NeedsRoof { get; private set; }

		public bool IsRested => RemainingSeconds > 0f;

		public RestEvent Tick(float dt, RestInput input, RestedTuning tuning)
		{
			if (float.IsNaN(dt) || dt < 0f)
			{
				dt = 0f;
			}
			if (!input.Alive)
			{
				bool isRested = IsRested;
				Reset();
				if (!isRested)
				{
					return RestEvent.None;
				}
				return RestEvent.Expired;
			}
			bool isRested2 = IsRested;
			RemainingSeconds = Math.Max(0f, RemainingSeconds - dt);
			CanRest = input.Sheltered && input.Comfort.HasFire;
			NeedsRoof = !input.Sheltered && input.Comfort.HasFire;
			if (CanRest)
			{
				RestingSeconds += dt;
				if (RestingSeconds >= tuning.RestSeconds)
				{
					RemainingSeconds = Math.Max(RemainingSeconds, tuning.DurationFor(input.Comfort.DistinctKinds));
				}
			}
			else
			{
				RestingSeconds = 0f;
			}
			if (!isRested2 && IsRested)
			{
				return RestEvent.BecameRested;
			}
			if (isRested2 && !IsRested)
			{
				return RestEvent.Expired;
			}
			return RestEvent.None;
		}

		public void Reset()
		{
			RestingSeconds = 0f;
			RemainingSeconds = 0f;
			CanRest = false;
			NeedsRoof = false;
		}

		public void Grant(float seconds)
		{
			RemainingSeconds = Math.Max(RemainingSeconds, seconds);
		}
	}
	public enum StationKind
	{
		None,
		Workbench,
		Anvil,
		Cauldron,
		Fletch,
		Furnace
	}
	public static class StationKinds
	{
		public static bool IsFire(StationKind kind)
		{
			if (kind != StationKind.Furnace)
			{
				return kind == StationKind.Cauldron;
			}
			return true;
		}

		public static int Bit(StationKind kind)
		{
			if (kind == StationKind.None)
			{
				return 0;
			}
			return 1 << (int)kind;
		}
	}
	internal sealed class RestedConfig
	{
		internal static class Defaults
		{
			internal const float RestSeconds = 20f;

			internal const float BaseDurationSeconds = 120f;

			internal const float DurationPerStationSeconds = 60f;

			internal const float MaxDurationSeconds = 360f;

			internal const float StaminaRegenMultiplier = 2f;

			internal const float HealthRegenPerSecond = 0.5f;

			internal const float ComfortRadius = 6f;

			internal const float RoofCheckHeight = 12f;

			internal const float ScanInterval = 0.5f;
		}

		public readonly ConfigEntry<float> RestSeconds;

		public readonly ConfigEntry<float> BaseDurationSeconds;

		public readonly ConfigEntry<float> DurationPerStationSeconds;

		public readonly ConfigEntry<float> MaxDurationSeconds;

		public readonly ConfigEntry<float> StaminaRegenMultiplier;

		public readonly ConfigEntry<float> HealthRegenPerSecond;

		public readonly ConfigEntry<float> ComfortRadius;

		public readonly ConfigEntry<bool> RequireRoof;

		public readonly ConfigEntry<float> RoofCheckHeight;

		public readonly ConfigEntry<bool> OwnBuildsOnly;

		public readonly ConfigEntry<bool> DisableInVersus;

		public readonly ConfigEntry<float> ScanInterval;

		public readonly ConfigEntry<bool> ShowHud;

		public readonly ConfigEntry<bool> ChatMessages;

		public readonly ConfigEntry<KeyboardShortcut> GiveRestedKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		public RestedConfig(ConfigFile cfg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Expected O, but got Unknown
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			RestSeconds = cfg.Bind<float>("Rested", "RestSeconds", 20f, new ConfigDescription("Seconds you must stay sheltered within ComfortRadius of a furnace or cauldron before the buff starts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			BaseDurationSeconds = cfg.Bind<float>("Rested", "BaseDurationSeconds", 120f, new ConfigDescription("Base seconds. The fire and each other distinct station kind within ComfortRadius add DurationPerStationSeconds. A lone furnace gives Base + PerStation. Valheim's base is 420. Muck days are shorter, so the default is 120.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), Array.Empty<object>()));
			DurationPerStationSeconds = cfg.Bind<float>("Rested", "DurationPerStationSeconds", 60f, new ConfigDescription("Extra seconds added for each distinct station kind within ComfortRadius. Kinds are workbench, anvil, cauldron, fletching table, and furnace.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), Array.Empty<object>()));
			MaxDurationSeconds = cfg.Bind<float>("Rested", "MaxDurationSeconds", 360f, new ConfigDescription("Longest Rested buff in seconds. Raised to BaseDurationSeconds when that value is higher.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 7200f), Array.Empty<object>()));
			StaminaRegenMultiplier = cfg.Bind<float>("Rested", "StaminaRegenMultiplier", 2f, new ConfigDescription("Multiplier on the stamina vanilla regenerated that frame while Rested. 2 is double regen. 1 disables the bonus.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			HealthRegenPerSecond = cfg.Bind<float>("Rested", "HealthRegenPerSecond", 0.5f, new ConfigDescription("Flat health restored per second while Rested. Skipped at 0 health, at full health, and while hunger is empty.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			ComfortRadius = cfg.Bind<float>("Rested", "ComfortRadius", 6f, new ConfigDescription("Meters around the player in which a furnace, cauldron, or other station counts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			RequireRoof = cfg.Bind<bool>("Rested", "RequireRoof", true, "When true, a built piece must sit overhead within RoofCheckHeight before resting can start.");
			RoofCheckHeight = cfg.Bind<float>("Rested", "RoofCheckHeight", 12f, new ConfigDescription("Meters of the upward roof check, measured from the player camera.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			OwnBuildsOnly = cfg.Bind<bool>("Rested", "OwnBuildsOnly", false, "When true, only pieces you placed count. When false, any player's builds count.");
			DisableInVersus = cfg.Bind<bool>("Rested", "DisableInVersus", true, "When true, the rest timer and the health bonus do not run in a Versus match.");
			ScanInterval = cfg.Bind<float>("Rested", "ScanInterval", 0.5f, new ConfigDescription("Seconds between station and roof scans. The rest timer still advances every frame.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			ShowHud = cfg.Bind<bool>("Display", "ShowHud", true, "Show the Resting and Rested timer at the top left.");
			ChatMessages = cfg.Bind<bool>("Display", "ChatMessages", true, "Write a local chat line when Rested starts and when it ends.");
			GiveRestedKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveRestedKey", KeyboardShortcut.Empty, "Local cheat. Press in-game to grant yourself the max Rested duration. Empty disables the key.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each Rested state change and each scan's build count and station kinds.");
		}

		public RestedTuning Tuning()
		{
			return new RestedTuning(RestSeconds.Value, BaseDurationSeconds.Value, DurationPerStationSeconds.Value, MaxDurationSeconds.Value, StaminaRegenMultiplier.Value, HealthRegenPerSecond.Value);
		}
	}
	[BepInPlugin("evan.muck.rested", "Rested", "0.1.2")]
	public class RestedPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.rested";

		public const string Name = "Rested";

		public const string Version = "0.1.2";

		internal static ManualLogSource Log;

		internal static RestedConfig Cfg;

		internal static bool StaminaPatched;

		private void Awake()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new RestedConfig(((BaseUnityPlugin)this).Config);
			StaminaPatched = TryPatch(new Harmony("evan.muck.rested"), typeof(StaminaPatch));
			if (!StaminaPatched)
			{
				Log.LogError((object)"Rested: stamina patch failed, stamina bonus disabled. HP regen still works.");
			}
			RestedRunner.Ensure();
			RestedTuning restedTuning = (RestedSession.Tuning = Cfg.Tuning());
			Log.LogInfo((object)("Rested 0.1.2 loaded. Local-only: other players do not need this mod. " + $"Rest {restedTuning.RestSeconds}s, duration {restedTuning.BaseDurationSeconds}s + {restedTuning.DurationPerStationSeconds}s per station, " + $"max {restedTuning.MaxDurationSeconds}s, stamina x{restedTuning.StaminaRegenMultiplier}, +{restedTuning.HealthRegenPerSecond} HP/s."));
		}

		private static bool TryPatch(Harmony harmony, Type patchClass)
		{
			try
			{
				harmony.CreateClassProcessor(patchClass).Patch();
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} failed to patch {1}: {2}", "Rested", patchClass.Name, arg));
				return false;
			}
		}
	}
	internal sealed class RestedRunner : MonoBehaviour
	{
		private static RestedRunner _instance;

		private float _scanTimer;

		private bool _sheltered;

		private Comfort _comfort;

		private bool _errored;

		private GUIStyle _label;

		private string _measured;

		private Vector2 _textSize;

		private bool _loggedGate;

		public static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("MuckRested")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<RestedRunner>();
			}
		}

		private void OnEnable()
		{
			SceneManager.activeSceneChanged += OnSceneChanged;
		}

		private void OnDisable()
		{
			SceneManager.activeSceneChanged -= OnSceneChanged;
		}

		private void OnSceneChanged(Scene previous, Scene next)
		{
			RestedSession.State.Reset();
			StationProbe.Clear();
			_comfort = Comfort.None;
			_sheltered = false;
		}

		private void Update()
		{
			if (_errored)
			{
				return;
			}
			try
			{
				RunFrame();
			}
			catch (Exception arg)
			{
				_errored = true;
				RestedPlugin.Log.LogError((object)$"Rested update failed: {arg}");
			}
		}

		private void RunFrame()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			if (RestedPlugin.Cfg.DisableInVersus.Value && IsVersus())
			{
				RestedSession.State.Reset();
				return;
			}
			KeyboardShortcut value = RestedPlugin.Cfg.GiveRestedKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				RestedSession.State.Grant(RestedSession.Tuning.MaxDurationSeconds);
			}
			PlayerStatus instance = PlayerStatus.Instance;
			PlayerMovement instance2 = PlayerMovement.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)ResourceManager.Instance == (Object)null || (Object)(object)LocalClient.instance == (Object)null)
			{
				RestedSession.State.Reset();
				return;
			}
			float deltaTime = Time.deltaTime;
			_scanTimer -= deltaTime;
			if (_scanTimer <= 0f)
			{
				_scanTimer = RestedPlugin.Cfg.ScanInterval.Value;
				RestedSession.Tuning = RestedPlugin.Cfg.Tuning();
				Vector3 position = ((Component)instance2).transform.position;
				List<StationSample> samples = StationProbe.Collect();
				_comfort = ComfortScan.Evaluate(samples, position.x, position.y, position.z, RestedPlugin.Cfg.ComfortRadius.Value, RestedPlugin.Cfg.OwnBuildsOnly.Value, LocalClient.instance.myId);
				_sheltered = !RestedPlugin.Cfg.RequireRoof.Value || ShelterProbe.IsSheltered(instance2.playerCam.position, RestedPlugin.Cfg.RoofCheckHeight.Value);
				if (RestedPlugin.Cfg.VerboseLogging.Value)
				{
					int num = 0;
					if (ResourceManager.Instance.builds != null)
					{
						num = ResourceManager.Instance.builds.Count;
					}
					RestedPlugin.Log.LogInfo((object)$"Rested scan: {num} builds, kinds {Describe(samples)}, sheltered {_sheltered}.");
				}
			}
			bool alive = !instance.IsPlayerDead() && instance.hp > 0f;
			RestEvent restEvent = RestedSession.State.Tick(deltaTime, new RestInput(alive, _sheltered, _comfort), RestedSession.Tuning);
			if (RestedSession.State.IsRested)
			{
				instance.hp = RegenMath.RegenHp(instance.hp, instance.maxHp, instance.hunger, RestedSession.Tuning.HealthRegenPerSecond, deltaTime);
			}
			if (RestedPlugin.Cfg.ChatMessages.Value && (Object)(object)ChatBox.Instance != (Object)null)
			{
				switch (restEvent)
				{
				case RestEvent.BecameRested:
					ChatBox.Instance.AppendMessage(-1, RestedText.BecameRested(RestedSession.State.RemainingSeconds, RestedSession.Tuning), "");
					break;
				case RestEvent.Expired:
					ChatBox.Instance.AppendMessage(-1, "You are no longer rested.", "");
					break;
				}
			}
			if (RestedPlugin.Cfg.VerboseLogging.Value && restEvent != RestEvent.None)
			{
				RestedPlugin.Log.LogInfo((object)$"Rested event {restEvent}, remaining {RestedSession.State.RemainingSeconds}.");
			}
		}

		private static bool IsVersus()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			GameSettings gameSettings = GameManager.gameSettings;
			if (gameSettings == null)
			{
				return false;
			}
			return (int)gameSettings.gameMode == 1;
		}

		private void OnGUI()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_008b: 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_0099: 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_00a8: Expected O, but got Unknown
			//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_00b7: Expected O, but got Unknown
			//IL_00bc: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type != 7 || !RestedPlugin.Cfg.ShowHud.Value)
			{
				return;
			}
			string text = RestedText.Hud(RestedSession.State, RestedSession.Tuning, RestedPlugin.StaminaPatched);
			if (text.Length != 0 && !Covered())
			{
				int num = HudLayout.FontSize(Screen.height);
				if (_label == null || _label.fontSize != num)
				{
					_label = new GUIStyle(GUI.skin.label)
					{
						fontSize = num,
						fontStyle = (FontStyle)1,
						alignment = (TextAnchor)3,
						wordWrap = false,
						clipping = (TextClipping)0,
						padding = new RectOffset(0, 0, 0, 0),
						margin = new RectOffset(0, 0, 0, 0)
					};
					_label.normal.textColor = Color.white;
					_measured = null;
				}
				if (_measured != text)
				{
					_measured = text;
					_textSize = _label.CalcSize(new GUIContent(text));
				}
				DrawHud(HudLayout.For(Screen.width, Screen.height, _textSize.x, _textSize.y), text, _label, RestedText.HudAlpha(text));
			}
		}

		private bool Covered()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			try
			{
				OtherInput instance = OtherInput.Instance;
				PlayerStatus instance2 = PlayerStatus.Instance;
				bool flag = (int)GameManager.state != 1;
				return HudGate.Hidden(flag, !flag && (Object)(object)instance != (Object)null && instance.OtherUiActive(), !flag && (Object)(object)instance != (Object)null && instance.IsAnyMenuOpen(), (Object)(object)instance2 != (Object)null && instance2.IsPlayerDead());
			}
			catch (Exception arg)
			{
				if (!_loggedGate)
				{
					_loggedGate = true;
					RestedPlugin.Log.LogError((object)$"Rested: reading Muck's menu state for the label failed. The label stays visible. {arg}");
				}
				return false;
			}
		}

		private static void DrawHud(HudBox box, string text, GUIStyle style, float alpha)
		{
			//IL_0000: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, 0.45f * alpha);
			GUI.DrawTexture(new Rect(box.PlateX, box.PlateY, box.PlateWidth, box.PlateHeight), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = new Color(0f, 0f, 0f, 0.9f * alpha);
			GUI.Label(new Rect(box.TextX + box.Shadow, box.TextY + box.Shadow, box.TextWidth, box.TextHeight), text, style);
			GUI.color = new Color(1f, 1f, 1f, alpha);
			GUI.Label(new Rect(box.TextX, box.TextY, box.TextWidth, box.TextHeight), text, style);
			GUI.color = color;
		}

		private static string Describe(List<StationSample> samples)
		{
			int num = 0;
			string text = "";
			for (int i = 0; i < samples.Count; i++)
			{
				int num2 = StationKinds.Bit(samples[i].Kind);
				if (num2 != 0 && (num & num2) == 0)
				{
					num |= num2;
					if (text.Length > 0)
					{
						text += ", ";
					}
					text += samples[i].Kind;
				}
			}
			if (text.Length != 0)
			{
				return text;
			}
			return "none";
		}
	}
	internal static class RestedSession
	{
		public static readonly RestState State = new RestState();

		public static RestedTuning Tuning = RestedTuning.Defaults;
	}
	internal static class ShelterProbe
	{
		private static readonly RaycastHit[] Hits = (RaycastHit[])(object)new RaycastHit[16];

		internal static bool IsSheltered(Vector3 head, float height)
		{
			//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)
			int num = Physics.RaycastNonAlloc(head, Vector3.up, Hits, height, -1, (QueryTriggerInteraction)1);
			for (int i = 0; i < num; i++)
			{
				if ((Object)(object)((Component)((RaycastHit)(ref Hits[i])).collider).GetComponentInParent<BuildInfo>() != (Object)null)
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerStatus), "Stamina")]
	internal static class StaminaPatch
	{
		private static bool _logged;

		[HarmonyPriority(800)]
		private static void Prefix(PlayerStatus __instance, out float __state)
		{
			try
			{
				__state = __instance.stamina;
			}
			catch
			{
				__state = float.NaN;
			}
		}

		[HarmonyPriority(0)]
		private static void Postfix(PlayerStatus __instance, float __state)
		{
			try
			{
				if (RestedSession.State.IsRested && !((Object)(object)__instance != (Object)(object)PlayerStatus.Instance) && !float.IsNaN(__state))
				{
					__instance.stamina = RegenMath.BoostedStamina(__state, __instance.stamina, __instance.maxStamina, RestedSession.Tuning.StaminaRegenMultiplier);
				}
			}
			catch (Exception arg)
			{
				if (!_logged)
				{
					_logged = true;
					RestedPlugin.Log.LogError((object)$"Rested stamina bonus failed: {arg}");
				}
			}
		}
	}
	internal static class StationProbe
	{
		private static readonly Dictionary<int, StationKind> KindByKey = new Dictionary<int, StationKind>();

		private static readonly List<StationSample> Samples = new List<StationSample>();

		internal static void Clear()
		{
			KindByKey.Clear();
		}

		internal static List<StationSample> Collect()
		{
			//IL_0080: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			Samples.Clear();
			Dictionary<int, GameObject> builds = ResourceManager.Instance.builds;
			if (builds == null)
			{
				return Samples;
			}
			foreach (KeyValuePair<int, GameObject> item in builds)
			{
				GameObject value = item.Value;
				if ((Object)(object)value == (Object)null)
				{
					continue;
				}
				if (!KindByKey.TryGetValue(item.Key, out var value2))
				{
					value2 = Classify(value);
					KindByKey[item.Key] = value2;
				}
				if (value2 != StationKind.None)
				{
					Vector3 position = value.transform.position;
					BuildInfo component = value.GetComponent<BuildInfo>();
					int ownerId = -1;
					if ((Object)(object)component != (Object)null)
					{
						ownerId = component.ownerId;
					}
					Samples.Add(new StationSample(value2, position.x, position.y, position.z, ownerId));
				}
			}
			return Samples;
		}

		private static StationKind Classify(GameObject go)
		{
			//IL_0034: 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)
			if ((Object)(object)go.GetComponentInChildren<FurnaceSync>(true) != (Object)null)
			{
				return StationKind.Furnace;
			}
			if ((Object)(object)go.GetComponentInChildren<CauldronSync>(true) != (Object)null)
			{
				return StationKind.Cauldron;
			}
			CraftingInteract componentInChildren = go.GetComponentInChildren<CraftingInteract>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				return Map(componentInChildren.state);
			}
			ChestInteract componentInChildren2 = go.GetComponentInChildren<ChestInteract>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				return Map(componentInChildren2.state);
			}
			return StationKind.None;
		}

		internal static StationKind Map(CraftingState state)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected I4, but got Unknown
			return (state - 1) switch
			{
				0 => StationKind.Workbench, 
				1 => StationKind.Anvil, 
				2 => StationKind.Cauldron, 
				3 => StationKind.Fletch, 
				4 => StationKind.Furnace, 
				_ => StationKind.None, 
			};
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckRested";

		public const string PLUGIN_NAME = "Rested";

		public const string PLUGIN_VERSION = "0.1.2";
	}
}