Decompiled source of Stavebound v0.9.0

plugins\Stavebound.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Splatform;
using Stavebound.Compat;
using Stavebound.Config;
using Stavebound.Portals;
using Stavebound.Staves;
using Stavebound.Tiers;
using Stavebound.Travel;
using Stavebound.UI;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("recognizerHD")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 recognizerHD")]
[assembly: AssemblyFileVersion("0.9.0.0")]
[assembly: AssemblyInformationalVersion("0.9.0+79f0f11b78b5c276b3ccb4c9bce73cfc2ad3e329")]
[assembly: AssemblyProduct("Stavebound")]
[assembly: AssemblyTitle("Stavebound")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/recognizerHD/stave-bound")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.9.0.0")]
namespace Stavebound
{
	internal static class BuildInfo
	{
		internal const string Guid = "com.recognizerhd.stavebound";

		internal const string Name = "Stavebound";

		internal const string Version = "0.9.0";
	}
	[BepInPlugin("com.recognizerhd.stavebound", "Stavebound", "0.9.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal sealed class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Instance = this;
			StaveboundConfig.Bind(((BaseUnityPlugin)this).Config);
			SelectorKeys.Bind(((BaseUnityPlugin)this).Config);
			Translations.Add();
			StavePieces.Register();
			PortalRegistry.Register();
			_harmony = new Harmony("com.recognizerhd.stavebound");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Logger.LogInfo((object)"Stavebound 0.9.0 loaded.");
		}

		private void Start()
		{
			ConflictDetector.WarnAboutKnownConflicts();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			Instance = null;
		}
	}
}
namespace Stavebound.UI
{
	internal static class DestinationSelector
	{
		private enum SortOrder
		{
			Distance,
			Name
		}

		private static readonly List<PortalRecord> Candidates = new List<PortalRecord>();

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

		private const int VisibleRows = 7;

		private static ZDOID _sourceId;

		private static long _sourcePid;

		private static Vector3 _sourcePosition;

		private static string _sourceName;

		private static SortOrder _order = SortOrder.Distance;

		private static bool _onlyWhatAcceptsMyCargo;

		private static int _highlight;

		private static Clearance _carrying;

		private static Clearance _sourceMask;

		private static GameObject _panel;

		private static Text _text;

		private static bool _updateSeen;

		internal static bool IsOpen { get; private set; }

		internal static void Open(TeleportWorld portal, Humanoid who)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = PortalTarget.ZdoOf(portal);
			if (val == null || (Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			if (!ReaimGuard.MayReaim(((Component)portal).transform.position, out var refusal))
			{
				if (who != null)
				{
					((Character)who).Message((MessageType)2, refusal, 0, (Sprite)null);
				}
				return;
			}
			_sourceId = val.m_uid;
			long pid = PortalTarget.GetPid(val);
			string text = val.GetString(ZDOVars.s_tag, string.Empty);
			_sourceName = (string.IsNullOrEmpty(text) ? "this portal" : ("\"" + text + "\""));
			_sourcePosition = ((Component)portal).transform.position;
			_sourcePid = pid;
			_sourceMask = ClearanceGate.MaskOf(val);
			_carrying = CarriedTiers((Player)(object)((who is Player) ? who : null));
			_onlyWhatAcceptsMyCargo = false;
			Rebuild(0L);
			if (Candidates.Count == 0)
			{
				if (who != null)
				{
					((Character)who).Message((MessageType)2, Translations.Get("stave_sel_nowhere"), 0, (Sprite)null);
				}
				return;
			}
			long current = PortalTarget.GetDestination(val);
			_highlight = Candidates.FindIndex((PortalRecord p) => p.Pid == current);
			if (_highlight < 0)
			{
				_highlight = 0;
			}
			AddPins();
			BuildPanel();
			IsOpen = true;
			_updateSeen = false;
			Minimap.instance.SetMapMode((MapMode)2);
			ShowHighlight();
			Logger.LogInfo((object)$"Selector opened for {_sourceName} with {Candidates.Count} destination(s).");
		}

		internal static void Update()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			if (!IsOpen)
			{
				return;
			}
			if (!_updateSeen)
			{
				_updateSeen = true;
				Logger.LogInfo((object)"Selector is receiving frames.");
			}
			if ((Object)(object)Minimap.instance == (Object)null || (int)Minimap.instance.m_mode != 2)
			{
				Close();
				return;
			}
			if (Cancelled())
			{
				Close();
				return;
			}
			if (Confirmed())
			{
				Commit();
				return;
			}
			if (SelectorKeys.Pressed("stave_filter"))
			{
				_onlyWhatAcceptsMyCargo = !_onlyWhatAcceptsMyCargo;
				Rebuild(Held());
				ShowHighlight();
				return;
			}
			if (SelectorKeys.Pressed("stave_sort"))
			{
				_order = ((_order == SortOrder.Distance) ? SortOrder.Name : SortOrder.Distance);
				Rebuild(Held());
				ShowHighlight();
				return;
			}
			int num = Stepped();
			if (num != 0)
			{
				_highlight = (_highlight + num + Candidates.Count) % Candidates.Count;
				ShowHighlight();
			}
		}

		private static long Held()
		{
			if (_highlight < 0 || _highlight >= Candidates.Count)
			{
				return 0L;
			}
			return Candidates[_highlight].Pid;
		}

		private static void Rebuild(long keep)
		{
			Candidates.Clear();
			Candidates.AddRange(PortalRegistry.All.Where((PortalRecord p) => p.Pid != _sourcePid));
			if (_onlyWhatAcceptsMyCargo && _carrying != Clearance.None)
			{
				Candidates.RemoveAll((PortalRecord p) => !Accepts(p));
			}
			Sort();
			_highlight = ((keep != 0L) ? Candidates.FindIndex((PortalRecord p) => p.Pid == keep) : 0);
			if (_highlight < 0)
			{
				_highlight = 0;
			}
		}

		private static void Sort()
		{
			if (_order == SortOrder.Name)
			{
				Candidates.Sort((PortalRecord a, PortalRecord b) => string.Compare(Describe(a), Describe(b), StringComparison.CurrentCultureIgnoreCase));
			}
			else
			{
				Candidates.Sort((PortalRecord a, PortalRecord b) => Vector3.Distance(_sourcePosition, a.Position).CompareTo(Vector3.Distance(_sourcePosition, b.Position)));
			}
		}

		internal static void HighlightNearest(Vector3 worldPoint)
		{
			//IL_002f: 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_0035: 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)
			if (!IsOpen || Candidates.Count == 0)
			{
				return;
			}
			int highlight = 0;
			float num = float.MaxValue;
			for (int i = 0; i < Candidates.Count; i++)
			{
				Vector3 val = Candidates[i].Position - worldPoint;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					highlight = i;
				}
			}
			_highlight = highlight;
			ShowHighlight();
		}

		private static void Commit()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(_sourceId) : null);
			if (val == null)
			{
				Close();
				return;
			}
			PortalRecord portal = Candidates[_highlight];
			PortalTarget.Set(val, portal.Pid);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, Translations.Format("stave_sel_aimed", _sourceName, Describe(portal)), 0, (Sprite)null);
			}
			Close();
		}

		internal static void Reset()
		{
			Close();
		}

		private static void Close()
		{
			IsOpen = false;
			RemovePins();
			if ((Object)(object)_panel != (Object)null)
			{
				Object.Destroy((Object)(object)_panel);
				_panel = null;
				_text = null;
			}
			Candidates.Clear();
			if ((Object)(object)Minimap.instance != (Object)null && (Object)(object)Minimap.instance.m_mapLarge != (Object)null && Minimap.instance.m_mapLarge.activeSelf)
			{
				Minimap.instance.SetMapMode((MapMode)1);
			}
		}

		private static void AddPins()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			RemovePins();
			foreach (PortalRecord candidate in Candidates)
			{
				PinData item = Minimap.instance.AddPin(candidate.Position, (PinType)3, Describe(candidate), false, false, 0L, default(PlatformUserID));
				Pins.Add(item);
			}
		}

		private static void RemovePins()
		{
			foreach (PinData pin in Pins)
			{
				if (pin != null)
				{
					Minimap instance = Minimap.instance;
					if (instance != null)
					{
						instance.RemovePin(pin);
					}
				}
			}
			Pins.Clear();
		}

		private static void ShowHighlight()
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_text == (Object)null)
			{
				return;
			}
			if (Candidates.Count == 0)
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine(Translations.Format("stave_sel_title", _sourceName));
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("<color=#E06C4A>" + Translations.Get("stave_sel_empty") + "</color>");
				stringBuilder.AppendLine();
				stringBuilder.Append("<size=13>[" + Bound("stave_filter") + "] " + Translations.Get("stave_sel_show_all") + "   [" + Bound("stave_cancel") + "] " + Translations.Get("stave_sel_cancel") + "</size>");
				SetPanelText(stringBuilder.ToString());
				return;
			}
			PortalRecord destination = Candidates[_highlight];
			Minimap instance = Minimap.instance;
			if (instance != null)
			{
				instance.ShowPointOnMap(destination.Position);
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			stringBuilder2.AppendLine(Translations.Format("stave_sel_title", _sourceName));
			string text = Translations.Get((_order == SortOrder.Distance) ? "stave_sel_by_distance" : "stave_sel_by_name");
			string text2 = (_onlyWhatAcceptsMyCargo ? Translations.Get("stave_sel_filtered") : string.Empty);
			stringBuilder2.AppendLine("<size=13>" + text + text2 + "</size>");
			string text3 = FlowNote();
			if (text3 != null)
			{
				stringBuilder2.AppendLine("<size=13><color=#B9A67A>" + text3 + "</color></size>");
			}
			stringBuilder2.AppendLine(Verdict(destination));
			stringBuilder2.AppendLine();
			int num = Mathf.Clamp(_highlight - 3, 0, Mathf.Max(0, Candidates.Count - 7));
			int num2 = Mathf.Min(num + 7, Candidates.Count);
			stringBuilder2.AppendLine((num > 0) ? ("<size=13>" + Translations.Format("stave_sel_more_above", num) + "</size>") : " ");
			for (int i = num; i < num2; i++)
			{
				PortalRecord portal = Candidates[i];
				float num3 = Vector3.Distance(_sourcePosition, portal.Position);
				string text4 = $"{Describe(portal)}  <size=13>{num3:F0}m</size>\n     {Chips(portal)}";
				stringBuilder2.AppendLine((i == _highlight) ? ("<color=#FFB726>» " + text4 + "</color>") : ("<color=#C9C0AC>   " + text4 + "</color>"));
			}
			stringBuilder2.AppendLine((num2 < Candidates.Count) ? ("<size=13>" + Translations.Format("stave_sel_more_below", Candidates.Count - num2) + "</size>") : " ");
			stringBuilder2.AppendLine();
			stringBuilder2.AppendLine("<size=13>[" + Bound("stave_confirm") + "] " + Translations.Get("stave_sel_confirm") + "   [" + Bound("stave_cancel") + "] " + Translations.Get("stave_sel_cancel") + "</size>");
			stringBuilder2.Append("<size=13>[" + Bound("stave_previous") + " / " + Bound("stave_next") + "] " + Translations.Get("stave_sel_change") + "   [" + Bound("stave_sort") + "] " + Translations.Get("stave_sel_sort") + "   [" + Bound("stave_filter") + "] " + Translations.Get("stave_sel_filter") + "</size>");
			SetPanelText(stringBuilder2.ToString());
		}

		private static void SetPanelText(string text)
		{
			if (text.Length > 2400)
			{
				Logger.LogWarning((object)($"Selector text is {text.Length} characters, past the {2400} this panel can " + "safely draw. Expect it to render blank. Shorten a row, or drop VisibleRows."));
			}
			_text.text = text;
		}

		private static string Chips(PortalRecord portal)
		{
			Clearance clearanceMask = (Clearance)portal.ClearanceMask;
			StringBuilder stringBuilder = new StringBuilder("<size=12>");
			Clearance[] ladder = ClearanceExtensions.Ladder;
			foreach (Clearance clearance in ladder)
			{
				bool num = (clearanceMask & clearance) == clearance;
				bool flag = (_carrying & clearance) == clearance;
				if (num)
				{
					stringBuilder.Append(clearance.Symbol());
				}
				else if (flag)
				{
					stringBuilder.Append("<color=#E06C4A>" + clearance.Symbol() + "</color>");
				}
				else
				{
					stringBuilder.Append("··");
				}
				stringBuilder.Append(' ');
			}
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>()) + "</size>";
		}

		private static string Verdict(PortalRecord destination)
		{
			if (_carrying == Clearance.None)
			{
				return "<size=13>" + Translations.Get("stave_sel_carrying_nothing") + "</size>";
			}
			int num = Candidates.Count(Accepts);
			string text = "<size=13>" + Translations.Format("stave_sel_tally", num, Candidates.Count) + "</size>";
			if (!Accepts(destination))
			{
				return "<size=13><color=#E06C4A>" + Translations.Get("stave_sel_refuses") + "</color></size>  " + text;
			}
			return "<size=13><color=#8FC97A>" + Translations.Get("stave_sel_takes") + "</color></size>  " + text;
		}

		private static string FlowNote()
		{
			return (StaveboundConfig.Flow?.Value ?? MaterialFlow.Both) switch
			{
				MaterialFlow.Both => Translations.Get("stave_sel_flow_both"), 
				MaterialFlow.Deliver => Translations.Get("stave_sel_flow_deliver"), 
				_ => null, 
			};
		}

		private static bool Accepts(PortalRecord portal)
		{
			return (ClearanceGate.EffectiveMask(_sourceMask, (Clearance)portal.ClearanceMask) & _carrying) == _carrying;
		}

		private static Clearance CarriedTiers(Player player)
		{
			Clearance clearance = Clearance.None;
			Inventory val = ((player != null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return clearance;
			}
			foreach (ItemData allItem in val.GetAllItems())
			{
				if (allItem?.m_shared != null && !allItem.m_shared.m_teleportable)
				{
					string prefabName = (((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : null);
					clearance |= TierMap.RequiredFor(prefabName);
				}
			}
			return clearance;
		}

		private static string Bound(string button)
		{
			return SelectorKeys.KeyLabel(button);
		}

		private static string Describe(PortalRecord portal)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (StaveboundConfig.HidePortalNames.Value)
			{
				return Translations.Format("stave_portal_at", portal.Position.x.ToString("F0"), portal.Position.z.ToString("F0"));
			}
			if (!string.IsNullOrEmpty(portal.Name))
			{
				return portal.Name;
			}
			return Translations.Get("stave_unnamed_portal");
		}

		private static void BuildPanel()
		{
			//IL_0030: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panel != (Object)null)
			{
				Object.Destroy((Object)(object)_panel);
			}
			_panel = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-600f, -60f), 380f, 540f);
			_text = GUIManager.Instance.CreateText(string.Empty, _panel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, GUIManager.Instance.AveriaSerifBold, 16, GUIManager.Instance.ValheimOrange, true, Color.black, 340f, 500f, false).GetComponent<Text>();
			_text.alignment = (TextAnchor)0;
			_text.supportRichText = true;
		}

		private static int Stepped()
		{
			if (SelectorKeys.Pressed("stave_next"))
			{
				return 1;
			}
			if (!SelectorKeys.Pressed("stave_previous"))
			{
				return 0;
			}
			return -1;
		}

		private static bool Confirmed()
		{
			return SelectorKeys.Pressed("stave_confirm");
		}

		private static bool Cancelled()
		{
			return SelectorKeys.Pressed("stave_cancel");
		}
	}
}
namespace Stavebound.Travel
{
	internal static class ApproachWarning
	{
		private static ZDOID _warnedAbout = ZDOID.None;

		internal static void Consider(ZDO portal, ZDO destination, Player nearby, bool allowed)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (portal == null)
			{
				return;
			}
			if ((Object)(object)nearby == (Object)null || (Object)(object)nearby != (Object)(object)Player.m_localPlayer)
			{
				Forget(portal);
			}
			else if (allowed || !StaveboundConfig.WarnOnApproach.Value)
			{
				Forget(portal);
			}
			else if (!(_warnedAbout == portal.m_uid))
			{
				ClearanceGate.Refusal refusal = ClearanceGate.FirstRefusal(((Humanoid)nearby).GetInventory(), ClearanceGate.EffectiveMask(portal, destination));
				if (refusal != null)
				{
					_warnedAbout = portal.m_uid;
					((Character)nearby).Message((MessageType)2, ClearanceGate.Explain(refusal, destination.GetString(ZDOVars.s_tag, string.Empty)), 0, (Sprite)null);
				}
			}
		}

		private static void Forget(ZDO portal)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (_warnedAbout == portal.m_uid)
			{
				_warnedAbout = ZDOID.None;
			}
		}

		internal static void Reset()
		{
			//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)
			_warnedAbout = ZDOID.None;
		}
	}
	internal static class CargoPreview
	{
		internal static bool TryGetNearbyDestination(out Clearance mask, out bool allowsEverything)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			mask = Clearance.None;
			allowsEverything = false;
			if (!StaveboundConfig.ShowBlockedCargoOverlay.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			TeleportWorld val = PortalTarget.FindNearest(((Component)Player.m_localPlayer).transform.position, StaveboundConfig.CargoPreviewRange.Value);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (val.m_allowAllItems)
			{
				allowsEverything = true;
				return true;
			}
			ZDO val2 = PortalTarget.ZdoOf(val);
			if (!ClearanceGate.TryResolveDestination(val2, out var destination, out var _) || destination == null)
			{
				return false;
			}
			mask = ClearanceGate.EffectiveMask(val2, destination);
			return true;
		}

		internal static bool WouldBeRefused(ItemData item, Clearance destinationMask, bool allowsEverything)
		{
			if (item?.m_shared == null || item.m_shared.m_teleportable)
			{
				return false;
			}
			if (allowsEverything)
			{
				return false;
			}
			string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			return !destinationMask.Permits(TierMap.RequiredFor(prefabName));
		}
	}
	internal static class ClearanceGate
	{
		internal sealed class Refusal
		{
			internal string Item;

			internal Clearance Missing;

			internal int OtherStacks;
		}

		internal static bool TryResolveDestination(ZDO portal, out ZDO destination, out long pendingPid)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			destination = null;
			pendingPid = 0L;
			if (portal == null || ZDOMan.instance == null)
			{
				return false;
			}
			long destination2 = PortalTarget.GetDestination(portal);
			if (destination2 != 0L)
			{
				if (!PortalRegistry.TryGet(destination2, out var record))
				{
					return false;
				}
				pendingPid = destination2;
				destination = ZDOMan.instance.GetZDO(record.Id);
				return true;
			}
			ZDOID connectionZDOID = portal.GetConnectionZDOID((ConnectionType)1);
			if (((ZDOID)(ref connectionZDOID)).IsNone())
			{
				return false;
			}
			destination = ZDOMan.instance.GetZDO(connectionZDOID);
			return true;
		}

		internal static bool Allows(Player player, ZDO source, ZDO destination, bool allowAllItems)
		{
			if (allowAllItems)
			{
				return true;
			}
			if ((Object)(object)player != (Object)null)
			{
				return FirstRefusal(((Humanoid)player).GetInventory(), EffectiveMask(source, destination)) == null;
			}
			return false;
		}

		internal static Refusal FirstRefusal(Inventory inventory, Clearance permitted)
		{
			if (inventory == null)
			{
				return null;
			}
			Refusal refusal = null;
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem?.m_shared == null || allItem.m_shared.m_teleportable)
				{
					continue;
				}
				string text = (((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : null);
				Clearance clearance = TierMap.RequiredFor(text);
				if (!permitted.Permits(clearance))
				{
					if (refusal == null)
					{
						refusal = new Refusal
						{
							Item = ((Localization.instance != null) ? Localization.instance.Localize(allItem.m_shared.m_name) : text),
							Missing = clearance
						};
					}
					else
					{
						num++;
					}
				}
			}
			if (refusal != null)
			{
				refusal.OtherStacks = num;
			}
			return refusal;
		}

		internal static string Explain(Refusal refusal, string destinationName)
		{
			string text = (string.IsNullOrEmpty(destinationName) ? Translations.Get("stave_that_portal") : ("\"" + destinationName + "\""));
			string text2 = Translations.Format("stave_refusal", refusal.Item, text, refusal.Missing.StaveName());
			if (refusal.OtherStacks > 0)
			{
				text2 += Translations.Format("stave_refusal_more", refusal.OtherStacks);
			}
			return text2;
		}

		internal static Clearance MaskOf(ZDO portal)
		{
			if (portal != null)
			{
				return (Clearance)portal.GetInt(ZdoKeys.ClearanceMask, 0);
			}
			return Clearance.None;
		}

		internal static Clearance EffectiveMask(ZDO source, ZDO destination)
		{
			return EffectiveMask(MaskOf(source), MaskOf(destination));
		}

		internal static Clearance EffectiveMask(Clearance source, Clearance destination)
		{
			MaterialFlow num = StaveboundConfig.Flow?.Value ?? MaterialFlow.Both;
			Clearance clearance = Clearance.None;
			if (num != MaterialFlow.Deliver)
			{
				clearance |= destination;
			}
			if (num != MaterialFlow.Receive)
			{
				clearance |= source;
			}
			return clearance;
		}
	}
	internal static class SeamlessTransit
	{
		private const float VanillaPause = 2f;

		private static readonly FieldRef<Player, float> TeleportTimer = AccessTools.FieldRefAccess<Player, float>("m_teleportTimer");

		internal static bool Enabled => StaveboundConfig.SeamlessTransit.Value;

		internal static bool DestinationIsLoaded(Vector3 destination)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				return ZNetScene.instance.IsAreaReady(destination);
			}
			return false;
		}

		internal static bool NeedsLoadingScreen(Vector3 destination)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled)
			{
				return !DestinationIsLoaded(destination);
			}
			return true;
		}

		internal static void ShortenPause(Player player, Vector3 destination)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !NeedsLoadingScreen(destination))
			{
				float num = Mathf.Clamp(StaveboundConfig.TransitPause.Value, 0f, 2f);
				TeleportTimer.Invoke(player) = 2f - num;
			}
		}

		internal static bool ArrivalIsReady(Vector3 destination)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled)
			{
				return DestinationIsLoaded(destination);
			}
			return false;
		}
	}
}
namespace Stavebound.Tiers
{
	internal sealed class BlockedItemsCommand : StaveboundCommand
	{
		public override string Name => "stave_items";

		public override string Help => "List every item the game refuses to teleport, grouped by the stave that will permit it. Anything unrecognised is listed separately and defaults to the highest tier.";

		protected override void Execute(string[] args, Terminal context)
		{
			if (!TierMap.Built)
			{
				StaveboundCommand.Echo(context, "Stavebound: the tier map has not been built yet. Load a world first — the main menu's item database is only a partial one.");
				return;
			}
			StaveboundCommand.Echo(context, $"Stavebound tiers - {TierMap.BlockedCount} blocked item(s):");
			Clearance[] array = new Clearance[5]
			{
				Clearance.Elder,
				Clearance.Bonemass,
				Clearance.Moder,
				Clearance.Yagluth,
				Clearance.Ashen
			};
			foreach (Clearance tier in array)
			{
				List<string> list = TierMap.AtTier(tier).Select(TierMap.Describe).ToList();
				StaveboundCommand.Echo(context, $"  {tier.StaveName()} ({list.Count}): " + ((list.Count == 0) ? "nothing" : string.Join(", ", list)));
			}
			IReadOnlyList<string> unclassifiedItems = TierMap.UnclassifiedItems;
			if (unclassifiedItems.Count == 0)
			{
				StaveboundCommand.Echo(context, "  Every blocked item is classified.");
				return;
			}
			StaveboundCommand.Echo(context, $"  UNCLASSIFIED ({unclassifiedItems.Count}), defaulting to " + Clearance.Ashen.StaveName() + ":");
			foreach (string item in unclassifiedItems.OrderBy((string n) => n))
			{
				StaveboundCommand.Echo(context, "    " + TierMap.Describe(item));
			}
			StaveboundCommand.Echo(context, "  Add these to the 2 - Clearance config lists to place them properly.");
		}
	}
	[Flags]
	internal enum Clearance
	{
		None = 0,
		Elder = 1,
		Bonemass = 2,
		Moder = 4,
		Yagluth = 8,
		Queen = 0x10,
		Ashen = 0x20
	}
	internal static class ClearanceExtensions
	{
		internal const Clearance Highest = Clearance.Ashen;

		internal const Clearance All = Clearance.Elder | Clearance.Bonemass | Clearance.Moder | Clearance.Yagluth | Clearance.Queen | Clearance.Ashen;

		internal static readonly Clearance[] Ladder = new Clearance[6]
		{
			Clearance.Elder,
			Clearance.Bonemass,
			Clearance.Moder,
			Clearance.Yagluth,
			Clearance.Queen,
			Clearance.Ashen
		};

		internal static Clearance UpToFirstGap(Clearance mask)
		{
			Clearance clearance = Clearance.None;
			Clearance[] ladder = Ladder;
			foreach (Clearance clearance2 in ladder)
			{
				if ((mask & clearance2) != clearance2)
				{
					break;
				}
				clearance |= clearance2;
			}
			return clearance;
		}

		internal static bool Permits(this Clearance mask, Clearance required)
		{
			if (required != Clearance.None)
			{
				return (mask & required) == required;
			}
			return true;
		}

		internal static string Symbol(this Clearance tier)
		{
			return tier switch
			{
				Clearance.Elder => "Cu", 
				Clearance.Bonemass => "Fe", 
				Clearance.Moder => "Ag", 
				Clearance.Yagluth => "Bm", 
				Clearance.Queen => "Dv", 
				Clearance.Ashen => "Fl", 
				_ => "--", 
			};
		}

		internal static string StaveName(this Clearance tier)
		{
			return Translations.Get(tier.NameToken());
		}

		internal static string NameToken(this Clearance tier)
		{
			return tier switch
			{
				Clearance.Elder => "stave_rune_elder", 
				Clearance.Bonemass => "stave_rune_bonemass", 
				Clearance.Moder => "stave_rune_moder", 
				Clearance.Yagluth => "stave_rune_yagluth", 
				Clearance.Queen => "stave_rune_queen", 
				Clearance.Ashen => "stave_rune_ashen", 
				_ => "stave_rune_none", 
			};
		}
	}
	internal sealed class PrefabInspectCommand : StaveboundCommand
	{
		private const int MaxDepth = 4;

		public override string Name => "stave_inspect";

		public override string Help => "stave_inspect <prefab> - show a prefab's child objects, renderers, materials and shader colour properties. Use it to find out what can be lit or tinted separately.";

		protected override void Execute(string[] args, Terminal context)
		{
			string text = string.Join(" ", args).Trim();
			if (text.Length == 0)
			{
				StaveboundCommand.Echo(context, "Stavebound: name a prefab, e.g. stave_inspect portal_wood");
				return;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no world loaded.");
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no prefab called \"" + text + "\". Try stave_prefabs " + text);
				return;
			}
			StaveboundCommand.Echo(context, "Stavebound inspect - " + ((Object)prefab).name);
			Describe(context, prefab.transform, 0);
		}

		private static void Describe(Terminal context, Transform node, int depth)
		{
			string text = new string(' ', (depth + 1) * 2);
			List<string> list = (from c in ((Component)node).GetComponents<Component>()
				where (Object)(object)c != (Object)null && !(c is Transform)
				select ((object)c).GetType().Name).ToList();
			StringBuilder stringBuilder = new StringBuilder(text + ((Object)node).name);
			if (list.Count > 0)
			{
				stringBuilder.Append("  [" + string.Join(", ", list) + "]");
			}
			if (!((Component)node).gameObject.activeSelf)
			{
				stringBuilder.Append("  (inactive)");
			}
			StaveboundCommand.Echo(context, stringBuilder.ToString());
			Renderer component = ((Component)node).GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				foreach (Material item in component.sharedMaterials.Where((Material m) => (Object)(object)m != (Object)null))
				{
					string[] obj = new string[7]
					{
						text,
						"  material '",
						((Object)item).name,
						"' shader '",
						null,
						null,
						null
					};
					Shader shader = item.shader;
					obj[4] = ((shader != null) ? ((Object)shader).name : null);
					obj[5] = "'";
					obj[6] = DescribeColours(item);
					StaveboundCommand.Echo(context, string.Concat(obj));
				}
			}
			if (depth >= 4)
			{
				if (node.childCount > 0)
				{
					StaveboundCommand.Echo(context, $"{text}  ... {node.childCount} more child object(s), deeper than this dump goes");
				}
			}
			else
			{
				for (int num = 0; num < node.childCount; num++)
				{
					Describe(context, node.GetChild(num), depth + 1);
				}
			}
		}

		private static string DescribeColours(Material material)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			string[] array = new string[4] { "_Color", "_EmissionColor", "_EmissiveColor", "_TintColor" };
			foreach (string text in array)
			{
				try
				{
					if (material.HasProperty(text))
					{
						list.Add($"{text}={material.GetColor(text)}");
					}
				}
				catch (Exception)
				{
				}
			}
			if (list.Count != 0)
			{
				return "  " + string.Join(" ", list);
			}
			return string.Empty;
		}
	}
	internal sealed class PrefabPreviewCommand : StaveboundCommand
	{
		private static readonly List<GameObject> Previews = new List<GameObject>();

		public override string Name => "stave_preview";

		public override string Help => "stave_preview <prefab> - place a look-at-only copy in front of you. It is not saved, not networked and vanishes on reload. 'stave_preview clear' removes them. For anything spawnable, use vanilla spawn instead.";

		protected override void Execute(string[] args, Terminal context)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00ff: 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)
			string text = string.Join(" ", args).Trim();
			if (string.Equals(text, "clear", StringComparison.OrdinalIgnoreCase))
			{
				Clear(context);
				return;
			}
			if (text.Length == 0)
			{
				StaveboundCommand.Echo(context, "Stavebound: name a prefab, or 'clear' to remove the previews.");
				return;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no world loaded.");
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no prefab called \"" + text + "\". Try stave_prefabs " + text);
			}
			else if ((Object)(object)prefab.GetComponent<ZNetView>() != (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: " + ((Object)prefab).name + " is a networked object - use 'spawn " + ((Object)prefab).name + "' instead, which places it properly and can be removed with the hammer.");
			}
			else
			{
				Transform transform = ((Component)Player.m_localPlayer).transform;
				Vector3 val = transform.position + transform.forward * 3f;
				GameObject val2 = Object.Instantiate<GameObject>(prefab, val, transform.rotation);
				((Object)val2).name = "stave_preview_" + ((Object)prefab).name;
				Previews.Add(val2);
				StaveboundCommand.Echo(context, "Stavebound: previewing " + ((Object)prefab).name + " in front of you " + $"({Previews.Count} up). This is scenery only - nothing was added to the world.");
			}
		}

		private static void Clear(Terminal context)
		{
			int num = Previews.Count((GameObject p) => (Object)(object)p != (Object)null);
			foreach (GameObject item in Previews.Where((GameObject p) => (Object)(object)p != (Object)null))
			{
				Object.Destroy((Object)(object)item);
			}
			Previews.Clear();
			StaveboundCommand.Echo(context, (num == 0) ? "Stavebound: nothing was being previewed." : $"Stavebound: removed {num} preview(s).");
		}
	}
	internal sealed class PrefabSearchCommand : StaveboundCommand
	{
		private const int Limit = 60;

		public override string Name => "stave_prefabs";

		public override string Help => "stave_prefabs <text>[,<text>...] - list loaded prefabs whose name contains each term, marking which are buildable pieces and which are items. Comma-separate to search for several things at once.";

		protected override void Execute(string[] args, Terminal context)
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no world loaded - prefabs only exist once you are in a game.");
				return;
			}
			string[] array = (from t in string.Join(" ", args).Split(new char[1] { ',' })
				select t.Trim() into t
				where t.Length > 0
				select t).ToArray();
			if (array.Length == 0)
			{
				StaveboundCommand.Echo(context, "Stavebound: give me something to search for, e.g. stave_prefabs trophy,sconce,brazier");
				return;
			}
			string[] array2 = array;
			foreach (string term in array2)
			{
				Search(context, term);
			}
		}

		private static void Search(Terminal context, string term)
		{
			List<GameObject> list = (from p in ZNetScene.instance.m_prefabs
				where (Object)(object)p != (Object)null && ((Object)p).name.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0
				orderby ((Object)p).name
				select p).ToList();
			if (list.Count == 0)
			{
				StaveboundCommand.Echo(context, "\"" + term + "\": nothing.");
				return;
			}
			StaveboundCommand.Echo(context, $"\"{term}\": {list.Count}" + ((list.Count > 60) ? $" (showing {60})" : string.Empty));
			foreach (GameObject item in list.Take(60))
			{
				List<string> list2 = new List<string>();
				if ((Object)(object)item.GetComponent<Piece>() != (Object)null)
				{
					list2.Add("piece");
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				if (component?.m_itemData?.m_shared != null)
				{
					list2.Add("item \"" + Localization.instance.Localize(component.m_itemData.m_shared.m_name) + "\"");
				}
				StaveboundCommand.Echo(context, "  " + ((Object)item).name + ((list2.Count > 0) ? ("  [" + string.Join(", ", list2) + "]") : string.Empty));
			}
		}
	}
	internal static class TierMap
	{
		private static readonly Dictionary<string, Clearance> Required = new Dictionary<string, Clearance>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<string> Unclassified = new List<string>();

		private static readonly Dictionary<string, string> DisplayTokens = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		internal static bool Built { get; private set; }

		internal static int BlockedCount => Required.Count;

		internal static IReadOnlyList<string> UnclassifiedItems => Unclassified;

		internal static Clearance RequiredFor(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return Clearance.None;
			}
			if (!Required.TryGetValue(prefabName, out var value))
			{
				return Clearance.None;
			}
			return value;
		}

		internal static IEnumerable<string> AtTier(Clearance tier)
		{
			return from pair in Required
				where pair.Value == tier
				select pair.Key into name
				orderby name
				select name;
		}

		internal static string Describe(string prefabName)
		{
			if (!DisplayTokens.TryGetValue(prefabName, out var value) || string.IsNullOrEmpty(value))
			{
				return prefabName;
			}
			string text = ((Localization.instance != null) ? Localization.instance.Localize(value) : value);
			if (!string.Equals(text, prefabName, StringComparison.OrdinalIgnoreCase))
			{
				return prefabName + " (" + text + ")";
			}
			return prefabName;
		}

		internal static void Build()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_items == null || instance.m_items.Count == 0)
			{
				return;
			}
			Required.Clear();
			Unclassified.Clear();
			DisplayTokens.Clear();
			Dictionary<string, Clearance> dictionary = ReadConfiguredTiers();
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null && !component.m_itemData.m_shared.m_teleportable)
				{
					string name = ((Object)item).name;
					DisplayTokens[name] = component.m_itemData.m_shared.m_name;
					if (dictionary.TryGetValue(name, out var value))
					{
						Required[name] = value;
						continue;
					}
					Required[name] = Clearance.Ashen;
					Unclassified.Add(name);
				}
			}
			Built = true;
			Logger.LogInfo((object)$"Tier map built: {Required.Count} blocked item(s) across {instance.m_items.Count} in ObjectDB.");
			if (Unclassified.Count > 0)
			{
				Logger.LogWarning((object)($"{Unclassified.Count} blocked item(s) are not in the tier config and default to " + $"{Clearance.Ashen} ({Clearance.Ashen.StaveName()}): " + string.Join(", ", Unclassified.OrderBy((string n) => n)) + ". Add them to the 2 - Clearance section to place them properly."));
			}
		}

		private static Dictionary<string, Clearance> ReadConfiguredTiers()
		{
			Dictionary<string, Clearance> dictionary = new Dictionary<string, Clearance>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<Clearance, string> item in StaveboundConfig.TierPrefabs())
			{
				string[] array = item.Value.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0)
					{
						if (dictionary.TryGetValue(text, out var value) && value != item.Key)
						{
							Logger.LogWarning((object)($"'{text}' is listed under both {value} and {item.Key}. Using {value}; " + "remove one of them."));
						}
						else
						{
							dictionary[text] = item.Key;
						}
					}
				}
			}
			return dictionary;
		}
	}
}
namespace Stavebound.Staves
{
	internal static class CoreTint
	{
		private static readonly string[] ColourProperties = new string[3] { "_EmissionColor", "_Color", "_TintColor" };

		internal static void Apply(GameObject prefab, Color tint, string describe)
		{
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			List<string> list = new List<string>();
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val2 = sharedMaterials[j];
					if ((Object)(object)val2 == (Object)null || !IsEmissive(val2))
					{
						continue;
					}
					Material val3 = new Material(val2)
					{
						name = ((Object)val2).name + "_" + describe
					};
					string[] colourProperties = ColourProperties;
					foreach (string text in colourProperties)
					{
						if (val3.HasProperty(text))
						{
							val3.SetColor(text, (text == "_EmissionColor") ? AtIntensityOf(tint, val2.GetColor(text)) : tint);
						}
					}
					sharedMaterials[j] = val3;
					flag = true;
				}
				if (flag)
				{
					val.sharedMaterials = sharedMaterials;
					list.Add("material on " + ((Object)val).name);
				}
			}
			Light[] componentsInChildren2 = prefab.GetComponentsInChildren<Light>(true);
			foreach (Light val4 in componentsInChildren2)
			{
				val4.color = tint;
				list.Add("light on " + ((Object)val4).name);
			}
			ParticleSystem[] componentsInChildren3 = prefab.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val5 in componentsInChildren3)
			{
				MainModule main = val5.main;
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(tint);
				list.Add("particles on " + ((Object)val5).name);
			}
			Logger.LogInfo((object)((list.Count == 0) ? ("Tint " + describe + ": found nothing emissive on " + ((Object)prefab).name + " - the glow is not where this expects it.") : string.Format("Tint {0} on {1}: {2} target(s) - {3}", describe, ((Object)prefab).name, list.Count, string.Join(", ", list.Distinct()))));
		}

		private static Color AtIntensityOf(Color tint, Color original)
		{
			//IL_0020: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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)
			float maxColorComponent = ((Color)(ref original)).maxColorComponent;
			float maxColorComponent2 = ((Color)(ref tint)).maxColorComponent;
			if (maxColorComponent <= 0f || maxColorComponent2 <= 0f)
			{
				return tint;
			}
			float num = maxColorComponent / maxColorComponent2;
			return new Color(tint.r * num, tint.g * num, tint.b * num, tint.a);
		}

		private static bool IsEmissive(Material material)
		{
			//IL_002f: 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)
			if (material.IsKeywordEnabled("_EMISSION") || material.IsKeywordEnabled("_EMISSIVE"))
			{
				return true;
			}
			if (material.HasProperty("_EmissionColor"))
			{
				Color color = material.GetColor("_EmissionColor");
				return ((Color)(ref color)).maxColorComponent > 0.01f;
			}
			return false;
		}
	}
	internal static class PlacementFeedback
	{
		private const float SearchInterval = 0.2f;

		private static readonly Vector3 BeamLift = Vector3.up * 1.2f;

		private static GameObject _circle;

		private static CircleProjector _projector;

		private static GameObject _beam;

		private static bool _beamUnavailable;

		private static float _nextSearch;

		private static readonly List<TeleportWorld> InRange = new List<TeleportWorld>();

		private static string _lastAnswer;

		internal static void Update(GameObject ghost)
		{
			//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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ghost == (Object)null || !IsOurs(ghost))
			{
				Hide();
				return;
			}
			Vector3 position = ghost.transform.position;
			float value = StaveboundConfig.StaveRadius.Value;
			Search(position, value);
			bool num = StaveboundConfig.Binding.Value == PortalBinding.AllInRadius;
			TeleportWorld val = ((InRange.Count > 0) ? InRange[0] : null);
			if (!num && (Object)(object)val != (Object)null)
			{
				ShowBeam(position + BeamLift, ((Component)val).transform.position + BeamLift);
				HideCircle();
			}
			else
			{
				ShowCircle(position, value);
				HideBeam();
			}
			Announce(num);
		}

		internal static void Reset()
		{
			if ((Object)(object)_circle != (Object)null)
			{
				Object.Destroy((Object)(object)_circle);
			}
			if ((Object)(object)_beam != (Object)null)
			{
				Object.Destroy((Object)(object)_beam);
			}
			_circle = null;
			_projector = null;
			_beam = null;
			_beamUnavailable = false;
			_lastAnswer = null;
			_nextSearch = 0f;
			InRange.Clear();
		}

		private static bool IsOurs(GameObject ghost)
		{
			return StavePieces.ClearanceOf(((Object)ghost).name.Replace("(Clone)", string.Empty).Trim()) != Clearance.None;
		}

		private static void Search(Vector3 at, float radius)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextSearch)
			{
				return;
			}
			_nextSearch = Time.time + 0.2f;
			float limit = radius * radius;
			InRange.Clear();
			InRange.AddRange(Object.FindObjectsByType<TeleportWorld>((FindObjectsSortMode)0).Where(delegate(TeleportWorld p)
			{
				//IL_000f: 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_001f: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)p != (Object)null)
				{
					Vector3 val = ((Component)p).transform.position - at;
					return ((Vector3)(ref val)).sqrMagnitude <= limit;
				}
				return false;
			}).OrderBy(delegate(TeleportWorld p)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)p).transform.position - at;
				return ((Vector3)(ref val)).sqrMagnitude;
			}));
		}

		private static void ShowBeam(Vector3 from, Vector3 to)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_beam == (Object)null && !BuildBeam(from))
			{
				return;
			}
			Vector3 val = to - from;
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.001f))
			{
				_beam.transform.position = from;
				_beam.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized);
				_beam.transform.localScale = new Vector3(1f, 1f, ((Vector3)(ref val)).magnitude);
				if (!_beam.activeSelf)
				{
					_beam.SetActive(true);
				}
			}
		}

		private static bool BuildBeam(Vector3 at)
		{
			//IL_0054: 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)
			if (_beamUnavailable)
			{
				return false;
			}
			StationExtension val = ((IEnumerable<StationExtension>)Resources.FindObjectsOfTypeAll<StationExtension>()).FirstOrDefault((Func<StationExtension, bool>)((StationExtension e) => (Object)(object)e != (Object)null && (Object)(object)e.m_connectionPrefab != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				_beamUnavailable = true;
				Logger.LogWarning((object)"No station-connection effect to borrow, so stave binding will be shown as a circle instead of a beam.");
				return false;
			}
			_beam = Object.Instantiate<GameObject>(val.m_connectionPrefab, at, Quaternion.identity);
			((Object)_beam).name = "stave_binding_beam";
			return true;
		}

		private static void HideBeam()
		{
			if ((Object)(object)_beam != (Object)null && _beam.activeSelf)
			{
				_beam.SetActive(false);
			}
		}

		private static void ShowCircle(Vector3 at, float radius)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_projector == (Object)null) || BuildCircle())
			{
				_circle.transform.position = at;
				_projector.m_radius = radius;
				if (!_circle.activeSelf)
				{
					_circle.SetActive(true);
				}
			}
		}

		private static bool BuildCircle()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			CircleProjector val = ((IEnumerable<CircleProjector>)Resources.FindObjectsOfTypeAll<CircleProjector>()).FirstOrDefault((Func<CircleProjector, bool>)((CircleProjector c) => (Object)(object)c != (Object)null && (Object)(object)c.m_prefab != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning((object)"No CircleProjector to borrow, so stave range will not be drawn. The portal it binds to is still named on placement.");
				return false;
			}
			_circle = new GameObject("stave_range");
			_projector = _circle.AddComponent<CircleProjector>();
			_projector.m_prefab = val.m_prefab;
			_projector.m_mask = val.m_mask;
			_projector.m_nrOfSegments = val.m_nrOfSegments;
			return true;
		}

		private static void HideCircle()
		{
			if ((Object)(object)_circle != (Object)null && _circle.activeSelf)
			{
				_circle.SetActive(false);
			}
		}

		private static void Hide()
		{
			HideCircle();
			HideBeam();
			_lastAnswer = null;
		}

		private static void Announce(bool all)
		{
			string text = Describe(all);
			if (!(text == _lastAnswer))
			{
				_lastAnswer = text;
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, text, 0, (Sprite)null);
				}
			}
		}

		private static string Describe(bool all)
		{
			if (InRange.Count == 0)
			{
				return Translations.Get("stave_place_no_portal");
			}
			if (all)
			{
				if (InRange.Count != 1)
				{
					return Translations.Format("stave_place_binds_all", InRange.Count);
				}
				return Translations.Format("stave_place_binds", Name(InRange[0]));
			}
			if (InRange.Count != 1)
			{
				return Translations.Format("stave_place_binds_nearest", Name(InRange[0]), InRange.Count);
			}
			return Translations.Format("stave_place_binds", Name(InRange[0]));
		}

		private static string Name(TeleportWorld portal)
		{
			ZDO val = PortalTarget.ZdoOf(portal);
			string text = ((val != null) ? val.GetString(ZDOVars.s_tag, string.Empty) : string.Empty);
			if (!string.IsNullOrEmpty(text))
			{
				return "\"" + text + "\"";
			}
			return Translations.Get("stave_an_unnamed_portal");
		}
	}
	internal static class SiteSweep
	{
		private const float SweepSeconds = 10f;

		private static Coroutine _sweep;

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

		private static readonly Dictionary<ZDO, Clearance> PortalMasks = new Dictionary<ZDO, Clearance>();

		internal static void Start()
		{
			Stop();
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				_sweep = ((MonoBehaviour)Plugin.Instance).StartCoroutine(Run());
			}
		}

		internal static void Stop()
		{
			if (_sweep != null && (Object)(object)Plugin.Instance != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StopCoroutine(_sweep);
			}
			_sweep = null;
			Found.Clear();
			PortalMasks.Clear();
		}

		private static IEnumerator Run()
		{
			WaitForSeconds wait = new WaitForSeconds(10f);
			while (true)
			{
				yield return Recompute();
				yield return wait;
			}
		}

		private static IEnumerator Recompute()
		{
			if (ZDOMan.instance == null)
			{
				yield break;
			}
			PortalMasks.Clear();
			float radius = StaveboundConfig.StaveRadius.Value;
			bool all = StaveboundConfig.Binding.Value == PortalBinding.AllInRadius;
			foreach (KeyValuePair<string, Clearance> stave in StavePieces.Staves)
			{
				yield return Collect(stave.Key, Found);
				foreach (ZDO item in Found)
				{
					Bind(item.GetPosition(), stave.Value, radius, all);
				}
			}
			if (StaveboundConfig.StrictLadder.Value)
			{
				foreach (ZDO item2 in new List<ZDO>(PortalMasks.Keys))
				{
					PortalMasks[item2] = ClearanceExtensions.UpToFirstGap(PortalMasks[item2]);
				}
			}
			WriteChangedMasks();
		}

		private static void Bind(Vector3 runePosition, Clearance tier, float radius, bool all)
		{
			//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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			List<ZDO> list = ((instance != null) ? instance.GetPortals() : null);
			if (list == null)
			{
				return;
			}
			float num = radius * radius;
			ZDO val = null;
			float num2 = num;
			foreach (ZDO item in list)
			{
				if (item == null || !item.IsValid())
				{
					continue;
				}
				Vector3 val2 = item.GetPosition() - runePosition;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				if (!(sqrMagnitude > num))
				{
					if (all)
					{
						Grant(item, tier);
					}
					else if (sqrMagnitude <= num2)
					{
						num2 = sqrMagnitude;
						val = item;
					}
				}
			}
			if (!all && val != null)
			{
				Grant(val, tier);
			}
		}

		private static IEnumerator Collect(string prefab, List<ZDO> into)
		{
			into.Clear();
			int index = 0;
			bool complete = false;
			while (!complete)
			{
				if (ZDOMan.instance == null)
				{
					into.Clear();
					break;
				}
				complete = ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefab, into, ref index);
				yield return null;
			}
		}

		private static void Grant(ZDO portal, Clearance tier)
		{
			PortalMasks[portal] = (PortalMasks.TryGetValue(portal, out var value) ? (value | tier) : tier);
		}

		private static void WriteChangedMasks()
		{
			ZDOMan instance = ZDOMan.instance;
			List<ZDO> list = ((instance != null) ? instance.GetPortals() : null);
			if (list == null)
			{
				return;
			}
			foreach (ZDO item in list)
			{
				if (item != null && item.IsValid())
				{
					PortalMasks.TryGetValue(item, out var value);
					Write(item, value);
				}
			}
		}

		private static void Write(ZDO portal, Clearance mask)
		{
			if (portal.GetInt(ZdoKeys.ClearanceMask, 0) != (int)mask)
			{
				PortalTarget.Claim(portal);
				portal.Set(ZdoKeys.ClearanceMask, (int)mask, false);
				PortalTarget.Publish(portal);
				string text = portal.GetString(ZDOVars.s_tag, string.Empty);
				Logger.LogInfo((object)("[site] Clearance of " + (string.IsNullOrEmpty(text) ? "an unnamed portal" : ("\"" + text + "\"")) + " is now " + Describe(mask) + "."));
			}
		}

		private static string Describe(Clearance mask)
		{
			if (mask == Clearance.None)
			{
				return "nothing";
			}
			List<string> list = new List<string>();
			Clearance[] ladder = ClearanceExtensions.Ladder;
			for (int i = 0; i < ladder.Length; i++)
			{
				Clearance clearance = ladder[i];
				if ((mask & clearance) == clearance)
				{
					list.Add(clearance.ToString());
				}
			}
			return string.Join(" + ", list);
		}
	}
	internal static class StavePieces
	{
		private sealed class PieceSpec
		{
			internal string Name;

			internal string Display;

			internal string Description;

			internal Color Tint;

			internal RequirementConfig[] Requirements;
		}

		private const string CloneSource = "Pickable_BlackCoreStand";

		private const string TrophyElder = "TrophyTheElder";

		private const string TrophyBonemass = "TrophyBonemass";

		private const string TrophyModer = "TrophyDragonQueen";

		private const string TrophyYagluth = "TrophyGoblinKing";

		private const string TrophyQueen = "TrophySeekerQueen";

		private const string TrophyFader = "TrophyFader";

		private static readonly List<PieceSpec> Specs = new List<PieceSpec>
		{
			new PieceSpec
			{
				Name = "stave_elder",
				Display = "stave_rune_elder",
				Description = "stave_desc_elder",
				Tint = new Color(0.95f, 0.8f, 0.15f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophyTheElder",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Copper",
						Amount = 10,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			},
			new PieceSpec
			{
				Name = "stave_bonemass",
				Display = "stave_rune_bonemass",
				Description = "stave_desc_bonemass",
				Tint = new Color(0.25f, 0.85f, 0.3f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophyBonemass",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Iron",
						Amount = 10,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			},
			new PieceSpec
			{
				Name = "stave_moder",
				Display = "stave_rune_moder",
				Description = "stave_desc_moder",
				Tint = new Color(0.75f, 0.85f, 1f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophyDragonQueen",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Silver",
						Amount = 10,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			},
			new PieceSpec
			{
				Name = "stave_yagluth",
				Display = "stave_rune_yagluth",
				Description = "stave_desc_yagluth",
				Tint = new Color(0.45f, 0.15f, 0.65f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophyGoblinKing",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "BlackMetal",
						Amount = 10,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			},
			new PieceSpec
			{
				Name = "stave_queen",
				Display = "stave_rune_queen",
				Description = "stave_desc_queen",
				Tint = new Color(0.4f, 0.95f, 0.95f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophySeekerQueen",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "DvergrNeedle",
						Amount = 3,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			},
			new PieceSpec
			{
				Name = "stave_ashen",
				Display = "stave_rune_ashen",
				Description = "stave_desc_ashen",
				Tint = new Color(1f, 0.35f, 0.1f),
				Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
				{
					new RequirementConfig
					{
						Item = "TrophyFader",
						Amount = 1,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "FlametalNew",
						Amount = 10,
						Recover = true
					},
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 20,
						Recover = true
					}
				}
			}
		};

		private static readonly Dictionary<string, Clearance> Granted = new Dictionary<string, Clearance>
		{
			{
				"stave_elder",
				Clearance.Elder
			},
			{
				"stave_bonemass",
				Clearance.Bonemass
			},
			{
				"stave_moder",
				Clearance.Moder
			},
			{
				"stave_yagluth",
				Clearance.Yagluth
			},
			{
				"stave_queen",
				Clearance.Queen
			},
			{
				"stave_ashen",
				Clearance.Ashen
			}
		};

		internal static IEnumerable<KeyValuePair<string, Clearance>> Staves => Granted;

		internal static Clearance ClearanceOf(string prefabName)
		{
			if (!Granted.TryGetValue(prefabName, out var value))
			{
				return Clearance.None;
			}
			return value;
		}

		internal static void Register()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Create;
		}

		private static void Create()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Create;
			if ((Object)(object)PrefabManager.Instance.GetPrefab("Pickable_BlackCoreStand") == (Object)null)
			{
				Logger.LogError((object)"'Pickable_BlackCoreStand' is missing, so no staves can be built. A game update may have renamed it — run stave_prefabs corestand to find its new name.");
				return;
			}
			foreach (PieceSpec spec in Specs)
			{
				try
				{
					CreateOne(spec);
				}
				catch (Exception arg)
				{
					Logger.LogError((object)$"Could not create {spec.Name}: {arg}");
				}
			}
		}

		private static void CheckRequirements(PieceSpec spec)
		{
			RequirementConfig[] requirements = spec.Requirements;
			foreach (RequirementConfig val in requirements)
			{
				if ((Object)(object)PrefabManager.Instance.GetPrefab(val.Item) == (Object)null)
				{
					Logger.LogError((object)(spec.Name + " needs '" + val.Item + "', which does not exist. That piece will be unbuildable until the name is corrected - try stave_prefabs to find it."));
				}
			}
		}

		private static void CreateOne(PieceSpec spec)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			CheckRequirements(spec);
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(spec.Name, "Pickable_BlackCoreStand");
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)("Could not clone Pickable_BlackCoreStand for " + spec.Name + "."));
				return;
			}
			Pickable component = val.GetComponent<Pickable>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			MakeBuildable(val);
			CoreTint.Apply(val, spec.Tint, spec.Name);
			CustomPiece val2 = new CustomPiece(val, false, new PieceConfig
			{
				Name = "$" + spec.Display,
				Description = "$" + spec.Description,
				PieceTable = PieceTables.Hammer,
				Category = "Misc",
				Icon = RenderIcon(val, spec.Name),
				Requirements = spec.Requirements
			});
			if (!val2.IsValid())
			{
				Logger.LogError((object)(spec.Name + " did not come out valid and will not be registered."));
				return;
			}
			PieceManager.Instance.AddPiece(val2);
			Logger.LogInfo((object)("Registered piece " + spec.Name + "."));
		}

		private static Sprite RenderIcon(GameObject prefab, string describe)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0038: Expected O, but got Unknown
			try
			{
				Sprite obj = RenderManager.Instance.Render(new RenderRequest(prefab)
				{
					Rotation = RenderManager.IsometricRotation,
					Width = 128,
					Height = 128,
					UseCache = false
				});
				if ((Object)(object)obj == (Object)null)
				{
					Logger.LogWarning((object)("No icon could be rendered for " + describe + "."));
				}
				return obj;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Icon render failed for " + describe + ": " + ex.Message));
				return null;
			}
		}

		private static void MakeBuildable(GameObject prefab)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Piece obj = prefab.GetComponent<Piece>() ?? prefab.AddComponent<Piece>();
			obj.m_canBeRemoved = true;
			obj.m_canRotate = true;
			obj.m_allowedInDungeons = false;
			obj.m_groundPiece = false;
			obj.m_groundOnly = false;
			obj.m_noInWater = true;
			if (!((Object)(object)prefab.GetComponent<WearNTear>() != (Object)null))
			{
				WearNTear obj2 = prefab.AddComponent<WearNTear>();
				obj2.m_health = 500f;
				obj2.m_burnable = false;
				obj2.m_noRoofWear = true;
				obj2.m_noSupportWear = true;
				obj2.m_materialType = (MaterialType)1;
			}
		}
	}
}
namespace Stavebound.Portals
{
	internal sealed class PortalAimCommand : StaveboundCommand
	{
		private const float Range = 10f;

		public override string Name => "stave_aim";

		public override string Help => "stave_aim <destination name> - point the nearest portal at the portal with that name. With no arguments, clears the target and hands the portal back to vanilla tag pairing.";

		protected override void Execute(string[] args, Terminal context)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no player.");
				return;
			}
			TeleportWorld val = PortalTarget.FindNearest(((Component)Player.m_localPlayer).transform.position, 10f);
			if ((Object)(object)val == (Object)null)
			{
				StaveboundCommand.Echo(context, $"Stavebound: no portal within {10f:F0}m. Stand at the one you want to re-aim.");
				return;
			}
			ZDO val2 = PortalTarget.ZdoOf(val);
			if (val2 == null)
			{
				StaveboundCommand.Echo(context, "Stavebound: that portal is not ready yet.");
				return;
			}
			string text = val2.GetString(ZDOVars.s_tag, string.Empty);
			text = (string.IsNullOrEmpty(text) ? "the unnamed portal" : ("\"" + text + "\""));
			string wanted = string.Join(" ", args).Trim();
			if (wanted.Length == 0)
			{
				PortalTarget.Clear(val2);
				StaveboundCommand.Echo(context, "Stavebound: " + text + " now follows vanilla tag pairing again.");
				return;
			}
			List<PortalRecord> list = PortalRegistry.All.Where((PortalRecord p) => string.Equals(p.Name, wanted, StringComparison.OrdinalIgnoreCase)).ToList();
			if (list.Count == 0)
			{
				StaveboundCommand.Echo(context, "Stavebound: no portal named \"" + wanted + "\". Try stave_portals for the list.");
				return;
			}
			if (list.Count > 1)
			{
				StaveboundCommand.Echo(context, $"Stavebound: {list.Count} portals are named \"{wanted}\". Rename one, or wait for the map selector.");
				return;
			}
			PortalRecord portalRecord = list[0];
			if (portalRecord.Pid == PortalTarget.GetPid(val2))
			{
				StaveboundCommand.Echo(context, "Stavebound: a portal cannot point at itself.");
				return;
			}
			PortalTarget.Set(val2, portalRecord.Pid);
			StaveboundCommand.Echo(context, $"Stavebound: {text} now points at {portalRecord}. Nothing was written to the far side - walk back and you will not return here unless it points at you.");
		}

		public override List<string> CommandOptionList()
		{
			return (from n in (from p in PortalRegistry.All
					select p.Name into n
					where !string.IsNullOrEmpty(n)
					select n).Distinct()
				orderby n
				select n).ToList();
		}
	}
	internal sealed class PortalNetCommand : StaveboundCommand
	{
		public override string Name => "stave_net";

		public override string Help => "Report the portal registry's network state: role, peers, traffic, and whether every destination resolves. Run on the server and the client and compare.";

		protected override void Execute(string[] args, Terminal context)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				StaveboundCommand.Echo(context, "Stavebound: no world loaded, so there is no sync to report on.");
				return;
			}
			bool num = ZNet.instance.IsServer();
			bool flag = ZNet.instance.IsDedicated();
			List<ZNetPeer> list = ZNet.instance.GetConnectedPeers() ?? new List<ZNetPeer>();
			string text = ((!num) ? "CLIENT" : (flag ? "DEDICATED SERVER" : ((list.Count > 0) ? "HOST (server + client)" : "SINGLE PLAYER (own server)")));
			StaveboundCommand.Echo(context, "Stavebound sync - " + text);
			StaveboundCommand.Echo(context, $"  connected peers: {list.Count}" + ((list.Count > 0) ? (" (" + string.Join(", ", list.Select((ZNetPeer p) => p.m_uid.ToString())) + ")") : string.Empty));
			StaveboundCommand.Echo(context, "  " + PortalRegistry.Traffic);
			IReadOnlyList<PortalRecord> all = PortalRegistry.All;
			StaveboundCommand.Echo(context, $"  portals known: {all.Count}");
			if (num && list.Count == 0)
			{
				StaveboundCommand.Echo(context, "  Note: nothing is broadcast with no peers connected, so an absence of traffic here is expected rather than a fault.");
			}
			if (!num && all.Count == 0)
			{
				StaveboundCommand.Echo(context, "  A client with no portals has not been told anything. Either the server is not running Stavebound, or the initial sync did not arrive.");
				return;
			}
			List<PortalRecord> list2 = all.Where((PortalRecord p) => p.TargetPid != 0).ToList();
			PortalRecord record;
			List<PortalRecord> list3 = list2.Where((PortalRecord p) => !PortalRegistry.TryGet(p.TargetPid, out record)).ToList();
			StaveboundCommand.Echo(context, $"  re-aimed: {list2.Count}, of which {list3.Count} point at something " + "this instance does not know about");
			foreach (PortalRecord item in list3)
			{
				StaveboundCommand.Echo(context, $"    {item} -> pid {item.TargetPid}, unresolved");
			}
		}
	}
	internal readonly struct PortalRecord : IEquatable<PortalRecord>
	{
		internal long Pid { get; }

		internal ZDOID Id { get; }

		internal string Name { get; }

		internal Vector3 Position { get; }

		internal long TargetPid { get; }

		internal int ClearanceMask { get; }

		internal PortalRecord(long pid, ZDOID id, string name, Vector3 position, long targetPid, int clearanceMask)
		{
			//IL_0008: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			Pid = pid;
			Id = id;
			Name = name ?? string.Empty;
			Position = position;
			TargetPid = targetPid;
			ClearanceMask = clearanceMask;
		}

		internal void WriteTo(ZPackage package)
		{
			//IL_000e: 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)
			package.Write(Pid);
			package.Write(Id);
			package.Write(Name);
			package.Write(Position);
			package.Write(TargetPid);
			package.Write(ClearanceMask);
		}

		internal static PortalRecord ReadFrom(ZPackage package)
		{
			//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_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_002a: 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)
			long pid = package.ReadLong();
			ZDOID id = package.ReadZDOID();
			string name = package.ReadString();
			Vector3 position = package.ReadVector3();
			long targetPid = package.ReadLong();
			int clearanceMask = package.ReadInt();
			return new PortalRecord(pid, id, name, position, targetPid, clearanceMask);
		}

		public bool Equals(PortalRecord other)
		{
			//IL_0010: 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_0057: 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)
			if (Pid == other.Pid && Id == other.Id && TargetPid == other.TargetPid && ClearanceMask == other.ClearanceMask && string.Equals(Name, other.Name, StringComparison.Ordinal))
			{
				return Position == other.Position;
			}
			return false;
		}

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

		public override int GetHashCode()
		{
			return Pid.GetHashCode();
		}

		public override string ToString()
		{
			if (!string.IsNullOrEmpty(Name))
			{
				return "\"" + Name + "\"";
			}
			return "(unnamed portal)";
		}
	}
	internal static class PortalRegistry
	{
		private const byte WireVersion = 2;

		private const float ServerSweepSeconds = 2f;

		private static readonly List<PortalRecord> Ordered = new List<PortalRecord>();

		private static readonly Dictionary<long, PortalRecord> ByPid = new Dictionary<long, PortalRecord>();

		private static readonly List<PortalRecord> SweepBuffer = new List<PortalRecord>();

		private static readonly HashSet<long> SweepPids = new HashSet<long>();

		private static CustomRPC _rpc;

		private static Coroutine _sweep;

		private static int _lastSentBytes;

		private static int _lastReceivedBytes;

		private static int _receiveCount;

		internal static string Traffic => $"packages received: {_receiveCount}" + ((_receiveCount > 0) ? $" (last {_lastReceivedBytes} bytes)" : string.Empty) + ((_lastSentBytes > 0) ? $"; last broadcast {_lastSentBytes} bytes" : "; nothing broadcast yet");

		internal static IReadOnlyList<PortalRecord> All => Ordered;

		internal static bool TryGet(long pid, out PortalRecord record)
		{
			return ByPid.TryGetValue(pid, out record);
		}

		internal static void Register()
		{
			//IL_0011: 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_0027: Expected O, but got Unknown
			//IL_0027: Expected O, but got Unknown
			_rpc = NetworkManager.Instance.AddRPC("stave_portals", new CoroutineHandler(OnServerReceive), new CoroutineHandler(OnClientReceive));
			SynchronizationManager.Instance.AddInitialSynchronization(_rpc, (Func<ZPackage>)BuildSnapshotForJoiningClient);
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PortalRegistryCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PortalAimCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PortalNetCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BlockedItemsCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PrefabSearchCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PrefabInspectCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PrefabPreviewCommand());
		}

		internal static void OnWorldStart()
		{
			Clear();
			if (!IsServer())
			{
				SyncLog.Say("World started as a CLIENT. Waiting for the server's portal list; nothing will be known until it arrives.");
				return;
			}
			bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated();
			SyncLog.Say("World started as " + (flag ? "a DEDICATED SERVER" : "the SERVER (host or single player)") + ". " + $"Sweeping every {2f}s; broadcasting only on change.");
			_sweep = ((MonoBehaviour)Plugin.Instance).StartCoroutine(SweepRoutine());
		}

		internal static void OnWorldEnd()
		{
			if (_sweep != null)
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					((MonoBehaviour)Plugin.Instance).StopCoroutine(_sweep);
				}
				_sweep = null;
			}
			Clear();
		}

		private static void Clear()
		{
			Ordered.Clear();
			ByPid.Clear();
			SweepBuffer.Clear();
			SweepPids.Clear();
		}

		private static bool IsServer()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}

		private static IEnumerator SweepRoutine()
		{
			WaitForSeconds wait = new WaitForSeconds(2f);
			while (true)
			{
				if (RebuildFromWorld())
				{
					Broadcast();
				}
				yield return wait;
			}
		}

		private static bool RebuildFromWorld()
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			SweepBuffer.Clear();
			SweepPids.Clear();
			ZDOMan instance = ZDOMan.instance;
			List<ZDO> list = ((instance != null) ? instance.GetPortals() : null);
			if (list == null)
			{
				return false;
			}
			foreach (ZDO item in list)
			{
				if (item == null || !item.IsValid())
				{
					continue;
				}
				long pid = PortalTarget.GetPid(item);
				long num = PortalTarget.EnsurePid(item, SweepPids.Contains);
				if (num != 0L)
				{
					if (pid != num)
					{
						SyncLog.Say((pid == 0L) ? $"Minted pid {num} for a portal at {item.GetPosition().x:F0},{item.GetPosition().z:F0}." : $"Re-minted pid {pid} -> {num}: another portal already claimed it.");
					}
					SweepPids.Add(num);
					SweepBuffer.Add(new PortalRecord(num, item.m_uid, item.GetString(ZDOVars.s_tag, string.Empty), item.GetPosition(), PortalTarget.GetDestination(item), item.GetInt(ZdoKeys.ClearanceMask, 0)));
				}
			}
			if (Matches(SweepBuffer))
			{
				return false;
			}
			Apply(SweepBuffer);
			return true;
		}

		private static bool Matches(List<PortalRecord> candidate)
		{
			if (candidate.Count != Ordered.Count)
			{
				return false;
			}
			for (int i = 0; i < candidate.Count; i++)
			{
				if (!candidate[i].Equals(Ordered[i]))
				{
					return false;
				}
			}
			return true;
		}

		private static void Broadcast()
		{
			ZNet instance = ZNet.instance;
			List<ZNetPeer> list = ((instance != null) ? instance.GetConnectedPeers() : null);
			if (list == null || list.Count == 0)
			{
				SyncLog.Say($"Change not broadcast - no connected peers. {Ordered.Count} portal(s) held locally.");
				return;
			}
			if (_rpc == null)
			{
				SyncLog.Warn("No RPC registered; connected clients will never receive the portal list.");
				return;
			}
			ZPackage val = BuildSnapshot();
			_lastSentBytes = val.Size();
			SyncLog.Say($"Broadcasting {Ordered.Count} portal(s), {_lastSentBytes} bytes, to {list.Count} peer(s): " + string.Join(", ", list.Select((ZNetPeer p) => p.m_uid.ToString())));
			_rpc.SendPackage(list, val);
		}

		private static ZPackage BuildSnapshotForJoiningClient()
		{
			RebuildFromWorld();
			ZPackage val = BuildSnapshot();
			SyncLog.Say($"A client is joining - sending the initial portal list: {Ordered.Count} portal(s), " + $"{val.Size()} bytes. This is guaranteed to arrive before they load in.");
			return val;
		}

		private static ZPackage BuildSnapshot()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)2);
			val.Write(Ordered.Count);
			foreach (PortalRecord item in Ordered)
			{
				item.WriteTo(val);
			}
			return val;
		}

		private static IEnumerator OnClientReceive(long sender, ZPackage package)
		{
			int num = package.Size();
			byte b = package.ReadByte();
			if (b != 2)
			{
				Logger.LogError((object)($"Ignoring a portal list in wire format {b}; this build speaks {(byte)2}. " + "The server and this client are running different Stavebound builds - portal destinations will not work until they match."));
				yield break;
			}
			int num2 = package.ReadInt();
			List<PortalRecord> list = new List<PortalRecord>(num2);
			for (int i = 0; i < num2; i++)
			{
				list.Add(PortalRecord.ReadFrom(package));
			}
			SyncLog.Say($"Received {num2} portal(s) from the server (peer {sender}), {num} bytes, wire v{b}.");
			_lastReceivedBytes = num;
			_receiveCount++;
			Apply(list);
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				SyncLog.Say("Player has not spawned yet, so this is the join-time sync.");
				yield break;
			}
			Vector3 here = ((Component)Player.m_localPlayer).transform.position;
			int num3 = list.Count((PortalRecord p) => Vector3.Distance(here, p.Position) > 200f);
			SyncLog.Say($"Of those, {num3} are more than 200m away - portals this client could not " + "see for itself, which is what the registry exists to deliver.");
		}

		private static IEnumerator OnServerReceive(long sender, ZPackage package)
		{
			if (package != null && package.Size() > 0)
			{
				Logger.LogWarning((object)$"Discarding an unexpected portal-registry package from peer {sender}.");
			}
			yield break;
		}

		private static void Apply(List<PortalRecord> records)
		{
			if (StaveboundConfig.LogNetworkSync != null && StaveboundConfig.LogNetworkSync.Value)
			{
				SyncLog.Say($"Registry {Ordered.Count} -> {records.Count} portal(s): " + SyncLog.Difference(Ordered, records));
			}
			Ordered.Clear();
			ByPid.Clear();
			foreach (PortalRecord record in records)
			{
				Ordered.Add(record);
				ByPid[record.Pid] = record;
			}
		}
	}
	internal sealed class PortalRegistryCommand : StaveboundCommand
	{
		public override string Name => "stave_portals";

		public override string Help => "List the portals Stavebound knows about, nearest first. On a client this is what the server has told you, not what is loaded around you.";

		protected override void Execute(string[] args, Terminal context)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<PortalRecord> all = PortalRegistry.All;
			string text = (((Object)(object)ZNet.instance == (Object)null) ? "no world loaded" : (ZNet.instance.IsServer() ? "server" : "client"));
			if (all.Count == 0)
			{
				StaveboundCommand.Echo(context, "Stavebound (" + text + "): no portals known.");
				return;
			}
			Vector3 from = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero);
			StaveboundCommand.Echo(context, $"Stavebound ({text}): {all.Count} portal(s).");
			foreach (PortalRecord item in all.OrderBy((PortalRecord p) => Vector3.Distance(from, p.Position)))
			{
				PortalRecord record;
				string arg = ((item.TargetPid == 0L) ? "vanilla tag pairing" : (PortalRegistry.TryGet(item.TargetPid, out record) ? record.ToString() : "a portal that no longer exists"));
				StaveboundCommand.Echo(context, $"  {item} at {item.Position.x:F0},{item.Position.z:F0} " + $"({Vector3.Distance(from, item.Position):F0}m) " + $"-> {arg}, clearance 0x{item.ClearanceMask:X}");
			}
		}
	}
	internal static class PortalTarget
	{
		private static readonly FieldRef<TeleportWorld, ZNetView> NetView = AccessTools.FieldRefAccess<TeleportWorld, ZNetView>("m_nview");

		internal const long NoPid = 0L;

		internal static long GetPid(ZDO zdo)
		{
			if (zdo != null)
			{
				return zdo.GetLong(ZdoKeys.Pid, 0L);
			}
			return 0L;
		}

		internal static long GetDestination(ZDO zdo)
		{
			if (zdo != null)
			{
				return zdo.GetLong(ZdoKeys.Destination, 0L);
			}
			return 0L;
		}

		internal static long GetDestination(TeleportWorld portal)
		{
			return GetDestination(ZdoOf(portal));
		}

		internal static ZDO ZdoOf(TeleportWorld portal)
		{
			if ((Object)(object)portal == (Object)null)
			{
				return null;
			}
			ZNetView val = NetView.Invoke(portal);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return null;
			}
			return val.GetZDO();
		}

		internal static long EnsurePid(ZDO zdo, Func<long, bool> isTaken)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null)
			{
				return 0L;
			}
			long num = GetPid(zdo);
			ZDOID zDOID = zdo.GetZDOID(ZdoKeys.LegacyTarget);
			bool flag = !((ZDOID)(ref zDOID)).IsNone();
			bool flag2 = num == 0L || isTaken(num);
			if (!flag2 && !flag)
			{
				return num;
			}
			Claim(zdo);
			if (flag2)
			{
				num = Mint();
				zdo.Set(ZdoKeys.Pid, num);
			}
			if (flag)
			{
				zdo.RemoveZDOID(ZdoKeys.LegacyTarget);
			}
			Publish(zdo);
			return num;
		}

		internal static void Set(ZDO zdo, long destinationPid)
		{
			if (zdo != null && destinationPid != 0L)
			{
				Claim(zdo);
				zdo.Set(ZdoKeys.Destination, destinationPid);
				Publish(zdo);
			}
		}

		internal static void Clear(ZDO zdo)
		{
			if (zdo != null)
			{
				Claim(zdo);
				zdo.Set(ZdoKeys.Destination, 0L);
				Publish(zdo);
			}
		}

		private static long Mint()
		{
			long num = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0);
			if (num != 0L)
			{
				return num;
			}
			return 1L;
		}

		internal static void Claim(ZDO zdo)
		{
			if (!zdo.IsOwner())
			{
				zdo.SetOwner(ZDOMan.GetSessionID());
			}
		}

		internal static void Publish(ZDO zdo)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			if (instance != null)
			{
				instance.ForceSendZDO(zdo.m_uid);
			}
		}

		internal static TeleportWorld FindNearest(Vector3 point, float range)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			TeleportWorld result = null;
			float num = range * range;
			TeleportWorld[] array = Object.FindObjectsByType<TeleportWorld>((FindObjectsSortMode)0);
			foreach (TeleportWorld val in array)
			{
				if (ZdoOf(val) != null)
				{
					Vector3 val2 = ((Component)val).transform.position - point;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (!(sqrMagnitude > num))
					{
						num = sqrMagnitude;
						result = val;
					}
				}
			}
			return result;
		}
	}
	internal static class ReaimGuard
	{
		internal static bool MayReaim(Vector3 position, out string refusal)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			refusal = null;
			switch (StaveboundConfig.Reaim.Value)
			{
			case ReaimPermission.Admin:
				if (!SynchronizationManager.Instance.PlayerIsAdmin)
				{
					refusal = Translations.Get("stave_reaim_admin_only");
					return false;
				}
				return true;
			case ReaimPermission.GuardStonePermitted:
				if (!PrivateArea.CheckAccess(position, 0f, true, false))
				{
					refusal = "$piece_noaccess";
					return false;
				}
				return true;
			default:
				return true;
			}
		}
	}
	internal abstract class StaveboundCommand : ConsoleCommand
	{
		public sealed override void Run(string[] args)
		{
			((ConsoleCommand)this).Run(args, (Terminal)(object)Console.instance);
		}

		public sealed override void Run(string[] args, Terminal context)
		{
			if ((Object)(object)context == (Object)null)
			{
				return;
			}
			try
			{
				Execute(args ?? Array.Empty<string>(), context);
			}
			catch (Exception ex)
			{
				context.AddString("Stavebound: " + ((ConsoleCommand)this).Name + " failed - " + ex.GetType().Name + ": " + ex.Message);
				Logger.LogError((object)$"{((ConsoleCommand)this).Name} threw: {ex}");
			}
		}

		protected abstract void Execute(string[] args, Terminal context);

		protected static void Echo(Terminal context, string line)
		{
			context.AddString(line);
			Logger.LogInfo((object)line);
		}
	}
	internal static class SyncLog
	{
		private static bool Enabled => StaveboundConfig.LogNetworkSync?.Value ?? false;

		internal static void Say(string message)
		{
			if (Enabled)
			{
				Logger.LogInfo((object)("[sync] " + message));
			}
		}

		internal static void Warn(string message)
		{
			Logger.LogWarning((object)("[sync] " + message));
		}

		internal static string Difference(IReadOnlyList<PortalRecord> before, IReadOnlyList<PortalRecord> after)
		{
			Dictionary<long, PortalRecord> dictionary = new Dictionary<long, PortalRecord>();
			foreach (PortalRecord item in before)
			{
				dictionary[item.Pid] = item;
			}
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			List<string> list3 = new List<string>();
			foreach (PortalRecord item2 in after)
			{
				if (!dictionary.TryGetValue(item2.Pid, out var value))
				{
					list.Add(item2.ToString());
					continue;
				}
				if (value.TargetPid != item2.TargetPid)
				{
					list2.Add($"{item2} -> {Name(after, item2.TargetPid)}");
				}
				if (value.Name != item2.Name)
				{
					list3.Add($"{value} is now {item2}");
				}
				dictionary.Remove(item2.Pid);
			}
			List<string> list4 = new List<string>();
			if (list.Count > 0)
			{
				list4.Add("added " + string.Join(", ", list));
			}
			if (dictionary.Count > 0)
			{
				list4.Add("gone " + string.Join(", ", dictionary.Values.Select((PortalRecord p) => p.ToString())));
			}
			if (list2.Count > 0)
			{
				list4.Add("re-aimed " + string.Join(", ", list2));
			}
			if (list3.Count > 0)
			{
				list4.Add("renamed " + string.Join(", ", list3));
			}
			if (list4.Count != 0)
			{
				return string.Join("; ", list4);
			}
			return "no visible difference";
		}

		internal static string Name(IReadOnlyList<PortalRecord> among, long pid)
		{
			if (pid == 0L)
			{
				return "vanilla tag pairing";
			}
			foreach (PortalRecord item in among)
			{
				if (item.Pid == pid)
				{
					return item.ToString();
				}
			}
			return $"an unknown portal (pid {pid})";
		}
	}
	internal static class ZdoKeys
	{
		internal static readonly int Pid = StringExtensionMethods.GetStableHashCode("stave_pid");

		internal static readonly int Destination = StringExtensionMethods.GetStableHashCode("stave_dest");

		internal static readonly KeyValuePair<int, int> LegacyTarget = ZDO.GetHashZDOID("stave_target");

		internal static readonly int ClearanceMask = StringExtensionMethods.GetStableHashCode("stave_mask");
	}
}
namespace Stavebound.Patches
{
	[HarmonyPatch(typeof(InventoryGrid))]
	internal static class CargoPreviewPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("UpdateGui")]
		private static void MarkWhatTheDestinationRefuses(List<Element> ___m_elements, Inventory ___m_inventory)
		{
			if (___m_elements == null || ___m_inventory == null || !CargoPreview.TryGetNearbyDestination(out var mask, out var allowsEverything))
			{
				return;
			}
			foreach (Element ___m_element in ___m_elements)
			{
				if (!((Object)(object)___m_element?.m_noteleport == (Object)null) && ___m_element.m_used)
				{
					ItemData itemAt = ___m_inventory.GetItemAt(___m_element.m_pos.x, ___m_element.m_pos.y);
					((Behaviour)___m_element.m_noteleport).enabled = CargoPreview.WouldBeRefused(itemAt, mask, allowsEverything);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Game))]
	internal static class GameLifecyclePatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void StartRegistry()
		{
			PortalRegistry.OnWorldStart();
			SiteSweep.Start();
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnDestroy")]
		private static void StopRegistry()
		{
			PortalRegistry.OnWorldEnd();
			SiteSweep.Stop();
			PlacementFeedback.Reset();
			ApproachWarning.Reset();
			DestinationSelector.Reset();
		}
	}
	[HarmonyPatch(typeof(Minimap))]
	internal static class MinimapPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void DriveSelector()
		{
			DestinationSelector.Update();
		}

		[HarmonyPrefix]
		[HarmonyPatch("OnMapLeftClick")]
		private static bool ClickToHighlight(Minimap __instance)
		{
			//IL_0028: 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)
			if (!DestinationSelector.IsOpen)
			{
				return true;
			}
			DestinationSelector.HighlightNearest((Vector3)AccessTools.Method(typeof(Minimap), "ScreenToWorldPoint", (Type[])null, (Type[])null).Invoke(__instance, new object[1] { Input.mousePosition }));
			return false;
		}
	}
	[HarmonyPatch(typeof(ObjectDB))]
	internal static class ObjectDbPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void BuildOnAwake()
		{
			TierMap.Build();
		}

		[HarmonyPostfix]
		[HarmonyPatch("CopyOtherDB")]
		private static void BuildOnCopy()
		{
			TierMap.Build();
		}
	}
	[HarmonyPatch(typeof(Player))]
	internal static class PlacementPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("UpdatePlacementGhost")]
		private static void ShowStaveRange(GameObject ___m_placementGhost)
		{
			PlacementFeedback.Update(___m_placementGhost);
		}
	}
	[HarmonyPatch(typeof(TeleportWorld))]
	internal static class TeleportWorldPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("Interact")]
		private static bool InteractReaims(TeleportWorld __instance, Humanoid human, bool alt, ref bool __result)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			__result = true;
			if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false))
			{
				if (human != null)
				{
					((Character)human).Message((MessageType)2, "$piece_noaccess", 0, (Sprite)null);
				}
				return false;
			}
			if (alt)
			{
				TextInput.instance.RequestText((TextReceiver)(object)__instance, "$piece_portal_tag", 10);
				return false;
			}
			DestinationSelector.Open(__instance, human);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("Teleport")]
		private static bool TravelToOurTarget(TeleportWorld __instance, Player player, ZNetView ___m_nview)
		{
			//IL_011f: 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)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type