Decompiled source of XpandedPortal v1.0.0

XpandedPortal.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using PortalTweaks.Configuration;
using PortalTweaks.Data;
using PortalTweaks.Energy;
using PortalTweaks.Patches;
using PortalTweaks.StatusEffects;
using PortalTweaks.UI;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("XpandedPortal Contributors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("XpandedPortal")]
[assembly: AssemblyTitle("XpandedPortal")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PortalTweaks
{
	[BepInPlugin("com.user.xpandedportal", "XpandedPortal", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PortalTweaksPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.user.xpandedportal";

		public const string ModName = "XpandedPortal";

		public const string ModVersion = "1.0.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"XpandedPortal v1.0.0 initializing...");
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.user.xpandedportal");
			_harmony.PatchAll();
			Log.LogInfo((object)"XpandedPortal v1.0.0 loaded successfully.");
			if (Chainloader.PluginInfos.ContainsKey("com.jotunn.jotunn"))
			{
				HookJotunnSync();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void HookJotunnSync()
		{
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				Log.LogInfo((object)"Server configuration synchronized successfully.");
			};
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace PortalTweaks.UI
{
	public class PortalConfigPanel : MonoBehaviour
	{
		private GameObject? _rootPanel;

		private RectTransform? _rootRect;

		private TeleportWorld? _currentPortal;

		private Text? _titleText;

		private Text? _tagLabel;

		private InputField? _tagInput;

		private Text? _passLabel;

		private InputField? _passwordInput;

		private Text? _targetLabel;

		private Dropdown? _targetDropdown;

		private Text? _outPassLabel;

		private InputField? _outboundPasswordInput;

		private Text? _distanceText;

		private Text? _energyText;

		private Text? _weightText;

		private Text? _statusText;

		private float _uiUpdateTimer;

		private Button? _saveButton;

		private Button? _cancelButton;

		private List<PortalInfo> _availablePortals = new List<PortalInfo>();

		public static PortalConfigPanel? Instance { get; private set; }

		public static void Initialize()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null) && !((Object)(object)Instance != (Object)null))
			{
				GameObject val = new GameObject("PortalTweaks_UIHandler");
				val.transform.SetParent(GUIManager.CustomGUIFront.transform, false);
				Instance = val.AddComponent<PortalConfigPanel>();
			}
		}

		private void Awake()
		{
			Instance = this;
			BuildUI();
		}

		private void Update()
		{
			if (!((Object)(object)_rootPanel != (Object)null) || !_rootPanel.activeSelf)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)27))
			{
				Hide();
				return;
			}
			_uiUpdateTimer += Time.deltaTime;
			if (_uiUpdateTimer >= 0.25f)
			{
				_uiUpdateTimer = 0f;
				UpdateStatus();
			}
		}

		private void BuildUI()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0425: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_0483: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0507: Unknown result type (might be due to invalid IL or missing references)
			//IL_0516: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Unknown result type (might be due to invalid IL or missing references)
			//IL_057c: Unknown result type (might be due to invalid IL or missing references)
			//IL_058b: Unknown result type (might be due to invalid IL or missing references)
			//IL_059a: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Expected O, but got Unknown
			//IL_05f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0607: Unknown result type (might be due to invalid IL or missing references)
			//IL_0616: Unknown result type (might be due to invalid IL or missing references)
			//IL_064b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0655: Expected O, but got Unknown
			if (GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				_rootPanel = GUIManager.Instance.CreateWoodpanel(((Component)this).transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 580f, 360f, true);
				((Object)_rootPanel).name = "PortalTweaks_ConfigPanel";
				_rootRect = _rootPanel.GetComponent<RectTransform>();
				GameObject val = GUIManager.Instance.CreateText("PORTAL CONFIGURATION", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -25f), GUIManager.Instance.AveriaSerifBold, 19, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 28f, false);
				_titleText = val.GetComponent<Text>();
				_titleText.alignment = (TextAnchor)4;
				_tagLabel = CreateTextElement("Portal Tag", new Vector2(-140f, -55f), 250f);
				GameObject val2 = GUIManager.Instance.CreateInputField(_rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(-140f, -85f), (ContentType)0, "Enter portal tag...", 15, 250f, 32f);
				_tagInput = val2.GetComponent<InputField>();
				_passLabel = CreateTextElement("Portal Password", new Vector2(140f, -55f), 250f);
				GameObject val3 = GUIManager.Instance.CreateInputField(_rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(140f, -85f), (ContentType)0, "None (Unprotected)", 15, 250f, 32f);
				_passwordInput = val3.GetComponent<InputField>();
				_targetLabel = CreateTextElement("Outbound Destination", new Vector2(-140f, -125f), 250f);
				GameObject val4 = GUIManager.Instance.CreateDropDown(_rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(-140f, -155f), 14, 250f, 32f);
				_targetDropdown = val4.GetComponent<Dropdown>();
				((UnityEvent<int>)(object)_targetDropdown.onValueChanged).AddListener((UnityAction<int>)delegate
				{
					UpdateStatus();
				});
				_outPassLabel = CreateTextElement("Destination Password", new Vector2(140f, -125f), 250f);
				GameObject val5 = GUIManager.Instance.CreateInputField(_rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(140f, -155f), (ContentType)0, "Password for target...", 15, 250f, 32f);
				_outboundPasswordInput = val5.GetComponent<InputField>();
				((UnityEvent<string>)(object)_outboundPasswordInput.onValueChanged).AddListener((UnityAction<string>)delegate
				{
					UpdateStatus();
				});
				GameObject val6 = GUIManager.Instance.CreateText("", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -198f), GUIManager.Instance.AveriaSerif, 14, Color.white, true, Color.black, 520f, 22f, false);
				_distanceText = val6.GetComponent<Text>();
				_distanceText.alignment = (TextAnchor)4;
				GameObject val7 = GUIManager.Instance.CreateText("", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -220f), GUIManager.Instance.AveriaSerif, 14, Color.white, true, Color.black, 520f, 22f, false);
				_energyText = val7.GetComponent<Text>();
				_energyText.alignment = (TextAnchor)4;
				GameObject val8 = GUIManager.Instance.CreateText("", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -220f), GUIManager.Instance.AveriaSerif, 14, Color.white, true, Color.black, 520f, 22f, false);
				_weightText = val8.GetComponent<Text>();
				_weightText.alignment = (TextAnchor)4;
				GameObject val9 = GUIManager.Instance.CreateText("", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -244f), GUIManager.Instance.AveriaSerif, 14, Color.yellow, true, Color.black, 520f, 22f, false);
				_statusText = val9.GetComponent<Text>();
				_statusText.alignment = (TextAnchor)4;
				GameObject val10 = GUIManager.Instance.CreateButton("Save & Connect", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(-80f, -288f), 140f, 36f);
				_saveButton = val10.GetComponent<Button>();
				((UnityEvent)_saveButton.onClick).AddListener(new UnityAction(OnSaveClicked));
				GameObject val11 = GUIManager.Instance.CreateButton("Cancel", _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(80f, -288f), 140f, 36f);
				_cancelButton = val11.GetComponent<Button>();
				((UnityEvent)_cancelButton.onClick).AddListener(new UnityAction(Hide));
				_rootPanel.SetActive(false);
			}
		}

		private Text CreateTextElement(string text, Vector2 pos, float width)
		{
			//IL_001b: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			Text component = GUIManager.Instance.CreateText(text, _rootPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), pos, GUIManager.Instance.AveriaSerif, 14, Color.white, true, Color.black, width, 24f, false).GetComponent<Text>();
			component.alignment = (TextAnchor)3;
			return component;
		}

		public void Show(TeleportWorld portal)
		{
			//IL_0104: 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_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_045e: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0560: Unknown result type (might be due to invalid IL or missing references)
			//IL_0593: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Expected O, but got Unknown
			//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0606: Unknown result type (might be due to invalid IL or missing references)
			//IL_0610: Expected O, but got Unknown
			//IL_0611: Unknown result type (might be due to invalid IL or missing references)
			//IL_0616: Unknown result type (might be due to invalid IL or missing references)
			//IL_0623: Unknown result type (might be due to invalid IL or missing references)
			//IL_062a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_rootPanel == (Object)null)
			{
				BuildUI();
			}
			_currentPortal = portal;
			if ((Object)(object)_currentPortal == (Object)null || (Object)(object)_rootPanel == (Object)null || (Object)(object)_rootRect == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)_currentPortal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			PortalInfo portalInfo = PortalDataManager.FromZDO(component.GetZDO());
			if (portalInfo == null)
			{
				return;
			}
			if ((Object)(object)_titleText != (Object)null)
			{
				_titleText.text = (ModConfig.IsStonePortal(_currentPortal) ? "STONE PORTAL CONFIGURATION" : "PORTAL CONFIGURATION");
			}
			bool value = ModConfig.EnablePasswordSystem.Value;
			bool value2 = ModConfig.EnableTargetSelectionUI.Value;
			float num;
			float num2;
			float width;
			if (value)
			{
				num = 580f;
				num2 = -140f;
				width = 250f;
			}
			else
			{
				num = 380f;
				num2 = 0f;
				width = 290f;
			}
			Text? tagLabel = _tagLabel;
			SetPosAndSize((tagLabel != null) ? ((Graphic)tagLabel).rectTransform : null, new Vector2(num2, -55f), width, 24f);
			InputField? tagInput = _tagInput;
			SetPosAndSize((tagInput != null) ? ((Component)tagInput).GetComponent<RectTransform>() : null, new Vector2(num2, -85f), width, 32f);
			Text? passLabel = _passLabel;
			if (passLabel != null)
			{
				((Component)passLabel).gameObject.SetActive(value);
			}
			InputField? passwordInput = _passwordInput;
			if (passwordInput != null)
			{
				((Component)passwordInput).gameObject.SetActive(value);
			}
			if (value)
			{
				Text? passLabel2 = _passLabel;
				SetPosAndSize((passLabel2 != null) ? ((Graphic)passLabel2).rectTransform : null, new Vector2(140f, -55f), 250f, 24f);
				InputField? passwordInput2 = _passwordInput;
				SetPosAndSize((passwordInput2 != null) ? ((Component)passwordInput2).GetComponent<RectTransform>() : null, new Vector2(140f, -85f), 250f, 32f);
			}
			Text? targetLabel = _targetLabel;
			if (targetLabel != null)
			{
				((Component)targetLabel).gameObject.SetActive(value2);
			}
			Dropdown? targetDropdown = _targetDropdown;
			if (targetDropdown != null)
			{
				((Component)targetDropdown).gameObject.SetActive(value2);
			}
			if (value2)
			{
				Text? targetLabel2 = _targetLabel;
				SetPosAndSize((targetLabel2 != null) ? ((Graphic)targetLabel2).rectTransform : null, new Vector2(num2, -125f), width, 24f);
				Dropdown? targetDropdown2 = _targetDropdown;
				SetPosAndSize((targetDropdown2 != null) ? ((Component)targetDropdown2).GetComponent<RectTransform>() : null, new Vector2(num2, -155f), width, 32f);
			}
			bool flag = value && value2;
			Text? outPassLabel = _outPassLabel;
			if (outPassLabel != null)
			{
				((Component)outPassLabel).gameObject.SetActive(flag);
			}
			InputField? outboundPasswordInput = _outboundPasswordInput;
			if (outboundPasswordInput != null)
			{
				((Component)outboundPasswordInput).gameObject.SetActive(flag);
			}
			if (flag)
			{
				Text? outPassLabel2 = _outPassLabel;
				SetPosAndSize((outPassLabel2 != null) ? ((Graphic)outPassLabel2).rectTransform : null, new Vector2(140f, -125f), 250f, 24f);
				InputField? outboundPasswordInput2 = _outboundPasswordInput;
				SetPosAndSize((outboundPasswordInput2 != null) ? ((Component)outboundPasswordInput2).GetComponent<RectTransform>() : null, new Vector2(140f, -155f), 250f, 32f);
			}
			float num3 = (value2 ? (-198f) : (-128f));
			Text? distanceText = _distanceText;
			if (distanceText != null)
			{
				((Component)distanceText).gameObject.SetActive(value2);
			}
			if (value2 && (Object)(object)_distanceText != (Object)null)
			{
				SetPosAndSize(((Graphic)_distanceText).rectTransform, new Vector2(0f, num3), num - 40f, 22f);
				num3 -= 22f;
			}
			bool value3 = ModConfig.EnableEnergySystem.Value;
			Text? energyText = _energyText;
			if (energyText != null)
			{
				((Component)energyText).gameObject.SetActive(value3);
			}
			if (value3 && (Object)(object)_energyText != (Object)null)
			{
				SetPosAndSize(((Graphic)_energyText).rectTransform, new Vector2(0f, num3), num - 40f, 22f);
				num3 -= 22f;
			}
			if ((Object)(object)_weightText != (Object)null)
			{
				SetPosAndSize(((Graphic)_weightText).rectTransform, new Vector2(0f, num3), num - 40f, 22f);
				num3 -= 22f;
			}
			if ((Object)(object)_statusText != (Object)null)
			{
				SetPosAndSize(((Graphic)_statusText).rectTransform, new Vector2(0f, num3), num - 40f, 22f);
				num3 -= 32f;
			}
			Button? saveButton = _saveButton;
			SetPosAndSize((saveButton != null) ? ((Component)saveButton).GetComponent<RectTransform>() : null, new Vector2(-80f, num3), 140f, 36f);
			Button? cancelButton = _cancelButton;
			SetPosAndSize((cancelButton != null) ? ((Component)cancelButton).GetComponent<RectTransform>() : null, new Vector2(80f, num3), 140f, 36f);
			float num4 = Mathf.Abs(num3) + 55f;
			_rootRect.sizeDelta = new Vector2(num, num4);
			if ((Object)(object)_tagInput != (Object)null)
			{
				_tagInput.text = portalInfo.Tag;
			}
			if ((Object)(object)_passwordInput != (Object)null)
			{
				_passwordInput.text = portalInfo.Password;
			}
			if ((Object)(object)_outboundPasswordInput != (Object)null)
			{
				_outboundPasswordInput.text = portalInfo.OutboundPassword;
			}
			if (value2 && (Object)(object)_targetDropdown != (Object)null)
			{
				_availablePortals = PortalDataManager.GetAllPortals(portalInfo.Id);
				Vector3 myPos = ((Component)_currentPortal).transform.position;
				_availablePortals.Sort((PortalInfo a, PortalInfo b) => Utils.DistanceXZ(myPos, a.Position).CompareTo(Utils.DistanceXZ(myPos, b.Position)));
				_targetDropdown.ClearOptions();
				List<OptionData> list = new List<OptionData>
				{
					new OptionData("[ None / Unconnected ]")
				};
				int value4 = 0;
				for (int num5 = 0; num5 < _availablePortals.Count; num5++)
				{
					PortalInfo portalInfo2 = _availablePortals[num5];
					float num6 = Utils.DistanceXZ(myPos, portalInfo2.Position);
					string arg = ((!string.IsNullOrEmpty(portalInfo2.Password)) ? " [Locked]" : "");
					list.Add(new OptionData($"{portalInfo2.DisplayName}{arg} ({num6:F0}m)"));
					if (portalInfo.OutboundTarget != ZDOID.None && portalInfo.OutboundTarget == portalInfo2.Id)
					{
						value4 = num5 + 1;
					}
				}
				_targetDropdown.AddOptions(list);
				_targetDropdown.value = value4;
			}
			UpdateStatus();
			_rootPanel.SetActive(true);
			GUIManager.BlockInput(true);
		}

		private static void SetPosAndSize(RectTransform? rt, Vector2 pos, float width, float height)
		{
			//IL_0015: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rt == (Object)null))
			{
				rt.anchorMin = new Vector2(0.5f, 1f);
				rt.anchorMax = new Vector2(0.5f, 1f);
				rt.anchoredPosition = pos;
				rt.sizeDelta = new Vector2(width, height);
			}
		}

		public void Hide()
		{
			if ((Object)(object)_rootPanel != (Object)null)
			{
				_rootPanel.SetActive(false);
			}
			GUIManager.BlockInput(false);
			_currentPortal = null;
		}

		private void UpdateStatus()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_currentPortal == (Object)null)
			{
				return;
			}
			bool value = ModConfig.EnablePasswordSystem.Value;
			bool value2 = ModConfig.EnableTargetSelectionUI.Value;
			if ((Object)(object)_weightText != (Object)null)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && ((Humanoid)localPlayer).GetInventory() != null)
				{
					float totalWeight = ((Humanoid)localPlayer).GetInventory().GetTotalWeight();
					float maxWeight = ModConfig.GetMaxWeight(_currentPortal);
					if (ModConfig.EnableMaxWeightLimit.Value && totalWeight > maxWeight)
					{
						_weightText.text = $"<color=red>Carry Weight: {totalWeight:F0} / {maxWeight:F0} lbs (Overweight)</color>";
					}
					else
					{
						_weightText.text = $"<color=#cccccc>Carry Weight: {totalWeight:F0} / {maxWeight:F0} lbs</color>";
					}
				}
			}
			bool interactable = true;
			string text = "";
			float? overrideDistance = null;
			if (value2 && (Object)(object)_targetDropdown != (Object)null)
			{
				int value3 = _targetDropdown.value;
				if (value3 > 0 && value3 - 1 < _availablePortals.Count)
				{
					PortalInfo portalInfo = _availablePortals[value3 - 1];
					float num = Utils.DistanceXZ(((Component)_currentPortal).transform.position, portalInfo.Position);
					overrideDistance = num;
					if ((Object)(object)_distanceText != (Object)null)
					{
						float maxDistance = ModConfig.GetMaxDistance(_currentPortal);
						float minDistance = ModConfig.GetMinDistance(_currentPortal);
						if (ModConfig.EnableDistanceLimit.Value && num > maxDistance)
						{
							_distanceText.text = $"<color=red>Distance: {num:F0}m (Exceeds {maxDistance:F0}m limit)</color>";
							text = "Destination is too far away!";
							interactable = false;
						}
						else if (ModConfig.EnableMinDistanceLimit.Value && num < minDistance)
						{
							_distanceText.text = $"<color=red>Distance: {num:F0}m (Below {minDistance:F0}m limit)</color>";
							text = "Destination is too close!";
							interactable = false;
						}
						else
						{
							_distanceText.text = $"<color=#80c0ff>Distance to target: {num:F0}m</color>";
						}
					}
					if (value)
					{
						string suppliedOutboundPassword = (((Object)(object)_outboundPasswordInput != (Object)null) ? _outboundPasswordInput.text.Trim() : "");
						if (!PortalDataManager.ValidatePasswordMatch(portalInfo.Password, suppliedOutboundPassword, out string _))
						{
							text = "<color=red>Destination is password-protected! Incorrect password.</color>";
							interactable = false;
						}
						else if (!string.IsNullOrEmpty(portalInfo.Password))
						{
							text = "<color=#80ff80>Destination password matched.</color>";
						}
					}
				}
				else if ((Object)(object)_distanceText != (Object)null)
				{
					_distanceText.text = "<color=#aaaaaa>No destination selected (Portal will be unlinked).</color>";
				}
			}
			else if ((Object)(object)_distanceText != (Object)null)
			{
				_distanceText.text = "";
			}
			if (ModConfig.EnableEnergySystem.Value && (Object)(object)_energyText != (Object)null && EnergyManager.GetCurrentEnergy(_currentPortal, out var currentEnergy, out var maxEnergy, out var rechargeRate))
			{
				Player localPlayer2 = Player.m_localPlayer;
				float totalCost;
				string costBreakdown;
				float num2 = EnergyManager.CalculateTeleportCost(localPlayer2, _currentPortal, out totalCost, out costBreakdown, overrideDistance);
				if (currentEnergy >= num2)
				{
					_energyText.text = $"<color=#ffd700>Portal Energy: {currentEnergy:F0} / {maxEnergy:F0} (+{rechargeRate:F1}/s) | Jump Cost: {num2:F0} ({costBreakdown})</color>";
				}
				else if (ModConfig.LowEnergyAction.Value == LowEnergyBehavior.ExtendSickness)
				{
					float num3 = EnergyManager.CalculateOverdraftExtraSickness(num2 - currentEnergy);
					if (num2 > maxEnergy)
					{
						string exceedsCapacityReason = EnergyManager.GetExceedsCapacityReason(localPlayer2, _currentPortal, maxEnergy, overrideDistance);
						_energyText.text = $"<color=#ffaa55>Portal Overdraft: {currentEnergy:F0} / {maxEnergy:F0} (Cost: {num2:F0} - {exceedsCapacityReason}) (+{num3:F0}s sickness)</color>";
					}
					else
					{
						float rechargeTimeRemaining = EnergyManager.GetRechargeTimeRemaining(currentEnergy, num2, rechargeRate);
						_energyText.text = $"<color=#ffaa55>Portal Overdraft: {currentEnergy:F0} / {maxEnergy:F0} (+{num3:F0}s sickness! Ready in ~{rechargeTimeRemaining:F0}s)</color>";
					}
				}
				else if (num2 > maxEnergy)
				{
					string exceedsCapacityReason2 = EnergyManager.GetExceedsCapacityReason(localPlayer2, _currentPortal, maxEnergy, overrideDistance);
					_energyText.text = $"<color=#ff8080>Portal Energy: {currentEnergy:F0} / {maxEnergy:F0} (Cost: {num2:F0} - {exceedsCapacityReason2})</color>";
				}
				else
				{
					float rechargeTimeRemaining2 = EnergyManager.GetRechargeTimeRemaining(currentEnergy, num2, rechargeRate);
					_energyText.text = $"<color=#ff8080>Portal Energy: {currentEnergy:F0} / {maxEnergy:F0} (Cost: {num2:F0} - Recharging: ~{rechargeTimeRemaining2:F0}s)</color>";
				}
			}
			if (TeleportPatch.HasProximityConflict(_currentPortal, out var closestDist))
			{
				float value4 = ModConfig.MinDistanceBetweenPortals.Value;
				string text2 = $"<color=red>Interference: Portal is {closestDist:F0}m from another portal (min {value4:F0}m)!</color>";
				text = (string.IsNullOrEmpty(text) ? text2 : (text + "\n" + text2));
			}
			if (ModConfig.EnableTeleportSickness.Value && ModConfig.SicknessBlockTeleport.Value && TeleportSicknessManager.HasSickness(Player.m_localPlayer, out var remainingSeconds))
			{
				string text3 = $"<color=red>Teleportation sickness active ({remainingSeconds:F0}s remaining)!</color>";
				text = (string.IsNullOrEmpty(text) ? text3 : (text + "\n" + text3));
			}
			if ((Object)(object)_statusText != (Object)null)
			{
				_statusText.text = text;
			}
			if ((Object)(object)_saveButton != (Object)null)
			{
				((Selectable)_saveButton).interactable = interactable;
			}
		}

		private void OnSaveClicked()
		{
			//IL_0061: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_currentPortal == (Object)null)
			{
				Hide();
				return;
			}
			string tag = (((Object)(object)_tagInput != (Object)null) ? _tagInput.text.Trim() : "");
			string password = (((Object)(object)_passwordInput != (Object)null) ? _passwordInput.text.Trim() : "");
			ZDOID val = ZDOID.None;
			string outboundPassword = (((Object)(object)_outboundPasswordInput != (Object)null) ? _outboundPasswordInput.text.Trim() : "");
			bool value = ModConfig.EnableTargetSelectionUI.Value;
			if (value && (Object)(object)_targetDropdown != (Object)null)
			{
				int value2 = _targetDropdown.value;
				if (value2 > 0 && value2 - 1 < _availablePortals.Count)
				{
					val = _availablePortals[value2 - 1].Id;
				}
			}
			PortalDataManager.SavePortal(_currentPortal, tag, password, val, outboundPassword, value);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, (value && val != ZDOID.None) ? "Portal destination routed!" : "Portal configuration saved.", 0, (Sprite)null, false);
			}
			Hide();
		}
	}
}
namespace PortalTweaks.StatusEffects
{
	public class SE_TeleportSickness : SE_Stats
	{
		public const string EffectName = "SE_TeleportSickness";

		public static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("SE_TeleportSickness");

		public SE_TeleportSickness()
		{
			((Object)this).name = "SE_TeleportSickness";
			((StatusEffect)this).m_name = "Teleport Sickness";
			((StatusEffect)this).m_tooltip = "Suffering from spatial displacement disorientation.";
		}

		public override void Setup(Character character)
		{
			((SE_Stats)this).Setup(character);
			ApplyConfigModifiers();
		}

		public void ApplyConfigModifiers()
		{
			base.m_addMaxCarryWeight = 0f - Mathf.Max(0f, ModConfig.SicknessCarryWeightReduction.Value);
			float num = Mathf.Clamp(ModConfig.SicknessMovementSpeed.Value, 0f, 100f);
			base.m_speedModifier = num / 100f - 1f;
			base.m_healthRegenMultiplier = Mathf.Clamp(ModConfig.SicknessHealthRegen.Value, 0f, 100f) / 100f;
			base.m_staminaRegenMultiplier = Mathf.Clamp(ModConfig.SicknessStaminaRegen.Value, 0f, 100f) / 100f;
			base.m_eitrRegenMultiplier = Mathf.Clamp(ModConfig.SicknessEitrRegen.Value, 0f, 100f) / 100f;
		}

		public override string GetTooltipString()
		{
			List<string> list = new List<string>();
			if (ModConfig.SicknessBlockTeleport.Value)
			{
				list.Add("<color=#ff6060>Unable to use portals</color>");
			}
			if (ModConfig.SicknessCarryWeightReduction.Value > 0f)
			{
				list.Add($"Carry weight: -{ModConfig.SicknessCarryWeightReduction.Value:F0}");
			}
			if (ModConfig.SicknessMovementSpeed.Value < 100f)
			{
				list.Add($"Movement speed: {ModConfig.SicknessMovementSpeed.Value:F0}%");
			}
			if (ModConfig.SicknessHealthRegen.Value < 100f)
			{
				list.Add($"Health regen: {ModConfig.SicknessHealthRegen.Value:F0}%");
			}
			if (ModConfig.SicknessStaminaRegen.Value < 100f)
			{
				list.Add($"Stamina regen: {ModConfig.SicknessStaminaRegen.Value:F0}%");
			}
			if (ModConfig.SicknessEitrRegen.Value < 100f)
			{
				list.Add($"Eitr regen: {ModConfig.SicknessEitrRegen.Value:F0}%");
			}
			return string.Join("\n", list);
		}

		public override string GetIconText()
		{
			float remaningTime = ((StatusEffect)this).GetRemaningTime();
			if (!(remaningTime > 0f))
			{
				return "";
			}
			return remaningTime.ToString("F0") + "s";
		}
	}
	public static class TeleportSicknessManager
	{
		private struct PendingSicknessData
		{
			public float JumpDistance;

			public bool IsStonePortal;

			public float OverdraftExtraSeconds;
		}

		private static SE_TeleportSickness? _templateEffect;

		private static readonly Dictionary<long, PendingSicknessData> _pendingSickness = new Dictionary<long, PendingSicknessData>();

		public static void SchedulePendingSickness(Player? player, float jumpDist, bool isStonePortal = false, float overdraftExtraSeconds = 0f)
		{
			if (!((Object)(object)player == (Object)null))
			{
				_pendingSickness[player.GetPlayerID()] = new PendingSicknessData
				{
					JumpDistance = jumpDist,
					IsStonePortal = isStonePortal,
					OverdraftExtraSeconds = overdraftExtraSeconds
				};
			}
		}

		public static void ClearPendingSickness(Player? player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				_pendingSickness.Remove(player.GetPlayerID());
			}
		}

		public static void ApplyPendingSickness(Player? player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			long playerID = player.GetPlayerID();
			if (!_pendingSickness.TryGetValue(playerID, out var value))
			{
				return;
			}
			_pendingSickness.Remove(playerID);
			if (ModConfig.EnableTeleportSickness.Value)
			{
				ApplyDurabilityLoss(player, value.IsStonePortal);
			}
			float num = 0f;
			if (ModConfig.EnableTeleportSickness.Value)
			{
				num = CalculateDuration(value.JumpDistance, value.IsStonePortal);
			}
			float num2 = num + value.OverdraftExtraSeconds;
			if (num2 > 0.001f)
			{
				ApplySickness(player, num2);
				if (value.OverdraftExtraSeconds > 0.001f)
				{
					((Character)player).Message((MessageType)2, $"Teleported via overdraft! Sickness extended (+{value.OverdraftExtraSeconds:F0}s)", 0, (Sprite)null, false);
				}
			}
		}

		public static float CalculateDuration(float distance, bool isStonePortal = false)
		{
			float sicknessBaseDuration = ModConfig.GetSicknessBaseDuration(isStonePortal);
			if (sicknessBaseDuration <= 0.001f)
			{
				return 0f;
			}
			float value = ModConfig.SicknessBaseDistance.Value;
			float value2 = ModConfig.SicknessDurationExponent.Value;
			if (distance <= 0f || value <= 0.001f)
			{
				return sicknessBaseDuration;
			}
			float num = Mathf.Max(0f, distance / value);
			float num2 = sicknessBaseDuration * Mathf.Pow(num, value2);
			return Mathf.Max(0f, num2);
		}

		public static void ApplyDurabilityLoss(Player? player, bool isStonePortal = false)
		{
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return;
			}
			float num = Mathf.Clamp(ModConfig.GetSicknessDurabilityLossPercent(isStonePortal), 0f, 100f);
			if (num <= 0.001f)
			{
				return;
			}
			float num2 = num / 100f;
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if (allItem?.m_shared != null && allItem.m_shared.m_useDurability)
				{
					float num3 = allItem.GetMaxDurability() * num2;
					allItem.m_durability = Mathf.Max(0f, allItem.m_durability - num3);
				}
			}
		}

		public static Sprite? GetSicknessIcon()
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("portal_wood");
				if ((Object)(object)prefab != (Object)null)
				{
					Piece component = prefab.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null && (Object)(object)component.m_icon != (Object)null)
					{
						return component.m_icon;
					}
				}
			}
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Frost"));
				if ((Object)(object)statusEffect != (Object)null && (Object)(object)statusEffect.m_icon != (Object)null)
				{
					return statusEffect.m_icon;
				}
			}
			return null;
		}

		public static SE_TeleportSickness GetOrCreateTemplate()
		{
			if ((Object)(object)_templateEffect == (Object)null)
			{
				_templateEffect = ScriptableObject.CreateInstance<SE_TeleportSickness>();
				((Object)_templateEffect).name = "SE_TeleportSickness";
				((StatusEffect)_templateEffect).m_name = "Teleport Sickness";
				((StatusEffect)_templateEffect).m_icon = GetSicknessIcon();
			}
			if ((Object)(object)((StatusEffect)_templateEffect).m_icon == (Object)null)
			{
				((StatusEffect)_templateEffect).m_icon = GetSicknessIcon();
			}
			return _templateEffect;
		}

		public static void ApplySickness(Player? player, float duration)
		{
			if (!((Object)(object)player == (Object)null) && !(duration <= 0.001f))
			{
				SE_TeleportSickness orCreateTemplate = GetOrCreateTemplate();
				orCreateTemplate.ApplyConfigModifiers();
				((StatusEffect)orCreateTemplate).m_ttl = duration;
				SEMan sEMan = ((Character)player).GetSEMan();
				if (sEMan != null)
				{
					sEMan.AddStatusEffect((StatusEffect)(object)orCreateTemplate, true, 0, 0f, (short)(-1));
				}
			}
		}

		public static bool HasSickness(Player? player, out float remainingSeconds)
		{
			remainingSeconds = 0f;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null || !sEMan.HaveStatusEffect(SE_TeleportSickness.EffectHash))
			{
				return false;
			}
			StatusEffect statusEffect = sEMan.GetStatusEffect(SE_TeleportSickness.EffectHash);
			if ((Object)(object)statusEffect != (Object)null)
			{
				remainingSeconds = Mathf.Max(0f, statusEffect.GetRemaningTime());
				return remainingSeconds > 0.001f;
			}
			return false;
		}
	}
}
namespace PortalTweaks.Patches
{
	[HarmonyPatch]
	public static class TeleportPatch
	{
		public static bool IsOverWeightLimit(Player? player, TeleportWorld? portal, out float currentWeight, out float maxWeight)
		{
			currentWeight = 0f;
			maxWeight = ModConfig.GetMaxWeight(portal);
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return false;
			}
			currentWeight = ((Humanoid)player).GetInventory().GetTotalWeight();
			if (ModConfig.EnableMaxWeightLimit.Value)
			{
				return currentWeight > maxWeight;
			}
			return false;
		}

		public static bool IsOverWeightLimit(Player? player, bool isStone, out float currentWeight, out float maxWeight)
		{
			currentWeight = 0f;
			maxWeight = ModConfig.GetMaxWeight(isStone);
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return false;
			}
			currentWeight = ((Humanoid)player).GetInventory().GetTotalWeight();
			if (ModConfig.EnableMaxWeightLimit.Value)
			{
				return currentWeight > maxWeight;
			}
			return false;
		}

		public static bool IsOverWeightLimit(Player? player, out float currentWeight, out float maxWeight)
		{
			return IsOverWeightLimit(player, null, out currentWeight, out maxWeight);
		}

		public static bool CanTeleportItems(Player? player, TeleportWorld? portal, out string rejectionReason)
		{
			return CanTeleportItems(player, ModConfig.IsStonePortal(portal), out rejectionReason);
		}

		public static bool CanTeleportItems(Player? player, bool isStone, out string rejectionReason)
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Invalid comparison between Unknown and I4
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Invalid comparison between Unknown and I4
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Invalid comparison between Unknown and I4
			rejectionReason = string.Empty;
			if (!ModConfig.ModEnabled.Value)
			{
				return true;
			}
			ItemRestrictionTier restrictionTier = ModConfig.GetRestrictionTier(isStone);
			switch (restrictionTier)
			{
			case ItemRestrictionTier.NoTeleport:
				rejectionReason = "Portals are disabled";
				return false;
			case ItemRestrictionTier.Unrestricted:
				return true;
			default:
			{
				if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
				{
					return true;
				}
				List<ItemData> allItems = ((Humanoid)player).GetInventory().GetAllItems();
				switch (restrictionTier)
				{
				case ItemRestrictionTier.Naked:
					if (allItems.Count > 0)
					{
						rejectionReason = "Inventory must be empty";
						return false;
					}
					break;
				case ItemRestrictionTier.EquippedOnly:
					foreach (ItemData item in allItems)
					{
						if (!item.m_equipped)
						{
							rejectionReason = "Only actively equipped items allowed";
							return false;
						}
					}
					break;
				case ItemRestrictionTier.EquipmentOnly:
					foreach (ItemData item2 in allItems)
					{
						if (!item2.IsEquipable())
						{
							rejectionReason = "Only equipment is allowed";
							return false;
						}
					}
					break;
				case ItemRestrictionTier.GearAndProvisions:
					foreach (ItemData item3 in allItems)
					{
						bool num = item3.IsEquipable();
						bool flag = item3.m_shared != null && (int)item3.m_shared.m_itemType == 2;
						bool flag2 = item3.m_shared != null && ((int)item3.m_shared.m_itemType == 9 || (int)item3.m_shared.m_itemType == 23);
						if (!num && !flag && !flag2)
						{
							rejectionReason = "Only equipment, food, and ammo allowed";
							return false;
						}
					}
					break;
				case ItemRestrictionTier.Vanilla:
					foreach (ItemData item4 in allItems)
					{
						if (item4.m_shared != null && !item4.m_shared.m_teleportable)
						{
							rejectionReason = "Carrying non-teleportable items (ores/metals)";
							return false;
						}
					}
					break;
				}
				return true;
			}
			}
		}

		public static bool CanTeleportItems(Player? player, out string rejectionReason)
		{
			return CanTeleportItems(player, isStone: false, out rejectionReason);
		}

		public static bool TryGetTargetPortalDistance(TeleportWorld portal, out float distance)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			distance = 0f;
			if ((Object)(object)portal == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)1);
			if (connectionZDOID == ZDOID.None)
			{
				return false;
			}
			if (ZDOMan.instance == null)
			{
				return false;
			}
			ZDO zDO2 = ZDOMan.instance.GetZDO(connectionZDOID);
			if (zDO2 == null)
			{
				ZDOMan.instance.RequestZDO(connectionZDOID);
				return false;
			}
			Vector3 position = ((Component)portal).transform.position;
			Vector3 position2 = zDO2.GetPosition();
			distance = Utils.DistanceXZ(position, position2);
			return true;
		}

		public static bool IsDistanceViolated(TeleportWorld portal, out float distance, out string rejectionReason)
		{
			distance = 0f;
			rejectionReason = string.Empty;
			if (!ModConfig.ModEnabled.Value || !ModConfig.EnableDistanceLimit.Value)
			{
				return false;
			}
			if (!TryGetTargetPortalDistance(portal, out distance))
			{
				return false;
			}
			float maxDistance = ModConfig.GetMaxDistance(portal);
			if (distance > maxDistance)
			{
				rejectionReason = $"Portals are too far apart ({distance:F0}m / {maxDistance:F0}m)";
				return true;
			}
			if (ModConfig.EnableMinDistanceLimit.Value)
			{
				float minDistance = ModConfig.GetMinDistance(portal);
				if (distance < minDistance)
				{
					rejectionReason = $"Portals are too close together ({distance:F0}m / {minDistance:F0}m)";
					return true;
				}
			}
			return false;
		}

		public static bool IsPasswordMismatched(TeleportWorld portal, out string reason)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			reason = string.Empty;
			if (!ModConfig.ModEnabled.Value || !ModConfig.EnablePasswordSystem.Value)
			{
				return false;
			}
			if ((Object)(object)portal == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)1);
			if (connectionZDOID == ZDOID.None || ZDOMan.instance == null)
			{
				return false;
			}
			ZDO zDO2 = ZDOMan.instance.GetZDO(connectionZDOID);
			if (zDO2 == null)
			{
				return false;
			}
			string text = zDO2.GetString("PortalTweaks_Password", string.Empty);
			if (ModConfig.EnableTargetSelectionUI.Value)
			{
				string suppliedOutboundPassword = zDO.GetString("PortalTweaks_OutboundPassword", string.Empty);
				if (!PortalDataManager.ValidatePasswordMatch(text, suppliedOutboundPassword, out reason))
				{
					return true;
				}
			}
			else if (zDO.GetString("PortalTweaks_Password", string.Empty) != text)
			{
				reason = "Password mismatch between paired portals";
				return true;
			}
			return false;
		}

		public static bool HasProximityConflict(Vector3 pos, ZDOID excludeId, out float closestDist, out Vector3 closestPos)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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)
			closestDist = float.MaxValue;
			closestPos = Vector3.zero;
			if (!ModConfig.ModEnabled.Value || !ModConfig.EnablePortalProximityLimit.Value)
			{
				return false;
			}
			float value = ModConfig.MinDistanceBetweenPortals.Value;
			if (value <= 0f)
			{
				return false;
			}
			if (ZDOMan.instance == null)
			{
				return false;
			}
			List<ZDO> portalList = ZDOMan.instance.GetPortalList();
			if (portalList == null || portalList.Count == 0)
			{
				return false;
			}
			foreach (ZDO item in portalList)
			{
				if (item != null && !(item.m_uid == ZDOID.None) && !(item.m_uid == excludeId))
				{
					float num = Utils.DistanceXZ(pos, item.GetPosition());
					if (num < closestDist)
					{
						closestDist = num;
						closestPos = item.GetPosition();
					}
				}
			}
			return closestDist < value;
		}

		public static bool HasProximityConflict(TeleportWorld portal, out float closestDist)
		{
			//IL_003e: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			closestDist = float.MaxValue;
			if ((Object)(object)portal == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDOID excludeId = component.GetZDO()?.m_uid ?? ZDOID.None;
			Vector3 closestPos;
			return HasProximityConflict(((Component)portal).transform.position, excludeId, out closestDist, out closestPos);
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		[HarmonyPostfix]
		private static void PostfixUpdatePlacementGhost(Player __instance, GameObject ___m_placementGhost, ref PlacementStatus ___m_placementStatus)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.ModEnabled.Value && ModConfig.EnablePortalProximityLimit.Value && !((Object)(object)___m_placementGhost == (Object)null) && !((Object)(object)___m_placementGhost.GetComponentInChildren<TeleportWorld>() == (Object)null) && HasProximityConflict(___m_placementGhost.transform.position, ZDOID.None, out var _, out var _))
			{
				___m_placementStatus = (PlacementStatus)1;
				Piece component = ___m_placementGhost.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null)
				{
					component.SetInvalidPlacementHeightlight(true);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		[HarmonyPrefix]
		private static bool PrefixTryPlacePiece(Player __instance, Piece piece, GameObject ___m_placementGhost)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.ModEnabled.Value || !ModConfig.EnablePortalProximityLimit.Value)
			{
				return true;
			}
			if ((Object)(object)piece == (Object)null)
			{
				return true;
			}
			if (!((Object)(object)((Component)piece).GetComponentInChildren<TeleportWorld>() != (Object)null) && (!((Object)(object)___m_placementGhost != (Object)null) || !((Object)(object)___m_placementGhost.GetComponentInChildren<TeleportWorld>() != (Object)null)))
			{
				return true;
			}
			if (HasProximityConflict(((Object)(object)___m_placementGhost != (Object)null) ? ___m_placementGhost.transform.position : ((Component)__instance).transform.position, ZDOID.None, out var closestDist, out var _))
			{
				float value = ModConfig.MinDistanceBetweenPortals.Value;
				((Character)__instance).Message((MessageType)2, $"Too close to another portal ({closestDist:F0}m / {value:F0}m)", 0, (Sprite)null, false);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Player), "PlacePiece")]
		[HarmonyPrefix]
		private static bool PrefixPlacePiece(Player __instance, Piece piece, Vector3 pos)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.ModEnabled.Value || !ModConfig.EnablePortalProximityLimit.Value)
			{
				return true;
			}
			if ((Object)(object)piece != (Object)null && (Object)(object)((Component)piece).GetComponentInChildren<TeleportWorld>() != (Object)null && HasProximityConflict(pos, ZDOID.None, out var closestDist, out var _))
			{
				float value = ModConfig.MinDistanceBetweenPortals.Value;
				((Character)__instance).Message((MessageType)2, $"Too close to another portal ({closestDist:F0}m / {value:F0}m)", 0, (Sprite)null, false);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(TeleportWorld), "TargetFound")]
		[HarmonyPostfix]
		private static void PostfixTargetFound(TeleportWorld __instance, ref bool __result)
		{
			if (ModConfig.ModEnabled.Value && __result)
			{
				string rejectionReason;
				if (HasProximityConflict(__instance, out var closestDist))
				{
					__result = false;
				}
				else if (IsDistanceViolated(__instance, out closestDist, out rejectionReason))
				{
					__result = false;
				}
				else if (IsPasswordMismatched(__instance, out rejectionReason))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")]
		[HarmonyPostfix]
		private static void PostfixUpdatePortal(TeleportWorld __instance)
		{
			if (!ModConfig.ModEnabled.Value)
			{
				return;
			}
			float currentEnergy;
			float rechargeRate;
			if (HasProximityConflict(__instance, out var closestDist))
			{
				if ((Object)(object)__instance.m_target_found != (Object)null)
				{
					__instance.m_target_found.SetActive(false);
				}
			}
			else if (ModConfig.EnableEnergySystem.Value && ModConfig.DeactivateVortexWhenInsufficientEnergy && EnergyManager.GetCurrentEnergy(__instance, out currentEnergy, out closestDist, out rechargeRate))
			{
				string costBreakdown;
				float num = EnergyManager.CalculateTeleportCost(Player.m_localPlayer, __instance, out rechargeRate, out costBreakdown);
				if (currentEnergy < num && (Object)(object)__instance.m_target_found != (Object)null)
				{
					__instance.m_target_found.SetActive(false);
				}
			}
		}

		[HarmonyPatch(typeof(TeleportWorld), "Update")]
		[HarmonyPostfix]
		private static void PostfixUpdate(TeleportWorld __instance)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.ModEnabled.Value && ModConfig.EnableEnergySystem.Value && ModConfig.ScalePortalVfxWithEnergy.Value && !((Object)(object)__instance.m_model == (Object)null) && !((Object)(object)((Renderer)__instance.m_model).material == (Object)null) && EnergyManager.GetCurrentEnergy(__instance, out var currentEnergy, out var maxEnergy, out var _))
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				bool num = (Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null && component.GetZDO().GetConnectionZDOID((ConnectionType)1) != ZDOID.None;
				float num2 = ((maxEnergy > 0.001f) ? Mathf.Clamp01(currentEnergy / maxEnergy) : 0f);
				Color colorUnconnected = __instance.m_colorUnconnected;
				Color val = (num ? __instance.m_colorTargetfound : colorUnconnected);
				Color val2 = Color.Lerp(colorUnconnected, val, num2);
				((Renderer)__instance.m_model).material.SetColor("_EmissionColor", val2);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "IsTeleportable")]
		[HarmonyPrefix]
		private static void PrefixIsTeleportable(ref bool allowAllItems)
		{
			if (ModConfig.ModEnabled.Value && ModConfig.GetRestrictionTier(allowAllItems) == ItemRestrictionTier.Unrestricted)
			{
				allowAllItems = true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "IsTeleportable")]
		[HarmonyPostfix]
		private static void PostfixIsTeleportable(Humanoid __instance, bool allowAllItems, ref bool __result)
		{
			if (!ModConfig.ModEnabled.Value)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				string rejectionReason;
				if (IsOverWeightLimit(val, allowAllItems, out var _, out var maxWeight))
				{
					__result = false;
				}
				else if (!CanTeleportItems(val, allowAllItems, out rejectionReason))
				{
					__result = false;
				}
				else if (ModConfig.EnableTeleportSickness.Value && ModConfig.SicknessBlockTeleport.Value && TeleportSicknessManager.HasSickness(val, out maxWeight))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
		[HarmonyPrefix]
		private static bool OnTeleportPrefix(TeleportWorld __instance, Player player)
		{
			if (!ModConfig.ModEnabled.Value)
			{
				return true;
			}
			if (ModConfig.EnableTeleportSickness.Value && ModConfig.SicknessBlockTeleport.Value && TeleportSicknessManager.HasSickness(player, out var remainingSeconds))
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, $"Cannot teleport: Teleportation sickness active ({remainingSeconds:F0}s remaining)", 0, (Sprite)null, false);
				}
				return false;
			}
			if (HasProximityConflict(__instance, out var closestDist))
			{
				float value = ModConfig.MinDistanceBetweenPortals.Value;
				if (player != null)
				{
					((Character)player).Message((MessageType)2, $"Cannot teleport: Portal interference (within {closestDist:F0}m of another portal, minimum {value:F0}m required)", 0, (Sprite)null, false);
				}
				return false;
			}
			if (IsPasswordMismatched(__instance, out string reason))
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, "Cannot teleport: " + reason, 0, (Sprite)null, false);
				}
				return false;
			}
			if (IsDistanceViolated(__instance, out float distance, out string rejectionReason))
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, "Cannot teleport: " + rejectionReason, 0, (Sprite)null, false);
				}
				return false;
			}
			if (IsOverWeightLimit(player, __instance, out var currentWeight, out var maxWeight))
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, $"Too heavy for portal: {currentWeight:F0} / {maxWeight:F0}", 0, (Sprite)null, false);
				}
				return false;
			}
			if (!CanTeleportItems(player, __instance, out string rejectionReason2))
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, "Cannot teleport: " + rejectionReason2, 0, (Sprite)null, false);
				}
				return false;
			}
			float num = 0f;
			if (ModConfig.EnableEnergySystem.Value)
			{
				EnergyManager.GetCurrentEnergy(__instance, out var currentEnergy, out var maxEnergy, out var rechargeRate);
				string costBreakdown;
				float num2 = EnergyManager.CalculateTeleportCost(player, __instance, out distance, out costBreakdown);
				if (currentEnergy < num2)
				{
					if (ModConfig.LowEnergyAction.Value == LowEnergyBehavior.RefuseTeleport)
					{
						if (num2 > maxEnergy)
						{
							string exceedsCapacityReason = EnergyManager.GetExceedsCapacityReason(player, __instance, maxEnergy);
							if (player != null)
							{
								((Character)player).Message((MessageType)2, "Cannot teleport: Exceeds portal capacity (" + exceedsCapacityReason + ")", 0, (Sprite)null, false);
							}
							return false;
						}
						float rechargeTimeRemaining = EnergyManager.GetRechargeTimeRemaining(currentEnergy, num2, rechargeRate);
						if (player != null)
						{
							((Character)player).Message((MessageType)2, $"Cannot teleport: Portal recharging ({currentEnergy:F0} / {num2:F0} energy, ~{rechargeTimeRemaining:F0}s)", 0, (Sprite)null, false);
						}
						return false;
					}
					float num3 = num2 - currentEnergy;
					EnergyManager.ConsumeEnergy(__instance, currentEnergy, out distance);
					num = EnergyManager.CalculateOverdraftExtraSickness(num3);
					if (ModConfig.LogPortalTeleports.Value)
					{
						PortalTweaksPlugin.Log.LogInfo((object)$"Portal [{__instance.GetText()}] overdrafted by {num3:F1} energy. Sickness extended by {num:F0}s.");
					}
				}
				else
				{
					EnergyManager.ConsumeEnergy(__instance, num2, out var remainingEnergy);
					if (ModConfig.LogPortalTeleports.Value)
					{
						PortalTweaksPlugin.Log.LogInfo((object)$"Portal [{__instance.GetText()}] consumed {num2:F1} energy ({costBreakdown}). Remaining: {remainingEnergy:F1}/{maxEnergy:F1}");
					}
				}
			}
			if (ModConfig.LogPortalTeleports.Value && (Object)(object)player != (Object)null)
			{
				string text = __instance.GetText();
				PortalTweaksPlugin.Log.LogInfo((object)("Player '" + player.GetPlayerName() + "' teleporting via portal [Tag: '" + text + "']"));
			}
			if ((ModConfig.EnableTeleportSickness.Value || num > 0f) && (Object)(object)player != (Object)null)
			{
				bool isStonePortal = ModConfig.IsStonePortal(__instance);
				TryGetTargetPortalDistance(__instance, out var distance2);
				TeleportSicknessManager.SchedulePendingSickness(player, distance2, isStonePortal, num);
			}
			return true;
		}

		[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
		[HarmonyPostfix]
		private static void OnTeleportPostfix(TeleportWorld __instance, Player player)
		{
			if ((Object)(object)player != (Object)null && !((Character)player).IsTeleporting())
			{
				TeleportSicknessManager.ClearPendingSickness(player);
			}
		}

		[HarmonyPatch(typeof(TeleportWorld), "GetHoverText")]
		[HarmonyPostfix]
		private static void GetHoverTextPostfix(TeleportWorld __instance, ref string __result)
		{
			if (!ModConfig.ModEnabled.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (ModConfig.EnableTeleportSickness.Value && ModConfig.SicknessBlockTeleport.Value && (Object)(object)localPlayer != (Object)null && TeleportSicknessManager.HasSickness(localPlayer, out var remainingSeconds))
			{
				__result += $"\n<color=red>Cannot teleport: Teleportation sickness ({remainingSeconds:F0}s)</color>";
			}
			if (HasProximityConflict(__instance, out var closestDist))
			{
				float value = ModConfig.MinDistanceBetweenPortals.Value;
				__result += $"\n<color=red>Portal interference: Too close to another portal ({closestDist:F0}m / {value:F0}m)</color>";
			}
			if (IsPasswordMismatched(__instance, out string reason))
			{
				__result = __result + "\n<color=red>Cannot teleport: " + reason + "</color>";
			}
			float distance2;
			if (TryGetTargetPortalDistance(__instance, out var distance))
			{
				if (IsDistanceViolated(__instance, out distance2, out string rejectionReason))
				{
					__result = __result + "\n<color=red>Cannot teleport: " + rejectionReason + "</color>";
				}
				else if (ModConfig.ShowDistanceInHoverText.Value)
				{
					__result += $"\n<color=#80c0ff>Portal distance: {distance:F0}m</color>";
				}
			}
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (ModConfig.ShowHoverWeightWarning.Value && IsOverWeightLimit(localPlayer, __instance, out var currentWeight, out var maxWeight))
			{
				__result += $"\n<color=red>Too heavy to teleport: {currentWeight:F0} / {maxWeight:F0}</color>";
			}
			if (ModConfig.ShowHoverItemWarning.Value && !CanTeleportItems(localPlayer, __instance, out string rejectionReason2))
			{
				__result = __result + "\n<color=red>Cannot teleport: " + rejectionReason2 + "</color>";
			}
			if (!ModConfig.EnableEnergySystem.Value || !ModConfig.ShowHoverEnergyInfo.Value || !EnergyManager.GetCurrentEnergy(__instance, out var currentEnergy, out var maxEnergy, out var rechargeRate))
			{
				return;
			}
			string costBreakdown;
			float num = EnergyManager.CalculateTeleportCost(localPlayer, __instance, out distance2, out costBreakdown);
			if (currentEnergy >= num)
			{
				__result += $"\n<color=#ffd700>Portal Energy: {currentEnergy:F0} / {maxEnergy:F0} (Cost: {num:F0})</color>";
				return;
			}
			if (num > maxEnergy && ModConfig.LowEnergyAction.Value == LowEnergyBehavior.RefuseTeleport)
			{
				string exceedsCapacityReason = EnergyManager.GetExceedsCapacityReason(localPlayer, __instance, maxEnergy);
				__result += $"\n<color=#ff8080>Portal Energy: {currentEnergy:F0} / {maxEnergy:F0} (Cost: {num:F0} - {exceedsCapacityReason})</color>";
				return;
			}
			float rechargeTimeRemaining = EnergyManager.GetRechargeTimeRemaining(currentEnergy, num, rechargeRate);
			if (ModConfig.LowEnergyAction.Value == LowEnergyBehavior.RefuseTeleport)
			{
				__result += $"\n<color=#ff8080>Portal Recharging: {currentEnergy:F0} / {maxEnergy:F0} (Requires {num:F0}, ready in ~{rechargeTimeRemaining:F0}s)</color>";
			}
			else
			{
				float num2 = EnergyManager.CalculateOverdraftExtraSickness(num - currentEnergy);
				__result += $"\n<color=#ffaa00>Portal Overdraft: {currentEnergy:F0} / {num:F0} (+{num2:F0}s sickness! Ready in ~{rechargeTimeRemaining:F0}s)</color>";
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		[HarmonyPostfix]
		private static void PostfixObjectDBAwake(ObjectDB __instance)
		{
			RegisterCustomStatusEffects(__instance);
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		[HarmonyPostfix]
		private static void PostfixCopyOtherDB(ObjectDB __instance)
		{
			RegisterCustomStatusEffects(__instance);
		}

		private static void RegisterCustomStatusEffects(ObjectDB db)
		{
			if (!((Object)(object)db == (Object)null) && db.m_StatusEffects != null)
			{
				SE_TeleportSickness orCreateTemplate = TeleportSicknessManager.GetOrCreateTemplate();
				if (!db.m_StatusEffects.Contains((StatusEffect)(object)orCreateTemplate))
				{
					db.m_StatusEffects.Add((StatusEffect)(object)orCreateTemplate);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateTeleport")]
		[HarmonyPrefix]
		private static void PrefixUpdateTeleport(Player __instance, ref float dt, bool ___m_teleporting, out bool __state)
		{
			__state = ___m_teleporting;
			if (!ModConfig.ModEnabled.Value || !___m_teleporting)
			{
				return;
			}
			float value = ModConfig.PortalLoadTime.Value;
			if (!Mathf.Approximately(value, 8f))
			{
				if (value <= 0.05f)
				{
					dt = Mathf.Max(dt, 8f);
				}
				else
				{
					dt *= 8f / value;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateTeleport")]
		[HarmonyPostfix]
		private static void PostfixUpdateTeleport(Player __instance, bool __state, bool ___m_teleporting)
		{
			if (__state && !___m_teleporting)
			{
				TeleportSicknessManager.ApplyPendingSickness(__instance);
			}
		}
	}
	[HarmonyPatch]
	public static class UIPatches
	{
		[HarmonyPatch(typeof(Game), "Awake")]
		[HarmonyPostfix]
		private static void OnGameAwake()
		{
			PortalConfigPanel.Initialize();
		}

		[HarmonyPatch(typeof(TextInput), "RequestText")]
		[HarmonyPrefix]
		private static bool PrefixRequestText(TextReceiver sign)
		{
			if (!ModConfig.ModEnabled.Value)
			{
				return true;
			}
			if (ModConfig.EnableTargetSelectionUI.Value || ModConfig.EnablePasswordSystem.Value)
			{
				TeleportWorld val = (TeleportWorld)(object)((sign is TeleportWorld) ? sign : null);
				if (val != null)
				{
					if ((Object)(object)PortalConfigPanel.Instance == (Object)null)
					{
						PortalConfigPanel.Initialize();
					}
					PortalConfigPanel.Instance?.Show(val);
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(Game), "ConnectPortals")]
		[HarmonyPrefix]
		private static bool PrefixConnectPortals()
		{
			if (ModConfig.ModEnabled.Value && ModConfig.EnableTargetSelectionUI.Value)
			{
				return false;
			}
			return true;
		}
	}
}
namespace PortalTweaks.Energy
{
	public static class EnergyManager
	{
		public static bool GetCurrentEnergy(TeleportWorld? portal, out float currentEnergy, out float maxEnergy, out float rechargeRate)
		{
			maxEnergy = ModConfig.GetMaxEnergy(portal);
			rechargeRate = ModConfig.GetRechargeRate(portal);
			currentEnergy = maxEnergy;
			if ((Object)(object)portal == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			long num = zDO.GetLong("PortalTweaks_EnergyTime", 0L);
			double num2 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : ((double)Time.time));
			if (num == 0L)
			{
				float num3 = Mathf.Clamp(ModConfig.StartingEnergyPercentage.Value, 0f, 100f) / 100f;
				currentEnergy = maxEnergy * num3;
				if (!component.IsOwner())
				{
					component.ClaimOwnership();
				}
				zDO.Set("PortalTweaks_Energy", currentEnergy);
				zDO.Set("PortalTweaks_EnergyTime", BitConverter.DoubleToInt64Bits(num2));
				return true;
			}
			double num4 = BitConverter.Int64BitsToDouble(num);
			float num5 = zDO.GetFloat("PortalTweaks_Energy", 0f);
			double num6 = num2 - num4;
			if (num6 < 0.0)
			{
				num6 = 0.0;
			}
			currentEnergy = Mathf.Min(maxEnergy, num5 + (float)(num6 * (double)rechargeRate));
			currentEnergy = Mathf.Max(0f, currentEnergy);
			return true;
		}

		public static float CalculateTeleportCost(Player? player, TeleportWorld? portal, out float totalCost, out string costBreakdown, float? overrideDistance = null)
		{
			float baseCost = ModConfig.GetBaseCost(portal);
			float num = baseCost;
			List<string> list = new List<string> { $"Base: {baseCost:F0}" };
			if ((Object)(object)player != (Object)null && ((Humanoid)player).GetInventory() != null)
			{
				if (ModConfig.EnableWeightCostScaling.Value)
				{
					float num2 = ((Humanoid)player).GetInventory().GetTotalWeight() * ModConfig.GetWeightCostFactor(portal);
					num += num2;
					if (num2 > 0.01f)
					{
						list.Add($"Weight: +{num2:F0}");
					}
				}
				if (ModConfig.EnableOreCostScaling.Value)
				{
					float num3 = 0f;
					foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
					{
						if (allItem.m_shared != null && !allItem.m_shared.m_teleportable)
						{
							num3 += allItem.GetWeight(-1);
						}
					}
					if (num3 > 0f)
					{
						float num4 = num3 * ModConfig.GetOreCostFactor(portal);
						num += num4;
						list.Add($"Ore: +{num4:F0}");
					}
				}
			}
			num = (totalCost = Mathf.Max(0f, num));
			if (ModConfig.EnableDistanceCostScaling.Value)
			{
				float num5 = 0f;
				bool flag = false;
				float distance;
				if (overrideDistance.HasValue)
				{
					num5 = overrideDistance.Value;
					flag = true;
				}
				else if ((Object)(object)portal != (Object)null && TeleportPatch.TryGetTargetPortalDistance(portal, out distance))
				{
					num5 = distance;
					flag = true;
				}
				if (flag)
				{
					float distanceCostBase = ModConfig.GetDistanceCostBase(portal);
					float distanceCostExponent = ModConfig.GetDistanceCostExponent(portal);
					float num6 = 1f;
					if (distanceCostBase > 0.001f)
					{
						num6 = Mathf.Pow(Mathf.Max(0f, num5 / distanceCostBase), distanceCostExponent);
						num6 = Mathf.Max(ModConfig.MinDistanceCostMultiplier.Value, num6);
					}
					totalCost = num * num6;
					list.Add($"Dist ({num5:F0}m): x{num6:F2}");
				}
			}
			totalCost = Mathf.Max(0f, totalCost);
			costBreakdown = string.Join(", ", list);
			return totalCost;
		}

		public static void ConsumeEnergy(TeleportWorld? portal, float amount, out float remainingEnergy)
		{
			remainingEnergy = 0f;
			if ((Object)(object)portal == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				GetCurrentEnergy(portal, out var currentEnergy, out var _, out var _);
				remainingEnergy = Mathf.Max(0f, currentEnergy - amount);
				if (!component.IsOwner())
				{
					component.ClaimOwnership();
				}
				double value = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : ((double)Time.time));
				zDO.Set("PortalTweaks_Energy", remainingEnergy);
				zDO.Set("PortalTweaks_EnergyTime", BitConverter.DoubleToInt64Bits(value));
			}
		}

		public static float GetRechargeTimeRemaining(float currentEnergy, float targetCost, float rechargeRate)
		{
			if (currentEnergy >= targetCost || rechargeRate <= 0.0001f)
			{
				return 0f;
			}
			return Mathf.Max(0f, (targetCost - currentEnergy) / rechargeRate);
		}

		public static string GetExceedsCapacityReason(Player? player, TeleportWorld? portal, float maxEnergy, float? overrideDistance = null)
		{
			float baseCost = ModConfig.GetBaseCost(portal);
			float num = 0f;
			float num2 = 0f;
			if ((Object)(object)player != (Object)null && ((Humanoid)player).GetInventory() != null)
			{
				if (ModConfig.EnableWeightCostScaling.Value)
				{
					num = ((Humanoid)player).GetInventory().GetTotalWeight() * ModConfig.GetWeightCostFactor(portal);
				}
				if (ModConfig.EnableOreCostScaling.Value)
				{
					float num3 = 0f;
					foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
					{
						if (allItem.m_shared != null && !allItem.m_shared.m_teleportable)
						{
							num3 += allItem.GetWeight(-1);
						}
					}
					num2 = num3 * ModConfig.GetOreCostFactor(portal);
				}
			}
			float num4 = baseCost + num + num2;
			float num5 = 1f;
			if (ModConfig.EnableDistanceCostScaling.Value)
			{
				float num6 = 0f;
				bool flag = false;
				float distance;
				if (overrideDistance.HasValue)
				{
					num6 = overrideDistance.Value;
					flag = true;
				}
				else if ((Object)(object)portal != (Object)null && TeleportPatch.TryGetTargetPortalDistance(portal, out distance))
				{
					num6 = distance;
					flag = true;
				}
				float distanceCostBase = ModConfig.GetDistanceCostBase(portal);
				float distanceCostExponent = ModConfig.GetDistanceCostExponent(portal);
				if (flag && distanceCostBase > 0.001f)
				{
					num5 = Mathf.Pow(Mathf.Max(0f, num6 / distanceCostBase), distanceCostExponent);
					num5 = Mathf.Max(ModConfig.MinDistanceCostMultiplier.Value, num5);
				}
			}
			bool flag2 = num4 > maxEnergy;
			bool flag3 = baseCost * num5 > maxEnergy;
			if (flag2 && !flag3)
			{
				return "Carrying too much";
			}
			if (flag3 && !flag2)
			{
				return "Portal is too far away";
			}
			if (num5 > 1.8f && num + num2 <= baseCost)
			{
				return "Portal is too far away";
			}
			if (num + num2 > baseCost * 2f && num5 <= 1.2f)
			{
				return "Carrying too much";
			}
			return "Carrying too much or portal too far";
		}

		public static float CalculateOverdraftExtraSickness(float deficit)
		{
			if (deficit <= 0f)
			{
				return 0f;
			}
			return Mathf.Max(0f, deficit * ModConfig.OverdraftSicknessSecondsPerEnergy.Value);
		}
	}
}
namespace PortalTweaks.Data
{
	public class PortalInfo
	{
		public ZDOID Id { get; set; }

		public string Tag { get; set; } = string.Empty;

		public string Password { get; set; } = string.Empty;

		public Vector3 Position { get; set; }

		public ZDOID OutboundTarget { get; set; } = ZDOID.None;

		public string OutboundPassword { get; set; } = string.Empty;

		public string DisplayName
		{
			get
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				if (!string.IsNullOrEmpty(Tag))
				{
					return Tag;
				}
				return $"[Unnamed Portal] ({Id})";
			}
		}
	}
	public static class PortalDataManager
	{
		public const string KeyPassword = "PortalTweaks_Password";

		public const string KeyOutboundTarget = "PortalTweaks_OutboundTarget";

		public const string KeyOutboundPassword = "PortalTweaks_OutboundPassword";

		public const string KeyEnergy = "PortalTweaks_Energy";

		public const string KeyEnergyTime = "PortalTweaks_EnergyTime";

		public static PortalInfo? FromZDO(ZDO? zdo)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null || zdo.m_uid == ZDOID.None)
			{
				return null;
			}
			return new PortalInfo
			{
				Id = zdo.m_uid,
				Tag = zdo.GetString(ZDOVars.s_tag, string.Empty),
				Password = zdo.GetString("PortalTweaks_Password", string.Empty),
				Position = zdo.GetPosition(),
				OutboundTarget = zdo.GetZDOID("PortalTweaks_OutboundTarget"),
				OutboundPassword = zdo.GetString("PortalTweaks_OutboundPassword", string.Empty)
			};
		}

		public static List<PortalInfo> GetAllPortals(ZDOID excludeId = default(ZDOID))
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//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)
			List<PortalInfo> list = new List<PortalInfo>();
			if (ZDOMan.instance == null)
			{
				return list;
			}
			List<ZDO> portalList = ZDOMan.instance.GetPortalList();
			if (portalList == null)
			{
				return list;
			}
			foreach (ZDO item in portalList)
			{
				if (item != null && !(item.m_uid == ZDOID.None) && !(item.m_uid == excludeId))
				{
					PortalInfo portalInfo = FromZDO(item);
					if (portalInfo != null)
					{
						list.Add(portalInfo);
					}
				}
			}
			list.Sort((PortalInfo a, PortalInfo b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase));
			return list;
		}

		public static void SavePortal(TeleportWorld portal, string tag, string password, ZDOID outboundTarget, string outboundPassword, bool updateTarget)
		{
			//IL_0062: 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)
			if ((Object)(object)portal == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				if (tag != portal.GetText())
				{
					portal.SetText(tag);
				}
				zDO.Set("PortalTweaks_Password", password ?? string.Empty);
				if (updateTarget)
				{
					zDO.Set("PortalTweaks_OutboundTarget", outboundTarget);
					zDO.Set("PortalTweaks_OutboundPassword", outboundPassword ?? string.Empty);
					zDO.SetConnection((ConnectionType)1, outboundTarget);
				}
			}
		}

		public static bool ValidatePasswordMatch(string targetPortalPassword, string suppliedOutboundPassword, out string error)
		{
			error = string.Empty;
			if (string.IsNullOrEmpty(targetPortalPassword))
			{
				return true;
			}
			if (targetPortalPassword == suppliedOutboundPassword)
			{
				return true;
			}
			error = "Password mismatch: incorrect password for destination.";
			return false;
		}
	}
}
namespace PortalTweaks.Configuration
{
	public class ConfigurationManagerAttributes
	{
		public bool? Browsable;

		public string? Category;

		public bool? DefaultValue;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public string? Description;

		public int? Order;

		public bool? ReadOnly;

		public bool? IsAdvanced;

		public bool? IsAdminOnly;
	}
	public enum ItemRestrictionTier
	{
		NoTeleport,
		Naked,
		EquippedOnly,
		EquipmentOnly,
		GearAndProvisions,
		Vanilla,
		Unrestricted
	}
	public enum LowEnergyBehavior
	{
		RefuseTeleport,
		ExtendSickness
	}
	public static class ModConfig
	{
		public static ConfigEntry<bool> ModEnabled { get; private set; }

		public static ConfigEntry<float> PortalLoadTime { get; private set; }

		public static ConfigEntry<bool> EnableMaxWeightLimit { get; private set; }

		public static ConfigEntry<float> MaxPortalWeight { get; private set; }

		public static ConfigEntry<float> StoneMaxPortalWeight { get; private set; }

		public static ConfigEntry<bool> ShowHoverWeightWarning { get; private set; }

		public static ConfigEntry<ItemRestrictionTier> ItemRestrictionTierConfig { get; private set; }

		public static ConfigEntry<ItemRestrictionTier> StoneRestrictionTier { get; private set; }

		public static ConfigEntry<bool> ShowHoverItemWarning { get; private set; }

		public static ConfigEntry<bool> EnableDistanceLimit { get; private set; }

		public static ConfigEntry<float> MaxPortalDistance { get; private set; }

		public static ConfigEntry<float> StoneMaxPortalDistance { get; private set; }

		public static ConfigEntry<bool> EnableMinDistanceLimit { get; private set; }

		public static ConfigEntry<float> MinPortalDistance { get; private set; }

		public static ConfigEntry<float> StoneMinPortalDistance { get; private set; }

		public static ConfigEntry<bool> ShowDistanceInHoverText { get; private set; }

		public static ConfigEntry<bool> EnableTargetSelectionUI { get; private set; }

		public static ConfigEntry<bool> EnablePasswordSystem { get; private set; }

		public static ConfigEntry<bool> EnablePortalProximityLimit { get; private set; }

		public static ConfigEntry<float> MinDistanceBetweenPortals { get; private set; }

		public static ConfigEntry<bool> EnableEnergySystem { get; private set; }

		public static ConfigEntry<float> MaxPortalEnergy { get; private set; }

		public static ConfigEntry<float> StoneMaxPortalEnergy { get; private set; }

		public static ConfigEntry<float> EnergyRechargeRate { get; private set; }

		public static ConfigEntry<float> StoneEnergyRechargeRate { get; private set; }

		public static ConfigEntry<float> StartingEnergyPercentage { get; private set; }

		public static ConfigEntry<float> BaseTeleportCost { get; private set; }

		public static ConfigEntry<float> StoneBaseTeleportCost { get; private set; }

		public static ConfigEntry<bool> EnableWeightCostScaling { get; private set; }

		public static ConfigEntry<float> WeightCostFactor { get; private set; }

		public static ConfigEntry<float> StoneWeightCostFactor { get; private set; }

		public static ConfigEntry<bool> EnableOreCostScaling { get; private set; }

		public static ConfigEntry<float> OreCostFactor { get; private set; }

		public static ConfigEntry<float> StoneOreCostFactor { get; private set; }

		public static ConfigEntry<bool> EnableDistanceCostScaling { get; private set; }

		public static ConfigEntry<float> DistanceCostBase { get; private set; }

		public static ConfigEntry<float> StoneDistanceCostBase { get; private set; }

		public static ConfigEntry<float> DistanceCostExponent { get; private set; }

		public static ConfigEntry<float> StoneDistanceCostExponent { get; private set; }

		public static ConfigEntry<float> MinDistanceCostMultiplier { get; private set; }

		public static ConfigEntry<LowEnergyBehavior> LowEnergyAction { get; private set; }

		public static ConfigEntry<float> OverdraftSicknessSecondsPerEnergy { get; private set; }

		public static bool DeactivateVortexWhenInsufficientEnergy => LowEnergyAction.Value == LowEnergyBehavior.RefuseTeleport;

		public static ConfigEntry<bool> ScalePortalVfxWithEnergy { get; private set; }

		public static ConfigEntry<bool> ShowHoverEnergyInfo { get; private set; }

		public static ConfigEntry<bool> EnableTeleportSickness { get; private set; }

		public static ConfigEntry<float> SicknessBaseDuration { get; private set; }

		public static ConfigEntry<float> StoneSicknessBaseDuration { get; private set; }

		public static ConfigEntry<float> SicknessBaseDistance { get; private set; }

		public static ConfigEntry<float> SicknessDurationExponent { get; private set; }

		public static ConfigEntry<bool> SicknessBlockTeleport { get; private set; }

		public static ConfigEntry<float> SicknessDurabilityLossPercent { get; private set; }

		public static ConfigEntry<float> StoneSicknessDurabilityLossPercent { get; private set; }

		public static ConfigEntry<float> SicknessCarryWeightReduction { get; private set; }

		public static ConfigEntry<float> SicknessMovementSpeed { get; private set; }

		public static ConfigEntry<float> SicknessHealthRegen { get; private set; }

		public static ConfigEntry<float> SicknessStaminaRegen { get; private set; }

		public static ConfigEntry<float> SicknessEitrRegen { get; private set; }

		public static ConfigEntry<bool> LogPortalTeleports { get; private set; }

		public static void Init(ConfigFile config)
		{
			ModEnabled = Bind(config, "1 - General", "General", "Enabled", defaultValue: true, "Master switch to enable or disable all features of PortalTweaks.", 100);
			PortalLoadTime = Bind(config, "1 - General", "General", "PortalLoadTime", 8f, "Portal teleport load / transition screen duration in seconds. Vanilla default is 8.0 seconds.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 30f));
			EnableTargetSelectionUI = Bind(config, "2 - Portal UI", "Portal UI", "EnableTargetSelectionUI", defaultValue: true, "If true, interacting with a portal displays a destination selection dropdown to pick outbound portals.", 100);
			EnablePasswordSystem = Bind(config, "2 - Portal UI", "Portal UI", "EnablePasswordSystem", defaultValue: false, "If true, enables portal passwords. Portals only connect if passwords match.", 90);
			EnablePortalProximityLimit = Bind(config, "3 - Portal Proximity", "Portal Proximity", "EnablePortalProximityLimit", defaultValue: false, "If true, enforces a minimum distance between any two portals to prevent portal stacking.", 100);
			MinDistanceBetweenPortals = Bind(config, "3 - Portal Proximity", "Portal Proximity", "MinDistanceBetweenPortals", 400f, "Minimum distance (in meters) required between any two portals in the world.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2000f));
			ItemRestrictionTierConfig = Bind(config, "4 - Item Restrictions", "Item Restrictions", "RestrictionTier", ItemRestrictionTier.Vanilla, "Progressive item filtering tier for standard wood portals:\n- NoTeleport: Portals cannot be used at all.\n- Naked: Inventory must be 100% empty.\n- EquippedOnly: Only actively worn armor and wielded items allowed.\n- EquipmentOnly: Weapons, armor, and tools allowed (in bag or equipped), but no materials or food.\n- GearAndProvisions: Equipment + food, potions, and ammo allowed, but no raw materials or loot.\n- Vanilla: Standard Valheim rules (materials allowed, but ores/metals blocked).\n- Unrestricted: All items allowed, including ores and metals.", 100);
			StoneRestrictionTier = Bind(config, "4 - Item Restrictions", "Item Restrictions", "StoneRestrictionTier", ItemRestrictionTier.Unrestricted, "Progressive item filtering tier for Ashlands Stone Portals. Defaults to Unrestricted.", 90);
			ShowHoverItemWarning = Bind(config, "4 - Item Restrictions", "Item Restrictions", "ShowHoverItemWarning", defaultValue: true, "If true, displays a warning in the portal hover text when carrying items disallowed by the active tier.", 80, null, isAdminOnly: false);
			EnableMaxWeightLimit = Bind(config, "5 - Weight Limit", "Weight Limit", "EnableMaxWeightLimit", defaultValue: false, "If true, portals only allow players through if their total inventory weight is below MaxPortalWeight.", 100);
			MaxPortalWeight = Bind(config, "5 - Weight Limit", "Weight Limit", "MaxPortalWeight", 300f, "Maximum player inventory weight allowed to use wood portals.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2000f));
			StoneMaxPortalWeight = Bind(config, "5 - Weight Limit", "Weight Limit", "StoneMaxPortalWeight", 600f, "Maximum player inventory weight allowed to use Stone Portals.", 85, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2000f));
			ShowHoverWeightWarning = Bind(config, "5 - Weight Limit", "Weight Limit", "ShowHoverWeightWarning", defaultValue: true, "If true, displays a warning in the portal hover text when carrying too much weight.", 80, null, isAdminOnly: false);
			EnableDistanceLimit = Bind(config, "6 - Distance Limit", "Distance Limit", "EnableDistanceLimit", defaultValue: false, "If true, portals only connect and allow travel if the distance between them is within configured limits.", 100);
			MaxPortalDistance = Bind(config, "6 - Distance Limit", "Distance Limit", "MaxPortalDistance", 2000f, "Maximum distance (in meters) allowed between connected wood portals.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(50f, 20000f));
			StoneMaxPortalDistance = Bind(config, "6 - Distance Limit", "Distance Limit", "StoneMaxPortalDistance", 4000f, "Maximum distance (in meters) allowed between connected Stone Portals.", 85, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(50f, 20000f));
			EnableMinDistanceLimit = Bind(config, "6 - Distance Limit", "Distance Limit", "EnableMinDistanceLimit", defaultValue: false, "If true, portals also require a minimum distance between them to activate.", 80);
			MinPortalDistance = Bind(config, "6 - Distance Limit", "Distance Limit", "MinPortalDistance", 100f, "Minimum distance (in meters) required between connected wood portals.", 75, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2000f));
			StoneMinPortalDistance = Bind(config, "6 - Distance Limit", "Distance Limit", "StoneMinPortalDistance", 100f, "Minimum distance (in meters) required between connected Stone Portals.", 70, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2000f));
			ShowDistanceInHoverText = Bind(config, "6 - Distance Limit", "Distance Limit", "ShowDistanceInHoverText", defaultValue: true, "If true, displays the distance to the connected portal on the portal hover HUD text.", 60, null, isAdminOnly: false);
			EnableEnergySystem = Bind(config, "7 - Energy System", "Energy System", "EnableEnergySystem", defaultValue: false, "If true, portals require and consume energy to teleport players, recharging over time.", 100);
			MaxPortalEnergy = Bind(config, "7 - Energy System", "Energy System", "MaxPortalEnergy", 360f, "Maximum energy reserve a wood portal can store.", 95, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 3000f));
			StoneMaxPortalEnergy = Bind(config, "7 - Energy System", "Energy System", "StoneMaxPortalEnergy", 1080f, "Maximum energy reserve a Stone Portal can store.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 3000f));
			EnergyRechargeRate = Bind(config, "7 - Energy System", "Energy System", "EnergyRechargeRate", 0.1f, "Rate at which wood portal energy recharges per second.", 85, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 50f));
			StoneEnergyRechargeRate = Bind(config, "7 - Energy System", "Energy System", "StoneEnergyRechargeRate", 0.2f, "Rate at which Stone Portal energy recharges per second.", 80, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 50f));
			StartingEnergyPercentage = Bind(config, "7 - Energy System", "Energy System", "StartingEnergyPercentage", 0f, "Starting energy percentage (0-100%) when a portal is newly placed or first discovered. Defaults to 0%.", 75, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 100f));
			BaseTeleportCost = Bind(config, "7 - Energy System", "Energy System", "BaseTeleportCost", 6f, "Base fixed energy cost deducted per wood portal teleport.", 70, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 500f));
			StoneBaseTeleportCost = Bind(config, "7 - Energy System", "Energy System", "StoneBaseTeleportCost", 6f, "Base fixed energy cost deducted per teleport on Stone Portals.", 65, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 500f));
			EnableWeightCostScaling = Bind(config, "7 - Energy System", "Energy System", "EnableWeightCostScaling", defaultValue: true, "If true, player inventory carry weight adds additional energy cost to the teleport.", 60);
			WeightCostFactor = Bind(config, "7 - Energy System", "Energy System", "WeightCostFactor", 0.1f, "Additional energy cost per unit of inventory weight carried on wood portals.", 55, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f));
			StoneWeightCostFactor = Bind(config, "7 - Energy System", "Energy System", "StoneWeightCostFactor", 0.1f, "Additional energy cost per unit of inventory weight carried on Stone Portals.", 50, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f));
			EnableOreCostScaling = Bind(config, "7 - Energy System", "Energy System", "EnableOreCostScaling", defaultValue: true, "If true, carrying non-teleportable items (ores/metals) adds an additional energy surcharge per weight unit.", 45);
			OreCostFactor = Bind(config, "7 - Energy System", "Energy System", "OreCostFactor", 0.9f, "Extra energy cost per unit of non-teleportable ore/metal weight in inventory for wood portals.", 40, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 5f));
			StoneOreCostFactor = Bind(config, "7 - Energy System", "Energy System", "StoneOreCostFactor", 0f, "Extra energy cost per unit of non-teleportable ore/metal weight on Stone Portals.", 35, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 5f));
			EnableDistanceCostScaling = Bind(config, "7 - Energy System", "Energy System", "EnableDistanceCostScaling", defaultValue: true, "If true, scales the teleport energy cost based on the distance between portals.", 30);
			DistanceCostBase = Bind(config, "7 - Energy System", "Energy System", "DistanceCostBase", 1000f, "Base distance (in meters) for cost scaling on wood portals: mult(dist) = (dist / base)^exponent.", 25, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 20000f));
			StoneDistanceCostBase = Bind(config, "7 - Energy System", "Energy System", "StoneDistanceCostBase", 1000f, "Base distance (in meters) for cost scaling on Stone Portals.", 22, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 20000f));
			DistanceCostExponent = Bind(config, "7 - Energy System", "Energy System", "DistanceCostExponent", 1.5f, "Exponent for distance multiplier formula on wood portals: mult(dist) = (dist / base)^exponent.", 20, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 4f));
			StoneDistanceCostExponent = Bind(config, "7 - Energy System", "Energy System", "StoneDistanceCostExponent", 1f, "Exponent for distance multiplier formula on Stone Portals.", 18, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 4f));
			MinDistanceCostMultiplier = Bind(config, "7 - Energy System", "Energy System", "MinDistanceCostMultiplier", 0.25f, "Minimum floor for the distance cost multiplier. Defaults to 0.25.", 15, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 10f));
			LowEnergyAction = Bind(config, "7 - Energy System", "Energy System", "LowEnergyAction", LowEnergyBehavior.RefuseTeleport, "Behavior when attempting to teleport through a portal with insufficient energy:\n- RefuseTeleport: Overdraft disabled; portal rejects the jump until recharged.\n- ExtendSickness: Overdraft enabled; drains energy to 0 and extends Teleportation Sickness duration.", 12);
			OverdraftSicknessSecondsPerEnergy = Bind(config, "7 - Energy System", "Energy System", "OverdraftSicknessSecondsPerEnergy", 10f, "Additional Teleportation Sickness seconds added per point of energy deficit when overdrafting.", 10, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 120f));
			ScalePortalVfxWithEnergy = Bind(config, "7 - Energy System", "Energy System", "ScalePortalVfxWithEnergy", defaultValue: true, "If true, scales the emission brightness of the portal arch based on current energy reserve (dim when empty, glowing brighter as it recharges).", 5, null, isAdminOnly: false);
			ShowHoverEnergyInfo = Bind(config, "7 - Energy System", "Energy System", "ShowHoverEnergyInfo", defaultValue: true, "If true, displays portal energy reserve and recharge status on the portal hover HUD text.", 2, null, isAdminOnly: false);
			EnableTeleportSickness = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "EnableTeleportSickness", defaultValue: false, "If true, imposes a temporary debuff on players upon teleporting.", 100);
			SicknessBlockTeleport = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "SicknessBlockTeleport", defaultValue: true, "If true, prevents players from using portals while affected by teleportation sickness.", 95);
			SicknessBaseDuration = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "SicknessBaseDuration", 180f, "Base debuff duration (in seconds) at base distance for wood portals: duration(dist) = base_duration * (dist / base_dist)^exponent.", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 600f));
			StoneSicknessBaseDuration = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "StoneSicknessBaseDuration", 120f, "Base debuff duration (in seconds) at base distance for Stone Portals.", 85, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 600f));
			SicknessBaseDistance = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "SicknessBaseDistance", 1000f, "Base distance (in meters) for sickness duration scaling.", 80, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 20000f));
			SicknessDurationExponent = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "SicknessDurationExponent", 0.5f, "Exponent for sickness duration scaling: duration(dist) = base_duration * (dist / base_dist)^exponent.", 75, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 5f));
			SicknessDurabilityLossPercent = Bind(config, "8 - Teleportation Sickness", "Teleportation Sickness", "SicknessDurabilityLossPercent", 10f, "Instant percentage of max durability dama