Decompiled source of ForgeStack v1.1.1

ForgeStack.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Digger.Core")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ForgeStack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Send resources to the Forge with G: tap for 1, hold for the whole stack, keep holding and scroll the hotbar to dump every stack. Hosting, the whole stack is credited at once. Anywhere or near-Forge mode. Only you need it.")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+fb776bf142bca17ae66fbd035a8d0f6d6af8b232")]
[assembly: AssemblyProduct("ForgeStack")]
[assembly: AssemblyTitle("ForgeStack")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ForgeStack
{
	internal sealed class ConfigHotReload : IDisposable
	{
		private const float DebounceSeconds = 0.5f;

		private const float RetrySeconds = 0.5f;

		private const int MaxRetries = 5;

		private readonly ConfigFile _config;

		private readonly ManualLogSource _log;

		private readonly string _path;

		private FileSystemWatcher _watcher;

		private int _eventFlag;

		private float _lastEventTime = -1f;

		private float _retryAt = -1f;

		private int _retries;

		private long _appliedStamp;

		private bool _pollFailedLogged;

		public ConfigHotReload(ConfigFile config, ManualLogSource log)
		{
			_config = config;
			_log = log;
			_path = ((config != null) ? config.ConfigFilePath : null);
			try
			{
				string directoryName = Path.GetDirectoryName(_path);
				string fileName = Path.GetFileName(_path);
				if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileName) || !Directory.Exists(directoryName))
				{
					_log.LogWarning((object)"ForgeStack: pasta da config não encontrada, recarga automática desligada.");
					return;
				}
				_appliedStamp = ReadStamp();
				_watcher = new FileSystemWatcher(directoryName, fileName)
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
					IncludeSubdirectories = false
				};
				_watcher.Changed += OnFileEvent;
				_watcher.Created += OnFileEvent;
				_watcher.Renamed += OnFileEvent;
				_watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("ForgeStack: não foi possível observar o arquivo de config (edições só valem ao reiniciar o jogo): " + ex.Message));
				DisposeWatcher();
			}
		}

		private void OnFileEvent(object sender, FileSystemEventArgs e)
		{
			Interlocked.Exchange(ref _eventFlag, 1);
		}

		public void Poll(float now)
		{
			if (_config == null)
			{
				return;
			}
			try
			{
				if (Interlocked.Exchange(ref _eventFlag, 0) == 1)
				{
					_lastEventTime = now;
				}
				bool num = _lastEventTime >= 0f && now - _lastEventTime >= 0.5f;
				bool flag = _retryAt >= 0f && now >= _retryAt;
				if (num || flag)
				{
					_lastEventTime = -1f;
					_retryAt = -1f;
					TryReload(now);
				}
			}
			catch (Exception ex)
			{
				if (!_pollFailedLogged)
				{
					_pollFailedLogged = true;
					_log.LogWarning((object)("ForgeStack: erro ao verificar a config: " + ex.Message));
				}
			}
		}

		private void TryReload(float now)
		{
			long num;
			try
			{
				num = ReadStamp();
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
			{
				ScheduleRetry(now);
				return;
			}
			if (num == _appliedStamp)
			{
				_retries = 0;
				return;
			}
			bool saveOnConfigSet = _config.SaveOnConfigSet;
			_config.SaveOnConfigSet = false;
			try
			{
				_config.Reload();
				_appliedStamp = num;
				_retries = 0;
				_pollFailedLogged = false;
				_log.LogInfo((object)"ForgeStack: config recarregada do arquivo.");
			}
			catch (Exception ex2) when (ex2 is IOException || ex2 is UnauthorizedAccessException)
			{
				ScheduleRetry(now);
			}
			finally
			{
				_config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void ScheduleRetry(float now)
		{
			if (_retries >= 5)
			{
				_retries = 0;
				_log.LogWarning((object)"ForgeStack: não foi possível ler a config agora (arquivo em uso).");
			}
			else
			{
				_retries++;
				_retryAt = now + 0.5f;
			}
		}

		private long ReadStamp()
		{
			FileInfo fileInfo = new FileInfo(_path);
			fileInfo.Refresh();
			if (!fileInfo.Exists)
			{
				return 0L;
			}
			return fileInfo.LastWriteTimeUtc.Ticks * 31 + fileInfo.Length;
		}

		public void Dispose()
		{
			DisposeWatcher();
		}

		private void DisposeWatcher()
		{
			if (_watcher != null)
			{
				try
				{
					_watcher.EnableRaisingEvents = false;
					_watcher.Changed -= OnFileEvent;
					_watcher.Created -= OnFileEvent;
					_watcher.Renamed -= OnFileEvent;
					_watcher.Dispose();
				}
				catch
				{
				}
				_watcher = null;
			}
		}
	}
	internal sealed class FeedbackHud
	{
		private struct TallyItem
		{
			public string ItemId;

			public int Units;
		}

		private const float TallySeconds = 2.5f;

		private const float FadeSeconds = 0.5f;

		private readonly List<TallyItem> _tally = new List<TallyItem>();

		private float _tallyUpdatedAt = -100f;

		private string _hint;

		private float _hintUntil;

		private string _notice;

		private Color _noticeColor = Color.white;

		private float _noticeUntil;

		public float HoldProgress = -1f;

		public bool Sweeping;

		public int QueuedUnits;

		private GUIStyle _textStyle;

		private GUIStyle _noticeStyle;

		private float _styleScale;

		private readonly StringBuilder _sb = new StringBuilder();

		public void AddSent(string itemId, int units, float now)
		{
			if (now - _tallyUpdatedAt > 2.5f)
			{
				_tally.Clear();
			}
			for (int i = 0; i < _tally.Count; i++)
			{
				if (!(_tally[i].ItemId != itemId))
				{
					TallyItem value = _tally[i];
					value.Units += units;
					_tally[i] = value;
					_tallyUpdatedAt = now;
					return;
				}
			}
			_tally.Add(new TallyItem
			{
				ItemId = itemId,
				Units = units
			});
			_tallyUpdatedAt = now;
		}

		public void ShowHint(string text, float now, float seconds = 1.6f)
		{
			_hint = text;
			_hintUntil = now + seconds;
		}

		public void ShowNotice(string text, Color color, float now, float seconds)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			_notice = text;
			_noticeColor = color;
			_noticeUntil = now + seconds;
		}

		public void Draw(float now, bool showFeedback)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0093: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			if (Event.current == null || (int)Event.current.type != 7)
			{
				return;
			}
			float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f);
			EnsureStyles(num);
			float num2 = (float)Screen.width / 2f;
			float num3 = (float)Screen.height / 2f;
			if (now < _noticeUntil && !string.IsNullOrEmpty(_notice))
			{
				float alpha = Fade(now, _noticeUntil);
				DrawShadowed(new Rect(0f, (float)Screen.height * 0.12f, (float)Screen.width, 40f * num), _notice, _noticeStyle, _noticeColor, alpha);
			}
			if (showFeedback)
			{
				if (HoldProgress >= 0f)
				{
					float num4 = 150f * num;
					float num5 = 8f * num;
					Rect val = default(Rect);
					((Rect)(ref val))..ctor(num2 - num4 / 2f, num3 + 38f * num, num4, num5);
					GUI.color = new Color(0f, 0f, 0f, 0.6f);
					GUI.DrawTexture(new Rect(((Rect)(ref val)).x - 2f, ((Rect)(ref val)).y - 2f, ((Rect)(ref val)).width + 4f, ((Rect)(ref val)).height + 4f), (Texture)(object)Texture2D.whiteTexture);
					GUI.color = (Sweeping ? new Color(0.45f, 1f, 0.45f, 0.95f) : new Color(1f, 0.62f, 0.2f, 0.95f));
					GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width * Mathf.Clamp01(HoldProgress), ((Rect)(ref val)).height), (Texture)(object)Texture2D.whiteTexture);
					GUI.color = Color.white;
				}
				if (_tally.Count > 0 && now - _tallyUpdatedAt <= 2.5f)
				{
					float alpha2 = Fade(now, _tallyUpdatedAt + 2.5f);
					DrawShadowed(new Rect(0f, num3 + 70f * num, (float)Screen.width, 32f * num), BuildTallyText(), _textStyle, new Color(1f, 0.85f, 0.45f), alpha2);
				}
				if (now < _hintUntil && !string.IsNullOrEmpty(_hint))
				{
					float alpha3 = Fade(now, _hintUntil);
					DrawShadowed(new Rect(0f, num3 + 104f * num, (float)Screen.width, 30f * num), _hint, _textStyle, new Color(1f, 0.55f, 0.35f), alpha3);
				}
				GUI.color = Color.white;
			}
		}

		private string BuildTallyText()
		{
			_sb.Clear();
			foreach (TallyItem item in _tally)
			{
				if (_sb.Length > 0)
				{
					_sb.Append("  ");
				}
				_sb.Append('+').Append(item.Units).Append(' ')
					.Append(ItemNames.Get(item.ItemId));
			}
			_sb.Append(" → Forja");
			if (QueuedUnits > 0)
			{
				_sb.Append(" …");
			}
			return _sb.ToString();
		}

		private static float Fade(float now, float until)
		{
			return Mathf.Clamp01((until - now) / 0.5f);
		}

		private static void DrawShadowed(Rect rect, string text, GUIStyle style, Color color, float alpha)
		{
			//IL_0017: 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_0055: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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)
			GUI.color = new Color(0f, 0f, 0f, 0.85f * alpha);
			GUI.Label(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
			GUI.color = new Color(color.r, color.g, color.b, alpha);
			GUI.Label(rect, text, style);
			GUI.color = Color.white;
		}

		private void EnsureStyles(float scale)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0065: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (_textStyle == null || !Mathf.Approximately(scale, _styleScale))
			{
				_styleScale = scale;
				_textStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontStyle = (FontStyle)1,
					fontSize = Mathf.RoundToInt(20f * scale),
					wordWrap = false
				};
				_textStyle.normal.textColor = Color.white;
				_noticeStyle = new GUIStyle(_textStyle)
				{
					fontSize = Mathf.RoundToInt(22f * scale)
				};
				_noticeStyle.normal.textColor = Color.white;
			}
		}
	}
	internal enum Block
	{
		None,
		NotInRun,
		PlayerState,
		Menu,
		Paused
	}
	internal static class ForgeGame
	{
		private const float FarOriginDistance = 10f;

		private static Forge _forge;

		private static float _nextForgeSearch;

		private static bool _loggedFallback;

		public static bool TryGetRun(out FirstPersonController player, out Forge forge)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			player = null;
			forge = null;
			GameManager instance = GameManager.Instance;
			if ((Object)(object)instance == (Object)null || !instance.IsInitialized || (Object)(object)instance.sceneManager == (Object)null || (int)instance.SceneType != 1)
			{
				return false;
			}
			Forge val = FindForge();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			player = instance.LocalPlayer;
			if ((Object)(object)player == (Object)null || !player.IsAvailable || (Object)(object)player.PlayerStats == (Object)null || (Object)(object)player.PlayerEquipment == (Object)null || (Object)(object)player.InventorySystem == (Object)null || (Object)(object)player.PlayerInteraction == (Object)null)
			{
				player = null;
				return false;
			}
			forge = val;
			return true;
		}

		private static Forge FindForge()
		{
			LevelManager instance = LevelManager.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.Forge != (Object)null)
			{
				_forge = instance.Forge;
				return _forge;
			}
			if ((Object)(object)_forge != (Object)null)
			{
				return _forge;
			}
			if (Time.unscaledTime < _nextForgeSearch)
			{
				return null;
			}
			_nextForgeSearch = Time.unscaledTime + 3f;
			try
			{
				_forge = Object.FindAnyObjectByType<Forge>();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ForgeStack: erro procurando a Forja na cena: " + ex.Message));
				return null;
			}
			if ((Object)(object)_forge != (Object)null && !_loggedFallback)
			{
				_loggedFallback = true;
				Plugin.Log.LogInfo((object)"ForgeStack: a Forja não estava ligada no LevelManager desta partida; usando a que está na cena.");
			}
			return _forge;
		}

		public static void ForgetForge()
		{
			_forge = null;
			_nextForgeSearch = 0f;
			_loggedFallback = false;
		}

		public static Block CheckPlayer(FirstPersonController player)
		{
			//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)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_0048: 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)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Invalid comparison between Unknown and I4
			if (player.PlayerStats.isDead)
			{
				return Block.PlayerState;
			}
			if (player.PlayerEquipment.HasCarriable() || (Object)(object)player.CurrentSiegeWeapon != (Object)null)
			{
				return Block.PlayerState;
			}
			InputManager instance = InputManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return Block.Menu;
			}
			InputState currentInputState = instance.CurrentInputState;
			if ((int)currentInputState != 2)
			{
				if (currentInputState - 9 <= 1 || (int)currentInputState == 19)
				{
					return Block.Paused;
				}
				return Block.Menu;
			}
			if (!Application.isFocused || (int)Cursor.lockState != 1)
			{
				return Block.Menu;
			}
			return Block.None;
		}

		public static bool IsTemporarilyBusy()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkClient.active || !NetworkClient.ready)
			{
				return true;
			}
			NetworkHelper instance = NetworkHelper.Instance;
			if ((Object)(object)instance != (Object)null && (int)instance.NetworkblockingState != 0)
			{
				return true;
			}
			return false;
		}

		public static bool IsVanillaSlotActionPressed(FirstPersonController player)
		{
			//IL_0022: 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)
			PlayerInteraction playerInteraction = player.PlayerInteraction;
			PlayerInputActions playerInputActions = InputManager.PlayerInputActions;
			if (playerInputActions == null)
			{
				if (!playerInteraction.isPrimaryHeld)
				{
					return playerInteraction.isHolding;
				}
				return true;
			}
			PlayerActions player2 = playerInputActions.Player;
			bool num = playerInteraction.isPrimaryHeld && (((PlayerActions)(ref player2)).PrimaryAction.IsPressed() || playerInteraction.currentTarget is IItemInteractable);
			bool flag = playerInteraction.isHolding || ((PlayerActions)(ref player2)).Interact.IsPressed() || ((PlayerActions)(ref player2)).HoldInteract.IsPressed();
			if (!(num || flag) && !((PlayerActions)(ref player2)).Release.IsPressed())
			{
				return ((PlayerActions)(ref player2)).HoldRelease.IsPressed();
			}
			return true;
		}

		public static int SelectedSlot(FirstPersonController player)
		{
			return player.InventorySystem.slotSelected;
		}

		public static bool IsLookingAtForge(FirstPersonController player, Forge forge)
		{
			ICrosshairTarget currentTarget = player.PlayerInteraction.currentTarget;
			Forge val = (Forge)(object)((currentTarget is Forge) ? currentTarget : null);
			if (val != null)
			{
				return (Object)(object)val == (Object)(object)forge;
			}
			return false;
		}

		public static float DistanceToForge(FirstPersonController player, Forge forge)
		{
			//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)
			return Vector3.Distance(((Component)player).transform.position, ((Component)forge).transform.position);
		}

		public static bool IsForgeMaxLevel(Forge forge)
		{
			RogueLikeDatabaseSO rogueLikeDatabase = forge.rogueLikeDatabase;
			int networkdisplayedLevel = forge.NetworkdisplayedLevel;
			if ((Object)(object)rogueLikeDatabase != (Object)null && networkdisplayedLevel > 0)
			{
				return rogueLikeDatabase.IsMaxLevel(networkdisplayedLevel - 1);
			}
			return false;
		}

		public static bool IsForgeNetworkReachable(Forge forge)
		{
			NetworkIdentity netIdentity = ((NetworkBehaviour)forge).netIdentity;
			if ((Object)(object)netIdentity == (Object)null || netIdentity.netId == 0 || !((Component)forge).gameObject.activeInHierarchy)
			{
				return false;
			}
			if (NetworkServer.active && !ForgeServer.CanDeliver(forge))
			{
				LocalConnectionToClient localConnection = NetworkServer.localConnection;
				Dictionary<int, NetworkConnectionToClient> observers = netIdentity.observers;
				if (localConnection != null && observers != null && !observers.ContainsKey(((NetworkConnectionToClient)localConnection).connectionId))
				{
					return false;
				}
			}
			return true;
		}

		public static bool IsPlaceable(Forge forge, string itemId)
		{
			if (!string.IsNullOrEmpty(itemId) && forge.IsPlaceable(itemId))
			{
				return ResourceValue(forge, itemId) > 0;
			}
			return false;
		}

		public static int ResourceValue(Forge forge, string itemId)
		{
			RogueLikeDatabaseSO rogueLikeDatabase = forge.rogueLikeDatabase;
			if (!((Object)(object)rogueLikeDatabase != (Object)null))
			{
				return 0;
			}
			return rogueLikeDatabase.GetResourceValueByID(itemId);
		}

		public static int FreePoolObjects(Forge forge, string itemId)
		{
			if (forge.itemPools == null || !forge.itemPools.TryGetValue(itemId, out var value) || value == null)
			{
				return -1;
			}
			int num = 0;
			foreach (GameObject item in value)
			{
				if ((Object)(object)item != (Object)null && !item.activeSelf)
				{
					num++;
				}
			}
			return num;
		}

		public static void GetAnimationOrigin(FirstPersonController player, Forge forge, out Vector3 position, out Quaternion rotation)
		{
			//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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			PlayerEquipment playerEquipment = player.PlayerEquipment;
			Transform val = null;
			EquippedItem currentEquippedItem = playerEquipment.CurrentEquippedItem;
			if ((Object)(object)currentEquippedItem != (Object)null && (Object)(object)currentEquippedItem.ItemAnimator != (Object)null)
			{
				val = ((Component)currentEquippedItem.ItemAnimator).transform;
			}
			if ((Object)(object)val == (Object)null)
			{
				val = (((Object)(object)playerEquipment.equipmentClientParent != (Object)null) ? playerEquipment.equipmentClientParent : ((Component)player).transform);
			}
			position = val.position;
			rotation = val.rotation;
			Vector3 val2 = (((Object)(object)forge.Target != (Object)null) ? forge.Target.position : ((Component)forge).transform.position);
			Vector3 val3 = position - val2;
			if (!(((Vector3)(ref val3)).sqrMagnitude <= 100f))
			{
				val3.y = 0f;
				val3 = ((((Vector3)(ref val3)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val3)).normalized : Vector3.forward);
				position = val2 + val3 * 1.5f + Vector3.up * 2.5f;
				rotation = Quaternion.LookRotation(-val3);
			}
		}

		public static float RoundTripTime()
		{
			try
			{
				return (float)NetworkTime.rtt;
			}
			catch
			{
				return 0f;
			}
		}
	}
	internal enum DeliveryStop
	{
		Done,
		LevelUp,
		Blocked,
		SlotChanged,
		MaxLevel,
		NoValue,
		Unavailable,
		Error
	}
	internal static class ForgeServer
	{
		private const int PoolReserveForOthers = 6;

		private const int MaxVisualsPerDelivery = 6;

		private static bool? _visualSafe;

		private static float _nextErrorLog;

		private static bool VisualSafe
		{
			get
			{
				if (!_visualSafe.HasValue)
				{
					bool flag = false;
					try
					{
						MethodInfo methodInfo = AccessTools.Method(typeof(RogueLikeUpgradeManager), "AddResources", (Type[])null, (Type[])null);
						Patches val = ((methodInfo != null) ? Harmony.GetPatchInfo((MethodBase)methodInfo) : null);
						if (val != null)
						{
							foreach (Patch prefix in val.Prefixes)
							{
								if (prefix.owner == "catedralemchamas.stonewards.forgestack")
								{
									flag = true;
								}
							}
						}
					}
					catch
					{
						flag = false;
					}
					_visualSafe = flag;
					if (!flag)
					{
						Plugin.Log.LogWarning((object)"ForgeStack: patch de AddResources não encontrado; a entrega direta vai creditar sem animação.");
					}
				}
				return _visualSafe.Value;
			}
		}

		public static bool CanDeliver(Forge forge)
		{
			if (!Plugin.InstantWhenHost.Value || (Object)(object)forge == (Object)null || !NetworkServer.active || !((NetworkBehaviour)forge).isServer)
			{
				return false;
			}
			RogueLikeUpgradeManager instance = RogueLikeUpgradeManager.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.rogueLikeDatabase != (Object)null)
			{
				return (Object)(object)NetworkHelper.Instance != (Object)null;
			}
			return false;
		}

		public static bool PhaseBlocked()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			NetworkHelper instance = NetworkHelper.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return true;
			}
			if ((int)instance.NetworkblockingState == 0)
			{
				return instance.isRogueUpgradePhaseActive;
			}
			return true;
		}

		public static DeliveryStop Deliver(FirstPersonController player, Forge forge, int slot, string itemId, int maxUnits, out int credited)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			credited = 0;
			if (maxUnits <= 0 || (Object)(object)player == (Object)null || string.IsNullOrEmpty(itemId) || !CanDeliver(forge))
			{
				return DeliveryStop.Unavailable;
			}
			InventorySystem inventorySystem = player.InventorySystem;
			RogueLikeUpgradeManager instance = RogueLikeUpgradeManager.Instance;
			if ((Object)(object)inventorySystem == (Object)null || (Object)(object)instance == (Object)null)
			{
				return DeliveryStop.Unavailable;
			}
			if (SafeIsMaxLevel(instance))
			{
				return DeliveryStop.MaxLevel;
			}
			if (ResourceValue(instance, itemId) <= 0)
			{
				return DeliveryStop.NoValue;
			}
			if (PhaseBlocked())
			{
				return DeliveryStop.Blocked;
			}
			if (!AnyPlayerAvailable())
			{
				return DeliveryStop.Blocked;
			}
			int num = VisualBudget(forge, itemId);
			DeliveryStop result = DeliveryStop.Done;
			for (int i = 0; i < maxUnits; i++)
			{
				SyncList<InventoryEntry> inventoryEntries = inventorySystem.InventoryEntries;
				if (inventoryEntries == null || slot < 0 || slot >= inventoryEntries.Count)
				{
					result = DeliveryStop.SlotChanged;
					break;
				}
				InventoryEntry val = inventoryEntries[slot];
				if (((InventoryEntry)(ref val)).IsEmpty || val.itemID != itemId || val.count <= 0)
				{
					result = DeliveryStop.SlotChanged;
					break;
				}
				if (SafeIsMaxLevel(instance))
				{
					result = DeliveryStop.MaxLevel;
					break;
				}
				int count = val.count;
				bool flag;
				try
				{
					flag = inventorySystem.ServerRemoveItemFromSlot(slot, 1);
				}
				catch (Exception ex)
				{
					LogError($"ForgeStack: erro tirando {itemId} do espaço {slot} do inventário: {ex.Message}");
					int num2 = SlotCount(inventorySystem, slot, itemId);
					if (num2 >= 0 && count - num2 > 0)
					{
						credited += CreditUnits(instance, itemId, 1);
					}
					result = DeliveryStop.Error;
					break;
				}
				int num3 = (flag ? MeasureRemoved(inventorySystem, slot, itemId, count) : 0);
				if (num3 <= 0)
				{
					result = DeliveryStop.SlotChanged;
					break;
				}
				int num4 = CreditUnits(instance, itemId, num3);
				credited += num4;
				if (num4 < num3)
				{
					Refund(player, instance, itemId, num3 - num4);
					result = DeliveryStop.Error;
					break;
				}
				if (num > 0 && PlayVisual(player, forge, itemId))
				{
					num--;
				}
				if (PhaseBlocked())
				{
					result = DeliveryStop.LevelUp;
					break;
				}
			}
			if (credited > 0)
			{
				AnnounceToGame(player, itemId, credited);
			}
			return result;
		}

		private static int CreditUnits(RogueLikeUpgradeManager manager, string itemId, int units)
		{
			int num = 0;
			for (int i = 0; i < units; i++)
			{
				int num2 = 0;
				int num3 = 0;
				bool flag = false;
				try
				{
					num2 = manager.GetCurrentCharge();
					num3 = manager.GetCurrentLevel();
					flag = true;
				}
				catch
				{
				}
				try
				{
					manager.AddResources(itemId, 1);
					num++;
				}
				catch (Exception ex)
				{
					bool flag2 = false;
					if (flag)
					{
						try
						{
							flag2 = manager.GetCurrentCharge() != num2 || manager.GetCurrentLevel() != num3;
						}
						catch
						{
						}
					}
					if (flag2)
					{
						num++;
					}
					LogError($"ForgeStack: erro creditando {itemId} na Forja (creditado: {flag2}): {ex.Message}");
					break;
				}
			}
			return num;
		}

		private static void Refund(FirstPersonController player, RogueLikeUpgradeManager manager, string itemId, int units)
		{
			if (units <= 0)
			{
				return;
			}
			ItemDataSO val = ResourceItem(manager, itemId);
			int num = units;
			if ((Object)(object)val != (Object)null)
			{
				try
				{
					num = player.InventorySystem.ServerAddItem(val, units);
				}
				catch (Exception ex)
				{
					LogError($"ForgeStack: erro devolvendo {units}x {itemId} ao inventário: {ex.Message}");
					num = units;
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogError((object)$"ForgeStack: {num}x {itemId} saiu do inventário sem ser creditado e não coube de volta (inventário cheio?).");
			}
			else
			{
				Plugin.Log.LogWarning((object)$"ForgeStack: {units}x {itemId} não foi creditado na Forja e voltou para o inventário.");
			}
		}

		private static void AnnounceToGame(FirstPersonController player, string itemId, int units)
		{
			try
			{
				player.TargetRPCOnResourcesToForge(itemId, units);
			}
			catch (Exception ex)
			{
				LogError($"ForgeStack: erro avisando o jogo do depósito de {units}x {itemId}: {ex.Message}");
			}
			try
			{
				if ((Object)(object)player.FPSAudioPlayer != (Object)null)
				{
					((NetworkedAudioPlayer)player.FPSAudioPlayer).PlaySound("DROP_ITEM");
				}
			}
			catch
			{
			}
		}

		private static int VisualBudget(Forge forge, string itemId)
		{
			if (!VisualSafe)
			{
				return 0;
			}
			int num = ForgeGame.FreePoolObjects(forge, itemId);
			if (num < 0)
			{
				return 0;
			}
			return Mathf.Clamp(num - 6, 0, 6);
		}

		private static bool PlayVisual(FirstPersonController player, Forge forge, string itemId)
		{
			//IL_000c: 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)
			try
			{
				ForgeGame.GetAnimationOrigin(player, forge, out var position, out var rotation);
				forge.RpcPlayPlaceAnimation(position, rotation, itemId, 0);
				return true;
			}
			catch (Exception ex)
			{
				LogError("ForgeStack: erro na animação da Forja: " + ex.Message);
				_visualSafe = false;
				return false;
			}
		}

		private static int SlotCount(InventorySystem inventory, int slot, string itemId)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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)
			try
			{
				SyncList<InventoryEntry> inventoryEntries = inventory.InventoryEntries;
				if (inventoryEntries == null || slot < 0 || slot >= inventoryEntries.Count)
				{
					return 0;
				}
				InventoryEntry val = inventoryEntries[slot];
				return (val.itemID == itemId) ? Math.Max(0, val.count) : 0;
			}
			catch (Exception ex)
			{
				LogError($"ForgeStack: erro relendo o espaço {slot} do inventário: {ex.Message}");
				return -1;
			}
		}

		private static int MeasureRemoved(InventorySystem inventory, int slot, string itemId, int before)
		{
			int num = SlotCount(inventory, slot, itemId);
			if (num < 0)
			{
				return 1;
			}
			int num2 = before - num;
			if (num2 > 1)
			{
				LogError($"ForgeStack: o espaço {slot} perdeu {num2}x {itemId} numa remoção de 1; creditando 1 (algo mais mexeu no inventário).");
				num2 = 1;
			}
			if (num2 >= 0)
			{
				return num2;
			}
			return 0;
		}

		private static int ResourceValue(RogueLikeUpgradeManager manager, string itemId)
		{
			try
			{
				return manager.rogueLikeDatabase.GetResourceValueByID(itemId);
			}
			catch (Exception ex)
			{
				LogError("ForgeStack: erro lendo o valor de " + itemId + " no gerenciador da Forja: " + ex.Message);
				return 0;
			}
		}

		private static ItemDataSO ResourceItem(RogueLikeUpgradeManager manager, string itemId)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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)
			try
			{
				SResourcesValue[] resourcesValues = manager.rogueLikeDatabase.ResourcesValues;
				if (resourcesValues != null)
				{
					SResourcesValue[] array = resourcesValues;
					foreach (SResourcesValue val in array)
					{
						if ((Object)(object)val.item != (Object)null && val.item.itemID == itemId)
						{
							return val.item;
						}
					}
				}
			}
			catch (Exception ex)
			{
				LogError("ForgeStack: erro procurando o item " + itemId + " no banco da Forja: " + ex.Message);
			}
			return null;
		}

		private static bool SafeIsMaxLevel(RogueLikeUpgradeManager manager)
		{
			try
			{
				return manager.IsMaxLevel();
			}
			catch
			{
				return true;
			}
		}

		private static bool AnyPlayerAvailable()
		{
			try
			{
				foreach (FirstPersonController localPlayer in FirstPersonController.LocalPlayers)
				{
					if ((Object)(object)localPlayer != (Object)null && localPlayer.IsAvailable)
					{
						return true;
					}
				}
			}
			catch
			{
				return false;
			}
			return false;
		}

		private static void LogError(string message)
		{
			if (!(Time.unscaledTime < _nextErrorLog))
			{
				_nextErrorLog = Time.unscaledTime + 10f;
				Plugin.Log.LogError((object)message);
			}
		}

		public static void Reset()
		{
			_visualSafe = null;
		}
	}
	[HarmonyPatch(typeof(RogueLikeUpgradeManager), "AddResources")]
	internal static class ForgeZeroCountAnimationPatch
	{
		private static bool Prefix(int _Count)
		{
			return _Count > 0;
		}
	}
	internal class ForgeStackController : MonoBehaviour
	{
		private enum KeyPhase
		{
			Idle,
			Holding,
			Suppressed
		}

		private static readonly string[] ConflictingGuids = new string[2] { "catedralemchamas.stonewards.remoteforge", "br.begamerxd.stonewards.remoteforge" };

		private const float BarDelaySeconds = 0.08f;

		private const float TargetGraceSeconds = 1f;

		private readonly SlotLedger _ledger = new SlotLedger();

		private readonly SendQueue _queue = new SendQueue();

		private readonly FeedbackHud _hud = new FeedbackHud();

		private KeyPhase _phase;

		private bool _keyWasDown;

		private float _pressStart;

		private bool _holdFired;

		private int _sweepLastSlot = -1;

		private FirstPersonController _lastPlayer;

		private InventorySystem _subscribedInventory;

		private readonly List<int> _slotEvents = new List<int>();

		private float _quietUntil;

		private float _targetFailSince = -1f;

		private readonly HashSet<int> _timedOutSlots = new HashSet<int>();

		private bool _modeKnown;

		private SendMode _lastMode;

		private bool _conflictChecked;

		private string _conflictName;

		private bool _conflictShown;

		private readonly HashSet<string> _loggedErrors = new HashSet<string>();

		private void Awake()
		{
			_queue.Sent = delegate(string itemId, int units)
			{
				_hud.AddSent(itemId, units, Time.unscaledTime);
			};
			_queue.NotCreditable = delegate(string itemId)
			{
				_hud.ShowHint("A Forja não aceita " + ItemNames.Get(itemId) + " agora", Time.unscaledTime);
			};
			_queue.Refused = delegate(string message)
			{
				_hud.ShowHint(message, Time.unscaledTime, 3f);
			};
			_ledger.TimedOut = delegate(int slot)
			{
				_queue.CancelSlot(slot);
				_timedOutSlots.Add(slot);
				Plugin.Log.LogWarning((object)$"ForgeStack: o servidor não confirmou o envio do espaço {slot}; envio cancelado (aperte de novo).");
			};
		}

		private void Update()
		{
			float unscaledTime = Time.unscaledTime;
			try
			{
				CheckConflicts();
				CheckModeChanged(unscaledTime);
				Tick(unscaledTime);
			}
			catch (Exception ex)
			{
				LogOnce(ex);
				_queue.CancelAll();
				_phase = KeyPhase.Suppressed;
				_hud.HoldProgress = -1f;
			}
		}

		private void OnGUI()
		{
			try
			{
				_hud.Draw(Time.unscaledTime, Plugin.ShowFeedback.Value);
			}
			catch (Exception ex)
			{
				LogOnce(ex);
			}
		}

		private void Tick(float now)
		{
			bool keyDown = ReadKey();
			if (!ForgeGame.TryGetRun(out var player, out var forge))
			{
				if ((Object)(object)_lastPlayer != (Object)null)
				{
					ResetRunState();
				}
				_lastPlayer = null;
				HandleKey(keyDown, Block.NotInRun, null, null, now);
				_hud.HoldProgress = -1f;
				_hud.QueuedUnits = 0;
				return;
			}
			if ((Object)(object)player != (Object)(object)_lastPlayer)
			{
				ResetRunState();
				_lastPlayer = player;
				SubscribeSlotEvents(player.InventorySystem);
				ItemNames.Clear();
			}
			ShowConflictOnce(now);
			_ledger.Refresh(player.InventorySystem.InventoryEntries, now);
			Block block = ForgeGame.CheckPlayer(player);
			switch (block)
			{
			case Block.Paused:
				_quietUntil = Mathf.Max(_quietUntil, now + QuietSeconds());
				break;
			default:
				_queue.CancelAll();
				_quietUntil = Mathf.Max(_quietUntil, now + QuietSeconds());
				break;
			case Block.None:
				if (ForgeGame.IsVanillaSlotActionPressed(player))
				{
					_quietUntil = Mathf.Max(_quietUntil, now + QuietSeconds());
				}
				break;
			}
			HandleKey(keyDown, block, player, forge, now);
			PumpQueue(player, forge, block, now);
			UpdateHud(block == Block.None, now);
		}

		private static string BlockHint(Block block)
		{
			return block switch
			{
				Block.NotInRun => "Forja só dentro da partida", 
				Block.PlayerState => "Não dá carregando algo, na arma de cerco ou caído", 
				Block.Menu => "Feche o menu primeiro", 
				Block.Paused => "Espere a tela fechar", 
				_ => "Não dá agora", 
			};
		}

		private static bool ReadKey()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			KeyCode value = Plugin.Key.Value;
			if ((int)value != 0)
			{
				return UnityInput.Current.GetKey(value);
			}
			return false;
		}

		private void HandleKey(bool keyDown, Block block, FirstPersonController player, Forge forge, float now)
		{
			bool flag = keyDown && !_keyWasDown;
			_keyWasDown = keyDown;
			switch (_phase)
			{
			case KeyPhase.Idle:
				if (flag)
				{
					if (block == Block.None)
					{
						_phase = KeyPhase.Holding;
						_pressStart = now;
						_holdFired = false;
						_sweepLastSlot = -1;
						_timedOutSlots.Clear();
					}
					else
					{
						_hud.ShowHint(BlockHint(block), now, 2.5f);
						_phase = KeyPhase.Suppressed;
					}
				}
				break;
			case KeyPhase.Holding:
				switch (block)
				{
				case Block.Paused:
					if (!keyDown)
					{
						_phase = KeyPhase.Idle;
					}
					else if (!_holdFired)
					{
						_phase = KeyPhase.Suppressed;
					}
					break;
				default:
					_phase = (keyDown ? KeyPhase.Suppressed : KeyPhase.Idle);
					break;
				case Block.None:
					if (!_holdFired && now - _pressStart >= Plugin.HoldSeconds.Value)
					{
						_holdFired = true;
						_slotEvents.Clear();
						_sweepLastSlot = ForgeGame.SelectedSlot(player);
						DoHold(player, forge, now);
					}
					else if (_holdFired && keyDown && Plugin.SweepWhileHolding.Value)
					{
						Sweep(player, forge, now);
					}
					if (!keyDown)
					{
						if (!_holdFired && Plugin.TapSendsOne.Value)
						{
							DoTap(player, forge, now);
						}
						_phase = KeyPhase.Idle;
					}
					break;
				}
				break;
			case KeyPhase.Suppressed:
				if (!keyDown)
				{
					_phase = KeyPhase.Idle;
				}
				break;
			}
		}

		private void DoTap(FirstPersonController player, Forge forge, float now)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (CanTarget(player, forge, now))
			{
				if (!TryGetSelectedResource(player, forge, out var slot, out var entry))
				{
					_hud.ShowHint("Selecione um recurso da Forja", now);
				}
				else if (!_timedOutSlots.Contains(slot))
				{
					_queue.Enqueue(slot, entry.itemID, 1, additive: true);
				}
			}
		}

		private void DoHold(FirstPersonController player, Forge forge, float now)
		{
			if (!CanTarget(player, forge, now))
			{
				return;
			}
			int slot;
			InventoryEntry entry;
			if (Plugin.HoldActionSetting.Value == HoldAction.AllResources)
			{
				SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
				int num = ForgeGame.SelectedSlot(player);
				int num2 = 0;
				if (num >= 0 && num < inventoryEntries.Count && EnqueueWholeSlot(player, forge, num))
				{
					num2++;
				}
				for (int i = 0; i < inventoryEntries.Count; i++)
				{
					if (i != num && EnqueueWholeSlot(player, forge, i))
					{
						num2++;
					}
				}
				if (num2 == 0 && !_queue.HasJobs)
				{
					_hud.ShowHint("Nenhum recurso da Forja no inventário", now);
				}
			}
			else if (!TryGetSelectedResource(player, forge, out slot, out entry))
			{
				if (!Plugin.SweepWhileHolding.Value)
				{
					_hud.ShowHint("Selecione um recurso da Forja", now);
				}
			}
			else
			{
				EnqueueWholeSlot(player, forge, slot);
			}
		}

		private void Sweep(FirstPersonController player, Forge forge, float now)
		{
			foreach (int slotEvent in _slotEvents)
			{
				SweepSlot(player, forge, slotEvent, now);
			}
			_slotEvents.Clear();
			SweepSlot(player, forge, ForgeGame.SelectedSlot(player), now);
		}

		private void SweepSlot(FirstPersonController player, Forge forge, int slot, float now)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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)
			if (slot == _sweepLastSlot)
			{
				return;
			}
			_sweepLastSlot = slot;
			SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
			if (slot >= 0 && slot < inventoryEntries.Count)
			{
				InventoryEntry val = inventoryEntries[slot];
				if (!((InventoryEntry)(ref val)).IsEmpty && ForgeGame.IsPlaceable(forge, val.itemID) && val.count - _ledger.Unconfirmed(slot) > 0 && !_timedOutSlots.Contains(slot) && CanTarget(player, forge, now))
				{
					EnqueueWholeSlot(player, forge, slot);
				}
			}
		}

		private bool EnqueueWholeSlot(FirstPersonController player, Forge forge, int slot)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
			if (slot < 0 || slot >= inventoryEntries.Count)
			{
				return false;
			}
			InventoryEntry val = inventoryEntries[slot];
			if (((InventoryEntry)(ref val)).IsEmpty || !ForgeGame.IsPlaceable(forge, val.itemID) || _timedOutSlots.Contains(slot))
			{
				return false;
			}
			int num = val.count - _ledger.Unconfirmed(slot);
			if (num <= 0)
			{
				return false;
			}
			_queue.Enqueue(slot, val.itemID, num, additive: false);
			return true;
		}

		private static bool TryGetSelectedResource(FirstPersonController player, Forge forge, out int slot, out InventoryEntry entry)
		{
			//IL_0001: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			entry = default(InventoryEntry);
			slot = ForgeGame.SelectedSlot(player);
			SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
			if (slot < 0 || slot >= inventoryEntries.Count)
			{
				return false;
			}
			entry = inventoryEntries[slot];
			if (!((InventoryEntry)(ref entry)).IsEmpty)
			{
				return ForgeGame.IsPlaceable(forge, entry.itemID);
			}
			return false;
		}

		private bool CanTarget(FirstPersonController player, Forge forge, float now)
		{
			if (Plugin.Mode.Value == SendMode.NearForge)
			{
				if (!ForgeGame.IsLookingAtForge(player, forge))
				{
					_hud.ShowHint("Olhe para a Forja", now);
					return false;
				}
			}
			else
			{
				float value = Plugin.MaxDistance.Value;
				if (value > 0f)
				{
					float num = ForgeGame.DistanceToForge(player, forge);
					if (num > value)
					{
						_hud.ShowHint($"Forja longe demais ({num:0} m, máx. {value:0} m)", now);
						return false;
					}
				}
			}
			if (ForgeGame.IsForgeMaxLevel(forge))
			{
				_hud.ShowHint("Forja no nível máximo", now);
				return false;
			}
			if (!ForgeGame.IsForgeNetworkReachable(forge))
			{
				_hud.ShowHint("Forja fora do alcance: chegue mais perto", now);
				return false;
			}
			return true;
		}

		private void PumpQueue(FirstPersonController player, Forge forge, Block block, float now)
		{
			if (!_queue.HasJobs)
			{
				_targetFailSince = -1f;
			}
			else
			{
				if (block != Block.None)
				{
					return;
				}
				if (!CanTarget(player, forge, now))
				{
					if (_targetFailSince < 0f)
					{
						_targetFailSince = now;
					}
					else if (now - _targetFailSince > 1f)
					{
						_queue.CancelAll();
						_targetFailSince = -1f;
					}
				}
				else
				{
					_targetFailSince = -1f;
					if (!(now < _quietUntil) && !ForgeGame.IsTemporarilyBusy())
					{
						_queue.Tick(player, forge, _ledger, now);
					}
				}
			}
		}

		private void UpdateHud(bool gatesOk, float now)
		{
			_hud.QueuedUnits = _queue.RemainingUnits;
			if (_phase != KeyPhase.Holding || !gatesOk)
			{
				_hud.HoldProgress = -1f;
				_hud.Sweeping = false;
				return;
			}
			float num = now - _pressStart;
			if (!_holdFired && num < 0.08f)
			{
				_hud.HoldProgress = -1f;
				return;
			}
			float num2 = Mathf.Max(0.01f, Plugin.HoldSeconds.Value);
			_hud.HoldProgress = (_holdFired ? 1f : Mathf.Clamp01(num / num2));
			_hud.Sweeping = _holdFired;
		}

		private void SubscribeSlotEvents(InventorySystem inventory)
		{
			UnsubscribeSlotEvents();
			if (!((Object)(object)inventory == (Object)null))
			{
				inventory.OnSlotSelectedUpdated += OnSlotSelected;
				_subscribedInventory = inventory;
			}
		}

		private void UnsubscribeSlotEvents()
		{
			if ((Object)(object)_subscribedInventory != (Object)null)
			{
				_subscribedInventory.OnSlotSelectedUpdated -= OnSlotSelected;
			}
			_subscribedInventory = null;
			_slotEvents.Clear();
		}

		private void OnSlotSelected(int slot)
		{
			if (_phase == KeyPhase.Holding && _holdFired && _slotEvents.Count < 32)
			{
				_slotEvents.Add(slot);
			}
		}

		private void OnDestroy()
		{
			try
			{
				UnsubscribeSlotEvents();
			}
			catch
			{
			}
		}

		private void ResetRunState()
		{
			ForgeGame.ForgetForge();
			UnsubscribeSlotEvents();
			_queue.ResetAll();
			_ledger.Clear();
			_holdFired = false;
			_sweepLastSlot = -1;
			_quietUntil = 0f;
			_targetFailSince = -1f;
			_timedOutSlots.Clear();
			if (_phase == KeyPhase.Holding)
			{
				_phase = KeyPhase.Suppressed;
			}
		}

		private static float QuietSeconds()
		{
			return Mathf.Max(0.5f, 0.3f + ForgeGame.RoundTripTime() * 3f);
		}

		private void CheckModeChanged(float now)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			SendMode value = Plugin.Mode.Value;
			if (!_modeKnown)
			{
				_modeKnown = true;
				_lastMode = value;
			}
			else if (value != _lastMode)
			{
				_lastMode = value;
				string text = ((value == SendMode.NearForge) ? "De perto" : "De longe");
				_hud.ShowNotice("ForgeStack: modo " + text, new Color(0.55f, 1f, 0.55f), now, 3f);
				Plugin.Log.LogInfo((object)$"ForgeStack: modo alterado para {value} ({text}).");
			}
		}

		private void CheckConflicts()
		{
			if (_conflictChecked)
			{
				return;
			}
			_conflictChecked = true;
			string[] conflictingGuids = ConflictingGuids;
			foreach (string text in conflictingGuids)
			{
				if (Chainloader.PluginInfos != null && Chainloader.PluginInfos.TryGetValue(text, out var value))
				{
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.Name : null);
					}
					if (obj == null)
					{
						obj = text;
					}
					_conflictName = (string)obj;
					Plugin.Log.LogWarning((object)("ForgeStack: o mod '" + _conflictName + "' (" + text + ") também envia recursos para a Forja. Se os dois usarem a mesma tecla, cada aperto envia em dobro. Desative um deles ou troque a tecla."));
					break;
				}
			}
		}

		private void ShowConflictOnce(float now)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!_conflictShown && _conflictName != null)
			{
				_conflictShown = true;
				_hud.ShowNotice("ForgeStack: '" + _conflictName + "' na mesma tecla envia em dobro", new Color(1f, 0.5f, 0.35f), now, 8f);
			}
		}

		private void LogOnce(Exception ex)
		{
			string item = ex.GetType().FullName + ":" + ex.Message;
			if (_loggedErrors.Count < 20 && _loggedErrors.Add(item))
			{
				Plugin.Log.LogError((object)("ForgeStack: erro (mostrado uma vez): " + ex));
			}
		}
	}
	internal static class ItemNames
	{
		private static readonly Dictionary<string, string> Cache = new Dictionary<string, string>();

		public static string Get(string itemId)
		{
			if (string.IsNullOrEmpty(itemId))
			{
				return "?";
			}
			if (Cache.TryGetValue(itemId, out var value))
			{
				return value;
			}
			string text = null;
			try
			{
				ItemDataSO val = (((Object)(object)ItemManager.Instance != (Object)null) ? ItemManager.Instance.GetItemDataSoByID(itemId) : null);
				if ((Object)(object)val != (Object)null)
				{
					text = val.GetLocalizedName();
				}
			}
			catch (Exception)
			{
			}
			if (string.IsNullOrEmpty(text) || text.IndexOf("No translation found", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return Prettify(itemId);
			}
			Cache[itemId] = text;
			return text;
		}

		public static void Clear()
		{
			Cache.Clear();
		}

		private static string Prettify(string itemId)
		{
			string text = itemId.Replace('_', ' ').ToLowerInvariant();
			if (text.Length <= 0)
			{
				return itemId;
			}
			return char.ToUpperInvariant(text[0]) + text.Substring(1);
		}
	}
	public enum SendMode
	{
		Anywhere,
		NearForge
	}
	public enum HoldAction
	{
		WholeStack,
		AllResources
	}
	[BepInPlugin("catedralemchamas.stonewards.forgestack", "ForgeStack", "1.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<SendMode> Mode;

		internal static ConfigEntry<bool> InstantWhenHost;

		internal static ConfigEntry<KeyCode> Key;

		internal static ConfigEntry<float> HoldSeconds;

		internal static ConfigEntry<HoldAction> HoldActionSetting;

		internal static ConfigEntry<bool> TapSendsOne;

		internal static ConfigEntry<bool> SweepWhileHolding;

		internal static ConfigEntry<bool> ShowFeedback;

		internal static ConfigEntry<float> MaxDistance;

		internal static ConfigEntry<int> UnitsPerSend;

		internal static ConfigEntry<float> SendInterval;

		private ConfigHotReload _hotReload;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected O, but got Unknown
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Mode = ((BaseUnityPlugin)this).Config.Bind<SendMode>("General", "Mode", SendMode.Anywhere, "Anywhere = from anywhere in the run / NearForge = only looking at the Forge. PT: Anywhere = de qualquer lugar da partida / NearForge = só olhando para a Forja.");
			InstantWhenHost = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "InstantWhenHost", true, "Hosting: the whole stack is credited at once, stopping at each Forge level-up. / PT: Sendo host: a pilha inteira é creditada de uma vez, parando a cada nível da Forja.");
			Key = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "Key", (KeyCode)103, "Key: tap = 1 unit, hold = whole stack. / PT: Tecla: toque = 1 unidade, segurar = pilha inteira.");
			HoldSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HoldSeconds", 0.4f, new ConfigDescription("Seconds to hold the key for the hold action. / PT: Segundos segurando a tecla para enviar tudo.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			HoldActionSetting = ((BaseUnityPlugin)this).Config.Bind<HoldAction>("General", "HoldAction", HoldAction.WholeStack, "WholeStack = selected stack / AllResources = every stack the Forge accepts. PT: WholeStack = pilha selecionada / AllResources = todas as pilhas aceitas pela Forja.");
			TapSendsOne = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "TapSendsOne", true, "Tapping the key sends 1 unit. / PT: Um toque rápido envia 1 unidade.");
			SweepWhileHolding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SweepWhileHolding", true, "While holding, every slot you select is sent whole. / PT: Segurando a tecla, cada espaço selecionado vai inteiro.");
			ShowFeedback = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowFeedback", true, "On-screen text and hold progress bar. / PT: Texto na tela e barra de progresso ao segurar.");
			MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Anywhere", "MaxDistance", 0f, new ConfigDescription("Anywhere mode only: max distance to the Forge in meters (0 = unlimited). / PT: Só no modo Anywhere: distância máxima até a Forja em metros (0 = sem limite).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			UnitsPerSend = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "UnitsPerSend", 8, new ConfigDescription("As a client only: target units per send (the real batch follows the free space in the Forge animation). / PT: Só como cliente: alvo de unidades por envio (o lote real acompanha a folga da animação da Forja).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			SendInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "SendInterval", 0.1f, new ConfigDescription("As a client only: seconds between sends. / PT: Só como cliente: segundos entre envios.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			try
			{
				_harmony = new Harmony("catedralemchamas.stonewards.forgestack");
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				_harmony = null;
				Log.LogWarning((object)("ForgeStack: não foi possível aplicar o patch da animação da Forja; a entrega direta vai creditar sem animação. " + ex.Message));
			}
			((Component)this).gameObject.AddComponent<ForgeStackController>();
			_hotReload = new ConfigHotReload(((BaseUnityPlugin)this).Config, Log);
			Log.LogInfo((object)(string.Format("{0} v{1} carregado. Modo {2}, tecla {3}, ", "ForgeStack", "1.1.1", Mode.Value, Key.Value) + "entrega direta como host " + (InstantWhenHost.Value ? "ligada" : "desligada") + "."));
		}

		private void Update()
		{
			_hotReload?.Poll(Time.unscaledTime);
		}

		private void OnDestroy()
		{
			_hotReload?.Dispose();
			_hotReload = null;
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
			_harmony = null;
		}
	}
	internal sealed class SendQueue
	{
		private sealed class Job
		{
			public int Slot;

			public string ItemId;

			public int Remaining;
		}

		private struct RecentSend
		{
			public float Time;

			public string ItemId;

			public int Units;

			public int Heat;
		}

		private const int PoolMargin = 4;

		private const float ForgeTweenSeconds = 0.4f;

		private const int InstantUnitsPerFrame = 128;

		private const float InstantRetrySeconds = 0.25f;

		private readonly List<Job> _jobs = new List<Job>();

		private readonly List<RecentSend> _recent = new List<RecentSend>();

		private float _nextSendAt;

		private float _levelUpWaitUntil;

		public Action<string, int> Sent;

		public Action<string> NotCreditable;

		public Action<string> Refused;

		public bool HasJobs => _jobs.Count > 0;

		public int RemainingUnits
		{
			get
			{
				int num = 0;
				foreach (Job job in _jobs)
				{
					num += job.Remaining;
				}
				return num;
			}
		}

		public bool HasJob(int slot)
		{
			foreach (Job job in _jobs)
			{
				if (job.Slot == slot)
				{
					return true;
				}
			}
			return false;
		}

		public void Enqueue(int slot, string itemId, int units, bool additive)
		{
			if (slot < 0 || units <= 0 || string.IsNullOrEmpty(itemId))
			{
				return;
			}
			foreach (Job job in _jobs)
			{
				if (job.Slot == slot)
				{
					if (job.ItemId == itemId)
					{
						job.Remaining = (additive ? (job.Remaining + units) : Math.Max(job.Remaining, units));
						return;
					}
					job.Remaining = 0;
				}
			}
			_jobs.RemoveAll((Job j) => j.Remaining <= 0);
			_jobs.Add(new Job
			{
				Slot = slot,
				ItemId = itemId,
				Remaining = units
			});
		}

		public void CancelAll()
		{
			_jobs.Clear();
		}

		public void CancelSlot(int slot)
		{
			_jobs.RemoveAll((Job j) => j.Slot == slot);
		}

		public void ResetAll()
		{
			_jobs.Clear();
			_recent.Clear();
			_nextSendAt = 0f;
			_levelUpWaitUntil = 0f;
			ForgeServer.Reset();
		}

		public void Tick(FirstPersonController player, Forge forge, SlotLedger ledger, float now)
		{
			float num = ForgeGame.RoundTripTime();
			float num2 = 0.9f + num * 1.5f;
			PruneRecent(now, num2);
			if (_jobs.Count != 0 && !(now < _nextSendAt) && !(now < _levelUpWaitUntil))
			{
				if (ForgeServer.CanDeliver(forge))
				{
					TickInstant(player, forge, now);
				}
				else
				{
					TickNetwork(player, forge, ledger, now, num, num2);
				}
			}
		}

		private void TickInstant(FirstPersonController player, Forge forge, float now)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			int num = 128;
			int num2 = 0;
			while (num2 < _jobs.Count && num > 0)
			{
				Job job = _jobs[num2];
				if (!IsJobStillValid(player, forge, job, out var entry))
				{
					_jobs.RemoveAt(num2);
					continue;
				}
				int num3 = Math.Min(job.Remaining, Math.Min(entry.count, num));
				if (num3 <= 0)
				{
					_jobs.RemoveAt(num2);
					continue;
				}
				int credited;
				DeliveryStop deliveryStop = ForgeServer.Deliver(player, forge, job.Slot, job.ItemId, num3, out credited);
				if (credited > 0)
				{
					job.Remaining -= credited;
					num -= credited;
					Sent?.Invoke(job.ItemId, credited);
				}
				bool flag = job.Remaining <= 0;
				bool flag2 = false;
				switch (deliveryStop)
				{
				case DeliveryStop.SlotChanged:
					flag = true;
					break;
				case DeliveryStop.LevelUp:
					flag2 = true;
					break;
				case DeliveryStop.Blocked:
					_nextSendAt = now + 0.25f;
					flag2 = true;
					break;
				case DeliveryStop.MaxLevel:
					_jobs.Clear();
					Refused?.Invoke("Forja no nível máximo");
					Plugin.Log.LogWarning((object)"ForgeStack: Forja no nível máximo; entrega cancelada (o jogo descartaria os recursos).");
					return;
				case DeliveryStop.NoValue:
					flag = true;
					NotCreditable?.Invoke(job.ItemId);
					Plugin.Log.LogWarning((object)("ForgeStack: a Forja aceita " + job.ItemId + ", mas ele vale 0 no gerenciador da Forja; nada enviado."));
					break;
				case DeliveryStop.Unavailable:
					flag2 = true;
					break;
				default:
					_jobs.Clear();
					_nextSendAt = now + 0.25f;
					Refused?.Invoke("Erro ao entregar na Forja: envio cancelado");
					return;
				case DeliveryStop.Done:
					break;
				}
				if (flag)
				{
					_jobs.RemoveAt(num2);
				}
				else
				{
					num2++;
				}
				if (!flag2)
				{
					continue;
				}
				break;
			}
		}

		private void TickNetwork(FirstPersonController player, Forge forge, SlotLedger ledger, float now, float rtt, float creditWindow)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
			for (int i = 0; i < _jobs.Count; i++)
			{
				Job job = _jobs[i];
				if (job.Slot >= inventoryEntries.Count)
				{
					_jobs.RemoveAt(i--);
					continue;
				}
				InventoryEntry val = inventoryEntries[job.Slot];
				if (((InventoryEntry)(ref val)).IsEmpty || val.itemID != job.ItemId || !ForgeGame.IsPlaceable(forge, val.itemID))
				{
					_jobs.RemoveAt(i--);
					continue;
				}
				int num = ledger.Unconfirmed(job.Slot);
				int num2 = val.count - num;
				if (num2 <= 0)
				{
					if (num <= 0)
					{
						_jobs.RemoveAt(i--);
					}
					continue;
				}
				int num3 = ForgeGame.FreePoolObjects(forge, val.itemID);
				if (num3 < 0)
				{
					_jobs.RemoveAt(i--);
					NotCreditable?.Invoke(val.itemID);
					continue;
				}
				int num4 = num3 - RecentUnits(val.itemID, now, rtt + 0.15f) - 4;
				if (num4 <= 0)
				{
					continue;
				}
				int val2 = Math.Max(1, Plugin.UnitsPerSend.Value);
				int val3 = Math.Min(job.Remaining, Math.Min(num2, Math.Max(val2, num4)));
				val3 = Math.Min(val3, num4);
				bool flag = false;
				int num5 = ForgeGame.ResourceValue(forge, val.itemID);
				int networkcurrentMaxCharge = forge.NetworkcurrentMaxCharge;
				if (num5 > 0 && networkcurrentMaxCharge > 0)
				{
					int num6 = forge.NetworkcurrentCharge + RecentHeat();
					int num7 = networkcurrentMaxCharge - num6;
					int num8 = ((num7 <= 0) ? 1 : ((num7 + num5 - 1) / num5));
					if (val3 >= num8)
					{
						val3 = num8;
						flag = true;
					}
				}
				if (val3 > 0)
				{
					ForgeGame.GetAnimationOrigin(player, forge, out var position, out var rotation);
					player.CmdTryPlaceItemInItemInteractable(player, val.itemID, job.Slot, val3, forge.NetworkIdentity, position, rotation);
					ledger.RecordSend(job.Slot, val.itemID, val.count, val3, now);
					_recent.Add(new RecentSend
					{
						Time = now,
						ItemId = val.itemID,
						Units = val3,
						Heat = val3 * num5
					});
					job.Remaining -= val3;
					if (job.Remaining <= 0)
					{
						_jobs.RemoveAt(i);
					}
					_nextSendAt = now + Plugin.SendInterval.Value;
					if (flag)
					{
						_levelUpWaitUntil = now + creditWindow + 0.3f;
					}
					Sent?.Invoke(val.itemID, val3);
					break;
				}
			}
		}

		private static bool IsJobStillValid(FirstPersonController player, Forge forge, Job job, out InventoryEntry entry)
		{
			//IL_0001: 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)
			entry = default(InventoryEntry);
			SyncList<InventoryEntry> inventoryEntries = player.InventorySystem.InventoryEntries;
			if (inventoryEntries == null || job.Slot < 0 || job.Slot >= inventoryEntries.Count)
			{
				return false;
			}
			entry = inventoryEntries[job.Slot];
			if (!((InventoryEntry)(ref entry)).IsEmpty && entry.itemID == job.ItemId)
			{
				return ForgeGame.IsPlaceable(forge, entry.itemID);
			}
			return false;
		}

		private void PruneRecent(float now, float window)
		{
			_recent.RemoveAll((RecentSend r) => now - r.Time > window);
		}

		private int RecentUnits(string itemId, float now, float window)
		{
			int num = 0;
			foreach (RecentSend item in _recent)
			{
				if (item.ItemId == itemId && now - item.Time <= window)
				{
					num += item.Units;
				}
			}
			return num;
		}

		private int RecentHeat()
		{
			int num = 0;
			foreach (RecentSend item in _recent)
			{
				num += item.Heat;
			}
			return num;
		}
	}
	internal sealed class SlotLedger
	{
		private sealed class Pending
		{
			public string ItemId;

			public int BaseCount;

			public int Unconfirmed;

			public float LastSendTime;
		}

		private readonly Dictionary<int, Pending> _pending = new Dictionary<int, Pending>();

		private readonly List<int> _toRemove = new List<int>();

		public Action<int> TimedOut;

		public bool IsEmpty => _pending.Count == 0;

		public int Unconfirmed(int slot)
		{
			if (!_pending.TryGetValue(slot, out var value))
			{
				return 0;
			}
			return value.Unconfirmed;
		}

		public void RecordSend(int slot, string itemId, int countBeforeSend, int units, float now)
		{
			if (_pending.TryGetValue(slot, out var value) && value.ItemId == itemId)
			{
				value.Unconfirmed += units;
				value.LastSendTime = now;
				return;
			}
			_pending[slot] = new Pending
			{
				ItemId = itemId,
				BaseCount = countBeforeSend,
				Unconfirmed = units,
				LastSendTime = now
			};
		}

		public void Refresh(SyncList<InventoryEntry> entries, float now)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			if (_pending.Count == 0)
			{
				return;
			}
			float num = ConfirmTimeout();
			_toRemove.Clear();
			foreach (KeyValuePair<int, Pending> item in _pending)
			{
				int key = item.Key;
				Pending value = item.Value;
				if (entries == null || key < 0 || key >= entries.Count)
				{
					_toRemove.Add(key);
					continue;
				}
				InventoryEntry val = entries[key];
				if (((InventoryEntry)(ref val)).IsEmpty || val.itemID != value.ItemId)
				{
					_toRemove.Add(key);
					continue;
				}
				if (val.count <= value.BaseCount - value.Unconfirmed)
				{
					_toRemove.Add(key);
					continue;
				}
				if (val.count > value.BaseCount)
				{
					value.BaseCount = val.count;
				}
				if (now - value.LastSendTime > num)
				{
					_toRemove.Add(key);
					TimedOut?.Invoke(key);
				}
			}
			foreach (int item2 in _toRemove)
			{
				_pending.Remove(item2);
			}
		}

		public void Clear()
		{
			_pending.Clear();
		}

		private static float ConfirmTimeout()
		{
			float num = 0f;
			try
			{
				num = (float)NetworkTime.rtt;
			}
			catch
			{
			}
			return Math.Max(5f, num * 10f + 2f);
		}
	}
	internal static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "catedralemchamas.stonewards.forgestack";

		public const string PLUGIN_NAME = "ForgeStack";

		public const string PLUGIN_VERSION = "1.1.1";

		public const string PLUGIN_AUTHOR = "CatedralEmChamas";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}