Decompiled source of Norse Sense v0.13.0

NorseSense.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("NorseSense")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.13.0.0")]
[assembly: AssemblyInformationalVersion("0.13.0+f84dd43f9f0873a92b7ecad47b73b6cb596cde0d")]
[assembly: AssemblyProduct("NorseSense")]
[assembly: AssemblyTitle("NorseSense")]
[assembly: AssemblyVersion("0.13.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace NorseSense
{
	internal class AreaTools : MonoBehaviour
	{
		internal enum Kind
		{
			None,
			Plant,
			Terrain
		}

		private const float ValidateInterval = 0.1f;

		private const float ScrollCooldown = 0.08f;

		private const float TileSpacing = 1f;

		private static readonly int[,] PlantShapes = new int[5, 2]
		{
			{ 1, 1 },
			{ 1, 3 },
			{ 1, 5 },
			{ 3, 3 },
			{ 5, 5 }
		};

		private static readonly int[,] TerrainShapes = new int[5, 2]
		{
			{ 1, 1 },
			{ 1, 3 },
			{ 2, 2 },
			{ 3, 3 },
			{ 4, 4 }
		};

		private static readonly FieldRef<Player, GameObject> PlacementGhost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

		private static readonly FieldRef<Player, PieceTable> BuildPieces = AccessTools.FieldRefAccess<Player, PieceTable>("m_buildPieces");

		private static readonly FieldRef<Player, int> BuildRemoveDebt = AccessTools.FieldRefAccess<Player, int>("m_buildRemoveDebt");

		private static readonly Func<Player, float> GetBuildStamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Player, ItemData, float> GetPlaceDurability = AccessTools.MethodDelegate<Func<Player, ItemData, float>>(AccessTools.Method(typeof(Player), "GetPlaceDurability", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Color GoodColor = new Color(0.45f, 1f, 0.45f, 0.9f);

		private static readonly Color BadColor = new Color(1f, 0.3f, 0.2f, 0.9f);

		private static readonly Color AreaColor = new Color(1f, 0.85f, 0.4f, 0.9f);

		private static int _plantShape;

		private static int _terrainShape;

		internal static Kind Current;

		internal static int Columns = 1;

		internal static int Rows = 1;

		internal static int Valid;

		private static readonly List<Vector3> Cells = new List<Vector3>(25);

		private static readonly List<bool> CellOk = new List<bool>(25);

		private static Quaternion _gridRotation;

		private static int _centerIndex;

		private static float _spacing;

		private static float _tileRadius;

		private static float _growRadius;

		private static float _stableYaw = float.NaN;

		private static float _scrollReadyAt;

		private static bool _raiseGrid;

		private static Piece _pending;

		private static readonly List<Vector3> PendingCells = new List<Vector3>(25);

		private static Quaternion _pendingRotation;

		private static Vector3 _pendingCenter;

		private readonly List<LineRenderer> _rings = new List<LineRenderer>();

		private readonly List<Vector3> _ringDrawnAt = new List<Vector3>();

		private LineRenderer _outline;

		private float _timer;

		internal static bool ModifierHeld()
		{
			//IL_0011: 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)
			if (ServerLimits.Allowed(Plugin.AreaToolsEnabled) && (int)Plugin.AreaModifier.Value != 0)
			{
				return UnityInput.Current.GetKey(Plugin.AreaModifier.Value);
			}
			return false;
		}

		private void Awake()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			_outline = SpawnView.CreateLine(((Component)this).transform, "AreaOutline", AreaColor, 0.06f);
			((Component)_outline).gameObject.SetActive(false);
		}

		private void Update()
		{
			HandleScroll();
			_timer -= Time.deltaTime;
			bool flag = _timer <= 0f;
			if (flag)
			{
				_timer = 0.1f;
			}
			Refresh(Player.m_localPlayer, flag);
			DrawPreview();
		}

		private static void HandleScroll()
		{
			if (ScrollCaptured() && !Plugin.IsTyping() && !Hud.IsPieceSelectionVisible() && !(Time.time < _scrollReadyAt))
			{
				float mouseScrollWheel = ZInput.GetMouseScrollWheel();
				if (!(Mathf.Abs(mouseScrollWheel) < 0.001f))
				{
					_scrollReadyAt = Time.time + 0.08f;
					StepSize((mouseScrollWheel > 0f) ? 1 : (-1));
				}
			}
		}

		internal static bool ScrollCaptured()
		{
			if (Current != Kind.None)
			{
				return ModifierHeld();
			}
			return false;
		}

		private void OnDisable()
		{
			Current = Kind.None;
			Cells.Clear();
			DrawPreview();
		}

		private static Kind KindOf(Player player, out Piece piece, out GameObject ghost)
		{
			piece = null;
			ghost = (((Object)(object)player != (Object)null && ((Character)player).InPlaceMode()) ? PlacementGhost.Invoke(player) : null);
			if (!ServerLimits.Allowed(Plugin.AreaToolsEnabled) || (Object)(object)ghost == (Object)null || !ghost.activeInHierarchy)
			{
				return Kind.None;
			}
			piece = player.GetSelectedPiece();
			if ((Object)(object)piece == (Object)null)
			{
				return Kind.None;
			}
			if ((Object)(object)((Component)piece).GetComponent<Plant>() != (Object)null)
			{
				if (Compat.AreaPlantingDisabledBy != null)
				{
					return Kind.None;
				}
				return Kind.Plant;
			}
			if ((Object)(object)((Component)piece).GetComponent<TerrainOp>() != (Object)null && ServerLimits.Allowed(Plugin.AreaTerrain) && Compat.AreaTerrainDisabledBy == null)
			{
				return Kind.Terrain;
			}
			return Kind.None;
		}

		private static void Refresh(Player player, bool validate)
		{
			//IL_00c7: 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_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			int count = Cells.Count;
			Cells.Clear();
			if (validate || count == 0)
			{
				CellOk.Clear();
			}
			Valid = 0;
			Current = KindOf(player, out var piece, out var ghost);
			if (Current == Kind.None)
			{
				return;
			}
			int[,] obj = ((Current == Kind.Plant) ? PlantShapes : TerrainShapes);
			int num = ((Current == Kind.Plant) ? MaxShape(PlantShapes, ServerLimits.Max(Plugin.MaxPlantGrid)) : MaxShape(TerrainShapes, ServerLimits.Max(Plugin.MaxHoeSize)));
			int num2 = Mathf.Clamp((Current == Kind.Plant) ? _plantShape : _terrainShape, 0, num);
			Columns = obj[num2, 0];
			Rows = obj[num2, 1];
			Vector3 position = ghost.transform.position;
			if (Current == Kind.Plant)
			{
				Plant component = ghost.GetComponent<Plant>();
				_growRadius = component.m_growRadius;
				_spacing = component.m_growRadius * 2f + 0.05f;
				_gridRotation = (piece.m_randomInitBuildRotation ? StableViewYaw() : Quaternion.Euler(0f, ghost.transform.eulerAngles.y, 0f));
			}
			else
			{
				_tileRadius = CoverRadius(((Component)piece).GetComponent<TerrainOp>().m_settings);
				_spacing = _tileRadius * 1f;
				_gridRotation = ((Columns == 1 && Rows > 1) ? StableViewYaw() : Quaternion.Euler(0f, ghost.transform.eulerAngles.y, 0f));
			}
			bool flag = Current == Kind.Terrain && ((Component)piece).GetComponent<TerrainOp>().m_settings.m_level;
			_raiseGrid = Current == Kind.Terrain && ((Component)piece).GetComponent<TerrainOp>().m_settings.m_raise;
			Vector3 val = default(Vector3);
			float y = default(float);
			for (int i = 0; i < Rows; i++)
			{
				for (int j = 0; j < Columns; j++)
				{
					((Vector3)(ref val))..ctor((float)(j - (Columns - 1) / 2) * _spacing, 0f, (float)(i - (Rows - 1) / 2) * _spacing);
					if (val == Vector3.zero)
					{
						_centerIndex = Cells.Count;
					}
					Vector3 val2 = position + _gridRotation * val;
					if (!flag && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val2, ref y))
					{
						val2.y = y;
					}
					else if (flag)
					{
						val2.y = position.y;
					}
					int count2 = Cells.Count;
					Cells.Add(val2);
					if (count2 >= CellOk.Count)
					{
						CellOk.Add(val == Vector3.zero || CellAllowed(Current, ghost, val2));
					}
					if (CellOk[count2])
					{
						Valid++;
					}
				}
			}
		}

		private static float CoverRadius(Settings s)
		{
			if (s.m_raise)
			{
				return s.m_raiseRadius;
			}
			if (s.m_level)
			{
				return s.m_levelRadius;
			}
			if (s.m_paintCleared)
			{
				return s.m_paintRadius;
			}
			if (s.m_smooth)
			{
				return s.m_smoothRadius;
			}
			return s.GetRadius();
		}

		private static bool CellAllowed(Kind kind, GameObject ghost, Vector3 p)
		{
			//IL_0000: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			if (Location.IsInsideNoBuildLocation(p) || !PrivateArea.CheckAccess(p, 0f, false, false))
			{
				return false;
			}
			if (kind == Kind.Plant)
			{
				return PlantPreview.WhyWontGrow(ghost.GetComponent<Plant>(), p) == null;
			}
			return true;
		}

		private static Quaternion StableViewYaw()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform.eulerAngles.y : 0f);
			if (float.IsNaN(_stableYaw) || Mathf.Abs(Mathf.DeltaAngle(num, _stableYaw)) > 35f)
			{
				_stableYaw = Mathf.Round(num / 45f) * 45f;
			}
			return Quaternion.Euler(0f, _stableYaw, 0f);
		}

		private void DrawPreview()
		{
			//IL_0044: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Current != Kind.None && Cells.Count > 1;
			int num = ((flag && Current == Kind.Plant) ? Cells.Count : 0);
			while (_rings.Count < num)
			{
				_rings.Add(SpawnView.CreateLine(((Component)this).transform, "AreaCell", GoodColor, 0.04f));
				_ringDrawnAt.Add(Vector3.positiveInfinity);
			}
			for (int i = 0; i < _rings.Count; i++)
			{
				LineRenderer val = _rings[i];
				bool flag2 = i < num && i != _centerIndex;
				if (((Component)val).gameObject.activeSelf != flag2)
				{
					((Component)val).gameObject.SetActive(flag2);
				}
				if (!flag2)
				{
					_ringDrawnAt[i] = Vector3.positiveInfinity;
					continue;
				}
				Color startColor = (val.endColor = (CellOk[i] ? GoodColor : BadColor));
				val.startColor = startColor;
				Vector3 val3 = Cells[i] - _ringDrawnAt[i];
				if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f))
				{
					DrawFlatRing(val, Cells[i], _growRadius);
					_ringDrawnAt[i] = Cells[i];
				}
			}
			bool flag3 = flag && Current == Kind.Terrain;
			if (((Component)_outline).gameObject.activeSelf != flag3)
			{
				((Component)_outline).gameObject.SetActive(flag3);
			}
			if (flag3)
			{
				DrawSquare(_outline, AreaCenter(), (float)(Columns - 1) * 0.5f * _spacing + _tileRadius, (float)(Rows - 1) * 0.5f * _spacing + _tileRadius, _gridRotation);
			}
		}

		private static Vector3 AreaCenter()
		{
			//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_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_001b: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//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)
			Vector3 val = Vector3.zero;
			foreach (Vector3 cell in Cells)
			{
				val += cell;
			}
			Vector3 result = val / (float)Cells.Count;
			result.y = Cells[_centerIndex].y;
			return result;
		}

		private static void DrawFlatRing(LineRenderer lr, Vector3 center, float radius)
		{
			//IL_0023: 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_003e: 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)
			lr.positionCount = 20;
			for (int i = 0; i < 20; i++)
			{
				float num = (float)i * (float)Math.PI * 2f / 20f;
				lr.SetPosition(i, new Vector3(center.x + Mathf.Cos(num) * radius, center.y + 0.08f, center.z + Mathf.Sin(num) * radius));
			}
		}

		private static void DrawSquare(LineRenderer lr, Vector3 center, float halfX, float halfZ, Quaternion rotation)
		{
			//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)
			//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_0040: 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_0054: 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_006c: 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)
			//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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00d7: 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)
			lr.positionCount = 48;
			Vector3[] array = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(0f - halfX, 0f, 0f - halfZ),
				new Vector3(0f - halfX, 0f, halfZ),
				new Vector3(halfX, 0f, halfZ),
				new Vector3(halfX, 0f, 0f - halfZ)
			};
			float y = default(float);
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 12; j++)
				{
					Vector3 val = Vector3.Lerp(array[i], array[(i + 1) % 4], (float)j / 12f);
					Vector3 val2 = center + rotation * val;
					if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val2, ref y))
					{
						val2.y = y;
					}
					val2.y += 0.3f;
					lr.SetPosition(i * 12 + j, val2);
				}
			}
		}

		internal static void StepSize(int direction)
		{
			if (Current == Kind.Plant)
			{
				_plantShape = Mathf.Clamp(_plantShape + direction, 0, MaxShape(PlantShapes, ServerLimits.Max(Plugin.MaxPlantGrid)));
			}
			else if (Current == Kind.Terrain)
			{
				_terrainShape = Mathf.Clamp(_terrainShape + direction, 0, MaxShape(TerrainShapes, ServerLimits.Max(Plugin.MaxHoeSize)));
			}
		}

		private static int MaxShape(int[,] shapes, int maxSide)
		{
			int result = 0;
			for (int i = 0; i < shapes.GetLength(0); i++)
			{
				if (shapes[i, 0] <= maxSide && shapes[i, 1] <= maxSide)
				{
					result = i;
				}
			}
			return result;
		}

		internal static void AddRows(InfoLines lines)
		{
			if (Current != Kind.None && Cells.Count > 1)
			{
				string arg = ((Columns == 1) ? $"Row of {Rows}" : $"{Columns} × {Rows}");
				if (Current == Kind.Plant)
				{
					lines.Add(null, "Area", $"{arg}, {Valid} of {Cells.Count} can grow", "one seed and stamina each", (Valid == Cells.Count) ? Tone.Good : Tone.Warn);
				}
				else
				{
					lines.Add(null, "Area", $"{arg}, {Cells.Count} tiles", _raiseGrid ? "raised, then levelled flat: two hoe uses per tile" : "stamina and durability per tile", (Valid != Cells.Count) ? Tone.Warn : Tone.None);
				}
			}
		}

		internal static void AddHint(InfoLines lines)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (Current != Kind.None && lines.HintKeys.Count == 0 && (int)Plugin.AreaModifier.Value != 0)
			{
				lines.Hint(Ui.KeyName(Plugin.AreaModifier.Value) + " + Scroll", "Area size");
			}
		}

		internal static void OnPlaced(Player player, Piece piece)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0057: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			_pending = null;
			Refresh(player, validate: true);
			if (Current == Kind.None || Cells.Count <= 1)
			{
				return;
			}
			PendingCells.Clear();
			Vector3 center = Cells[_centerIndex];
			for (int i = 0; i < Cells.Count; i++)
			{
				if (Cells[i] != center)
				{
					PendingCells.Add(Cells[i]);
				}
			}
			PendingCells.Sort(delegate(Vector3 a, Vector3 b)
			{
				//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_0007: 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_0017: 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_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = a - center;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = b - center;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			});
			_pending = piece;
			_pendingCenter = center;
			_pendingRotation = PlacementGhost.Invoke(player).transform.rotation;
		}

		internal static void PlacePending(Player player)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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_0088: 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_00b1: 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_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: 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)
			Piece pending = _pending;
			_pending = null;
			if ((Object)(object)pending == (Object)null)
			{
				return;
			}
			GameObject val = PlacementGhost.Invoke(player);
			ItemData tool = StaminaPatches.RightItem.Invoke((Humanoid)(object)player);
			PieceTable table = BuildPieces.Invoke(player);
			Kind kind = (((Object)(object)((Component)pending).GetComponent<Plant>() != (Object)null) ? Kind.Plant : Kind.Terrain);
			int num = 0;
			string text = null;
			foreach (Vector3 pendingCell in PendingCells)
			{
				if (!((Object)(object)val == (Object)null) && CellAllowed(kind, val, pendingCell))
				{
					Quaternion rotation = ((kind == Kind.Plant) ? Quaternion.Euler(0f, Random.Range(0f, 360f), 0f) : _pendingRotation);
					text = PlaceOne(player, pending, pendingCell, rotation, tool, table);
					if (text != null)
					{
						break;
					}
					num++;
				}
			}
			TerrainOp component = ((Component)pending).GetComponent<TerrainOp>();
			Piece val2 = (((Object)(object)component != (Object)null && component.m_settings.m_raise && text == null) ? LevelPiece(table) : null);
			if ((Object)(object)val2 != (Object)null)
			{
				float num2 = _pendingCenter.y + component.m_settings.m_raiseDelta;
				List<Vector3> list = new List<Vector3>(PendingCells.Count + 1);
				list.Add(_pendingCenter);
				list.AddRange(PendingCells);
				foreach (Vector3 item in list)
				{
					if (CellAllowed(Kind.Terrain, val, item))
					{
						text = PlaceOne(player, val2, new Vector3(item.x, num2, item.z), _pendingRotation, tool, table);
						if (text != null)
						{
							break;
						}
					}
				}
			}
			if (text != null)
			{
				Plugin.Tell("Area", $"placed {num + 1} of {PendingCells.Count + 1}, {text}", Tone.Warn);
			}
		}

		private static string PlaceOne(Player player, Piece piece, Vector3 pos, Quaternion rotation, ItemData tool, PieceTable table)
		{
			//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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			bool placementCostDisabled = player.PlacementCostDisabled;
			if (!placementCostDisabled && !player.HaveRequirements(piece, (RequirementMode)0))
			{
				return "out of materials";
			}
			float num = GetBuildStamina(player);
			if (player.GetStamina() < num)
			{
				return "out of stamina";
			}
			if (tool != null && tool.m_shared.m_useDurability && tool.m_durability <= 0f)
			{
				return "tool broken";
			}
			player.PlacePiece(piece, pos, rotation, false, false);
			if (!placementCostDisabled && !ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
			{
				player.ConsumeResources(piece.m_resources, 0, -1, 1);
			}
			((Character)player).UseStamina(num);
			if ((Object)(object)table != (Object)null && (int)table.m_skill != 0)
			{
				if (BuildRemoveDebt.Invoke(player) > 0)
				{
					BuildRemoveDebt.Invoke(player)--;
				}
				else
				{
					((Character)player).RaiseSkill(table.m_skill, 1f);
				}
			}
			if (tool != null && tool.m_shared.m_useDurability)
			{
				tool.m_durability -= GetPlaceDurability(player, tool) * Game.m_durabilityRate;
			}
			return null;
		}

		private static Piece LevelPiece(PieceTable table)
		{
			if ((Object)(object)table == (Object)null)
			{
				return null;
			}
			foreach (GameObject piece in table.m_pieces)
			{
				TerrainOp val = (((Object)(object)piece != (Object)null) ? piece.GetComponent<TerrainOp>() : null);
				if ((Object)(object)val != (Object)null && val.m_settings.m_level && !val.m_settings.m_raise)
				{
					return piece.GetComponent<Piece>();
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	internal static class AreaToolsPlacementPatch
	{
		private static void Prefix(Player __instance, out int __state)
		{
			__state = BuildHelpers.PlaceRotation.Invoke(__instance);
		}

		private static void Postfix(Player __instance, int __state)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				if (AreaTools.ScrollCaptured())
				{
					BuildHelpers.PlaceRotation.Invoke(__instance) = __state;
				}
				AreaTools.PlacePending(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	internal static class AreaToolsZoomPatch
	{
		private static readonly FieldRef<GameCamera, float> Distance = AccessTools.FieldRefAccess<GameCamera, float>("m_distance");

		private static void Prefix(GameCamera __instance, out float __state)
		{
			__state = Distance.Invoke(__instance);
		}

		private static void Postfix(GameCamera __instance, float __state)
		{
			if (AreaTools.ScrollCaptured())
			{
				Distance.Invoke(__instance) = __state;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class AreaToolsCrouchPatch
	{
		private static void Prefix(Player __instance, ref bool crouch)
		{
			if (crouch && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && AreaTools.Current != AreaTools.Kind.None && AreaTools.ModifierHeld())
			{
				crouch = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class AreaToolsClickPatch
	{
		private static void Postfix(Player __instance, Piece piece, bool __result)
		{
			if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				AreaTools.OnPlaced(__instance, piece);
			}
		}
	}
	internal enum ViewMode
	{
		Off,
		Spawns,
		Repairs,
		Comfort
	}
	[Flags]
	internal enum ViewModes
	{
		None = 0,
		Spawns = 1,
		Repairs = 2,
		Comfort = 4,
		All = 7
	}
	internal static class BaseView
	{
		internal class Legend
		{
			internal readonly List<KeyValuePair<Color, string>> Lines = new List<KeyValuePair<Color, string>>(12);

			internal string BarText;

			internal float BarFraction = -1f;

			internal Color BarColor;

			internal void Clear()
			{
				Lines.Clear();
				BarText = null;
				BarFraction = -1f;
			}

			internal void Add(Color swatch, string text)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Lines.Add(new KeyValuePair<Color, string>(swatch, text));
			}

			internal void Add(string text)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Lines.Add(new KeyValuePair<Color, string>(Color.clear, text));
			}
		}

		private static readonly ViewMode[] Order = new ViewMode[3]
		{
			ViewMode.Spawns,
			ViewMode.Repairs,
			ViewMode.Comfort
		};

		internal static Rect LegendRect;

		internal static float LegendDrawnAt = -10f;

		private static readonly PanelMotion LegendMotion = new PanelMotion();

		internal const float Edge = 10f;

		internal static ViewMode Mode => Plugin.BaseViewMode.Value;

		internal static bool LegendShowing => Time.unscaledTime - LegendDrawnAt < 0.25f;

		internal static bool LegendVisible
		{
			get
			{
				if (Plugin.ShowLegend.Value && (Object)(object)Player.m_localPlayer != (Object)null && !Hud.IsUserHidden() && !Menu.IsVisible() && !InventoryGui.IsVisible())
				{
					return !Minimap.IsOpen();
				}
				return false;
			}
		}

		private static bool InCycle(ViewMode mode)
		{
			ViewModes value = Plugin.BaseViewModes.Value;
			return mode switch
			{
				ViewMode.Spawns => (value & ViewModes.Spawns) != 0, 
				ViewMode.Repairs => (value & ViewModes.Repairs) != 0, 
				ViewMode.Comfort => (value & ViewModes.Comfort) != 0, 
				_ => false, 
			};
		}

		internal static void Cycle()
		{
			int num = Array.IndexOf(Order, Mode);
			ViewMode value = ViewMode.Off;
			for (int i = num + 1; i < Order.Length; i++)
			{
				if (InCycle(Order[i]))
				{
					value = Order[i];
					break;
				}
			}
			Plugin.BaseViewMode.Value = value;
		}

		internal static string Name(ViewMode mode)
		{
			return mode.ToString();
		}

		internal static void DrawLegend(Legend legend)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0167: 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_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: 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_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			float width;
			float height;
			Matrix4x4 matrix = Ui.BeginScaled(out width, out height);
			int num = 0;
			foreach (KeyValuePair<Color, string> line in legend.Lines)
			{
				if (line.Key.a > 0f)
				{
					num++;
				}
			}
			int num2 = legend.Lines.Count - num;
			bool flag = legend.BarFraction >= 0f;
			float num3 = TabsWidth();
			if (flag)
			{
				num3 = Mathf.Max(num3, Ui.LineWidth(legend.BarText, Ui.Small));
			}
			foreach (KeyValuePair<Color, string> line2 in legend.Lines)
			{
				num3 = Mathf.Max(num3, Ui.LineWidth(line2.Value, Ui.Text) + ((line2.Key.a > 0f) ? 22f : 0f));
			}
			float num4 = Mathf.Clamp(num3 + 24f + 4f, 300f, 520f);
			float h = 58f + (flag ? 37f : 0f) + (float)legend.Lines.Count * 22f + ((num2 > 0 && num > 0) ? 9f : 0f);
			LegendRect = PlaceLegend(num4, h, width, height);
			LegendDrawnAt = Time.unscaledTime;
			Rect val = LegendMotion.Step(LegendRect);
			Color color = GUI.color;
			GUI.color = new Color(color.r, color.g, color.b, color.a * LegendMotion.Alpha);
			Ui.Panel(val);
			GUI.BeginClip(val);
			float num5 = 12f;
			float num6 = 12f;
			float num7 = num4 - 24f;
			DrawTabs(num5, num6, num7, 28f);
			num6 += 28f;
			Ui.Divider(num5, num6 + 2f, num7);
			num6 += 6f;
			if (flag)
			{
				Ui.Label(new Rect(num5, num6, num7, 18f), legend.BarText, Ui.Small);
				Ui.Bar(new Rect(num5, num6 + 19f, num7, 12f), legend.BarFraction, legend.BarColor);
				num6 += 37f;
			}
			bool flag2 = num == 0;
			foreach (KeyValuePair<Color, string> line3 in legend.Lines)
			{
				float num8 = num5;
				if (line3.Key.a > 0f)
				{
					Ui.Swatch(new Rect(num5 + 1f, num6 + 5f, 12f, 12f), line3.Key);
					num8 += 22f;
				}
				else if (!flag2)
				{
					flag2 = true;
					Ui.Divider(num5, num6 + 4f, num7);
					num6 += 9f;
				}
				Ui.Label(new Rect(num8, num6, num7 - (num8 - num5), 22f), line3.Value, Ui.Text);
				num6 += 22f;
			}
			GUI.EndClip();
			GUI.color = color;
			GUI.matrix = matrix;
		}

		internal static Rect PlaceLegend(float w, float h, float sw, float sh)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			return Ui.FromFraction(Plugin.LegendX.Value, Plugin.LegendY.Value, w, h, sw, sh, 10f);
		}

		internal static void SaveLegendPlace(Rect r, float sw, float sh)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Ui.ToFraction(r, sw, sh, 10f, out var fx, out var fy);
			Plugin.LegendX.Value = fx;
			Plugin.LegendY.Value = fy;
		}

		private static float TabsWidth()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			float num = Ui.KeycapWidth(Ui.KeyName(Plugin.ToggleKey.Value)) + 6f + Ui.LineWidth("Next", Ui.Small) + 16f;
			ViewMode[] order = Order;
			foreach (ViewMode mode in order)
			{
				if (InCycle(mode))
				{
					num += Ui.LineWidth("<b>" + Name(mode) + "</b>", Ui.Title) + 16f;
				}
			}
			return num;
		}

		private static void DrawTabs(float x, float y, float width, float height)
		{
			//IL_0005: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			string key = Ui.KeyName(Plugin.ToggleKey.Value);
			float num = Ui.KeycapWidth(key);
			float num2 = num + 6f + Ui.LineWidth("Next", Ui.Small);
			Ui.Keycap(new Rect(x + width - num2, y + (height - 18f) * 0.5f, 0f, 18f), key);
			Ui.Label(new Rect(x + width - num2 + num + 6f, y, num2, height), "Next", Ui.Small);
			float num3 = x;
			ViewMode[] order = Order;
			foreach (ViewMode viewMode in order)
			{
				if (InCycle(viewMode))
				{
					bool num4 = viewMode == Mode;
					string text = (num4 ? ("<b>" + Name(viewMode) + "</b>") : ("<color=#b9ad98>" + Name(viewMode) + "</color>"));
					GUIStyle style = (num4 ? Ui.Title : Ui.Text);
					float num5 = Ui.LineWidth(text, style);
					Ui.Label(new Rect(num3, y, num5 + 4f, height), text, style);
					if (num4)
					{
						Ui.Accent(new Rect(num3, y + height - 3f, num5, 2f), Ui.GoldColor);
					}
					num3 += num5 + 16f;
				}
			}
		}
	}
	internal class BuildHelpers : MonoBehaviour
	{
		internal static readonly FieldRef<Player, int> PlaceRotation = AccessTools.FieldRefAccess<Player, int>("m_placeRotation");

		internal static readonly FieldRef<Player, float> PlaceRotationDegrees = AccessTools.FieldRefAccess<Player, float>("m_placeRotationDegrees");

		private static readonly FieldRef<Player, GameObject> PlacementGhost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

		internal static float FineOffset;

		internal static float RotationShownUntil;

		private const float RotationShowTime = 2.5f;

		private static readonly List<ComfortInfo.Entry> Entries = new List<ComfortInfo.Entry>();

		private const float TipTime = 7f;

		private static bool _tipShown;

		private static float _tipUntil;

		private int _comfortNow;

		private int _comfortAfter;

		private bool _showComfort;

		private bool _inShelter;

		private float _comfortTimer;

		private string _noGainReason;

		private InfoRow _shelterLine;

		private InfoRow _stationLine;

		private InfoRow _supportLine;

		private InfoRow _hoverLine;

		private float _shelterTimer;

		private static readonly InfoLines Lines = new InfoLines();

		internal static bool FineModifierHeld()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!Compat.FineRotationDisabled && (int)Plugin.FineRotationModifier.Value != 0)
			{
				return UnityInput.Current.GetKey(Plugin.FineRotationModifier.Value);
			}
			return false;
		}

		private void Update()
		{
			//IL_0081: 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_0151: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).InPlaceMode() && !_tipShown && Plugin.ShowKeyTips.Value)
			{
				_tipShown = true;
				_tipUntil = Time.time + 7f;
			}
			if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode())
			{
				_showComfort = false;
				_shelterLine = null;
				_supportLine = null;
				_stationLine = null;
				_hoverLine = null;
				return;
			}
			if (!Compat.FineRotationDisabled)
			{
				KeyboardShortcut value = Plugin.MatchRotationKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && !Hud.IsPieceSelectionVisible() && !Plugin.IsTyping())
				{
					MatchOrResetRotation(localPlayer);
				}
			}
			_comfortTimer -= Time.deltaTime;
			if (_comfortTimer <= 0f)
			{
				_comfortTimer = 0.25f;
				UpdateComfort(localPlayer);
				Piece hoveringPiece = localPlayer.GetHoveringPiece();
				_supportLine = (Plugin.ShowSupportInfo.Value ? SupportInfo.Describe(hoveringPiece) : null);
				_hoverLine = HoverInfo.Describe(hoveringPiece);
				GameObject val = PlacementGhost.Invoke(localPlayer);
				StationExtension val2 = ((Plugin.ShowStationInfo.Value && (Object)(object)val != (Object)null && val.activeInHierarchy) ? val.GetComponent<StationExtension>() : null);
				_stationLine = (((Object)(object)val2 != (Object)null) ? StationInfo.DescribeGhost(val2, val.transform.position) : null);
			}
			_shelterTimer -= Time.deltaTime;
			if (_shelterTimer <= 0f)
			{
				_shelterTimer = 1f;
				_shelterLine = ((Plugin.ShowShelterInfo.Value && PlantPreview.Line == null) ? ShelterInfo.Describe(localPlayer, _showComfort) : null);
			}
		}

		private static void MatchOrResetRotation(Player player)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			RotationShownUntil = Time.time + 2.5f;
			Piece hoveringPiece = player.GetHoveringPiece();
			float num = PlaceRotationDegrees.Invoke(player);
			if ((Object)(object)hoveringPiece != (Object)null)
			{
				Quaternion rotation = ((Component)hoveringPiece).transform.rotation;
				float y = ((Quaternion)(ref rotation)).eulerAngles.y;
				int num2 = Mathf.RoundToInt(y / num);
				PlaceRotation.Invoke(player) = num2;
				FineOffset = y - (float)num2 * num;
				Plugin.Tell("Rotation", $"matched, {y:0.#}°", Tone.Good);
			}
			else
			{
				FineOffset = 0f;
				Plugin.Tell("Rotation", "fine steps reset");
			}
		}

		internal static float CurrentYaw(Player player)
		{
			return Mathf.Repeat(PlaceRotationDegrees.Invoke(player) * (float)PlaceRotation.Invoke(player) + FineOffset, 360f);
		}

		private void UpdateComfort(Player player)
		{
			//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_006e: 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_00a1: 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_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: 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)
			_showComfort = false;
			if (!Plugin.ShowComfortPreview.Value)
			{
				return;
			}
			GameObject val = PlacementGhost.Invoke(player);
			Piece val2 = (((Object)(object)val != (Object)null && val.activeInHierarchy) ? val.GetComponent<Piece>() : null);
			if ((Object)(object)val2 == (Object)null || val2.m_comfort <= 0)
			{
				return;
			}
			Vector3 position = ((Component)player).transform.position;
			_inShelter = player.InShelter();
			_comfortNow = ComfortInfo.Calculate(_inShelter, position, null, Entries);
			_comfortAfter = _comfortNow;
			_noGainReason = null;
			if (_inShelter && Vector3.Distance(position, val.transform.position) >= 10f)
			{
				_noGainReason = $"too far from you, comfort counts within {10f:0} m";
			}
			else if (_inShelter)
			{
				_comfortAfter = ComfortInfo.Calculate(inShelter: true, position, val2, Entries);
				if (_comfortAfter <= _comfortNow)
				{
					foreach (ComfortInfo.Entry entry in Entries)
					{
						if ((Object)(object)entry.Piece == (Object)(object)val2)
						{
							if (entry.Skipped.Count != 0)
							{
								_noGainReason = entry.Skipped[0] + " nearby already gives the same";
								break;
							}
						}
						else if (((int)val2.m_comfortGroup != 0 && entry.Piece.m_comfortGroup == val2.m_comfortGroup) || entry.Piece.m_name == val2.m_name)
						{
							string arg = (((int)val2.m_comfortGroup != 0) ? ((object)Unsafe.As<ComfortGroup, ComfortGroup>(ref val2.m_comfortGroup)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() : "piece");
							_noGainReason = $"{Localization.instance.Localize(entry.Piece.m_name)} (+{entry.Value}) already counts as your {arg}";
							break;
						}
					}
				}
			}
			_showComfort = true;
		}

		private void OnGUI()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Plugin.ShowBuildInfo.Value || (Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode() || Hud.IsUserHidden() || Hud.IsPieceSelectionVisible() || Menu.IsVisible())
			{
				return;
			}
			bool flag = FineModifierHeld() || Time.time < RotationShownUntil;
			InfoRow line = PlantPreview.Line;
			InfoRow row = ((line == null) ? PlacementInfo.Line : null);
			InfoRow infoRow = ((line == null) ? _shelterLine : null);
			InfoLines lines = Lines;
			lines.Clear();
			Sprite icon = localPlayer.GetSelectedPiece()?.m_icon;
			if (RemovalLockPatch.Locked)
			{
				lines.Add(null, "Removal", "Locked", Ui.KeyName(Plugin.RemovalLockKey.Value) + " to unlock", Tone.Warn);
			}
			if (Freecam.Active)
			{
				Freecam.AddRows(lines, localPlayer);
			}
			lines.Add(line, icon);
			lines.Add(row, icon);
			AreaTools.AddRows(lines);
			lines.Add(_stationLine, icon);
			lines.Add(TerrainInfo.Line, icon);
			if (flag)
			{
				lines.Add(icon, "Rotation", $"<b>{CurrentYaw(localPlayer):0.#}°</b>", $"{Plugin.FineRotationStep.Value:0.#}° fine steps");
			}
			lines.Add(PrecisionInfo.SnapLine, icon);
			lines.Add(PrecisionInfo.HeightLine, icon);
			if (_showComfort)
			{
				AddComfort(lines);
			}
			bool flag2 = FineModifierHeld();
			if (infoRow != null && infoRow.Tone == Tone.Good && !_showComfort && !flag2)
			{
				infoRow = null;
			}
			lines.Add(infoRow);
			lines.Add(_hoverLine);
			if (_supportLine != null && (flag2 || _supportLine.Tone == Tone.Warn || _supportLine.Tone == Tone.Bad))
			{
				lines.Add(_supportLine);
			}
			lines.Add(RemovalPreview.Line);
			if (Freecam.Active)
			{
				Freecam.AddHints(lines);
			}
			else if (flag && !Compat.FineRotationDisabled)
			{
				if ((int)Plugin.FineRotationModifier.Value != 0)
				{
					lines.Hint(Ui.KeyName(Plugin.FineRotationModifier.Value) + " + Scroll", "Fine rotate");
				}
				lines.Hint(Ui.KeyName(Plugin.MatchRotationKey.Value), "Match / reset");
				lines.Hint(Ui.KeyName(Plugin.BuildInfoToggleKey.Value), "Hide");
			}
			else if (Time.time < _tipUntil)
			{
				if (!Compat.FreecamDisabled)
				{
					lines.Hint(Ui.KeyName(Plugin.FreecamKey.Value), "Freecam");
				}
				if (!Compat.FineRotationDisabled && (int)Plugin.FineRotationModifier.Value != 0)
				{
					lines.Hint(Ui.KeyName(Plugin.FineRotationModifier.Value) + " + Scroll", "Fine rotate");
				}
				lines.Hint(Ui.KeyName(Plugin.RemovalLockKey.Value), "Lock removal");
				lines.Hint(Ui.KeyName(Plugin.ToggleKey.Value), "Base view");
				lines.Hint(Ui.KeyName(Plugin.BuildInfoToggleKey.Value), "Hide this");
			}
			AreaTools.AddHint(lines);
			if (lines.Count != 0 || lines.HintKeys.Count != 0)
			{
				InfoBox.Draw(lines);
			}
		}

		private void AddComfort(InfoLines lines)
		{
			Sprite icon = Ui.StatusIcon(SEMan.s_statusEffectRested);
			if (!_inShelter)
			{
				lines.Add(icon, "Comfort", "Not sheltered here", "comfort pieces only count under a roof", Tone.Warn);
			}
			else if (_comfortAfter > _comfortNow)
			{
				lines.Add(icon, "Comfort", $"{_comfortNow} » <b>{_comfortAfter}</b>", null, Tone.Good);
			}
			else
			{
				lines.Add(icon, "Comfort", $"{_comfortNow}, no gain", _noGainReason);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	internal static class FineRotationInputPatch
	{
		private static void Prefix(Player __instance, out int __state)
		{
			__state = BuildHelpers.PlaceRotation.Invoke(__instance);
		}

		private static void Postfix(Player __instance, int __state)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				int num = BuildHelpers.PlaceRotation.Invoke(__instance);
				if (num != __state && BuildHelpers.FineModifierHeld())
				{
					BuildHelpers.PlaceRotation.Invoke(__instance) = __state;
					BuildHelpers.RotationShownUntil = Time.time + 2.5f;
					BuildHelpers.FineOffset = Mathf.Repeat(BuildHelpers.FineOffset + (float)(num - __state) * Plugin.FineRotationStep.Value, 360f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
	internal static class FineRotationGhostPatch
	{
		private static float _savedDegrees;

		private static int _savedRotation;

		private static void Prefix(Player __instance, out bool __state)
		{
			__state = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && !Mathf.Approximately(BuildHelpers.FineOffset, 0f);
			if (__state)
			{
				_savedDegrees = BuildHelpers.PlaceRotationDegrees.Invoke(__instance);
				_savedRotation = BuildHelpers.PlaceRotation.Invoke(__instance);
				BuildHelpers.PlaceRotationDegrees.Invoke(__instance) = _savedDegrees * (float)_savedRotation + BuildHelpers.FineOffset;
				BuildHelpers.PlaceRotation.Invoke(__instance) = 1;
			}
		}

		private static void Finalizer(Player __instance, bool __state)
		{
			if (__state)
			{
				BuildHelpers.PlaceRotationDegrees.Invoke(__instance) = _savedDegrees;
				BuildHelpers.PlaceRotation.Invoke(__instance) = _savedRotation;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "CopyPiece")]
	internal static class CopyPiecePatch
	{
		private static void Postfix(bool __result)
		{
			if (__result)
			{
				BuildHelpers.FineOffset = 0f;
			}
		}
	}
	internal enum MarkerImport
	{
		All,
		Filtered,
		None
	}
	[HarmonyPatch(typeof(Minimap), "AddSharedMapData")]
	internal static class TableImportPatch
	{
		private static readonly FieldRef<Minimap, List<PinData>> Pins = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");

		private static int _skippedChecked;

		private static int _skippedDuplicate;

		private static int _skippedType;

		private static int _skippedAll;

		private static int _updated;

		internal static bool Bypass;

		internal static bool Silent;

		private static void Prefix(Minimap __instance, ref byte[] dataArray)
		{
			_skippedChecked = (_skippedDuplicate = (_skippedType = (_skippedAll = (_updated = 0))));
			if ((Object)(object)Player.m_localPlayer == (Object)null || dataArray == null)
			{
				return;
			}
			bool flag = !Bypass && Plugin.MarkerImportMode.Value != MarkerImport.All;
			bool flag2 = Plugin.UpdateSharedMarkers.Value && !Compat.CartographyManagerDisabled;
			if (!flag && !flag2)
			{
				return;
			}
			try
			{
				TableData tableData = TableData.Parse(dataArray, __instance);
				if (tableData != null)
				{
					if (flag && Filter(__instance, tableData))
					{
						dataArray = tableData.Write();
					}
					if (flag2)
					{
						RefreshChanged(__instance, tableData);
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[Norse Sense] Couldn't process cartography table markers, importing as vanilla: " + ex.Message));
			}
		}

		private static void Postfix()
		{
			if (!Silent && !((Object)(object)Player.m_localPlayer == (Object)null) && Plugin.ShowImportSummary.Value)
			{
				List<string> list = new List<string>();
				if (_skippedAll > 0)
				{
					list.Add($"{_skippedAll} (markers off)");
				}
				if (_skippedChecked > 0)
				{
					list.Add($"{_skippedChecked} crossed out");
				}
				if (_skippedDuplicate > 0)
				{
					list.Add($"{_skippedDuplicate} duplicates");
				}
				if (_skippedType > 0)
				{
					list.Add($"{_skippedType} filtered types");
				}
				int num = _skippedChecked + _skippedDuplicate + _skippedType + _skippedAll;
				string text = ((num > 0) ? string.Format("skipped {0} markers ({1})", num, string.Join(", ", list)) : null);
				if (_updated > 0)
				{
					text = ((text == null) ? "" : (text + ", ")) + $"updated {_updated}";
				}
				if (text != null)
				{
					Plugin.Tell("Map table", text);
				}
			}
		}

		private static bool Filter(Minimap map, TableData table)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			long playerID = Player.m_localPlayer.GetPlayerID();
			bool flag = Plugin.MarkerImportMode.Value == MarkerImport.None;
			HashSet<PinType> hashSet = ParseTypes(Plugin.SkipMarkerTypes.Value);
			float value = Plugin.DuplicateMergeDistance.Value;
			List<PinData> existing = Pins.Invoke(map);
			List<TableMarker> list = new List<TableMarker>(table.Markers.Count);
			foreach (TableMarker marker in table.Markers)
			{
				if (marker.Owner == playerID)
				{
					list.Add(marker);
				}
				else if (flag)
				{
					_skippedAll++;
				}
				else if (hashSet.Contains(marker.Type))
				{
					_skippedType++;
				}
				else if (marker.Checked && Plugin.SkipCrossedOutMarkers.Value)
				{
					_skippedChecked++;
				}
				else if (value > 0f && IsDuplicate(marker, existing, list, value))
				{
					_skippedDuplicate++;
				}
				else
				{
					list.Add(marker);
				}
			}
			if (list.Count == table.Markers.Count)
			{
				return false;
			}
			table.Markers = list;
			return true;
		}

		private static void RefreshChanged(Minimap map, TableData table)
		{
			//IL_0061: 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)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			long playerID = Player.m_localPlayer.GetPlayerID();
			List<PinData> list = Pins.Invoke(map);
			foreach (TableMarker marker in table.Markers)
			{
				if (marker.Owner == playerID)
				{
					continue;
				}
				for (int num = list.Count - 1; num >= 0; num--)
				{
					PinData val = list[num];
					if (val.m_ownerID != 0L)
					{
						Vector3 val2 = val.m_pos - marker.Pos;
						if (!(((Vector3)(ref val2)).sqrMagnitude > 1f))
						{
							if (val.m_name != marker.Name || val.m_type != marker.Type || val.m_checked != marker.Checked)
							{
								map.RemovePin(val);
								_updated++;
							}
							break;
						}
					}
				}
			}
		}

		private static bool IsDuplicate(TableMarker marker, List<PinData> existing, List<TableMarker> kept, float radius)
		{
			//IL_0026: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			float num = radius * radius;
			Vector3 val;
			foreach (PinData item in existing)
			{
				if (item.m_ownerID == 0L && item.m_save && item.m_type == marker.Type)
				{
					val = item.m_pos - marker.Pos;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						return true;
					}
				}
			}
			foreach (TableMarker item2 in kept)
			{
				if (item2.Type == marker.Type)
				{
					val = item2.Pos - marker.Pos;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static HashSet<PinType> ParseTypes(string list)
		{
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			HashSet<PinType> hashSet = new HashSet<PinType>();
			string[] array = list.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				switch (text.Trim().ToLowerInvariant())
				{
				case "fire":
					hashSet.Add((PinType)0);
					continue;
				case "home":
				case "house":
					hashSet.Add((PinType)1);
					continue;
				case "hammer":
					hashSet.Add((PinType)2);
					continue;
				case "circle":
				case "dot":
					hashSet.Add((PinType)3);
					continue;
				case "rune":
				case "portal":
					hashSet.Add((PinType)6);
					continue;
				}
				if (Enum.TryParse<PinType>(text.Trim(), ignoreCase: true, out PinType result))
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}
	}
	[HarmonyPatch(typeof(MapTable), "OnWrite")]
	internal static class TableWritePatch
	{
		private static void Prefix()
		{
			TableImportPatch.Bypass = true;
		}

		private static void Finalizer()
		{
			TableImportPatch.Bypass = false;
			if (Plugin.MarkerImportMode.Value != MarkerImport.All && !((Object)(object)Minimap.instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				ReimportOwnMap();
			}
		}

		internal static void ReimportOwnMap()
		{
			TableImportPatch.Silent = true;
			try
			{
				Minimap.instance.AddSharedMapData(Minimap.instance.GetSharedMapData((byte[])null));
			}
			finally
			{
				TableImportPatch.Silent = false;
			}
		}
	}
	internal class ComfortInfo : MonoBehaviour
	{
		internal class Entry
		{
			internal Piece Piece;

			internal int Value;

			internal readonly List<string> Skipped = new List<string>(2);
		}

		internal const float Radius = 10f;

		private const float RefreshInterval = 1f;

		private static readonly List<Piece> TempPieces = new List<Piece>();

		private static Piece _ghost;

		private readonly List<Entry> _entries = new List<Entry>();

		private bool _show;

		private bool _inShelter;

		private int _level;

		private InfoRow _shelterReason;

		private float _timer;

		private const float Pad = 10f;

		private const float HeaderH = 24f;

		private const float CellH = 30f;

		private const float IconSize = 26f;

		private const float Gap = 12f;

		private const float FootH = 18f;

		private readonly PanelMotion _motion = new PanelMotion();

		internal static Rect LastRect;

		private const int PerRow = 6;

		internal const float Margin = 10f;

		internal static int Calculate(bool inShelter, Vector3 pos, Piece ghost, List<Entry> entries)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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)
			entries.Clear();
			if (!inShelter)
			{
				return 1;
			}
			TempPieces.Clear();
			Piece.GetAllComfortPiecesInRadius(pos, 10f, TempPieces);
			if ((Object)(object)ghost != (Object)null)
			{
				TempPieces.Add(ghost);
			}
			_ghost = ghost;
			TempPieces.Sort(ComfortSort);
			int num = 2;
			Entry entry = null;
			for (int i = 0; i < TempPieces.Count; i++)
			{
				Piece val = TempPieces[i];
				if (i > 0)
				{
					Piece val2 = TempPieces[i - 1];
					if (((int)val.m_comfortGroup != 0 && val.m_comfortGroup == val2.m_comfortGroup) || val.m_name == val2.m_name)
					{
						string item = Localization.instance.Localize(val.m_name);
						if (entry != null && !entry.Skipped.Contains(item))
						{
							entry.Skipped.Add(item);
						}
						continue;
					}
				}
				entry = new Entry
				{
					Piece = val,
					Value = Value(val)
				};
				entries.Add(entry);
				num += entry.Value;
			}
			_ghost = null;
			return num;
		}

		private static int Value(Piece piece)
		{
			if (!((Object)(object)piece == (Object)(object)_ghost))
			{
				return piece.GetComfort();
			}
			return piece.m_comfort;
		}

		private static int ComfortSort(Piece x, Piece y)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (x.m_comfortGroup != y.m_comfortGroup)
			{
				ref ComfortGroup comfortGroup = ref x.m_comfortGroup;
				object target = y.m_comfortGroup;
				return ((Enum)Unsafe.As<ComfortGroup, ComfortGroup>(ref comfortGroup)/*cast due to .constrained prefix*/).CompareTo(target);
			}
			float num = Value(x);
			float num2 = Value(y);
			if (num != num2)
			{
				return num2.CompareTo(num);
			}
			return y.m_name.CompareTo(x.m_name);
		}

		internal static float RestedMinutes(int comfort)
		{
			SE_Rested val = (SE_Rested)(((Object)(object)ObjectDB.instance != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			float num = (((Object)(object)val != (Object)null) ? val.m_baseTTL : 300f);
			float num2 = (((Object)(object)val != (Object)null) ? val.m_TTLPerComfortLevel : 60f);
			return (num + (float)(comfort - 1) * num2) / 60f;
		}

		private void Update()
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			_show = Plugin.ShowComfortBreakdown.Value && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).InPlaceMode() && ((Character)localPlayer).IsSitting() && !((Character)localPlayer).IsAttachedToShip();
			if (!_show)
			{
				_timer = 0f;
				return;
			}
			_timer -= Time.deltaTime;
			if (!(_timer > 0f))
			{
				_timer = 1f;
				_inShelter = localPlayer.InShelter();
				_level = Calculate(_inShelter, ((Component)localPlayer).transform.position, null, _entries);
				_shelterReason = (_inShelter ? null : ShelterInfo.Describe(localPlayer, always: true));
			}
		}

		private void OnGUI()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: 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_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			if (!_show || Hud.IsUserHidden() || Menu.IsVisible() || InventoryGui.IsVisible() || Minimap.IsOpen())
			{
				return;
			}
			float width;
			float height;
			Matrix4x4 matrix = Ui.BeginScaled(out width, out height);
			float scale = (float)Screen.height / height;
			string text = string.Format("<color={0}>Comfort</color> <b>{1}</b>", "#e8c77e", _level);
			string text2 = (_inShelter ? $"Rested {RestedMinutes(_level):0} min" : "<color=#f0b04a>not sheltered</color>");
			int num = 0;
			foreach (Entry entry2 in _entries)
			{
				num += entry2.Skipped.Count;
			}
			string text3 = (_inShelter ? ((num > 0) ? $"{num} more nearby don't count (same kind)" : null) : ((_shelterReason != null) ? (char.ToUpper(ShelterInfo.Summary[0]) + ShelterInfo.Summary.Substring(1)) : null));
			int num2 = (_inShelter ? (_entries.Count + 1) : 0);
			int num3 = (num2 + 6 - 1) / 6;
			float num4 = 31f + Ui.LineWidth("<b>+3</b>", Ui.Small) + 12f;
			float num5 = Mathf.Max(Ui.LineWidth(text, Ui.Text) + 16f + Ui.LineWidth(text2, Ui.Small), (float)Mathf.Min(num2, 6) * num4 - 12f);
			if (text3 != null)
			{
				num5 = Mathf.Max(num5, Ui.LineWidth(text3, Ui.Small));
			}
			num5 = Mathf.Min(num5, width * 0.4f) + 20f;
			float h = 44f + (float)num3 * 30f + ((text3 != null) ? 18f : 0f);
			Rect target = (LastRect = Place(num5, h, width, height, scale));
			Rect val = _motion.Step(target);
			Color color = GUI.color;
			GUI.color = new Color(color.r, color.g, color.b, color.a * _motion.Alpha);
			Ui.Panel(val);
			GUI.BeginClip(val);
			float num6 = 10f;
			float num7 = 10f;
			Ui.Label(new Rect(num6, num7, num5, 24f), text, Ui.Text);
			Ui.Label(new Rect(num5 - 10f - Ui.LineWidth(text2, Ui.Small) - 2f, num7, num5, 24f), text2, Ui.Small);
			num7 += 24f;
			for (int i = 0; i < num2; i++)
			{
				float num8 = num6 + (float)(i % 6) * num4;
				float num9 = num7 + (float)(i / 6) * 30f;
				Sprite sprite;
				string text4;
				if (i == 0)
				{
					sprite = Ui.StatusIcon(SEMan.s_statusEffectShelter);
					text4 = "<b>+1</b>";
				}
				else
				{
					Entry entry = _entries[i - 1];
					sprite = entry.Piece.m_icon;
					text4 = ((entry.Value > 0) ? $"<b>+{entry.Value}</b>" : "<color=#f0b04a>+0</color>");
				}
				Ui.SlotIcon(new Rect(num8, num9 + 2f, 26f, 26f), sprite);
				Ui.Label(new Rect(num8 + 26f + 5f, num9, num4, 30f), text4, Ui.Small);
			}
			num7 += (float)num3 * 30f;
			if (text3 != null)
			{
				Ui.Label(new Rect(num6, num7, num5 - 20f, 18f), text3, Ui.Small);
			}
			GUI.EndClip();
			GUI.color = color;
			GUI.matrix = matrix;
		}

		internal static void SavePlace(Rect r, float sw, float sh)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Ui.ToFraction(r, sw, sh, 10f, out var fx, out var fy);
			Plugin.ComfortX.Value = fx;
			Plugin.ComfortY.Value = fy;
		}

		internal static Rect Place(float w, float h, float sw, float sh, float scale)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ComfortX.Value >= 0f && Plugin.ComfortY.Value >= 0f)
			{
				return Ui.FromFraction(Plugin.ComfortX.Value, Plugin.ComfortY.Value, w, h, sw, sh, 10f);
			}
			float num = sw - 24f;
			float num2 = sh * 0.4f;
			Hud instance = Hud.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_statusEffectListRoot != (Object)null && InfoBox.ContentRect(((Component)instance.m_statusEffectListRoot).gameObject, scale, out var r))
			{
				num = ((Rect)(ref r)).xMax;
				num2 = ((Rect)(ref r)).yMax + 10f;
			}
			Rect result = default(Rect);
			((Rect)(ref result))..ctor(num - w, num2, w, h);
			if (BaseView.LegendShowing && ((Rect)(ref result)).Overlaps(BaseView.LegendRect))
			{
				((Rect)(ref result)).y = ((Rect)(ref BaseView.LegendRect)).yMax + 10f;
				if (((Rect)(ref result)).yMax > sh - 10f)
				{
					((Rect)(ref result)).y = ((Rect)(ref BaseView.LegendRect)).yMin - h - 10f;
				}
			}
			((Rect)(ref result)).y = Mathf.Clamp(((Rect)(ref result)).y, 10f, sh - h - 10f);
			return result;
		}
	}
	internal class ComfortView : MonoBehaviour
	{
		private const float Radius = 12f;

		private const float Cell = 1f;

		private const float StandHeight = 1f;

		private const float NeedCover = 0.8f;

		private const float FloorOffset = 0.06f;

		private const float RefreshInterval = 3f;

		private const int CellsPerFrame = 60;

		internal static readonly Color NoShelterColor = Color.clear;

		internal static readonly Color LowColor = Ui.Alpha(Ui.WarnColor, 0.35f);

		internal static readonly Color HighColor = Ui.Alpha(Ui.GoodColor, 0.45f);

		private static int _floorMask;

		private GameObject _root;

		private Mesh _mesh;

		private LineRenderer _bestRing;

		private Vector3[] _verts;

		private Color[] _colors;

		private int[] _levels;

		private int _n;

		private bool _active;

		private bool _scanning;

		private float _timer;

		private Vector3 _scanCenter;

		private int _best;

		private int _here;

		private int _sheltered;

		private readonly BaseView.Legend _legend = new BaseView.Legend();

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0038: 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_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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_007c: 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)
			_root = new GameObject("ComfortView");
			_root.transform.SetParent(((Component)this).transform, false);
			_root.SetActive(false);
			GameObject val = new GameObject("Grid");
			val.transform.SetParent(_root.transform, false);
			_mesh = new Mesh
			{
				name = "NorseSense_ComfortGrid",
				indexFormat = (IndexFormat)1
			};
			_mesh.MarkDynamic();
			val.AddComponent<MeshFilter>().sharedMesh = _mesh;
			MeshRenderer obj = val.AddComponent<MeshRenderer>();
			((Renderer)obj).sharedMaterial = SpawnView.GetMaterial();
			((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)obj).receiveShadows = false;
			_bestRing = SpawnView.CreateLine(_root.transform, "Best", HighColor, 0.08f);
			((Component)_bestRing).gameObject.SetActive(false);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_mesh != (Object)null)
			{
				Object.Destroy((Object)(object)_mesh);
			}
		}

		private void OnDisable()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			_scanning = false;
			_active = false;
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
		}

		private void Update()
		{
			//IL_00d6: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			bool flag = BaseView.Mode == ViewMode.Comfort && (Object)(object)localPlayer != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null;
			if (flag != _active)
			{
				_active = flag;
				_root.SetActive(flag);
				if (!flag)
				{
					((MonoBehaviour)this).StopAllCoroutines();
					_scanning = false;
					_mesh.Clear();
					_n = 0;
				}
				_timer = 0f;
			}
			if (flag)
			{
				_here = localPlayer.GetComfortLevel();
				_timer -= Time.deltaTime;
				if (!_scanning && (_timer <= 0f || Utils.DistanceXZ(((Component)localPlayer).transform.position, _scanCenter) > 3f))
				{
					_timer = 3f;
					((MonoBehaviour)this).StartCoroutine(Scan(((Component)localPlayer).transform.position));
				}
			}
		}

		private IEnumerator Scan(Vector3 center)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			_scanning = true;
			_scanCenter = center;
			if (_floorMask == 0)
			{
				_floorMask = LayerMask.GetMask(new string[4] { "piece", "terrain", "Default", "static_solid" });
			}
			int n = Mathf.CeilToInt(24f);
			if (n != _n)
			{
				Allocate(n);
			}
			float originX = Mathf.Floor(center.x / 1f) * 1f - (float)(n / 2) * 1f;
			float originZ = Mathf.Floor(center.z / 1f) * 1f - (float)(n / 2) * 1f;
			int best = 0;
			int sheltered = 0;
			int bestCell = -1;
			float[] floors = new float[n * n];
			Vector3 val = default(Vector3);
			RaycastHit val2 = default(RaycastHit);
			float num3 = default(float);
			bool flag = default(bool);
			for (int c = 0; c < n * n; c++)
			{
				if (c > 0 && c % 60 == 0)
				{
					yield return null;
					if ((Object)(object)Player.m_localPlayer == (Object)null)
					{
						_scanning = false;
						yield break;
					}
				}
				int num = c % n;
				int num2 = c / n;
				((Vector3)(ref val))..ctor(originX + ((float)num + 0.5f) * 1f, center.y, originZ + ((float)num2 + 0.5f) * 1f);
				_levels[c] = -1;
				floors[c] = float.NaN;
				if (Utils.DistanceXZ(val, center) > 12f || !Physics.Raycast(val + Vector3.up * 1.4f, Vector3.down, ref val2, 4.5f, _floorMask) || ((RaycastHit)(ref val2)).normal.y < 0.7f)
				{
					continue;
				}
				floors[c] = ((RaycastHit)(ref val2)).point.y;
				Cover.GetCoverForPoint(((RaycastHit)(ref val2)).point + Vector3.up * 1f, ref num3, ref flag, 0.5f);
				bool flag2 = flag && num3 >= 0.8f;
				int num4 = SE_Rested.CalculateComfortLevel(flag2, ((RaycastHit)(ref val2)).point);
				_levels[c] = (flag2 ? num4 : 0);
				if (flag2)
				{
					sheltered++;
					if (num4 > best)
					{
						best = num4;
						bestCell = c;
					}
				}
			}
			int num5 = 2;
			for (int i = 0; i < n * n; i++)
			{
				int num6 = i % n;
				int num7 = i / n;
				int num8 = i * 4;
				float num9 = originX + (float)num6 * 1f;
				float num10 = originZ + (float)num7 * 1f;
				Color val3 = Color.clear;
				float num11 = (float.IsNaN(floors[i]) ? (center.y - 100f) : (floors[i] + 0.06f));
				if (_levels[i] == 0)
				{
					val3 = NoShelterColor;
				}
				else if (_levels[i] > 0)
				{
					val3 = Color.Lerp(LowColor, HighColor, (best > num5) ? ((float)(_levels[i] - num5) / (float)(best - num5)) : 1f);
				}
				_verts[num8] = new Vector3(num9 + 0.05f, num11, num10 + 0.05f);
				_verts[num8 + 1] = new Vector3(num9 + 0.05f, num11, num10 + 1f - 0.05f);
				_verts[num8 + 2] = new Vector3(num9 + 1f - 0.05f, num11, num10 + 1f - 0.05f);
				_verts[num8 + 3] = new Vector3(num9 + 1f - 0.05f, num11, num10 + 0.05f);
				_colors[num8] = (_colors[num8 + 1] = (_colors[num8 + 2] = (_colors[num8 + 3] = val3)));
			}
			_mesh.vertices = _verts;
			_mesh.colors = _colors;
			_mesh.RecalculateBounds();
			((Component)_bestRing).gameObject.SetActive(bestCell >= 0);
			if (bestCell >= 0)
			{
				Vector3 center2 = default(Vector3);
				((Vector3)(ref center2))..ctor(originX + ((float)(bestCell % n) + 0.5f) * 1f, floors[bestCell] + 0.06f, originZ + ((float)(bestCell / n) + 0.5f) * 1f);
				DrawFlatRing(_bestRing, center2, 0.6f);
			}
			_best = best;
			_sheltered = sheltered;
			_scanning = false;
		}

		private void Allocate(int n)
		{
			_n = n;
			_verts = (Vector3[])(object)new Vector3[n * n * 4];
			_colors = (Color[])(object)new Color[n * n * 4];
			_levels = new int[n * n];
			int[] array = new int[n * n * 6];
			for (int i = 0; i < n * n; i++)
			{
				int num = i * 4;
				int num2 = i * 6;
				array[num2] = num;
				array[num2 + 1] = num + 1;
				array[num2 + 2] = num + 2;
				array[num2 + 3] = num;
				array[num2 + 4] = num + 2;
				array[num2 + 5] = num + 3;
			}
			_mesh.Clear();
			_mesh.vertices = _verts;
			_mesh.colors = _colors;
			_mesh.triangles = array;
		}

		private static void DrawFlatRing(LineRenderer lr, Vector3 center, float radius)
		{
			//IL_0023: 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_003e: 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)
			lr.positionCount = 24;
			for (int i = 0; i < 24; i++)
			{
				float num = (float)i * (float)Math.PI * 2f / 24f;
				lr.SetPosition(i, new Vector3(center.x + Mathf.Cos(num) * radius, center.y + 0.05f, center.z + Mathf.Sin(num) * radius));
			}
		}

		private void OnGUI()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if (_active && BaseView.LegendVisible)
			{
				if ((int)Event.current.type == 8)
				{
					BuildLegend();
				}
				BaseView.DrawLegend(_legend);
			}
		}

		private void BuildLegend()
		{
			//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_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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			_legend.Clear();
			if (_best > 0)
			{
				_legend.BarFraction = Mathf.Clamp01((float)_here / (float)_best);
				_legend.BarText = string.Format("Here <b>{0}</b>, best <b>{1}</b> <size=12><color={2}>(Rested {3:0} min)</color></size>", _here, _best, "#b9ad98", ComfortInfo.RestedMinutes(_best));
				_legend.BarColor = Color.Lerp(LowColor, HighColor, _legend.BarFraction);
			}
			if (_sheltered == 0)
			{
				_legend.Add("<color=#b9ad98>No sheltered floor nearby. Comfort only counts under a roof.</color>");
				return;
			}
			_legend.Add(HighColor, "Most comfort (ringed)");
			_legend.Add(LowColor, "Less comfort");
		}
	}
	internal static class Compat
	{
		internal static bool Checked;

		internal static bool FineRotationDisabled;

		internal static bool PlayerTweaksDisabled;

		internal static bool CartographyManagerDisabled;

		internal static bool FreecamDisabled;

		internal static string FreecamDisabledBy;

		internal static string AreaPlantingDisabledBy;

		internal static string AreaTerrainDisabledBy;

		internal static string SpawnRulesChangedBy;

		internal static string RaidRulesChangedBy;

		internal static string PlantRulesChangedBy;

		internal static string TerrainRulesChangedBy;

		private static readonly string[] RotationMods = new string[1] { "gizmo" };

		private static readonly string[] PlayerTweakMods = new string[3] { "valheim_plus", "valheimplus", "valheim plus" };

		private static readonly string[] SpawnMods = new string[5] { "spawn_that", "spawnthat", "spawn that", "expand_world_spawns", "expand world spawns" };

		private static readonly string[] TableManagerMods = new string[2] { "cartographyadmin", "cartography admin" };

		private static readonly string[] CameraBuildMods = new string[6] { "buildcamera", "build camera", "build_camera", "freecam", "free cam", "freebuildcam" };

		private static readonly string[] PlantingMods = new string[4] { "planteasily", "plant easily", "massfarming", "mass farming" };

		private static readonly string[] TerrainRadiusMods = new string[5] { "advancedterrainmodifiers", "advanced terrain", "terraintools", "terrain tools", "hoeradius" };

		private static readonly string[] RaidMods = new string[5] { "custom_raids", "customraids", "custom raids", "expand_world_events", "expand world events" };

		internal static void Run(string ownHarmonyId, ManualLogSource log)
		{
			Checked = true;
			bool value = Plugin.AutoCompat.Value;
			List<string> list = new List<string>();
			string text = FindPlugin(RotationMods);
			if (text != null && value)
			{
				FineRotationDisabled = true;
				list.Add("Fine rotation is off because " + text + " handles piece rotation.");
			}
			string text2 = FindPlugin(TableManagerMods);
			if (text2 != null && value)
			{
				CartographyManagerDisabled = true;
				list.Add("The table marker manager is off because " + text2 + " provides one.");
			}
			string text3 = FindPlugin(CameraBuildMods);
			if (text3 != null && value)
			{
				FreecamDisabled = true;
				FreecamDisabledBy = text3;
				list.Add("Freecam building is off because " + text3 + " provides a build camera.");
			}
			string text4 = FindPlugin(PlantingMods);
			if (text4 != null && value)
			{
				AreaPlantingDisabledBy = text4;
				list.Add("Area planting and harvesting are off because " + text4 + " does them.");
			}
			string text5 = FindPlugin(TerrainRadiusMods);
			if (text5 != null && value)
			{
				AreaTerrainDisabledBy = text5;
				list.Add("The bigger hoe is off because " + text5 + " changes the hoe's area.");
			}
			string text6 = FindPlugin(PlayerTweakMods);
			if (text6 != null)
			{
				if (value)
				{
					PlayerTweaksDisabled = true;
				}
				list.Add(value ? ("Build distance and pickup range are left to " + text6 + ".") : (text6 + " may also change build distance and pickup range."));
				if (Plugin.BuildStaminaMultiplier.Value < 1f || Plugin.SowStaminaMultiplier.Value < 1f)
				{
					list.Add(text6 + " may also change build stamina; the reductions will stack.");
				}
			}
			SpawnRulesChangedBy = FindPlugin(SpawnMods) ?? PatchOwners(ownHarmonyId, Method(typeof(SpawnSystem), "IsSpawnPointGood"), Method(typeof(SpawnSystem), "FindBaseSpawnPoint"), Method(typeof(EffectArea), "IsPointInsideArea"), Method(typeof(ZoneSystem), "IsBlocked"));
			if (SpawnRulesChangedBy != null)
			{
				list.Add(SpawnRulesChangedBy + " changes spawn rules, so the spawn view may not be exact.");
			}
			RaidRulesChangedBy = FindPlugin(RaidMods) ?? PatchOwners(ownHarmonyId, Method(typeof(RandEventSystem), "CheckBase"), Method(typeof(RandEventSystem), "GetValidEventPoints"), Method(typeof(EffectArea), "GetBaseValue"));
			if (RaidRulesChangedBy != null)
			{
				list.Add(RaidRulesChangedBy + " changes raid rules, so the raid info may not be exact.");
			}
			PlantRulesChangedBy = PatchOwners(ownHarmonyId, Method(typeof(Plant), "UpdateHealth"), Method(typeof(Plant), "HaveGrowSpace"), Method(typeof(Plant), "HaveRoof"));
			if (PlantRulesChangedBy != null)
			{
				list.Add(PlantRulesChangedBy + " changes how plants grow, so the planting preview may not be exact.");
			}
			TerrainRulesChangedBy = PatchOwners(ownHarmonyId, Method(typeof(TerrainComp), "ApplyToHeightmap"), Method(typeof(TerrainComp), "RaiseTerrain"), Method(typeof(TerrainComp), "LevelTerrain"));
			if (TerrainRulesChangedBy != null)
			{
				list.Add(TerrainRulesChangedBy + " changes terrain editing, so the terrain limits may not be exact.");
			}
			string text7 = PatchOwners(ownHarmonyId, Method(typeof(Player), "UpdatePlacementGhost"));
			if (text7 != null && !FineRotationDisabled)
			{
				log.LogInfo((object)("Also patching build placement: " + text7 + ". If fine rotation misbehaves, set the fine rotation modifier to None."));
			}
			string text8 = PatchOwners(ownHarmonyId, Method(typeof(Player), "GetBuildStamina"), Method(typeof(Player), "HaveStamina"));
			if (text8 != null)
			{
				log.LogInfo((object)("Also patching stamina: " + text8 + ". Stamina multipliers will combine with theirs."));
			}
			string text9 = PatchOwners(ownHarmonyId, Method(typeof(Minimap), "AddSharedMapData"));
			if (text9 != null)
			{
				log.LogInfo((object)("Also patching cartography table reading: " + text9 + ". Marker filters run alongside it."));
			}
			foreach (string item in list)
			{
				log.LogWarning((object)item);
			}
			if (list.Count == 0)
			{
				log.LogInfo((object)"No overlapping mods found.");
			}
			if (!Plugin.ShowCompatWarnings.Value || !((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return;
			}
			foreach (string item2 in list)
			{
				Plugin.Tell("Norse Sense", item2, Tone.Warn);
			}
		}

		private static MethodBase Method(Type type, string name)
		{
			return AccessTools.Method(type, name, (Type[])null, (Type[])null);
		}

		private static string FindPlugin(string[] keys)
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				string guid = value.Metadata.GUID.ToLowerInvariant();
				string name = value.Metadata.Name.ToLowerInvariant();
				if (keys.Any((string k) => guid.Contains(k) || name.Contains(k)))
				{
					return value.Metadata.Name;
				}
			}
			return null;
		}

		private static string PatchOwners(string ownHarmonyId, params MethodBase[] methods)
		{
			HashSet<string> hashSet = new HashSet<string>();
			foreach (MethodBase methodBase in methods)
			{
				if (methodBase == null)
				{
					continue;
				}
				Patches patchInfo = Harmony.GetPatchInfo(methodBase);
				if (patchInfo == null)
				{
					continue;
				}
				foreach (string owner in patchInfo.Owners)
				{
					if (owner != ownHarmonyId)
					{
						hashSet.Add(PluginName(owner));
					}
				}
			}
			if (hashSet.Count <= 0)
			{
				return null;
			}
			return string.Join(", ", hashSet);
		}

		private static string PluginName(string harmonyId)
		{
			if (!Chainloader.PluginInfos.TryGetValue(harmonyId, out var value))
			{
				return harmonyId;
			}
			return value.Metadata.Name;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? IsAdvanced;

		public bool? Browsable;
	}
	internal class Freecam : MonoBehaviour
	{
		private const float RunMultiplier = 3f;

		private const float GroundClearance = 0.4f;

		private const float CameraRadius = 0.3f;

		private static int _solidMask;

		internal static bool Active;

		internal static Vector3 Position;

		private static readonly FieldRef<Player, int> RemoveRayMask = AccessTools.FieldRefAccess<Player, int>("m_removeRayMask");

		private static Player _player;

		private static float _savedPlaceDistance;

		private const float ResumeDistance = 2f;

		private static bool _hasLast;

		private static Vector3 _lastCamera;

		private static Vector3 _lastBody;

		private Vector3 _velocity;

		private float _lastHealth;

		private void Update()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Active)
			{
				string text = WhyStop(localPlayer);
				if (text != null)
				{
					Exit(text);
					return;
				}
			}
			if ((Object)(object)localPlayer == (Object)null || Plugin.IsTyping())
			{
				return;
			}
			KeyboardShortcut value = Plugin.FreecamKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				if (Active)
				{
					Exit(null);
				}
				else
				{
					Enter(localPlayer);
				}
			}
			if (!Active)
			{
				return;
			}
			float health = ((Character)localPlayer).GetHealth();
			if (health < _lastHealth - 0.01f && health < ((Character)localPlayer).GetMaxHealth() - 0.01f)
			{
				Exit("you took damage");
				return;
			}
			_lastHealth = health;
			float num = ServerLimits.Max(Plugin.FreecamRange) + ServerLimits.Max(Plugin.FreecamReach) + 2f;
			if (!Mathf.Approximately(localPlayer.m_maxPlaceDistance, num))
			{
				localPlayer.m_maxPlaceDistance = num;
			}
			Move(localPlayer, Time.unscaledDeltaTime);
			if (DistanceFromPlayer(localPlayer) > ServerLimits.Max(Plugin.FreecamRange) + 2f)
			{
				Exit("too far from you");
			}
		}

		private void Enter(Player player)
		{
			//IL_00b4: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			if (!ServerLimits.Allowed(Plugin.FreecamAllowed))
			{
				Plugin.Tell("Freecam", (ServerLimits.Active && Plugin.FreecamAllowed.Value) ? "off on this server" : "turned off in the settings", Tone.Warn);
			}
			else if (Compat.FreecamDisabled)
			{
				Plugin.Tell("Freecam", "off, " + Compat.FreecamDisabledBy + " provides one", Tone.Warn);
			}
			else if (!((Character)player).InPlaceMode() || (Object)(object)GameCamera.instance == (Object)null)
			{
				Plugin.Tell("Freecam", "works in build mode: equip a hammer, hoe or cultivator", Tone.Warn);
			}
			else if (WhyStop(player) == null)
			{
				Active = true;
				_player = player;
				_savedPlaceDistance = player.m_maxPlaceDistance;
				_lastHealth = ((Character)player).GetHealth();
				_velocity = Vector3.zero;
				Position = ((Component)GameCamera.instance).transform.position;
				if (_hasLast && Vector3.Distance(((Component)player).transform.position, _lastBody) < 2f)
				{
					Position = (ThroughWalls() ? _lastCamera : Sweep(Position, _lastCamera));
				}
			}
		}

		internal static void Exit(string why)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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)
			if (Active)
			{
				Active = false;
				_hasLast = (Object)(object)_player != (Object)null;
				if (_hasLast)
				{
					_lastCamera = Position;
					_lastBody = ((Component)_player).transform.position;
				}
				if ((Object)(object)_player != (Object)null)
				{
					_player.m_maxPlaceDistance = _savedPlaceDistance;
				}
				_player = null;
				PlayerTweaks.Apply();
				if ((Object)(object)Player.m_localPlayer != (Object)null && why != null)
				{
					Plugin.Tell("Freecam", "off, " + why, Tone.Warn);
				}
			}
		}

		private static string WhyStop(Player player)
		{
			if ((Object)(object)player == (Object)null || (Active && (Object)(object)player != (Object)(object)_player))
			{
				return "player changed";
			}
			if (((Character)player).IsDead())
			{
				return "you died";
			}
			if (!((Character)player).InPlaceMode())
			{
				return "left build mode";
			}
			if (((Character)player).IsAttached() || ((Character)player).IsTeleporting() || ((Character)player).InCutscene())
			{
				return "busy";
			}
			return null;
		}

		private void Move(Player player, float dt)
		{
			//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_00ee: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: 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)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 zero = Vector3.zero;
			bool flag = false;
			if (!InventoryGui.IsVisible() && !Minimap.IsOpen() && !Hud.IsPieceSelectionVisible() && !Menu.IsVisible())
			{
				if (ZInput.GetButton("Forward"))
				{
					zero.z += 1f;
				}
				if (ZInput.GetButton("Backward"))
				{
					zero.z -= 1f;
				}
				if (ZInput.GetButton("Left"))
				{
					zero.x -= 1f;
				}
				if (ZInput.GetButton("Right"))
				{
					zero.x += 1f;
				}
				if (ZInput.GetButton("Jump"))
				{
					zero.y += 1f;
				}
				if (ZInput.GetButton("Crouch"))
				{
					zero.y -= 1f;
				}
				flag = ZInput.GetButton("Run");
			}
			Transform transform = ((Component)GameCamera.instance).transform;
			Vector3 val = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			val = Vector3.ProjectOnPlane(transform.right, Vector3.up);
			Vector3 normalized2 = ((Vector3)(ref val)).normalized;
			Vector3 val2 = normalized * zero.z + normalized2 * zero.x + Vector3.up * zero.y;
			if (((Vector3)(ref val2)).sqrMagnitude > 1f)
			{
				((Vector3)(ref val2)).Normalize();
			}
			Vector3 val3 = val2 * Plugin.FreecamSpeed.Value * (flag ? 3f : 1f);
			_velocity = Vector3.Lerp(_velocity, val3, 1f - Mathf.Exp(-12f * dt));
			Vector3 val4 = Position + _velocity * dt;
			Vector3 centerPoint = ((Character)player).GetCenterPoint();
			Vector3 val5 = val4 - centerPoint;
			if (((Vector3)(ref val5)).magnitude > ServerLimits.Max(Plugin.FreecamRange))
			{
				val4 = centerPoint + ((Vector3)(ref val5)).normalized * ServerLimits.Max(Plugin.FreecamRange);
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				val4.y = Mathf.Max(val4.y, ZoneSystem.instance.GetGroundHeight(val4) + 0.4f);
			}
			Position = (ThroughWalls() ? val4 : Sweep(Position, val4));
		}

		private static bool ThroughWalls()
		{
			return ServerLimits.Allowed(Plugin.FreecamThroughWalls);
		}

		private static Vector3 Sweep(Vector3 from, Vector3 to)
		{
			//IL_000d: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or m