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 FulingWorkforce v1.0.0
plugins\FulingWorkforce.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.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FulingWorkforce")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("FulingWorkforce")] [assembly: AssemblyTitle("FulingWorkforce")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FulingWorkforce { public sealed class BartenderPostMarker : MonoBehaviour, TextReceiver { private const string NameKey = "fw_bartender_post_name"; private const string StatusKey = "fw_bartender_status"; private const string TransfersKey = "fw_bartender_transfers"; private static readonly HashSet<BartenderPostMarker> Active = new HashSet<BartenderPostMarker>(); private int _selectedSlot; private int _fermenterCursor; private bool _editName; private bool _queuedEdit; private float _lastInteraction; private float _nextWork; private object _textUser; internal string PostName => R.ZString(((Component)this).gameObject, "fw_bartender_post_name"); internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_bartender_status", "Inactive — no Bartender assigned through a Bartender Bunk"); private void OnEnable() { Active.Add(this); } private void OnDisable() { Active.Remove(this); } private void Start() { _nextWork = Time.time + Random.Range(0.5f, 1.5f); } private void LateUpdate() { if (!_queuedEdit) { return; } _queuedEdit = false; try { string text = (_editName ? "Bartender post name" : (SlotLabel(_selectedSlot) + " chest name")); if ((Object)(object)TextInput.instance != (Object)null) { TextInput.instance.RequestText((TextReceiver)(object)this, text, 32); } else { Plugin.Message(_textUser, "The text-entry window is unavailable."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not open Bartender Post editor: " + ex.Message)); Plugin.Message(_textUser, "The Bartender Post editor could not be opened."); } } private void Update() { if (Time.time < _nextWork) { return; } _nextWork = Time.time + Mathf.Max(0.5f, Plugin.BartenderWorkInterval.Value); if (!R.IsOwner(((Component)this).gameObject)) { return; } if ((Object)(object)JobStationMarker.FindBartenderBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null) { SetStatus("Inactive — no Bartender assigned through a Bartender Bunk"); return; } string problem; NamedContainerMarker namedContainerMarker = Storage(0, out problem); if ((Object)(object)namedContainerMarker == (Object)null) { SetStatus("Paused — mead base chest " + problem); return; } string problem2; NamedContainerMarker namedContainerMarker2 = Storage(1, out problem2); if ((Object)(object)namedContainerMarker2 == (Object)null) { SetStatus("Paused — finished mead chest " + problem2); return; } List<Component> fermenters = FindFermenters(); string action2; string waiting; string action3; string action4; if (TryCollectOutput(fermenters, namedContainerMarker2, out var action)) { RecordTransfer(action); } else if (BartenderServingFrame.TryStockNext(this, namedContainerMarker2, out action2, out waiting)) { RecordTransfer(action2); } else if (TryTapReady(fermenters, namedContainerMarker2, out action3)) { RecordTransfer(action3); } else if (TryLoadEmpty(fermenters, namedContainerMarker, out action4)) { RecordTransfer(action4); } else { SetIdleStatus(fermenters, namedContainerMarker, namedContainerMarker2, waiting); } } internal bool Interact(object user, bool hold, bool alt, bool control) { if (Time.time - _lastInteraction < 0.45f) { return true; } _lastInteraction = Time.time; _textUser = user; if (control && !alt) { _selectedSlot = (_selectedSlot + 1) % 2; Plugin.Message(user, "Selected " + SlotLabel(_selectedSlot) + " chest."); return true; } if (hold) { return true; } _editName = !alt; _queuedEdit = true; return true; } public string GetText() { if (!_editName) { return R.ZString(((Component)this).gameObject, SlotKey(_selectedSlot, "name")); } return PostName; } public void SetText(string text) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) string text2 = PatrolPointMarker.CleanLabel(text); R.ClaimOwnership(((Component)this).gameObject); if (_editName) { R.ZSet(((Component)this).gameObject, "fw_bartender_post_name", text2); R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2); Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Bartender Post name cleared." : ("Bartender Post named " + text2 + ".")); return; } string key = SlotKey(_selectedSlot, "name"); string key2 = SlotKey(_selectedSlot, "pos"); string key3 = SlotKey(_selectedSlot, "linked"); R.ZSet(((Component)this).gameObject, key, text2); bool ambiguous; NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.BartenderStorageRange.Value, out ambiguous); if ((Object)(object)namedContainerMarker != (Object)null) { R.ZSet(((Component)this).gameObject, key2, ((Component)namedContainerMarker).transform.position); R.ZSet(((Component)this).gameObject, key3, true); Plugin.Message(_textUser, SlotLabel(_selectedSlot) + " chest linked to " + namedContainerMarker.StorageName + "."); } else { R.ZSet(((Component)this).gameObject, key3, false); string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? (SlotLabel(_selectedSlot) + " chest link cleared.") : $"No storage named '{text2}' is within {Plugin.BartenderStorageRange.Value:0}m.")); Plugin.Message(_textUser, text3); } } internal string HoverText() { string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>")); string text2 = ""; for (int i = 0; i < 2; i++) { string text3 = ((i == _selectedSlot) ? "<color=#FFD966>▶</color> " : " "); text2 = text2 + text3 + SlotLabel(i) + ": " + StorageDisplay(i) + "\n"; } int num = BartenderServingFrame.CountFor(this); return "Fuling Bartender Post\nName: " + text + "\n" + text2 + string.Format("Serving frames: {0}\nStatus: {1}\nTransfers: {2}\n", num, CurrentStatus, R.ZInt(((Component)this).gameObject, "fw_bartender_transfers")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Ctrl + E</b></color>] Select chest\n[<color=yellow><b>Shift + E</b></color>] Set selected chest"; } internal static BartenderPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) ambiguous = false; if (string.IsNullOrEmpty(name)) { return null; } BartenderPostMarker bartenderPostMarker = null; float num = radius * radius; foreach (BartenderPostMarker item in Active) { if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase)) { continue; } Vector3 val = ((Component)item).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { if ((Object)(object)bartenderPostMarker != (Object)null && (Object)(object)bartenderPostMarker != (Object)(object)item) { ambiguous = true; return null; } bartenderPostMarker = item; } } return bartenderPostMarker; } internal static BartenderPostMarker FindAtPosition(Vector3 position) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) BartenderPostMarker result = null; float num = 2.25f; foreach (BartenderPostMarker item in Active) { if (Usable(item)) { Vector3 val = ((Component)item).transform.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = item; } } } return result; } internal static BartenderPostMarker FindNearest(Vector3 position, float radius) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) BartenderPostMarker result = null; float num = radius * radius; foreach (BartenderPostMarker item in Active) { if (Usable(item)) { Vector3 val = ((Component)item).transform.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = item; } } } return result; } internal bool Matches(string name, Vector3 position) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(name) && string.Equals(PostName, name, StringComparison.OrdinalIgnoreCase)) { return Vector3.Distance(((Component)this).transform.position, position) <= 2f; } Vector3 val = ((Component)this).transform.position - position; return ((Vector3)(ref val)).sqrMagnitude <= 2.25f; } internal bool IsFermenterProduct(int itemHash) { foreach (Component item in FindFermenters()) { foreach (object item2 in Conversions(item)) { GameObject val = AsGameObject(R.Get(item2, "m_to")); if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == itemHash) { return true; } } } return false; } private NamedContainerMarker Storage(int slot, out string problem) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) string text = R.ZString(((Component)this).gameObject, SlotKey(slot, "name")); if (string.IsNullOrEmpty(text)) { problem = "is not configured"; return null; } bool flag = R.ZBool(((Component)this).gameObject, SlotKey(slot, "linked")); Vector3 val = R.ZVector(((Component)this).gameObject, SlotKey(slot, "pos"), ((Component)this).transform.position); NamedContainerMarker namedContainerMarker = (flag ? NamedContainerMarker.FindAtPosition(val) : null); bool ambiguous = false; if ((Object)(object)namedContainerMarker == (Object)null) { namedContainerMarker = NamedContainerMarker.FindNamed(text, ((Component)this).transform.position, Plugin.BartenderStorageRange.Value, out ambiguous); } if (ambiguous) { problem = "name is ambiguous"; return null; } if ((Object)(object)namedContainerMarker == (Object)null) { problem = $"was not found within {Plugin.BartenderStorageRange.Value:0}m"; return null; } if (Vector3.Distance(((Component)this).transform.position, ((Component)namedContainerMarker).transform.position) > Plugin.BartenderStorageRange.Value) { problem = $"is more than {Plugin.BartenderStorageRange.Value:0}m away"; return null; } if (R.IsOwner(((Component)this).gameObject)) { if (flag && string.Equals(text, namedContainerMarker.StorageName, StringComparison.Ordinal)) { Vector3 val2 = val - ((Component)namedContainerMarker).transform.position; if (!(((Vector3)(ref val2)).sqrMagnitude > 0.01f)) { goto IL_01b6; } } R.ZSet(((Component)this).gameObject, SlotKey(slot, "name"), namedContainerMarker.StorageName); R.ZSet(((Component)this).gameObject, SlotKey(slot, "pos"), ((Component)namedContainerMarker).transform.position); R.ZSet(((Component)this).gameObject, SlotKey(slot, "linked"), true); } goto IL_01b6; IL_01b6: problem = null; return namedContainerMarker; } private string StorageDisplay(int slot) { string text = R.ZString(((Component)this).gameObject, SlotKey(slot, "name")); string problem; NamedContainerMarker namedContainerMarker = Storage(slot, out problem); if ((Object)(object)namedContainerMarker != (Object)null) { return "<color=#7CFF7C>" + namedContainerMarker.StorageName + "</color>"; } if (string.IsNullOrEmpty(text)) { return "<color=#aaaaaa>Not configured</color>"; } return "<color=#FF7777>" + text + " — " + problem + "</color>"; } private List<Component> FindFermenters() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) List<Component> list = new List<Component>(); HashSet<int> hashSet = new HashSet<int>(); foreach (Component item in R.FindNear("Fermenter", ((Component)this).transform.position, Plugin.BartenderRadius.Value)) { if (!((Object)(object)item == (Object)null) && item.gameObject.activeInHierarchy && hashSet.Add(((Object)item).GetInstanceID())) { list.Add(item); } } return list; } private bool TryCollectOutput(List<Component> fermenters, NamedContainerMarker output, out string action) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) action = null; object inventory = output.Inventory; if (inventory == null) { return false; } for (int i = 0; i < fermenters.Count; i++) { Component fermenter = fermenters[(_fermenterCursor + i) % fermenters.Count]; foreach (Component item in R.FindNear("ItemDrop", OutputPosition(fermenter), 4f)) { if ((Object)(object)item == (Object)null || !IsOutput(fermenter, item.gameObject)) { continue; } object obj = R.Call(R.Get(item, "m_itemData"), "Clone"); if (obj != null && CanAdd(inventory, obj)) { R.ClaimOwnership(((Component)output).gameObject); object obj2 = R.Call(inventory, "AddItem", obj); if (obj2 is bool && (bool)obj2) { R.ClaimOwnership(item.gameObject); R.DestroyNetworked(item.gameObject); _fermenterCursor = (_fermenterCursor + i + 1) % fermenters.Count; action = "Active — collected " + ItemDisplayName(obj); return true; } } } } return false; } private bool TryTapReady(List<Component> fermenters, NamedContainerMarker output, out string action) { action = null; object inventory = output.Inventory; for (int i = 0; i < fermenters.Count; i++) { int num = (_fermenterCursor + i) % fermenters.Count; Component val = fermenters[num]; if (Number(R.Call(val, "GetStatus")) != 3) { continue; } object obj = CurrentConversion(val); if (obj != null) { object obj2 = R.Call(ItemData(AsGameObject(R.Get(obj, "m_to"))), "Clone"); if (obj2 != null) { int num2 = Math.Max(1, Number(R.Get(obj, "m_producedItems"))); R.Set(obj2, "m_stack", num2); if (!CanAdd(inventory, obj2)) { continue; } } } if (InvokeRpc(val, "RPC_Tap")) { _fermenterCursor = (num + 1) % fermenters.Count; action = "Active — tapped a finished fermenter"; return true; } } return false; } private bool TryLoadEmpty(List<Component> fermenters, NamedContainerMarker input, out string action) { action = null; object inventory = input.Inventory; if (inventory == null) { return false; } bool flag = default(bool); for (int i = 0; i < fermenters.Count; i++) { int num = (_fermenterCursor + i) % fermenters.Count; Component val = fermenters[num]; if (Number(R.Call(val, "GetStatus")) != 0 || IsExposed(val)) { continue; } object obj = FindAcceptedInput(inventory, val); if (obj != null) { object obj2 = R.Get(obj, "m_dropPrefab"); int num2 = R.StableHash(R.CleanName((GameObject)((obj2 is GameObject) ? obj2 : null))); object obj3 = R.Get(obj, "m_cheated"); int num3; if (obj3 is bool) { flag = (bool)obj3; num3 = 1; } else { num3 = 0; } bool flag2 = (byte)((uint)num3 & (flag ? 1u : 0u)) != 0; if (num2 != 0 && InvokeRpc(val, "RPC_AddItem", num2, flag2)) { RemoveOne(input, inventory, obj); _fermenterCursor = (num + 1) % fermenters.Count; action = "Active — loaded " + ItemDisplayName(obj) + " into a fermenter"; return true; } } } return false; } private void SetIdleStatus(List<Component> fermenters, NamedContainerMarker input, NamedContainerMarker output, string frameWaiting) { if (fermenters.Count == 0) { SetStatus($"Active — no fermenters within {Plugin.BartenderRadius.Value:0}m"); return; } bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; foreach (Component fermenter in fermenters) { switch (Number(R.Call(fermenter, "GetStatus"))) { case 3: flag2 = true; break; case 0: flag4 = true; if (IsExposed(fermenter)) { flag3 = true; } break; default: flag = true; break; } } if (flag2 && !CanAcceptReady(fermenters, output.Inventory)) { SetStatus("Paused — finished mead chest is full"); } else if (!string.IsNullOrEmpty(frameWaiting)) { SetStatus(frameWaiting); } else if (flag || flag2) { SetStatus("Active — fermenters are working or ready"); } else if (flag4 && !HasAcceptedInput(input.Inventory, fermenters)) { SetStatus("Paused — mead base chest needs a compatible mead base"); } else if (flag3) { SetStatus("Paused — empty fermenters need shelter"); } else { SetStatus("Active — fermenters are supplied"); } } private static bool CanAcceptReady(List<Component> fermenters, object inventory) { foreach (Component fermenter in fermenters) { if (Number(R.Call(fermenter, "GetStatus")) == 3) { object obj = R.Call(ItemData(AsGameObject(R.Get(CurrentConversion(fermenter), "m_to"))), "Clone"); if (obj == null || CanAdd(inventory, obj)) { return true; } } } return false; } private static bool HasAcceptedInput(object inventory, List<Component> fermenters) { foreach (Component fermenter in fermenters) { if (FindAcceptedInput(inventory, fermenter) != null) { return true; } } return false; } private static object FindAcceptedInput(object inventory, Component fermenter) { foreach (object item in InventoryItems(inventory)) { object obj = R.Get(item, "m_dropPrefab"); int num = R.StableHash(R.CleanName((GameObject)((obj is GameObject) ? obj : null))); foreach (object item2 in Conversions(fermenter)) { GameObject val = AsGameObject(R.Get(item2, "m_from")); if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num) { return item; } } } return null; } private static object CurrentConversion(Component fermenter) { int num = Number(R.Call(fermenter, "GetContent")); foreach (object item in Conversions(fermenter)) { GameObject val = AsGameObject(R.Get(item, "m_from")); if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num) { return item; } } return null; } private static bool IsOutput(Component fermenter, GameObject dropObject) { object obj = R.Get(R.Get(R.Component(dropObject, "ItemDrop"), "m_itemData"), "m_dropPrefab"); int num = R.StableHash(R.CleanName((GameObject)(((obj is GameObject) ? obj : null) ?? dropObject))); foreach (object item in Conversions(fermenter)) { GameObject val = AsGameObject(R.Get(item, "m_to")); if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num) { return true; } } return false; } private static bool IsExposed(Component fermenter) { object obj = R.Get(fermenter, "m_exposed"); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } if (R.Get(fermenter, "m_hasRoof") is bool flag2) { return !flag2; } return false; } private static Vector3 OutputPosition(Component fermenter) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0068: Unknown result type (might be due to invalid IL or missing references) object obj = R.Get(fermenter, "m_outputPoint"); object obj2 = ((obj is Transform) ? obj : null); if (obj2 == null) { object obj3 = ((obj is Component) ? obj : null); obj2 = ((obj3 != null) ? ((Component)obj3).transform : null); if (obj2 == null) { object obj4 = ((obj is GameObject) ? obj : null); obj2 = ((obj4 != null) ? ((GameObject)obj4).transform : null); } } Transform val = (Transform)obj2; if (!((Object)(object)val != (Object)null)) { return fermenter.transform.position + fermenter.transform.forward * 2f; } return val.position; } private static bool InvokeRpc(Component component, string method, params object[] arguments) { object obj = R.Get(component, "m_nview"); ZNetView val = (ZNetView)(((obj is ZNetView) ? obj : null) ?? component.GetComponent<ZNetView>() ?? component.GetComponentInParent<ZNetView>()); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } try { val.InvokeRPC(method, arguments ?? new object[0]); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not send " + method + " to fermenter: " + ex.Message)); return false; } } private static void RemoveOne(NamedContainerMarker storage, object inventory, object item) { R.ClaimOwnership(((Component)storage).gameObject); string text = ItemSharedName(item); int num = Number(R.Call(inventory, "CountItems", text)); R.Call(inventory, "RemoveOneItem", item); if (num > 0 && Number(R.Call(inventory, "CountItems", text)) >= num) { R.Call(inventory, "RemoveItem", text, 1); } } internal static IEnumerable<object> InventoryItems(object inventory) { if (!(R.Call(inventory, "GetAllItems") is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { if (item != null && Number(R.Get(item, "m_stack")) > 0) { yield return item; } } } private static IEnumerable<object> Conversions(Component fermenter) { if (!(R.Get(fermenter, "m_conversion") is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { if (item != null) { yield return item; } } } internal static bool CanAdd(object inventory, object itemData) { if (inventory == null || itemData == null) { return false; } object obj = R.Call(inventory, "CanAddItem", itemData); if (obj is bool) { return (bool)obj; } object obj2 = R.Call(inventory, "HaveEmptySlot"); bool flag = default(bool); int num; if (obj2 is bool) { flag = (bool)obj2; num = ((1 == 0) ? 1 : 0); } else { num = 1; } return (byte)((uint)num | (flag ? 1u : 0u)) != 0; } private static object ItemData(GameObject prefab) { return R.Get(R.Component(prefab, "ItemDrop"), "m_itemData"); } private static GameObject AsGameObject(object value) { object obj = ((value is GameObject) ? value : null); if (obj == null) { object obj2 = ((value is Component) ? value : null); if (obj2 == null) { return null; } obj = ((Component)obj2).gameObject; } return (GameObject)obj; } internal static string ItemSharedName(object itemData) { return (R.Get(R.Get(itemData, "m_shared"), "m_name") as string) ?? ItemDisplayName(itemData); } internal static string ItemDisplayName(object itemData) { string text = R.Get(R.Get(itemData, "m_shared"), "m_name") as string; if (!string.IsNullOrEmpty(text)) { return text.Replace("$item_", "").Replace("_", " "); } object obj = R.Get(itemData, "m_dropPrefab"); return R.CleanName((GameObject)((obj is GameObject) ? obj : null)); } internal static int Number(object value) { try { return (value != null) ? Convert.ToInt32(value) : 0; } catch { return 0; } } private static string SlotLabel(int slot) { if (slot != 0) { return "Finished mead"; } return "Mead base"; } private static string SlotKey(int slot, string suffix) { return $"fw_bartender_slot_{slot}_{suffix}"; } private void RecordTransfer(string status) { R.ZSet(((Component)this).gameObject, "fw_bartender_transfers", R.ZInt(((Component)this).gameObject, "fw_bartender_transfers") + 1); SetStatus(status); } private void SetStatus(string status) { if (!string.Equals(R.ZString(((Component)this).gameObject, "fw_bartender_status"), status, StringComparison.Ordinal)) { R.ZSet(((Component)this).gameObject, "fw_bartender_status", status); } } private static bool Usable(BartenderPostMarker post) { if ((Object)(object)post != (Object)null && ((Component)post).gameObject.activeInHierarchy) { return R.Zdo(((Component)post).gameObject) != null; } return false; } } public sealed class BartenderServingFrame : MonoBehaviour { private const string LinkedKey = "fw_bartender_frame_linked"; private const string PostNameKey = "fw_bartender_frame_post_name"; private const string PostPositionKey = "fw_bartender_frame_post_pos"; private const string ItemHashKey = "fw_bartender_frame_item_hash"; private const string ItemNameKey = "fw_bartender_frame_item_name"; private static readonly HashSet<BartenderServingFrame> Active = new HashSet<BartenderServingFrame>(); private float _lastInteraction; private void OnEnable() { Active.Add(this); } private void OnDisable() { Active.Remove(this); } internal static BartenderServingFrame Ensure(Component stand) { if ((Object)(object)stand == (Object)null || !Supported(stand.gameObject)) { return null; } return stand.GetComponent<BartenderServingFrame>() ?? stand.GetComponentInParent<BartenderServingFrame>() ?? stand.gameObject.AddComponent<BartenderServingFrame>(); } internal bool Interact(object user, bool hold, bool alt, bool control) { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) if (Time.time - _lastInteraction < 0.35f) { return true; } _lastInteraction = Time.time; if (hold) { return true; } if (control && !alt) { R.ClaimOwnership(((Component)this).gameObject); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_linked", false); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", ""); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_hash", 0); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_name", ""); Plugin.Message(user, "Bartender serving-frame link cleared. Its displayed item was left in place."); return true; } if (!alt) { return false; } Component instance = R.Component(((Component)this).gameObject, "ItemStand"); object obj = R.Call(instance, "HaveAttachment"); if (!(obj is bool) || !(bool)obj) { Plugin.Message(user, "Place the finished mead you want on this item frame first."); return true; } int num = Number(R.Call(instance, "GetAttachedItem")); if (num == 0) { Plugin.Message(user, "The displayed item could not be identified."); return true; } BartenderPostMarker bartenderPostMarker = BartenderPostMarker.FindNearest(((Component)this).transform.position, Plugin.BartenderFrameRange.Value); if ((Object)(object)bartenderPostMarker == (Object)null) { Plugin.Message(user, $"No Bartender Post is within {Plugin.BartenderFrameRange.Value:0}m."); return true; } if (!bartenderPostMarker.IsFermenterProduct(num)) { Plugin.Message(user, "That item is not a product of a fermenter managed by the nearby Bartender Post."); return true; } string text = ProductName(bartenderPostMarker, num); R.ClaimOwnership(((Component)this).gameObject); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_linked", true); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", bartenderPostMarker.PostName); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)bartenderPostMarker).transform.position); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_hash", num); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_name", text); Plugin.Message(user, "Serving frame registered to " + bartenderPostMarker.PostName + " for " + text + "."); return true; } internal string HoverText(string original) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) bool flag = R.ZBool(((Component)this).gameObject, "fw_bartender_frame_linked"); BartenderPostMarker bartenderPostMarker = (flag ? LinkedPost() : BartenderPostMarker.FindNearest(((Component)this).transform.position, Plugin.BartenderFrameRange.Value)); if (!flag && (Object)(object)bartenderPostMarker == (Object)null) { return original; } string text; if (!flag) { text = "\n[<color=yellow><b>Shift + E</b></color>] Register displayed mead with nearest Bartender Post"; } else { object obj = R.Call(R.Component(((Component)this).gameObject, "ItemStand"), "HaveAttachment"); bool flag2 = default(bool); int num; if (obj is bool) { flag2 = (bool)obj; num = 1; } else { num = 0; } bool flag3 = (byte)((uint)num & (flag2 ? 1u : 0u)) != 0; string text2 = (((Object)(object)bartenderPostMarker == (Object)null) ? "<color=#FF7777>Missing</color>" : ("<color=#7CFF7C>" + bartenderPostMarker.PostName + "</color>")); string text3 = R.ZString(((Component)this).gameObject, "fw_bartender_frame_item_name", "Unknown mead"); text = "\nBartender: " + text2 + "\nRequested stock: " + text3 + "\n" + (flag3 ? "Status: Stocked" : "Status: Waiting for finished mead") + "\n[<color=yellow><b>Shift + E</b></color>] Register current displayed mead\n[<color=yellow><b>Ctrl + E</b></color>] Clear Bartender link"; } return (original ?? "Item Stand") + text; } internal static int CountFor(BartenderPostMarker post) { Discover(post); int num = 0; foreach (BartenderServingFrame item in Active) { if (Usable(item) && (Object)(object)item.LinkedPost() == (Object)(object)post) { num++; } } return num; } internal static bool TryStockNext(BartenderPostMarker post, NamedContainerMarker output, out string action, out string waiting) { action = null; waiting = null; Discover(post); object inventory = output.Inventory; bool flag = default(bool); foreach (BartenderServingFrame item in Active) { if (!Usable(item) || (Object)(object)item.LinkedPost() != (Object)(object)post) { continue; } Component val = R.Component(((Component)item).gameObject, "ItemStand"); object obj = R.Call(val, "HaveAttachment"); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) == 0) { int hash = R.ZInt(((Component)item).gameObject, "fw_bartender_frame_item_hash"); object obj2 = FindItem(inventory, hash); if (obj2 == null) { waiting = "Active — serving frame is waiting for " + R.ZString(((Component)item).gameObject, "fw_bartender_frame_item_name", "finished mead"); } else if (item.Attach(val, obj2)) { RemoveOne(output, inventory, obj2); action = "Active — stocked " + BartenderPostMarker.ItemDisplayName(obj2) + " on a serving frame"; return true; } } } return false; } private bool Attach(Component stand, object itemData) { try { object obj = R.Call(itemData, "Clone"); if (obj == null) { return false; } R.Set(obj, "m_stack", 1); object obj2 = R.Get(obj, "m_dropPrefab"); int num = R.StableHash(R.CleanName((GameObject)((obj2 is GameObject) ? obj2 : null))); if (num == 0) { return false; } R.ClaimOwnership(((Component)this).gameObject); object obj3 = R.Zdo(((Component)this).gameObject); if (obj3 == null) { return false; } R.ZSet(((Component)this).gameObject, "item", num); R.CallStatic(R.Type("ItemDrop"), "SaveToZDO", obj, obj3, -1); int num2 = Number(R.Get(obj, "m_variant")); int num3 = Math.Max(1, Number(R.Get(obj, "m_quality"))); int num4 = Number(R.Call(stand, "GetOrientation")); ZNetView val = stand.GetComponent<ZNetView>() ?? stand.GetComponentInParent<ZNetView>(); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } val.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { num, num2, num3, num4 }); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not stock Bartender serving frame: " + ex.Message)); return false; } } private BartenderPostMarker LinkedPost() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) if (!R.ZBool(((Component)this).gameObject, "fw_bartender_frame_linked")) { return null; } string name = R.ZString(((Component)this).gameObject, "fw_bartender_frame_post_name"); Vector3 position = R.ZVector(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)this).transform.position); BartenderPostMarker bartenderPostMarker = BartenderPostMarker.FindAtPosition(position); if ((Object)(object)bartenderPostMarker != (Object)null && bartenderPostMarker.Matches(name, position)) { return bartenderPostMarker; } bartenderPostMarker = BartenderPostMarker.FindNamed(name, ((Component)this).transform.position, Plugin.BartenderFrameRange.Value, out var ambiguous); if (ambiguous || (Object)(object)bartenderPostMarker == (Object)null) { return null; } if (R.IsOwner(((Component)this).gameObject)) { R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", bartenderPostMarker.PostName); R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)bartenderPostMarker).transform.position); } return bartenderPostMarker; } private static void Discover(BartenderPostMarker post) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) foreach (Component item in R.FindNear("ItemStand", ((Component)post).transform.position, Plugin.BartenderFrameRange.Value)) { Ensure(item); } } private static object FindItem(object inventory, int hash) { foreach (object item in BartenderPostMarker.InventoryItems(inventory)) { object obj = R.Get(item, "m_dropPrefab"); if (R.StableHash(R.CleanName((GameObject)((obj is GameObject) ? obj : null))) == hash) { return item; } } return null; } private static string ProductName(BartenderPostMarker post, int hash) { object obj = R.Call(R.StaticProperty(R.Type("ZNetScene"), "instance") ?? R.StaticProperty(R.Type("ZNetScene"), "m_instance") ?? R.StaticProperty(R.Type("ZNetScene"), "s_instance"), "GetPrefab", hash); string text = BartenderPostMarker.ItemDisplayName(R.Get(R.Component((GameObject)((obj is GameObject) ? obj : null), "ItemDrop"), "m_itemData")); if (!string.IsNullOrEmpty(text)) { return text; } return "finished mead"; } private static void RemoveOne(NamedContainerMarker storage, object inventory, object item) { R.ClaimOwnership(((Component)storage).gameObject); string text = BartenderPostMarker.ItemSharedName(item); int num = Number(R.Call(inventory, "CountItems", text)); R.Call(inventory, "RemoveOneItem", item); if (num > 0 && Number(R.Call(inventory, "CountItems", text)) >= num) { R.Call(inventory, "RemoveItem", text, 1); } } private static bool Supported(GameObject go) { string text = R.CleanName(go).ToLowerInvariant(); if (!(text == "itemstand")) { return text == "itemstandh"; } return true; } private static bool Usable(BartenderServingFrame frame) { if ((Object)(object)frame != (Object)null && ((Component)frame).gameObject.activeInHierarchy) { return R.Zdo(((Component)frame).gameObject) != null; } return false; } private static int Number(object value) { return BartenderPostMarker.Number(value); } } public sealed class LumberjackPostMarker : MonoBehaviour, TextReceiver { private const string NameKey = "fw_lumberjack_post_name"; private const string StorageNameKey = "fw_lumberjack_storage_name"; private const string StoragePositionKey = "fw_lumberjack_storage_pos"; private const string StorageLinkedKey = "fw_lumberjack_storage_linked"; private const string StatusKey = "fw_lumberjack_status"; private const string HarvestedKey = "fw_lumberjack_harvested"; private static readonly HashSet<LumberjackPostMarker> Active = new HashSet<LumberjackPostMarker>(); private bool _editStorage; private bool _queuedEdit; private float _lastInteraction; private float _nextUpdate; private float _nextHarvest; private object _textUser; internal string PostName => R.ZString(((Component)this).gameObject, "fw_lumberjack_post_name"); internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_lumberjack_status", "Inactive — no Lumberjack assigned through a Lumberjack Bunk"); private void OnEnable() { Active.Add(this); } private void OnDisable() { Active.Remove(this); } private void Start() { _nextUpdate = Time.time + Random.Range(0.5f, 1.5f); _nextHarvest = Time.time + 5f; } private void LateUpdate() { if (!_queuedEdit) { return; } _queuedEdit = false; try { string text = (_editStorage ? "Wood chest name" : "Lumberjack post name"); if ((Object)(object)TextInput.instance != (Object)null) { TextInput.instance.RequestText((TextReceiver)(object)this, text, 32); } else { Plugin.Message(_textUser, "The text-entry window is unavailable."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not open Lumberjack Post editor: " + ex.Message)); Plugin.Message(_textUser, "The Lumberjack Post editor could not be opened."); } } private void Update() { if (Time.time < _nextUpdate) { return; } _nextUpdate = Time.time + 2.5f; if (!R.IsOwner(((Component)this).gameObject)) { return; } if ((Object)(object)JobStationMarker.FindLumberjackBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null) { SetStatus("Inactive — no Lumberjack assigned through a Lumberjack Bunk"); return; } string problem; NamedContainerMarker namedContainerMarker = Storage(out problem); if ((Object)(object)namedContainerMarker == (Object)null) { SetStatus("Paused — wood chest " + problem); return; } if (Time.time < _nextHarvest) { float num = Mathf.Ceil(_nextHarvest - Time.time); SetStatus($"Active — next beech in {num:0}s"); return; } Component val = FindNearestBeech(); if ((Object)(object)val == (Object)null) { _nextHarvest = Time.time + 10f; SetStatus($"Active — no mature beech trees within {Plugin.LumberjackRadius.Value:0}m"); } else { ProcessBeech(val, namedContainerMarker); } } internal bool Interact(object user, bool hold, bool alt) { if (Time.time - _lastInteraction < 0.45f) { return true; } _lastInteraction = Time.time; if (hold) { return true; } _textUser = user; _editStorage = alt; _queuedEdit = true; return true; } public string GetText() { if (!_editStorage) { return PostName; } return R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name"); } public void SetText(string text) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) string text2 = PatrolPointMarker.CleanLabel(text); R.ClaimOwnership(((Component)this).gameObject); if (!_editStorage) { R.ZSet(((Component)this).gameObject, "fw_lumberjack_post_name", text2); R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2); Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Lumberjack Post name cleared." : ("Lumberjack Post named " + text2 + ".")); return; } R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_name", text2); bool ambiguous; NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.LumberjackStorageRange.Value, out ambiguous); if ((Object)(object)namedContainerMarker != (Object)null) { R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)namedContainerMarker).transform.position); R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", true); Plugin.Message(_textUser, "Wood chest linked to " + namedContainerMarker.StorageName + "."); } else { R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", false); string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? "Wood chest link cleared." : $"No storage named '{text2}' is within {Plugin.LumberjackStorageRange.Value:0}m.")); Plugin.Message(_textUser, text3); } } internal string HoverText() { string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>")); return "Fuling Lumberjack Post\nName: " + text + "\nWood chest: " + StorageDisplay() + "\n" + $"Range: {Plugin.LumberjackRadius.Value:0}m\nStatus: {CurrentStatus}\n" + string.Format("Beeches processed: {0}\n", R.ZInt(((Component)this).gameObject, "fw_lumberjack_harvested")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Shift + E</b></color>] Set wood chest"; } internal static LumberjackPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) ambiguous = false; if (string.IsNullOrEmpty(name)) { return null; } LumberjackPostMarker lumberjackPostMarker = null; float num = radius * radius; foreach (LumberjackPostMarker item in Active) { if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase)) { continue; } Vector3 val = ((Component)item).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { if ((Object)(object)lumberjackPostMarker != (Object)null && (Object)(object)lumberjackPostMarker != (Object)(object)item) { ambiguous = true; return null; } lumberjackPostMarker = item; } } return lumberjackPostMarker; } internal static LumberjackPostMarker FindAtPosition(Vector3 position) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) LumberjackPostMarker result = null; float num = 2.25f; foreach (LumberjackPostMarker item in Active) { if (Usable(item)) { Vector3 val = ((Component)item).transform.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = item; } } } return result; } private NamedContainerMarker Storage(out string problem) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_0133: Unknown result type (might be due to invalid IL or missing references) string text = R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name"); if (string.IsNullOrEmpty(text)) { problem = "is not configured"; return null; } bool flag = R.ZBool(((Component)this).gameObject, "fw_lumberjack_storage_linked"); Vector3 val = R.ZVector(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)this).transform.position); NamedContainerMarker namedContainerMarker = (flag ? NamedContainerMarker.FindAtPosition(val) : null); bool ambiguous = false; if ((Object)(object)namedContainerMarker == (Object)null) { namedContainerMarker = NamedContainerMarker.FindNamed(text, ((Component)this).transform.position, Plugin.LumberjackStorageRange.Value, out ambiguous); } if (ambiguous) { problem = "name is ambiguous"; return null; } if ((Object)(object)namedContainerMarker == (Object)null) { problem = $"was not found within {Plugin.LumberjackStorageRange.Value:0}m"; return null; } if (Vector3.Distance(((Component)this).transform.position, ((Component)namedContainerMarker).transform.position) > Plugin.LumberjackStorageRange.Value) { problem = $"is more than {Plugin.LumberjackStorageRange.Value:0}m away"; return null; } if (R.IsOwner(((Component)this).gameObject)) { if (flag && string.Equals(text, namedContainerMarker.StorageName, StringComparison.Ordinal)) { Vector3 val2 = val - ((Component)namedContainerMarker).transform.position; if (!(((Vector3)(ref val2)).sqrMagnitude > 0.01f)) { goto IL_018f; } } R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_name", namedContainerMarker.StorageName); R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)namedContainerMarker).transform.position); R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", true); } goto IL_018f; IL_018f: problem = null; return namedContainerMarker; } private string StorageDisplay() { string text = R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name"); string problem; NamedContainerMarker namedContainerMarker = Storage(out problem); if ((Object)(object)namedContainerMarker != (Object)null) { return "<color=#7CFF7C>" + namedContainerMarker.StorageName + "</color>"; } if (string.IsNullOrEmpty(text)) { return "<color=#aaaaaa>Not configured</color>"; } return "<color=#FF7777>" + text + " — " + problem + "</color>"; } private Component FindNearestBeech() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Component result = null; float num = Plugin.LumberjackRadius.Value * Plugin.LumberjackRadius.Value; foreach (Component item in R.FindNear("TreeBase", ((Component)this).transform.position, Plugin.LumberjackRadius.Value)) { if (IsMatureBeech(item)) { Vector3 val = item.transform.position - ((Component)this).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = item; } } } return result; } private void ProcessBeech(Component tree, NamedContainerMarker storage) { //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) Component val = R.Component(((Component)storage).gameObject, "Container"); if (!R.IsOwner(((Component)storage).gameObject)) { R.ClaimOwnership(((Component)storage).gameObject); SetStatus("Active — waiting for ownership of the wood chest"); return; } object obj = R.Call(val, "GetInventory"); GameObject prefab = Plugin.GetPrefab("Wood"); GameObject prefab2 = Plugin.GetPrefab("Beech_Sapling"); object obj2 = R.Call(R.Get(R.Component(prefab, "ItemDrop"), "m_itemData"), "Clone"); int num = Mathf.Max(1, Plugin.LumberjackWoodPerTree.Value); if ((Object)(object)val == (Object)null || obj == null || (Object)(object)prefab == (Object)null || (Object)(object)prefab2 == (Object)null || obj2 == null) { SetStatus("Paused — beech, sapling or wood prefab data is unavailable"); return; } R.Set(obj2, "m_stack", num); object obj3 = R.Call(obj, "CanAddItem", obj2); if (obj3 is bool && !(bool)obj3) { SetStatus($"Paused — wood chest needs room for {num} Wood"); return; } if (obj3 == null) { object obj4 = R.Call(obj, "HaveEmptySlot"); if (obj4 is bool && !(bool)obj4) { SetStatus($"Paused — wood chest needs room for {num} Wood"); return; } } Vector3 position = tree.transform.position; Quaternion rotation = tree.transform.rotation; Quaternion val2 = Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y, 0f); GameObject val3 = Object.Instantiate<GameObject>(prefab2, position, val2); if ((Object)(object)val3 == (Object)null) { SetStatus("Paused — could not replant the beech"); return; } object obj5 = R.Call(obj, "AddItem", obj2); if (!(obj5 is bool) || !(bool)obj5) { R.DestroyNetworked(val3); SetStatus("Paused — wood chest could not accept the harvest"); return; } R.Call(val, "Save"); R.ClaimOwnership(tree.gameObject); R.DestroyNetworked(tree.gameObject); _nextHarvest = Time.time + Mathf.Max(1f, Plugin.LumberjackWorkInterval.Value); R.ZSet(((Component)this).gameObject, "fw_lumberjack_harvested", R.ZInt(((Component)this).gameObject, "fw_lumberjack_harvested") + 1); SetStatus($"Active — processed one beech, stored {num} Wood and replanted it"); } private static bool IsMatureBeech(Component tree) { if ((Object)(object)tree == (Object)null || !tree.gameObject.activeInHierarchy || R.Zdo(tree.gameObject) == null) { return false; } string text = R.CleanName(tree.gameObject); if (text.StartsWith("Beech", StringComparison.OrdinalIgnoreCase) && text.IndexOf("Sapling", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Log", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Stub", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("Stump", StringComparison.OrdinalIgnoreCase) < 0; } return false; } private void SetStatus(string status) { if (!string.Equals(R.ZString(((Component)this).gameObject, "fw_lumberjack_status"), status, StringComparison.Ordinal)) { R.ZSet(((Component)this).gameObject, "fw_lumberjack_status", status); } } private static bool Usable(LumberjackPostMarker post) { if ((Object)(object)post != (Object)null && ((Component)post).gameObject.activeInHierarchy) { return R.Zdo(((Component)post).gameObject) != null; } return false; } } [BepInPlugin("com.sophie.fulingworkforce", "Fuling Workforce", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.sophie.fulingworkforce"; public const string Name = "Fuling Workforce"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<int> RecruitmentCost; internal static ConfigEntry<float> OathRadius; internal static ConfigEntry<float> PatrolRange; internal static ConfigEntry<float> PatrolLinkDistance; internal static ConfigEntry<float> PatrolWaitSeconds; internal static ConfigEntry<float> PatrolStuckSeconds; internal static ConfigEntry<float> FarmRadius; internal static ConfigEntry<float> FarmWorkInterval; internal static ConfigEntry<float> FarmStorageRange; internal static ConfigEntry<float> JobPostRange; internal static ConfigEntry<float> JobWanderRadius; internal static ConfigEntry<float> JobWanderMaximumIdle; internal static ConfigEntry<float> MachineRadius; internal static ConfigEntry<float> MachineWorkInterval; internal static ConfigEntry<float> MachineStorageRange; internal static ConfigEntry<float> AnimalRadius; internal static ConfigEntry<int> AnimalLimit; internal static ConfigEntry<float> AnimalWorkInterval; internal static ConfigEntry<float> AnimalStorageRange; internal static ConfigEntry<float> RepairRadius; internal static ConfigEntry<float> RepairWorkInterval; internal static ConfigEntry<float> ExpeditionInterval; internal static ConfigEntry<float> ExpeditionRadius; internal static ConfigEntry<float> ExpeditionTableRange; internal static ConfigEntry<float> BartenderRadius; internal static ConfigEntry<float> BartenderWorkInterval; internal static ConfigEntry<float> BartenderStorageRange; internal static ConfigEntry<float> BartenderFrameRange; internal static ConfigEntry<float> FireKeeperRadius; internal static ConfigEntry<float> FireKeeperWorkInterval; internal static ConfigEntry<float> FireKeeperScanInterval; internal static ConfigEntry<float> FireKeeperStorageRange; internal static ConfigEntry<float> SorterRadius; internal static ConfigEntry<float> SorterWorkInterval; internal static ConfigEntry<float> SorterStorageRange; internal static ConfigEntry<float> LumberjackRadius; internal static ConfigEntry<float> LumberjackWorkInterval; internal static ConfigEntry<float> LumberjackStorageRange; internal static ConfigEntry<int> LumberjackWoodPerTree; private Harmony _harmony; private Delegate _vanillaReadyHandler; private void Awake() { //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_058e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; RecruitmentCost = ((BaseUnityPlugin)this).Config.Bind<int>("Recruitment", "CoinCost", 20, "One-time coin cost to recruit a surrendered Fuling."); OathRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Recruitment", "OathTotemRadius", 12f, "Distance from an Oath Totem in which a Fuling may surrender."); PatrolRange = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "MaximumDistanceFromBunk", 100f, "Every point in a Guard's patrol route must remain within this distance of its bunk."); PatrolLinkDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "MaximumPointSpacing", 40f, "Maximum straight-line distance between a patrol point and its named next point."); PatrolWaitSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "WaitAtPointSeconds", 2f, "How long a Guard waits after reaching each patrol point."); PatrolStuckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "SkipUnreachablePointSeconds", 45f, "How long a Guard tries to reach a point before skipping to the next one."); FarmRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "WorkRadius", 25f, "How far a Farming Post looks for mature crops."); FarmWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "WorkIntervalSeconds", 2.5f, "Minimum time between crops processed by each active Farming Post."); FarmStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "StorageRange", 30f, "Maximum distance from a Farming Post to its named storage chest."); JobPostRange = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "MaximumBunkToPostDistance", 100f, "Maximum distance from a job-specific bunk to its matching named job post."); JobWanderRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "WorkerWanderRadius", 5f, "How far non-Guard workers wander from their linked job post while appearing to work."); JobWanderMaximumIdle = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "WorkerWanderMaximumIdleSeconds", 25f, "Maximum random idle time between work-wander destinations."); MachineRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "MachineRadius", 25f, "How far Kiln and Smelter Posts look for compatible machines."); MachineWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "WorkIntervalSeconds", 1.5f, "Minimum time between item transfers by each active Kiln or Smelter Post."); MachineStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "StorageRange", 30f, "Maximum distance from a Kiln or Smelter Post to each named storage chest."); AnimalRadius = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "WorkRadius", 20f, "How far an Animal Keeper Post manages supported tamed animals and collects their drops."); AnimalLimit = ((BaseUnityPlugin)this).Config.Bind<int>("AnimalKeeper", "PopulationLimitPerSpecies", 10, "Population limit applied separately to boars, wolves and lox within an Animal Keeper Post's radius."); AnimalWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "WorkIntervalSeconds", 2.5f, "Minimum time between Animal Keeper Post feeding, culling or collection actions."); AnimalStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "StorageRange", 30f, "Maximum distance from an Animal Keeper Post to its named food and products chests."); RepairRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Repairer", "WorkRadius", 25f, "How far a Repair Post looks for damaged player-built structures."); RepairWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Repairer", "WorkIntervalSeconds", 2.5f, "Minimum time between structures repaired by each active Repair Post."); ExpeditionInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "ReportIntervalSeconds", 600f, "Seconds between landmark reports from each active Expedition Post."); ExpeditionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "DiscoveryRadius", 2000f, "Maximum distance from an Expedition Post at which Explorers can discover landmarks."); ExpeditionTableRange = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "CartographyTableRange", 15f, "Maximum distance from an Expedition Post to its linked cartography table."); BartenderRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "FermenterRadius", 25f, "How far a Bartender Post looks for fermenters."); BartenderWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "WorkIntervalSeconds", 1.5f, "Minimum time between fermenter, collection or serving-frame actions by each Bartender Post."); BartenderStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "StorageRange", 30f, "Maximum distance from a Bartender Post to its mead base and finished mead chests."); BartenderFrameRange = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "ServingFrameRange", 25f, "Maximum distance from a Bartender Post to a registered serving item frame."); FireKeeperRadius = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "FireRadius", 100f, "How far a Fire Keeper Post looks for fireplaces, hearths, braziers and other fuelled fires."); FireKeeperWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "WorkIntervalSeconds", 1.5f, "Minimum time between fuel additions by each active Fire Keeper Post."); FireKeeperScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "FireScanIntervalSeconds", 15f, "How often a Fire Keeper Post refreshes its cached list of fires. Larger values reduce scanning cost."); FireKeeperStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "StorageRange", 30f, "Maximum distance from a Fire Keeper Post to its named fuel chest."); SorterRadius = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "ControlledChestRadius", 30f, "How far a Chest Sorter Post looks for explicitly controlled destination chests."); SorterWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "WorkIntervalSeconds", 0.75f, "Minimum time between stack transfers by each active Chest Sorter Post."); SorterStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "InputChestRange", 30f, "Maximum distance from a Chest Sorter Post to its named input chest."); LumberjackRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "WorkRadius", 30f, "How far a Lumberjack Post looks for mature beech trees."); LumberjackWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "WorkIntervalSeconds", 60f, "Minimum time between beech trees processed by each active Lumberjack Post."); LumberjackStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "StorageRange", 30f, "Maximum distance from a Lumberjack Post to its named wood chest."); LumberjackWoodPerTree = ((BaseUnityPlugin)this).Config.Bind<int>("Lumberjack", "WoodPerTree", 20, "Wood placed in the output chest for each mature beech tree processed."); _harmony = new Harmony("com.sophie.fulingworkforce"); WorkforcePatches.Apply(_harmony); SubscribeToJotunn(); Log.LogInfo((object)"Fuling Workforce 1.0.0 loaded"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void SubscribeToJotunn() { EventInfo eventInfo = R.Type("Jotunn.Managers.PrefabManager")?.GetEvent("OnVanillaPrefabsAvailable", BindingFlags.Static | BindingFlags.Public); if (eventInfo == null) { Log.LogError((object)"Jotunn PrefabManager event was not found."); return; } _vanillaReadyHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "RegisterContent"); eventInfo.AddEventHandler(null, _vanillaReadyHandler); } public void RegisterContent() { try { AddControllerToFulings(); AddPiece("FW_OathTotem", "guard_stone", "Fuling Oath Totem", "Fulings reduced below 10% health within its radius surrender and may be recruited for coins.", typeof(OathTotemMarker), Req("FineWood", 10), Req("BlackMetal", 5), Req("GoblinTotem", 1)); AddPiece("FW_GuardPost", "bed", "Fuling Guard Bunk", "Assign a Guard and optionally enter the name of a starting Patrol Point with Shift + E.", typeof(JobStationMarker), "Guard", Req("Wood", 20), Req("BlackMetal", 2)); AddPiece("FW_PatrolPoint", "sign", "Fuling Patrol Point", "Name this point and the next point in the route. Linked points may be up to 40 metres apart.", typeof(PatrolPointMarker), Req("Wood", 2), Req("Resin", 1)); AddPiece("FW_FarmBunk", "bed", "Fuling Farming Bunk", "Assign a Farmer and use Shift + E to enter the name of its Farming Post.", typeof(JobStationMarker), "Farmer", Req("Wood", 20), Req("IronNails", 10)); AddPiece("FW_FarmingPost", "sign", "Fuling Farming Post", "Name the post with E and link a named storage chest with Shift + E. An assigned Farmer activates it.", typeof(FarmingPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("IronNails", 5)); AddPiece("FW_KilnBunk", "bed", "Fuling Kiln Bunk", "Assign a Kiln Operator and use Shift + E to enter the name of its Kiln Post.", typeof(JobStationMarker), "Kiln", Req("Wood", 20), Req("SurtlingCore", 1)); AddPiece("FW_KilnPost", "sign", "Fuling Kiln Post", "Operates nearby charcoal kilns using separately named wood and coal chests.", typeof(MachinePostMarker), "Kiln", Req("Wood", 10), Req("FineWood", 5), Req("SurtlingCore", 1)); AddPiece("FW_SmelterBunk", "bed", "Fuling Smelter Bunk", "Assign a Smelter Operator and use Shift + E to enter the name of its Smelter Post.", typeof(JobStationMarker), "Smelter", Req("Wood", 20), Req("IronNails", 10)); AddPiece("FW_SmelterPost", "sign", "Fuling Smelter Post", "Operates nearby smelters and blast furnaces using separate ore, coal and output chests.", typeof(MachinePostMarker), "Smelter", Req("Wood", 10), Req("FineWood", 5), Req("Iron", 5), Req("SurtlingCore", 2)); AddPiece("FW_KeeperBunk", "bed", "Fuling Animal Keeper Bunk", "Assign an Animal Keeper and use Shift + E to enter the name of its Animal Keeper Post.", typeof(JobStationMarker), "Keeper", Req("Wood", 20), Req("LeatherScraps", 10), Req("IronNails", 10)); AddPiece("FW_KeeperPost", "sign", "Fuling Animal Keeper Post", "Feeds and manages nearby tamed boars, wolves and lox using separate food and products chests.", typeof(AnimalKeeperPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("IronNails", 5)); AddPiece("FW_RepairBunk", "bed", "Fuling Repairer Bunk", "Assign a Repairer and use Shift + E to enter the name of its Repair Post.", typeof(JobStationMarker), "Repairer", Req("Wood", 20), Req("IronNails", 10)); AddPiece("FW_RepairPost", "sign", "Fuling Repair Post", "Repairs nearby damaged player-built structures while a linked Repairer is assigned.", typeof(RepairPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("Iron", 5)); AddPiece("FW_ExpeditionBunk", "bed", "Fuling Expedition Manager Bunk", "Assign an Expedition Manager and use Shift + E to enter the name of its Expedition Post.", typeof(JobStationMarker), "Expedition", Req("Wood", 20), Req("FineWood", 10), Req("DeerHide", 5), Req("IronNails", 10)); AddPiece("FW_ExpeditionPost", "sign", "Fuling Expedition Post", "Enlists up to ten Explorers and records discovered landmarks for a linked cartography table.", typeof(ExpeditionPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Bronze", 5)); AddPiece("FW_BartenderBunk", "bed", "Fuling Bartender Bunk", "Assign a Bartender and use Shift + E to enter the name of its Bartender Post.", typeof(JobStationMarker), "Bartender", Req("Wood", 20), Req("FineWood", 10), Req("DeerHide", 5), Req("Bronze", 2)); AddPiece("FW_BartenderPost", "sign", "Fuling Bartender Post", "Operates nearby fermenters and stocks registered item frames from a finished mead chest.", typeof(BartenderPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Bronze", 5)); AddPiece("FW_FireKeeperBunk", "bed", "Fuling Fire Keeper Bunk", "Assign a Fire Keeper and use Shift + E to enter the name of its Fire Keeper Post.", typeof(JobStationMarker), "FireKeeper", Req("Wood", 20), Req("FineWood", 5), Req("SurtlingCore", 2)); AddPiece("FW_FireKeeperPost", "sign", "Fuling Fire Keeper Post", "Uses a named fuel chest to keep compatible fuelled fires supplied within 100 metres.", typeof(FireKeeperPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("SurtlingCore", 2)); AddPiece("FW_SorterBunk", "bed", "Fuling Chest Sorter Bunk", "Assign a Chest Sorter and use Shift + E to enter the name of its Chest Sorter Post.", typeof(JobStationMarker), "Sorter", Req("Wood", 20), Req("FineWood", 10), Req("IronNails", 10)); AddPiece("FW_SorterPost", "sign", "Fuling Chest Sorter Post", "Moves input items only into controlled nearby chests that already contain the same item.", typeof(ChestSorterPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Iron", 5)); AddPiece("FW_LumberjackBunk", "bed", "Fuling Lumberjack Bunk", "Assign a Lumberjack and use Shift + E to enter the name of its Lumberjack Post.", typeof(JobStationMarker), "Lumberjack", Req("Wood", 20), Req("FineWood", 5), Req("IronNails", 10)); AddPiece("FW_LumberjackPost", "sign", "Fuling Lumberjack Post", "Processes one mature beech per minute, stores its wood and replants it for free.", typeof(LumberjackPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("Bronze", 5)); Log.LogInfo((object)"Registered recruitment, Guard patrols, Farming, Kiln, Smelter, Animal Keeper, Repairer, Expedition, Bartender, Fire Keeper, Chest Sorter and Lumberjack job workflows."); } catch (Exception arg) { Log.LogError((object)$"Content registration failed: {arg}"); } } private static Tuple<string, int> Req(string item, int amount) { return Tuple.Create(item, amount); } private static void AddControllerToFulings() { GameObject prefab = GetPrefab("Goblin"); if (!((Object)(object)prefab == (Object)null) && (Object)(object)prefab.GetComponent<FulingWorker>() == (Object)null) { prefab.AddComponent<FulingWorker>(); } } private static void AddPiece(string internalName, string basePrefab, string name, string description, Type markerType, params object[] roleAndRequirements) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) string job = roleAndRequirements.FirstOrDefault() as string; IEnumerable<Tuple<string, int>> enumerable = roleAndRequirements.OfType<Tuple<string, int>>(); object obj = R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PrefabManager"), "Instance"), "CreateClonedPrefab", internalName, basePrefab); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("Could not clone " + basePrefab); } Component val2 = val.GetComponent(markerType) ?? val.AddComponent(markerType); if (val2 is JobStationMarker jobStationMarker) { jobStationMarker.Job = job; val.transform.localScale = val.transform.localScale * 0.78f; } if (val2 is PatrolPointMarker) { val.transform.localScale = val.transform.localScale * 0.7f; } if (val2 is FarmingPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is MachinePostMarker machinePostMarker) { machinePostMarker.Job = job; val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is AnimalKeeperPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is RepairPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is ExpeditionPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is BartenderPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is FireKeeperPostMarker || val2 is ChestSorterPostMarker || val2 is LumberjackPostMarker) { val.transform.localScale = val.transform.localScale * 0.8f; } if (val2 is OathTotemMarker) { Type type = R.Type("PrivateArea"); Component val3 = ((type == null) ? null : val.GetComponent(type)); if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)val3); } } Type type2 = R.Type("Jotunn.Configs.PieceConfig"); object obj2 = Activator.CreateInstance(type2); R.Property(obj2, "Name", name); R.Property(obj2, "Description", description); R.Property(obj2, "PieceTable", "Hammer"); R.Property(obj2, "Category", "Furniture"); Type type3 = R.Type("Jotunn.Configs.RequirementConfig"); Array array = Array.CreateInstance(type3, enumerable.Count()); int num = 0; foreach (Tuple<string, int> item in enumerable) { object value = Activator.CreateInstance(type3, item.Item1, item.Item2, 0, true); array.SetValue(value, num++); } R.Property(obj2, "Requirements", array); object obj3 = R.Type("Jotunn.Entities.CustomPiece").GetConstructor(new Type[3] { typeof(GameObject), typeof(bool), type2 }).Invoke(new object[3] { val, false, obj2 }); R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PieceManager"), "Instance"), "AddPiece", obj3); } internal static GameObject GetPrefab(string name) { object obj = R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PrefabManager"), "Instance"), "GetPrefab", name); return (GameObject)((obj is GameObject) ? obj : null); } internal static void Message(object humanoid, string text) { try { object obj = Enum.Parse(R.Type("MessageHud+MessageType"), "Center"); MethodInfo methodInfo = humanoid.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Message" && m.GetParameters().Length >= 2); if (methodInfo != null) { R.InvokeWithDefaults(methodInfo, humanoid, obj, text); return; } } catch { } Log.LogInfo((object)text); } } internal static class R { private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>(); internal static Type Type(string name) { if (TypeCache.TryGetValue(name, out var value)) { return value; } Type type = AccessTools.TypeByName(name); if (type != null) { TypeCache[name] = type; } return type; } internal static object StaticProperty(Type type, string name) { if (type == null) { return null; } object obj = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); if (obj == null) { FieldInfo? field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if ((object)field == null) { return null; } obj = field.GetValue(null); } return obj; } internal static object Get(object instance, string name) { if (instance == null) { return null; } Type type = instance.GetType(); object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj == null) { PropertyInfo? property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property == null) { return null; } obj = property.GetValue(instance, null); } return obj; } internal static bool Set(object instance, string name, object value) { if (instance == null) { return false; } Type type = instance.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(instance, value); return true; } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(instance, value, null); return true; } return false; } internal static void Property(object instance, string name, object value) { PropertyInfo? property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { throw new MissingMemberException(instance.GetType().FullName, name); } property.SetValue(instance, value, null); } internal static object Call(object instance, string name, params object[] args) { if (instance == null) { return null; } MethodInfo methodInfo = BestMethod(instance.GetType(), name, args); if (!(methodInfo == null)) { return InvokeWithDefaults(methodInfo, instance, args); } return null; } internal static object CallStatic(Type type, string name, params object[] args) { MethodInfo methodInfo = BestMethod(type, name, args, requireStatic: true); if (!(methodInfo == null)) { return InvokeWithDefaults(methodInfo, null, args); } return null; } internal static MethodInfo BestMethod(Type type, string name, object[] args, bool requireStatic = false) { if (type == null) { return null; } return (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == name && (!requireStatic || m.IsStatic) orderby Math.Abs(m.GetParameters().Length - args.Length) select m).FirstOrDefault((MethodInfo m) => Compatible(m.GetParameters(), args)); } private static bool Compatible(ParameterInfo[] parameters, object[] args) { if (args.Length > parameters.Length) { return false; } for (int i = 0; i < args.Length; i++) { if (args[i] != null) { Type type = parameters[i].ParameterType; if (type.IsByRef) { type = type.GetElementType(); } if (!type.IsInstanceOfType(args[i]) && !CanConvert(args[i].GetType(), type)) { return false; } } } return parameters.Skip(args.Length).All((ParameterInfo p) => p.IsOptional || p.HasDefaultValue || !p.ParameterType.IsValueType); } private static bool CanConvert(Type source, Type target) { if (!(source == target)) { if (source.IsPrimitive) { return target.IsPrimitive; } return false; } return true; } internal static object InvokeWithDefaults(MethodInfo method, object instance, params object[] supplied) { ParameterInfo[] parameters = method.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { if (i < supplied.Length) { array[i] = ConvertValue(supplied[i], parameters[i].ParameterType); continue; } if (parameters[i].HasDefaultValue) { array[i] = parameters[i].DefaultValue; continue; } Type type = (parameters[i].ParameterType.IsByRef ? parameters[i].ParameterType.GetElementType() : parameters[i].ParameterType); array[i] = (type.IsValueType ? Activator.CreateInstance(type) : null); } return method.Invoke(instance, array); } private static object ConvertValue(object value, Type target) { if (value == null) { return null; } if (target.IsByRef) { target = target.GetElementType(); } if (target.IsInstanceOfType(value)) { return value; } try { return Convert.ChangeType(value, target); } catch { return value; } } internal static Component Component(GameObject gameObject, string typeName) { Type type = Type(typeName); if (!(type == null) && !((Object)(object)gameObject == (Object)null)) { return gameObject.GetComponent(type); } return null; } internal static Object[] Find(string typeName) { Type type = Type(typeName); if (!(type == null)) { return Object.FindObjectsOfType(type); } return (Object[])(object)new Object[0]; } internal static IEnumerable<Component> FindNear(string typeName, Vector3 centre, float radius) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Type type = Type(typeName); if (type == null) { yield break; } HashSet<int> seen = new HashSet<int>(); Collider[] colliders = Physics.OverlapSphere(centre, radius); int i = 0; while (i < colliders.Length) { Collider val = colliders[i]; if (!((Object)(object)val == (Object)null)) { Component val2 = ((Component)val).GetComponentInParent(type); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).GetComponent(type); } if ((Object)(object)val2 != (Object)null && seen.Add(((Object)val2).GetInstanceID())) { yield return val2; } } int num = i + 1; i = num; } } internal static object Zdo(GameObject gameObject) { return Call(Component(gameObject, "ZNetView"), "GetZDO"); } internal static bool IsOwner(GameObject gameObject) { object obj = Call(Component(gameObject, "ZNetView"), "IsOwner"); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } internal static void ClaimOwnership(GameObject gameObject) { object instance = Component(gameObject, "ZNetView"); object obj = Call(instance, "IsOwner"); if (obj is bool && !(bool)obj) { Call(instance, "ClaimOwnership"); } } internal static bool ZBool(GameObject gameObject, string key, bool fallback = false) { object obj = ZGet(gameObject, "GetBool", key, fallback); if (obj is bool) { return (bool)obj; } return fallback; } internal static string ZString(GameObject gameObject, string key, string fallback = "") { return (ZGet(gameObject, "GetString", key, fallback) as string) ?? fallback; } internal static int ZInt(GameObject gameObject, string key, int fallback = 0) { object obj = ZGet(gameObject, "GetInt", key, fallback); if (obj is int) { return (int)obj; } return fallback; } internal static float ZFloat(GameObject gameObject, string key, float fallback = 0f) { object obj = ZGet(gameObject, "GetFloat", key, fallback); if (obj is float) { return (float)obj; } return fallback; } internal static Vector3 ZVector(GameObject gameObject, string key, Vector3 fallback) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_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_0026: Unknown result type (might be due to invalid IL or missing references) object obj = ZGet(gameObject, "GetVec3", key, fallback); if (obj is Vector3) { return (Vector3)obj; } return fallback; } internal static void ZSet(GameObject gameObject, string key, object value) { object obj = Zdo(gameObject); if (obj == null) { return; } object[] supplied = new object[2] { StableHash(key), value }; MethodInfo methodInfo = HashedMethod(obj.GetType(), "Set", value); if (methodInfo != null) { InvokeWithDefaults(methodInfo, obj, supplied); return; } MethodInfo methodInfo2 = KeyedMethod(obj.GetType(), "Set", typeof(string), value); if (methodInfo2 != null) { InvokeWithDefaults(methodInfo2, obj, key, value); } } private static object ZGet(GameObject gameObject, string methodName, string key, object fallback) { object obj = Zdo(gameObject); if (obj == null) { return fallback; } object[] supplied = new object[2] { StableHash(key), fallback }; MethodInfo methodInfo = HashedMethod(obj.GetType(), methodName, fallback); if (methodInfo != null) { return InvokeWithDefaults(methodInfo, obj, supplied); } MethodInfo methodInfo2 = KeyedMethod(obj.GetType(), methodName, typeof(string), fallback); if (!(methodInfo2 != null)) { return fallback; } return InvokeWithDefaults(methodInfo2, obj, key, fallback); } private static MethodInfo HashedMethod(Type type, string name, object value) { return KeyedMethod(type, name, typeof(int), value); } private static MethodInfo KeyedMethod(Type type, string name, Type keyType, object value) { Type valueType = value?.GetType(); return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != name) { return false; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != 2 || parameters[0].ParameterType != keyType) { return false; } Type type2 = parameters[1].ParameterType; if (type2.IsByRef) { type2 = type2.GetElementType(); } return (value != null) ? (type2 == valueType || type2.IsInstanceOfType(value)) : (!type2.IsValueType); }); } internal static int StableHash(string text) { int num = 5381; int num2 = num; for (int i = 0; i < text.Length && text[i] != 0; i += 2) { num = ((num << 5) + num) ^ text[i]; if (i == text.Length - 1 || text[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ text[i + 1]; } return num + num2 * 1566083941; } internal static void DestroyNetworked(GameObject gameObject) { if (!((Object)(object)gameObject == (Object)null)) { Type type = Type("ZNetScene"); object obj = StaticProperty(type, "instance") ?? StaticProperty(type, "m_instance"); if (obj != null) { Call(obj, "Destroy", gameObject); } else { Object.Destroy((Object)(object)gameObject); } } } internal static string CleanName(GameObject go) { if (!((Object)(object)go == (Object)null)) { return ((Object)go).name.Replace("(Clone)", "").Trim(); } return ""; } } public sealed class FireKeeperPostMarker : MonoBehaviour, TextReceiver { private const string NameKey = "fw_firekeeper_post_name"; private const string StorageNameKey = "fw_firekeeper_storage_name"; private const string StoragePositionKey = "fw_firekeeper_storage_pos"; private const string StorageLinkedKey = "fw_firekeeper_storage_linked"; private const string StatusKey = "fw_firekeeper_status"; private const string FueledKey = "fw_firekeeper_fueled"; private static readonly HashSet<FireKeeperPostMarker> Active = new HashSet<FireKeeperPostMarker>(); private readonly List<Component> _fires = new List<Component>(); private bool _editStorage; private bool _queuedEdit; private float _lastInteraction; private float _nextScan; private float _nextWork; private int _fireCursor; private object _textUser; internal string PostName => R.ZString(((Component)this).gameObject, "fw_firekeeper_post_name"); internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_firekeeper_status", "Inactive — no Fire Keeper assigned through a Fire Keeper Bunk"); private void OnEnable() { Active.Add(this); } private void OnDisable() { Active.Remove(this); } private void Start() { _nextWork = Time.time + Random.Range(0.5f, 1.5f); _nextScan = 0f; } private void LateUpdate() { if (!_queuedEdit) { return; } _queuedEdit = false; try { string text = (_editStorage ? "Fuel chest name" : "Fire Keeper post name"); if ((Object)(object)TextInput.instance != (Object)null) { TextInput.instance.RequestText((TextReceiver)(object)this, text, 32); } else { Plugin.Message(_textUser, "The text-entry window is unavailable."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not open Fire Keeper Post editor: " + ex.Message)); Plugin.Message(_textUser, "The Fire Keeper Post editor could not be opened."); } } private void Update() { if (Time.time < _nextWork) { return; } _nextWork = Time.time + Mathf.Max(0.5f, Plugin.FireKeeperWorkInterval.Value); if (!R.IsOwner(((Component)this).gameObject)) { return; } if ((Object)(object)JobStationMarker.FindFireKeeperBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null) { SetStatus("Inactive — no Fire Keeper assigned through a Fire Keeper Bunk"); return; } string problem; NamedContainerMarker namedContainerMarker = Storage(out problem); if ((Object)(object)namedContainerMarker == (Object)null) { SetStatus("Paused — fuel chest " + problem); return; } if (Time.time >= _nextScan) { RefreshFires(); } if (_fires.Count == 0) { SetStatus($"Active — no fuelled fires within {Plugin.FireKeeperRadius.Value:0}m"); return; } object inventory = namedContainerMarker.Inventory; bool flag = false; bool flag2 = false; for (int i = 0; i < _fires.Count; i++) { int num = (_fireCursor + i) % _fires.Count; Component fire = _fires[num]; if (!UsableFire(fire) || !NeedsFuel(fire)) { continue; } flag = true; object obj = FindFuel(inventory, fire); if (obj != null) { flag2 = true; if (TryAddFuel(fire)) { RemoveOne(namedContainerMarker, inventory, obj); _fireCursor = (num + 1) % _fires.Count; R.ZSet(((Component)this).gameObject, "fw_firekeeper_fueled", R.ZInt(((Component)this).gameObject, "fw_firekeeper_fueled") + 1); SetStatus("Active — added " + ItemDisplayName(obj) + " to " + FireDisplayName(fire)); return; } } } if (!flag) { SetStatus($"Active — all {_fires.Count} nearby fires are above the 1-fuel reserve"); } else if (!flag2) { SetStatus("Paused — fuel chest needs fuel accepted by nearby fires"); } else { SetStatus("Paused — could not add fuel to a nearby fire"); } } internal bool Interact(object user, bool hold, bool alt) { if (Time.time - _lastInteraction < 0.45f) { return true; } _lastInteraction = Time.time; if (hold) { return true; } _textUser = user; _editStorage = alt; _queuedEdit = true; return true; } public string GetText() { if (!_editStorage) { return PostName; } return R.ZString(((Component)this).gameObject, "fw_firekeeper_storage_name"); } public void SetText(string text) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) string text2 = PatrolPointMarker.CleanLabel(text); R.ClaimOwnership(((Component)this).gameObject); if (!_editStorage) { R.ZSet(((Component)this).gameObject, "fw_firekeeper_post_name", text2); R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2); Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Fire Keeper Post name cleared." : ("Fire Keeper Post named " + text2 + ".")); return; } R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_name", text2); bool ambiguous; NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.FireKeeperStorageRange.Value, out ambiguous); if ((Object)(object)namedContainerMarker != (Object)null) { R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_pos", ((Component)namedContainerMarker).transform.position); R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_linked", true); Plugin.Message(_textUser, "Fuel chest linked to " + namedContainerMarker.StorageName + "."); } else { R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_linked", false); string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? "Fuel chest link cleared." : $"No storage named '{text2}' is within {Plugin.FireKeeperStorageRange.Value:0}m.")); Plugin.Message(_textUser, text3); } } internal string HoverText() { string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>")); return "Fuling Fire Keeper Post\nName: " + text + "\nFuel chest: " + StorageDisplay() + "\n" + string.Format("Range: {0:0}m\nStatus: {1}\nFires fueled: {2}\n", Plugin.FireKeeperRadius.Value, CurrentStatus, R.ZInt(((Component)this).gameObject, "fw_firekeeper_fueled")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Shift + E</b></color>] Set fuel chest"; } internal static FireKeeperPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) ambiguous = false; if (string.IsNullOrEmpty(name)) { return null; } FireKeeperPostMarker fireKeeperPostMarker = null; float num = radius * radius; foreach (FireKeeperPostMarker item in Active) { if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase)) { continue; } Vector3 val = ((Component)item).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { if ((Object)(object)fireKeeperPostMarker != (Object)null && (Object)(object)fireKeeperPostMarker != (Object)(object)item) { ambiguous = true; return null; } fireKeeperPostMarker = item; } } return fireKeeperPostMarker; } internal static FireKeeperPostMarker FindAtPosition(Vector3 position) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) FireKeeperPostMarker result = null; float num = 2.25f; foreach (FireKeeperPostMarker item in Active) { if (Usable(item)) { Vector3 val = ((Component)item).transform.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = item; } } } return result; } private NamedContainerMarker Storage(out string problem) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_0133: Unknown result type (might be due to invalid IL or missing references) string text = R.ZString(((Component)this).g