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 Station Tasks v1.1.0
plugins/StationTasks.dll
Decompiled 4 days agousing System; 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 BetrValheim.Drawers; using HarmonyLib; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("StationTasks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("StationTasks")] [assembly: AssemblyTitle("StationTasks")] [assembly: AssemblyVersion("1.1.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 StationTasks { [BepInPlugin("local.valheim.stationtasks", "Station Tasks", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "local.valheim.stationtasks"; internal static Plugin Instance; internal static bool InRefill; internal static StationHandle Current; private static Func<Vector3, Vector3, string, bool> usableMaterial; private static bool reportedError; private Harmony harmony; internal ConfigEntry<KeyCode> Modifier; internal ConfigEntry<float> DefaultRadius; internal TaskWindow Window; private void Awake() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) Instance = this; Modifier = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Task modifier", (KeyCode)306, "Hold this key while interacting with a production station to configure its task."); DefaultRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Defaults", "Stock radius", 20f, new ConfigDescription("Starting stock-count radius for a new task. Each station can override this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>())); harmony = new Harmony("local.valheim.stationtasks"); try { Assembly assembly = typeof(Drawer).Assembly; Type type = assembly.GetType("BetrValheim.Refill.Refiller", throwOnError: true); StockCounter.Initialize(assembly); usableMaterial = AccessTools.MethodDelegate<Func<Vector3, Vector3, string, bool>>(Required(type, "IsUsableMaterial"), (object)null, true); Patch(Required(type, "Tick"), "BeginTick", null, "EndTick"); Patch(Required(type, "ResetPassDiagnostics"), null, "BeginPass"); Patch(Required(type, "Mine"), null, "SelectStation"); Type[] array = new Type[3] { typeof(Smelter), typeof(CookingStation), typeof(Fermenter) }; foreach (Type type2 in array) { Patch(Required(type, "PickMaterial", new Type[3] { typeof(Vector3), typeof(Vector3), type2 }), "PickMaterial"); Patch(Required(type2, "Awake"), null, "StationAwake"); } Patch(Required(type, "TryTake"), "BeforeTake", "AfterTake"); Patch(Required(typeof(Player), "Interact", new Type[3] { typeof(GameObject), typeof(bool), typeof(bool) }), "Interact"); string[] array2 = new string[3] { "OnHoverAddOre", "OnHoverAddFuel", "OnHoverEmptyOre" }; foreach (string name in array2) { Patch(Required(typeof(Smelter), name), null, "Hover"); } array2 = new string[2] { "GetHoverText", "OnHoverFuelSwitch" }; foreach (string name2 in array2) { Patch(Required(typeof(CookingStation), name2), null, "Hover"); } Patch(Required(typeof(Fermenter), "GetHoverText"), null, "Hover"); Window = ((Component)this).gameObject.AddComponent<TaskWindow>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Station Tasks ready. Hold " + ((object)Modifier.Value/*cast due to .constrained prefix*/).ToString() + " and interact with a production station.")); } catch (Exception ex) { harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)("Station Tasks could not integrate with this BetrValheim version; no hooks remain installed. " + ex)); } } private static MethodInfo Required(Type type, string name, Type[] args = null) { return AccessTools.Method(type, name, args, (Type[])null) ?? throw new MissingMethodException(type.FullName, name); } private void Patch(MethodInfo method, string prefix = null, string postfix = null, string finalizer = null) { harmony.Patch((MethodBase)method, Hook(prefix), Hook(postfix), (HarmonyMethod)null, Hook(finalizer), (HarmonyMethod)null); static HarmonyMethod Hook(string name) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (name != null) { return new HarmonyMethod(typeof(Plugin), name, (Type[])null); } return null; } } private void OnDestroy() { if (Object.op_Implicit((Object)(object)Window)) { Window.Close(); } Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private static void BeginTick() { InRefill = true; Current = null; } private static void EndTick() { InRefill = false; Current = null; } private static void BeginPass() { StockCounter.BeginPass(); } private static void StationAwake(Component __instance) { StationHandle.Attach(__instance); } private static void SelectStation(ZNetView nview, bool __result) { if (InRefill) { Current = ((__result && Object.op_Implicit((Object)(object)nview)) ? ((Component)nview).GetComponent<StationHandle>() : null); } } private static bool PickMaterial(Vector3 playerPosition, Vector3 station, ref string __result) { //IL_004d: 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) if (!InRefill || !Object.op_Implicit((Object)(object)Current)) { return true; } __result = null; try { TaskRule rule = Current.Rule; ProductionRecipe productionRecipe = ((rule == null) ? null : Current.Recipe(rule.Input)); if (StockCounter.CanAdd(Current, rule, productionRecipe) && usableMaterial(playerPosition, station, productionRecipe.Input)) { __result = productionRecipe.Input; } } catch (Exception error) { Report(error); } return false; } private static bool BeforeTake(Vector3 playerPosition, Vector3 station, string prefabName, ref bool __result, out ProductionRecipe __state) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) __state = null; if (!InRefill || !Object.op_Implicit((Object)(object)Current) || !Current.Valid) { return true; } bool flag = Current.Fuel == prefabName; if (!flag) { __state = Current.Recipe(prefabName); } try { TaskRule rule = Current.Rule; ProductionRecipe productionRecipe = ((rule == null) ? null : Current.Recipe(rule.Input)); if ((!flag) ? (prefabName == rule?.Input && StockCounter.CanAdd(Current, rule, productionRecipe)) : RefillPolicy.AllowFuel(rule, productionRecipe != null, Current.NeedsFuel(), StockCounter.CanAdd(Current, rule, productionRecipe) && usableMaterial(playerPosition, station, productionRecipe.Input))) { return true; } } catch (Exception error) { Report(error); } __result = false; return false; } private static void AfterTake(bool __result, ProductionRecipe __state) { if (__result && InRefill && Object.op_Implicit((Object)(object)Current) && __state != null) { StockCounter.Reserve(Current, __state); } } private static void Report(Exception error) { if (!reportedError) { reportedError = true; ((BaseUnityPlugin)Instance).Logger.LogError((object)("A station task check failed. Pulling for the affected task is stopped. " + error)); } } private static bool Interact(Player __instance, GameObject go, bool hold) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)Instance.Window)) { return true; } if (Instance.Window.IsOpen) { return false; } if (!Input.GetKey(Instance.Modifier.Value)) { return true; } StationHandle stationHandle = StationHandle.Find(go); if (!Object.op_Implicit((Object)(object)stationHandle) || !stationHandle.Valid || stationHandle.Recipes.Count == 0) { return true; } if (!hold && stationHandle.MayEdit()) { Instance.Window.Open(stationHandle); } return false; } private static void Hover(Component __instance, ref string __result) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) StationHandle component = __instance.GetComponent<StationHandle>(); if (Object.op_Implicit((Object)(object)component) && component.Valid && component.Recipes.Count != 0) { TaskRule rule = component.Rule; string text = "Idle (target 0) — configure task"; if (component.HasTask) { ProductionRecipe productionRecipe = ((rule == null) ? null : component.Recipe(rule.Input)); text = ((productionRecipe == null) ? "Invalid task — edit or reset" : (((rule.Target == 0) ? "Idle: " : (rule.Paused ? "Paused: " : "Maintain: ")) + ProductionRecipe.Local(productionRecipe.To.m_itemData.m_shared.m_name) + " ×" + rule.Target)); } __result = __result + "\n<color=#ffd479>[" + ((object)Instance.Modifier.Value/*cast due to .constrained prefix*/).ToString() + " + Use]</color> " + text; } } } public sealed class ProductionRecipe { public ItemDrop From; public ItemDrop To; public int Amount; public string Input => ((Object)From).name; public string Output => ((Object)To).name; public string Label => Local(From.m_itemData.m_shared.m_name) + " → " + Local(To.m_itemData.m_shared.m_name) + ((Amount > 1) ? (" ×" + Amount) : ""); public static string Local(string value) { if (Localization.instance == null) { return value; } return Localization.instance.Localize(value); } } public sealed class StationHandle : MonoBehaviour { public const string TaskKey = "codex_station_tasks_v1"; internal static readonly List<StationHandle> All = new List<StationHandle>(); private static readonly FieldInfo DelayedTap = AccessTools.Field(typeof(Fermenter), "m_delayedTapItem"); public ZNetView View; public Smelter Smelter; public CookingStation Cooker; public Fermenter Fermenter; public readonly List<ProductionRecipe> Recipes = new List<ProductionRecipe>(); private string cachedValue; private TaskRule cachedRule; public string Title => ProductionRecipe.Local(Object.op_Implicit((Object)(object)Smelter) ? Smelter.m_name : (Object.op_Implicit((Object)(object)Cooker) ? Cooker.m_name : Fermenter.m_name)); public bool Valid { get { if (Object.op_Implicit((Object)(object)View)) { return View.IsValid(); } return false; } } public bool HasTask { get { if (Valid) { return !string.IsNullOrEmpty(View.GetZDO().GetString("codex_station_tasks_v1", "")); } return false; } } public TaskRule Rule { get { string text = (Valid ? View.GetZDO().GetString("codex_station_tasks_v1", "") : ""); if (text != cachedValue) { cachedValue = text; TaskRule.TryDecode(text, out cachedRule); } return cachedRule; } } public string Fuel { get { if (!Object.op_Implicit((Object)(object)Smelter) || !Object.op_Implicit((Object)(object)Smelter.m_fuelItem)) { if (!Object.op_Implicit((Object)(object)Cooker) || !Cooker.m_useFuel || !Object.op_Implicit((Object)(object)Cooker.m_fuelItem)) { return null; } return ((Object)Cooker.m_fuelItem).name; } return ((Object)Smelter.m_fuelItem).name; } } private void Awake() { View = ((Component)this).GetComponent<ZNetView>(); Smelter = ((Component)this).GetComponent<Smelter>(); Cooker = ((Component)this).GetComponent<CookingStation>(); Fermenter = ((Component)this).GetComponent<Fermenter>(); if (Object.op_Implicit((Object)(object)Smelter)) { foreach (ItemConversion item in Smelter.m_conversion) { Add(item.m_from, item.m_to, 1); } } if (Object.op_Implicit((Object)(object)Cooker)) { foreach (ItemConversion item2 in Cooker.m_conversion) { Add(item2.m_from, item2.m_to, 1); } } if (Object.op_Implicit((Object)(object)Fermenter)) { foreach (ItemConversion item3 in Fermenter.m_conversion) { Add(item3.m_from, item3.m_to, item3.m_producedItems); } } All.Add(this); } private void Add(ItemDrop from, ItemDrop to, int amount) { if (Object.op_Implicit((Object)(object)from) && Object.op_Implicit((Object)(object)to) && amount > 0 && !Recipes.Any((ProductionRecipe r) => r.Input == ((Object)from).name)) { Recipes.Add(new ProductionRecipe { From = from, To = to, Amount = amount }); } } private void OnDestroy() { All.Remove(this); } public ProductionRecipe Recipe(string input) { return Recipes.Find((ProductionRecipe r) => r.Input == input); } public static StationHandle Find(GameObject go) { if (!Object.op_Implicit((Object)(object)go)) { return null; } StationHandle componentInParent = go.GetComponentInParent<StationHandle>(); if (Object.op_Implicit((Object)(object)componentInParent)) { return componentInParent; } Component componentInParent2 = (Component)(object)go.GetComponentInParent<Smelter>(); if (!Object.op_Implicit((Object)(object)componentInParent2)) { componentInParent2 = (Component)(object)go.GetComponentInParent<CookingStation>(); } if (!Object.op_Implicit((Object)(object)componentInParent2)) { componentInParent2 = (Component)(object)go.GetComponentInParent<Fermenter>(); } if (!Object.op_Implicit((Object)(object)componentInParent2)) { return null; } return Attach(componentInParent2); } public static StationHandle Attach(Component station) { return station.GetComponent<StationHandle>() ?? station.gameObject.AddComponent<StationHandle>(); } public bool MayEdit() { //IL_0024: 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) //IL_0039: 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) Player localPlayer = Player.m_localPlayer; if (Valid && Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead()) { Vector3 val = ((Component)localPlayer).transform.position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude <= 100f) { return PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false); } } return false; } public bool Save(TaskRule rule) { if (!MayEdit() || (rule != null && Recipe(rule.Input) == null)) { return false; } View.ClaimOwnership(); if (!View.IsOwner()) { return false; } View.GetZDO().Set("codex_station_tasks_v1", rule?.Encode() ?? ""); StockCounter.Invalidate(); return true; } public long Queued(string output) { if (!Valid) { return 0L; } ZDO zDO = View.GetZDO(); long num = 0L; if (Object.op_Implicit((Object)(object)Smelter)) { int num2 = zDO.GetInt(ZDOVars.s_queued, 0); for (int i = 0; i < num2; i++) { if (Recipe(zDO.GetString("item" + i, ""))?.Output == output) { num++; } } if (Recipe(zDO.GetString(ZDOVars.s_spawnOre, ""))?.Output == output) { num += zDO.GetInt(ZDOVars.s_spawnAmount, 0); } } if (Object.op_Implicit((Object)(object)Cooker)) { for (int j = 0; j < Cooker.m_slots.Length; j++) { if (zDO.GetInt("slotstatus" + j, 0) != 2) { string text = zDO.GetString("slot" + j, ""); if (text == output || Recipe(text)?.Output == output) { num++; } } } } if (Object.op_Implicit((Object)(object)Fermenter)) { int num3 = zDO.GetInt(ZDOVars.s_content, 0); int num4 = (((MonoBehaviour)Fermenter).IsInvoking("DelayedTap") ? ((int)DelayedTap.GetValue(Fermenter)) : 0); foreach (ProductionRecipe recipe in Recipes) { if (recipe.Output == output) { int stableHashCode = StringExtensionMethods.GetStableHashCode(recipe.Input); if (stableHashCode == num3) { num += recipe.Amount; } if (stableHashCode == num4) { num += recipe.Amount; } } } } return num; } public bool NeedsFuel() { if (!Valid) { return false; } ZDO zDO = View.GetZDO(); if (Object.op_Implicit((Object)(object)Smelter)) { return zDO.GetInt(ZDOVars.s_queued, 0) > 0; } if (Object.op_Implicit((Object)(object)Cooker)) { for (int i = 0; i < Cooker.m_slots.Length; i++) { if (!string.IsNullOrEmpty(zDO.GetString("slot" + i, "")) && zDO.GetInt("slotstatus" + i, 0) == 0) { return true; } } } return false; } } public static class StockCounter { private static Func<List<Container>> containers; private static Func<List<Drawer>> drawers; private static Func<Container, bool> mayOpen; private static Func<List<ItemDrop>> groundItems; private static readonly Dictionary<(int, string), long> Stored = new Dictionary<(int, string), long>(); private static readonly Dictionary<(int, string), long> Baseline = new Dictionary<(int, string), long>(); private static readonly Dictionary<(int, string), long> Reserved = new Dictionary<(int, string), long>(); public static void Initialize(Assembly assembly) { Type type = assembly.GetType("BetrValheim.Core.ContainerIndex", throwOnError: true); FieldInfo field = AccessTools.Field(type, "Known") ?? throw new MissingFieldException("ContainerIndex.Known"); containers = () => (List<Container>)field.GetValue(null); mayOpen = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(type, "PlayerMayOpen", (Type[])null, (Type[])null), (object)null, true); drawers = AccessTools.MethodDelegate<Func<List<Drawer>>>(AccessTools.PropertyGetter(assembly.GetType("BetrValheim.Drawers.DrawerIndex", throwOnError: true), "All"), (object)null, true); FieldInfo groundField = AccessTools.Field(typeof(ItemDrop), "s_instances") ?? throw new MissingFieldException("ItemDrop.s_instances"); groundItems = () => (List<ItemDrop>)groundField.GetValue(null); } public static void Invalidate() { Stored.Clear(); Baseline.Clear(); Reserved.Clear(); } public static void BeginPass() { Invalidate(); foreach (StationHandle item in StationHandle.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Valid) { continue; } foreach (ProductionRecipe recipe in item.Recipes) { (int, string) key = (((Object)item).GetInstanceID(), recipe.Output); if (!Baseline.ContainsKey(key)) { Baseline[key] = item.Queued(recipe.Output); } } } } public static void Reserve(StationHandle station, ProductionRecipe recipe) { (int, string) key = (((Object)station).GetInstanceID(), recipe.Output); Reserved.TryGetValue(key, out var value); Reserved[key] = value + recipe.Amount; } public static long CountStored(StationHandle station, ProductionRecipe recipe, float radius, bool cache) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: 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) (int, string) key = (((Object)station).GetInstanceID(), recipe.Output); if (cache && Stored.TryGetValue(key, out var value)) { return value; } Vector3 position = ((Component)station).transform.position; float num = radius * radius; long num2 = 0L; Vector3 val; foreach (Container item in containers()) { if (!Object.op_Implicit((Object)(object)item)) { continue; } val = ((Component)item).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude > num || !mayOpen(item)) { continue; } Inventory inventory = item.GetInventory(); if (inventory == null) { continue; } foreach (ItemData allItem in inventory.GetAllItems()) { if (Object.op_Implicit((Object)(object)allItem.m_dropPrefab) && ((Object)allItem.m_dropPrefab).name == recipe.Output) { num2 += allItem.m_stack; } } } foreach (Drawer item2 in drawers()) { if (Object.op_Implicit((Object)(object)item2)) { val = ((Component)item2).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num && PrivateArea.CheckAccess(((Component)item2).transform.position, 0f, false, false) && item2.ItemName == recipe.Output) { num2 += item2.Count; } } } foreach (ItemDrop item3 in groundItems()) { if (Object.op_Implicit((Object)(object)item3)) { val = ((Component)item3).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num && item3.m_itemData != null && Utils.GetPrefabName(((Component)item3).gameObject) == recipe.Output) { num2 += item3.m_itemData.m_stack; } } } if (cache) { Stored[key] = num2; } return num2; } public static long CountQueued(StationHandle station, ProductionRecipe recipe, float radius, bool reservations) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) long num = 0L; float num2 = radius * radius; foreach (StationHandle item in StationHandle.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Valid) { continue; } Vector3 val = ((Component)item).transform.position - ((Component)station).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > num2)) { long num3 = item.Queued(recipe.Output); (int, string) key = (((Object)item).GetInstanceID(), recipe.Output); if (reservations && Baseline.TryGetValue(key, out var value)) { Reserved.TryGetValue(key, out var value2); num3 = QueueMath.Projected(num3, value, value2); } num += num3; } } return num; } public static bool CanAdd(StationHandle station, TaskRule rule, ProductionRecipe recipe) { if (rule != null && rule.Target > 0 && !rule.Paused && recipe != null) { return rule.CanAdd(recipe.Input, CountStored(station, recipe, rule.Radius, cache: true), CountQueued(station, recipe, rule.Radius, reservations: true), recipe.Amount); } return false; } } public sealed class TaskRule { public string Input; public int Target; public float Radius; public bool Paused; public string Encode() { return string.Join("|", "1", Input, Target.ToString(CultureInfo.InvariantCulture), Radius.ToString(CultureInfo.InvariantCulture), Paused ? "1" : "0"); } public static bool TryDecode(string value, out TaskRule rule) { rule = null; if (string.IsNullOrEmpty(value)) { return false; } string[] array = value.Split(new char[1] { '|' }); if (array.Length != 5 || array[0] != "1" || string.IsNullOrWhiteSpace(array[1]) || !int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || result < 0 || result > 1000000 || float.IsNaN(result2) || float.IsInfinity(result2) || result2 < 1f || result2 > 200f || (array[4] != "0" && array[4] != "1")) { return false; } rule = new TaskRule { Input = array[1], Target = result, Radius = result2, Paused = (array[4] == "1") }; return true; } public bool CanAdd(string input, long stored, long queued, int batchSize) { if (!Paused && input == Input && batchSize > 0) { return Math.Max(0L, stored) + Math.Max(0L, queued) + batchSize <= Target; } return false; } } public static class RefillPolicy { public static bool AllowFuel(TaskRule rule, bool hasRecipe, bool loadedWork, bool canStart) { if (rule != null && rule.Target > 0 && hasRecipe) { return loadedWork || canStart; } return false; } } public static class QueueMath { public static long Projected(long observed, long baseline, long reserved) { return Math.Max(observed, baseline + reserved); } } public sealed class TaskWindow : MonoBehaviour { private const float Width = 860f; private const float Height = 652f; private static readonly Vector2 TopLeft = new Vector2(0f, 1f); private StationHandle station; private int selected; private GameObject root; private RectTransform panel; private InputField targetField; private InputField radiusField; private Toggle pauseToggle; private Text stockText; private Text queueText; private Text feedback; private Text stateText; private Text batchText; private Text saveText; private Button saveButton; private readonly List<Button> recipeButtons = new List<Button>(); private readonly List<Image> recipeMarkers = new List<Image>(); private float nextStatus; private GUIManager Gui => GUIManager.Instance; private Color Gold => Gui.ValheimOrange; private Color Beige => Gui.ValheimBeige; public bool IsOpen { get; private set; } public void Open(StationHandle value) { Close(); if (!Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront) || value.Recipes.Count == 0) { return; } station = value; TaskRule rule = station.Rule; selected = ((rule != null) ? Math.Max(0, station.Recipes.FindIndex((ProductionRecipe r) => r.Input == rule.Input)) : 0); try { Build(rule); IsOpen = true; GUIManager.BlockInput(true); RefreshSelection(); nextStatus = 0f; } catch { Close(); throw; } } public void Close() { if (IsOpen) { GUIManager.BlockInput(false); } IsOpen = false; if (Object.op_Implicit((Object)(object)root)) { root.SetActive(false); Object.Destroy((Object)(object)root); } root = null; station = null; recipeButtons.Clear(); recipeMarkers.Clear(); } private void OnDestroy() { Close(); } private void Build(TaskRule rule) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_051c: Unknown result type (might be due to invalid IL or missing references) //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_0636: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) root = new GameObject("StationTasks", new Type[2] { typeof(RectTransform), typeof(Image) }); root.transform.SetParent(GUIManager.CustomGUIFront.transform, false); root.transform.SetAsLastSibling(); RectTransform component = root.GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; Vector2 offsetMin = (component.offsetMax = Vector2.zero); component.offsetMin = offsetMin; ((Graphic)root.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.58f); panel = Gui.CreateWoodpanel(root.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 860f, 652f, false).GetComponent<RectTransform>(); ((Object)((Component)panel).gameObject).name = "StationTaskWoodPanel"; Label((Transform)(object)panel, station.Title, 32f, 20f, 714f, 48f, 36, Gold, Gui.NorseBold); Label((Transform)(object)panel, "STATION TASK", 34f, 75f, 440f, 26f, 19, Beige); stateText = Label((Transform)(object)panel, "IDLE", 624f, 75f, 188f, 26f, 19, Gold); stateText.alignment = (TextAnchor)5; MakeButton((Transform)(object)panel, "×", 790f, 25f, 40f, 36f, Close); Fill((Transform)(object)panel, 30f, 111f, 800f, 1f, new Color(0.85f, 0.64f, 0.3f, 0.55f)); Fill((Transform)(object)panel, 407f, 128f, 1f, 376f, new Color(0.85f, 0.64f, 0.3f, 0.25f)); Label((Transform)(object)panel, "RECIPE", 34f, 128f, 344f, 26f, 20, Gold); BuildRecipeList(); Label((Transform)(object)panel, "KEEP IN STOCK", 434f, 128f, 374f, 26f, 20, Gold); targetField = MakeInput("Target", 434f, 163f, 210f, 52f, (ContentType)2, (rule?.Target ?? 0).ToString(CultureInfo.InvariantCulture), 7, 26); Label((Transform)(object)panel, "finished items", 659f, 172f, 157f, 32f, 18, Beige); Label((Transform)(object)panel, "0 keeps this station idle. Set a target to start production.", 434f, 224f, 382f, 46f, 17, Beige); Label((Transform)(object)panel, "Stock radius", 434f, 279f, 234f, 32f, 20, Beige); radiusField = MakeInput("Radius", 679f, 273f, 99f, 42f, (ContentType)3, (rule?.Radius ?? Plugin.Instance.DefaultRadius.Value).ToString(CultureInfo.InvariantCulture), 6, 21); Label((Transform)(object)panel, "m", 790f, 280f, 30f, 30f, 19, Beige); Fill((Transform)(object)panel, 430f, 330f, 392f, 80f, new Color(0f, 0f, 0f, 0.3f)); Label((Transform)(object)panel, "Available nearby", 444f, 339f, 250f, 28f, 18, Beige); stockText = Label((Transform)(object)panel, "—", 700f, 339f, 108f, 28f, 22, Gold); stockText.alignment = (TextAnchor)5; Label((Transform)(object)panel, "In production", 444f, 375f, 250f, 28f, 18, Beige); queueText = Label((Transform)(object)panel, "—", 700f, 375f, 108f, 28f, 22, Gold); queueText.alignment = (TextAnchor)5; GameObject val = Gui.CreateToggle((Transform)(object)panel, 26f, 26f); Place(val.GetComponent<RectTransform>(), 434f, 429f, 26f, 26f); pauseToggle = val.GetComponent<Toggle>(); Text componentInChildren = val.GetComponentInChildren<Text>(); if (Object.op_Implicit((Object)(object)componentInChildren)) { componentInChildren.text = ""; } pauseToggle.SetIsOnWithoutNotify(rule?.Paused ?? false); Label((Transform)(object)panel, "Pause new production", 473f, 426f, 344f, 34f, 19, Beige); batchText = Label((Transform)(object)panel, "", 434f, 470f, 380f, 44f, 16, Beige); Label((Transform)(object)panel, "Choose the item this station will make.", 34f, 505f, 350f, 28f, 16, Beige); feedback = Label((Transform)(object)panel, "", 34f, 535f, 790f, 24f, 16, new Color(1f, 0.56f, 0.38f)); MakeButton((Transform)(object)panel, "Reset to idle", 32f, 571f, 210f, 44f, ResetTask); saveButton = MakeButton((Transform)(object)panel, "Save as idle", 433f, 571f, 195f, 44f, Save); saveText = ((Component)saveButton).GetComponentInChildren<Text>(); MakeButton((Transform)(object)panel, "Cancel", 646f, 571f, 182f, 44f, Close); Label((Transform)(object)panel, "Ctrl + Use to configure · Escape to close", 32f, 623f, 796f, 22f, 15, Beige).alignment = (TextAnchor)4; ((UnityEvent<string>)(object)targetField.onValueChanged).AddListener((UnityAction<string>)delegate { RefreshDraft(); }); ((UnityEvent<string>)(object)radiusField.onValueChanged).AddListener((UnityAction<string>)delegate { nextStatus = 0f; RefreshDraft(); }); ((UnityEvent<bool>)(object)pauseToggle.onValueChanged).AddListener((UnityAction<bool>)delegate { RefreshDraft(); }); Canvas.ForceUpdateCanvases(); FitToScreen(); } private void BuildRecipeList() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) GameObject obj = DefaultControls.CreateScrollView(default(Resources)); ((Object)obj).name = "Recipes"; obj.transform.SetParent((Transform)(object)panel, false); Place(obj.GetComponent<RectTransform>(), 32f, 163f, 358f, 334f); ScrollRect component = obj.GetComponent<ScrollRect>(); component.horizontal = false; component.movementType = (MovementType)2; ((Component)component.horizontalScrollbar).gameObject.SetActive(false); component.horizontalScrollbar = null; component.verticalScrollbarVisibility = (ScrollbarVisibility)2; Gui.ApplyScrollRectStyle(component); component.content.anchorMin = new Vector2(0f, 1f); component.content.anchorMax = new Vector2(1f, 1f); component.content.pivot = TopLeft; component.content.anchoredPosition = Vector2.zero; component.content.sizeDelta = new Vector2(0f, (float)(station.Recipes.Count * 70 + 8)); for (int i = 0; i < station.Recipes.Count; i++) { int index = i; ProductionRecipe productionRecipe = station.Recipes[i]; Button val = MakeButton((Transform)(object)component.content, "", 4f, 4 + i * 70, 330f, 64f, delegate { selected = index; RefreshSelection(); }); recipeButtons.Add(val); Image item = Fill(((Component)val).transform, 2f, 3f, 3f, 58f, Gold); recipeMarkers.Add(item); Image obj2 = Fill(((Component)val).transform, 12f, 12f, 40f, 40f, Color.white); obj2.sprite = productionRecipe.To.m_itemData.GetIcon(); obj2.preserveAspect = true; Label(((Component)val).transform, ProductionRecipe.Local(productionRecipe.To.m_itemData.m_shared.m_name), 64f, 6f, 254f, 28f, 20, Gold); Label(((Component)val).transform, "From " + ProductionRecipe.Local(productionRecipe.From.m_itemData.m_shared.m_name), 64f, 33f, 254f, 25f, 16, Beige); } component.verticalNormalizedPosition = 1f; } private void RefreshSelection() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < recipeButtons.Count; i++) { ColorBlock valheimButtonColorBlock = Gui.ValheimButtonColorBlock; if (i == selected) { ((ColorBlock)(ref valheimButtonColorBlock)).normalColor = new Color(1f, 0.81f, 0.48f); } ((Selectable)recipeButtons[i]).colors = valheimButtonColorBlock; ((Behaviour)recipeMarkers[i]).enabled = i == selected; } ProductionRecipe productionRecipe = station.Recipes[selected]; batchText.text = ((productionRecipe.Amount > 1) ? $"Produces {productionRecipe.Amount} per batch. A whole batch must fit below your target." : "Counts nearby storage, finished drops and queued production."); nextStatus = 0f; RefreshDraft(); } private bool ReadDraft(out TaskRule rule) { return TaskRule.TryDecode("1|" + station.Recipes[selected].Input + "|" + targetField.text + "|" + radiusField.text + "|" + (pauseToggle.isOn ? "1" : "0"), out rule); } private void RefreshDraft() { TaskRule rule; bool flag = ReadDraft(out rule); ((Selectable)saveButton).interactable = flag; feedback.text = (flag ? "" : "Enter a target from 0 to 1,000,000 and a radius from 1 to 200."); stateText.text = ((!flag) ? "CHECK VALUES" : ((rule.Target == 0) ? "IDLE" : (rule.Paused ? "PAUSED" : "MAINTAIN STOCK"))); saveText.text = ((!flag) ? "Save task" : ((rule.Target == 0) ? "Save as idle" : (rule.Paused ? "Save paused" : "Start task"))); } private void Save() { if (!ReadDraft(out var rule)) { RefreshDraft(); } else if (!station.Save(rule)) { feedback.text = "Could not save. Move closer and check ward access."; } else { Close(); } } private void ResetTask() { if (!station.Save(null)) { feedback.text = "Could not reset. Move closer and check ward access."; } else { Close(); } } private void Update() { if (!IsOpen) { return; } if (!Object.op_Implicit((Object)(object)root) || !Object.op_Implicit((Object)(object)station) || !station.MayEdit() || Input.GetKeyDown((KeyCode)27)) { Close(); return; } FitToScreen(); if (Time.unscaledTime < nextStatus) { return; } nextStatus = Time.unscaledTime + 0.5f; if (!ReadDraft(out var rule)) { Text obj = stockText; string text = (queueText.text = "—"); obj.text = text; return; } ProductionRecipe recipe = station.Recipes[selected]; try { stockText.text = StockCounter.CountStored(station, recipe, rule.Radius, cache: false).ToString(); queueText.text = StockCounter.CountQueued(station, recipe, rule.Radius, reservations: false).ToString(); } catch (Exception) { Text obj2 = stockText; string text = (queueText.text = "—"); obj2.text = text; feedback.text = "Stock count unavailable. Automatic pulling stops if a task check fails."; } } private void FitToScreen() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) Rect rect = root.GetComponent<RectTransform>().rect; float num = Mathf.Min(new float[3] { 1f, (((Rect)(ref rect)).width - 24f) / 860f, (((Rect)(ref rect)).height - 24f) / 652f }); ((Transform)panel).localScale = Vector3.one * Mathf.Max(0.2f, num); } private Text Label(Transform parent, string text, float x, float y, float width, float height, int size, Color color, Font font = null) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Text component = Gui.CreateText(text, parent, TopLeft, TopLeft, Vector2.zero, font ?? Gui.AveriaSerifBold, size, color, true, Color.black, width, height, false).GetComponent<Text>(); Place(((Graphic)component).rectTransform, x, y, width, height); component.alignment = (TextAnchor)3; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; ((Graphic)component).raycastTarget = false; return component; } private Button MakeButton(Transform parent, string text, float x, float y, float width, float height, Action action) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown Button component = Gui.CreateButton(text, parent, TopLeft, TopLeft, Vector2.zero, width, height).GetComponent<Button>(); Place(((Component)component).GetComponent<RectTransform>(), x, y, width, height); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { action(); }); return component; } private InputField MakeInput(string name, float x, float y, float width, float height, ContentType type, string value, int limit, int size) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) InputField component = Gui.CreateInputField((Transform)(object)panel, TopLeft, TopLeft, Vector2.zero, type, "0", size, width, height).GetComponent<InputField>(); ((Object)((Component)component).gameObject).name = name; Place(((Component)component).GetComponent<RectTransform>(), x, y, width, height); component.characterLimit = limit; component.SetTextWithoutNotify(value); component.textComponent.alignment = (TextAnchor)3; return component; } private static Image Fill(Transform parent, float x, float y, float width, float height, Color color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Accent", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); Place(val.GetComponent<RectTransform>(), x, y, width, height); Image component = val.GetComponent<Image>(); ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; return component; } private static void Place(RectTransform rect, float x, float y, float width, float height) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Vector2 anchorMin = (rect.anchorMax = TopLeft); rect.anchorMin = anchorMin; rect.pivot = TopLeft; rect.anchoredPosition = new Vector2(x, 0f - y); rect.sizeDelta = new Vector2(width, height); } } }