Decompiled source of FullingPorter v0.1.3

FullingPorter.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FullingPorter.Contract;
using FullingPorter.Core;
using FullingPorter.Localization;
using FullingPorter.Network;
using FullingPorter.Porter;
using FullingPorter.Storage;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("FullingPorter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3")]
[assembly: AssemblyProduct("FullingPorter")]
[assembly: AssemblyTitle("FullingPorter")]
[assembly: AssemblyVersion("0.1.3.0")]
namespace FullingPorter
{
	[BepInPlugin("Likhtenvald.FullingPorter", "FullingPorter", "0.1.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Likhtenvald.FullingPorter";

		public const string PluginName = "FullingPorter";

		public const string PluginVersion = "0.1.3";

		public const string QuickStackPlusGuid = "Goneryx.QuickStackPlus";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> ContractPrice;

		internal static ConfigEntry<float> WorkRadius;

		internal static ConfigEntry<int> MaxStacksPerTrip;

		internal static ConfigEntry<KeyCode> DismissKey;

		internal static ConfigEntry<KeyCode> RenameKey;

		internal static ConfigEntry<KeyCode> SourceChestKey;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00ab: 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_00b8: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			ContractPrice = ((BaseUnityPlugin)this).Config.Bind<int>("Contract", "Price", 1500, new ConfigDescription("Haldor contract price in coins. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { val }));
			WorkRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Porter", "WorkRadius", 30f, new ConfigDescription("Maximum work radius in metres. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			MaxStacksPerTrip = ((BaseUnityPlugin)this).Config.Bind<int>("Porter", "MaxStacksPerTrip", 10, new ConfigDescription("Maximum distinct stacks carried per trip. Synchronized from the server.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			DismissKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "DismissKey", (KeyCode)127, "Key used while looking at a porter to dismiss it. Press twice to confirm.");
			RenameKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "RenameKey", (KeyCode)279, "Key used while looking at a porter to rename it.");
			SourceChestKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Porter", "SourceChestKey", (KeyCode)278, "Key used while looking at a container to toggle it as a porter source.");
			PorterRpc.Register();
			_harmony = new Harmony("Likhtenvald.FullingPorter");
			_harmony.PatchAll();
			LocalizationRegistry.Register();
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FullingPorter 0.1.3 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnVanillaPrefabsAvailable()
		{
			ContractRegistry.Register();
			PorterPrefabRegistry.Register();
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
		}
	}
}
namespace FullingPorter.Storage
{
	internal static class ContainerUseGuard
	{
		internal const string PorterLockKey = "FullingPorter.TransferLock";

		private static readonly HashSet<ZDOID> HeldLocks = new HashSet<ZDOID>();

		internal static bool IsPlayerBusy(Container container)
		{
			if (!Object.op_Implicit((Object)(object)container))
			{
				return true;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return true;
			}
			if (container.IsInUse())
			{
				return true;
			}
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				return zDO.GetInt(ZDOVars.s_inUse, 0) == 1;
			}
			return true;
		}

		internal static bool IsPorterLocked(Container container)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 != null)
			{
				return val2.GetBool("FullingPorter.TransferLock", false);
			}
			return false;
		}

		internal static bool TryAcquire(Container container, out bool newlyAcquired)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			newlyAcquired = false;
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || IsPlayerBusy(container))
			{
				return false;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return false;
			}
			if (HeldLocks.Contains(val.m_uid))
			{
				return true;
			}
			if (val.GetBool("FullingPorter.TransferLock", false))
			{
				Plugin.Log.LogWarning((object)$"Clearing orphaned porter lock on container {val.m_uid}.");
				val.Set("FullingPorter.TransferLock", false);
			}
			val.Set("FullingPorter.TransferLock", true);
			HeldLocks.Add(val.m_uid);
			newlyAcquired = true;
			return true;
		}

		internal static string DescribeState(Container container)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 == null)
			{
				return "unavailable";
			}
			return $"id={val2.m_uid}, localInUse={container.IsInUse()}, " + $"networkInUse={val2.GetInt(ZDOVars.s_inUse, 0)}, " + string.Format("porterLocked={0}, ", val2.GetBool("FullingPorter.TransferLock", false)) + $"serverHeld={HeldLocks.Contains(val2.m_uid)}";
		}

		internal static void Release(Container container)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 != null && HeldLocks.Remove(val2.m_uid) && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				val2.Set("FullingPorter.TransferLock", false);
			}
		}

		internal static void ClearStaleLock(Container container)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 != null)
			{
				HeldLocks.Remove(val2.m_uid);
				if (val2.GetBool("FullingPorter.TransferLock", false))
				{
					val2.Set("FullingPorter.TransferLock", false);
				}
			}
		}
	}
	internal static class QuickStackPlusBridge
	{
		internal const string StorageFilterZdoKey = "Goneryx.QuickStackPlus.StorageFilter";

		internal const char Separator = '\u001f';

		internal static HashSet<string> GetAcceptedItemIds(Container container)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if ((Object)(object)container == (Object)null)
			{
				return hashSet;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return hashSet;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return hashSet;
			}
			string text = zDO.GetString("Goneryx.QuickStackPlus.StorageFilter", string.Empty);
			if (string.IsNullOrEmpty(text))
			{
				return hashSet;
			}
			string[] array = text.Split(new char[1] { '\u001f' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string item in array)
			{
				hashSet.Add(item);
			}
			return hashSet;
		}

		internal static bool Accepts(Container container, ItemData item)
		{
			if ((Object)(object)item?.m_dropPrefab == (Object)null)
			{
				return false;
			}
			return GetAcceptedItemIds(container).Contains(((Object)item.m_dropPrefab).name);
		}
	}
	internal static class SourceContainerMarker
	{
		internal const string SourceKey = "FullingPorter.Source";

		internal static bool IsSource(Container container)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				ZDO zDO = val.GetZDO();
				if (zDO == null)
				{
					return false;
				}
				return zDO.GetBool("FullingPorter.Source", false);
			}
			return false;
		}

		internal static bool TryToggleServer(Container container, out bool enabled)
		{
			enabled = false;
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			ZDO zDO = val.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			enabled = !zDO.GetBool("FullingPorter.Source", false);
			zDO.Set("FullingPorter.Source", enabled);
			return true;
		}
	}
	internal static class TransferService
	{
		internal enum TransferResult
		{
			Success,
			WaitingForOwnership,
			SourceUnavailable,
			DestinationRejected,
			DestinationFull,
			ContainerBusy,
			Failed
		}

		private sealed class InventorySnapshot : IDisposable
		{
			private readonly Inventory _inventory;

			private readonly List<ItemData> _items;

			private bool _committed;

			internal InventorySnapshot(Inventory inventory)
			{
				//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)
				_inventory = inventory;
				_items = new List<ItemData>();
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem != null)
					{
						ItemData val = allItem.Clone();
						val.m_stack = allItem.m_stack;
						val.m_gridPos = allItem.m_gridPos;
						_items.Add(val);
					}
				}
			}

			internal void Commit()
			{
				_committed = true;
			}

			public void Dispose()
			{
				//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)
				if (_committed)
				{
					return;
				}
				foreach (ItemData item in new List<ItemData>(_inventory.GetAllItems()))
				{
					_inventory.RemoveItem(item);
				}
				foreach (ItemData item2 in _items)
				{
					ItemData val = item2.Clone();
					val.m_stack = item2.m_stack;
					val.m_gridPos = item2.m_gridPos;
					_inventory.AddItem(val);
				}
			}
		}

		internal static TransferResult TryMoveWholeStack(Container source, Container destination, ItemData item)
		{
			if (!Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)destination) || item == null || (Object)(object)source == (Object)(object)destination)
			{
				return TransferResult.Failed;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return TransferResult.Failed;
			}
			if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination))
			{
				ContainerUseGuard.Release(destination);
				ContainerUseGuard.Release(source);
				return TransferResult.ContainerBusy;
			}
			ZNetView component = ((Component)source).GetComponent<ZNetView>();
			ZNetView component2 = ((Component)destination).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component.IsValid() || !component2.IsValid())
			{
				return TransferResult.Failed;
			}
			bool newlyAcquired = false;
			bool newlyAcquired2 = false;
			if (!ContainerUseGuard.TryAcquire(source, out newlyAcquired))
			{
				ContainerUseGuard.Release(destination);
				ContainerUseGuard.Release(source);
				return TransferResult.ContainerBusy;
			}
			if (!ContainerUseGuard.TryAcquire(destination, out newlyAcquired2))
			{
				ContainerUseGuard.Release(destination);
				ContainerUseGuard.Release(source);
				return TransferResult.ContainerBusy;
			}
			if (newlyAcquired || newlyAcquired2)
			{
				return TransferResult.WaitingForOwnership;
			}
			try
			{
				if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination))
				{
					return TransferResult.ContainerBusy;
				}
				if (!component.IsOwner())
				{
					component.ClaimOwnership();
				}
				if (!component2.IsOwner())
				{
					component2.ClaimOwnership();
				}
				if (!component.IsOwner() || !component2.IsOwner())
				{
					return TransferResult.WaitingForOwnership;
				}
				if (ContainerUseGuard.IsPlayerBusy(source) || ContainerUseGuard.IsPlayerBusy(destination))
				{
					return TransferResult.ContainerBusy;
				}
				Inventory inventory = source.GetInventory();
				Inventory inventory2 = destination.GetInventory();
				if (inventory == null || inventory2 == null)
				{
					return TransferResult.Failed;
				}
				ItemData val = FindMatchingSourceStack(inventory, item);
				if (val == null)
				{
					return TransferResult.SourceUnavailable;
				}
				if (!QuickStackPlusBridge.Accepts(destination, val))
				{
					return TransferResult.DestinationRejected;
				}
				if (!inventory2.CanAddItem(val, -1))
				{
					return TransferResult.DestinationFull;
				}
				using InventorySnapshot inventorySnapshot = new InventorySnapshot(inventory2);
				ItemData val2 = val.Clone();
				val2.m_stack = val.m_stack;
				val2.m_equipped = false;
				if (!inventory2.AddItem(val2))
				{
					return TransferResult.Failed;
				}
				if (!inventory.RemoveItem(val))
				{
					return TransferResult.SourceUnavailable;
				}
				inventorySnapshot.Commit();
				return TransferResult.Success;
			}
			finally
			{
				ContainerUseGuard.Release(destination);
				ContainerUseGuard.Release(source);
			}
		}

		internal static ItemData FindMatchingSourceStack(Inventory inventory, ItemData planned)
		{
			if (inventory == null || (Object)(object)planned?.m_dropPrefab == (Object)null)
			{
				return null;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if ((Object)(object)allItem?.m_dropPrefab == (Object)null || allItem.m_gridPos.x != planned.m_gridPos.x || allItem.m_gridPos.y != planned.m_gridPos.y || ((Object)allItem.m_dropPrefab).name != ((Object)planned.m_dropPrefab).name || allItem.m_stack != planned.m_stack || allItem.m_quality != planned.m_quality || allItem.m_variant != planned.m_variant)
				{
					continue;
				}
				Dictionary<string, string> customData = allItem.m_customData;
				Dictionary<string, string> customData2 = planned.m_customData;
				if (customData == null || customData2 == null)
				{
					if (customData == customData2)
					{
						return allItem;
					}
				}
				else
				{
					if (customData.Count != customData2.Count)
					{
						continue;
					}
					bool flag = true;
					foreach (KeyValuePair<string, string> item in customData2)
					{
						if (!customData.TryGetValue(item.Key, out var value) || value != item.Value)
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						return allItem;
					}
				}
			}
			return null;
		}
	}
}
namespace FullingPorter.Porter
{
	internal sealed class PorterInteraction : MonoBehaviour, Interactable, TextReceiver
	{
		private const float DismissConfirmSeconds = 3f;

		private PorterState _state;

		private ZNetView _view;

		private PorterPersonality _personality;

		private float _dismissConfirmUntil;

		private void Awake()
		{
			_state = ((Component)this).GetComponent<PorterState>();
			_view = ((Component)this).GetComponent<ZNetView>();
			_personality = ((Component)this).GetComponent<PorterPersonality>();
		}

		private void Update()
		{
			//IL_001c: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			bool num = Plugin.DismissKey != null && Input.GetKeyDown(Plugin.DismissKey.Value);
			bool flag = Plugin.RenameKey != null && Input.GetKeyDown(Plugin.RenameKey.Value);
			if (!num && !flag)
			{
				return;
			}
			GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
			if (!((Object)(object)hoverObject == (Object)null) && !((Object)(object)hoverObject.GetComponentInParent<PorterInteraction>() != (Object)(object)this))
			{
				if (flag)
				{
					RequestRename();
				}
				else if (Time.time <= _dismissConfirmUntil)
				{
					PorterRpc.RequestDismiss(_view);
					_dismissConfirmUntil = 0f;
				}
				else
				{
					_dismissConfirmUntil = Time.time + 3f;
					((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismiss_confirm", 0, (Sprite)null, false);
				}
			}
		}

		public bool Interact(Humanoid character, bool hold, bool alt)
		{
			if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if ((Object)(object)_personality != (Object)null)
			{
				return _personality.React(Player.m_localPlayer);
			}
			return false;
		}

		private void RequestRename()
		{
			if ((Object)(object)TextInput.instance != (Object)null)
			{
				TextInput.instance.RequestText((TextReceiver)(object)this, "$fullingporter_rename_title", 24);
			}
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public string GetText()
		{
			return _state?.PorterName ?? string.Empty;
		}

		public void SetText(string text)
		{
			PorterRpc.RequestRename(_view, text);
		}
	}
	internal sealed class PorterMovementAI : BaseAI
	{
		internal void WalkTo(Vector3 point, float stopDistance)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			((BaseAI)this).MoveTo(Time.deltaTime, point, stopDistance, false);
		}

		internal void Halt()
		{
			((BaseAI)this).StopMoving();
		}
	}
	internal sealed class PorterPersonality : MonoBehaviour
	{
		private static readonly string[] IdleLines = new string[6] { "$fullingporter_talk_idle_01", "$fullingporter_talk_idle_02", "$fullingporter_talk_idle_03", "$fullingporter_talk_idle_04", "$fullingporter_talk_idle_05", "$fullingporter_talk_idle_06" };

		private static readonly string[] WorkingLines = new string[7] { "$fullingporter_talk_work_01", "$fullingporter_talk_work_02", "$fullingporter_talk_work_03", "$fullingporter_talk_work_05", "$fullingporter_talk_work_06", "$fullingporter_talk_work_07", "$fullingporter_talk_work_04" };

		private static readonly string[] ReturningLines = new string[4] { "$fullingporter_talk_return_01", "$fullingporter_talk_return_02", "$fullingporter_talk_return_03", "$fullingporter_talk_idle_040" };

		private static readonly string[] BlockedLines = new string[4] { "$fullingporter_talk_blocked_01", "$fullingporter_talk_blocked_02", "$fullingporter_talk_blocked_03", "$fullingporter_talk_idle_041" };

		private static readonly string[] VoicePrefabs = new string[4] { "sfx_goblin_idle", "sfx_goblin_idle", "sfx_goblin_idle", "sfx_goblin_alerted" };

		private PorterWorker _worker;

		private PorterState _state;

		private ZNetView _view;

		private float _nextReactionTime;

		private float _nextAmbientReactionTime;

		private const float ReactionCooldown = 2.5f;

		internal const float AmbientHearDistance = 18f;

		private const float AmbientMinInterval = 18f;

		private const float AmbientMaxInterval = 42f;

		private void Awake()
		{
			_worker = ((Component)this).GetComponent<PorterWorker>();
			_state = ((Component)this).GetComponent<PorterState>();
			_view = ((Component)this).GetComponent<ZNetView>();
			ScheduleNextAmbient();
		}

		private void Update()
		{
			//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_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)
			if (Time.time < _nextAmbientReactionTime)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				ScheduleNextAmbient();
				return;
			}
			Vector3 val = ((Component)localPlayer).transform.position - ((Component)this).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude > 324f)
			{
				_nextAmbientReactionTime = Time.time + 5f;
				return;
			}
			PorterRpc.RequestSpeak(_view, ambient: true);
			ScheduleNextAmbient();
		}

		internal bool React(Player player)
		{
			if ((Object)(object)player == (Object)null || Time.time < _nextReactionTime)
			{
				return false;
			}
			if (!PorterRpc.RequestSpeak(_view, ambient: false))
			{
				return false;
			}
			_nextReactionTime = Time.time + 2.5f;
			if (_nextAmbientReactionTime < Time.time + 8f)
			{
				_nextAmbientReactionTime = Time.time + 8f;
			}
			return true;
		}

		internal bool TrySpeakServer(bool ambient)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)_view == (Object)null || !_view.IsValid() || Time.time < _nextReactionTime || (ambient && Time.time < _nextAmbientReactionTime))
			{
				return false;
			}
			_nextReactionTime = Time.time + 2.5f;
			ScheduleNextAmbient();
			string line = PickLine();
			int voiceIndex = Random.Range(0, VoicePrefabs.Length);
			PlaySpeech(line, voiceIndex);
			PorterRpc.BroadcastSpeech(_view, line, voiceIndex);
			return true;
		}

		internal void PlaySpeech(string line, int voiceIndex)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(line) && voiceIndex >= 0 && voiceIndex < VoicePrefabs.Length && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position;
				if (!(((Vector3)(ref val)).sqrMagnitude > 400f))
				{
					string text = LocalizationManager.Instance.TryTranslate(line);
					ShowSpeechBubble(Player.m_localPlayer, text);
					PlayVoice(voiceIndex);
				}
			}
		}

		private void ScheduleNextAmbient()
		{
			_nextAmbientReactionTime = Time.time + Random.Range(18f, 42f);
		}

		private void ShowSpeechBubble(Player player, string text)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Chat.instance == (Object)null)
			{
				((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
				return;
			}
			string text2 = (((Object)(object)_state != (Object)null) ? _state.PorterName : "$fullingporter_name");
			text2 = LocalizationManager.Instance.TryTranslate(text2);
			Chat.instance.SetNpcText(((Component)this).gameObject, Vector3.up * 1.8f, 20f, 4f, text2, text, false);
		}

		private string PickLine()
		{
			string[] array = (((Object)(object)_worker != (Object)null) ? _worker.GetDialogueContext() : PorterDialogueContext.Idle) switch
			{
				PorterDialogueContext.Working => WorkingLines, 
				PorterDialogueContext.Returning => ReturningLines, 
				PorterDialogueContext.Blocked => BlockedLines, 
				_ => IdleLines, 
			};
			return array[Random.Range(0, array.Length)];
		}

		private void PlayVoice(int voiceIndex)
		{
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			string text = VoicePrefabs[voiceIndex];
			GameObject prefab = PrefabManager.Instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogDebug((object)("Porter voice prefab not found: " + text));
			}
			else
			{
				Object.Instantiate<GameObject>(prefab, ((Component)this).transform.position + Vector3.up * 1.2f, Quaternion.identity);
			}
		}
	}
	internal static class PorterPrefabRegistry
	{
		internal const string PrefabName = "FullingPorter_Fuling";

		internal static void Register()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab("Goblin");
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogError((object)"Vanilla Goblin prefab was not found.");
				return;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("FullingPorter_Fuling", prefab);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Could not clone the porter prefab from vanilla Goblin.");
				return;
			}
			MonsterAI component = val.GetComponent<MonsterAI>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			PorterMovementAI porterMovementAI = val.GetComponent<PorterMovementAI>();
			if ((Object)(object)porterMovementAI == (Object)null)
			{
				porterMovementAI = val.AddComponent<PorterMovementAI>();
			}
			((BaseAI)porterMovementAI).m_pathAgentType = (AgentType)1;
			Character component2 = val.GetComponent<Character>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_faction = (Faction)0;
			}
			CustomPrefab val2 = new CustomPrefab(val, true);
			val.AddComponent<PorterState>();
			val.AddComponent<PorterWorker>();
			val.AddComponent<PorterPersonality>();
			val.AddComponent<PorterInteraction>();
			PrefabManager.Instance.AddPrefab(val2);
		}
	}
	internal sealed class PorterState : MonoBehaviour, Hoverable
	{
		internal const string WorldPresenceKey = "FullingPorter.ActivePorter";

		private const string WorldPresencePrefix = "FullingPorter.ActivePorter:";

		private const string NameKey = "FullingPorter.Name";

		private const string HomeKey = "FullingPorter.Home";

		private const string HasHomeKey = "FullingPorter.HasHome";

		private const string DefaultName = "$fullingporter_name";

		private static PorterState _serverActive;

		private ZNetView _view;

		private Character _character;

		private PorterWorker _worker;

		private float _nextNameRefresh;

		private const float NameRefreshInterval = 0.5f;

		internal string PorterName
		{
			get
			{
				if ((Object)(object)_view == (Object)null || !_view.IsValid())
				{
					return "$fullingporter_name";
				}
				return _view.GetZDO().GetString("FullingPorter.Name", "$fullingporter_name");
			}
			set
			{
				SetNameServer(value);
			}
		}

		private void Awake()
		{
			_view = ((Component)this).GetComponent<ZNetView>();
			_character = ((Component)this).GetComponent<Character>();
			_worker = ((Component)this).GetComponent<PorterWorker>();
			ApplyDisplayName();
		}

		private void Update()
		{
			if (!(Time.time < _nextNameRefresh))
			{
				_nextNameRefresh = Time.time + 0.5f;
				if ((Object)(object)_character != (Object)null && _character.m_name != PorterName)
				{
					ApplyDisplayName();
				}
			}
		}

		private void Start()
		{
			ApplyDisplayName();
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			if ((Object)(object)_serverActive != (Object)null && (Object)(object)_serverActive != (Object)(object)this)
			{
				Plugin.Log.LogWarning((object)"Duplicate porter detected on server; removing the newer instance.");
				if ((Object)(object)_view != (Object)null && _view.IsValid() && !_view.IsOwner())
				{
					_view.ClaimOwnership();
				}
				if ((Object)(object)ZNetScene.instance != (Object)null)
				{
					ZNetScene.instance.Destroy(((Component)this).gameObject);
				}
			}
			else
			{
				_serverActive = this;
				MarkWorldOccupied(_view);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_serverActive == (Object)(object)this)
			{
				_serverActive = null;
			}
		}

		internal static bool WorldHasPorter()
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_serverActive != (Object)null)
			{
				return true;
			}
			PorterState[] array = Object.FindObjectsByType<PorterState>((FindObjectsSortMode)0);
			foreach (PorterState porterState in array)
			{
				if (!((Object)(object)porterState == (Object)null))
				{
					_serverActive = porterState;
					MarkWorldOccupied(porterState._view);
					return true;
				}
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			bool flag = false;
			List<string> list = new List<string>();
			foreach (string globalKey in instance.GetGlobalKeys())
			{
				if (globalKey == "FullingPorter.ActivePorter")
				{
					flag = true;
				}
				else if (globalKey.StartsWith("FullingPorter.ActivePorter:"))
				{
					if (TryParseWorldKey(globalKey, out var id) && ZDOMan.instance != null && ZDOMan.instance.GetZDO(id) != null)
					{
						return true;
					}
					list.Add(globalKey);
				}
			}
			foreach (string item in list)
			{
				instance.RemoveGlobalKey(item);
			}
			if (flag)
			{
				instance.RemoveGlobalKey("FullingPorter.ActivePorter");
			}
			return false;
		}

		internal static void MarkWorldOccupied(ZNetView view)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (!((Object)(object)instance == (Object)null) && val != null)
			{
				if (instance.GetGlobalKey("FullingPorter.ActivePorter"))
				{
					instance.RemoveGlobalKey("FullingPorter.ActivePorter");
				}
				string text = MakeWorldKey(val.m_uid);
				if (!instance.GetGlobalKey(text))
				{
					instance.SetGlobalKey(text);
				}
			}
		}

		internal static void ClearWorldOccupied()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			foreach (string item in new List<string>(instance.GetGlobalKeys()))
			{
				if (item == "FullingPorter.ActivePorter" || item.StartsWith("FullingPorter.ActivePorter:"))
				{
					instance.RemoveGlobalKey(item);
				}
			}
			_serverActive = null;
		}

		private static string MakeWorldKey(ZDOID id)
		{
			return "FullingPorter.ActivePorter:" + ((ZDOID)(ref id)).UserID.ToString(CultureInfo.InvariantCulture) + ":" + ((ZDOID)(ref id)).ID.ToString(CultureInfo.InvariantCulture);
		}

		private static bool TryParseWorldKey(string key, out ZDOID id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			id = ZDOID.None;
			if (string.IsNullOrEmpty(key) || !key.StartsWith("FullingPorter.ActivePorter:"))
			{
				return false;
			}
			string[] array = key.Substring("FullingPorter.ActivePorter:".Length).Split(new char[1] { ':' });
			if (array.Length != 2)
			{
				return false;
			}
			if (!long.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !uint.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			id = new ZDOID(result, result2);
			return true;
		}

		internal Vector3 GetOrCreateHome(Vector3 fallback)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return fallback;
			}
			ZDO zDO = _view.GetZDO();
			if (zDO == null)
			{
				return fallback;
			}
			if (zDO.GetBool("FullingPorter.HasHome", false))
			{
				return zDO.GetVec3("FullingPorter.Home", fallback);
			}
			if (!_view.IsOwner())
			{
				_view.ClaimOwnership();
			}
			if (!_view.IsOwner())
			{
				return fallback;
			}
			zDO.Set("FullingPorter.Home", fallback);
			zDO.Set("FullingPorter.HasHome", true);
			return fallback;
		}

		internal bool SetNameServer(string value)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return false;
			}
			ZDO zDO = _view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			zDO.Set("FullingPorter.Name", string.IsNullOrWhiteSpace(value) ? "$fullingporter_name" : value.Trim());
			ApplyDisplayName();
			return true;
		}

		private void ApplyDisplayName()
		{
			if ((Object)(object)_character != (Object)null)
			{
				_character.m_name = PorterName;
			}
		}

		internal string BuildHoverText()
		{
			string value = (((Object)(object)_worker != (Object)null) ? _worker.GetStatusText() : "$fullingporter_status_idle");
			string text = TranslateTokenWithSuffix(PorterName);
			string text2 = TranslateTokenWithSuffix(value);
			return text + "\n" + text2;
		}

		private static string TranslateTokenWithSuffix(string value)
		{
			if (string.IsNullOrEmpty(value) || value[0] != '$')
			{
				return value;
			}
			int num = value.IndexOf(" (");
			if (num < 0)
			{
				return LocalizationManager.Instance.TryTranslate(value);
			}
			string text = value.Substring(0, num);
			return LocalizationManager.Instance.TryTranslate(text) + value.Substring(num);
		}

		public string GetHoverName()
		{
			return PorterName;
		}

		public float GetHoverOffset()
		{
			return 1.5f;
		}

		public string GetHoverText()
		{
			return BuildHoverText();
		}
	}
	internal sealed class PorterWorker : MonoBehaviour
	{
		private sealed class CargoEntry
		{
			internal ItemData Item;

			internal Container Destination;

			internal int TransferFailures;
		}

		private Character _character;

		private PorterMovementAI _ai;

		private ZNetView _view;

		private PorterWorkState _state;

		private Container _source;

		private readonly List<CargoEntry> _cargo = new List<CargoEntry>();

		private readonly Dictionary<Container, Dictionary<string, float>> _destinationCooldowns = new Dictionary<Container, Dictionary<string, float>>();

		private Vector3 _home;

		private float _nextScan;

		private Vector3 _moveTarget;

		private Vector3 _lastProgressPosition;

		private float _lastProgressTime;

		private bool _trackingMove;

		private float _nextTransferTime;

		private bool _homeInitialized;

		private int _activeTripCapacity;

		private ItemData _ownershipWaitItem;

		private float _ownershipWaitSince;

		private float _containerBusySince = -1f;

		private float _playerBusySince = -1f;

		private Container _activeDestination;

		private bool _returningFromWork;

		private float _blockedStatusUntil;

		private float _blockedDialogueUntil;

		private const string StatusKey = "FullingPorter.Status";

		private const string DialogueContextKey = "FullingPorter.DialogueContext";

		private const float InteractionDistance = 2.5f;

		private const float HomeDistance = 0.35f;

		private const float ScanInterval = 2f;

		private const float StuckTimeout = 8f;

		private const float ProgressDistance = 0.25f;

		private const float TransferInterval = 0.12f;

		private const float DestinationRetryCooldown = 8f;

		private const float BlockedDialogueDuration = 20f;

		private const float OwnershipWaitTimeout = 3f;

		private const float ContainerBusyTimeout = 5f;

		private const float PlayerBusyTimeout = 30f;

		private const int MaxTransferFailures = 3;

		private const float TransferFailureRetryDelay = 0.3f;

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			_character = ((Component)this).GetComponent<Character>();
			_ai = ((Component)this).GetComponent<PorterMovementAI>();
			_view = ((Component)this).GetComponent<ZNetView>();
			_home = ((Component)this).transform.position;
			if ((Object)(object)_character != (Object)null)
			{
				_character.m_faction = (Faction)0;
			}
		}

		private void Update()
		{
			//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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			if (!_view.IsOwner())
			{
				_view.ClaimOwnership();
			}
			if (_view.IsOwner())
			{
				if (!_homeInitialized)
				{
					PorterState component = ((Component)this).GetComponent<PorterState>();
					_home = (((Object)(object)component != (Object)null) ? component.GetOrCreateHome(((Component)this).transform.position) : ((Component)this).transform.position);
					_homeInitialized = true;
				}
				switch (_state)
				{
				case PorterWorkState.Idle:
					TickIdle();
					break;
				case PorterWorkState.ToSource:
					TickToSource();
					break;
				case PorterWorkState.ToDestination:
					TickToDestination();
					break;
				case PorterWorkState.ReturningHome:
					TickReturningHome();
					break;
				}
				PublishPresentation();
			}
		}

		private void TickIdle()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 2f;
			if (TryFindBatch())
			{
				_state = PorterWorkState.ToSource;
				return;
			}
			Vector3 val = ((Component)this).transform.position - _home;
			if (((Vector3)(ref val)).sqrMagnitude > 0.122499995f)
			{
				_returningFromWork = false;
				_state = PorterWorkState.ReturningHome;
			}
		}

		private void TickToSource()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!IsUsable(_source))
			{
				AbortBatch();
				return;
			}
			if (ContainerUseGuard.IsPlayerBusy(_source))
			{
				_nextTransferTime = Time.time + 0.3f;
				return;
			}
			if (!MoveTowards(((Component)_source).transform.position, 2.5f, out var stuck))
			{
				if (stuck)
				{
					AbortBatch();
				}
				return;
			}
			Inventory inventory = _source.GetInventory();
			if (inventory == null)
			{
				AbortBatch();
				return;
			}
			for (int num = _cargo.Count - 1; num >= 0; num--)
			{
				CargoEntry cargoEntry = _cargo[num];
				Inventory val = (IsUsable(cargoEntry.Destination) ? cargoEntry.Destination.GetInventory() : null);
				if (cargoEntry.Item == null || TransferService.FindMatchingSourceStack(inventory, cargoEntry.Item) == null || val == null || !QuickStackPlusBridge.Accepts(cargoEntry.Destination, cargoEntry.Item) || !val.CanAddItem(cargoEntry.Item, -1))
				{
					Plugin.Log.LogDebug((object)("Porter skipped unavailable cargo during source revalidation: " + DescribeItem(cargoEntry.Item) + "."));
					_cargo.RemoveAt(num);
				}
			}
			if (_cargo.Count == 0)
			{
				Plugin.Log.LogDebug((object)"Porter trip has no valid cargo after source revalidation.");
				FinishBatch();
			}
			else
			{
				Plugin.Log.LogDebug((object)$"Porter collected {_cargo.Count} stack(s) from source and is heading to destination.");
				_state = PorterWorkState.ToDestination;
			}
		}

		private void TickToDestination()
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (!IsUsable(_source) || _cargo.Count == 0)
			{
				FinishBatch();
				return;
			}
			if (!HasCargoForDestination(_activeDestination))
			{
				_activeDestination = FindNearestDestination();
			}
			int num = FindCargoIndexForDestination(_activeDestination);
			if (num < 0)
			{
				FinishBatch();
				return;
			}
			CargoEntry cargoEntry = _cargo[num];
			if (!IsUsable(cargoEntry.Destination) || cargoEntry.Item == null)
			{
				RemoveCargoEntryAndContinue(num);
				return;
			}
			if (ContainerUseGuard.IsPlayerBusy(_source) || ContainerUseGuard.IsPlayerBusy(cargoEntry.Destination))
			{
				WaitForPlayerContainer(cargoEntry);
				return;
			}
			ResetPlayerBusyWait();
			if (!MoveTowards(((Component)cargoEntry.Destination).transform.position, 2.5f, out var stuck))
			{
				if (stuck)
				{
					Plugin.Log.LogDebug((object)"Porter could not reach a destination; skipping this stack.");
					RemoveCargoEntryAndContinue(num);
				}
			}
			else
			{
				if (Time.time < _nextTransferTime)
				{
					return;
				}
				_nextTransferTime = Time.time + 0.12f;
				TransferService.TransferResult transferResult = TransferService.TryMoveWholeStack(_source, cargoEntry.Destination, cargoEntry.Item);
				switch (transferResult)
				{
				case TransferService.TransferResult.ContainerBusy:
					if (ContainerUseGuard.IsPlayerBusy(_source) || ContainerUseGuard.IsPlayerBusy(cargoEntry.Destination))
					{
						WaitForPlayerContainer(cargoEntry);
					}
					else
					{
						WaitForBusyContainer(cargoEntry);
					}
					return;
				case TransferService.TransferResult.WaitingForOwnership:
					if (_containerBusySince >= 0f && Time.time - _containerBusySince >= 5f)
					{
						Plugin.Log.LogWarning((object)("Porter container access did not stabilize for " + DescribeItem(cargoEntry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(cargoEntry.Destination) + "]. Returning home."));
						AbortBatch();
					}
					else if (_ownershipWaitItem != cargoEntry.Item)
					{
						_ownershipWaitItem = cargoEntry.Item;
						_ownershipWaitSince = Time.time;
						Plugin.Log.LogDebug((object)("Porter waiting for container ownership for " + DescribeItem(cargoEntry.Item) + "."));
					}
					else if (!(Time.time - _ownershipWaitSince < 3f))
					{
						Plugin.Log.LogWarning((object)("Porter ownership wait timed out for " + DescribeItem(cargoEntry.Item) + "; skipping this stack."));
						ResetOwnershipWait();
						RemoveCargoEntryAndContinue(num);
					}
					return;
				}
				ResetOwnershipWait();
				ResetContainerBusyWait();
				ResetPlayerBusyWait();
				switch (transferResult)
				{
				case TransferService.TransferResult.Success:
					ClearDestinationCooldown(cargoEntry.Destination, cargoEntry.Item);
					Plugin.Log.LogDebug((object)("Porter moved " + DescribeItem(cargoEntry.Item) + " successfully."));
					break;
				case TransferService.TransferResult.DestinationFull:
					SetDestinationCooldown(cargoEntry.Destination, cargoEntry.Item);
					Plugin.Log.LogDebug((object)("Porter destination is full for " + DescribeItem(cargoEntry.Item) + "; cooldown started."));
					break;
				case TransferService.TransferResult.DestinationRejected:
					Plugin.Log.LogDebug((object)("Porter destination no longer accepts " + DescribeItem(cargoEntry.Item) + "."));
					break;
				case TransferService.TransferResult.SourceUnavailable:
					Plugin.Log.LogDebug((object)("Porter source no longer contains " + DescribeItem(cargoEntry.Item) + "."));
					break;
				default:
					cargoEntry.TransferFailures++;
					if (PorterRules.ShouldRetryTransfer(cargoEntry.TransferFailures, 3))
					{
						_nextTransferTime = Time.time + 0.3f;
						Plugin.Log.LogDebug((object)("Porter transfer failed transiently for " + DescribeItem(cargoEntry.Item) + "; retry " + $"{cargoEntry.TransferFailures}/{2} at the same destination."));
						return;
					}
					Plugin.Log.LogWarning((object)$"Porter transfer failed {3} times for {DescribeItem(cargoEntry.Item)}; skipping this stack.");
					break;
				}
				RemoveCargoEntryAndContinue(num);
			}
		}

		private void TickReturningHome()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (MoveTowards(_home, 0.35f, out var stuck) || stuck)
			{
				_ai?.Halt();
				ResetMoveTracking();
				_state = PorterWorkState.Idle;
				_returningFromWork = false;
				_nextScan = 0f;
			}
		}

		private bool TryFindBatch()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			ClearBatch();
			CleanupDestinationCooldowns();
			float value = Plugin.WorkRadius.Value;
			float num = value * value;
			int num2 = (_activeTripCapacity = Mathf.Max(1, Plugin.MaxStacksPerTrip.Value));
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			Dictionary<Container, HashSet<string>> acceptedIds = new Dictionary<Container, HashSet<string>>();
			Dictionary<Container, Inventory> planningInventories = new Dictionary<Container, Inventory>();
			List<Container> list = new List<Container>();
			Container[] array2 = array;
			foreach (Container val in array2)
			{
				if (IsUsable(val) && !ContainerUseGuard.IsPlayerBusy(val) && SourceContainerMarker.IsSource(val))
				{
					Vector3 val2 = ((Component)val).transform.position - _home;
					if (!(((Vector3)(ref val2)).sqrMagnitude > num))
					{
						list.Add(val);
					}
				}
			}
			list.Sort(delegate(Container a, Container b)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val5 = ((Component)a).transform.position - ((Component)this).transform.position;
				float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude;
				val5 = ((Component)b).transform.position - ((Component)this).transform.position;
				return sqrMagnitude.CompareTo(((Vector3)(ref val5)).sqrMagnitude);
			});
			foreach (Container item in list)
			{
				Inventory inventory = item.GetInventory();
				List<ItemData> list2 = ((inventory != null) ? inventory.GetAllItems() : null);
				if (list2 == null || list2.Count == 0)
				{
					continue;
				}
				foreach (ItemData item2 in list2)
				{
					Container val3 = FindDestination(item2, item, num, array, acceptedIds, planningInventories);
					if (!((Object)(object)val3 == (Object)null))
					{
						ItemData val4 = item2.Clone();
						val4.m_gridPos = item2.m_gridPos;
						val4.m_stack = item2.m_stack;
						_cargo.Add(new CargoEntry
						{
							Item = val4,
							Destination = val3
						});
						if (_cargo.Count >= num2)
						{
							break;
						}
					}
				}
				if (_cargo.Count != 0)
				{
					_source = item;
					Plugin.Log.LogDebug((object)$"Porter planned trip with {_cargo.Count}/{num2} stack(s) from nearest source {((Object)item).name}.");
					return true;
				}
			}
			return false;
		}

		private Container FindDestination(ItemData item, Container source, float radiusSqr, Container[] containers, Dictionary<Container, HashSet<string>> acceptedIds, Dictionary<Container, Inventory> planningInventories)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item?.m_dropPrefab == (Object)null)
			{
				return null;
			}
			Container val = null;
			float num = float.MaxValue;
			string name = ((Object)item.m_dropPrefab).name;
			foreach (Container val2 in containers)
			{
				if (!IsUsable(val2) || ContainerUseGuard.IsPlayerBusy(val2) || (Object)(object)val2 == (Object)(object)source || SourceContainerMarker.IsSource(val2))
				{
					continue;
				}
				Vector3 val3 = ((Component)val2).transform.position - _home;
				if (((Vector3)(ref val3)).sqrMagnitude > radiusSqr)
				{
					continue;
				}
				if (!acceptedIds.TryGetValue(val2, out var value))
				{
					value = (acceptedIds[val2] = QuickStackPlusBridge.GetAcceptedItemIds(val2));
				}
				if (!value.Contains(name) || IsDestinationCoolingDown(val2, name))
				{
					continue;
				}
				Inventory planningInventory = GetPlanningInventory(val2, planningInventories);
				if (planningInventory == null)
				{
					continue;
				}
				if (!planningInventory.CanAddItem(item, -1))
				{
					SetDestinationCooldown(val2, item);
					continue;
				}
				val3 = ((Component)source).transform.position - ((Component)val2).transform.position;
				float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					val = val2;
					num = sqrMagnitude;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Inventory planningInventory2 = GetPlanningInventory(val, planningInventories);
			if (planningInventory2 == null)
			{
				return null;
			}
			ItemData val4 = item.Clone();
			val4.m_stack = item.m_stack;
			val4.m_equipped = false;
			if (!planningInventory2.AddItem(val4))
			{
				Plugin.Log.LogDebug((object)("Porter could not reserve planned capacity for " + DescribeItem(item) + " in " + ((Object)val).name + "."));
				return null;
			}
			return val;
		}

		private static Inventory GetPlanningInventory(Container container, Dictionary<Container, Inventory> planningInventories)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			if (planningInventories.TryGetValue(container, out var value))
			{
				return value;
			}
			Inventory inventory = container.GetInventory();
			if (inventory == null)
			{
				return null;
			}
			Inventory val = new Inventory("FullingPorter_Planning", (Sprite)null, inventory.GetWidth(), inventory.GetHeight());
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null)
				{
					ItemData val2 = allItem.Clone();
					val2.m_stack = allItem.m_stack;
					val2.m_equipped = false;
					if (!val.AddItem(val2))
					{
						Plugin.Log.LogDebug((object)("Porter could not mirror inventory for planning: " + ((Object)container).name + "."));
						return null;
					}
				}
			}
			planningInventories[container] = val;
			return val;
		}

		internal PorterDialogueContext GetDialogueContext()
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				ZDO val = (((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO() : null);
				int num = ((val != null) ? val.GetInt("FullingPorter.DialogueContext", 0) : 0);
				if (num < 0 || num > 3)
				{
					return PorterDialogueContext.Idle;
				}
				return (PorterDialogueContext)num;
			}
			return PorterRules.GetDialogueContext(_state, _returningFromWork, Time.time < _blockedDialogueUntil);
		}

		internal string GetStatusText()
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				ZDO obj = (((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO() : null);
				return ((obj != null) ? obj.GetString("FullingPorter.Status", "$fullingporter_status_idle") : null) ?? "$fullingporter_status_idle";
			}
			int capacity = ((_activeTripCapacity > 0) ? _activeTripCapacity : Mathf.Max(1, Plugin.MaxStacksPerTrip.Value));
			return PorterRules.GetStatusText(_state, _returningFromWork, Time.time < _blockedStatusUntil, _cargo.Count, capacity);
		}

		private void PublishPresentation()
		{
			ZDO zDO = _view.GetZDO();
			if (zDO != null)
			{
				string statusText = GetStatusText();
				if (zDO.GetString("FullingPorter.Status", string.Empty) != statusText)
				{
					zDO.Set("FullingPorter.Status", statusText);
				}
				int dialogueContext = (int)GetDialogueContext();
				if (zDO.GetInt("FullingPorter.DialogueContext", -1) != dialogueContext)
				{
					zDO.Set("FullingPorter.DialogueContext", dialogueContext);
				}
			}
		}

		private bool IsDestinationCoolingDown(Container container, string itemId)
		{
			if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(itemId))
			{
				return false;
			}
			if (!_destinationCooldowns.TryGetValue(container, out var value))
			{
				return false;
			}
			if (!value.TryGetValue(itemId, out var value2))
			{
				return false;
			}
			if (Time.time < value2)
			{
				return true;
			}
			value.Remove(itemId);
			if (value.Count == 0)
			{
				_destinationCooldowns.Remove(container);
			}
			return false;
		}

		private void SetDestinationCooldown(Container container, ItemData item)
		{
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				GameObject dropPrefab = item.m_dropPrefab;
				obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			string text = (string)obj;
			if (!((Object)(object)container == (Object)null) && !string.IsNullOrEmpty(text))
			{
				if (!_destinationCooldowns.TryGetValue(container, out var value))
				{
					value = new Dictionary<string, float>();
					_destinationCooldowns[container] = value;
				}
				float num = (value[text] = Time.time + 8f);
				if (num > _blockedStatusUntil)
				{
					_blockedStatusUntil = num;
				}
				float num3 = Time.time + 20f;
				if (num3 > _blockedDialogueUntil)
				{
					_blockedDialogueUntil = num3;
				}
			}
		}

		private void ClearDestinationCooldown(Container container, ItemData item)
		{
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				GameObject dropPrefab = item.m_dropPrefab;
				obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			string text = (string)obj;
			if (!((Object)(object)container == (Object)null) && !string.IsNullOrEmpty(text) && _destinationCooldowns.TryGetValue(container, out var value))
			{
				value.Remove(text);
				if (value.Count == 0)
				{
					_destinationCooldowns.Remove(container);
				}
			}
		}

		private void RemoveCargoEntryAndContinue(int index)
		{
			if (index >= 0 && index < _cargo.Count)
			{
				ContainerUseGuard.Release(_cargo[index].Destination);
				_cargo.RemoveAt(index);
			}
			ContainerUseGuard.Release(_source);
			_activeDestination = null;
			ResetOwnershipWait();
			ResetContainerBusyWait();
			ResetPlayerBusyWait();
			if (_cargo.Count == 0)
			{
				FinishBatch();
			}
		}

		private Container FindNearestDestination()
		{
			//IL_0031: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Container result = null;
			float num = float.MaxValue;
			foreach (CargoEntry item in _cargo)
			{
				Container destination = item.Destination;
				if (IsUsable(destination))
				{
					Vector3 val = ((Component)this).transform.position - ((Component)destination).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						result = destination;
						num = sqrMagnitude;
					}
				}
			}
			return result;
		}

		private bool HasCargoForDestination(Container destination)
		{
			if ((Object)(object)destination == (Object)null)
			{
				return false;
			}
			for (int i = 0; i < _cargo.Count; i++)
			{
				if ((Object)(object)_cargo[i].Destination == (Object)(object)destination)
				{
					return true;
				}
			}
			return false;
		}

		private int FindCargoIndexForDestination(Container destination)
		{
			if ((Object)(object)destination == (Object)null)
			{
				return -1;
			}
			for (int i = 0; i < _cargo.Count; i++)
			{
				if ((Object)(object)_cargo[i].Destination == (Object)(object)destination)
				{
					return i;
				}
			}
			return -1;
		}

		private void CleanupDestinationCooldowns()
		{
			if (_destinationCooldowns.Count == 0)
			{
				return;
			}
			List<Container> list = new List<Container>();
			foreach (KeyValuePair<Container, Dictionary<string, float>> destinationCooldown in _destinationCooldowns)
			{
				Container key = destinationCooldown.Key;
				Dictionary<string, float> value = destinationCooldown.Value;
				if (!Object.op_Implicit((Object)(object)key) || value == null)
				{
					list.Add(key);
					continue;
				}
				List<string> list2 = new List<string>();
				foreach (KeyValuePair<string, float> item in value)
				{
					if (Time.time >= item.Value)
					{
						list2.Add(item.Key);
					}
				}
				foreach (string item2 in list2)
				{
					value.Remove(item2);
				}
				if (value.Count == 0)
				{
					list.Add(key);
				}
			}
			foreach (Container item3 in list)
			{
				_destinationCooldowns.Remove(item3);
			}
		}

		private bool MoveTowards(Vector3 point, float stopDistance, out bool stuck)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			stuck = false;
			if (DistanceXZ(((Component)this).transform.position, point) <= stopDistance)
			{
				_ai?.Halt();
				ResetMoveTracking();
				return true;
			}
			if ((Object)(object)_ai == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Porter movement AI is missing.");
				stuck = true;
				return false;
			}
			if (!_trackingMove || DistanceXZ(_moveTarget, point) > 0.5f)
			{
				_trackingMove = true;
				_moveTarget = point;
				_lastProgressPosition = ((Component)this).transform.position;
				_lastProgressTime = Time.time;
			}
			else if (DistanceXZ(_lastProgressPosition, ((Component)this).transform.position) >= 0.25f)
			{
				_lastProgressPosition = ((Component)this).transform.position;
				_lastProgressTime = Time.time;
			}
			else if (Time.time - _lastProgressTime >= 8f)
			{
				_ai.Halt();
				ResetMoveTracking();
				stuck = true;
				return false;
			}
			_ai.WalkTo(point, stopDistance);
			return false;
		}

		private static float DistanceXZ(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return Mathf.Sqrt(num * num + num2 * num2);
		}

		private void ResetMoveTracking()
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_trackingMove = false;
			_moveTarget = Vector3.zero;
			_lastProgressPosition = Vector3.zero;
			_lastProgressTime = 0f;
		}

		private static bool IsUsable(Container c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null)
			{
				return component.IsValid();
			}
			return false;
		}

		private void FinishBatch()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogDebug((object)"Porter trip complete; returning home.");
			ClearBatch();
			Vector3 val = ((Component)this).transform.position - _home;
			if (((Vector3)(ref val)).sqrMagnitude > 0.122499995f)
			{
				_returningFromWork = true;
				_state = PorterWorkState.ReturningHome;
			}
			else
			{
				_state = PorterWorkState.Idle;
				_nextScan = Time.time + 2f;
			}
		}

		private void AbortBatch()
		{
			ClearBatch();
			_returningFromWork = true;
			_state = PorterWorkState.ReturningHome;
		}

		private static string DescribeItem(ItemData item)
		{
			if (item == null)
			{
				return "<null item>";
			}
			string arg = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : (item.m_shared?.m_name ?? "<unknown>"));
			return $"{arg} x{item.m_stack}";
		}

		private void WaitForPlayerContainer(CargoEntry entry)
		{
			if (_playerBusySince < 0f)
			{
				_playerBusySince = Time.time;
				Plugin.Log.LogDebug((object)("Porter waiting for player to close a container for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "]."));
			}
			ContainerUseGuard.Release(entry.Destination);
			ContainerUseGuard.Release(_source);
			ResetOwnershipWait();
			ResetContainerBusyWait();
			if (Time.time - _playerBusySince >= 30f)
			{
				Plugin.Log.LogWarning((object)("Porter player container wait timed out for " + DescribeItem(entry.Item) + "; returning home."));
				AbortBatch();
			}
			else
			{
				_nextTransferTime = Time.time + 0.3f;
			}
		}

		private void WaitForBusyContainer(CargoEntry entry)
		{
			if (_containerBusySince < 0f)
			{
				_containerBusySince = Time.time;
				Plugin.Log.LogWarning((object)("Porter waiting for container access for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "]."));
			}
			ContainerUseGuard.Release(entry.Destination);
			ContainerUseGuard.Release(_source);
			ResetOwnershipWait();
			if (Time.time - _containerBusySince >= 5f)
			{
				Plugin.Log.LogWarning((object)("Porter container wait timed out for " + DescribeItem(entry.Item) + "; source [" + ContainerUseGuard.DescribeState(_source) + "], destination [" + ContainerUseGuard.DescribeState(entry.Destination) + "]. Returning home."));
				AbortBatch();
			}
			else
			{
				_nextTransferTime = Time.time + 0.3f;
			}
		}

		private void ResetContainerBusyWait()
		{
			_containerBusySince = -1f;
		}

		private void ResetPlayerBusyWait()
		{
			_playerBusySince = -1f;
		}

		private void ResetOwnershipWait()
		{
			_ownershipWaitItem = null;
			_ownershipWaitSince = 0f;
		}

		private void ClearBatch()
		{
			_ai?.Halt();
			ResetMoveTracking();
			ContainerUseGuard.Release(_source);
			foreach (CargoEntry item in _cargo)
			{
				ContainerUseGuard.Release(item?.Destination);
			}
			_source = null;
			_cargo.Clear();
			_nextTransferTime = 0f;
			_activeTripCapacity = 0;
			_activeDestination = null;
			ResetOwnershipWait();
			ResetContainerBusyWait();
			ResetPlayerBusyWait();
		}
	}
}
namespace FullingPorter.Patches
{
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class ContainerInteractionPatch
	{
		private static void Postfix(Player __instance)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.SourceChestKey != null && Input.GetKeyDown(Plugin.SourceChestKey.Value))
			{
				GameObject hoverObject = ((Humanoid)__instance).GetHoverObject();
				Container val = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<Container>() : null);
				if (!((Object)(object)val == (Object)null))
				{
					PorterRpc.RequestToggleSource(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class ContainerHoverTextPatch
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			if (SourceContainerMarker.IsSource(__instance))
			{
				string text = LocalizationManager.Instance.TryTranslate("$fullingporter_source_hover");
				__result = __result + "\n<color=yellow>" + text + "</color>";
			}
		}
	}
	[HarmonyPatch(typeof(Character), "GetHoverText")]
	internal static class PorterCharacterHoverTextPatch
	{
		private static void Postfix(Character __instance, ref string __result)
		{
			PorterState porterState = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<PorterState>() : null);
			if (!((Object)(object)porterState == (Object)null))
			{
				__result = porterState.BuildHoverText();
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Interact")]
	internal static class PorterContainerTransferLockPatch
	{
		private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer || !ContainerUseGuard.IsPorterLocked(__instance))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, "$msg_inuse", 0, (Sprite)null, false);
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class PorterContainerStaleLockPatch
	{
		private static void Postfix(Container __instance)
		{
			ContainerUseGuard.ClearStaleLock(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class PorterDamagePatch
	{
		private static bool Prefix(Character __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				return (Object)(object)((Component)__instance).GetComponent<PorterState>() == (Object)null;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
	internal static class PorterEnemyHudPatch
	{
		private static bool Prefix(Character c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				return (Object)(object)((Component)c).GetComponent<PorterState>() == (Object)null;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ZRoutedRpc), "RPC_RoutedRPC")]
	internal static class PorterRoutedRpcGuardPatch
	{
		private static readonly int PorterActionsHash = StringExtensionMethods.GetStableHashCode("Likhtenvald.FullingPorter!PorterActions");

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(ZRpc rpc, ZPackage pkg)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || rpc == null || pkg == null)
			{
				return true;
			}
			int pos = pkg.GetPos();
			try
			{
				pkg.ReadLong();
				long num = pkg.ReadLong();
				pkg.ReadLong();
				pkg.ReadZDOID();
				if (pkg.ReadInt() != PorterActionsHash)
				{
					pkg.SetPos(pos);
					return true;
				}
				ZNetPeer val = FindPeerForRpc(rpc);
				if (val == null || !val.IsReady())
				{
					Plugin.Log.LogWarning((object)"Dropped FullingPorter RPC from unresolved or unready connection.");
					return false;
				}
				if (!PorterServerRequestRules.IsAuthenticatedSender(num, val.m_uid))
				{
					Plugin.Log.LogWarning((object)$"Dropped forged FullingPorter RPC: claimed peer {num}, actual peer {val.m_uid}.");
					return false;
				}
				pkg.SetPos(pos);
				return true;
			}
			catch
			{
				pkg.SetPos(pos);
				return true;
			}
		}

		private static ZNetPeer FindPeerForRpc(ZRpc rpc)
		{
			if (rpc == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return null;
			}
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			if (peers == null)
			{
				return null;
			}
			foreach (ZNetPeer item in peers)
			{
				if (item != null && item.m_rpc == rpc)
				{
					return item;
				}
			}
			return null;
		}
	}
}
namespace FullingPorter.Network
{
	internal static class PorterRpc
	{
		private enum ActionCode
		{
			Spawn = 1,
			ToggleSource,
			Dismiss,
			Rename,
			SpeakRequest,
			SpeechEvent
		}

		private static CustomRPC _rpc;

		private static bool _spawnPending;

		private static Inventory _pendingContractInventory;

		private static ItemData _pendingContractItem;

		private static readonly MethodInfo GetServerPeerIdMethod = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);

		private static readonly FieldInfo RoutedRpcIdField = AccessTools.Field(typeof(ZRoutedRpc), "m_id");

		private static readonly Dictionary<long, float> LastActionAt = new Dictionary<long, float>();

		internal static void Register()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0027: Expected O, but got Unknown
			_rpc = NetworkManager.Instance.AddRPC("PorterActions", new CoroutineHandler(ServerReceive), new CoroutineHandler(ClientReceive));
		}

		internal static void RequestSpawn(Vector3 position, Vector3 forward, Inventory inventory, ItemData contractItem)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (_spawnPending || inventory == null || contractItem == null)
			{
				return;
			}
			_pendingContractInventory = inventory;
			_pendingContractItem = contractItem;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				CompleteLocalSpawn(TrySpawnServer(position, forward));
				return;
			}
			if (_rpc == null || ZRoutedRpc.instance == null)
			{
				ClearPendingSpawn();
				return;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId == 0L)
			{
				Plugin.Log.LogWarning((object)"Porter spawn request skipped because the server peer ID could not be resolved.");
				ClearPendingSpawn();
				return;
			}
			_spawnPending = true;
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(forward);
			_rpc.SendPackage(serverPeerId, val);
		}

		internal static void RequestToggleSource(Container container)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = (Object.op_Implicit((Object)(object)container) ? ((Component)container).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (_rpc == null || ZRoutedRpc.instance == null || val2 == null)
			{
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				if (SourceContainerMarker.TryToggleServer(container, out var enabled))
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, enabled ? "$fullingporter_source_enabled" : "$fullingporter_source_disabled", 0, (Sprite)null, false);
					}
				}
				return;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId != 0L)
			{
				ZPackage val3 = new ZPackage();
				val3.Write(2);
				val3.Write(val2.m_uid);
				_rpc.SendPackage(serverPeerId, val3);
			}
		}

		internal static void RequestDismiss(ZNetView view)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (_rpc == null || ZRoutedRpc.instance == null || val == null)
			{
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				if (TryDismissServer(((Component)view).gameObject))
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismissed", 0, (Sprite)null, false);
					}
				}
				return;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId != 0L)
			{
				ZPackage val2 = new ZPackage();
				val2.Write(3);
				val2.Write(val.m_uid);
				_rpc.SendPackage(serverPeerId, val2);
			}
		}

		internal static void RequestRename(ZNetView view, string name)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (_rpc == null || ZRoutedRpc.instance == null || val == null)
			{
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				((Component)view).GetComponent<PorterState>()?.SetNameServer(name);
				return;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId != 0L)
			{
				ZPackage val2 = new ZPackage();
				val2.Write(4);
				val2.Write(val.m_uid);
				val2.Write(name ?? string.Empty);
				_rpc.SendPackage(serverPeerId, val2);
			}
		}

		internal static bool RequestSpeak(ZNetView view, bool ambient)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (_rpc == null || ZRoutedRpc.instance == null || val == null)
			{
				return false;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				return ((Component)view).GetComponent<PorterPersonality>()?.TrySpeakServer(ambient) ?? false;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId == 0L)
			{
				return false;
			}
			ZPackage val2 = new ZPackage();
			val2.Write(5);
			val2.Write(val.m_uid);
			val2.Write(ambient);
			_rpc.SendPackage(serverPeerId, val2);
			return true;
		}

		internal static void BroadcastSpeech(ZNetView view, string line, int voiceIndex)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (_rpc != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)view == (Object)null) && view.IsValid())
			{
				ZPackage val = NewResult(ActionCode.SpeechEvent, success: true);
				val.Write(view.GetZDO().m_uid);
				val.Write(line);
				val.Write(voiceIndex);
				_rpc.SendPackage(ZNet.instance.GetPeers(), val);
			}
		}

		private static long GetServerPeerId()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null)
			{
				return 0L;
			}
			try
			{
				if (GetServerPeerIdMethod != null && GetServerPeerIdMethod.Invoke(instance, null) is long num && num != 0L)
				{
					return num;
				}
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && RoutedRpcIdField != null && RoutedRpcIdField.GetValue(instance) is long num2 && num2 != 0L)
				{
					return num2;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not resolve Valheim server peer ID: " + ex.GetBaseException().Message));
				return 0L;
			}
			Plugin.Log.LogWarning((object)"Valheim server peer ID is unavailable on this build.");
			return 0L;
		}

		private static IEnumerator ServerReceive(long sender, ZPackage pkg)
		{
			switch ((ActionCode)pkg.ReadInt())
			{
			case ActionCode.Spawn:
				HandleSpawn(sender, pkg);
				break;
			case ActionCode.ToggleSource:
				HandleToggleSource(sender, pkg);
				break;
			case ActionCode.Dismiss:
				yield return HandleDismiss(sender, pkg);
				yield break;
			case ActionCode.Rename:
				HandleRename(sender, pkg);
				break;
			case ActionCode.SpeakRequest:
				HandleSpeak(sender, pkg);
				break;
			}
			yield return null;
		}

		private static IEnumerator ClientReceive(long sender, ZPackage pkg)
		{
			ActionCode actionCode = (ActionCode)pkg.ReadInt();
			bool flag = pkg.ReadBool();
			switch (actionCode)
			{
			case ActionCode.Spawn:
				CompleteLocalSpawn(flag);
				break;
			case ActionCode.ToggleSource:
				if (flag)
				{
					bool flag2 = pkg.ReadBool();
					Player localPlayer2 = Player.m_localPlayer;
					if (localPlayer2 != null)
					{
						((Character)localPlayer2).Message((MessageType)2, flag2 ? "$fullingporter_source_enabled" : "$fullingporter_source_disabled", 0, (Sprite)null, false);
					}
				}
				break;
			case ActionCode.Dismiss:
				if (flag)
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$fullingporter_dismissed", 0, (Sprite)null, false);
					}
				}
				break;
			case ActionCode.SpeechEvent:
				if (flag && sender == GetServerPeerId())
				{
					GameObject obj = FindObject(pkg.ReadZDOID());
					string line = pkg.ReadString();
					int voiceIndex = pkg.ReadInt();
					if (obj != null)
					{
						obj.GetComponent<PorterPersonality>()?.PlaySpeech(line, voiceIndex);
					}
				}
				break;
			}
			yield return null;
		}

		private static void HandleSpawn(long sender, ZPackage pkg)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!TryResolveRemoteSender(sender, out var _, out var position) || !TryConsumeActionBudget(sender))
			{
				SendResult(sender, ActionCode.Spawn, success: false);
				return;
			}
			Vector3 forward = pkg.ReadVector3();
			SendResult(sender, ActionCode.Spawn, TrySpawnServer(position, forward));
		}

		private static bool TrySpawnServer(Vector3 position, Vector3 forward)
		{
			//IL_0077: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			if (PorterState.WorldHasPorter())
			{
				return false;
			}
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.GetPrefab("FullingPorter_Fuling") : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Porter prefab is not registered in ZNetScene.");
				return false;
			}
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = Vector3.forward;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, position + ((Vector3)(ref forward)).normalized * 2f, Quaternion.LookRotation(-((Vector3)(ref forward)).normalized));
			ZNetView val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ZNetView>() : null);
			if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
			{
				return false;
			}
			PorterState.MarkWorldOccupied(val3);
			return true;
		}

		private static void HandleToggleSource(long sender, ZPackage pkg)
		{
			//IL_0001: 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)
			GameObject val = FindObject(pkg.ReadZDOID());
			Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Container>() : null);
			if ((Object)(object)val2 == (Object)null || !ValidateRemoteTargetAction(sender, val.transform.position) || !SourceContainerMarker.TryToggleServer(val2, out var enabled))
			{
				SendResult(sender, ActionCode.ToggleSource, success: false);
				return;
			}
			ZPackage val3 = NewResult(ActionCode.ToggleSource, success: true);
			val3.Write(enabled);
			_rpc.SendPackage(sender, val3);
		}

		private static IEnumerator HandleDismiss(long sender, ZPackage pkg)
		{
			GameObject target = FindObject(pkg.ReadZDOID());
			ZNetView view = (((Object)(object)target != (Object)null) ? target.GetComponent<ZNetView>() : null);
			PorterState porterState = (((Object)(object)target != (Object)null) ? target.GetComponent<PorterState>() : null);
			if ((Object)(object)view == (Object)null || (Object)(object)porterState == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || !ValidateRemoteTargetAction(sender, target.transform.position))
			{
				SendResult(sender, ActionCode.Dismiss, success: false);
				yield break;
			}
			float deadline = Time.time + 3f;
			while (!view.IsOwner() && Time.time < deadline)
			{
				view.ClaimOwnership();
				yield return null;
			}
			if (!view.IsOwner())
			{
				SendResult(sender, ActionCode.Dismiss, success: false);
			}
			else
			{
				SendResult(sender, ActionCode.Dismiss, TryDismissServer(target));
			}
		}

		private static bool TryDismissServer(GameObject target)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)target == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)target.GetComponent<PorterState>() == (Object)null)
			{
				return false;
			}
			PorterState.ClearWorldOccupied();
			ZNetScene.instance.Destroy(target);
			return true;
		}

		private static void HandleRename(long sender, ZPackage pkg)
		{
			//IL_0001: 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)
			GameObject val = FindObject(pkg.ReadZDOID());
			PorterState porterState = (((Object)(object)val != (Object)null) ? val.GetComponent<PorterState>() : null);
			string text = pkg.ReadString();
			if ((Object)(object)porterState == (Object)null || !PorterServerRequestRules.IsRenamePayloadValid(text) || !ValidateRemoteTargetAction(sender, val.transform.position))
			{
				SendResult(sender, ActionCode.Rename, success: false);
				return;
			}
			bool success = porterState.SetNameServer(text);
			SendResult(sender, ActionCode.Rename, success);
		}

		private static void HandleSpeak(long sender, ZPackage pkg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = FindObject(pkg.ReadZDOID());
			PorterPersonality porterPersonality = (((Object)(object)val != (Object)null) ? val.GetComponent<PorterPersonality>() : null);
			bool flag = pkg.ReadBool();
			if (!((Object)(object)porterPersonality == (Object)null) && TryResolveRemoteSender(sender, out var _, out var position) && TryConsumeActionBudget(sender))
			{
				float num = (flag ? 18f : 5f);
				Vector3 val2 = position - val.transform.position;
				if (((Vector3)(ref val2)).sqrMagnitude > num * num)
				{
					Plugin.Log.LogWarning((object)$"Rejected porter speech request from peer {sender}: target is out of range.");
				}
				else
				{
					porterPersonality.TrySpeakServer(flag);
				}
			}
		}

		private static bool ValidateRemoteTargetAction(long sender, Vector3 targetPosition)
		{
			//IL_0018: 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_0024: 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_0030: 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)
			if (!TryResolveRemoteSender(sender, out var _, out var position))
			{
				return false;
			}
			if (!TryConsumeActionBudget(sender))
			{
				return false;
			}
			bool num = PorterServerRequestRules.IsWithinInteractionDistance(position.x, position.y, position.z, targetPosition.x, targetPosition.y, targetPosition.z);
			if (!num)
			{
				Plugin.Log.LogWarning((object)$"Rejected porter RPC from peer {sender}: target is out of interaction range.");
			}
			return num;
		}

		private static bool TryResolveRemoteSender(long sender, out ZNetPeer peer, out Vector3 position)
		{
			//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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			peer = null;
			position = Vector3.zero;
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			peer = ZNet.instance.GetPeer(sender);
			if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone())
			{
				Plugin.Log.LogWarning((object)$"Rejected porter RPC from unknown or unready peer {sender}.");
				return false;
			}
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null);
			position = ((val != null) ? val.GetPosition() : peer.m_refPos);
			return true;
		}

		private static bool TryConsumeActionBudget(long sender)
		{
			float time = Time.time;
			float value;
			float lastActionAt = (LastActionAt.TryGetValue(sender, out value) ? value : (-1f));
			if (!PorterServerRequestRules.IsActionRateAllowed(time, lastActionAt))
			{
				Plugin.Log.LogWarning((object)$"Rejected porter RPC from peer {sender}: action rate limit exceeded.");
				return false;
			}
			LastActionAt[sender] = time;
			return true;
		}

		private static GameObject FindObject(ZDOID id)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNetScene.instance != (Object)null))
			{
				return null;
			}
			return ZNetScene.instance.FindInstance(id);
		}

		private static void SendResult(long target, ActionCode action, bool success)
		{
			_rpc.SendPackage(target, NewResult(action, success));
		}

		private static ZPackage NewResult(ActionCode action, bool success)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((int)action);
			val.Write(success);
			return val;
		}

		private static void CompleteLocalSpawn(bool success)
		{
			_spawnPending = false;
			if (success)
			{
				ConsumePendingContract();
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "$fullingporter_spawned", 0, (Sprite)null, false);
				}
			}
			else
			{
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					((Character)localPlayer2).Message((MessageType)2, "$fullingporter_already_exists", 0, (Sprite)null, false);
				}
			}
			ClearPendingSpawn();
		}

		private static void ConsumePendingContract()
		{
			object obj = _pendingContractInventory;
			if (obj == null)
			{
				Player localPlayer = Player.m_localPlayer;
				obj = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null);
			}
			Inventory val = (Inventory)obj;
			if (val == null)
			{
				return;
			}
			if (_pendingContractItem != null && val.ContainsItem(_pendingContractItem))
			{
				val.RemoveOneItem(_pendingContractItem);
				return;
			}
			List<ItemData> allItems = val.GetAllItems();
			if (allItems == null)
			{
				return;
			}
			foreach (ItemData item in allItems)
			{
				object obj2;
				if (item == null)
				{
					obj2 = null;
				}
				else
				{
					GameObject dropPrefab = item.m_dropPrefab;
					obj2 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				if (!((string?)obj2 != "FullingPorterContract"))
				{
					val.RemoveOneItem(item);
					break;
				}
			}
		}

		private static void ClearPendingSpawn()
		{
			_spawnPending = false;
			_pendingContractInventory = null;
			_pendingContractItem = null;
		}
	}
}
namespace FullingPorter.Localization
{
	internal static class LocalizationRegistry
	{
		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			["fullingporter_name"] = "Fuling Porter",
			["fullingporter_contract"] = "Fuling Porter Contract",
			["fullingporter_contract_desc"] = "Hire one friendly Fuling porter to move source-chest items into QuickStackPlus Smart Storage.",
			["fullingporter_source_enabled"] = "Porter source chest assigned",
			["fullingporter_source_disabled"] = "Porter source chest unassigned",
			["fullingporter_source_hover"] = "Porter source",
			["fullingporter_status_idle"] = "Waiting for work",
			["fullingporter_status_collecting"] = "Going to source",
			["fullingporter_status_delivering"] = "Delivering",
			["fullingporter_status_returning"] = "Returning home",
			["fullingporter_status_blocked"] = "Waiting for storage space",
			["fullingporter_spawned"] = "Your Fuling porter has arrived",
			["fullingporter_already_exists"] = "Only one porter can be contracted in this world",
			["fullingporter_rename_title"] = "Name your porter",
			["fullingporter_dismissed"] = "Porter contract terminated",
			["fullingporter_dismiss_confirm"] = "Press the dismissal key again within 3 seconds to terminate this contract",
			["fullingporter_talk_idle_01"] = "Finally, no work!",
			["fullingporter_talk_idle_02"] = "Porter carries. Viking watches.",
			["fullingporter_talk_idle_03"] = "Coins to Haldor. Work to me. Very fair.",
			["fullingporter_talk_idle_04"] = "I have seen berserkers carry less.",
			["fullingporter_talk_idle_05"] = "Work exists. Vacation does not.",
			["fullingporter_talk_idle_06"] = "That's how it goes, doggy.",
			["fullingporter_talk_work_01"] = "Work again!?",
			["fullingporter_talk_work_02"] = "Ten stacks! I have two hands!",
			["fullingporter_talk_work_03"] = "I am porter, not portal!",
			["fullingporter_talk_work_04"] = "Help me...",
			["fullingporter_talk_work_05"] = "More wood? Did the forest happen to run out?",
			["fullingporter_talk_work_06"] = "You put it in chest. I put it in another.",
			["fullingporter_talk_work_07"] = "Walk there. Carry this. Walk back...",
			["fullingporter_talk_return_01"] = "Done!",
			["fullingporter_talk_return_02"] = "Cargo delivered. Please leave five stars.",
			["fullingporter_talk_return_03"] = "I survived another chest.",
			["fullingporter_talk_idle_040"] = "Finished! Pretending to be busy now.",
			["fullingporter_talk_blocked_01"] = "Chest full! I am not magic!",
			["fullingporter_talk_blocked_02"] = "Where put it!? On your head?",
			["fullingporter_talk_blocked_03"] = "No room. Build chest. Big chest.",
			["fullingporter_talk_idle_041"] = "What do you want? Can't you see? Five-minute break!"
		};

		private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>
		{
			["fullingporter_name"] = "Фулинг-грузчик",
			["fullingporter_contract"] = "Контракт фулинга-грузчика",
			["fullingporter_contract_desc"] = "Наймите одного дружелюбного фулинга-грузчика для переноса вещей из источников в QuickStackPlus Smart Storage.",
			["fullingporter_source_enabled"] = "Сундук назначен источником грузчика",
			["fullingporter_source_disabled"] = "Сундук больше не является источником грузчика",
			["fullingporter_source_hover"] = "Источник грузчика",
			["fullingporter_status_idle"] = "Ожидает работу",
			["fullingporter_status_collecting"] = "Идёт к источнику",
			["fullingporter_status_delivering"] = "Разносит груз",
			["fullingporter_status_returning"] = "Возвращается домой",
			["fullingporter_status_blocked"] = "Ждёт освобождения склада",
			["fullingporter_spawned"] = "Ваш фулинг-грузчик прибыл",
			["fullingporter_already_exists"] = "В этом мире уже есть фулинг-грузчик",
			["fullingporter_rename_title"] = "Имя грузчика",
			["fullingporter_dismissed"] = "Контракт с грузчиком расторгнут",
			["fullingporter_dismiss_confirm"] = "Нажмите клавишу увольнения ещё раз в течение 3 секунд, чтобы расторгнуть контракт",
			["fullingporter_talk_idle_01"] = "Наконец-то нет работы!",
			["fullingporter_talk_idle_02"] = "Грузчик носит. Викинг смотрит.",
			["fullingporter_talk_idle_03"] = "Монеты Хальдору. Работа мне. Очень честно.",
			["fullingporter_talk_idle_04"] = "Я видел берсерков. Они меньше таскают.",
			["fullingporter_talk_idle_05"] = "Работа есть. Отпуска нет.",
			["fullingporter_talk_idle_06"] = "Вот такие дела, собачка.",
			["fullingporter_talk_work_01"] = "Опять работать!?",
			["fullingporter_talk_work_02"] = "Десять стопок! У меня две руки!",
			["fullingporter_talk_work_03"] = "Я грузчик, не портал!",
			["fullingporter_talk_work_04"] = "Помогите...",
			["fullingporter_talk_work_05"] = "Ещё дерево? У вас лес случайно не закончился?",
			["fullingporter_talk_work_06"] = "Ты кладёшь в сундук. Я кладу в другой.",
			["fullingporter_talk_work_07"] = "Иди туда. Неси это. Иди назад...",
			["fullingporter_talk_return_01"] = "Готово!",
			["fullingporter_talk_return_02"] = "Груз доставлен. Поставьте пять звезд.",
			["fullingporter_talk_return_03"] = "Я пережил ещё один сундук.",
			["fullingporter_talk_idle_040"] = "Закончил! Теперь делаю вид, что занят.",
			["fullingporter_talk_blocked_01"] = "Сундук полный! Я не маг!",
			["fullingporter_talk_blocked_02"] = "Куда класть!? На голову тебе?",
			["fullingporter_talk_blocked_03"] = "Места нет. Строй сундук. Большой сундук.",
			["fullingporter_talk_idle_041"] = "Чего тебе? Не видишь? Перерыв пять минут!"
		};

		internal static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			st