Decompiled source of AutomationByGoblins v0.7.0

AutomationByGoblins.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("AutomationByGoblins")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AutomationByGoblins")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("78b94844-bf74-4318-b728-1f83ab5873b3")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace AutomationByGoblins;

[BepInPlugin("mintmango.automationbygoblins", "AutomationByGoblins", "0.7.0")]
public sealed class AutomationByGoblinsPlugin : BaseUnityPlugin
{
	public const string ModGuid = "mintmango.automationbygoblins";

	public const string ModName = "AutomationByGoblins";

	public const string ModVersion = "0.7.0";

	internal static ManualLogSource ModLog;

	internal static ConfigEntry<float> WoodWorkerChance;

	internal static ConfigEntry<float> StoneWorkerChance;

	internal static ConfigEntry<float> GathererWorkerChance;

	internal static ConfigEntry<float> TamingTimeSeconds;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Expected O, but got Unknown
		ModLog = ((BaseUnityPlugin)this).Logger;
		WoodWorkerChance = ((BaseUnityPlugin)this).Config.Bind<float>("Spawning", "WoodWorkerChance", 10f, "Chance in percent for a normal melee Goblin to become the beige intelligent wood-worker type.");
		StoneWorkerChance = ((BaseUnityPlugin)this).Config.Bind<float>("Spawning", "StoneWorkerChance", 10f, "Chance in percent for a normal melee Goblin to become the gray intelligent stone-worker type.");
		GathererWorkerChance = ((BaseUnityPlugin)this).Config.Bind<float>("Spawning", "GathererWorkerChance", 10f, "Chance in percent for a normal melee Goblin to become the green intelligent gatherer type.");
		TamingTimeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "TamingTimeSeconds", 3600f, "Taming time in seconds. 3600 seconds is twice the normal 30 minute taming time.");
		_harmony = new Harmony("mintmango.automationbygoblins");
		_harmony.PatchAll();
		ModLog.LogInfo((object)"AutomationByGoblins 0.7.0 loaded.");
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}
}
internal enum GoblinWorkerType
{
	Unassigned,
	Wood,
	Stone,
	Normal,
	Gatherer
}
public sealed class GoblinWorkerController : MonoBehaviour
{
	private const string WorkerTypeZdoKey = "AutomationByGoblins.WorkerType";

	private const string TamingFoodPrefab = "PungentPebbles";

	private static readonly Color WoodClothingColor = new Color(0.96f, 0.78f, 0.48f, 1f);

	private static readonly Color StoneClothingColor = new Color(0.32f, 0.36f, 0.43f, 1f);

	private static readonly Color GathererClothingColor = new Color(0.4f, 0.68f, 0.3f, 1f);

	private readonly HashSet<Renderer> _alreadyTinted = new HashSet<Renderer>();

	private ZNetView _nview;

	private Character _character;

	private GoblinWorkerType _workerType = GoblinWorkerType.Unassigned;

	internal bool IsIntelligent => _workerType == GoblinWorkerType.Wood || _workerType == GoblinWorkerType.Stone || _workerType == GoblinWorkerType.Gatherer;

	internal GoblinWorkerType WorkerType => _workerType;

	private void Start()
	{
		_character = ((Component)this).GetComponent<Character>();
		_nview = ((Component)this).GetComponent<ZNetView>();
		((MonoBehaviour)this).StartCoroutine(InitializeWhenNetworkReady());
	}

	private IEnumerator InitializeWhenNetworkReady()
	{
		while ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
		{
			if ((Object)(object)_nview == (Object)null)
			{
				_nview = ((Component)this).GetComponent<ZNetView>();
			}
			yield return null;
		}
		while (_workerType == GoblinWorkerType.Unassigned)
		{
			int storedType = _nview.GetZDO().GetInt("AutomationByGoblins.WorkerType", 0);
			if (storedType >= 1 && storedType <= 4)
			{
				_workerType = (GoblinWorkerType)storedType;
				break;
			}
			if (_nview.IsOwner())
			{
				_workerType = RollWorkerType();
				_nview.GetZDO().Set("AutomationByGoblins.WorkerType", (int)_workerType);
				break;
			}
			yield return (object)new WaitForSeconds(0.25f);
		}
		if (IsIntelligent)
		{
			ConfigureTaming();
			AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Configured intelligent " + _workerType.ToString() + " Goblin."));
			while ((Object)(object)this != (Object)null && (Object)(object)((Component)this).gameObject != (Object)null)
			{
				ApplyWorkerClothingColor();
				yield return (object)new WaitForSeconds(2f);
			}
		}
	}

	private GoblinWorkerType RollWorkerType()
	{
		float num = Mathf.Clamp(AutomationByGoblinsPlugin.WoodWorkerChance.Value, 0f, 100f);
		float num2 = Mathf.Clamp(AutomationByGoblinsPlugin.StoneWorkerChance.Value, 0f, 100f);
		float num3 = Mathf.Clamp(AutomationByGoblinsPlugin.GathererWorkerChance.Value, 0f, 100f);
		float num4 = num + num2 + num3;
		if (num4 > 100f)
		{
			float num5 = 100f / num4;
			num *= num5;
			num2 *= num5;
			num3 *= num5;
		}
		float num6 = Random.Range(0f, 100f);
		if (num6 < num)
		{
			return GoblinWorkerType.Wood;
		}
		if (num6 < num + num2)
		{
			return GoblinWorkerType.Stone;
		}
		if (num6 < num + num2 + num3)
		{
			return GoblinWorkerType.Gatherer;
		}
		return GoblinWorkerType.Normal;
	}

	private void ConfigureTaming()
	{
		Tameable val = ((Component)this).GetComponent<Tameable>();
		if ((Object)(object)val == (Object)null)
		{
			val = ((Component)this).gameObject.AddComponent<Tameable>();
		}
		MonsterAI component = ((Component)this).GetComponent<MonsterAI>();
		if ((Object)(object)component == (Object)null)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)"Intelligent Goblin has no MonsterAI; taming cannot be configured.");
			return;
		}
		ApplyBoarTamingConfiguration(val, component);
		SetFieldIfPresent(val, "m_commandable", true);
		SetFieldIfPresent(component, "m_tamable", val);
		SetFieldIfPresent(component, "m_tameable", val);
		((MonoBehaviour)this).StartCoroutine(ConfigureTamingFoodWhenReady(component));
	}

	private void ApplyBoarTamingConfiguration(Tameable targetTameable, MonsterAI targetMonsterAI)
	{
		Tameable val = null;
		MonsterAI val2 = null;
		if ((Object)(object)ZNetScene.instance != (Object)null)
		{
			GameObject prefab = ZNetScene.instance.GetPrefab("Boar");
			if ((Object)(object)prefab != (Object)null)
			{
				val = prefab.GetComponent<Tameable>();
				val2 = prefab.GetComponent<MonsterAI>();
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)"Vanilla Boar Tameable was not found. Falling back to a 3600 second intelligent Goblin taming time.");
			SetFieldIfPresent(targetTameable, "m_tamingTime", Mathf.Max(1f, AutomationByGoblinsPlugin.TamingTimeSeconds.Value));
			return;
		}
		CopyPublicTamingSettings(val, targetTameable);
		if (!TryGetFloatField(val, "m_tamingTime", out var value))
		{
			value = 1800f;
		}
		float num = Mathf.Max(1f, value * 2f);
		SetFieldIfPresent(targetTameable, "m_tamingTime", num);
		if ((Object)(object)val2 != (Object)null)
		{
			CopyBoarConsumeSettings(val2, targetMonsterAI);
		}
		AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Intelligent Goblin uses vanilla Boar taming configuration; duration = " + num + " seconds."));
	}

	private static void CopyPublicTamingSettings(Tameable source, Tameable target)
	{
		FieldInfo[] fields = typeof(Tameable).GetFields(BindingFlags.Instance | BindingFlags.Public);
		foreach (FieldInfo fieldInfo in fields)
		{
			if (!fieldInfo.IsStatic && !fieldInfo.IsInitOnly && !(fieldInfo.Name == "m_tamingTime") && !(fieldInfo.Name == "m_commandable") && !fieldInfo.Name.StartsWith("m_command", StringComparison.OrdinalIgnoreCase) && !typeof(Component).IsAssignableFrom(fieldInfo.FieldType) && !typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType) && !typeof(Delegate).IsAssignableFrom(fieldInfo.FieldType))
			{
				try
				{
					fieldInfo.SetValue(target, fieldInfo.GetValue(source));
				}
				catch (Exception ex)
				{
					AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Skipped Boar Tameable field " + fieldInfo.Name + ": " + ex.Message));
				}
			}
		}
	}

	private static void CopyBoarConsumeSettings(MonsterAI source, MonsterAI target)
	{
		FieldInfo[] fields = typeof(MonsterAI).GetFields(BindingFlags.Instance | BindingFlags.Public);
		foreach (FieldInfo fieldInfo in fields)
		{
			if (!fieldInfo.IsStatic && !fieldInfo.IsInitOnly && fieldInfo.Name.StartsWith("m_consume", StringComparison.OrdinalIgnoreCase) && !(fieldInfo.Name == "m_consumeItems"))
			{
				try
				{
					fieldInfo.SetValue(target, fieldInfo.GetValue(source));
				}
				catch (Exception ex)
				{
					AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Skipped Boar MonsterAI field " + fieldInfo.Name + ": " + ex.Message));
				}
			}
		}
	}

	private static bool TryGetFloatField(object target, string fieldName, out float value)
	{
		value = 0f;
		if (target == null)
		{
			return false;
		}
		FieldInfo fieldInfo = AccessTools.Field(target.GetType(), fieldName);
		if (fieldInfo == null)
		{
			return false;
		}
		try
		{
			object value2 = fieldInfo.GetValue(target);
			if (value2 == null)
			{
				return false;
			}
			value = Convert.ToSingle(value2);
			return true;
		}
		catch
		{
			return false;
		}
	}

	private IEnumerator ConfigureTamingFoodWhenReady(MonsterAI monsterAI)
	{
		GameObject foodPrefab = null;
		while (true)
		{
			int num;
			if (!((Object)(object)ObjectDB.instance == (Object)null))
			{
				GameObject itemPrefab;
				foodPrefab = (itemPrefab = ObjectDB.instance.GetItemPrefab("PungentPebbles"));
				num = (((Object)(object)itemPrefab == (Object)null) ? 1 : 0);
			}
			else
			{
				num = 1;
			}
			if (num == 0)
			{
				break;
			}
			yield return (object)new WaitForSeconds(1f);
		}
		ItemDrop itemDrop = foodPrefab.GetComponent<ItemDrop>();
		if ((Object)(object)itemDrop == (Object)null)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)"PungentPebbles exists but has no ItemDrop component.");
			yield break;
		}
		FieldInfo consumeItemsField = AccessTools.Field(((object)monsterAI).GetType(), "m_consumeItems");
		if (consumeItemsField == null)
		{
			consumeItemsField = AccessTools.Field(typeof(MonsterAI), "m_consumeItems");
		}
		if (consumeItemsField == null)
		{
			AutomationByGoblinsPlugin.ModLog.LogError((object)"MonsterAI.m_consumeItems was not found; cannot register PungentPebbles.");
			yield break;
		}
		IList consumeItems = consumeItemsField.GetValue(monsterAI) as IList;
		if (consumeItems == null)
		{
			try
			{
				consumeItems = Activator.CreateInstance(consumeItemsField.FieldType) as IList;
				consumeItemsField.SetValue(monsterAI, consumeItems);
			}
			catch (Exception ex)
			{
				Exception ex2 = ex;
				AutomationByGoblinsPlugin.ModLog.LogError((object)("Could not create Goblin consume-items list: " + ex2.Message));
				yield break;
			}
		}
		if (consumeItems != null)
		{
			consumeItems.Clear();
			consumeItems.Add(itemDrop);
		}
		AutomationByGoblinsPlugin.ModLog.LogDebug((object)"PungentPebbles registered as intelligent Goblin taming food.");
	}

	private void ApplyWorkerClothingColor()
	{
		//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_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		Color color = ((_workerType == GoblinWorkerType.Wood) ? WoodClothingColor : ((_workerType != GoblinWorkerType.Stone) ? GathererClothingColor : StoneClothingColor));
		Transform[] componentsInChildren = ((Component)this).GetComponentsInChildren<Transform>(true);
		List<Transform> list = new List<Transform>();
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if (string.Equals(((Object)componentsInChildren[i]).name, "attach_skin(Clone)", StringComparison.OrdinalIgnoreCase))
			{
				list.Add(componentsInChildren[i]);
			}
		}
		if (list.Count < 2)
		{
			return;
		}
		int num = list.Count - 2;
		for (int j = num; j < list.Count; j++)
		{
			Renderer[] componentsInChildren2 = ((Component)list[j]).GetComponentsInChildren<Renderer>(true);
			for (int k = 0; k < componentsInChildren2.Length; k++)
			{
				TintRendererOnce(componentsInChildren2[k], color);
			}
		}
	}

	private void TintRendererOnce(Renderer renderer, Color color)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)renderer == (Object)null || _alreadyTinted.Contains(renderer))
		{
			return;
		}
		_alreadyTinted.Add(renderer);
		Material[] materials = renderer.materials;
		foreach (Material val in materials)
		{
			if (!((Object)(object)val == (Object)null))
			{
				if (val.HasProperty("_Color"))
				{
					val.color = color;
				}
				if (val.HasProperty("_BaseColor"))
				{
					val.SetColor("_BaseColor", color);
				}
			}
		}
	}

	private static void SetFieldIfPresent(object target, string fieldName, object value)
	{
		if (target == null)
		{
			return;
		}
		FieldInfo fieldInfo = AccessTools.Field(target.GetType(), fieldName);
		if (fieldInfo == null)
		{
			return;
		}
		try
		{
			fieldInfo.SetValue(target, value);
		}
		catch (Exception ex)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Could not set " + target.GetType().Name + "." + fieldName + ": " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(Character), "Awake")]
internal static class CharacterAwakePatch
{
	private static void Postfix(Character __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && IsVanillaMeleeGoblin(((Component)__instance).gameObject) && (Object)(object)((Component)__instance).GetComponent<GoblinWorkerController>() == (Object)null)
		{
			((Component)__instance).gameObject.AddComponent<GoblinWorkerController>();
		}
	}

	private static bool IsVanillaMeleeGoblin(GameObject gameObject)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return false;
		}
		string text = ((Object)gameObject).name;
		if (text.EndsWith("(Clone)", StringComparison.Ordinal))
		{
			text = text.Substring(0, text.Length - "(Clone)".Length);
		}
		return string.Equals(text, "Goblin", StringComparison.Ordinal);
	}
}
[HarmonyPatch(typeof(Tameable), "GetHoverText")]
internal static class TameableGetHoverTextPatch
{
	private static void Postfix(Tameable __instance, ref string __result)
	{
		if ((Object)(object)__instance == (Object)null)
		{
			return;
		}
		GoblinWorkerController component = ((Component)__instance).GetComponent<GoblinWorkerController>();
		if ((Object)(object)component == (Object)null || !component.IsIntelligent)
		{
			return;
		}
		Character component2 = ((Component)__instance).GetComponent<Character>();
		if ((Object)(object)component2 == (Object)null || string.IsNullOrEmpty(__result))
		{
			return;
		}
		int num = __result.IndexOf('\n');
		string text = ((num >= 0) ? __result.Substring(0, num) : __result);
		string text2 = ((num >= 0) ? __result.Substring(num) : string.Empty);
		int num2 = text.LastIndexOf(" (", StringComparison.Ordinal);
		string text3 = ((num2 >= 0) ? text.Substring(0, num2) : text);
		if (component2.IsTamed())
		{
			__result = text3 + " (Разумный/Приручен)" + text2;
			return;
		}
		string text4 = string.Empty;
		if (num2 >= 0 && text.EndsWith(")", StringComparison.Ordinal))
		{
			int num3 = num2 + 2;
			text4 = text.Substring(num3, text.Length - num3 - 1);
		}
		if (string.IsNullOrEmpty(text4))
		{
			__result = text3 + " (Разумный/Можно задобрить)" + text2;
			return;
		}
		string text5 = text4;
		text5 = text5.Replace("Привязанность: ", "Задобрен на ");
		text5 = text5.Replace("Привязанность ", "Задобрен на ");
		text5 = text5.Replace("Дикий, Голоден", "Можно задобрить");
		text5 = text5.Replace("Дикий", string.Empty).Trim();
		text5 = text5.TrimStart(',', ' ');
		text5 = ((!string.IsNullOrEmpty(text5) && !(text5 == "Голоден")) ? text5.Replace("Голоден", "Можно задобрить") : "Можно задобрить");
		__result = text3 + " (Разумный/" + text5 + ")" + text2;
	}
}
public sealed class GoblinWorkerJob : MonoBehaviour
{
	private const float UpgradeWorkRequirement = 1800f;

	private const int StorageUpgradeCostAmount = 10;

	private const float TreeContentWidth = 760f;

	private const float TreeContentHeight = 690f;

	private const float TreeNodeWidth = 228f;

	private const float TreeNodeHeight = 116f;

	private const float TreeNodeStartY = 140f;

	private const float TreeNodeStepY = 140f;

	private const float LoadedAssignmentValidationDistance = 100f;

	private const string LevelZdoKey = "AutomationByGoblins.Job.Level";

	private const string StoredZdoKey = "AutomationByGoblins.Job.Stored";

	private const string WorkSecondsZdoKey = "AutomationByGoblins.Job.WorkSeconds";

	private const string ProductionSecondsZdoKey = "AutomationByGoblins.Job.ProductionSeconds";

	private const string YieldLevelZdoKey = "AutomationByGoblins.Job.YieldLevel";

	private const string YieldWorkSecondsZdoKey = "AutomationByGoblins.Job.YieldWorkSeconds";

	private const string StorageLevelZdoKey = "AutomationByGoblins.Job.StorageLevel";

	private const string StorageWorkSecondsZdoKey = "AutomationByGoblins.Job.StorageWorkSeconds";

	private const string OfflineEligibleZdoKey = "AutomationByGoblins.Job.OfflineEligible";

	private const string OfflineSessionZdoKey = "AutomationByGoblins.Job.OfflineSession";

	private const string OfflineDayClockZdoKey = "AutomationByGoblins.Job.OfflineDayClock";

	private static readonly float[] ProductionIntervals = new float[5] { 900f, 720f, 600f, 420f, 300f };

	private static readonly string[] UpgradeCostPrefabs = new string[4] { "Flint", "Bronze", "Iron", "Silver" };

	private static readonly string[] UpgradeCostRussianNames = new string[4] { "Кремень", "Бронза", "Железо", "Серебро" };

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

	private static readonly string[] YieldUpgradeCostPrefabs = new string[4] { "FineWood", "SerpentScale", "ElderBark", "DragonTear" };

	private static readonly int[] YieldUpgradeCostAmounts = new int[4] { 10, 5, 20, 5 };

	private static readonly string[] YieldUpgradeCostRussianNames = new string[4] { "Ценная древесина", "Змеиная чешуя", "Древняя кора", "Драконья слеза" };

	private static readonly int[] StorageCapacities = new int[5] { 50, 75, 100, 125, 150 };

	private static readonly string[] StorageUpgradeCostPrefabs = new string[4] { "DeerHide", "BjornHide", "Root", "WolfPelt" };

	private static readonly string[] StorageUpgradeCostRussianNames = new string[4] { "Оленья шкура", "Шкура бьёрна", "Корень", "Волчья шкура" };

	private GoblinWorkerController _controller;

	private GoblinWoodcutterWorkBehaviour _workBehaviour;

	private Character _character;

	private Tameable _tameable;

	private ZNetView _nview;

	private ZDO _workerZdo;

	private int _level;

	private int _yieldLevel;

	private int _storageLevel;

	private int _stored;

	private float _workSeconds;

	private float _yieldWorkSeconds;

	private float _storageWorkSeconds;

	private float _productionSeconds;

	private float _saveTimer;

	private float _remoteSyncTimer;

	private float _offlineCheckpointTimer;

	private float _lastUpdateRealtime;

	private bool _ready;

	private bool _wasActivelyWorking;

	private bool _offlineEligible;

	private bool _ownsWorkerState;

	private bool _wasTamed;

	private bool _knownDead;

	private float _offlineInvalidSeconds;

	private bool _upgradeWindowOpen;

	private bool _storageWindowOpen;

	private Player _windowPlayer;

	private string _uiMessage = string.Empty;

	private float _speedMaterialCountCheckedAt = -1000f;

	private string _speedMaterialCountPrefab = string.Empty;

	private int _speedMaterialCountCached;

	private float _yieldMaterialCountCheckedAt = -1000f;

	private string _yieldMaterialCountPrefab = string.Empty;

	private int _yieldMaterialCountCached;

	private float _storageMaterialCountCheckedAt = -1000f;

	private string _storageMaterialCountPrefab = string.Empty;

	private int _storageMaterialCountCached;

	private bool _cursorStateSaved;

	private bool _previousCursorVisible;

	private CursorLockMode _previousCursorLockMode;

	private Rect _upgradeWindowRect = new Rect(0f, 0f, 820f, 720f);

	private Rect _storageWindowRect = new Rect(0f, 0f, 300f, 260f);

	private Vector2 _upgradeScrollPosition = Vector2.zero;

	private GUIStyle _treeRootStyle;

	private GUIStyle _treeBranchStyle;

	private GUIStyle _treeNodeTitleStyle;

	private GUIStyle _treeNodeDetailStyle;

	internal static GoblinWorkerJob ActiveWindow;

	internal bool IsUsableWorker => _ready && (Object)(object)_controller != (Object)null && (_controller.WorkerType == GoblinWorkerType.Wood || _controller.WorkerType == GoblinWorkerType.Stone) && (Object)(object)_character != (Object)null && _character.IsTamed();

	internal string ProfessionName
	{
		get
		{
			if ((Object)(object)_controller == (Object)null)
			{
				return "Неизвестно";
			}
			return (_controller.WorkerType == GoblinWorkerType.Wood) ? "Лесоруб" : "Каменщик";
		}
	}

	internal string ResourcePrefabName
	{
		get
		{
			if ((Object)(object)_controller == (Object)null)
			{
				return string.Empty;
			}
			return (_controller.WorkerType == GoblinWorkerType.Wood) ? "Wood" : "Stone";
		}
	}

	internal string ResourceRussianName => (ResourcePrefabName == "Wood") ? "Древесина" : "Камень";

	internal int StorageCapacity => StorageCapacities[Mathf.Clamp(_storageLevel, 0, StorageCapacities.Length - 1)];

	private void Start()
	{
		_controller = ((Component)this).GetComponent<GoblinWorkerController>();
		_character = ((Component)this).GetComponent<Character>();
		_tameable = ((Component)this).GetComponent<Tameable>();
		_nview = ((Component)this).GetComponent<ZNetView>();
		((MonoBehaviour)this).StartCoroutine(InitializeWhenReady());
	}

	private IEnumerator InitializeWhenReady()
	{
		while ((Object)(object)_controller == (Object)null || _controller.WorkerType == GoblinWorkerType.Unassigned || (Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
		{
			if ((Object)(object)_controller == (Object)null)
			{
				_controller = ((Component)this).GetComponent<GoblinWorkerController>();
			}
			if ((Object)(object)_character == (Object)null)
			{
				_character = ((Component)this).GetComponent<Character>();
			}
			if ((Object)(object)_tameable == (Object)null)
			{
				_tameable = ((Component)this).GetComponent<Tameable>();
			}
			if ((Object)(object)_nview == (Object)null)
			{
				_nview = ((Component)this).GetComponent<ZNetView>();
			}
			yield return null;
		}
		if (_controller.WorkerType != GoblinWorkerType.Wood && _controller.WorkerType != GoblinWorkerType.Stone)
		{
			((Behaviour)this).enabled = false;
			yield break;
		}
		while ((Object)(object)_tameable == (Object)null)
		{
			_tameable = ((Component)this).GetComponent<Tameable>();
			yield return null;
		}
		GoblinWorkerOfflineProductionManager.EnsureAttached();
		_workerZdo = _nview.GetZDO();
		_ownsWorkerState = _nview.IsOwner();
		_wasTamed = (Object)(object)_character != (Object)null && _character.IsTamed();
		_knownDead = (Object)(object)_character != (Object)null && _character.IsDead();
		LoadStateFromZdo();
		_workBehaviour = ((Component)this).GetComponent<GoblinWoodcutterWorkBehaviour>();
		_ready = true;
		ApplyOfflineCatchUp();
		_lastUpdateRealtime = Time.realtimeSinceStartup;
		if (_ownsWorkerState && _wasTamed && !_knownDead)
		{
			GoblinWorkerOfflineProduction.TrackLoaded(_workerZdo, this, _offlineEligible);
		}
	}

	private void Update()
	{
		if (!_ready || (Object)(object)_controller == (Object)null || !_controller.IsIntelligent || (Object)(object)_character == (Object)null)
		{
			return;
		}
		if ((_upgradeWindowOpen || _storageWindowOpen) && Input.GetKeyDown((KeyCode)27))
		{
			CloseWindows();
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (_lastUpdateRealtime > 0f && realtimeSinceStartup - _lastUpdateRealtime > 2f)
		{
			ApplyOfflineCatchUp();
		}
		_lastUpdateRealtime = realtimeSinceStartup;
		_wasTamed = _character.IsTamed();
		_knownDead = _character.IsDead();
		if (!_wasTamed || _knownDead)
		{
			GoblinWorkerOfflineProduction.Remove(_workerZdo, this);
		}
		else
		{
			if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
			{
				return;
			}
			_workerZdo = _nview.GetZDO();
			_ownsWorkerState = _nview.IsOwner();
			if (!_ownsWorkerState)
			{
				GoblinWorkerOfflineProduction.Remove(_workerZdo, this);
				_remoteSyncTimer += Time.deltaTime;
				if (_remoteSyncTimer >= 1f)
				{
					_remoteSyncTimer = 0f;
					LoadStateFromZdo();
				}
				return;
			}
			GoblinWorkerOfflineProduction.TrackLoaded(_workerZdo, this, _offlineEligible);
			_offlineCheckpointTimer += Time.deltaTime;
			if (_offlineCheckpointTimer >= 10f)
			{
				_offlineCheckpointTimer = 0f;
				SaveOfflineCheckpoint();
			}
			if (!GoblinNightRestJob.IsNightRestActive(_character) && (!((Object)(object)EnvMan.instance != (Object)null) || !EnvMan.IsNight()))
			{
				RefreshOfflineEligibility();
				if (_stored >= StorageCapacity)
				{
					return;
				}
				float deltaTime = Time.deltaTime;
				if ((Object)(object)_workBehaviour == (Object)null)
				{
					_workBehaviour = ((Component)this).GetComponent<GoblinWoodcutterWorkBehaviour>();
				}
				if (!((Object)(object)_workBehaviour != (Object)null) || !_workBehaviour.IsPerformingActiveWork)
				{
					StopActiveWorkTracking();
					return;
				}
				_wasActivelyWorking = true;
				_workSeconds += deltaTime;
				_yieldWorkSeconds += deltaTime;
				_storageWorkSeconds += deltaTime;
				_productionSeconds += deltaTime;
				float num = ProductionIntervals[Mathf.Clamp(_level, 0, ProductionIntervals.Length - 1)];
				if (_productionSeconds >= num)
				{
					_productionSeconds -= num;
					int num2 = ProductionAmounts[Mathf.Clamp(_yieldLevel, 0, ProductionAmounts.Length - 1)];
					_stored = Mathf.Min(StorageCapacity, _stored + num2);
					SaveStateToZdo();
				}
				_saveTimer += deltaTime;
				if (_saveTimer >= 5f)
				{
					_saveTimer = 0f;
					SaveStateToZdo();
				}
			}
			else
			{
				StopActiveWorkTracking();
			}
		}
	}

	private void OnDestroy()
	{
		ZDO val = ResolveWorkerZdo();
		if ((Object)(object)_character != (Object)null)
		{
			_wasTamed = _character.IsTamed();
			_knownDead = _character.IsDead();
		}
		if (_ready && val != null && _ownsWorkerState && _offlineEligible && _stored < StorageCapacity && _wasTamed && !_knownDead)
		{
			GoblinWorkerOfflineProduction.MarkUnloaded(val, this, eligible: true);
		}
		else
		{
			GoblinWorkerOfflineProduction.Remove(val, this);
		}
		if ((Object)(object)ActiveWindow == (Object)(object)this)
		{
			CloseWindows();
		}
	}

	private void LoadStateFromZdo()
	{
		ZDO val = ResolveWorkerZdo();
		if (val != null)
		{
			_level = Mathf.Clamp(val.GetInt("AutomationByGoblins.Job.Level", 0), 0, ProductionIntervals.Length - 1);
			_yieldLevel = Mathf.Clamp(val.GetInt("AutomationByGoblins.Job.YieldLevel", 0), 0, ProductionAmounts.Length - 1);
			_storageLevel = Mathf.Clamp(val.GetInt("AutomationByGoblins.Job.StorageLevel", 0), 0, StorageCapacities.Length - 1);
			_stored = Mathf.Clamp(val.GetInt("AutomationByGoblins.Job.Stored", 0), 0, StorageCapacity);
			_workSeconds = Mathf.Max(0f, val.GetFloat("AutomationByGoblins.Job.WorkSeconds", 0f));
			_yieldWorkSeconds = Mathf.Max(0f, val.GetFloat("AutomationByGoblins.Job.YieldWorkSeconds", 0f));
			_storageWorkSeconds = Mathf.Max(0f, val.GetFloat("AutomationByGoblins.Job.StorageWorkSeconds", 0f));
			_productionSeconds = Mathf.Max(0f, val.GetFloat("AutomationByGoblins.Job.ProductionSeconds", 0f));
			_offlineEligible = val.GetInt("AutomationByGoblins.Job.OfflineEligible", 0) != 0;
		}
	}

	private void SaveStateToZdo()
	{
		ZDO val = ResolveWorkerZdo();
		if (val != null && _ownsWorkerState)
		{
			val.Set("AutomationByGoblins.Job.Level", _level);
			val.Set("AutomationByGoblins.Job.YieldLevel", _yieldLevel);
			val.Set("AutomationByGoblins.Job.StorageLevel", _storageLevel);
			val.Set("AutomationByGoblins.Job.Stored", _stored);
			val.Set("AutomationByGoblins.Job.WorkSeconds", _workSeconds);
			val.Set("AutomationByGoblins.Job.YieldWorkSeconds", _yieldWorkSeconds);
			val.Set("AutomationByGoblins.Job.StorageWorkSeconds", _storageWorkSeconds);
			val.Set("AutomationByGoblins.Job.ProductionSeconds", _productionSeconds);
			val.Set("AutomationByGoblins.Job.OfflineEligible", _offlineEligible ? 1 : 0);
			WriteOfflineCheckpoint(val);
		}
	}

	private void SaveOfflineCheckpoint()
	{
		ZDO val = ResolveWorkerZdo();
		if (val != null && _ownsWorkerState)
		{
			WriteOfflineCheckpoint(val);
		}
	}

	internal static void WriteOfflineCheckpoint(ZDO zdo)
	{
		if (zdo != null && GoblinWorkerOfflineProduction.TryGetDayClock(out var sessionId, out var dayClock))
		{
			zdo.Set("AutomationByGoblins.Job.OfflineSession", sessionId);
			zdo.Set("AutomationByGoblins.Job.OfflineDayClock", dayClock);
		}
	}

	private void ApplyOfflineCatchUp()
	{
		if (!_ready || !_ownsWorkerState || !_wasTamed || _knownDead)
		{
			return;
		}
		ZDO val = ResolveWorkerZdo();
		if (val != null && GoblinWorkerOfflineProduction.TryGetDayClock(out var sessionId, out var dayClock))
		{
			int num = val.GetInt("AutomationByGoblins.Job.OfflineSession", 0);
			float num2 = Mathf.Max(0f, val.GetFloat("AutomationByGoblins.Job.OfflineDayClock", dayClock));
			float num3 = ((num == sessionId) ? Mathf.Max(0f, dayClock - num2) : 0f);
			if (_offlineEligible && num3 > 0.5f)
			{
				int num4 = AdvanceOfflineState(val, num3 * 0.8f);
				LoadStateFromZdo();
				AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Offline catch-up credited " + Mathf.RoundToInt(num3) + " daylight second(s); produced " + num4 + "."));
			}
			val.Set("AutomationByGoblins.Job.OfflineSession", sessionId);
			val.Set("AutomationByGoblins.Job.OfflineDayClock", dayClock);
			_offlineCheckpointTimer = 0f;
		}
	}

	private ZDO ResolveWorkerZdo()
	{
		if ((Object)(object)_nview != (Object)null)
		{
			ZDO zDO = _nview.GetZDO();
			if (zDO != null)
			{
				_workerZdo = zDO;
				_ownsWorkerState = _nview.IsOwner();
			}
		}
		return _workerZdo;
	}

	private void RefreshOfflineEligibility()
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_workBehaviour == (Object)null)
		{
			_workBehaviour = ((Component)this).GetComponent<GoblinWoodcutterWorkBehaviour>();
		}
		bool flag = (Object)(object)_workBehaviour != (Object)null && _workBehaviour.HasOfflineWorkAssignment;
		if (flag)
		{
			_offlineInvalidSeconds = 0f;
		}
		else
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Vector3 val = ((Component)localPlayer).transform.position - ((Component)this).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude > 10000f)
			{
				return;
			}
			_offlineInvalidSeconds += Time.deltaTime;
			if (_offlineInvalidSeconds < 5f)
			{
				return;
			}
		}
		if (_offlineEligible != flag)
		{
			_offlineEligible = flag;
			_saveTimer = 0f;
			SaveStateToZdo();
			AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Worker offline assignment: " + (_offlineEligible ? "ready" : "not ready") + "."));
			GoblinWorkerOfflineProduction.TrackLoaded(_workerZdo, this, _offlineEligible);
		}
	}

	private void StopActiveWorkTracking()
	{
		if (_wasActivelyWorking)
		{
			_wasActivelyWorking = false;
			_saveTimer = 0f;
			SaveStateToZdo();
		}
	}

	internal static int AdvanceOfflineState(ZDO zdo, float productiveSeconds)
	{
		if (zdo == null || productiveSeconds <= 0f || zdo.GetInt("AutomationByGoblins.Job.OfflineEligible", 0) == 0)
		{
			return 0;
		}
		int num = Mathf.Clamp(zdo.GetInt("AutomationByGoblins.Job.Level", 0), 0, ProductionIntervals.Length - 1);
		int num2 = Mathf.Clamp(zdo.GetInt("AutomationByGoblins.Job.YieldLevel", 0), 0, ProductionAmounts.Length - 1);
		int num3 = Mathf.Clamp(zdo.GetInt("AutomationByGoblins.Job.StorageLevel", 0), 0, StorageCapacities.Length - 1);
		int num4 = StorageCapacities[num3];
		int num5 = Mathf.Clamp(zdo.GetInt("AutomationByGoblins.Job.Stored", 0), 0, num4);
		if (num5 >= num4)
		{
			return 0;
		}
		int num6 = num5;
		float num7 = ProductionIntervals[num];
		int num8 = ProductionAmounts[num2];
		float num9 = Mathf.Clamp(zdo.GetFloat("AutomationByGoblins.Job.ProductionSeconds", 0f), 0f, num7);
		int num10 = Mathf.CeilToInt((float)(num4 - num5) / (float)num8);
		float num11 = Mathf.Max(0f, (float)num10 * num7 - num9);
		float num12 = Mathf.Min(productiveSeconds, num11);
		if (num12 <= 0f)
		{
			return 0;
		}
		float num13 = num9 + num12;
		int num14 = Mathf.FloorToInt(num13 / num7);
		if (num14 > 0)
		{
			num5 = Mathf.Min(num4, num5 + num14 * num8);
			num13 -= (float)num14 * num7;
		}
		if (num5 >= num4)
		{
			num13 = 0f;
		}
		zdo.Set("AutomationByGoblins.Job.Stored", num5);
		zdo.Set("AutomationByGoblins.Job.WorkSeconds", Mathf.Max(0f, zdo.GetFloat("AutomationByGoblins.Job.WorkSeconds", 0f)) + num12);
		zdo.Set("AutomationByGoblins.Job.YieldWorkSeconds", Mathf.Max(0f, zdo.GetFloat("AutomationByGoblins.Job.YieldWorkSeconds", 0f)) + num12);
		zdo.Set("AutomationByGoblins.Job.StorageWorkSeconds", Mathf.Max(0f, zdo.GetFloat("AutomationByGoblins.Job.StorageWorkSeconds", 0f)) + num12);
		zdo.Set("AutomationByGoblins.Job.ProductionSeconds", Mathf.Max(0f, num13));
		return num5 - num6;
	}

	internal void OpenUpgradeWindow(Player player)
	{
		//IL_0087: 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)
		if (IsUsableWorker && !((Object)(object)player == (Object)null))
		{
			MakeThisTheActiveWindow();
			_windowPlayer = player;
			_storageWindowOpen = false;
			_upgradeWindowOpen = true;
			_uiMessage = string.Empty;
			_speedMaterialCountCheckedAt = -1000f;
			_speedMaterialCountPrefab = string.Empty;
			_yieldMaterialCountCheckedAt = -1000f;
			_yieldMaterialCountPrefab = string.Empty;
			_storageMaterialCountCheckedAt = -1000f;
			_storageMaterialCountPrefab = string.Empty;
			_upgradeScrollPosition = Vector2.zero;
			CenterWindow(ref _upgradeWindowRect);
			SetCursorForWindow(open: true);
			if (_level >= 0 && _level < UpgradeCostPrefabs.Length)
			{
				string text = UpgradeCostPrefabs[_level];
				int cachedUpgradeMaterialCount = GetCachedUpgradeMaterialCount(player, text, force: true, ref _speedMaterialCountCheckedAt, ref _speedMaterialCountPrefab, ref _speedMaterialCountCached);
				LogInventorySnapshot(player, text, cachedUpgradeMaterialCount);
			}
			if (_yieldLevel >= 0 && _yieldLevel < YieldUpgradeCostPrefabs.Length)
			{
				string text2 = YieldUpgradeCostPrefabs[_yieldLevel];
				int cachedUpgradeMaterialCount2 = GetCachedUpgradeMaterialCount(player, text2, force: true, ref _yieldMaterialCountCheckedAt, ref _yieldMaterialCountPrefab, ref _yieldMaterialCountCached);
				LogInventorySnapshot(player, text2, cachedUpgradeMaterialCount2);
			}
			if (_storageLevel >= 0 && _storageLevel < StorageUpgradeCostPrefabs.Length)
			{
				string text3 = StorageUpgradeCostPrefabs[_storageLevel];
				int cachedUpgradeMaterialCount3 = GetCachedUpgradeMaterialCount(player, text3, force: true, ref _storageMaterialCountCheckedAt, ref _storageMaterialCountPrefab, ref _storageMaterialCountCached);
				LogInventorySnapshot(player, text3, cachedUpgradeMaterialCount3);
			}
		}
	}

	internal void OpenStorageWindow(Player player)
	{
		if (IsUsableWorker && !((Object)(object)player == (Object)null))
		{
			MakeThisTheActiveWindow();
			_windowPlayer = player;
			_upgradeWindowOpen = false;
			_storageWindowOpen = true;
			_uiMessage = string.Empty;
			CenterWindow(ref _storageWindowRect);
			SetCursorForWindow(open: true);
		}
	}

	private void MakeThisTheActiveWindow()
	{
		if ((Object)(object)ActiveWindow != (Object)null && (Object)(object)ActiveWindow != (Object)(object)this)
		{
			ActiveWindow.CloseWindows();
		}
		ActiveWindow = this;
	}

	internal void CloseWindows()
	{
		_upgradeWindowOpen = false;
		_storageWindowOpen = false;
		_windowPlayer = null;
		_uiMessage = string.Empty;
		if ((Object)(object)ActiveWindow == (Object)(object)this)
		{
			ActiveWindow = null;
		}
		SetCursorForWindow(open: false);
	}

	private static void CenterWindow(ref Rect rect)
	{
		((Rect)(ref rect)).x = ((float)Screen.width - ((Rect)(ref rect)).width) * 0.5f;
		((Rect)(ref rect)).y = ((float)Screen.height - ((Rect)(ref rect)).height) * 0.5f;
	}

	private void SetCursorForWindow(bool open)
	{
		//IL_005b: 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)
		if (open)
		{
			if (!_cursorStateSaved)
			{
				_previousCursorVisible = Cursor.visible;
				_previousCursorLockMode = Cursor.lockState;
				_cursorStateSaved = true;
			}
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
		}
		else if (_cursorStateSaved)
		{
			Cursor.visible = _previousCursorVisible;
			Cursor.lockState = _previousCursorLockMode;
			_cursorStateSaved = false;
		}
	}

	private void OnGUI()
	{
		//IL_0026: 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_0041: Expected O, but got Unknown
		//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_0060: 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_007b: Expected O, but got Unknown
		//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)
		if (!((Object)(object)ActiveWindow != (Object)(object)this))
		{
			if (_upgradeWindowOpen)
			{
				_upgradeWindowRect = GUI.Window(((Object)this).GetInstanceID(), _upgradeWindowRect, new WindowFunction(DrawUpgradeWindow), "Разумный фулинг — развитие");
			}
			if (_storageWindowOpen)
			{
				_storageWindowRect = GUI.Window(((Object)this).GetInstanceID() ^ 0x2739, _storageWindowRect, new WindowFunction(DrawStorageWindow), "Хранилище рабочего");
			}
		}
	}

	private void DrawUpgradeWindow(int windowId)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		EnsureTreeStyles();
		GUI.Label(new Rect(18f, 28f, 784f, 22f), "Профессия: " + ProfessionName, _treeBranchStyle);
		GUI.Label(new Rect(18f, 50f, 784f, 22f), "Сейчас: " + ProductionAmounts[_yieldLevel] + " ед. / " + FormatMinutes(ProductionIntervals[_level]) + "    •    Хранилище: " + _stored + "/" + StorageCapacity, _treeNodeDetailStyle);
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(18f, 78f, ((Rect)(ref _upgradeWindowRect)).width - 36f, ((Rect)(ref _upgradeWindowRect)).height - 190f);
		Rect val2 = default(Rect);
		((Rect)(ref val2))..ctor(0f, 0f, 760f, 690f);
		_upgradeScrollPosition = GUI.BeginScrollView(val, _upgradeScrollPosition, val2);
		DrawUpgradeTree();
		GUI.EndScrollView();
		if (!string.IsNullOrEmpty(_uiMessage))
		{
			GUI.Label(new Rect(20f, ((Rect)(ref _upgradeWindowRect)).height - 108f, 780f, 34f), _uiMessage, _treeNodeDetailStyle);
		}
		if (GUI.Button(new Rect(20f, ((Rect)(ref _upgradeWindowRect)).height - 68f, 380f, 30f), "Приказать следовать / стоять"))
		{
			WorkerStorageInteractPatch.RunVanillaTameableInteraction(_tameable, _windowPlayer);
		}
		if (GUI.Button(new Rect(620f, ((Rect)(ref _upgradeWindowRect)).height - 68f, 180f, 30f), "Закрыть"))
		{
			CloseWindows();
		}
		GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _upgradeWindowRect)).width, 24f));
	}

	private void EnsureTreeStyles()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Expected O, but got Unknown
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Expected O, but got Unknown
		if (_treeRootStyle == null)
		{
			_treeRootStyle = new GUIStyle(GUI.skin.box);
			_treeRootStyle.alignment = (TextAnchor)4;
			_treeRootStyle.fontSize = 15;
			_treeRootStyle.fontStyle = (FontStyle)1;
			_treeRootStyle.wordWrap = true;
			_treeBranchStyle = new GUIStyle(GUI.skin.label);
			_treeBranchStyle.alignment = (TextAnchor)4;
			_treeBranchStyle.fontSize = 14;
			_treeBranchStyle.fontStyle = (FontStyle)1;
			_treeNodeTitleStyle = new GUIStyle(GUI.skin.label);
			_treeNodeTitleStyle.alignment = (TextAnchor)1;
			_treeNodeTitleStyle.fontSize = 12;
			_treeNodeTitleStyle.fontStyle = (FontStyle)1;
			_treeNodeTitleStyle.wordWrap = true;
			_treeNodeDetailStyle = new GUIStyle(GUI.skin.label);
			_treeNodeDetailStyle.alignment = (TextAnchor)1;
			_treeNodeDetailStyle.fontSize = 11;
			_treeNodeDetailStyle.wordWrap = true;
		}
	}

	private void DrawUpgradeTree()
	{
		//IL_001c: 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_0036: 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_0063: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(220f, 8f, 320f, 76f);
		Color color = GUI.color;
		GUI.color = new Color(0.55f, 0.72f, 0.38f, 1f);
		GUI.Box(val, "РАЗУМНЫЙ ФУЛИНГ\n" + ProfessionName + "\nВсе ветки развиваются независимо", _treeRootStyle);
		GUI.color = color;
		DrawTreeConnections(10f, 266f, 522f, val);
		GUI.Label(new Rect(10f, 106f, 228f, 26f), "СКОРОСТЬ", _treeBranchStyle);
		GUI.Label(new Rect(266f, 106f, 228f, 26f), "КОЛИЧЕСТВО", _treeBranchStyle);
		GUI.Label(new Rect(522f, 106f, 228f, 26f), "ХРАНИЛИЩЕ", _treeBranchStyle);
		for (int i = 0; i < 4; i++)
		{
			float num = 140f + (float)i * 140f;
			DrawSpeedTreeNode(i, new Rect(10f, num, 228f, 116f));
			DrawYieldTreeNode(i, new Rect(266f, num, 228f, 116f));
			DrawStorageTreeNode(i, new Rect(522f, num, 228f, 116f));
		}
	}

	private void DrawTreeConnections(float leftX, float middleX, float rightX, Rect rootRect)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: 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_00aa: 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_0132: Unknown result type (might be due to invalid IL or missing references)
		float num = leftX + 114f;
		float num2 = middleX + 114f;
		float num3 = rightX + 114f;
		float num4 = ((Rect)(ref rootRect)).x + ((Rect)(ref rootRect)).width * 0.5f;
		DrawTreeLine(new Rect(num4 - 1f, ((Rect)(ref rootRect)).yMax, 2f, 96f - ((Rect)(ref rootRect)).yMax));
		DrawTreeLine(new Rect(num, 96f, num3 - num, 2f));
		DrawTreeLine(new Rect(num - 1f, 96f, 2f, 44f));
		DrawTreeLine(new Rect(num2 - 1f, 96f, 2f, 44f));
		DrawTreeLine(new Rect(num3 - 1f, 96f, 2f, 44f));
		for (int i = 0; i < 3; i++)
		{
			float num5 = i switch
			{
				1 => num2, 
				0 => num, 
				_ => num3, 
			};
			for (int j = 0; j < 3; j++)
			{
				float num6 = 140f + (float)j * 140f + 116f;
				float num7 = 140f + (float)(j + 1) * 140f;
				DrawTreeLine(new Rect(num5 - 1f, num6, 2f, num7 - num6));
			}
		}
	}

	private static void DrawTreeLine(Rect rect)
	{
		//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_001b: 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_0032: Unknown result type (might be due to invalid IL or missing references)
		Color color = GUI.color;
		GUI.color = new Color(0.68f, 0.58f, 0.32f, 0.9f);
		GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture);
		GUI.color = color;
	}

	private void DrawSpeedTreeNode(int upgradeIndex, Rect rect)
	{
		//IL_006d: 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_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		int num = upgradeIndex + 1;
		bool flag = _level > upgradeIndex;
		bool flag2 = _level < upgradeIndex;
		float num2 = Mathf.Max(0f, 1800f - _workSeconds);
		int materialCount = 0;
		if (!flag && !flag2)
		{
			materialCount = GetCachedUpgradeMaterialCount(_windowPlayer, UpgradeCostPrefabs[upgradeIndex], force: false, ref _speedMaterialCountCheckedAt, ref _speedMaterialCountPrefab, ref _speedMaterialCountCached);
		}
		DrawTreeNodeBackground(rect, flag, flag2, num2);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 10f, 20f), "Уровень " + (num + 1) + " • " + FormatMinutes(ProductionIntervals[num]), _treeNodeTitleStyle);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 6f, ((Rect)(ref rect)).y + 28f, ((Rect)(ref rect)).width - 12f, 51f), BuildTreeNodeDetails(flag, flag2, num2, materialCount, 5, UpgradeCostRussianNames[upgradeIndex]), _treeNodeDetailStyle);
		if (!flag && !flag2)
		{
			bool enabled = GUI.enabled;
			GUI.enabled = num2 <= 0f;
			if (GUI.Button(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 83f, ((Rect)(ref rect)).width - 24f, 26f), "Улучшить"))
			{
				TrySpeedUpgrade(_windowPlayer);
			}
			GUI.enabled = enabled;
		}
	}

	private void DrawYieldTreeNode(int upgradeIndex, Rect rect)
	{
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
		int num = upgradeIndex + 1;
		int costAmount = YieldUpgradeCostAmounts[upgradeIndex];
		bool flag = _yieldLevel > upgradeIndex;
		bool flag2 = _yieldLevel < upgradeIndex;
		float num2 = Mathf.Max(0f, 1800f - _yieldWorkSeconds);
		int materialCount = 0;
		if (!flag && !flag2)
		{
			materialCount = GetCachedUpgradeMaterialCount(_windowPlayer, YieldUpgradeCostPrefabs[upgradeIndex], force: false, ref _yieldMaterialCountCheckedAt, ref _yieldMaterialCountPrefab, ref _yieldMaterialCountCached);
		}
		DrawTreeNodeBackground(rect, flag, flag2, num2);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 10f, 20f), "Уровень " + (num + 1) + " • " + ProductionAmounts[num] + " ед.", _treeNodeTitleStyle);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 6f, ((Rect)(ref rect)).y + 28f, ((Rect)(ref rect)).width - 12f, 51f), BuildTreeNodeDetails(flag, flag2, num2, materialCount, costAmount, YieldUpgradeCostRussianNames[upgradeIndex]), _treeNodeDetailStyle);
		if (!flag && !flag2)
		{
			bool enabled = GUI.enabled;
			GUI.enabled = num2 <= 0f;
			if (GUI.Button(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 83f, ((Rect)(ref rect)).width - 24f, 26f), "Улучшить"))
			{
				TryYieldUpgrade(_windowPlayer);
			}
			GUI.enabled = enabled;
		}
	}

	private void DrawStorageTreeNode(int upgradeIndex, Rect rect)
	{
		//IL_006d: 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_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		int num = upgradeIndex + 1;
		bool flag = _storageLevel > upgradeIndex;
		bool flag2 = _storageLevel < upgradeIndex;
		float num2 = Mathf.Max(0f, 1800f - _storageWorkSeconds);
		int materialCount = 0;
		if (!flag && !flag2)
		{
			materialCount = GetCachedUpgradeMaterialCount(_windowPlayer, StorageUpgradeCostPrefabs[upgradeIndex], force: false, ref _storageMaterialCountCheckedAt, ref _storageMaterialCountPrefab, ref _storageMaterialCountCached);
		}
		DrawTreeNodeBackground(rect, flag, flag2, num2);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 10f, 20f), "Уровень " + (num + 1) + " • " + StorageCapacities[num] + " мест", _treeNodeTitleStyle);
		GUI.Label(new Rect(((Rect)(ref rect)).x + 6f, ((Rect)(ref rect)).y + 28f, ((Rect)(ref rect)).width - 12f, 51f), BuildTreeNodeDetails(flag, flag2, num2, materialCount, 10, StorageUpgradeCostRussianNames[upgradeIndex]), _treeNodeDetailStyle);
		if (!flag && !flag2)
		{
			bool enabled = GUI.enabled;
			GUI.enabled = num2 <= 0f;
			if (GUI.Button(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 83f, ((Rect)(ref rect)).width - 24f, 26f), "Улучшить"))
			{
				TryStorageUpgrade(_windowPlayer);
			}
			GUI.enabled = enabled;
		}
	}

	private static string BuildTreeNodeDetails(bool completed, bool locked, float remaining, int materialCount, int costAmount, string materialName)
	{
		if (completed)
		{
			return "✓ Улучшено\n" + costAmount + " × " + materialName;
		}
		if (locked)
		{
			return "Заблокировано\n" + costAmount + " × " + materialName;
		}
		string text = ((remaining > 0f) ? ("Работать: " + FormatClock(remaining)) : "Готово к улучшению");
		return text + "\nМатериалы: " + materialCount + "/" + costAmount + " × " + materialName;
	}

	private static void DrawTreeNodeBackground(Rect rect, bool completed, bool locked, float remaining)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		Color color = default(Color);
		if (completed)
		{
			((Color)(ref color))..ctor(0.3f, 0.62f, 0.32f, 0.95f);
		}
		else if (locked)
		{
			((Color)(ref color))..ctor(0.28f, 0.28f, 0.28f, 0.88f);
		}
		else if (remaining <= 0f)
		{
			((Color)(ref color))..ctor(0.78f, 0.6f, 0.18f, 0.98f);
		}
		else
		{
			((Color)(ref color))..ctor(0.34f, 0.46f, 0.68f, 0.95f);
		}
		Color color2 = GUI.color;
		GUI.color = color;
		GUI.Box(rect, GUIContent.none);
		GUI.color = color2;
	}

	private void DrawStorageWindow(int windowId)
	{
		GUILayout.Space(10f);
		GUILayout.Label("Профессия: " + ProfessionName, Array.Empty<GUILayoutOption>());
		GUILayout.Label("Один слот, максимум " + StorageCapacity + " ед.", Array.Empty<GUILayoutOption>());
		GUILayout.Label("За цикл: " + ProductionAmounts[_yieldLevel] + " ед.", Array.Empty<GUILayoutOption>());
		GUILayout.Space(12f);
		GUILayout.Box(ResourceRussianName + "\n" + _stored + " / " + StorageCapacity, (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.ExpandWidth(true),
			GUILayout.Height(72f)
		});
		if (GUILayout.Button("Забрать ресурсы", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
		{
			int num = CollectResources(_windowPlayer);
			_uiMessage = ((num > 0) ? ("Получено: " + num + " × " + ResourceRussianName) : ((_stored <= 0) ? "Хранилище пусто" : "В инвентаре нет места"));
		}
		if (!string.IsNullOrEmpty(_uiMessage))
		{
			GUILayout.Label(_uiMessage, Array.Empty<GUILayoutOption>());
		}
		GUILayout.FlexibleSpace();
		if (GUILayout.Button("Закрыть", Array.Empty<GUILayoutOption>()))
		{
			CloseWindows();
		}
		GUI.DragWindow();
	}

	private void TrySpeedUpgrade(Player player)
	{
		if ((Object)(object)player == (Object)null || _level >= ProductionIntervals.Length - 1)
		{
			return;
		}
		if (_workSeconds < 1800f)
		{
			_uiMessage = "Сначала гоблин должен отработать 30 минут на текущем уровне.";
			return;
		}
		if (!EnsureOwnership())
		{
			_uiMessage = "Не удалось получить доступ к состоянию этого гоблина.";
			return;
		}
		string text = UpgradeCostPrefabs[_level];
		int num = (_speedMaterialCountCached = CountPrefabInInventory(player, text));
		_speedMaterialCountPrefab = text;
		_speedMaterialCountCheckedAt = Time.unscaledTime;
		LogInventorySnapshot(player, text, num);
		if (num < 5)
		{
			_uiMessage = "Недостаточно: найдено " + num + "/5 × " + UpgradeCostRussianNames[_level] + ".";
		}
		else if (!RemovePrefabFromInventory(player, text, 5))
		{
			_uiMessage = "Не удалось списать материалы улучшения.";
			LogInventorySnapshot(player, text, CountPrefabInInventory(player, text));
		}
		else
		{
			_level++;
			_workSeconds = 0f;
			_productionSeconds = 0f;
			SaveStateToZdo();
			_speedMaterialCountCheckedAt = -1000f;
			_speedMaterialCountPrefab = string.Empty;
			_uiMessage = "Скорость улучшена. Новое время добычи: " + FormatMinutes(ProductionIntervals[_level]) + ".";
		}
	}

	private void TryYieldUpgrade(Player player)
	{
		if ((Object)(object)player == (Object)null || _yieldLevel >= ProductionAmounts.Length - 1)
		{
			return;
		}
		if (_yieldWorkSeconds < 1800f)
		{
			_uiMessage = "Сначала гоблин должен отработать 30 минут для улучшения количества.";
			return;
		}
		if (!EnsureOwnership())
		{
			_uiMessage = "Не удалось получить доступ к состоянию этого гоблина.";
			return;
		}
		string text = YieldUpgradeCostPrefabs[_yieldLevel];
		int num = YieldUpgradeCostAmounts[_yieldLevel];
		int num2 = (_yieldMaterialCountCached = CountPrefabInInventory(player, text));
		_yieldMaterialCountPrefab = text;
		_yieldMaterialCountCheckedAt = Time.unscaledTime;
		LogInventorySnapshot(player, text, num2);
		if (num2 < num)
		{
			_uiMessage = "Недостаточно: найдено " + num2 + "/" + num + " × " + YieldUpgradeCostRussianNames[_yieldLevel] + ".";
		}
		else if (!RemovePrefabFromInventory(player, text, num))
		{
			_uiMessage = "Не удалось списать материалы улучшения.";
			LogInventorySnapshot(player, text, CountPrefabInInventory(player, text));
		}
		else
		{
			_yieldLevel++;
			_yieldWorkSeconds = 0f;
			SaveStateToZdo();
			_yieldMaterialCountCheckedAt = -1000f;
			_yieldMaterialCountPrefab = string.Empty;
			_uiMessage = "Количество улучшено. Теперь за цикл добывается " + ProductionAmounts[_yieldLevel] + " ед.";
		}
	}

	private void TryStorageUpgrade(Player player)
	{
		if ((Object)(object)player == (Object)null || _storageLevel >= StorageCapacities.Length - 1)
		{
			return;
		}
		if (_storageWorkSeconds < 1800f)
		{
			_uiMessage = "Сначала гоблин должен отработать 30 минут для улучшения хранилища.";
			return;
		}
		if (!EnsureOwnership())
		{
			_uiMessage = "Не удалось получить доступ к состоянию этого гоблина.";
			return;
		}
		string text = StorageUpgradeCostPrefabs[_storageLevel];
		int num = (_storageMaterialCountCached = CountPrefabInInventory(player, text));
		_storageMaterialCountPrefab = text;
		_storageMaterialCountCheckedAt = Time.unscaledTime;
		LogInventorySnapshot(player, text, num);
		if (num < 10)
		{
			_uiMessage = "Недостаточно: найдено " + num + "/" + 10 + " × " + StorageUpgradeCostRussianNames[_storageLevel] + ".";
		}
		else if (!RemovePrefabFromInventory(player, text, 10))
		{
			_uiMessage = "Не удалось списать материалы улучшения.";
			LogInventorySnapshot(player, text, CountPrefabInInventory(player, text));
		}
		else
		{
			_storageLevel++;
			_storageWorkSeconds = 0f;
			SaveStateToZdo();
			_storageMaterialCountCheckedAt = -1000f;
			_storageMaterialCountPrefab = string.Empty;
			_uiMessage = "Хранилище улучшено. Новая вместимость: " + StorageCapacity + " ед.";
		}
	}

	private int GetCachedUpgradeMaterialCount(Player player, string prefabName, bool force, ref float checkedAt, ref string cachedPrefab, ref int cachedCount)
	{
		if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(prefabName))
		{
			return 0;
		}
		if (force || !string.Equals(cachedPrefab, prefabName, StringComparison.Ordinal) || Time.unscaledTime - checkedAt >= 0.5f)
		{
			cachedCount = CountPrefabInInventory(player, prefabName);
			cachedPrefab = prefabName;
			checkedAt = Time.unscaledTime;
		}
		return cachedCount;
	}

	private int CollectResources(Player player)
	{
		if ((Object)(object)player == (Object)null || _stored <= 0 || string.IsNullOrEmpty(ResourcePrefabName))
		{
			return 0;
		}
		if (!EnsureOwnership())
		{
			return 0;
		}
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			return 0;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ResourcePrefabName);
		if ((Object)(object)itemPrefab == (Object)null)
		{
			return 0;
		}
		ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null)
		{
			return 0;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "AddItem", new Type[1] { typeof(ItemData) }, (Type[])null);
		if (inventory == null || methodInfo == null)
		{
			return 0;
		}
		int num = 0;
		int stored = _stored;
		for (int i = 0; i < stored; i++)
		{
			ItemData val = component.m_itemData.Clone();
			val.m_stack = 1;
			bool flag;
			try
			{
				object obj = methodInfo.Invoke(inventory, new object[1] { val });
				flag = ((methodInfo.ReturnType == typeof(bool)) ? (obj != null && (bool)obj) : (methodInfo.ReturnType == typeof(void) || obj != null));
			}
			catch
			{
				flag = false;
			}
			if (!flag)
			{
				break;
			}
			num++;
			_stored--;
		}
		if (num > 0)
		{
			SaveStateToZdo();
		}
		return num;
	}

	private bool EnsureOwnership()
	{
		if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
		{
			return false;
		}
		if (_nview.IsOwner())
		{
			return true;
		}
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ZNetView), "ClaimOwnership", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(_nview, null);
			}
		}
		catch
		{
			return false;
		}
		if (_nview.IsOwner())
		{
			LoadStateFromZdo();
			return true;
		}
		return false;
	}

	private static int CountPrefabInInventory(Player player, string prefabName)
	{
		if ((Object)(object)player == (Object)null)
		{
			return 0;
		}
		try
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			string text = "$item_" + prefabName.ToLowerInvariant();
			int num = 0;
			int num2 = 0;
			try
			{
				num = inventory.CountItems(prefabName, -1, true);
			}
			catch
			{
			}
			try
			{
				num2 = inventory.CountItems(text, -1, true);
			}
			catch
			{
			}
			return num + num2;
		}
		catch
		{
			return 0;
		}
	}

	private static bool RemovePrefabFromInventory(Player player, string prefabName, int amount)
	{
		if ((Object)(object)player == (Object)null || amount <= 0)
		{
			return false;
		}
		try
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			string text = "$item_" + prefabName.ToLowerInvariant();
			int num = 0;
			int num2 = 0;
			try
			{
				num = inventory.CountItems(prefabName, -1, true);
			}
			catch
			{
			}
			try
			{
				num2 = inventory.CountItems(text, -1, true);
			}
			catch
			{
			}
			if (num + num2 < amount)
			{
				return false;
			}
			int num3 = amount;
			try
			{
				int num4 = Math.Min(num, num3);
				if (num4 > 0)
				{
					inventory.RemoveItem(prefabName, num4, -1, true);
					num3 -= num4;
				}
			}
			catch
			{
			}
			if (num3 > 0)
			{
				try
				{
					int val = inventory.CountItems(text, -1, true);
					int num5 = Math.Min(val, num3);
					if (num5 > 0)
					{
						inventory.RemoveItem(text, num5, -1, true);
						num3 -= num5;
					}
				}
				catch
				{
				}
			}
			return num3 <= 0;
		}
		catch
		{
			return false;
		}
	}

	private static void LogInventorySnapshot(Player player, string wantedPrefab, int counted)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		try
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				string text = "$item_" + wantedPrefab.ToLowerInvariant();
				int num = 0;
				int num2 = 0;
				try
				{
					num = inventory.CountItems(wantedPrefab, -1, true);
				}
				catch
				{
				}
				try
				{
					num2 = inventory.CountItems(text, -1, true);
				}
				catch
				{
				}
				AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Upgrade inventory check: Wanted=" + wantedPrefab + ", Counted=" + counted + ", CountItems(\"" + wantedPrefab + "\")=" + num + ", CountItems(\"" + text + "\")=" + num2));
			}
		}
		catch
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Upgrade inventory check failed for " + wantedPrefab + "."));
		}
	}

	private static string FormatMinutes(float seconds)
	{
		return Mathf.RoundToInt(seconds / 60f) + " мин.";
	}

	private static string FormatClock(float seconds)
	{
		int num = Mathf.Max(0, Mathf.CeilToInt(seconds));
		int num2 = num / 60;
		int num3 = num % 60;
		return num2.ToString("00") + ":" + num3.ToString("00");
	}
}
internal static class GoblinWorkerOfflineProduction
{
	private sealed class WorkerRecord
	{
		internal ZDO Zdo;

		internal GoblinWorkerJob LoadedJob;

		internal bool Eligible;

		internal bool BackgroundAnnounced;
	}

	internal const float ProductiveWorkFraction = 0.8f;

	private static readonly Dictionary<ZDO, WorkerRecord> Workers = new Dictionary<ZDO, WorkerRecord>();

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

	private static bool _managerAvailable;

	private static int _sessionId;

	private static float _dayClock;

	internal static bool HasWorkers => Workers.Count != 0;

	internal static void BeginSession()
	{
		Workers.Clear();
		InvalidWorkers.Clear();
		_sessionId = (int)DateTime.UtcNow.Ticks;
		if (_sessionId == 0)
		{
			_sessionId = 1;
		}
		_dayClock = 0f;
		_managerAvailable = true;
		AutomationByGoblinsPlugin.ModLog.LogDebug((object)"Background production clock started.");
	}

	internal static void EndSession()
	{
		_managerAvailable = false;
		Workers.Clear();
		InvalidWorkers.Clear();
		_sessionId = 0;
		_dayClock = 0f;
	}

	internal static bool TryGetDayClock(out int sessionId, out float dayClock)
	{
		sessionId = _sessionId;
		dayClock = _dayClock;
		return _managerAvailable && sessionId != 0;
	}

	internal static void AdvanceDayClock(float elapsedSeconds, bool isDaylight)
	{
		if (_managerAvailable && isDaylight && !(elapsedSeconds <= 0f))
		{
			_dayClock += elapsedSeconds;
		}
	}

	internal static void TrackLoaded(ZDO zdo, GoblinWorkerJob job, bool eligible)
	{
		if (_managerAvailable && zdo != null && !((Object)(object)job == (Object)null))
		{
			if (!Workers.TryGetValue(zdo, out var value))
			{
				value = new WorkerRecord
				{
					Zdo = zdo
				};
				Workers.Add(zdo, value);
			}
			value.LoadedJob = job;
			value.Eligible = eligible;
			value.BackgroundAnnounced = false;
		}
	}

	internal static void MarkUnloaded(ZDO zdo, GoblinWorkerJob job, bool eligible)
	{
		if (!_managerAvailable || zdo == null)
		{
			return;
		}
		if (!eligible)
		{
			Workers.Remove(zdo);
			return;
		}
		if (!Workers.TryGetValue(zdo, out var value))
		{
			value = new WorkerRecord
			{
				Zdo = zdo
			};
			Workers.Add(zdo, value);
		}
		if ((Object)(object)value.LoadedJob == (Object)null || (Object)(object)value.LoadedJob == (Object)(object)job)
		{
			value.LoadedJob = null;
		}
		value.Eligible = true;
	}

	internal static void Remove(ZDO zdo, GoblinWorkerJob job)
	{
		if (zdo != null && (!Workers.TryGetValue(zdo, out var value) || (Object)(object)value.LoadedJob == (Object)null || (Object)(object)value.LoadedJob == (Object)(object)job))
		{
			Workers.Remove(zdo);
		}
	}

	internal static void Advance(float elapsedDaySeconds)
	{
		if (elapsedDaySeconds <= 0f || Workers.Count == 0)
		{
			return;
		}
		float productiveSeconds = elapsedDaySeconds * 0.8f;
		InvalidWorkers.Clear();
		foreach (KeyValuePair<ZDO, WorkerRecord> worker in Workers)
		{
			WorkerRecord value = worker.Value;
			if (value == null || value.Zdo == null || !value.Eligible)
			{
				InvalidWorkers.Add(worker.Key);
			}
			else
			{
				if ((Object)(object)value.LoadedJob != (Object)null)
				{
					continue;
				}
				try
				{
					if (!value.BackgroundAnnounced)
					{
						value.BackgroundAnnounced = true;
						AutomationByGoblinsPlugin.ModLog.LogDebug((object)"Worker entered background production.");
					}
					int num = GoblinWorkerJob.AdvanceOfflineState(value.Zdo, productiveSeconds);
					GoblinWorkerJob.WriteOfflineCheckpoint(value.Zdo);
					if (num > 0)
					{
						AutomationByGoblinsPlugin.ModLog.LogDebug((object)("Background worker produced " + num + " resource(s)."));
					}
				}
				catch
				{
					InvalidWorkers.Add(worker.Key);
				}
			}
		}
		for (int i = 0; i < InvalidWorkers.Count; i++)
		{
			Workers.Remove(InvalidWorkers[i]);
		}
		InvalidWorkers.Clear();
	}
}
internal sealed class GoblinWorkerOfflineProductionManager : MonoBehaviour
{
	private const float TickInterval = 10f;

	private static readonly WaitForSeconds TickDelay = new WaitForSeconds(10f);

	internal static GoblinWorkerOfflineProductionManager Instance;

	internal static void EnsureAttached()
	{
		if (!((Object)(object)Instance != (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null))
		{
			GoblinWorkerOfflineProductionManager component = ((Component)ZNetScene.instance).GetComponent<GoblinWorkerOfflineProductionManager>();
			if ((Object)(object)component != (Object)null)
			{
				Instance = component;
			}
			else
			{
				((Component)ZNetScene.instance).gameObject.AddComponent<GoblinWorkerOfflineProductionManager>();
			}
		}
	}

	private void Awake()
	{
		if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
		{
			Object.Destroy((Object)(object)this);
			return;
		}
		Instance = this;
		GoblinWorkerOfflineProduction.BeginSession();
	}

	private IEnumerator Start()
	{
		float lastTickTime = Time.time;
		while (true)
		{
			yield return TickDelay;
			float now = Time.time;
			float elapsedSeconds = Mathf.Max(0f, now - lastTickTime);
			lastTickTime = now;
			bool isDaylight = (Object)(object)EnvMan.instance != (Object)null && !EnvMan.IsNight();
			GoblinWorkerOfflineProduction.AdvanceDayClock(elapsedSeconds, isDaylight);
			if (GoblinWorkerOfflineProduction.HasWorkers && isDaylight)
			{
				GoblinWorkerOfflineProduction.Advance(elapsedSeconds);
			}
		}
	}

	private void OnDestroy()
	{
		if (!((Object)(object)Instance != (Object)(object)this))
		{
			Instance = null;
			GoblinWorkerOfflineProduction.EndSession();
		}
	}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
internal static class GoblinWorkerOfflineProductionAttachPatch
{
	private static void Postfix()
	{
		GoblinWorkerOfflineProductionManager.EnsureAttached();
	}
}
[HarmonyPatch(typeof(GoblinWorkerController), "Start")]
internal static class GoblinWorkerJobAttachPatch
{
	private static void Postfix(GoblinWorkerController __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).GetComponent<GoblinWorkerJob>() != (Object)null))
		{
			((Component)__instance).gameObject.AddComponent<GoblinWorkerJob>();
		}
	}
}
[HarmonyPatch(typeof(Tameable), "GetHoverText")]
internal static class WorkerHoverCapturePatch
{
	internal static GoblinWorkerJob HoveredWorker;

	internal static int HoveredFrame = -100;

	private static void Postfix(Tameable __instance)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			GoblinWorkerJob component = ((Component)__instance).GetComponent<GoblinWorkerJob>();
			if (!((Object)(object)component == (Object)null) && component.IsUsableWorker)
			{
				HoveredWorker = component;
				HoveredFrame = Time.frameCount;
				GathererHoverCapturePatch.HoveredGatherer = null;
				GathererHoverCapturePatch.HoveredFrame = -100;
			}
		}
	}
}
[HarmonyPatch(typeof(Player), "Update")]
internal static class WorkerMenuHotkeyPatch
{
	private static void Postfix(Player __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)) && Input.GetKeyDown((KeyCode)121))
		{
			GoblinWorkerJob hoveredWorker = WorkerHoverCapturePatch.HoveredWorker;
			if (!((Object)(object)hoveredWorker == (Object)null) && hoveredWorker.IsUsableWorker && Time.frameCount - WorkerHoverCapturePatch.HoveredFrame <= 5)
			{
				hoveredWorker.OpenUpgradeWindow(__instance);
			}
		}
	}
}
[HarmonyPatch(typeof(InventoryGui), "IsVisible")]
internal static class WorkerWindowVanillaInputBlockPatch
{
	private static void Postfix(ref bool __result)
	{
		if ((Object)(object)GoblinWorkerJob.ActiveWindow != (Object)null)
		{
			__result = true;
		}
	}
}
[HarmonyPatch(typeof(Tameable), "Interact")]
internal static class WorkerStorageInteractPatch
{
	private static bool _allowVanillaInteraction;

	internal static bool IsVanillaInteractionAllowed => _allowVanillaInteraction;

	private static bool Prefix(Tameable __instance, Humanoid user, bool hold, bool alt, ref bool __result)
	{
		if (_allowVanillaInteraction || (Object)(object)__instance == (Object)null)
		{
			return true;
		}
		GoblinWorkerJob component = ((Component)__instance).GetComponent<GoblinWorkerJob>();
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)component == (Object)null || !component.IsUsableWorker || (Object)(object)val == (Object)null)
		{
			return true;
		}
		if (alt)
		{
			return true;
		}
		if (hold)
		{
			__result = false;
			return false;
		}
		component.OpenStorageWindow(val);
		__result = true;
		return false;
	}

	internal static void RunVanillaTameableInteraction(Tameable tameable, Player player)
	{
		if ((Object)(object)tameable == (Object)null || (Object)(object)player == (Object)null)
		{
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(Tameable), "Interact", new Type[3]
		{
			typeof(Humanoid),
			typeof(bool),
			typeof(bool)
		}, (Type[])null);
		if (methodInfo == null)
		{
			return;
		}
		try
		{
			_allowVanillaInteraction = true;
			methodInfo.Invoke(tameable, new object[3] { player, false, false });
		}
		finally
		{
			_allowVanillaInteraction = false;
		}
	}
}
public sealed class GoblinWorkerEquipment : MonoBehaviour
{
	private const string WoodToolPrefabName = "AxeFlint";

	private const string StoneToolPrefabName = "PickaxeAntler";

	private GoblinWorkerController _controller;

	private Humanoid _humanoid;

	private VisEquipment _visEquipment;

	private ItemData _toolItem;

	private string _toolPrefabName = string.Empty;

	private bool _isProfessionWorker;

	private bool _forceEmptyHands;

	private bool _actualToolEquipped;

	private static readonly MethodInfo[] LeftItemVisualMethods = ResolveLeftItemVisualMethods();

	internal bool ShouldForceToolVisual => _isProfessionWorker && (_forceEmptyHands || !string.IsNullOrEmpty(_toolPrefabName));

	internal string ForcedRightItemPrefab => _forceEmptyHands ? string.Empty : _toolPrefabName;

	internal bool ShouldForceEmptyHands => _isProfessionWorker && _forceEmptyHands;

	internal static IEnumerable<MethodBase> GetLeftItemVisualMethods()
	{
		int i = 0;
		while (i < LeftItemVisualMethods.Length)
		{
			yield return LeftItemVisualMethods[i];
			int num = i + 1;
			i = num;
		}
	}

	private void Start()
	{
		_controller = ((Component)this).GetComponent<GoblinWorkerController>();
		_humanoid = ((Component)this).GetComponent<Humanoid>();
		_visEquipment = ((Component)this).GetComponent<VisEquipment>();
		if ((Object)(object)_visEquipment == (Object)null)
		{
			_visEquipment = ((Component)this).GetComponentInChildren<VisEquipment>(true);
		}
		((MonoBehaviour)this).StartCoroutine(InitializeEquipment());
	}

	private IEnumerator InitializeEquipment()
	{
		while ((Object)(object)_controller != (Object)null && _controller.WorkerType == GoblinWorkerType.Unassigned)
		{
			yield return null;
		}
		if ((Object)(object)_controller == (Object)null)
		{
			yield break;
		}
		if (_controller.WorkerType == GoblinWorkerType.Wood)
		{
			_toolPrefabName = "AxeFlint";
		}
		else
		{
			if (_controller.WorkerType != GoblinWorkerType.Stone)
			{
				if (_controller.WorkerType == GoblinWorkerType.Gatherer)
				{
					_isProfessionWorker = true;
					_forceEmptyHands = true;
					ApplyToolVisual();
				}
				yield break;
			}
			_toolPrefabName = "PickaxeAntler";
		}
		_isProfessionWorker = true;
		GameObject toolPrefab = null;
		while (true)
		{
			int num;
			if ((Object)(object)this != (Object)null)
			{
				if (!((Object)(object)ObjectDB.instance == (Object)null))
				{
					GameObject itemPrefab;
					toolPrefab = (itemPrefab = ObjectDB.instance.GetItemPrefab(_toolPrefabName));
					num = (((Object)(object)itemPrefab == (Object)null) ? 1 : 0);
				}
				else
				{
					num = 1;
				}
			}
			else
			{
				num = 0;
			}
			if (num == 0)
			{
				break;
			}
			yield return (object)new WaitForSeconds(0.25f);
		}
		if ((Object)(object)this == (Object)null || (Object)(object)toolPrefab == (Object)null)
		{
			yield break;
		}
		ItemDrop itemDrop = toolPrefab.GetComponent<ItemDrop>();
		if ((Object)(object)itemDrop == (Object)null || itemDrop.m_itemData == null)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Worker equipment: " + _toolPrefabName + " has no ItemDrop data."));
			yield break;
		}
		_toolItem = itemDrop.m_itemData.Clone();
		_actualToolEquipped = TryEquipActualTool(_toolItem);
		ApplyToolVisual();
		if (_actualToolEquipped)
		{
			AutomationByGoblinsPlugin.ModLog.LogDebug((object)(_controller.WorkerType.ToString() + " Goblin equipped " + _toolPrefabName + " as its combat weapon."));
		}
		else
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)(_controller.WorkerType.ToString() + " Goblin could not equip " + _toolPrefabName + " as ItemData; using its visual with vanilla Goblin combat fallback."));
		}
	}

	private bool TryEquipActualTool(ItemData toolItem)
	{
		if ((Object)(object)_humanoid == (Object)null || toolItem == null)
		{
			return false;
		}
		Inventory val = null;
		try
		{
			val = _humanoid.GetInventory();
		}
		catch
		{
			return false;
		}
		if (val == null)
		{
			return false;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "AddItem", new Type[1] { typeof(ItemData) }, (Type[])null);
		if (methodInfo == null)
		{
			return false;
		}
		try
		{
			object obj2 = methodInfo.Invoke(val, new object[1] { toolItem });
			if (methodInfo.ReturnType == typeof(bool) && (obj2 == null || !(bool)obj2))
			{
				return false;
			}
		}
		catch (Exception exception)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Worker equipment: could not add " + _toolPrefabName + " to Goblin inventory: " + GetInnermostMessage(exception)));
			return false;
		}
		bool flag = InvokeEquipItem(_humanoid, toolItem);
		if (!flag)
		{
			RemoveInventoryItem(val, toolItem);
		}
		return flag;
	}

	private static void RemoveInventoryItem(Inventory inventory, ItemData item)
	{
		if (inventory == null || item == null)
		{
			return;
		}
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[1] { typeof(ItemData) }, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(inventory, new object[1] { item });
			}
		}
		catch
		{
		}
	}

	private static bool InvokeEquipItem(Humanoid humanoid, ItemData item)
	{
		try
		{
			MethodInfo[] methods = typeof(Humanoid).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!string.Equals(methodInfo.Name, "EquipItem", StringComparison.Ordinal))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 0 || parameters[0].ParameterType != typeof(ItemData))
				{
					continue;
				}
				object[] array = new object[parameters.Length];
				array[0] = item;
				bool flag = true;
				for (int j = 1; j < parameters.Length; j++)
				{
					Type parameterType = parameters[j].ParameterType;
					if (parameters[j].HasDefaultValue && parameters[j].DefaultValue != DBNull.Value && parameters[j].DefaultValue != Type.Missing)
					{
						array[j] = parameters[j].DefaultValue;
						continue;
					}
					if (parameterType == typeof(bool))
					{
						array[j] = false;
						continue;
					}
					if (parameterType.IsValueType)
					{
						array[j] = Activator.CreateInstance(parameterType);
						continue;
					}
					flag = false;
					break;
				}
				if (flag)
				{
					object obj = methodInfo.Invoke(humanoid, array);
					if (methodInfo.ReturnType == typeof(bool))
					{
						return obj != null && (bool)obj;
					}
					return true;
				}
			}
		}
		catch (Exception exception)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Worker equipment: Humanoid.EquipItem failed: " + GetInnermostMessage(exception)));
		}
		return false;
	}

	private void ApplyToolVisual()
	{
		if (!_isProfessionWorker || (!_forceEmptyHands && string.IsNullOrEmpty(_toolPrefabName)))
		{
			return;
		}
		if ((Object)(object)_visEquipment == (Object)null)
		{
			_visEquipment = ((Component)this).GetComponent<VisEquipment>();
			if ((Object)(object)_visEquipment == (Object)null)
			{
				_visEquipment = ((Component)this).GetComponentInChildren<VisEquipment>(true);
			}
		}
		if ((Object)(object)_visEquipment == (Object)null)
		{
			return;
		}
		try
		{
			_visEquipment.SetRightItem(_forceEmptyHands ? string.Empty : _toolPrefabName);
			if (_forceEmptyHands)
			{
				ClearLeftHandVisual();
			}
		}
		catch (Exception exception)
		{
			AutomationByGoblinsPlugin.ModLog.LogWarning((object)("Worker equipment: VisEquipment.SetRightItem failed for " + _toolPrefabName + ": " + GetInnermostMessage(exception)));
			_isProfessionWorker = false;
		}
	}

	private void ClearLeftHandVisual()
	{
		for (int i = 0; i < LeftItemVisualMethods.Length; i++)
		{
			MethodInfo methodInfo = LeftItemVisualMethods[i];
			ParameterInfo[] parameters = methodInfo.GetParameters();
			object[] array = new object[parameters.Length];
			array[0] = string.Empty;
			bool flag = true;
			for (int j = 1; j < parameters.Length; j++)
			{
				Type parameterType = parameters[j].ParameterType;
				if (parameterType == typeof(int))
				{
					array[j] = 0;
					continue;
				}
				if (parameterType == typeof(bool))
				{
					array[j] = false;
					continue;
				}
				if (parameterType.IsValueType)
				{
					array[j] = Activator.CreateInstance(parameterType);
					continue;
				}
				flag = false;
				break;
			}
			if (flag)
			{
				try
				{
					methodInfo.Invoke(_visEquipment, array);
					break;
				}
				catch
				{
				}
			}
		}
	}

	private static MethodInfo[] ResolveLeftItemVisualMethods()
	{
		List<MethodInfo> list = new List<MethodInfo>();
		MethodInfo[] methods = typeof(VisEquipment).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (string.Equals(methodInfo.Name, "SetLeftItem", StringComparison.Ordinal))
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length != 0 && parameters[0].ParameterType == typeof(string))
				{
					list.Add(methodInfo);
				}
			}
		}
		return list.ToArray();
	}

	private static string GetInnermostMessage(Exception exception)
	{
		Exception ex = exception;
		while (ex.InnerException != null)
		{
			ex = ex.InnerException;
		}
		return ex.Message;
	}
}
[HarmonyPatch(typeof(GoblinWorkerController), "Start")]
internal static class GoblinWorkerEquipmentAttachPatch
{
	private static void Postfix(GoblinWorkerController __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).GetComponent<GoblinWorkerEquipment>() != (Object)null))
		{
			((Component)__instance).gameObject.AddComponent<GoblinWorkerEquipment>();
		}
	}
}
[HarmonyPatch(typeof(VisEquipment), "SetRightItem", new Type[] { typeof(string) })]
internal static class GoblinWorkerRightItemVisualPatch
{
	private static void Prefix(VisEquipment __instance, ref string __0)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			GoblinWorkerEquipment componentInParent = ((Component)__instance).GetComponentInParent<GoblinWorkerEquipment>();
			if (!((Object)(object)componentInParent == (Object)null) && componentInParent.ShouldForceToolVisual)
			{
				__0 = componentInParent.ForcedRightItemPrefab;
			}
		}
	}
}
[HarmonyPatch]
internal static class GoblinGathererLeftItemVisualPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		return GoblinWorkerEquipment.GetLeftItemVisualMethods();
	}

	private static void Prefix(VisEquipment __instance, object[] __args)
	{
		if ((Object)(object)__instance == (Object)null || __args == null || __args.Length == 0)
		{
			return;
		}
		GoblinWorkerEquipment componentInParent = ((Component)__instance).GetComponentInParent<GoblinWorkerEquipment>();
		if (!((Object)(object)componentInParent == (Object)null) && componentInParent.ShouldForceEmptyHands)
		{
			__args[0] = string.Empty;
			if (__args.Length > 1 && __args[1] is int)
			{
				__args[1] = 0;
			}
		}
	}
}
internal enum GoblinWardZoneType
{
	None,
	Woodcutter,
	Stoneworker,
	Gatherer,
	Sleeping
}
public sealed class GoblinWardWorkZone : MonoBehaviour
{
	private const string ZoneTypeZdoKey = "AutomationByGoblins.Ward.ZoneType";

	private const float DefaultWardRadius = 32f;

	private const float ZoneTypeCacheDuration = 0.5f;

	private static readonly List<GoblinWardWorkZone> Instances = new List<GoblinWardWorkZone>();

	private static readonly FieldInfo WardRadiusField = AccessTools.Field(typeof(PrivateArea), "m_radius");

	private static readonly MethodInfo ClaimOwnershipMethod = AccessTools.Method(typeof(ZNetView), "ClaimOwnership", (Type[])null, (Type[])null);

	private PrivateArea _ward;

	private ZNetView _nview;

	private Vector3 _center;

	private float _radius = 32f;

	private GoblinWardZoneType _cachedZoneType = GoblinWardZoneType.None;

	private float _nextZoneTypeRefreshTime;

	private bool _menuOpen;

	private Player _menuPlayer;

	private string _uiMessage = string.Empty;

	private Rect _windowRect = new Rect(0f, 0f, 430f, 405f);

	private bool _cursorStateSaved;

	private bool _previousCursorVisible;

	private CursorLockMode _previousCursorLockMode;

	internal static GoblinWardWorkZone ActiveMenu;

	internal Vector3 Center => _center;

	internal float Radius => _radius;

	private void Awake()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		_ward = ((Component)this).GetComponent<PrivateArea>();
		_nview = ((Component)this).GetComponent<ZNetView>();
		_center = ((Component)this).transform.position;
		_radius = ReadWardRadius(_ward);
		Instances.Add(this);
		((Behaviour)this).enabled = false;
	}

	private static float ReadWardRadius(PrivateArea ward)
	{
		if ((Object)(object)ward == (Object)null)
		{
			return 32f;
		}
		try
		{
			if (WardRadiusField != null)
			{
				object value = WardRadiusField.GetValue(ward);
				if (value is float)
				{
					return Mathf.Max(1f, (float)value);
				}
			}
		}
		catch
		{
		}
		return 32f;
	}

	private void OnDestroy()
	{
		Instances.Remove(this);
		if ((Object)(object)ActiveMenu == (Object)(object)this)
		{
			CloseMenu();
		}
	}

	internal GoblinWardZoneType GetZoneType()
	{
		float time = Time.time;
		if (time < _nextZoneTypeRefreshTime)
		{
			return _cachedZoneType;
		}
		_nextZoneTypeRefreshTime = time + 0.5f;
		if ((Object)(object)_nview == (Object)null)
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
		}
		if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
		{
			_cachedZoneType = GoblinWardZoneType.None;
			return _cachedZoneType;
		}
		int num = _nview.GetZDO().GetInt("AutomationByGoblins.Ward.ZoneType", 0);
		if (num < 0 || num > 4)
		{
			_cachedZoneType = GoblinWardZoneType.None;
			return _cachedZoneType;
		}
		_cachedZoneType = (GoblinWardZoneType)num;
		return _cachedZoneType;
	}

	internal bool Contains(Vector3 worldPosition)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = worldPosition - Center;
		val.y = 0f;
		float radius = Radius;
		return ((Vector3)(ref val)).sqrMagnitude <= radius * radius;
	}

	internal static GoblinWardWorkZone FindNearest(Vector3 from, GoblinWardZoneType requestedType)
	{
		//IL_0057: 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_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		GoblinWardWorkZone result = null;
		float num = float.MaxValue;
		for (int num2 = Instances.Count - 1; num2 >= 0; num2--)
		{
			GoblinWardWorkZone goblinWardWorkZone = Instances[num2];
			if ((Object)(object)goblinWardWorkZone == (Object)null)
			{
				Instances.RemoveAt(num2);
			}
			else if (goblinWardWorkZone.GetZoneType() == requestedType)
			{
				Vector3 val = goblinWardWorkZone.Center - from;
				val.y = 0f;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					result = goblinWardWorkZone;
				}
			}
		}
		return result;
	}

	internal void OpenMenu(Player player)
	{
		if (!((Object)(object)player == (Object)null))
		{
			if ((Object)(object)ActiveMenu != (Object)null && (Object)(object)ActiveMenu != (Object)(object)this)
			{
				ActiveMenu.CloseMenu();
			}
			if ((Object)(object)GoblinWorkerJob.ActiveWindow != (Object)null)
			{
				GoblinWorkerJob.ActiveWindow.CloseWindows();
			}
			if ((Object)(object)GoblinGathererJob.ActiveWindow != (Object)null)
			{
				GoblinGathererJob.ActiveWindow.CloseWindows();
			}
			ActiveMenu = this;
			_menuPlayer = player;
			_menuOpen = true;
			((Behaviour)this).enabled = true;
			_uiMessage = string.Empty;
			((Rect)(ref _windowRect)).x = ((float)Screen.width - ((Rect)(ref _windowRect)).width) * 0.5f;
			((Rect)(ref _windowRect)).y = ((float)Screen.height - ((Rect)(ref _windowRect)).height) * 0.5f;
			SetCursorForMenu(open: true);
		}
	}

	internal void CloseMenu()
	{
		_menuOpen = false;
		_menuPlayer = null;
		_uiMessage = string.Empty;
		if ((Object)(object)ActiveMenu == (Object)(object)this)
		{
			ActiveMenu = null;
		}
		SetCursorForMenu(open: false);
		((Behaviour)this).enabled = false;
	}

	private void OnGUI()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Expected O, but got Unknown
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		if (_menuOpen && !((Object)(object)ActiveMenu != (Object)(object)this))
		{
			_windowRect = GUI.Window(((Object)this).GetInstanceID() ^ 0x4A41, _windowRect, new WindowFunction(DrawWindow), "Рабочая зона фулингов");
		}
	}

	private void DrawWindow(int windowId)
	{
		GUILayout.Space(8f);
		GUILayout.Label("Радиус Ward: " + Mathf.RoundToInt(Radius) + " м", Array.Empty<GUILayoutOption>());
		GUILayout.Label("Текущее назначение: " + GetZoneTypeRussianName(GetZoneType()), Array.Empty<GUILayoutOption>());
		GUILayout.Space(12f);
		GUILayout.Label("Выберите назначение этой зоны:", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		if (GUILayout.Button("Назначить зону лесорубам", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
		{
			SetZoneTypeFromMenu(GoblinWardZoneType.Woodcutter);
		}
		if (GUILayout.Button("Назначить зону каменщикам", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
		{
			SetZoneTypeFromMenu(GoblinWardZoneType.Stoneworker);
		}
		if (GUILayout.Button("Назначить зону собирателям", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
		{
			SetZoneTypeFromMenu(GoblinWardZoneType.Gatherer);
		}
		if (GUILayout.Button("Назначить спальную зону", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
		{
			SetZoneTypeFromMenu(GoblinWardZoneType.Sleeping);
		}
		if (GUILayout.Button("Убрать назначение", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
		{
			SetZoneTypeFromMenu(GoblinWardZoneType.None);
		}
		if (!string.IsNullOrEmpty(_uiMessage))
		{
			GUILayout.Space(8f);
			GUILayout.Label(_uiMessage, Array.Empty<GUILayoutOption>());
		}
		GUILayout.FlexibleSpace();
		if (GUILayout.Button("Закрыть", Array.Empty<GUILayoutOption>()))
		{
			CloseMenu();
		}
		GUI.DragWindow();
	}

	private void SetZoneTypeFromMenu(GoblinWardZoneType zoneType)
	{
		if (!((Object)(object)_menuPlayer == (Object)null))
		{
			if (!EnsureOwnership())
			{
				_uiMessage = "Не удалось получить доступ к Ward.";
				return;
			}
			_nview.GetZDO().Set("AutomationByGoblins.Ward.ZoneType", (int)zoneType);
			_cachedZoneType = zoneType;
			_nextZoneTypeRefreshTime = Time.time + 0.5f;
			_uiMessage = ((zoneType == GoblinWardZoneType.None) ? "Назначение рабочей зоны удалено." : ("Зона назначена: " + GetZoneTypeRussianName(zoneType) + "."));
		}
	}

	private bool EnsureOwnership()
	{
		if ((Object)(object)_nview == (Object)null)
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
		}
		if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null)
		{
			return false;
		}
		if (_nview.IsOwner())
		{
			return true;
		}
		try
		{
			if (ClaimOwnershipMethod != null)
			{
				ClaimOwnershipMethod.Invoke(_nview, null);
			}
		}
		catch
		{
			return false;
		}
		return _nview.IsOwner();
	}

	private void SetCursorForMenu(bool open)
	{
		//IL_005b: 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)
		if (open)
		{
			if (!_cursorStateSaved)
			{
				_previousCursorVisible = Cursor.visible;
				_previousCursorLockMode = Cursor.lockState;
				_cursorStateSaved = true;
			}
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
		}
		else if (_cursorStateSaved)
		{
			Cursor.visible = _previousCursorVisible;
			Cursor.lockState = _previousCursorLockMode;
			_cursorStateSaved = false;
		}
	}

	internal static string GetZoneTypeRussianName(GoblinWardZoneType zoneType)
	{
		return zoneType switch
		{
			GoblinWardZoneType.Woodcutter => "Лесоруб", 
			GoblinWardZoneType.Stoneworker => "Каменщик", 
			GoblinWardZoneType.Gatherer => "Собиратель", 
			GoblinWardZoneType.Sleeping => "Спальная зона", 
			_ => "Не назначена", 
		};
	}
}
public sealed class GoblinBeechWorkTarget : MonoBehaviour
{
	private const float DefaultTrunkRadius = 0.45f;

	private const float MinimumTrunkRadius = 0.2f;

	private const float MaximumTrunkRadius = 0.8f;

	private static readonly List<GoblinBeechWorkTarget> Instances = new List<GoblinBeechWorkTarget>();

	private TreeBase _tree;

	private Vector3 _position;

	private float _trunkRadius = 0.45f;

	private int _registryIndex = -1;

	internal TreeBase Tree => _tree;

	internal Vector3 Position => _position;

	internal float TrunkRadius => _trunkRadius;

	private void Awake()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		_tree = ((Component)this).GetComponent<TreeBase>();
		_position = ((Component)this).transform.position;
		_trunkRadius = ResolveTrunkRadius();
		_registryIndex = Instances.Count;
		Instances.Add(this);
	}

	private float ResolveTrunkRadius()
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		CapsuleCollider[] componentsInChildren = ((Component)this).GetComponentsInChildren<CapsuleCollider>(true);
		float num = 0f;
		foreach (CapsuleCollider val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)null) && val.direction == 1)
			{
				Vector3 lossyScale = ((Component)val).transform.lossyScale;
				float num2 = Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
				num = Mathf.Max(num, val.radius * num2);
			}
		}
		return (num > 0f) ? Mathf.Clamp(num, 0.2f, 0.8f) : 0.45f;
	}

	private void OnDestroy()
	{
		Unregister();
	}

	private void Unregister()
	{
		int registryIndex = _registryIndex;
		if (registryIndex >= 0 && registryIndex < Instances.Count && Instances[registryIndex] == this)
		{
			RemoveAtSwap(registryIndex);
			return;
		}
		registryIndex = Instances.IndexOf(this);
		if (registryIndex >= 0)
		{
			RemoveAtSwap(registryIndex);
		}
		_registryIndex = -1;
	}

	private static void RemoveAtSwap(int index)
	{
		int num = Instances.Count - 1;
		GoblinBeechWorkTarget goblinBeechWorkTarget = Instances[index];
		if (index != num)
		{
			GoblinBeechWorkTarget goblinBeechWorkTarget2 = Instances[num];
			Instances[index] = goblinBeechWorkTarget2;
			if (goblinBeechWorkTarget2 != null)
			{
				goblinBeechWorkTarget2._registryIndex = index;
			}
		}
		Instances.RemoveAt(num);
		if (goblinBeechWorkTarget != null)
		{
			goblinBeechWorkTarget._registryIndex = -1;
		}
	}

	internal static GoblinBeechWorkTarget FindNearest(Vector3 workerPosition, GoblinWardWorkZone ward)
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ward == (Object)null)
		{
			return null;
		}
		GoblinBeechWorkTarget result = null;
		float num = float.MaxValue;
		for (int num2 = Instances.Count - 1; num2 >= 0; num2--)
		{
			GoblinBeechWorkTarget goblinBeechWorkTarget = Instances[num2];
			if ((Object)(object)goblinBeechWorkTarget == (Object)null || (Object)(object)goblinBeechWorkTarget._tree == (Object)null)
			{
				RemoveAtSwap(num2);
			}
			else
			{
				Vector3 position = goblinBeechWorkTarget._position;
				if (ward.Contains(position))
				{
					Vector3 val = position - workerPosition;
					val.y = 0f;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = goblinBeechWorkTarget;
					}
				}
			}
		}
		return result;
	}

	internal static bool IsBeech(TreeBase tree)
	{
		if ((Object)(object)tree == (Object)null || (Object)(object)((Component)tree).gameObject == (Object)null)
		{
			return false;
		}
		string name = ((Object)((Component)tree).gameObject).name;
		return name.StartsWith("Beech", StringComparison.OrdinalIgnoreCase);
	}
}
public sealed class GoblinStonePileWorkTarget : MonoBehaviour
{
	private static readonly List<GoblinStonePileWorkTarget> Instances = new List<GoblinStonePileWorkTarget>();

	private Piece _piece;

	private Vector3 _position;

	private int _registryIndex = -1;

	internal Piece TargetPiece => _piece;

	internal Vector3 Position => _position;

	private void Awake()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		_piece = ((Component)this).GetComponent<Piece>();
		_position = ((Component)this).transform.position;
		_registryIndex = Instances.Count;
		Instances.Add(this);
	}

	private void OnDestroy()
	{
		Unregister();
	}

	private void Unregister()
	{
		int registryIndex = _registryIndex;
		if (registryIndex >= 0 && registryIndex < Instances.Count && Instances[registryIndex] == this)
		{
			RemoveAtSwap(registryIndex);
			return;
		}
		registryIndex = Instances.IndexOf(this);
		if (registryIndex >= 0)
		{
			RemoveAtSwap(registryIndex);
		}
		_registryIndex = -1;
	}

	private static void RemoveAtSwap(int index)
	{
		int num = Instances.Count - 1;
		GoblinStonePileWorkTarget goblinStonePileWorkTarget = Instances[index];
		if (index != num)
		{
			GoblinStonePileWorkTarget goblinStonePileWorkTarget2 = Instances[num];
			Instances[index] = goblinStonePileWorkTarget2;
			if