Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AutomationByGoblins v0.7.0
AutomationByGoblins.dll
Decompiled 21 hours ago
The result has been truncated due to the large size, download it to view full contents!
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