using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyTitle("Forge Pipes")] [assembly: AssemblyFileVersion("1.1.4.0")] [assembly: AssemblyInformationalVersion("1.1.4")] [assembly: AssemblyDescription("Forge Pipes for Valheim")] [assembly: AssemblyVersion("1.1.4.0")] namespace ForgePipes; internal static class AutomationScheduler { private static readonly List work = new List(); private static int cursor; private static float nextCycle; private static float nextMaintenance; private static ZNetScene scene; internal static void Tick() { //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Expected O, but got Unknown //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Expected O, but got Unknown if ((Object)(object)scene != (Object)(object)ZNetScene.instance) { scene = ZNetScene.instance; work.Clear(); cursor = 0; nextCycle = (nextMaintenance = 0f); InventoryWake.Clear(); Routing.Reset(); } long timestamp = Stopwatch.GetTimestamp(); try { if (Time.time >= nextMaintenance) { nextMaintenance = Time.time + 5f; Plugin.Chests.RemoveWhere((Container c) => !Object.op_Implicit((Object)(object)c)); Plugin.Machines.RemoveWhere((Smelter s) => !Object.op_Implicit((Object)(object)s)); Stations.All.RemoveWhere((Component s) => !Object.op_Implicit((Object)(object)s)); Pieces.AttachHammer(); PortFactory.EnsureAll(); InventoryWake.Prune(); } if (Plugin.RunConnectionChecks.Value && Routing.Dirty) { long timestamp2 = Stopwatch.GetTimestamp(); Routing.Rebuild(); PerformanceStats.Section("graph", timestamp2); } if (!Plugin.RunAutomation.Value) { return; } if (cursor >= work.Count) { if (Time.time < nextCycle) { return; } nextCycle = Time.time + 1f; work.Clear(); cursor = 0; foreach (Smelter machine in Plugin.Machines) { if (Object.op_Implicit((Object)(object)machine)) { work.Add((Component)(object)machine); } } foreach (Container chest in Plugin.Chests) { if (Object.op_Implicit((Object)(object)chest)) { work.Add((Component)(object)chest); } } foreach (Component item in Stations.All) { if (Object.op_Implicit((Object)(object)item) && !(item is Smelter)) { work.Add(item); } } if (Plugin.CollectLooseItems.Value) { work.Add(null); } } int num = 0; double num2 = Math.Max(0.25, Math.Min(8f, Plugin.WorkBudgetMs.Value)); while (cursor < work.Count && num++ < 8 && (num <= 1 || !((double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency >= num2))) { Component val = work[cursor++]; long timestamp3 = Stopwatch.GetTimestamp(); try { if (object.ReferenceEquals(val, null)) { LoosePickup.Tick(); PerformanceStats.Section("pickup", timestamp3); } else if (val is Smelter) { Smelter val2 = (Smelter)val; if (Object.op_Implicit((Object)(object)val2) && Automation.Supported(val2)) { MachineHandoff.Prepare((Component)(object)val2); Automation.Feed(val2); } PerformanceStats.Section("smelters", timestamp3); } else if (val is Container) { ChestTransfers.Tick((Container)val); PerformanceStats.Section("chests", timestamp3); } else if (Object.op_Implicit((Object)(object)val)) { Stations.Tick(val); PerformanceStats.Section("stations", timestamp3); } } catch (Exception ex) { Plugin.Error("Automation job failed: " + ex); } } } finally { PerformanceStats.Tick(timestamp); } } } public struct PipeRun { public int Node; public Point A; public Point B; public PipeRun(int node, Point a, Point b) { Node = node; A = a; B = b; } } public static class BodyConnections { private static Point Sub(Point a, Point b) { return new Point(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } private static Point At(Point a, Point delta, float t) { return new Point(a.X + delta.X * t, a.Y + delta.Y * t, a.Z + delta.Z * t); } private static float Clamp(float v) { return Math.Max(0f, Math.Min(1f, v)); } public static float DistanceSquared(PipeRun one, PipeRun two) { Point point = Sub(one.B, one.A); Point point2 = Sub(two.B, two.A); Point b = Sub(one.A, two.A); float num = Point.Dot(point, point); float num2 = Point.Dot(point, point2); float num3 = Point.Dot(point2, point2); float num4 = Point.Dot(point, b); float num5 = Point.Dot(point2, b); if (num < 1E-10f && num3 < 1E-10f) { return Point.DistanceSquared(one.A, two.A); } float num6 = ((num < 1E-10f) ? 0f : ((num3 < 1E-10f) ? Clamp((0f - num4) / num) : ((num * num3 - num2 * num2 > 1E-10f) ? Clamp((num2 * num5 - num3 * num4) / (num * num3 - num2 * num2)) : 0f))); float num7 = ((num3 < 1E-10f) ? 0f : ((num2 * num6 + num5) / num3)); if (num7 < 0f) { num7 = 0f; num6 = ((num < 1E-10f) ? 0f : Clamp((0f - num4) / num)); } else if (num7 > 1f) { num7 = 1f; num6 = ((num < 1E-10f) ? 0f : Clamp((num2 - num4) / num)); } return Point.DistanceSquared(At(one.A, point, num6), At(two.A, point2, num7)); } public static void Connect(NetworkGraph graph, IEnumerable runs) { Dictionary> dictionary = new Dictionary>(); foreach (PipeRun run in runs) { Point point = Sub(run.B, run.A); int num = Math.Max(1, (int)Math.Ceiling(Math.Sqrt(Point.Dot(point, point)) / 0.4)); for (int i = 0; i < num; i++) { PipeRun pipeRun = new PipeRun(run.Node, At(run.A, point, (float)i / (float)num), At(run.A, point, (float)(i + 1) / (float)num)); Point point2 = At(pipeRun.A, Sub(pipeRun.B, pipeRun.A), 0.5f); NetworkGraph.Cell key = new NetworkGraph.Cell { X = (int)Math.Floor(point2.X / 0.5f), Y = (int)Math.Floor(point2.Y / 0.5f), Z = (int)Math.Floor(point2.Z / 0.5f) }; for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { if (!dictionary.TryGetValue(new NetworkGraph.Cell { X = key.X + j, Y = key.Y + k, Z = key.Z + l }, out var value)) { continue; } foreach (PipeRun item in value) { if (item.Node != pipeRun.Node && !graph.Connected(item.Node, pipeRun.Node) && DistanceSquared(item, pipeRun) <= 0.0064f) { graph.Join(item.Node, pipeRun.Node); } } } } } if (!dictionary.TryGetValue(key, out var value2)) { value2 = new List(); dictionary.Add(key, value2); } value2.Add(pipeRun); } } } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] internal static class BuildMenuPatch { private static readonly MethodInfo AddKnown = AccessTools.Method(typeof(Player), "AddKnownPiece", (Type[])null, (Type[])null); private static Player lastPlayer; private static string lastReport; private static bool IsHammer(PieceTable table) { if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if (Object.op_Implicit((Object)(object)itemPrefab) && (Object)(object)itemPrefab.GetComponent().m_itemData.m_shared.m_buildPieces == (Object)(object)table) { return true; } } return table.m_pieces.Any((GameObject p) => Object.op_Implicit((Object)(object)p) && Native.Name(p) == "wood_beam"); } private static void Prefix(PieceTable __instance, HashSet __0, Player __1) { if (!Object.op_Implicit((Object)(object)__1) || (Object)(object)__1 != (Object)(object)Player.m_localPlayer || !IsHammer(__instance)) { return; } GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab("Copper") : null); ItemDrop val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent() : null); bool flag = Object.op_Implicit((Object)(object)val2) && __1.IsKnownMaterial(val2.m_itemData.m_shared.m_name); int num = 0; foreach (GameObject prefab in Plugin.Prefabs) { if (!__instance.m_pieces.Contains(prefab)) { __instance.m_pieces.Add(prefab); } Piece component = prefab.GetComponent(); if (!flag) { __0.Remove(component.m_name); continue; } if (!__0.Contains(component.m_name) && __1.HaveRequirements(component, (RequirementMode)1)) { if (Object.op_Implicit((Object)(object)Hud.instance) && Object.op_Implicit((Object)(object)Hud.instance.m_buildUi) && Object.op_Implicit((Object)(object)MessageHud.instance)) { AddKnown.Invoke(__1, new object[1] { component }); } else { __0.Add(component.m_name); } } if (__0.Contains(component.m_name)) { num++; } } string text = "Hammer registration: " + Plugin.Prefabs.Count + " pipes; " + num + " recipes unlocked. Find them under Crafting / Transport or search 'Forge pipe'."; if (num < Plugin.Prefabs.Count) { text += " Discover Wood, a Copper ingot, and a workbench to unlock the recipes."; } if ((Object)(object)lastPlayer != (Object)(object)__1 || lastReport != text) { Plugin.LogInfo(text); lastPlayer = __1; lastReport = text; } } } public enum ChestFlow { Both, SendOnly, ReceiveOnly } public static class ChestFlowRules { public static ChestFlow Read(int value) { if (value < 0 || value > 2) { return ChestFlow.Both; } return (ChestFlow)value; } public static ChestFlow Next(ChestFlow mode) { return (ChestFlow)((int)(mode + 1) % 3); } public static bool Sends(ChestFlow mode) { return mode != ChestFlow.ReceiveOnly; } public static bool Receives(ChestFlow mode) { return mode != ChestFlow.SendOnly; } public static bool Transfer(ChestFlow source, ChestFlow destination) { if (source != ChestFlow.SendOnly || !Receives(destination)) { if (source == ChestFlow.Both) { return destination == ChestFlow.ReceiveOnly; } return false; } return true; } public static string Label(ChestFlow mode) { return mode switch { ChestFlow.ReceiveOnly => "Receive only (input)", ChestFlow.SendOnly => "Send only (output)", _ => "Both (input + output)", }; } } public sealed class ChestPreviewVisibility : MonoBehaviour { private LODGroup[] lods; private void Awake() { lods = ((Component)this).GetComponentsInChildren(true); Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!(val is LineRenderer)) { val.allowOcclusionWhenDynamic = false; } } Refresh(); } private void LateUpdate() { Refresh(); } private void Refresh() { if (lods == null) { return; } LODGroup[] array = lods; foreach (LODGroup val in array) { if (Object.op_Implicit((Object)(object)val)) { val.RecalculateBounds(); val.ForceLOD(0); } } } } internal static class ChestSettings { private const string ModeKey = "FP_chest_mode_v1"; private static readonly FieldInfo Areas = AccessTools.Field(typeof(PrivateArea), "m_allAreas"); private static readonly MethodInfo Enabled = AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null); private static readonly MethodInfo Inside = AccessTools.Method(typeof(PrivateArea), "IsInside", (Type[])null, (Type[])null); private static readonly MethodInfo Permitted = AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null); internal static ChestFlow Mode(Container chest) { if (!Hopper.Is(chest)) { if (!Native.Valid((Component)(object)chest)) { return ChestFlow.Both; } return ChestFlowRules.Read(Native.View((Component)(object)chest).GetZDO().GetInt("FP_chest_mode_v1", 0)); } return ChestFlow.SendOnly; } internal static void SetFromPeer(Container chest, long sender, ZDOID playerId, int value) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (Hopper.Is(chest) || !Native.Owner((Component)(object)chest) || !ChestHandoff.Eligible(chest) || ChestHandoff.Busy(chest) || value < 0 || value > 2) { return; } GameObject val = ZNetScene.instance.FindInstance(playerId); Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent() : null); if (!Object.op_Implicit((Object)(object)val2) || !Native.Valid((Component)(object)val2) || Native.View((Component)(object)val2).GetZDO().GetOwner() != sender || Vector3.Distance(((Component)val2).transform.position, ((Component)chest).transform.position) > val2.m_maxInteractDistance + 1f) { return; } long playerID = val2.GetPlayerID(); foreach (PrivateArea item in (List)Areas.GetValue(null)) { if (Object.op_Implicit((Object)(object)item) && (bool)Enabled.Invoke(item, null) && (bool)Inside.Invoke(item, new object[2] { ((Component)chest).transform.position, 0f })) { Piece component = ((Component)item).GetComponent(); if ((!Object.op_Implicit((Object)(object)component) || component.GetCreator() != playerID) && !(bool)Permitted.Invoke(item, new object[1] { playerID })) { return; } } } Native.View((Component)(object)chest).GetZDO().Set("FP_chest_mode_v1", value); Routing.Dirty = true; } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class ChestModeHoverPatch { private static void Postfix(Container __instance, ref string __result) { if (!Hopper.Is(__instance) && ChestHandoff.Eligible(__instance) && ChestHandoff.Access((Component)(object)__instance)) { __result = __result + "\nForge Pipes: " + PipeLanguage.Text(ChestFlowRules.Label(ChestSettings.Mode(__instance))) + ""; __result = __result + Localization.instance.Localize("\n[$KEY_AltPlace + $KEY_Use] ") + PipeLanguage.Text("Change pipe mode"); } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class ChestModeInteractPatch { private static bool Prefix(Container __instance, Humanoid __0, bool __1, bool __2, ref bool __result) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (Hopper.Is(__instance) || !__2 || (Object)(object)__0 != (Object)(object)Player.m_localPlayer || !ChestHandoff.Eligible(__instance)) { return true; } __result = true; if (__1) { return false; } if (!ChestHandoff.Access((Component)(object)__instance)) { return false; } if (ChestHandoff.Busy(__instance)) { ((Character)__0).Message((MessageType)2, PipeLanguage.Text("Close the chest before changing its pipe mode."), 0, (Sprite)null, false); return false; } ChestFlow chestFlow = ChestFlowRules.Next(ChestSettings.Mode(__instance)); Native.View((Component)(object)__instance).InvokeRPC("FP_ChestMode", new object[2] { Native.View((Component)(object)__0).GetZDO().m_uid, (int)chestFlow }); ((Character)__0).Message((MessageType)2, PipeLanguage.Text("Requested pipe mode: ") + PipeLanguage.Text(ChestFlowRules.Label(chestFlow)), 0, (Sprite)null, false); return false; } } internal static class ChestTransfers { internal static void Tick(Container source) { if (!ChestHandoff.Eligible(source) || !Native.Owner((Component)(object)source)) { return; } List list = Routing.ConnectedChests(source); if (!InventoryWake.ShouldRun(source, list)) { return; } if (OpenChestAccess.Viewing(source)) { foreach (Container item in list) { if (OpenChestRules.MayPull(Native.Owner((Component)(object)source), targetViewed: true, ChestHandoff.Busy(item)) && ChestFlowRules.Transfer(ChestSettings.Mode(item), ChestSettings.Mode(source))) { ChestHandoff component = ((Component)item).GetComponent(); if (Object.op_Implicit((Object)(object)component)) { component.Ready((Component)(object)source); } } } } if (!ChestFlowRules.Sends(ChestSettings.Mode(source))) { return; } ChestHandoff component2 = ((Component)source).GetComponent(); if (!Object.op_Implicit((Object)(object)component2) || !component2.Ready((Component)(object)source)) { return; } int num = 10; try { foreach (Container item2 in list) { if (!ChestFlowRules.Transfer(ChestSettings.Mode(source), ChestSettings.Mode(item2))) { continue; } ChestHandoff component3 = ((Component)item2).GetComponent(); if (!Object.op_Implicit((Object)(object)component3) || !component3.Ready((Component)(object)source)) { continue; } Inventory inventory = source.GetInventory(); Inventory inventory2 = item2.GetInventory(); ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData val in array) { if (OpenChestAccess.Reserved(inventory, val) || !Hopper.Allows(source, item2, val)) { continue; } while (num > 0 && inventory.ContainsItem(val) && StockReserve.Available(inventory, val) > 0 && OpenChestAccess.CanEdit(source) && OpenChestAccess.CanEdit(item2) && ChestFlowRules.Transfer(ChestSettings.Mode(source), ChestSettings.Mode(item2))) { bool flag = false; for (int j = 0; j < inventory2.GetHeight(); j++) { if (flag) { break; } for (int k = 0; k < inventory2.GetWidth(); k++) { if (flag) { break; } ItemData itemAt = inventory2.GetItemAt(k, j); if (itemAt == null || !OpenChestAccess.Reserved(inventory2, itemAt)) { int stack = val.m_stack; inventory2.MoveItemToThis(inventory, val, 1, k, j); flag = val.m_stack < stack; } } } if (!flag) { break; } num--; } if (num == 0) { break; } } if (num == 0) { break; } } } finally { InventoryWake.Completed(source, list, num < 10); } } } internal static class CookingCredit { internal sealed class Snapshot { internal Container Source; internal Inventory From; internal Inventory To; internal Dictionary Before; internal Dictionary Destination; internal bool Completed; } private static readonly Dictionary containers = new Dictionary(); private static readonly Dictionary> observed = new Dictionary>(); private static readonly HashSet credited = new HashSet(); internal static int Moving; internal static void Clear() { containers.Clear(); observed.Clear(); credited.Clear(); Moving = 0; } internal static void Register(Container c) { if (Object.op_Implicit((Object)(object)c) && c.GetInventory() != null) { containers[c.GetInventory()] = c; Refresh(c); } } internal static void Forget(Container c) { if (Object.op_Implicit((Object)(object)c) && c.GetInventory() != null) { containers.Remove(c.GetInventory()); observed.Remove(c.GetInventory()); credited.Remove(c.GetInventory()); } } internal static void Refresh(Container c) { if (Object.op_Implicit((Object)(object)c) && Native.Owner((Component)(object)c) && Native.View((Component)(object)c).GetZDO().GetBool("FP_has_cook_credit", false)) { credited.Add(c.GetInventory()); observed[c.GetInventory()] = Counts(c.GetInventory()); } } internal static void Observe(Inventory inventory) { if (Moving > 0 || inventory == null || !credited.Contains(inventory)) { return; } Container val = Find(inventory); if (Object.op_Implicit((Object)(object)val) && Native.Owner((Component)(object)val)) { if (observed.TryGetValue(inventory, out var value)) { Snapshot snapshot = new Snapshot(); snapshot.Source = val; snapshot.From = inventory; snapshot.Before = value; Finish(snapshot); } else { Refresh(val); } } } private static string Identity(ItemData i) { return (Object.op_Implicit((Object)(object)i.m_dropPrefab) ? ((Object)i.m_dropPrefab).name : i.m_shared.m_name) + ":" + i.m_quality + ":" + i.m_worldLevel; } private static Dictionary Counts(Inventory inv) { return inv.GetAllItems().GroupBy(Identity).ToDictionary((IGrouping g) => g.Key, (IGrouping g) => g.Sum((ItemData i) => i.m_stack)); } private static int Count(Dictionary counts, string key) { if (!counts.TryGetValue(key, out var value)) { return 0; } return value; } private static Container Find(Inventory inv) { if (inv == null || !containers.TryGetValue(inv, out var value) || !Object.op_Implicit((Object)(object)value)) { return null; } return value; } internal static void Add(Container c, ItemData item, int amount, SkillType skill) { //IL_0000: 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_0012: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected I4, but got Unknown if ((int)skill != 0 && amount > 0 && Native.Owner((Component)(object)c) && !(((int)skill == -1) ? (!Plugin.HoneyAchievement.Value) : (!Plugin.CookingCollectionXP.Value))) { Register(c); Add(c, Identity(item), amount, (int)skill); } } private static void Add(Container c, string id, int amount, int skill) { ZDO zDO = Native.View((Component)(object)c).GetZDO(); string text = "FP_cook_credit_" + id; zDO.Set(text, Math.Max(0, zDO.GetInt(text, 0)) + amount); zDO.Set(text + "_skill", skill); zDO.Set("FP_has_cook_credit", true); credited.Add(c.GetInventory()); Refresh(c); } internal static Snapshot Capture(Inventory from, Inventory to = null) { Container val = Find(from); if (!Object.op_Implicit((Object)(object)val) || !Native.Owner((Component)(object)val) || !Native.View((Component)(object)val).GetZDO().GetBool("FP_has_cook_credit", false)) { return null; } Snapshot snapshot = new Snapshot(); snapshot.Source = val; snapshot.From = from; snapshot.To = to; snapshot.Before = Counts(from); snapshot.Destination = ((to == null) ? null : Counts(to)); return snapshot; } internal static void Finish(Snapshot state) { if (state == null || state.Completed || !Object.op_Implicit((Object)(object)state.Source) || !Native.Owner((Component)(object)state.Source)) { return; } state.Completed = true; Dictionary dictionary = Counts(state.From); Dictionary dictionary2 = ((state.To == null) ? null : Counts(state.To)); Container val = Find(state.To); Player localPlayer = Player.m_localPlayer; bool flag = Object.op_Implicit((Object)(object)localPlayer) && state.To == ((Humanoid)localPlayer).GetInventory() && OpenChestAccess.Viewing(state.Source) && ChestHandoff.Access((Component)(object)state.Source); ZDO z = Native.View((Component)(object)state.Source).GetZDO(); foreach (KeyValuePair item in state.Before) { int num = Math.Max(0, item.Value - Count(dictionary, item.Key)); if (num == 0) { continue; } string text = "FP_cook_credit_" + item.Key; int num2 = Math.Min(item.Value, Math.Max(0, z.GetInt(text, 0))); int num3 = Math.Min(num, num2); if (num3 == 0) { continue; } int num4 = z.GetInt(text + "_skill", 0); z.Set(text, num2 - num3); int num5 = ((dictionary2 != null) ? Math.Min(num3, Math.Max(0, Count(dictionary2, item.Key) - Count(state.Destination, item.Key))) : 0); if (num5 == 0 || num4 == 0) { continue; } if (flag) { if (num4 == -1) { if (Plugin.HoneyAchievement.Value && Object.op_Implicit((Object)(object)Game.instance)) { Game.instance.IncrementPlayerStat((PlayerStatType)76, (float)num5, false); } } else if (Plugin.CookingCollectionXP.Value) { for (int i = 0; i < num5; i++) { ((Character)localPlayer).RaiseSkill((SkillType)num4, 0.6f); } } } else if (Object.op_Implicit((Object)(object)val) && Native.Owner((Component)(object)val)) { Add(val, item.Key, num5, num4); } } bool flag2 = dictionary.Any((KeyValuePair entry) => entry.Value > 0 && z.GetInt("FP_cook_credit_" + entry.Key, 0) > 0); z.Set("FP_has_cook_credit", flag2); if (flag2) { Refresh(state.Source); } else { credited.Remove(state.Source.GetInventory()); observed.Remove(state.Source.GetInventory()); } if (Object.op_Implicit((Object)(object)val)) { Refresh(val); } } } [HarmonyPatch(typeof(Inventory), "Changed")] internal static class CookingCreditChangedPatch { private static void Postfix(Inventory __instance) { CookingCredit.Observe(__instance); } } [HarmonyPatch(typeof(Container), "Load")] internal static class CookingCreditLoadPatch { private static void Prefix() { CookingCredit.Moving++; } private static void Finalizer(Container __instance) { try { CookingCredit.Refresh(__instance); } finally { CookingCredit.Moving = Math.Max(0, CookingCredit.Moving - 1); } } } [HarmonyPatch] internal static class CookingCreditMovePatch { private static IEnumerable TargetMethods() { return (from m in typeof(Inventory).GetMethods() where m.Name == "MoveItemToThis" || m.Name == "MoveAll" || m.Name == "StackAll" select m).Cast(); } private static void Prefix(Inventory __instance, Inventory __0, out CookingCredit.Snapshot __state) { __state = ((CookingCredit.Moving++ == 0) ? CookingCredit.Capture(__0, __instance) : null); } private static void Finalizer(CookingCredit.Snapshot __state) { try { CookingCredit.Finish(__state); } finally { CookingCredit.Moving = Math.Max(0, CookingCredit.Moving - 1); } } } [HarmonyPatch] internal static class CookingCreditRemovePatch { private static IEnumerable TargetMethods() { return (from m in typeof(Inventory).GetMethods() where m.Name == "RemoveItem" || m.Name == "RemoveOneItem" || m.Name == "RemoveAll" select m).Cast(); } private static void Prefix(Inventory __instance, out CookingCredit.Snapshot __state) { __state = ((CookingCredit.Moving++ == 0) ? CookingCredit.Capture(__instance) : null); } private static void Finalizer(CookingCredit.Snapshot __state) { try { CookingCredit.Finish(__state); } finally { CookingCredit.Moving = Math.Max(0, CookingCredit.Moving - 1); } } } public static class CopperGrain { public const int Size = 256; private static int Wrap(int n, int size) { return (n % size + size) % size; } private static float Noise(int x, int y, int size) { uint num = (uint)(Wrap(x, size) * 374761393 + Wrap(y, size) * 668265263 + 9021); num = (num ^ (num >> 13)) * 1274126177; num ^= num >> 16; return (float)(num & 0xFFFF) / 65535f; } private static float Broad(int x, int y, int cell) { x = Wrap(x, 256); y = Wrap(y, 256); int num = x / cell; int num2 = y / cell; float num3 = (float)(x % cell) / (float)cell; float num4 = (float)(y % cell) / (float)cell; num3 = num3 * num3 * (3f - 2f * num3); num4 = num4 * num4 * (3f - 2f * num4); float num5 = Noise(num, num2, 256 / cell) * (1f - num3) + Noise(num + 1, num2, 256 / cell) * num3; float num6 = Noise(num, num2 + 1, 256 / cell) * (1f - num3) + Noise(num + 1, num2 + 1, 256 / cell) * num3; return num5 * (1f - num4) + num6 * num4; } public static float Height(int x, int y) { return 0.4f * Noise(x, y, 256) + 0.36f * Broad(x, y, 4) + 0.24f * Broad(x, y, 32); } public static float Tone(int x, int y) { return 0.63f + 0.43f * Height(x, y) + 0.12f * Broad(x, y, 32); } } internal static class CopperMaterial { internal const int AtlasTileSize = 256; internal const int AtlasTiles = 4; internal const int AtlasGutter = 32; private const int AtlasMipCount = 6; private static Material copper; private static Material collar; private static Material pipe; internal static Material Get(Material fallback, bool isCollar) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)copper)) { copper = Create(atlas: false); collar = new Material(copper); ((Object)collar).name = "ForgePipes dark copper collars"; collar.color = new Color(0.87f, 0.83f, 0.78f); } if (!isCollar) { return copper; } return collar; } internal static Material GetPipe() { if (!Object.op_Implicit((Object)(object)pipe)) { return pipe = Create(atlas: true); } return pipe; } internal static float AtlasU(float u, int surface) { int num = Mathf.Clamp(surface, 0, 2); float num2 = 191f; return ((float)(num * 256 + 32) + 0.5f + Mathf.Clamp01(u) * num2) / 1024f; } private static Material Create(bool atlas) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Standard") ?? Shader.Find("Legacy Shaders/Diffuse") ?? Shader.Find("Unlit/Texture"); if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("No non-deforming copper shader is available."); } Material val2 = new Material(val); ((Object)val2).name = (atlas ? "ForgePipes shared copper atlas" : "ForgePipes rough copper"); val2.color = Color.white; bool value = Plugin.MatteCopper.Value; bool flag = !value && val2.HasProperty("_BumpMap"); bool flag2 = !value && val2.HasProperty("_MetallicGlossMap"); int num = 256; int num2 = (atlas ? 1024 : num); Color[] array = (Color[])(object)new Color[num2 * num]; Color[] array2 = (Color[])(object)(flag ? new Color[array.Length] : null); Color[] array3 = (Color[])(object)(flag2 ? new Color[array.Length] : null); for (int i = 0; i < num; i++) { for (int j = 0; j < num2; j++) { int num3 = i * num2 + j; int num4 = (atlas ? (j / 256) : 0); int num5 = (atlas ? ((int)Mathf.Floor((float)(j % 256 - 32) * 256f / 191f)) : j); float num6 = CopperGrain.Tone(num5, i); Color val3 = (Color)(num4 switch { 2 => new Color(0.22f, 0.2f, 0.18f), 1 => new Color(0.76f, 0.72f, 0.68f), _ => Color.white, }); ref Color reference = ref array[num3]; reference = new Color(0.78f * num6 * val3.r, 0.405f * num6 * val3.g, 0.225f * num6 * val3.b, 1f); if (array2 != null) { float num7 = (CopperGrain.Height(num5 - 1, i) - CopperGrain.Height(num5 + 1, i)) * 0.7f; float num8 = (CopperGrain.Height(num5, i - 1) - CopperGrain.Height(num5, i + 1)) * 0.7f; Vector3 val4 = new Vector3(num7, num8, 1f); Vector3 normalized = ((Vector3)(ref val4)).normalized; ref Color reference2 = ref array2[num3]; reference2 = new Color(1f, normalized.y * 0.5f + 0.5f, 1f, normalized.x * 0.5f + 0.5f); } if (array3 != null) { ref Color reference3 = ref array3[num3]; reference3 = new Color((num4 == 2) ? 0.1f : 0.55f, 0f, 0f, 0.18f + CopperGrain.Height(num5, i) * 0.18f); } } } val2.mainTexture = (Texture)(object)Texture(num2, num, linear: false, atlas, array, "grainy copper albedo"); if (array2 != null) { val2.SetTexture("_BumpMap", (Texture)(object)Texture(num2, num, linear: true, atlas, array2, "copper grain normals")); val2.SetFloat("_BumpScale", 0.65f); val2.EnableKeyword("_NORMALMAP"); } if (array3 != null) { val2.SetTexture("_MetallicGlossMap", (Texture)(object)Texture(num2, num, linear: true, atlas, array3, "copper roughness")); val2.EnableKeyword("_METALLICGLOSSMAP"); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", value ? 0f : 0.55f); } if (val2.HasProperty("_Glossiness")) { val2.SetFloat("_Glossiness", value ? 0.08f : 0.26f); } val2.enableInstancing = true; return val2; } private static Texture2D Texture(int width, int height, bool linear, bool atlas, Color[] pixels, string name) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown Texture2D val = new Texture2D(width, height, (TextureFormat)4, atlas ? 6 : (-1), linear); Finish(val, pixels, name + (atlas ? " atlas" : "")); if (atlas) { ((Texture)val).wrapModeU = (TextureWrapMode)1; } return val; } private static void Finish(Texture2D texture, Color[] pixels, string name) { ((Object)texture).name = name; ((Texture)texture).wrapMode = (TextureWrapMode)0; ((Texture)texture).filterMode = (FilterMode)2; ((Texture)texture).anisoLevel = 4; texture.SetPixels(pixels); texture.Apply(true, true); } } internal static class CraftingInterop { internal static bool CanYield(Container chest) { MonoBehaviour val = null; MonoBehaviour[] components = ((Component)chest).GetComponents(); foreach (MonoBehaviour val2 in components) { if (Object.op_Implicit((Object)(object)val2) && ((object)val2).GetType().FullName == "ForgeChestCrafting.CraftChestLease") { val = val2; break; } } if (!Object.op_Implicit((Object)(object)val)) { return true; } MethodInfo methodInfo = AccessTools.Method(((object)val).GetType(), "CanYieldToPipes", (Type[])null, (Type[])null); if (methodInfo == null) { return true; } try { return (bool)methodInfo.Invoke(val, null); } catch (Exception ex) { Plugin.Error("Crafting lease check failed: " + ex); return false; } } } internal static class EitrDamageRules { internal static bool IsSpark(bool hasAttacker, DamageTypes damage) { if (!hasAttacker && damage.m_lightning > 0f && damage.m_poison > 0f && damage.m_damage <= 0f && damage.m_blunt <= 0f && damage.m_slash <= 0f && damage.m_pierce <= 0f && damage.m_chop <= 0f && damage.m_pickaxe <= 0f && damage.m_fire <= 0f && damage.m_frost <= 0f && damage.m_spirit <= 0f) { return damage.m_nonPlayer <= 0f; } return false; } } internal static class EitrProtection { internal static bool Protected(WearNTear wear) { if (!Native.Owner((Component)(object)wear)) { return false; } if (Object.op_Implicit((Object)(object)((Component)wear).GetComponent())) { return true; } Container componentInChildren = ((Component)wear).GetComponentInChildren(); if (!ChestHandoff.Eligible(componentInChildren)) { return false; } if (!Routing.Dirty) { return Routing.HasChestAnchor(componentInChildren); } return PipeStructure.Anchored(wear); } } [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] internal static class PipeEitrProtectionPatch { private static bool Prefix(WearNTear __instance, HitData __1) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (__1 != null && EitrDamageRules.IsSpark(__1.HaveAttacker(), __1.m_damage)) { return !EitrProtection.Protected(__instance); } return true; } } public sealed class Hopper : MonoBehaviour { public bool HasLegacyBounds; public Vector3 LegacyCenter; public Vector3 LegacySize; private static readonly HashSet inventories = new HashSet(); private void Awake() { RegisterInventory(((Component)this).GetComponent()); } internal static void RegisterInventory(Container c) { if (Is(c) && c.GetInventory() != null) { inventories.Add(c.GetInventory()); } } private void OnDestroy() { Container component = ((Component)this).GetComponent(); if (Object.op_Implicit((Object)(object)component) && component.GetInventory() != null) { inventories.Remove(component.GetInventory()); } } internal static bool InventoryIsHopper(Inventory inventory) { return inventories.Contains(inventory); } internal static bool Is(Container chest) { if (Object.op_Implicit((Object)(object)chest)) { return Object.op_Implicit((Object)(object)((Component)chest).GetComponent()); } return false; } internal static bool Allows(Container source, Container destination, ItemData item) { if (!Is(source)) { return true; } if (Is(destination) || !Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return false; } return destination.GetInventory().GetAllItems().Any((ItemData i) => Object.op_Implicit((Object)(object)i.m_dropPrefab) && i.m_stack > 0 && SortingRules.Matches(((Object)item.m_dropPrefab).name, item.m_quality, item.m_worldLevel, ((Object)i.m_dropPrefab).name, i.m_quality, i.m_worldLevel)); } private static void CaptureLegacySockets(GameObject go, Container chest) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //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_0045: 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_0055: 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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) List list = new List(); for (int i = 0; i < 4; i++) { GameObject val = new GameObject("legacy_hopper_bounds"); val.transform.SetParent(go.transform, false); Quaternion val2 = Quaternion.Euler(0f, (float)(i * 90), 0f); val.transform.localPosition = Vector3.up * 0.95f + val2 * (Vector3.forward * 0.43f); val.transform.localRotation = val2 * Quaternion.Euler(-25f, 0f, 0f); val.transform.localScale = new Vector3(1.15f, 0.65f, 0.065f); val.AddComponent(); list.Add(val); } Hopper component = go.GetComponent(); if (PortFactory.TryChestBounds(chest, preview: true, out var bounds)) { component.HasLegacyBounds = true; component.LegacyCenter = ((Bounds)(ref bounds)).center; component.LegacySize = ((Bounds)(ref bounds)).size; } foreach (GameObject item in list) { Object.DestroyImmediate((Object)(object)item); } } internal static void Register(ZNetScene scene) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown if (!Plugin.Prefabs.Any((GameObject p) => ((Object)p).name == "FP_Hopper")) { GameObject prefab = scene.GetPrefab("piece_chest_wood"); GameObject val = Object.Instantiate(prefab, Plugin.PrefabRoot.transform, false); ((Object)val).name = "FP_Hopper"; val.AddComponent(); Container component = val.GetComponent(); component.m_name = PipeLanguage.Token("Forge sorting hopper"); Piece component2 = val.GetComponent(); component2.m_name = "Forge sorting hopper"; component2.m_icon = PipeIcons.Load("Hopper"); component2.m_description = "Drop items into the hopper or open its inventory. Items move only to connected receiving chests already containing the same item. Unmatched items stay here."; component2.m_category = (PieceCategory)1; component2.m_usage = (UsageTagFlags)2050; component2.m_craftingStation = scene.GetPrefab("piece_workbench").GetComponent(); Requirement[] array = (Requirement[])(object)new Requirement[2]; Requirement val2 = new Requirement(); val2.m_resItem = scene.GetPrefab("Wood").GetComponent(); val2.m_amount = 10; val2.m_recover = true; array[0] = val2; Requirement val3 = new Requirement(); val3.m_resItem = scene.GetPrefab("Copper").GetComponent(); val3.m_amount = 4; val3.m_recover = true; array[1] = val3; component2.m_resources = array; MetalPlacement.Apply(component2, scene); CaptureLegacySockets(val, component); HopperModel.Build(val); Plugin.Prefabs.Add(val); val.SetActive(true); } } } internal static class HopperModel { internal static Mesh Create() { //IL_003f: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown HopperShape hopperShape = HopperShape.Create(); List list = new List(); List list2 = new List(); foreach (Point vertex in hopperShape.Vertices) { list.Add(new Vector3(vertex.X, vertex.Y, vertex.Z)); } foreach (Point item in hopperShape.UV) { list2.Add(new Vector2(item.X, item.Y)); } Mesh val = new Mesh(); ((Object)val).name = "Forge sorting hopper fitted funnel"; Mesh val2 = val; val2.SetVertices(list); val2.SetUVs(0, list2); val2.SetTriangles(hopperShape.Indices, 0); val2.RecalculateNormals(); val2.RecalculateTangents(); val2.RecalculateBounds(); return val2; } internal static void Build(GameObject go) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: 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) LODGroup[] componentsInChildren = go.GetComponentsInChildren(true); foreach (LODGroup val in componentsInChildren) { Object.DestroyImmediate((Object)(object)val); } Renderer[] componentsInChildren2 = go.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren2) { val2.enabled = false; } GameObject val3 = new GameObject("FP_hopper_funnel"); val3.layer = go.layer; val3.transform.SetParent(go.transform, false); val3.AddComponent().sharedMesh = Create(); ((Renderer)val3.AddComponent()).sharedMaterial = CopperMaterial.GetPipe(); Collider[] componentsInChildren3 = go.GetComponentsInChildren(true); foreach (Collider val4 in componentsInChildren3) { Object.DestroyImmediate((Object)(object)val4); } BoxCollider val5 = val3.AddComponent(); val5.center = new Vector3(0f, 0.365f, 0f); val5.size = new Vector3(0.76f, 0.43f, 0.76f); for (int l = 0; l < 4; l++) { GameObject val6 = new GameObject("hopper_wall_collider"); val6.layer = go.layer; val6.transform.SetParent(val3.transform, false); Quaternion val7 = Quaternion.Euler(0f, (float)(l * 90), 0f); val6.transform.localPosition = Vector3.up * 0.88f + val7 * (Vector3.forward * 0.515f); val6.transform.localRotation = val7 * Quaternion.FromToRotation(Vector3.up, new Vector3(0f, 0.6f, 0.27f)); val6.AddComponent().size = new Vector3(1.28f, 0.658f, 0.055f); } WearNTear component = go.GetComponent(); if (Object.op_Implicit((Object)(object)component)) { component.m_new = val3; component.m_worn = val3; component.m_broken = val3; component.m_wet = null; } } } public sealed class HopperShape { public readonly List Vertices = new List(); public readonly List UV = new List(); public readonly List Indices = new List(); public static HopperShape Create() { HopperShape hopperShape = new HopperShape(); Point[] array = new Point[6] { new Point(0.38f, 0.15f, 0f), new Point(0.38f, 0.58f, 0f), new Point(0.65f, 1.18f, 0f), new Point(0.59f, 1.18f, 0f), new Point(0.32f, 0.59f, 0f), new Point(0.32f, 0.19f, 0f) }; for (int i = 0; i < array.Length - 1; i++) { for (int j = 0; j < 4; j++) { int n = (j + 1) % 4; int count = hopperShape.Vertices.Count; hopperShape.Vertices.Add(Corner(j, array[i])); hopperShape.Vertices.Add(Corner(n, array[i])); hopperShape.Vertices.Add(Corner(n, array[i + 1])); hopperShape.Vertices.Add(Corner(j, array[i + 1])); float num = ((i == 2) ? 0.37f : 0.12f); hopperShape.UV.Add(new Point(num - 0.07f, 0.15f, 0f)); hopperShape.UV.Add(new Point(num + 0.07f, 0.15f, 0f)); hopperShape.UV.Add(new Point(num + 0.07f, 0.85f, 0f)); hopperShape.UV.Add(new Point(num - 0.07f, 0.85f, 0f)); hopperShape.Indices.AddRange(new int[6] { count, count + 2, count + 1, count, count + 3, count + 2 }); } } int count2 = hopperShape.Vertices.Count; for (int k = 0; k < 4; k++) { hopperShape.Vertices.Add(Corner(k, array[5])); hopperShape.UV.Add(new Point(0.62f, 0.5f, 0f)); } hopperShape.Indices.AddRange(new int[6] { count2, count2 + 2, count2 + 1, count2, count2 + 3, count2 + 2 }); count2 = hopperShape.Vertices.Count; for (int l = 0; l < 4; l++) { hopperShape.Vertices.Add(Corner(l, array[0])); hopperShape.UV.Add(new Point(0.12f, 0.5f, 0f)); } hopperShape.Indices.AddRange(new int[6] { count2, count2 + 1, count2 + 2, count2, count2 + 2, count2 + 3 }); return hopperShape; } private static Point Corner(int n, Point ring) { return new Point((n == 0 || n == 3) ? (0f - ring.X) : ring.X, ring.Y, (n < 2) ? (0f - ring.X) : ring.X); } } internal static class InventoryWake { private sealed class State { internal ulong Fingerprint; internal float Next; internal int Misses; } private static readonly Dictionary revisions = new Dictionary(); private static readonly Dictionary idle = new Dictionary(); internal static void Clear() { revisions.Clear(); idle.Clear(); } internal static void Watch(Inventory inventory) { if (inventory != null && !revisions.ContainsKey(inventory)) { revisions.Add(inventory, 0); } } internal static void Changed(Inventory inventory) { if (revisions.TryGetValue(inventory, out var value)) { revisions[inventory] = value + 1; } } private static ulong Fingerprint(Container source, List peers) { ulong n = (ulong)Routing.Revision; Mix(ref n, source); foreach (Container peer in peers) { if (Object.op_Implicit((Object)(object)peer)) { Mix(ref n, peer); } } return n; } private static void Mix(ref ulong n, Container c) { Inventory inventory = c.GetInventory(); Watch(inventory); revisions.TryGetValue(inventory, out var value); n = n * 1099511628211L + (uint)((Object)c).GetInstanceID(); n = n * 1099511628211L + (uint)value; n = n * 1099511628211L + (uint)ChestSettings.Mode(c); n = n * 1099511628211L + (ulong)(Native.Owner((Component)(object)c) ? 1 : 0); } internal static bool ShouldRun(Container source, List peers) { if (!idle.TryGetValue(source, out var value)) { idle[source] = new State(); return true; } if (!OpenChestAccess.Viewing(source) && !(Time.time >= value.Next)) { return value.Fingerprint != Fingerprint(source, peers); } return true; } internal static void Completed(Container source, List peers, bool moved) { if (!idle.TryGetValue(source, out var value)) { value = (idle[source] = new State()); } value.Misses = ((!moved) ? Math.Min(3, value.Misses + 1) : 0); value.Fingerprint = Fingerprint(source, peers); value.Next = Time.time + (float)((!moved) ? value.Misses : 0); } internal static void Prune() { Container[] array = idle.Keys.Where((Container c) => !Object.op_Implicit((Object)(object)c) || !Plugin.Chests.Contains(c)).ToArray(); foreach (Container key in array) { idle.Remove(key); } HashSet live = new HashSet(); foreach (Container chest in Plugin.Chests) { if (Object.op_Implicit((Object)(object)chest)) { live.Add(chest.GetInventory()); } } Inventory[] array2 = revisions.Keys.Where((Inventory i) => !live.Contains(i)).ToArray(); foreach (Inventory key2 in array2) { revisions.Remove(key2); } } } [HarmonyPatch(typeof(Inventory), "Changed")] internal static class InventoryWakePatch { private static void Postfix(Inventory __instance) { InventoryWake.Changed(__instance); } } internal static class LoosePickup { private sealed class Mouth { internal Component Owner; internal Transform Socket; internal List Chests; internal Vector3 Position { get { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Socket)) { return Owner.transform.TransformPoint(Vector3.up * 1.15f); } return Socket.position; } } } private static readonly Collider[] hits = (Collider[])(object)new Collider[64]; private static readonly HashSet seen = new HashSet(); private static readonly int itemMask = LayerMask.GetMask(new string[1] { "item" }); private static readonly int wallMask = LayerMask.GetMask(new string[3] { "terrain", "piece", "static_solid" }); private static int cursor; private static int revision = -1; private static readonly List mouths = new List(); internal static void Tick() { //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0266: 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_0079: Expected O, but got Unknown //IL_039b: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) if (revision != Routing.Revision) { mouths.Clear(); revision = Routing.Revision; foreach (PipeNode item in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Ready || !Native.Valid((Component)(object)item)) { continue; } foreach (Transform item2 in ((Component)item).transform) { Transform val = item2; if (!(((Object)val).name != "_snap") || (item.Kind == PipeKind.Output && ((Object)val).name == "_fp_dock")) { mouths.Add(new Mouth { Owner = (Component)(object)item, Socket = val }); } } } foreach (Container chest in Plugin.Chests) { if (Hopper.Is(chest) && ChestHandoff.Eligible(chest)) { mouths.Add(new Mouth { Owner = (Component)(object)chest, Chests = new List { chest } }); } } } if (mouths.Count == 0) { cursor = 0; return; } int num = Math.Min(4, mouths.Count); for (int i = 0; i < num; i++) { Mouth mouth = mouths[cursor++ % mouths.Count]; PipeNode pipeNode = mouth.Owner as PipeNode; if (!Object.op_Implicit((Object)(object)mouth.Owner) || !Native.Owner(mouth.Owner) || !ChestHandoff.Access(mouth.Owner)) { continue; } int num2 = Physics.OverlapSphereNonAlloc(mouth.Position, 0.85f, hits, itemMask, (QueryTriggerInteraction)2); if (num2 == 0) { continue; } if (Object.op_Implicit((Object)(object)pipeNode)) { MachinePort machinePort = SocketIndex.NearbyPorts(mouth.Position).FirstOrDefault((MachinePort p) => Object.op_Implicit((Object)(object)p) && !p.Preview && Object.op_Implicit((Object)(object)p.Target) && Vector3.SqrMagnitude(((Component)p).transform.position - mouth.Position) < 0.0324f); if ((Object.op_Implicit((Object)(object)machinePort) && machinePort.Kind != PipeKind.Output) || (!Object.op_Implicit((Object)(object)machinePort) && (!Object.op_Implicit((Object)(object)mouth.Socket) || PipeSnapping.Occupied(mouth.Socket, pipeNode)))) { continue; } mouth.Chests = Routing.StorageForPipe(pipeNode); } if (mouth.Chests.Count == 0) { continue; } seen.Clear(); int num3 = 10; for (int num4 = 0; num4 < num2; num4++) { if (num3 <= 0) { break; } ItemDrop val2 = (Object.op_Implicit((Object)(object)hits[num4]) ? ((Component)hits[num4]).GetComponentInParent() : null); if (!Object.op_Implicit((Object)(object)val2) || !seen.Add(val2) || !val2.m_autoPickup || !Native.Valid((Component)(object)val2) || !ChestHandoff.Access((Component)(object)val2)) { continue; } val2.Load(); if (val2.m_itemData.m_stack <= 0 || Physics.Linecast(mouth.Position, ((Component)val2).transform.position, wallMask, (QueryTriggerInteraction)1)) { continue; } foreach (Container chest2 in mouth.Chests) { ChestHandoff chestHandoff = (Object.op_Implicit((Object)(object)chest2) ? ((Component)chest2).GetComponent() : null); if (!Object.op_Implicit((Object)(object)chestHandoff) || !chestHandoff.Ready(mouth.Owner) || !chest2.GetInventory().CanAddItem(val2.m_itemData, 1)) { continue; } if (!Native.Owner((Component)(object)val2)) { val2.RequestOwn(); break; } if (!val2.CanPickup(true)) { break; } while (Object.op_Implicit((Object)(object)val2) && num3 > 0 && val2.m_itemData.m_stack > 0 && OpenChestAccess.CanEdit(chest2) && Native.Owner((Component)(object)val2)) { ItemData val3 = val2.m_itemData.Clone(); val3.m_stack = 1; if (!chest2.GetInventory().AddItem(val3)) { break; } num3--; if (val2.m_itemData.m_stack == 1) { val2.SetStack(0); ZNetScene.instance.Destroy(((Component)val2).gameObject); break; } val2.SetStack(val2.m_itemData.m_stack - 1); } if (!Object.op_Implicit((Object)(object)val2) || num3 == 0) { break; } } } } if (cursor > 1000000) { cursor %= mouths.Count; } } } public sealed class MachineHandoff : MonoBehaviour { private Component machine; private float nextRequest; private float nextGrant; private void Awake() { machine = Stations.Find(((Component)this).gameObject); Native.View(machine).Register("FP_RequestMachine", (Action)OnRequest); } private Container PreferredViewer() { return (from c in (from c in Routing.Connected(machine, PipeKind.Output).Concat(Routing.Connected(machine, PipeKind.Input)).Concat(Routing.Connected(machine, PipeKind.Fuel)) where ChestHandoff.Eligible(c) && ChestHandoff.Busy(c) select c).Distinct() orderby ((object)Unsafe.As(ref Native.View((Component)(object)c).GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString() select c).FirstOrDefault(); } internal static void Prepare(Component s) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (!Native.Valid(s)) { return; } MachineHandoff machineHandoff = s.GetComponent(); if (!Object.op_Implicit((Object)(object)machineHandoff)) { machineHandoff = s.gameObject.AddComponent(); } if (!Native.Owner(s) && !(Time.time < machineHandoff.nextRequest)) { Container val = machineHandoff.PreferredViewer(); if (Object.op_Implicit((Object)(object)val) && Native.Owner((Component)(object)val) && OpenChestAccess.Viewing(val) && ChestHandoff.Access((Component)(object)val) && ChestHandoff.Access(s)) { machineHandoff.nextRequest = Time.time + 3f; Native.View(s).InvokeRPC("FP_RequestMachine", new object[1] { Native.View((Component)(object)val).GetZDO().m_uid }); } } } private void OnRequest(long sender, ZDOID chestId) { //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_009d: Unknown result type (might be due to invalid IL or missing references) if (Native.Owner(machine) && Stations.Supported(machine) && Stations.CanHandoff(machine) && !(Time.time < nextGrant) && ChestHandoff.Access(machine)) { Container val = PreferredViewer(); if (Object.op_Implicit((Object)(object)val) && !(Native.View((Component)(object)val).GetZDO().m_uid != chestId) && Native.View((Component)(object)val).GetZDO().GetOwner() == sender && ChestHandoff.Access((Component)(object)val)) { ZDO zDO = Native.View(machine).GetZDO(); ZDOMan.instance.ForceSendZDO(sender, zDO.m_uid); zDO.SetOwner(sender); nextGrant = Time.time + 3f; } } } } internal static class MetalPlacement { internal static void Apply(Piece piece, ZNetScene scene) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown GameObject val = scene.GetPrefab("sfx_build_hammer_metal"); if (!Object.op_Implicit((Object)(object)val)) { string[] array = new string[3] { "iron_wall_1x1", "iron_floor_1x1", "piece_chest_blackmetal" }; foreach (string text in array) { GameObject prefab = scene.GetPrefab(text); Piece val2 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent() : null); if (Object.op_Implicit((Object)(object)val2) && val2.m_placeEffect != null) { val = (from e in val2.m_placeEffect.m_effectPrefabs where Object.op_Implicit((Object)(object)e.m_prefab) && ((Object)e.m_prefab).name.IndexOf("metal", StringComparison.OrdinalIgnoreCase) >= 0 && Object.op_Implicit((Object)(object)e.m_prefab.GetComponentInChildren(true)) select e.m_prefab).FirstOrDefault(); if (Object.op_Implicit((Object)(object)val)) { break; } } } } if (!Object.op_Implicit((Object)(object)val)) { Plugin.Error("Metal placement sound prefab unavailable; keeping native placement effects."); return; } EffectList val3 = new EffectList(); EffectData[] array2 = (EffectData[])(object)new EffectData[1]; EffectData val4 = new EffectData(); val4.m_prefab = val; val4.m_enabled = true; val4.m_scale = true; array2[0] = val4; val3.m_effectPrefabs = array2; piece.m_placeEffect = val3; } } public struct Point { public float X; public float Y; public float Z; public Point(float x, float y, float z) { X = x; Y = y; Z = z; } public static float DistanceSquared(Point a, Point b) { float num = a.X - b.X; float num2 = a.Y - b.Y; float num3 = a.Z - b.Z; return num * num + num2 * num2 + num3 * num3; } public static float Dot(Point a, Point b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } } public struct Socket { public int Node; public Point Position; public Point Direction; public Socket(int node, Point position, Point direction) { Node = node; Position = position; Direction = direction; } } public sealed class NetworkGraph { internal struct Cell : IEquatable { internal int X; internal int Y; internal int Z; public bool Equals(Cell c) { if (X == c.X && Y == c.Y) { return Z == c.Z; } return false; } public override bool Equals(object other) { if (other is Cell) { return Equals((Cell)other); } return false; } public override int GetHashCode() { return (X * 73856093) ^ (Y * 19349663) ^ (Z * 83492791); } } private readonly int[] parents; public NetworkGraph(int count, IList sockets, float tolerance) { if (count < 0 || tolerance <= 0f) { throw new ArgumentOutOfRangeException(); } parents = new int[count]; for (int i = 0; i < count; i++) { parents[i] = i; } Dictionary> dictionary = new Dictionary>(); foreach (Socket socket in sockets) { if (socket.Node < 0 || socket.Node >= count) { throw new ArgumentOutOfRangeException("sockets"); } int num = (int)Math.Floor(socket.Position.X / tolerance); int num2 = (int)Math.Floor(socket.Position.Y / tolerance); int num3 = (int)Math.Floor(socket.Position.Z / tolerance); for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { if (!dictionary.TryGetValue(Key(num + j, num2 + k, num3 + l), out var value)) { continue; } foreach (Socket item in value) { if (item.Node != socket.Node && Point.DistanceSquared(socket.Position, item.Position) <= tolerance * tolerance && Point.Dot(socket.Direction, item.Direction) < -0.8f) { parents[Root(socket.Node)] = Root(item.Node); } } } } } Cell key = Key(num, num2, num3); if (!dictionary.TryGetValue(key, out var value2)) { value2 = new List(); dictionary.Add(key, value2); } value2.Add(socket); } } private static Cell Key(int x, int y, int z) { return new Cell { X = x, Y = y, Z = z }; } public void Join(int a, int b) { parents[Root(a)] = Root(b); } public int Root(int i) { while (parents[i] != i) { parents[i] = parents[parents[i]]; i = parents[i]; } return i; } public bool Connected(int a, int b) { return Root(a) == Root(b); } } internal static class OpenChestAccess { private static readonly FieldInfo Current = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private static readonly FieldInfo Drag = AccessTools.Field(typeof(InventoryGui), "m_dragItem"); private static readonly FieldInfo DragInventory = AccessTools.Field(typeof(InventoryGui), "m_dragInventory"); private static readonly FieldInfo DragObject = AccessTools.Field(typeof(InventoryGui), "m_dragGo"); private static readonly FieldInfo Split = AccessTools.Field(typeof(InventoryGui), "m_splitItem"); private static readonly FieldInfo SplitInventory = AccessTools.Field(typeof(InventoryGui), "m_splitInventory"); internal static bool Viewing(Container chest) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)InventoryGui.instance)) { return (Object)(Container)Current.GetValue(InventoryGui.instance) == (Object)(object)chest; } return false; } internal static bool CanEdit(Container chest) { return OpenChestRules.CanEdit(Native.Owner((Component)(object)chest), ChestHandoff.Busy(chest), Viewing(chest)); } internal static bool Reserved(Inventory inventory, ItemData item) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown InventoryGui instance = InventoryGui.instance; if (!Object.op_Implicit((Object)(object)instance)) { return false; } if (!Object.op_Implicit((Object)(GameObject)DragObject.GetValue(instance)) || DragInventory.GetValue(instance) != inventory || Drag.GetValue(instance) != item) { if (Object.op_Implicit((Object)(object)instance.m_splitDialog) && ((Component)instance.m_splitDialog).gameObject.activeInHierarchy && SplitInventory.GetValue(instance) == inventory) { return Split.GetValue(instance) == item; } return false; } return true; } } internal static class OpenChestRules { internal static bool CanEdit(bool owned, bool inUse, bool localViewer) { if (owned) { if (inUse) { return localViewer; } return true; } return false; } internal static bool CanHandoff(bool owned, bool inUse, bool localViewer) { if (owned && !inUse) { return !localViewer; } return false; } internal static bool MayPull(bool targetOwned, bool targetViewed, bool sourceBusy) { if (targetOwned && targetViewed) { return !sourceBusy; } return false; } } internal static class PerformanceConsole { internal static void Register() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown new ConsoleCommand("fpperf", "Forge Pipes: fpperf status | render/automation/connections/groups on/off | reset", (ConsoleEvent)delegate(ConsoleEventArgs args) { if (args.Args.Length == 2 && args.Args[1] == "reset") { ConfigEntry renderPipes = Plugin.RenderPipes; ConfigEntry runAutomation = Plugin.RunAutomation; ConfigEntry runConnectionChecks = Plugin.RunConnectionChecks; bool flag = (Plugin.GroupRendering.Value = true); bool flag3 = (runConnectionChecks.Value = flag); bool value = (runAutomation.Value = flag3); renderPipes.Value = value; Routing.Dirty = true; } else if (args.Args.Length == 3 && (args.Args[2] == "on" || args.Args[2] == "off")) { bool value2 = args.Args[2] == "on"; switch (args.Args[1]) { case "render": Plugin.RenderPipes.Value = value2; break; case "automation": Plugin.RunAutomation.Value = value2; break; case "connections": Plugin.RunConnectionChecks.Value = value2; Routing.Dirty = true; break; case "groups": Plugin.GroupRendering.Value = value2; break; default: args.Context.AddString("Use render, automation, connections or groups."); return; } } string text = "Forge Pipes: render=" + Plugin.RenderPipes.Value + ", automation=" + Plugin.RunAutomation.Value + ", connections=" + Plugin.RunConnectionChecks.Value + ", groups=" + Plugin.GroupRendering.Value + ". Use fpperf reset to restore. Read 30-second FP timing samples in LogOutput.log."; args.Context.AddString(text); Plugin.LogInfo(text); }, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } internal static class PerformanceStats { private static readonly Dictionary sections = new Dictionary(); private static int frames; private static int ticks; private static int lastRevision; private static float elapsed; private static float worstFrame; private static double tickTotal; private static double tickWorst; internal static void Section(string name, long start) { if (Plugin.PerformanceLogging.Value) { if (!sections.TryGetValue(name, out var value)) { value = (sections[name] = new double[3]); } double num = (double)(Stopwatch.GetTimestamp() - start) * 1000.0 / (double)Stopwatch.Frequency; value[0] += num; value[1] = Math.Max(value[1], num); value[2] += 1.0; } } internal static void Tick(long started) { double num = (double)(Stopwatch.GetTimestamp() - started) * 1000.0 / (double)Stopwatch.Frequency; ticks++; tickTotal += num; tickWorst = Math.Max(tickWorst, num); } internal static void Frame() { if (!Plugin.PerformanceLogging.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { frames = (ticks = 0); elapsed = (worstFrame = 0f); tickTotal = (tickWorst = 0.0); lastRevision = Routing.Revision; return; } frames++; elapsed += Time.unscaledDeltaTime; worstFrame = Mathf.Max(worstFrame, Time.unscaledDeltaTime); if (elapsed < 30f) { return; } int num = 0; int num2 = 0; int num3 = 0; long num4 = 0L; foreach (PipeNode item in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item) || !Native.Valid((Component)(object)item)) { continue; } num++; MeshRenderer[] componentsInChildren = ((Component)item).GetComponentsInChildren(); foreach (MeshRenderer val in componentsInChildren) { if (((Renderer)val).enabled && !((Renderer)val).forceRenderingOff) { num2++; MeshFilter component = ((Component)val).GetComponent(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh)) { num4 += (long)component.sharedMesh.GetIndexCount(0) / 3L; } } } Collider[] componentsInChildren2 = ((Component)item).GetComponentsInChildren(); foreach (Collider val2 in componentsInChildren2) { if (val2.enabled) { num3++; } } } num2 += PipeRenderGroups.RendererCount; num4 += PipeRenderGroups.TriangleCount; Plugin.LogInfo($"FP performance 1.1.1: {(float)frames / elapsed:F1} FPS avg, worst frame {worstFrame * 1000f:F1} ms; pipes={num}, enabled renderers={num2}, triangles={num4}, colliders={num3}; automation avg/max={((ticks == 0) ? 0.0 : (tickTotal / (double)ticks)):F2}/{tickWorst:F2} ms; graph rebuilds={Routing.Revision - lastRevision} in {elapsed:F1}s."); foreach (KeyValuePair section in sections) { Plugin.LogInfo($"FP timing {section.Key}: avg {section.Value[0] / Math.Max(1.0, section.Value[2]):F3} ms, max {section.Value[1]:F3} ms, calls {section.Value[2]}; managed memory {(double)GC.GetTotalMemory(forceFullCollection: false) / 1048576.0:F1} MB"); } sections.Clear(); frames = (ticks = 0); elapsed = (worstFrame = 0f); tickTotal = (tickWorst = 0.0); lastRevision = Routing.Revision; } } public sealed class PipeNode : MonoBehaviour, Hoverable { public PipeKind Kind; public Vector3[] Ends; public Vector3[] Directions; public Vector3[] RunStarts; public Vector3[] RunEnds; private readonly List endGuides = new List(); internal static readonly HashSet All = new HashSet(); internal int VisualRevision; private bool registered; internal bool Terminal { get { if (Kind >= PipeKind.Chest) { return Kind <= PipeKind.Output; } return false; } } internal bool Ready { get { if (Kind == PipeKind.Stretch) { if (Object.op_Implicit((Object)(object)((Component)this).GetComponent())) { return ((Component)this).GetComponent().Shape != null; } return false; } return true; } } internal Vector3 LocalDock { get { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Component)this).transform.Find("_fp_dock"); if (!Object.op_Implicit((Object)(object)val)) { return Vector3.back * 0.5f; } return val.localPosition; } } internal Vector3 Dock => ((Component)this).transform.TransformPoint(LocalDock); private void Start() { if (Native.Valid((Component)(object)this)) { registered = true; Routing.Dirty = true; SocketIndex.Invalidate(); } } private void OnEnable() { All.Add(this); SocketIndex.Invalidate(); if (Native.Valid((Component)(object)this)) { registered = true; Routing.Dirty = true; } } private void OnDisable() { All.Remove(this); PipeRenderGroups.Remove(this); SocketIndex.Invalidate(); foreach (LineRenderer endGuide in endGuides) { if (Object.op_Implicit((Object)(object)endGuide)) { ((Renderer)endGuide).enabled = false; } } if (registered) { Routing.Dirty = true; } registered = false; } private void BuildEndGuides() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) if (endGuides.Count > 0 || Ends == null || Ends.Length == 0) { return; } Material guideMaterial = PortFactory.GuideMaterial; if (!Object.op_Implicit((Object)(object)guideMaterial)) { return; } for (int i = 0; i < Ends.Length; i++) { GameObject val = new GameObject("FP_pipe_end_guide"); val.layer = ((Component)this).gameObject.layer; val.transform.SetParent(((Component)this).transform, false); Vector3 localPosition = ((Kind == PipeKind.Chest) ? LocalDock : Ends[i]); Vector3 val2 = ((Kind == PipeKind.Chest) ? Vector3.back : Directions[i]); val.transform.localPosition = localPosition; val.transform.localRotation = Quaternion.FromToRotation(Vector3.forward, val2); LineRenderer val3 = val.AddComponent(); val3.useWorldSpace = false; val3.loop = true; val3.positionCount = 24; float startWidth = (val3.endWidth = 0.025f); val3.startWidth = startWidth; ((Renderer)val3).sharedMaterial = guideMaterial; Color startColor = (val3.endColor = Pieces.PortColor(Kind)); val3.startColor = startColor; ((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val3).receiveShadows = false; for (int j = 0; j < 24; j++) { float num2 = (float)j * (float)Math.PI / 12f; val3.SetPosition(j, new Vector3(Mathf.Cos(num2) * 0.24f, Mathf.Sin(num2) * 0.24f, 0.025f)); } ((Renderer)val3).enabled = false; endGuides.Add(val3); } } internal void RefreshGuides(bool chest, bool stretch, Vector3 playerPosition) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (chest || stretch) { BuildEndGuides(); } for (int i = 0; i < endGuides.Count; i++) { if (Object.op_Implicit((Object)(object)endGuides[i])) { Transform transform = ((Component)endGuides[i]).transform; if (Kind == PipeKind.Stretch || Kind == PipeKind.Chest) { transform.localPosition = ((Kind == PipeKind.Chest && !stretch) ? LocalDock : Ends[i]); transform.localRotation = Quaternion.FromToRotation(Vector3.forward, (Kind == PipeKind.Chest && !stretch) ? Vector3.back : Directions[i]); } ((Renderer)endGuides[i]).enabled = (chest || stretch) && (stretch || !Terminal || Kind == PipeKind.Chest) && Native.Valid((Component)(object)this) && (stretch || SnapGeometry.UprightPort(new Point(transform.forward.x, transform.forward.y, transform.forward.z)) || Mathf.Abs(transform.forward.y) > 0.999f) && !PipeSnapping.Occupied(transform, this); } } } public string GetHoverName() { return PipeLanguage.Text(((Component)this).GetComponent().m_name); } public float GetHoverOffset() { return 0.3f; } public string GetHoverText() { string text = Routing.Status(this); return GetHoverName() + "\n" + text + ""; } } internal static class Pieces { private sealed class FieldInfoCache { internal readonly FieldInfo Named = AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs"); } private static readonly FieldInfoCache fields = new FieldInfoCache(); internal static void Register(ZNetScene scene) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if (Plugin.Prefabs.Count == 0) { GameObject prefab = scene.GetPrefab("wood_beam"); if (!Object.op_Implicit((Object)(object)prefab)) { throw new InvalidOperationException("Vanilla wood_beam prefab not found."); } Plugin.PrefabRoot = new GameObject("ForgePipes_Prefabs"); Plugin.PrefabRoot.SetActive(false); Object.DontDestroyOnLoad((Object)(object)Plugin.PrefabRoot); foreach (PipeKind value in Enum.GetValues(typeof(PipeKind))) { Build(prefab, value, scene); } Hopper.Register(scene); } Dictionary dictionary = (Dictionary)fields.Named.GetValue(scene); foreach (GameObject prefab2 in Plugin.Prefabs) { dictionary[StringExtensionMethods.GetStableHashCode(((Object)prefab2).name)] = prefab2; if (!scene.m_prefabs.Contains(prefab2)) { scene.m_prefabs.Add(prefab2); } } AttachHammer(); Plugin.LogInfo("Registered " + Plugin.Prefabs.Count + " Forge pipe network prefabs."); } internal static void AttachHammer() { if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if (!Object.op_Implicit((Object)(object)itemPrefab)) { return; } PieceTable buildPieces = itemPrefab.GetComponent().m_itemData.m_shared.m_buildPieces; bool flag = false; foreach (GameObject prefab in Plugin.Prefabs) { if (!buildPieces.m_pieces.Contains(prefab)) { buildPieces.m_pieces.Add(prefab); flag = true; } } if (flag) { AccessTools.Field(typeof(ObjectDB), "m_buildPieces").SetValue(ObjectDB.instance, null); } } internal static string Label(PipeKind kind) { return kind switch { PipeKind.TightBend => "Forge pipe - tight elbow", PipeKind.TightUpBend => "Forge pipe - tight elbow up", PipeKind.TightDownBend => "Forge pipe - tight elbow down", PipeKind.ShortStraight => "Forge pipe - straight (1m)", PipeKind.Cross => "Forge pipe - four-way junction", PipeKind.SixWay => "Forge pipe - six-way junction", PipeKind.TeeUp => "Forge pipe - tee up", PipeKind.TeeDown => "Forge pipe - tee down", PipeKind.ShortTee => "Forge pipe - short junction", PipeKind.ShortTeeUp => "Forge pipe - short tee up", PipeKind.ShortTeeDown => "Forge pipe - short tee down", PipeKind.ShortCross => "Forge pipe - short four-way junction", PipeKind.ShortSixWay => "Forge pipe - short six-way junction", PipeKind.TeePlusUp => "Forge pipe - four-way tee up", PipeKind.TeePlusDown => "Forge pipe - four-way tee down", PipeKind.ShortTeePlusUp => "Forge pipe - short four-way tee up", PipeKind.ShortTeePlusDown => "Forge pipe - short four-way tee down", PipeKind.CompactChest => "Forge pipe - compact chest connector", PipeKind.CompactInput => "Forge pipe - compact ingredient inlet", PipeKind.CompactFuel => "Forge pipe - compact fuel inlet", PipeKind.CompactOutput => "Forge pipe - compact product outlet", PipeKind.Stretch => "Forge pipe - stretch (0.5-16m)", PipeKind.Straight => "Forge pipe - straight (2m)", PipeKind.Bend => "Forge pipe - elbow", PipeKind.Tee => "Forge pipe - junction", PipeKind.Riser => "Forge pipe - vertical (2m)", PipeKind.UpBend => "Forge pipe - elbow up", PipeKind.DownBend => "Forge pipe - elbow down", PipeKind.Chest => "Forge pipe - chest connector", PipeKind.Input => "Forge pipe - ingredient inlet", PipeKind.Fuel => "Forge pipe - coal inlet", _ => "Forge pipe - product outlet", }; } internal static Color PortColor(PipeKind kind) { //IL_0074: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) return (Color)(kind switch { PipeKind.Output => new Color(0.95f, 0.7f, 0.2f), PipeKind.Fuel => new Color(0.5f, 0.52f, 0.58f), PipeKind.Input => new Color(0.3f, 0.75f, 0.38f), PipeKind.Chest => new Color(0.2f, 0.6f, 0.85f), _ => new Color(0.62f, 0.36f, 0.18f), }); } private static void Build(GameObject source, PipeKind kind, ZNetScene scene) { //IL_002e: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Expected O, but got Unknown //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Expected O, but got Unknown //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_047b: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Expected O, but got Unknown //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04d5: Unknown result type (might be due to invalid IL or missing references) //IL_04f5: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0537: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) //IL_060f: Expected O, but got Unknown //IL_063c: Unknown result type (might be due to invalid IL or missing references) //IL_0643: Expected O, but got Unknown GameObject val = Object.Instantiate(source, Plugin.PrefabRoot.transform, false); ((Object)val).name = "FP_" + kind; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; Renderer componentInChildren = val.GetComponentInChildren(true); if (Object.op_Implicit((Object)(object)componentInChildren)) { _ = componentInChildren.sharedMaterial; } LODGroup[] componentsInChildren = val.GetComponentsInChildren(true); foreach (LODGroup val2 in componentsInChildren) { Object.DestroyImmediate((Object)(object)val2); } for (int num = val.transform.childCount - 1; num >= 0; num--) { Object.DestroyImmediate((Object)(object)((Component)val.transform.GetChild(num)).gameObject); } Rigidbody[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (Rigidbody val3 in componentsInChildren2) { Object.DestroyImmediate((Object)(object)val3); } Collider[] components = val.GetComponents(); foreach (Collider val4 in components) { Object.DestroyImmediate((Object)(object)val4); } Renderer[] components2 = val.GetComponents(); foreach (Renderer val5 in components2) { Object.DestroyImmediate((Object)(object)val5); } MeshFilter[] components3 = val.GetComponents(); foreach (MeshFilter val6 in components3) { Object.DestroyImmediate((Object)(object)val6); } bool flag = kind >= PipeKind.CompactChest && kind <= PipeKind.CompactOutput; bool flag2 = kind >= PipeKind.TightBend && kind <= PipeKind.TightDownBend; PipeNode pipeNode = val.AddComponent(); pipeNode.Kind = (flag ? (6 + kind - 16) : kind); bool flag3 = kind == PipeKind.ShortTee || kind == PipeKind.ShortTeeUp || kind == PipeKind.ShortTeeDown || kind == PipeKind.ShortCross || kind == PipeKind.ShortSixWay || kind == PipeKind.ShortTeePlusUp || kind == PipeKind.ShortTeePlusDown; float num2 = ((kind == PipeKind.ShortStraight || flag2 || flag3) ? 0.5f : 1f); float num3 = (flag ? 0.25f : 0.5f); List list = new List(); if (kind == PipeKind.Riser) { list.Add(Vector3.down); list.Add(Vector3.up); } else if (pipeNode.Terminal) { list.Add(Vector3.forward * num3); } else { list.Add(Vector3.back * num2); Vector3 item; switch (kind) { default: item = Vector3.forward * num2; break; case PipeKind.DownBend: case PipeKind.TightDownBend: item = Vector3.down * num2; break; case PipeKind.UpBend: case PipeKind.TightUpBend: item = Vector3.up * num2; break; case PipeKind.Bend: case PipeKind.TightBend: item = Vector3.right * num2; break; } list.Add(item); if (kind == PipeKind.Tee || kind == PipeKind.ShortTee || kind == PipeKind.TeePlusUp || kind == PipeKind.TeePlusDown || kind == PipeKind.ShortTeePlusUp || kind == PipeKind.ShortTeePlusDown) { list.Add(Vector3.right * num2); } if (kind == PipeKind.TeeUp || kind == PipeKind.ShortTeeUp || kind == PipeKind.TeePlusUp || kind == PipeKind.ShortTeePlusUp) { list.Add(Vector3.up * num2); } if (kind == PipeKind.TeeDown || kind == PipeKind.ShortTeeDown || kind == PipeKind.TeePlusDown || kind == PipeKind.ShortTeePlusDown) { list.Add(Vector3.down * num2); } if (kind == PipeKind.Cross || kind == PipeKind.SixWay || kind == PipeKind.ShortCross || kind == PipeKind.ShortSixWay) { list.Add(Vector3.left * num2); list.Add(Vector3.right * num2); } if (kind == PipeKind.SixWay || kind == PipeKind.ShortSixWay) { list.Add(Vector3.up * num2); list.Add(Vector3.down * num2); } } pipeNode.Ends = list.ToArray(); pipeNode.Directions = list.Select((Vector3 e) => ((Vector3)(ref e)).normalized).ToArray(); GameObject val7 = new GameObject("pipe_mesh"); val7.layer = val.layer; val7.transform.SetParent(val.transform, false); PipeModels.Build(val7, pipeNode, kind, num3); Vector3[] ends = pipeNode.Ends; for (int num4 = 0; num4 < ends.Length; num4++) { Vector3 localPosition = ends[num4]; GameObject val8 = new GameObject("_snap"); val8.tag = "snappoint"; val8.transform.SetParent(val.transform, false); val8.transform.localPosition = localPosition; val8.transform.localRotation = Quaternion.FromToRotation(Vector3.forward, ((Vector3)(ref localPosition)).normalized); } if (pipeNode.Terminal) { GameObject val9 = new GameObject("_fp_dock"); val9.tag = "snappoint"; val9.transform.SetParent(val.transform, false); val9.transform.localPosition = Vector3.back * num3; val9.transform.localRotation = Quaternion.Euler(0f, 180f, 0f); } if (kind == PipeKind.Stretch) { val.AddComponent(); } Piece component = val.GetComponent(); component.m_name = Label(kind); MetalPlacement.Apply(component, scene); component.m_category = (PieceCategory)1; component.m_usage = (UsageTagFlags)2050; component.m_description = (pipeNode.Terminal ? "Aim at a matching colored port to snap and align automatically. Connect pipes to the narrow end. Hold the game's alternative-placement control for free placement." : "Aim at a chest port, machine port, or pipe collar to snap automatically. Only physically connected pipes share items."); if (kind == PipeKind.Stretch) { component.m_description = "Click a port or free pipe end to start. Aim to stretch, then click to finish. Select another piece or unequip the Hammer to cancel. One saved piece, up to 16m."; } if (kind == PipeKind.Straight || kind == PipeKind.ShortStraight) { component.m_description = PipeLanguage.Token(component.m_description) + " " + PipeLanguage.Token("While snapping to a pipe: [ / ] turn, Page Up / Page Down tilt, backslash resets. Maximum bend: 15 degrees."); } Sprite val10 = PipeIcons.Render(val7, kind); if (Object.op_Implicit((Object)(object)val10)) { component.m_icon = val10; } component.m_enabled = true; component.m_noClipping = false; component.m_clipEverything = true; component.m_groundOnly = false; component.m_groundPiece = false; component.m_noInWater = false; component.m_craftingStation = scene.GetPrefab("piece_workbench").GetComponent(); Requirement[] array = (Requirement[])(object)new Requirement[2]; Requirement val11 = new Requirement(); val11.m_resItem = scene.GetPrefab("Wood").GetComponent(); val11.m_amount = 2; val11.m_recover = true; array[0] = val11; Requirement val12 = new Requirement(); val12.m_resItem = scene.GetPrefab("Copper").GetComponent(); val12.m_amount = 1; val12.m_recover = true; array[1] = val12; component.m_resources = array; WearNTear component2 = val.GetComponent(); component2.m_new = val7; component2.m_worn = val7; component2.m_broken = val7; component2.m_wet = null; component2.m_noRoofWear = false; component2.m_noSupportWear = false; component2.m_supports = true; component2.m_health = 150f; component2.m_autoCreateFragments = false; component2.m_fragmentRoots = (GameObject[])(object)new GameObject[0]; val.SetActive(true); Plugin.Prefabs.Add(val); } } public static class PipeCollisionPath { public const float Tolerance = 0.01f; public static int[] Simplify(Point[] points) { if (points == null || points.Length < 2) { throw new ArgumentException("A pipe needs two endpoints."); } List list = new List(); list.Add(0); Split(points, 0, points.Length - 1, list); return list.ToArray(); } private static void Split(Point[] p, int first, int last, List keep) { float num = 0.0001f; int num2 = -1; PipeRun one = new PipeRun(0, p[first], p[last]); for (int i = first + 1; i < last; i++) { float num3 = BodyConnections.DistanceSquared(one, new PipeRun(1, p[i], p[i])); if (num3 > num) { num = num3; num2 = i; } } if (num2 < 0) { keep.Add(last); return; } Split(p, first, num2, keep); Split(p, num2, last, keep); } } internal static class PipeDeflection { private static Transform ghost; private static int frame = -1; private static float yaw; private static float pitch; internal static void Update(Transform current) { if ((Object)(object)ghost != (Object)(object)current) { ghost = current; yaw = (pitch = 0f); } if (frame != Time.frameCount) { frame = Time.frameCount; if (ZInput.GetKeyDown((KeyCode)91, false)) { yaw = Mathf.Max(-15f, yaw - 5f); } if (ZInput.GetKeyDown((KeyCode)93, false)) { yaw = Mathf.Min(15f, yaw + 5f); } if (ZInput.GetKeyDown((KeyCode)280, false)) { pitch = Mathf.Min(15f, pitch + 5f); } if (ZInput.GetKeyDown((KeyCode)281, false)) { pitch = Mathf.Max(-15f, pitch - 5f); } if (ZInput.GetKeyDown((KeyCode)92, false)) { yaw = (pitch = 0f); } } } internal static Quaternion Apply(Quaternion aligned, Transform target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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) //IL_004e: 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_003a: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.AngleAxis(yaw, target.up) * Quaternion.AngleAxis(pitch, target.right); float num = Quaternion.Angle(Quaternion.identity, val); if (num > 15f) { val = Quaternion.Slerp(Quaternion.identity, val, 15f / num); } return val * aligned; } } internal static class PipeGuideScheduler { private static HashSet visiblePorts = new HashSet(); private static HashSet nextPorts = new HashSet(); private static HashSet visiblePipes = new HashSet(); private static HashSet nextPipes = new HashSet(); private static GameObject lastGhost; private static Player lastPlayer; private static float nextRefresh; private static bool showing; internal static void Tick() { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; PipeNode ghostNode = PipeSnapping.GhostNode; bool flag = Object.op_Implicit((Object)(object)PipeSnapping.GhostChest); bool flag2 = Object.op_Implicit((Object)(object)ghostNode) && ghostNode.Kind == PipeKind.Stretch; if (!Plugin.RunConnectionChecks.Value || !Object.op_Implicit((Object)(object)localPlayer) || (!Object.op_Implicit((Object)(object)ghostNode) && !flag)) { if (showing) { Hide(); } lastGhost = null; lastPlayer = localPlayer; return; } GameObject ghostObject = PipeSnapping.GhostObject; if (showing && !((Object)(object)ghostObject != (Object)(object)lastGhost) && !((Object)(object)localPlayer != (Object)(object)lastPlayer) && Time.unscaledTime < nextRefresh) { return; } showing = true; lastGhost = ghostObject; lastPlayer = localPlayer; nextRefresh = Time.unscaledTime + 0.15f; Vector3 position = ((Component)localPlayer).transform.position; nextPorts.Clear(); nextPipes.Clear(); foreach (MachinePort item in MachinePort.All) { if (Object.op_Implicit((Object)(object)item) && ((Behaviour)item).isActiveAndEnabled && Object.op_Implicit((Object)(object)item.Target) && !(Vector3.SqrMagnitude(position - ((Component)item).transform.position) >= 225f)) { item.RefreshGuide(placing: true, position); nextPorts.Add(item); } } if (flag || flag2) { float num = (flag2 ? 625f : 225f); foreach (PipeNode item2 in PipeNode.All) { if (Object.op_Implicit((Object)(object)item2) && ((Behaviour)item2).isActiveAndEnabled && item2.Ready && Native.Valid((Component)(object)item2) && !(Vector3.SqrMagnitude(position - ((Component)item2).transform.position) >= num)) { item2.RefreshGuides(flag, flag2, position); nextPipes.Add(item2); } } } foreach (MachinePort visiblePort in visiblePorts) { if (Object.op_Implicit((Object)(object)visiblePort) && !nextPorts.Contains(visiblePort)) { visiblePort.RefreshGuide(placing: false, position); } } foreach (PipeNode visiblePipe in visiblePipes) { if (Object.op_Implicit((Object)(object)visiblePipe) && !nextPipes.Contains(visiblePipe)) { visiblePipe.RefreshGuides(chest: false, stretch: false, position); } } HashSet hashSet = visiblePorts; visiblePorts = nextPorts; nextPorts = hashSet; HashSet hashSet2 = visiblePipes; visiblePipes = nextPipes; nextPipes = hashSet2; } private static void Hide() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) foreach (MachinePort visiblePort in visiblePorts) { if (Object.op_Implicit((Object)(object)visiblePort)) { visiblePort.RefreshGuide(placing: false, Vector3.zero); } } foreach (PipeNode visiblePipe in visiblePipes) { if (Object.op_Implicit((Object)(object)visiblePipe)) { visiblePipe.RefreshGuides(chest: false, stretch: false, Vector3.zero); } } visiblePorts.Clear(); nextPorts.Clear(); visiblePipes.Clear(); nextPipes.Clear(); showing = false; } } internal static class PipeIcons { internal static Sprite Render(GameObject visual, PipeKind kind) { return Load(kind.ToString()); } internal static Sprite Load(string kind) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) string text = "ForgePipes.Icons." + kind + ".png"; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Missing embedded pipe icon: " + text); } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, memoryStream.ToArray(), true)) { throw new InvalidOperationException("Invalid pipe icon: " + text); } ((Object)val).name = "ForgePipes " + kind + " icon"; ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).wrapMode = (TextureWrapMode)1; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 256f); } } internal static class PipeLanguage { private static readonly Dictionary keys; private static readonly List rows; private static readonly MethodInfo addWord; static PipeLanguage() { keys = new Dictionary(); rows = new List(); addWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null); using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ForgePipes.Translations.tsv"); using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); string text; while ((text = streamReader.ReadLine()) != null) { if (text.Length != 0 && !text.StartsWith("#")) { string[] array = text.Split('\t'); if (array.Length != 7) { throw new InvalidDataException("Invalid Forge Pipes translation row."); } keys.Add(array[0], "fp_text_" + rows.Count); rows.Add(array); } } } internal static void Register(Localization localization, string language = null) { if (localization == null) { return; } language = language ?? localization.GetSelectedLanguage(); int num = language switch { "Chinese" => 6, "Russian" => 5, "Portuguese_Brazilian" => 4, "German" => 3, "French" => 2, "Spanish" => 1, _ => 0, }; foreach (string[] row in rows) { addWord.Invoke(localization, new object[2] { keys[row[0]], string.IsNullOrEmpty(row[num]) ? row[0] : row[num] }); } } internal static string Token(string english) { if (english == null || !keys.TryGetValue(english, out var value)) { return english; } return "$" + value; } internal static string Text(string english) { return Localization.instance.Localize(Token(english)); } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class PipeLanguageSetupPatch { private static void Postfix(Localization __instance, string __0) { PipeLanguage.Register(__instance, __0); } } [HarmonyPatch(typeof(Localization), "Localize", new Type[] { typeof(string) })] internal static class PipeLanguageTextPatch { private static void Prefix(ref string __0) { __0 = PipeLanguage.Token(__0); } } public sealed class PipeMeshData { public readonly List Vertices = new List(); public readonly List Normals = new List(); public readonly List U = new List(); public readonly List V = new List(); public readonly List Triangles = new List(); private void Vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v) { Vertices.Add(new Point(x, y, z)); Normals.Add(new Point(nx, ny, nz)); U.Add(u); V.Add(v); } private void Face(int a, int b, int c) { Triangles.Add(a); Triangles.Add(b); Triangles.Add(c); } public static PipeMeshData Cylinder(int sides = 12) { if (sides < 3) { throw new ArgumentOutOfRangeException("sides"); } PipeMeshData pipeMeshData = new PipeMeshData(); for (int i = 0; i <= sides; i++) { double num = (double)i * Math.PI * 2.0 / (double)sides; float num2 = (float)Math.Cos(num); float num3 = (float)Math.Sin(num); pipeMeshData.Vertex(num2 * 0.5f, -1f, num3 * 0.5f, num2, 0f, num3, (float)i / (float)sides, 0f); pipeMeshData.Vertex(num2 * 0.5f, 1f, num3 * 0.5f, num2, 0f, num3, (float)i / (float)sides, 1f); if (i < sides) { int num4 = i * 2; pipeMeshData.Face(num4, num4 + 1, num4 + 3); pipeMeshData.Face(num4, num4 + 3, num4 + 2); } } for (int j = -1; j <= 1; j += 2) { int count = pipeMeshData.Vertices.Count; pipeMeshData.Vertex(0f, j, 0f, 0f, j, 0f, 0.5f, 0.5f); for (int k = 0; k <= sides; k++) { double num5 = (double)k * Math.PI * 2.0 / (double)sides; float num6 = (float)Math.Cos(num5) * 0.5f; float num7 = (float)Math.Sin(num5) * 0.5f; pipeMeshData.Vertex(num6, j, num7, 0f, j, 0f, num6 + 0.5f, num7 + 0.5f); if (k < sides) { if (j > 0) { pipeMeshData.Face(count, count + k + 2, count + k + 1); } else { pipeMeshData.Face(count, count + k + 1, count + k + 2); } } } } return pipeMeshData; } public static PipeMeshData Joint(int sides = 8, int rings = 4) { if (sides < 3 || rings < 2) { throw new ArgumentOutOfRangeException(); } PipeMeshData pipeMeshData = new PipeMeshData(); for (int i = 0; i <= rings; i++) { for (int j = 0; j <= sides; j++) { double num = (double)j * Math.PI * 2.0 / (double)sides; double num2 = (double)i * Math.PI / (double)rings; float num3 = (float)(Math.Sin(num2) * Math.Cos(num)); float num4 = (float)Math.Cos(num2); float num5 = (float)(Math.Sin(num2) * Math.Sin(num)); pipeMeshData.Vertex(num3 * 0.5f, num4 * 0.5f, num5 * 0.5f, num3, num4, num5, (float)j / (float)sides, (float)i / (float)rings); if (i < rings && j < sides) { int num6 = i * (sides + 1) + j; int num7 = num6 + sides + 1; if (i > 0) { pipeMeshData.Face(num6, num6 + 1, num7 + 1); } if (i < rings - 1) { pipeMeshData.Face(num6, num7 + 1, num7); } } } } return pipeMeshData; } } internal static class PipeMeshes { private static Mesh cylinder; private static Mesh joint; internal static Mesh Cylinder { get { if (!Object.op_Implicit((Object)(object)cylinder)) { return cylinder = Create(PipeMeshData.Cylinder(), "ForgePipes cylinder 12"); } return cylinder; } } internal static Mesh Joint { get { if (!Object.op_Implicit((Object)(object)joint)) { return joint = Create(PipeMeshData.Joint(), "ForgePipes joint 8x4"); } return joint; } } private static Mesh Create(PipeMeshData data, string name) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown Mesh val = new Mesh(); ((Object)val).name = name; val.vertices = ((IEnumerable)data.Vertices).Select((Func)((Point p) => new Vector3(p.X, p.Y, p.Z))).ToArray(); val.normals = ((IEnumerable)data.Normals).Select((Func)((Point p) => new Vector3(p.X, p.Y, p.Z))).ToArray(); val.uv = ((IEnumerable)data.U).Select((Func)((float u, int i) => new Vector2(u, data.V[i]))).ToArray(); val.triangles = data.Triangles.ToArray(); val.RecalculateBounds(); val.RecalculateTangents(); return val; } internal static void BakePrefab(GameObject visual) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown MeshRenderer[] componentsInChildren = visual.GetComponentsInChildren(true); foreach (IGrouping item in from r in componentsInChildren group r by ((Renderer)r).sharedMaterial) { Mesh val = new Mesh(); ((Object)val).name = "ForgePipes shared fixed mesh"; val.CombineMeshes(item.Select(delegate(MeshRenderer r) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0041: Unknown result type (might be due to invalid IL or missing references) CombineInstance result = default(CombineInstance); ((CombineInstance)(ref result)).mesh = ((Component)r).GetComponent().sharedMesh; ((CombineInstance)(ref result)).transform = visual.transform.worldToLocalMatrix * ((Component)r).transform.localToWorldMatrix; return result; }).ToArray(), true, true); GameObject val2 = new GameObject("FP_shared_visual"); val2.layer = visual.layer; val2.transform.SetParent(visual.transform, false); val2.AddComponent().sharedMesh = val; ((Renderer)val2.AddComponent()).sharedMaterial = item.Key; } MeshRenderer[] array = componentsInChildren; foreach (MeshRenderer val3 in array) { Object.DestroyImmediate((Object)(object)val3); } } } internal static class PipeMeshUpload { private static readonly List vertices = new List(); private static readonly List normals = new List(); private static readonly List uv = new List(); internal static Mesh Create(SweptPipeMesh data, string name, bool dynamic = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown Mesh val = new Mesh(); ((Object)val).name = name; if (dynamic) { val.MarkDynamic(); } try { Update(val, data); return val; } catch { Object.Destroy((Object)(object)val); throw; } } internal static void Update(Mesh mesh, SweptPipeMesh data) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)mesh)) { throw new ArgumentNullException("mesh"); } if (data == null) { throw new ArgumentNullException("data"); } int count = data.Vertices.Count; if (data.Normals.Count != count || data.U.Count != count || data.V.Count != count || data.Surface.Count != count) { throw new ArgumentException("Pipe geometry attributes do not match the vertex count.", "data"); } vertices.Clear(); normals.Clear(); uv.Clear(); for (int i = 0; i < count; i++) { Point point = data.Vertices[i]; Point point2 = data.Normals[i]; vertices.Add(new Vector3(point.X, point.Y, point.Z)); normals.Add(new Vector3(point2.X, point2.Y, point2.Z)); uv.Add(new Vector2(CopperMaterial.AtlasU(data.U[i], data.Surface[i]), data.V[i])); } mesh.Clear(); IndexFormat val = (IndexFormat)(count > 65535); if (mesh.indexFormat != val) { mesh.indexFormat = val; } mesh.SetVertices(vertices); mesh.SetNormals(normals); mesh.SetUVs(0, uv); mesh.SetTriangles(data.Triangles, 0, true); mesh.RecalculateTangents(); } } internal static class PipeModels { internal static void Build(GameObject visual, PipeNode node, PipeKind kind, float connector) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: 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_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) List list = new List(); SweptPipeMesh sweptPipeMesh; switch (kind) { case PipeKind.Stretch: { StretchShape stretchShape = new StretchShape(new Point(0f, 0f, -1f), new Point(0f, 0f, 1f), new Point(0f, 0f, -1f), new Point(0f, 0f, 1f)); list.Add(stretchShape.Sample()); sweptPipeMesh = StretchMesh(stretchShape); break; } case PipeKind.TightBend: case PipeKind.TightUpBend: case PipeKind.TightDownBend: { Point[] array4 = TightElbowShape.Path(ToPoint(node.Directions[1])); list.Add(array4); sweptPipeMesh = SweptPipeMesh.Build(array4, 0.16f, 0.22f, 0.22f, 0.1f, 12, ToPoint(-node.Directions[0]), ToPoint(node.Directions[1])); break; } default: { if (kind == PipeKind.Bend || kind == PipeKind.UpBend || kind == PipeKind.DownBend) { Point[] array = new Point[13]; Vector3 val = node.Ends[1]; for (int i = 0; i < array.Length; i++) { float num = (float)i * (float)Math.PI / 24f; ref Point reference = ref array[i]; reference = ToPoint(val * (1f - Mathf.Cos(num)) + Vector3.back * (1f - Mathf.Sin(num))); } list.Add(array); sweptPipeMesh = SweptPipeMesh.Build(array, 0.16f, 0.22f, 0.22f, 0.13f, 12, ToPoint(-node.Directions[0]), ToPoint(node.Directions[1])); break; } if (node.Terminal) { Point[] array2 = new Point[2] { new Point(0f, 0f, 0f - connector), new Point(0f, 0f, connector) }; list.Add(array2); sweptPipeMesh = SweptPipeMesh.Build(array2, 0.19f, 0.3f, 0.22f); break; } Point[] array3 = new Point[2] { ToPoint(node.Ends[0]), ToPoint(node.Ends[1]) }; list.Add(array3); sweptPipeMesh = SweptPipeMesh.Build(array3, 0.16f, 0.22f, 0.22f); for (int j = 2; j < node.Ends.Length; j++) { array3 = new Point[2] { new Point(0f, 0f, 0f), ToPoint(node.Ends[j]) }; list.Add(array3); sweptPipeMesh.Append(SweptPipeMesh.Build(array3, 0.16f, 0.16f, 0.22f)); } break; } } visual.AddComponent().sharedMesh = PipeMeshUpload.Create(sweptPipeMesh, "ForgePipes " + kind); ((Renderer)visual.AddComponent()).sharedMaterial = CopperMaterial.GetPipe(); SetCenterlines(node, list); foreach (Point[] item in list) { CreateColliders(visual.transform, item, node.Terminal ? 0.19f : 0.16f); } } internal static SweptPipeMesh StretchMesh(StretchShape shape) { return SweptPipeMesh.Build(shape.Sample(), 0.16f, 0.22f, 0.22f, 0.13f, 12, new Point(0f - shape.StartNormal.X, 0f - shape.StartNormal.Y, 0f - shape.StartNormal.Z), shape.EndNormal); } internal static Mesh DistantMesh(PipeNode node) { //IL_003b: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) int num = node.RunStarts.Length - node.Ends.Length - (node.Terminal ? 1 : 0); SweptPipeMesh sweptPipeMesh = null; int num2 = 0; while (num2 < num) { List list = new List(); list.Add(ToPoint(node.RunStarts[num2])); list.Add(ToPoint(node.RunEnds[num2++])); while (num2 < num) { Vector3 val = ToVector(list[list.Count - 1]) - node.RunStarts[num2]; if (!(((Vector3)(ref val)).sqrMagnitude < 1E-06f)) { break; } list.Add(ToPoint(node.RunEnds[num2++])); } SweptPipeMesh sweptPipeMesh2 = SweptPipeMesh.Build(list.ToArray(), node.Terminal ? 0.19f : 0.16f, node.Terminal ? 0.3f : 0.22f, 0.22f, 0.13f, 6); if (sweptPipeMesh == null) { sweptPipeMesh = sweptPipeMesh2; } else { sweptPipeMesh.Append(sweptPipeMesh2); } } return PipeMeshUpload.Create(sweptPipeMesh, "ForgePipes distant six-sided mesh"); } internal static Point ToPoint(Vector3 p) { return new Point(p.x, p.y, p.z); } internal static Vector3 ToVector(Point p) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(p.X, p.Y, p.Z); } internal static void SetCenterlines(PipeNode node, IEnumerable paths) { //IL_0031: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) List list = new List(); List list2 = new List(); foreach (Point[] path in paths) { for (int i = 1; i < path.Length; i++) { list.Add(ToVector(path[i - 1])); list2.Add(ToVector(path[i])); } } for (int j = 0; j < node.Ends.Length; j++) { list.Add(node.Ends[j] - node.Directions[j] * 0.13f); list2.Add(node.Ends[j]); } if (node.Terminal) { Vector3 val = -node.Ends[0]; list.Add(val); list2.Add(val + Vector3.forward * 0.15f); } node.RunStarts = list.ToArray(); node.RunEnds = list2.ToArray(); } internal static BoxCollider[] CreateColliders(Transform parent, Point[] path, float radius) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown int[] array = PipeCollisionPath.Simplify(path); BoxCollider[] array2 = (BoxCollider[])(object)new BoxCollider[array.Length - 1]; for (int i = 0; i < array2.Length; i++) { GameObject val = new GameObject("pipe_collider"); val.layer = ((Component)parent).gameObject.layer; val.transform.SetParent(parent, false); array2[i] = val.AddComponent(); SetCollider(array2[i], path[array[i]], path[array[i + 1]], radius); } return array2; } internal static void SetCollider(BoxCollider collider, Point a, Point b, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_000e: 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_0010: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_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_0080: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ToVector(a); Vector3 val2 = ToVector(b); Vector3 val3 = val2 - val; ((Component)collider).transform.localPosition = (val + val2) * 0.5f; ((Component)collider).transform.localRotation = Quaternion.FromToRotation(Vector3.forward, ((Vector3)(ref val3)).normalized); collider.size = new Vector3(radius * 2f + 0.02f, radius * 2f + 0.02f, Mathf.Max(0.002f, ((Vector3)(ref val3)).magnitude) + 0.02f); ((Collider)collider).enabled = true; } } internal static class PipeRenderGroups { private sealed class Entry { internal PipeNode Node; internal Mesh LowMesh; internal MeshRenderer Renderer; internal MeshFilter Filter; internal Group Group; internal Material Material; internal int Revision; internal Vector3 Position; internal Quaternion Rotation; } private sealed class Group { internal string Key; internal Material Material; internal readonly List Entries = new List(); internal GameObject Object; internal Mesh Mesh; internal Mesh LowMesh; internal MeshRenderer LowRenderer; internal MeshRenderer Renderer; internal bool Dirty = true; internal int Vertices; } private static readonly Dictionary entries = new Dictionary(); private static readonly List groups = new List(); private static float nextScan; private static float nextVisibility; private static bool lastBuilding; private static bool lastVisible; private static bool lastCombined; private static ZNetScene scene; private static bool failed; internal static int RendererCount; internal static int TriangleCount; internal static void Changed(PipeNode node) { if (entries.TryGetValue(node, out var value)) { Invalidate(value.Group); } nextScan = 0f; } internal static void Remove(PipeNode node) { if (entries.TryGetValue(node, out var value)) { Invalidate(value.Group); value.Group.Entries.Remove(value); if (Object.op_Implicit((Object)(object)value.Filter) && Object.op_Implicit((Object)(object)value.Filter.sharedMesh)) { value.Group.Vertices -= value.Filter.sharedMesh.vertexCount; } if (Object.op_Implicit((Object)(object)value.Renderer)) { ((Renderer)value.Renderer).forceRenderingOff = false; } if (Object.op_Implicit((Object)(object)value.LowMesh)) { Object.Destroy((Object)(object)value.LowMesh); } entries.Remove(node); } } private static void Invalidate(Group g) { g.Dirty = true; if (Object.op_Implicit((Object)(object)g.Renderer)) { ((Renderer)g.Renderer).enabled = false; } if (Object.op_Implicit((Object)(object)g.LowRenderer)) { ((Renderer)g.LowRenderer).enabled = false; } foreach (Entry entry in g.Entries) { if (Object.op_Implicit((Object)(object)entry.Renderer)) { ((Renderer)entry.Renderer).forceRenderingOff = false; } } } private static void Clear() { foreach (Entry value in entries.Values) { if (Object.op_Implicit((Object)(object)value.Renderer)) { ((Renderer)value.Renderer).forceRenderingOff = false; } if (Object.op_Implicit((Object)(object)value.LowMesh)) { Object.Destroy((Object)(object)value.LowMesh); } } foreach (Group group in groups) { if (Object.op_Implicit((Object)(object)group.Object)) { Object.Destroy((Object)(object)group.Object); } if (Object.op_Implicit((Object)(object)group.Mesh)) { Object.Destroy((Object)(object)group.Mesh); } if (Object.op_Implicit((Object)(object)group.LowMesh)) { Object.Destroy((Object)(object)group.LowMesh); } } groups.Clear(); entries.Clear(); nextScan = 0f; } internal static void SafeTick() { if (failed) { return; } try { Tick(); } catch (Exception ex) { Clear(); failed = true; Plugin.GroupRendering.Value = false; Plugin.Error("Pipe visual optimization fell back to individual native renderers. Automation remains enabled. Restart after reporting this error: " + ex); } } private static void Tick() { //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04be: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0163: 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_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_0594: Unknown result type (might be due to invalid IL or missing references) //IL_05af: Unknown result type (might be due to invalid IL or missing references) //IL_05b4: Unknown result type (might be due to invalid IL or missing references) //IL_05b8: Unknown result type (might be due to invalid IL or missing references) //IL_06d7: Unknown result type (might be due to invalid IL or missing references) //IL_06dc: Unknown result type (might be due to invalid IL or missing references) //IL_06e0: Unknown result type (might be due to invalid IL or missing references) if (!object.ReferenceEquals(scene, ZNetScene.instance)) { Clear(); scene = ZNetScene.instance; if (!Object.op_Implicit((Object)(object)scene)) { Routing.Reset(); InventoryWake.Clear(); CookingCredit.Clear(); } } if (!Object.op_Implicit((Object)(object)scene)) { return; } long timestamp = Stopwatch.GetTimestamp(); if (Time.unscaledTime >= nextScan) { nextScan = Time.unscaledTime + 0.5f; PipeNode[] array = entries.Keys.Where((PipeNode n) => !Object.op_Implicit((Object)(object)n) || !((Behaviour)n).isActiveAndEnabled || !Native.Valid((Component)(object)n)).ToArray(); foreach (PipeNode node in array) { Remove(node); } int num2 = 0; foreach (PipeNode item in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Ready || !Native.Valid((Component)(object)item)) { continue; } if (entries.TryGetValue(item, out var value)) { if (value.Revision == item.VisualRevision && Object.op_Implicit((Object)(object)value.Renderer) && (Object)(object)value.Material == (Object)(object)((Renderer)value.Renderer).sharedMaterial && value.Position == ((Component)item).transform.position && value.Rotation == ((Component)item).transform.rotation) { continue; } Remove(item); } if (num2++ >= 8) { nextScan = 0f; break; } Transform val = ((Component)item).transform.Find("pipe_mesh"); if (!Object.op_Implicit((Object)(object)val)) { continue; } MeshRenderer component = ((Component)val).GetComponent(); MeshFilter component2 = ((Component)val).GetComponent(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component2.sharedMesh)) { Vector3 position = ((Component)item).transform.position; Material sharedMaterial = ((Renderer)component).sharedMaterial; string key = Mathf.FloorToInt(position.x / 12f) + ":" + Mathf.FloorToInt(position.y / 12f) + ":" + Mathf.FloorToInt(position.z / 12f) + ":" + ((Object)sharedMaterial).GetInstanceID(); int vertices = component2.sharedMesh.vertexCount; Group obj = groups.FirstOrDefault((Group g) => g.Key == key && g.Vertices + vertices < 60000); if (obj == null) { Group obj2 = new Group(); obj2.Key = key; obj2.Material = sharedMaterial; obj = obj2; groups.Add(obj); } Invalidate(obj); Entry entry = new Entry(); entry.Node = item; entry.Renderer = component; entry.Filter = component2; entry.Group = obj; entry.Material = sharedMaterial; entry.LowMesh = PipeModels.DistantMesh(item); entry.Revision = item.VisualRevision; entry.Position = position; entry.Rotation = ((Component)item).transform.rotation; value = entry; entries.Add(item, value); obj.Entries.Add(value); obj.Vertices += vertices; } } } if (Plugin.GroupRendering.Value) { Group obj3 = groups.FirstOrDefault((Group g) => g.Dirty && g.Entries.Count > 0); if (obj3 != null) { Build(obj3); } } GameObject ghostObject = PipeSnapping.GhostObject; bool flag = Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).InPlaceMode() && Object.op_Implicit((Object)(object)ghostObject) && ghostObject.activeInHierarchy; bool value2 = Plugin.RenderPipes.Value; bool flag2 = Plugin.GroupRendering.Value && !flag; if (!(Time.unscaledTime >= nextVisibility) && flag == lastBuilding && value2 == lastVisible && flag2 == lastCombined) { PerformanceStats.Section("visuals", timestamp); return; } nextVisibility = Time.unscaledTime + 0.15f; lastBuilding = flag; lastVisible = value2; lastCombined = flag2; Vector3 val2 = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero); float num3 = Mathf.Max(30f, Plugin.PipeViewDistance.Value); RendererCount = (TriangleCount = 0); foreach (Group group in groups) { if (group.Entries.Count == 0) { if (Object.op_Implicit((Object)(object)group.Renderer)) { ((Renderer)group.Renderer).enabled = false; } if (Object.op_Implicit((Object)(object)group.LowRenderer)) { ((Renderer)group.LowRenderer).enabled = false; } continue; } bool flag3 = value2 && flag2 && !group.Dirty && Object.op_Implicit((Object)(object)group.Renderer); if (flag3) { Bounds bounds = ((Renderer)group.Renderer).bounds; flag3 = ((Bounds)(ref bounds)).SqrDistance(val2) < num3 * num3; } int num4; if (flag3) { Bounds bounds2 = ((Renderer)group.Renderer).bounds; num4 = ((((Bounds)(ref bounds2)).SqrDistance(val2) > 1225f) ? 1 : 0); } else { num4 = 0; } bool flag4 = (byte)num4 != 0; if (Object.op_Implicit((Object)(object)group.Renderer)) { ((Renderer)group.Renderer).enabled = flag3 && !flag4; ((Renderer)group.Renderer).shadowCastingMode = (ShadowCastingMode)(Plugin.PipeShadows.Value ? 1 : 0); } if (Object.op_Implicit((Object)(object)group.LowRenderer)) { ((Renderer)group.LowRenderer).enabled = flag4; ((Renderer)group.LowRenderer).shadowCastingMode = (ShadowCastingMode)(Plugin.PipeShadows.Value ? 1 : 0); } if (flag3) { RendererCount++; TriangleCount += (int)(flag4 ? group.LowMesh : group.Mesh).GetIndexCount(0) / 3; } foreach (Entry entry2 in group.Entries) { if (Object.op_Implicit((Object)(object)entry2.Renderer)) { ((Renderer)entry2.Renderer).shadowCastingMode = (ShadowCastingMode)(Plugin.PipeShadows.Value ? 1 : 0); int num5; if (value2 && (!flag2 || group.Dirty)) { Bounds bounds3 = ((Renderer)entry2.Renderer).bounds; num5 = ((((Bounds)(ref bounds3)).SqrDistance(val2) < num3 * num3) ? 1 : 0); } else { num5 = 0; } bool flag5 = (byte)num5 != 0; ((Renderer)entry2.Renderer).forceRenderingOff = !flag5; } } } for (int num6 = groups.Count - 1; num6 >= 0; num6--) { if (groups[num6].Entries.Count == 0) { Group obj4 = groups[num6]; if (Object.op_Implicit((Object)(object)obj4.Object)) { Object.Destroy((Object)(object)obj4.Object); } if (Object.op_Implicit((Object)(object)obj4.Mesh)) { Object.Destroy((Object)(object)obj4.Mesh); } if (Object.op_Implicit((Object)(object)obj4.LowMesh)) { Object.Destroy((Object)(object)obj4.LowMesh); } groups.RemoveAt(num6); } } PerformanceStats.Section("visuals", timestamp); } private static void Build(Group g) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_0058: 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_0087: 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) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown List list = new List(); List list2 = new List(); foreach (Entry entry in g.Entries) { if (Object.op_Implicit((Object)(object)entry.Node) && Object.op_Implicit((Object)(object)entry.Filter) && Object.op_Implicit((Object)(object)entry.Filter.sharedMesh)) { CombineInstance item = default(CombineInstance); ((CombineInstance)(ref item)).mesh = entry.Filter.sharedMesh; ((CombineInstance)(ref item)).transform = ((Component)entry.Filter).transform.localToWorldMatrix; list.Add(item); CombineInstance item2 = default(CombineInstance); ((CombineInstance)(ref item2)).mesh = entry.LowMesh; ((CombineInstance)(ref item2)).transform = ((Component)entry.Filter).transform.localToWorldMatrix; list2.Add(item2); } } if (!Object.op_Implicit((Object)(object)g.Object)) { g.Object = new GameObject("ForgePipes local visual group"); g.Object.layer = ((Component)g.Entries[0].Node).gameObject.layer; g.Object.AddComponent(); g.Renderer = g.Object.AddComponent(); ((Renderer)g.Renderer).sharedMaterial = g.Material; GameObject val = new GameObject("Distant"); val.layer = g.Object.layer; val.transform.SetParent(g.Object.transform, false); val.AddComponent(); g.LowRenderer = val.AddComponent(); ((Renderer)g.LowRenderer).sharedMaterial = g.Material; ((Renderer)g.LowRenderer).enabled = false; } if (Object.op_Implicit((Object)(object)g.Mesh)) { Object.Destroy((Object)(object)g.Mesh); } if (Object.op_Implicit((Object)(object)g.LowMesh)) { Object.Destroy((Object)(object)g.LowMesh); } Mesh val2 = new Mesh(); ((Object)val2).name = "ForgePipes combined cell"; g.Mesh = val2; g.Mesh.CombineMeshes(list.ToArray(), true, true); g.Mesh.RecalculateBounds(); Mesh val3 = new Mesh(); ((Object)val3).name = "ForgePipes distant cell"; g.LowMesh = val3; g.LowMesh.CombineMeshes(list2.ToArray(), true, true); g.LowMesh.RecalculateBounds(); ((Component)g.LowRenderer).GetComponent().sharedMesh = g.LowMesh; g.Object.GetComponent().sharedMesh = g.Mesh; g.Dirty = false; ((Renderer)g.Renderer).enabled = false; nextVisibility = 0f; } } internal static class PipeStructure { internal static bool Anchored(WearNTear wear) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)((Component)wear).GetComponent())) { return true; } Container componentInChildren = ((Component)wear).GetComponentInChildren(); if (!ChestHandoff.Eligible(componentInChildren)) { return false; } if (!Routing.Dirty) { return Routing.HasChestAnchor(componentInChildren); } PortFactory.Chest(componentInChildren); MachinePort[] componentsInChildren = ((Component)Native.PieceOf((Component)(object)componentInChildren)).GetComponentsInChildren(true); foreach (PipeNode item in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Ready || !Native.Valid((Component)(object)item) || Vector3.SqrMagnitude(((Component)item).transform.position - ((Component)componentInChildren).transform.position) > 400f) { continue; } MachinePort[] array = componentsInChildren; foreach (MachinePort machinePort in array) { if (!Object.op_Implicit((Object)(object)machinePort) || (Object)(object)machinePort.Target != (Object)(object)componentInChildren || machinePort.Preview) { continue; } if (item.Terminal) { if (item.Kind == PipeKind.Chest && PortFactory.Matches(item.Dock, -((Component)item).transform.forward, ((Component)machinePort).transform)) { return true; } continue; } for (int j = 0; j < item.Ends.Length; j++) { if (PortFactory.Matches(((Component)item).transform.TransformPoint(item.Ends[j]), ((Component)item).transform.TransformDirection(item.Directions[j]), ((Component)machinePort).transform)) { return true; } } } } return false; } } [HarmonyPatch(typeof(WearNTear), "UpdateSupport")] internal static class PipeStructuralSupportPatch { private static bool Prefix(WearNTear __instance, ref float ___m_support) { if (!Native.Owner((Component)(object)__instance) || !PipeStructure.Anchored(__instance)) { return true; } ___m_support = 1500f; Native.View((Component)(object)__instance).GetZDO().Set(ZDOVars.s_support, ___m_support); return false; } } [HarmonyPatch(typeof(WearNTear), "GetSupport")] internal static class PipeSupportValuePatch { private static bool Prefix(WearNTear __instance, ref float __result) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent())) { return true; } __result = 1500f; return false; } } public sealed class PipeTint : MonoBehaviour { private int last = int.MinValue; private Renderer[] renderers; private Material[] originals; private static readonly Dictionary> variants = new Dictionary>(); internal void Invalidate() { last = int.MinValue; renderers = null; originals = null; } internal void Restore() { if (renderers != null) { for (int i = 0; i < renderers.Length; i++) { if (Object.op_Implicit((Object)(object)renderers[i])) { renderers[i].sharedMaterial = originals[i]; } } } Invalidate(); } internal void Apply(int kind) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) if (last == kind) { return; } last = kind; PipeNode component = ((Component)this).GetComponent(); if (Object.op_Implicit((Object)(object)component)) { PipeRenderGroups.Changed(component); } if (renderers == null) { renderers = (Renderer[])(object)((Component)this).GetComponentsInChildren(true); originals = (Material[])(object)new Material[renderers.Length]; for (int i = 0; i < renderers.Length; i++) { originals[i] = renderers[i].sharedMaterial; } } for (int j = 0; j < renderers.Length; j++) { Material val = originals[j]; if (!Object.op_Implicit((Object)(object)renderers[j]) || !Object.op_Implicit((Object)(object)val) || !val.HasProperty("_Color")) { continue; } if (kind < 0) { renderers[j].sharedMaterial = val; continue; } if (!variants.TryGetValue(val, out var value)) { value = new Dictionary(); variants[val] = value; } if (!value.TryGetValue(kind, out var value2)) { value2 = new Material(val); ((Object)value2).name = ((Object)val).name + " port " + kind; value2.SetColor("_Color", Pieces.PortColor((PipeKind)kind)); value[kind] = value2; } renderers[j].sharedMaterial = value2; } } } [BepInPlugin("night.forgepipes", "Forge Pipes", "1.1.4")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "night.forgepipes"; internal static Plugin Instance; internal static ConfigEntry ColorByPort; internal static ConfigEntry ConnectOverlaps; internal static ConfigEntry CollectLooseItems; internal static ConfigEntry CookingCollectionXP; internal static ConfigEntry MatteCopper; internal static ConfigEntry PerformanceLogging; internal static ConfigEntry LeaveLastItem; internal static ConfigEntry KilnFineWood; internal static ConfigEntry KilnCoreWood; internal static ConfigEntry HoneyAchievement; internal static ConfigEntry GroupRendering; internal static ConfigEntry PipeShadows; internal static ConfigEntry RenderPipes; internal static ConfigEntry RunAutomation; internal static ConfigEntry RunConnectionChecks; internal static ConfigEntry WorkBudgetMs; internal static ConfigEntry PipeViewDistance; internal static readonly HashSet Machines = new HashSet(); internal static readonly HashSet Chests = new HashSet(); internal static readonly List Prefabs = new List(); internal static GameObject PrefabRoot; private Harmony harmony; private float nextShapePoll; private void LateUpdate() { if (RunConnectionChecks.Value) { StretchPlacement.Tick(); Container ghostChest = PipeSnapping.GhostChest; if (Object.op_Implicit((Object)(object)ghostChest)) { PortFactory.Chest(ghostChest, preview: true); } } PipeGuideScheduler.Tick(); } private void Awake() { //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Expected O, but got Unknown Instance = this; ColorByPort = ((BaseUnityPlugin)this).Config.Bind("Appearance", "ColorByMachinePort", true, "Tint a pipe network to its single machine port type. Mixed networks retain copper. Updates on connectivity refresh."); ConnectOverlaps = ((BaseUnityPlugin)this).Config.Bind("Routing", "ConnectOverlappingPipes", true, "Join pipe runs whose centerlines intersect within 8 cm. Crossing supply/output routes will share a network. Use the same setting on all peers."); CollectLooseItems = ((BaseUnityPlugin)this).Config.Bind("Routing", "CollectLooseItems", true, "Collect dropped items within 0.85 m of free pipe ends, attached output ends and hopper mouths. Use the same setting on all peers."); CookingCollectionXP = ((BaseUnityPlugin)this).Config.Bind("Features", "CookingCollectionXP", true, "Award native cooking skill progress when newly automated cooked food is collected from storage. Restart after changing."); MatteCopper = ((BaseUnityPlugin)this).Config.Bind("Appearance", "MatteCopper", true, "Use rough copper without normal and metallic maps. Restart after changing."); PerformanceLogging = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "PerformanceLogging", true, "Write a compact performance sample every 30 seconds while in a world."); ConnectOverlaps.SettingChanged += delegate { Routing.Dirty = true; }; ColorByPort.SettingChanged += delegate { Routing.Dirty = true; }; LeaveLastItem = ((BaseUnityPlugin)this).Config.Bind("Storage", "LeaveLastItem", true, "Keep one of each item type/quality/world level in source chests for quick stacking. Applies to automation only; hoppers can empty. Use the same setting on all peers."); KilnFineWood = ((BaseUnityPlugin)this).Config.Bind("Kiln", "AllowFineWood", true, "Allow pipe automation to consume Fine Wood in charcoal kilns. Off protects valuable wood. Use the same setting on all peers."); KilnCoreWood = ((BaseUnityPlugin)this).Config.Bind("Kiln", "AllowCoreWood", true, "Allow pipe automation to consume Core Wood in charcoal kilns. Off protects valuable wood. Use the same setting on all peers."); HoneyAchievement = ((BaseUnityPlugin)this).Config.Bind("Features", "HoneyHarvestCredit", true, "Credit newly automated hive harvests when a player collects the honey from a chest. Existing honey is not retroactively credited."); GroupRendering = ((BaseUnityPlugin)this).Config.Bind("Performance", "GroupPipeRendering", true, "Combine nearby placed pipe visuals by material. Individual pieces remain selectable. Local visual setting."); PipeShadows = ((BaseUnityPlugin)this).Config.Bind("Appearance", "PipeShadows", false, "Allow pipes to cast shadows. Turning this off reduces shadow rendering work."); PipeViewDistance = ((BaseUnityPlugin)this).Config.Bind("Performance", "PipeViewDistance", 100f, "Hide pipe visuals beyond this distance in meters (minimum 30). Does not stop automation."); WorkBudgetMs = ((BaseUnityPlugin)this).Config.Bind("Performance", "AutomationBudgetMs", 1.5f, "Soft per-frame automation budget in milliseconds. One atomic inventory transaction may exceed it."); RenderPipes = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "RenderPipes", true, "Temporary A/B test: turn off only pipe visuals. No objects are deleted."); RunAutomation = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "RunAutomation", true, "Temporary A/B test: pause pipe item transfers. Machines continue native production."); RunConnectionChecks = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "RunConnectionChecks", true, "Temporary A/B test: pause graph rebuilding, snap indicators and snapping. Restart/reenable before building."); PerformanceConsole.Register(); harmony = new Harmony("night.forgepipes"); Type[] types = typeof(Plugin).Assembly.GetTypes(); foreach (Type type in types) { if (type.IsDefined(typeof(HarmonyPatch), inherit: false)) { try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Feature hook unavailable: " + type.Name + ". Other features will continue. " + ex)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Forge Pipes 1.1.4: grouped visuals, budgeted automation and storage options."); } private void Update() { PerformanceStats.Frame(); if (Object.op_Implicit((Object)(object)ZNetScene.instance) && Time.time >= nextShapePoll) { nextShapePoll = Time.time + 0.5f; StretchPipe.PollAll(); } PipeRenderGroups.SafeTick(); if (Object.op_Implicit((Object)(object)ZNetScene.instance)) { AutomationScheduler.Tick(); } } private void OnDestroy() { if (harmony == null) { return; } try { harmony.UnpatchSelf(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Another mod prevented hook cleanup: " + ex.Message)); } } internal static void Error(string text) { if (Object.op_Implicit((Object)(object)Instance)) { ((BaseUnityPlugin)Instance).Logger.LogError((object)text); } } internal static void LogInfo(string text) { if (Object.op_Implicit((Object)(object)Instance)) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)text); } } } internal static class Native { internal static readonly MethodInfo Queue = AccessTools.Method(typeof(Smelter), "QueueOre", (Type[])null, (Type[])null); internal static readonly MethodInfo QueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null); internal static readonly MethodInfo Fuel = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null); internal static readonly MethodInfo SetFuel = AccessTools.Method(typeof(Smelter), "SetFuel", (Type[])null, (Type[])null); internal static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); internal static readonly MethodInfo Save = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null); internal static string Name(GameObject g) { return ((Object)g).name.Replace("(Clone)", "").Trim(); } internal static ZNetView View(Component c) { if (!Object.op_Implicit((Object)(object)c)) { return null; } return c.GetComponent() ?? c.GetComponentInParent(); } internal static string MachineName(Smelter s) { ZNetView val = View((Component)(object)s); return Name(Object.op_Implicit((Object)(object)val) ? ((Component)val).gameObject : ((Component)s).gameObject); } internal static Piece PieceOf(Component c) { if (!Object.op_Implicit((Object)(object)c)) { return null; } return c.GetComponent() ?? c.GetComponentInParent(); } internal static bool Valid(Component c) { ZNetView val = View(c); if (Object.op_Implicit((Object)(object)val)) { return val.IsValid(); } return false; } internal static bool Owner(Component c) { ZNetView val = View(c); if (Object.op_Implicit((Object)(object)val) && val.IsValid()) { return val.IsOwner(); } return false; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ScenePatch { private static void Postfix(ZNetScene __instance) { PipeLanguage.Register(Localization.instance); Pieces.Register(__instance); } } [HarmonyPatch(typeof(Smelter), "Awake")] internal static class KilnPatch { private static void Postfix(Smelter __instance) { if (ProductionRules.IsKiln(Native.MachineName(__instance))) { __instance.m_secPerProduct /= 2f; __instance.m_maxOre *= 4; } if (Native.Valid((Component)(object)__instance)) { Plugin.Machines.Add(__instance); } } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] internal static class MachineRegistrationPatch { private static void Postfix(Smelter __instance) { if (Native.Valid((Component)(object)__instance) && Automation.Supported(__instance)) { Plugin.Machines.Add(__instance); PortFactory.Machine(__instance); } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ChestPatch { internal static void Postfix(Container __instance) { if (ChestHandoff.IsChest(__instance) && Native.Valid((Component)(object)__instance)) { Plugin.Chests.Add(__instance); CookingCredit.Register(__instance); Hopper.RegisterInventory(__instance); if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent())) { ((Component)__instance).gameObject.AddComponent(); } } } } [HarmonyPatch(typeof(Container), "CheckForChanges")] internal static class ChestRegistrationRetry { private static void Postfix(Container __instance) { if (!Plugin.Chests.Contains(__instance)) { ChestPatch.Postfix(__instance); } } } [HarmonyPatch(typeof(Container), "OnDestroyed")] internal static class ChestDestroyedPatch { private static void Prefix(Container __instance) { Plugin.Chests.Remove(__instance); CookingCredit.Forget(__instance); } } [HarmonyPatch(typeof(Smelter), "Spawn")] internal static class OutputPatch { private static bool Prefix(Smelter __instance, string __0, ref int __1) { try { return !Automation.Deposit(__instance, __0, ref __1); } catch (Exception ex) { Plugin.Error("Output routing failed; dropping the remaining product normally: " + ex); return __1 > 0; } } } public static class ProductionRules { public static bool IsKiln(string name) { return name == "charcoal_kiln"; } public static bool Supported(string name, string fuel, bool conversions) { if (!IsKiln(name)) { switch (name) { default: if (!string.Equals(name, "piece_frostkiln", StringComparison.OrdinalIgnoreCase)) { if (fuel == "Coal") { return conversions; } return false; } break; case "smelter": case "blastfurnace": case "eitrrefinery": case "windmill": case "piece_spinningwheel": case "spinningwheel": break; } } return true; } public static PipeKind ReturnRoute(string name, PipeKind requested, int explicitChestCount) { if (IsKiln(name) && explicitChestCount == 0) { switch (requested) { case PipeKind.Input: return PipeKind.Output; case PipeKind.Output: return PipeKind.Input; } } return requested; } } internal static class Routing { private sealed class Attachment { internal PipeKind Kind; internal Component Target; } private static List nodes = new List(); private static Dictionary index = new Dictionary(); private static Dictionary> docks = new Dictionary>(); private static NetworkGraph graph; private static readonly Dictionary> storage = new Dictionary>(); private static readonly Dictionary>> targetRoots = new Dictionary>>(); private static readonly Dictionary rootCounts = new Dictionary(); private static readonly HashSet attachedChests = new HashSet(); private static readonly Dictionary>> candidates = new Dictionary>>(); internal static int Revision; internal static bool Dirty = true; internal static bool HasChestAnchor(Container chest) { return attachedChests.Contains(chest); } internal static void Reset() { graph = null; nodes.Clear(); index.Clear(); docks.Clear(); storage.Clear(); targetRoots.Clear(); rootCounts.Clear(); attachedChests.Clear(); candidates.Clear(); Dirty = true; SocketIndex.Clear(); } private static void Ensure() { if ((Dirty || graph == null) && Plugin.RunConnectionChecks.Value) { Rebuild(); } } internal static void Rebuild() { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Expected O, but got Unknown //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Expected O, but got Unknown if (!Plugin.RunConnectionChecks.Value) { return; } SocketIndex.Invalidate(); candidates.Clear(); Plugin.Chests.RemoveWhere((Container c) => !Object.op_Implicit((Object)(object)c)); Plugin.Machines.RemoveWhere((Smelter s) => !Object.op_Implicit((Object)(object)s)); nodes = PipeNode.All.Where((PipeNode n) => Object.op_Implicit((Object)(object)n) && ((Behaviour)n).enabled && n.Ready && Native.Valid((Component)(object)n)).ToList(); index.Clear(); docks.Clear(); attachedChests.Clear(); List list = new List(); for (int num = 0; num < nodes.Count; num++) { PipeNode pipeNode = nodes[num]; index.Add(pipeNode, num); for (int num2 = 0; num2 < pipeNode.Ends.Length; num2++) { list.Add(new Socket(num, Point(((Component)pipeNode).transform.TransformPoint(pipeNode.Ends[num2])), Point(((Component)pipeNode).transform.TransformDirection(pipeNode.Directions[num2])))); } List list2 = new List(); docks[pipeNode] = list2; PipeKind[] array = new PipeKind[4] { PipeKind.Chest, PipeKind.Input, PipeKind.Fuel, PipeKind.Output }; foreach (PipeKind pipeKind in array) { foreach (MachinePort item in PortFactory.Attached(pipeNode, pipeKind)) { list2.Add(new Attachment { Kind = pipeKind, Target = item.Target }); if (pipeKind == PipeKind.Chest) { attachedChests.Add((Container)item.Target); } } } if (list2.Count != 0 || !pipeNode.Terminal) { continue; } Component val = (Component)((pipeNode.Kind == PipeKind.Chest) ? ((object)FindChest(pipeNode)) : ((object)FindMachine(pipeNode))); if (Object.op_Implicit((Object)(object)val)) { list2.Add(new Attachment { Kind = pipeNode.Kind, Target = val }); if (pipeNode.Kind == PipeKind.Chest) { attachedChests.Add((Container)val); } } } graph = new NetworkGraph(nodes.Count, list, 0.18f); List list3 = new List(); for (int num4 = 0; num4 < nodes.Count; num4++) { PipeNode pipeNode2 = nodes[num4]; if (pipeNode2.RunStarts != null && pipeNode2.RunEnds != null) { for (int num5 = 0; num5 < pipeNode2.RunStarts.Length; num5++) { list3.Add(new PipeRun(num4, Point(((Component)pipeNode2).transform.TransformPoint(pipeNode2.RunStarts[num5])), Point(((Component)pipeNode2).transform.TransformPoint(pipeNode2.RunEnds[num5])))); } } } if (Plugin.ConnectOverlaps.Value) { BodyConnections.Connect(graph, list3); } storage.Clear(); targetRoots.Clear(); rootCounts.Clear(); Dictionary> dictionary = new Dictionary>(); foreach (PipeNode node in nodes) { int num6 = graph.Root(index[node]); if (!dictionary.TryGetValue(num6, out var value)) { value = (dictionary[num6] = new HashSet()); } if (!storage.ContainsKey(num6)) { storage[num6] = new HashSet(); rootCounts[num6] = 0; } rootCounts[num6]++; foreach (Attachment item2 in docks[node]) { if (Object.op_Implicit((Object)(object)item2.Target)) { if (item2.Kind != PipeKind.Chest) { value.Add(item2.Kind); } else { storage[num6].Add((Container)item2.Target); } if (!targetRoots.TryGetValue(item2.Target, out var value2)) { value2 = new Dictionary>(); targetRoots[item2.Target] = value2; } if (!value2.TryGetValue(item2.Kind, out var value3)) { value3 = new HashSet(); value2[item2.Kind] = value3; } value3.Add(num6); } } } foreach (PipeNode node2 in nodes) { HashSet hashSet2 = dictionary[graph.Root(index[node2])]; PipeTint pipeTint = ((Component)node2).GetComponent() ?? ((Component)node2).gameObject.AddComponent(); pipeTint.Apply((int)((Plugin.ColorByPort.Value && hashSet2.Count == 1) ? hashSet2.First() : ((PipeKind)(-1)))); } Revision++; Dirty = false; } private static Point Point(Vector3 p) { return new Point(p.x, p.y, p.z); } private static Container FindChest(PipeNode n) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) Container result = null; float num = 0.6f; foreach (Container chest in Plugin.Chests) { if (!ChestHandoff.Eligible(chest)) { continue; } float num2 = float.MaxValue; Collider[] componentsInChildren = ((Component)chest).GetComponentsInChildren(); foreach (Collider val in componentsInChildren) { if (val.enabled && !val.isTrigger) { num2 = Mathf.Min(num2, Vector3.Distance(val.ClosestPoint(n.Dock), n.Dock)); } } if (num2 < num) { result = chest; num = num2; } } return result; } private static Smelter FindMachine(PipeNode n) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Smelter result = null; float num = 1.15f; foreach (Smelter machine in Plugin.Machines) { if (!Native.Valid((Component)(object)machine) || !Automation.Supported(machine)) { continue; } Transform val = ((n.Kind != PipeKind.Input) ? ((n.Kind != PipeKind.Fuel) ? machine.m_outputPoint : (Object.op_Implicit((Object)(object)machine.m_addWoodSwitch) ? ((Component)machine.m_addWoodSwitch).transform : null)) : (Object.op_Implicit((Object)(object)machine.m_addOreSwitch) ? ((Component)machine.m_addOreSwitch).transform : null)); if (Object.op_Implicit((Object)(object)val) && (n.Kind != PipeKind.Fuel || Object.op_Implicit((Object)(object)machine.m_fuelItem))) { float num2 = Vector3.Distance(val.position, n.Dock); if (num2 < num) { result = machine; num = num2; } } } return result; } internal static List Connected(Component machine, PipeKind kind) { Ensure(); List list = ConnectedExact(machine, kind, kind); PipeKind pipeKind = ProductionRules.ReturnRoute(Stations.Name(machine), kind, list.Count); if (pipeKind != kind) { return ConnectedExact(machine, pipeKind, kind); } return list; } private static List ConnectedExact(Component machine, PipeKind kind, PipeKind purpose) { List source = StorageForTarget(machine, kind); return (from c in source where Object.op_Implicit((Object)(object)c) && Native.Valid((Component)(object)c) && !Hopper.Is(c) && ((purpose != PipeKind.Output) ? ChestFlowRules.Sends(ChestSettings.Mode(c)) : ChestFlowRules.Receives(ChestSettings.Mode(c))) orderby (purpose != PipeKind.Output || ChestSettings.Mode(c) != ChestFlow.ReceiveOnly) ? 1 : 0 select c).ToList(); } internal static List ConnectedChests(Container source) { Ensure(); List source2 = StorageForTarget((Component)(object)source, PipeKind.Chest); return (from c in source2 where (Object)(object)c != (Object)(object)source && ChestHandoff.Eligible(c) orderby (ChestSettings.Mode(c) != ChestFlow.ReceiveOnly) ? 1 : 0 select c).ToList(); } private static List StorageForTarget(Component target, PipeKind kind) { if (!candidates.TryGetValue(target, out var value)) { value = (candidates[target] = new Dictionary>()); } if (value.TryGetValue(kind, out var value2)) { return value2; } HashSet hashSet = new HashSet(); if (targetRoots.TryGetValue(target, out var value3) && value3.TryGetValue(kind, out var value4)) { foreach (int item in value4) { hashSet.UnionWith(storage[item]); } } IEnumerable source = hashSet.Where((Container c) => Object.op_Implicit((Object)(object)c) && Native.Valid((Component)(object)c)); return value[kind] = source.OrderBy((Container c) => ((object)Unsafe.As(ref Native.View((Component)(object)c).GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString()).ToList(); } internal static List StorageForPipe(PipeNode node) { Ensure(); if (!index.TryGetValue(node, out var value)) { return new List(); } int key = graph.Root(value); return storage[key].Where((Container c) => Object.op_Implicit((Object)(object)c) && Native.Valid((Component)(object)c) && ChestFlowRules.Receives(ChestSettings.Mode(c))).ToList(); } internal static bool Linked(Component s, Container c) { if (!Connected(s, PipeKind.Input).Contains(c) && !Connected(s, PipeKind.Fuel).Contains(c)) { return Connected(s, PipeKind.Output).Contains(c); } return true; } internal static string Status(PipeNode node) { Ensure(); if (!index.TryGetValue(node, out var value)) { return PipeLanguage.Text("Snap collars end to end"); } int key = graph.Root(value); int num = rootCounts[key]; bool flag = storage[key].Any((Container c) => Object.op_Implicit((Object)(object)c)); if (node.Terminal) { Component val = (from a in docks[node] where Object.op_Implicit((Object)(object)a.Target) select a.Target).FirstOrDefault(); if (!Object.op_Implicit((Object)(object)val)) { return PipeLanguage.Text((node.Kind == PipeKind.Chest) ? "Not attached - snap the wide collar to a blue chest port" : "Not attached - snap the wide collar to the matching colored machine port"); } Container val2 = (Container)(object)((val is Container) ? val : null); if (Object.op_Implicit((Object)(object)val2) && ChestHandoff.Busy(val2)) { return PipeLanguage.Text("Chest open | transport handled by its viewer"); } string text = (Object.op_Implicit((Object)(object)val2) ? val2.m_name : Stations.Label(val)); return PipeLanguage.Text("Attached: ") + Localization.instance.Localize(text) + " | " + num + PipeLanguage.Text(" connected pieces") + (flag ? "" : PipeLanguage.Text(" | no chest connected")); } return num + PipeLanguage.Text(" connected pieces") + PipeLanguage.Text(flag ? " | chest connected" : " | no chest connected"); } } public sealed class ChestHandoff : MonoBehaviour { private Container chest; private ZNetView view; private float nextRequest; private float ownerSince = -1f; private float nextGrant; private void OnDestroy() { CookingCredit.Forget(chest); } private void Awake() { chest = ((Component)this).GetComponent(); view = Native.View((Component)(object)chest); if (Object.op_Implicit((Object)(object)view) && view.IsValid()) { view.Register("FP_RequestChest", (Action)OnRequest); view.Register("FP_ChestMode", (Action)OnMode); } } private void OnMode(long sender, ZDOID playerId, int mode) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) ChestSettings.SetFromPeer(chest, sender, playerId, mode); } internal static bool Eligible(Container c) { if (IsChest(c) && Native.Valid((Component)(object)c)) { return Native.PieceOf((Component)(object)c).IsPlacedByPlayer(); } return false; } internal static bool IsChest(Container c) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if (Object.op_Implicit((Object)(object)c) && (int)c.m_privacy == 2 && !Object.op_Implicit((Object)(object)c.m_wagon) && !Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent()) && !Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent())) { return Object.op_Implicit((Object)(object)Native.PieceOf((Component)(object)c)); } return false; } internal static bool Busy(Container c) { if (!c.IsInUse() && !OpenChestAccess.Viewing(c)) { return Native.View((Component)(object)c).GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0; } return true; } internal static bool Access(Component c) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return PrivateArea.CheckAccess(c.transform.position, 0f, false, true); } return false; } private void OnRequest(long sender, ZDOID machineId) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) if (CraftingInterop.CanYield(chest) && Eligible(chest) && OpenChestRules.CanHandoff(Native.Owner((Component)(object)chest), Busy(chest), OpenChestAccess.Viewing(chest)) && !(Time.time < nextGrant) && Access((Component)(object)chest)) { GameObject val = ZNetScene.instance.FindInstance(machineId); Component val2 = (Object.op_Implicit((Object)(object)val) ? Stations.Find(val) : null); Container val3 = (Object.op_Implicit((Object)(object)val) ? val.GetComponentInChildren() : null); PipeNode pipeNode = (Object.op_Implicit((Object)(object)val) ? val.GetComponent() : null); bool flag = Object.op_Implicit((Object)(object)pipeNode) && Native.Owner((Component)(object)chest) && Native.Valid((Component)(object)pipeNode) && Native.View((Component)(object)pipeNode).GetZDO().GetOwner() == sender && Access((Component)(object)pipeNode) && Routing.StorageForPipe(pipeNode).Contains(chest); bool flag2 = Object.op_Implicit((Object)(object)val2) && Stations.Supported(val2) && Native.Valid(val2) && Native.View(val2).GetZDO().GetOwner() == sender && Routing.Linked(val2, chest); bool flag3 = Eligible(val3) && Native.View((Component)(object)val3).GetZDO().GetOwner() == sender && Access((Component)(object)val3) && (ChestFlowRules.Transfer(ChestSettings.Mode(val3), ChestSettings.Mode(chest)) || (Busy(val3) && ChestFlowRules.Transfer(ChestSettings.Mode(chest), ChestSettings.Mode(val3)))) && Routing.ConnectedChests(val3).Contains(chest); if (flag2 || flag3 || flag) { Native.Load.Invoke(chest, null); Native.Save.Invoke(chest, null); ZDOMan.instance.ForceSendZDO(sender, view.GetZDO().m_uid); view.GetZDO().SetOwner(sender); nextGrant = Time.time + 3f; ownerSince = -1f; } } } internal bool Ready(Component s) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || !Eligible(chest) || !Access((Component)(object)chest) || !Access(s)) { return false; } if (!view.IsOwner()) { ownerSince = -1f; if (Busy(chest)) { return false; } if (Time.time >= nextRequest) { nextRequest = Time.time + 3f; view.InvokeRPC("FP_RequestChest", new object[1] { Native.View(s).GetZDO().m_uid }); } return false; } if (!OpenChestAccess.CanEdit(chest)) { return false; } if (OpenChestAccess.Viewing(chest)) { ownerSince = Time.time; return true; } if (ownerSince < 0f) { ownerSince = Time.time; return false; } if (Time.time - ownerSince < 0.5f) { return false; } Native.Load.Invoke(chest, null); return OpenChestAccess.CanEdit(chest); } } internal static class Automation { internal static bool Supported(Smelter s) { string name = Native.MachineName(s); return ProductionRules.Supported(name, Object.op_Implicit((Object)(object)s.m_fuelItem) ? ((Object)((Component)s.m_fuelItem).gameObject).name : null, s.m_conversion != null && s.m_conversion.Count > 0); } private static bool Ready(Container c, Smelter s) { ChestHandoff component = ((Component)c).GetComponent(); if (Object.op_Implicit((Object)(object)component)) { return component.Ready((Component)(object)s); } return false; } internal static void Feed(Smelter s) { if (!Native.Owner((Component)(object)s)) { return; } int num = Math.Min(10, s.m_maxOre - (int)Native.QueueSize.Invoke(s, null)); if (num > 0) { foreach (Container item in Routing.Connected((Component)(object)s, PipeKind.Input)) { if (!Ready(item, s)) { continue; } Inventory inventory = item.GetInventory(); ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData val in array) { if (!Object.op_Implicit((Object)(object)val.m_dropPrefab) || OpenChestAccess.Reserved(inventory, val)) { continue; } string name = ((Object)val.m_dropPrefab).name; if ((ProductionRules.IsKiln(Native.MachineName(s)) && !StockReserve.KilnAllows(name)) || !s.m_conversion.Any((ItemConversion r) => Object.op_Implicit((Object)(object)r.m_from) && ((Object)((Component)r.m_from).gameObject).name == name)) { continue; } while (num > 0 && inventory.ContainsItem(val) && StockReserve.Available(inventory, val) > 0) { if (!Native.Owner((Component)(object)s) || !OpenChestAccess.CanEdit(item)) { return; } bool cheated = val.m_cheated; if (!inventory.RemoveItem(val, 1)) { break; } Native.Queue.Invoke(s, new object[2] { name, cheated }); num--; } if (num == 0) { break; } } if (num == 0) { break; } } } if (Object.op_Implicit((Object)(object)s.m_fuelItem)) { int num2 = Math.Min(10, Mathf.FloorToInt((float)s.m_maxFuel - (float)Native.Fuel.Invoke(s, null))); if (num2 > 0) { foreach (Container item2 in Routing.Connected((Component)(object)s, PipeKind.Fuel)) { if (!Ready(item2, s)) { continue; } Inventory inventory2 = item2.GetInventory(); ItemData[] array2 = inventory2.GetAllItems().ToArray(); foreach (ItemData val2 in array2) { if (Object.op_Implicit((Object)(object)val2.m_dropPrefab) && !(((Object)val2.m_dropPrefab).name != ((Object)((Component)s.m_fuelItem).gameObject).name) && !OpenChestAccess.Reserved(inventory2, val2)) { int num4 = Math.Min(num2, StockReserve.Available(inventory2, val2)); if (num4 > 0 && inventory2.RemoveItem(val2, num4)) { Native.SetFuel.Invoke(s, new object[1] { (float)Native.Fuel.Invoke(s, null) + (float)num4 }); num2 -= num4; } if (num2 == 0) { break; } } } if (num2 == 0) { break; } } } } foreach (Container item3 in Routing.Connected((Component)(object)s, PipeKind.Output)) { Ready(item3, s); } if (!s.m_spawnStack) { return; } ZDO zDO = Native.View((Component)(object)s).GetZDO(); int num5 = zDO.GetInt(ZDOVars.s_spawnAmount, 0); int amount = num5; if (num5 <= 0) { return; } Deposit(s, zDO.GetString(ZDOVars.s_spawnOre, ""), ref amount); if (amount != num5) { zDO.Set(ZDOVars.s_spawnAmount, amount, false); if (amount == 0) { zDO.Set(ZDOVars.s_spawnOre, ""); } } } internal static bool Deposit(Smelter s, string ore, ref int amount) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown if (!Plugin.RunAutomation.Value || !Supported(s) || !Native.Owner((Component)(object)s) || amount <= 0) { return false; } ItemConversion val = (ItemConversion)AccessTools.Method(typeof(Smelter), "GetItemConversion", (Type[])null, (Type[])null).Invoke(s, new object[1] { ore }); if (val == null || !Object.op_Implicit((Object)(object)val.m_to)) { return false; } foreach (Container item in Routing.Connected((Component)(object)s, PipeKind.Output)) { if (Ready(item, s)) { ItemData val2 = val.m_to.m_itemData.Clone(); val2.m_dropPrefab = ((Component)val.m_to).gameObject; val2.m_stack = 1; val2.m_worldLevel = (byte)Game.m_worldLevel; ZDO zDO = Native.View((Component)(object)s).GetZDO(); val2.m_cheated = (zDO.GetBool(ZDOVars.s_cheatedQueued, false) || zDO.GetBool(ZDOVars.s_cheated, false)) && !PlayerProfile.s_bypassCheatChecks; Inventory inventory = item.GetInventory(); while (amount > 0 && inventory.CanAddItem(val2, 1) && inventory.AddItem(val2.Clone())) { amount--; } if (amount == 0) { return true; } } } return false; } } public enum PipeKind { Straight, Bend, Tee, Riser, UpBend, DownBend, Chest, Input, Fuel, Output, Stretch, ShortStraight, Cross, SixWay, TeeUp, TeeDown, CompactChest, CompactInput, CompactFuel, CompactOutput, TightBend, TightUpBend, TightDownBend, ShortTee, ShortTeeUp, ShortTeeDown, ShortCross, ShortSixWay, TeePlusUp, TeePlusDown, ShortTeePlusUp, ShortTeePlusDown } public struct Rotation { public float X; public float Y; public float Z; public float W; public Rotation(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } } public static class SnapGeometry { public static bool UprightPort(Point normal) { return Math.Abs(Unit(normal).Y) < 0.05f; } public static bool Compatible(PipeKind piece, bool dock, bool host, PipeKind port) { if (piece < PipeKind.Chest || piece >= PipeKind.Stretch) { return !dock; } if (!host) { return !dock; } if (dock) { return piece == port; } return false; } public static Point Add(Point a, Point b) { return new Point(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } public static Point Subtract(Point a, Point b) { return new Point(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static Point Scale(Point a, float s) { return new Point(a.X * s, a.Y * s, a.Z * s); } public static Point Cross(Point a, Point b) { return new Point(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } public static Point Unit(Point p) { float num = (float)Math.Sqrt(Point.Dot(p, p)); if (num < 1E-08f) { throw new ArgumentException("Socket direction must not be zero."); } return Scale(p, 1f / num); } public static Rotation AlignOpposing(Point sourceOut, Point targetOut) { Point a = Unit(sourceOut); Point b = Scale(Unit(targetOut), -1f); float num = Math.Max(-1f, Math.Min(1f, Point.Dot(a, b))); if (num < -0.999999f) { Point point = Unit(Cross(a, (Math.Abs(a.X) < 0.8f) ? new Point(1f, 0f, 0f) : new Point(0f, 1f, 0f))); return new Rotation(point.X, point.Y, point.Z, 0f); } Point point2 = Cross(a, b); float num2 = 1f + num; float num3 = 1f / (float)Math.Sqrt(Point.Dot(point2, point2) + num2 * num2); return new Rotation(point2.X * num3, point2.Y * num3, point2.Z * num3, num2 * num3); } public static Point Rotate(Rotation q, Point p) { Point a = new Point(q.X, q.Y, q.Z); Point point = Scale(Cross(a, p), 2f); return Add(p, Add(Scale(point, q.W), Cross(a, point))); } public static Point Origin(Point target, Point rotatedSocketOffset) { return Subtract(target, rotatedSocketOffset); } } public sealed class MachinePort : MonoBehaviour { internal Component Target; internal PipeKind Kind; internal bool Preview; internal static readonly HashSet All = new HashSet(); private LineRenderer guide; private bool registered; private void OnEnable() { All.Add(this); SocketIndex.Invalidate(); if (!Preview && Object.op_Implicit((Object)(object)Target) && Native.Valid(Target)) { registered = true; Routing.Dirty = true; } } private void OnDisable() { All.Remove(this); SocketIndex.Invalidate(); if (registered) { Routing.Dirty = true; } registered = false; } internal void Initialize(Component target, PipeKind kind, Vector3 position, Vector3 outward, bool preview) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_004e: 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_0053: Unknown result type (might be due to invalid IL or missing references) Target = target; Kind = kind; Preview = preview; SocketIndex.Invalidate(); ((Component)this).transform.position = position; ((Component)this).transform.rotation = Quaternion.LookRotation(outward, (Mathf.Abs(Vector3.Dot(outward, Vector3.up)) > 0.95f) ? Vector3.forward : Vector3.up); if (!preview && Native.Valid(target)) { registered = true; Routing.Dirty = true; } } private void CreateGuide() { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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) if (!Object.op_Implicit((Object)(object)guide) && Object.op_Implicit((Object)(object)PortFactory.GuideMaterial)) { guide = ((Component)this).gameObject.AddComponent(); guide.useWorldSpace = false; guide.loop = true; guide.positionCount = 32; LineRenderer obj = guide; float startWidth = (guide.endWidth = 0.025f); obj.startWidth = startWidth; ((Renderer)guide).sharedMaterial = PortFactory.GuideMaterial; LineRenderer obj2 = guide; Color startColor = (guide.endColor = Pieces.PortColor(Kind)); obj2.startColor = startColor; ((Renderer)guide).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)guide).receiveShadows = false; for (int i = 0; i < 32; i++) { float num2 = (float)i * (float)Math.PI / 16f; guide.SetPosition(i, new Vector3(Mathf.Cos(num2) * 0.24f, Mathf.Sin(num2) * 0.24f, 0.04f)); } ((Renderer)guide).enabled = false; } } internal void RefreshGuide(bool placing, Vector3 playerPosition) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) bool flag = placing && Object.op_Implicit((Object)(object)Target) && (Preview || Native.Valid(Target)) && Vector3.SqrMagnitude(playerPosition - ((Component)this).transform.position) < 225f; if (flag && !Object.op_Implicit((Object)(object)guide)) { CreateGuide(); } if (Object.op_Implicit((Object)(object)guide) && ((Renderer)guide).enabled != flag) { ((Renderer)guide).enabled = flag; } } } internal static class PortFactory { private static readonly HashSet prepared = new HashSet(); private static Material guideMaterial; internal static Material GuideMaterial { get { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)guideMaterial)) { Shader val = Shader.Find("Hidden/Internal-Colored") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color"); if (Object.op_Implicit((Object)(object)val)) { guideMaterial = new Material(val); if (guideMaterial.HasProperty("_ZTest")) { guideMaterial.SetInt("_ZTest", 8); } if (guideMaterial.HasProperty("_ZWrite")) { guideMaterial.SetInt("_ZWrite", 0); } if (guideMaterial.HasProperty("_Cull")) { guideMaterial.SetInt("_Cull", 0); } if (guideMaterial.HasProperty("_SrcBlend")) { guideMaterial.SetInt("_SrcBlend", 5); } if (guideMaterial.HasProperty("_DstBlend")) { guideMaterial.SetInt("_DstBlend", 10); } guideMaterial.renderQueue = 4000; } } return guideMaterial; } } internal static void Machine(Smelter s) { if (Object.op_Implicit((Object)(object)s) && Native.Valid((Component)(object)s) && Automation.Supported(s) && !prepared.Contains((Component)(object)s)) { MachinePort[] componentsInChildren = ((Component)s).GetComponentsInChildren(true); if (Object.op_Implicit((Object)(object)s.m_addOreSwitch) && !componentsInChildren.Any((MachinePort p) => (Object)(object)p.Target == (Object)(object)s && p.Kind == PipeKind.Input)) { AddMachine((Component)(object)s, PipeKind.Input, ((Component)s.m_addOreSwitch).transform); } if (Object.op_Implicit((Object)(object)s.m_fuelItem) && Object.op_Implicit((Object)(object)s.m_addWoodSwitch) && !componentsInChildren.Any((MachinePort p) => (Object)(object)p.Target == (Object)(object)s && p.Kind == PipeKind.Fuel)) { AddMachine((Component)(object)s, PipeKind.Fuel, ((Component)s.m_addWoodSwitch).transform); } if (Object.op_Implicit((Object)(object)s.m_outputPoint) && !componentsInChildren.Any((MachinePort p) => (Object)(object)p.Target == (Object)(object)s && p.Kind == PipeKind.Output)) { AddMachine((Component)(object)s, PipeKind.Output, s.m_outputPoint); } prepared.Add((Component)(object)s); int num = ((Component)s).GetComponentsInChildren(true).Length; if (num != componentsInChildren.Length) { Plugin.LogInfo("Machine ports ready: " + Native.MachineName(s) + " (" + num + ")."); } } } internal static void AddMachine(Component s, PipeKind kind, Transform point) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) Transform val = (Object.op_Implicit((Object)(object)Native.PieceOf(s)) ? ((Component)Native.PieceOf(s)).transform : ((Component)Native.View(s)).transform); Vector3 val2 = val.InverseTransformPoint(point.position); Vector3 val3 = ((Mathf.Abs(val2.x) > Mathf.Abs(val2.z)) ? new Vector3(Mathf.Sign(val2.x), 0f, 0f) : new Vector3(0f, 0f, Mathf.Sign(val2.z))); if (val2.x * val2.x + val2.z * val2.z < 0.01f) { val3 = Vector3.back; } Vector3 val4 = val.TransformDirection(val3); Vector3 normalized = ((Vector3)(ref val4)).normalized; Add(s, kind, point.position + normalized * 0.025f, normalized); } internal static void Chest(Container c, bool preview = false) { //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0165: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0193: 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) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) if (!(preview ? ChestHandoff.IsChest(c) : ChestHandoff.Eligible(c)) || prepared.Contains((Component)(object)c)) { return; } MachinePort[] componentsInChildren = ((Component)Native.PieceOf((Component)(object)c)).GetComponentsInChildren(true); Bounds bounds; if (componentsInChildren.Any((MachinePort p) => (Object)(object)p.Target == (Object)(object)c)) { if (preview) { MachinePort[] array = componentsInChildren; foreach (MachinePort machinePort in array) { if ((Object)(object)machinePort.Target == (Object)(object)c) { machinePort.Preview = true; } } } if (!preview) { prepared.Add((Component)(object)c); } } else if (TryChestBounds(c, preview, out bounds)) { Hopper component = ((Component)c).GetComponent(); if (Object.op_Implicit((Object)(object)component) && component.HasLegacyBounds) { bounds = new Bounds(component.LegacyCenter, component.LegacySize); } if (!preview) { prepared.Add((Component)(object)c); } Vector3 center = ((Bounds)(ref bounds)).center; Vector3[] array2 = (Vector3[])(object)new Vector3[5] { Vector3.forward, Vector3.back, Vector3.left, Vector3.right, Vector3.up }; foreach (Vector3 val in array2) { Vector3 val2 = center + Vector3.Scale(((Bounds)(ref bounds)).extents, val) + val * 0.025f; Add((Component)(object)c, PipeKind.Chest, ((Component)c).transform.TransformPoint(val2), ((Component)c).transform.TransformDirection(val), preview); } } } internal static bool TryChestBounds(Container c, bool preview, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: 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_0151: 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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: 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) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: 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_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool flag = false; Collider[] componentsInChildren = ((Component)c).GetComponentsInChildren(true); Bounds bounds2 = default(Bounds); foreach (Collider val in componentsInChildren) { if ((!preview && !val.enabled) || val.isTrigger || (Object.op_Implicit((Object)(object)c.m_open) && ((Component)val).transform.IsChildOf(c.m_open.transform))) { continue; } BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null); MeshCollider val3 = (MeshCollider)(object)((val is MeshCollider) ? val : null); SphereCollider val4 = (SphereCollider)(object)((val is SphereCollider) ? val : null); CapsuleCollider val5 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null); bool flag2 = true; if (Object.op_Implicit((Object)(object)val2)) { ((Bounds)(ref bounds2))..ctor(val2.center, val2.size); } else if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val3.sharedMesh)) { bounds2 = val3.sharedMesh.bounds; } else if (Object.op_Implicit((Object)(object)val4)) { ((Bounds)(ref bounds2))..ctor(val4.center, Vector3.one * val4.radius * 2f); } else if (Object.op_Implicit((Object)(object)val5)) { Vector3 val6 = Vector3.one * val5.radius * 2f; ((Vector3)(ref val6))[val5.direction] = Mathf.Max(val5.height, val5.radius * 2f); ((Bounds)(ref bounds2))..ctor(val5.center, val6); } else { if (!val.enabled || !((Component)val).gameObject.activeInHierarchy) { continue; } bounds2 = val.bounds; flag2 = false; Vector3 size = ((Bounds)(ref bounds2)).size; if (((Vector3)(ref size)).sqrMagnitude < 1E-06f) { continue; } } for (int j = -1; j <= 1; j += 2) { for (int k = -1; k <= 1; k += 2) { for (int l = -1; l <= 1; l += 2) { Vector3 val7 = ((Bounds)(ref bounds2)).center + Vector3.Scale(((Bounds)(ref bounds2)).extents, new Vector3((float)j, (float)k, (float)l)); if (flag2) { val7 = ((Component)val).transform.TransformPoint(val7); } val7 = ((Component)c).transform.InverseTransformPoint(val7); if (!flag) { bounds = new Bounds(val7, Vector3.zero); flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val7); } } } } } return flag; } internal static void Add(Component target, PipeKind kind, Vector3 position, Vector3 normal, bool preview = false) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FP_port_" + kind); val.layer = LayerMask.NameToLayer("Ignore Raycast"); val.transform.SetParent(((Component)Native.PieceOf(target)).transform, false); if (preview) { val.tag = "snappoint"; } val.AddComponent().Initialize(target, kind, position, normal, preview); } internal static void EnsureAll() { prepared.RemoveWhere((Component c) => !Object.op_Implicit((Object)(object)c)); foreach (Container chest in Plugin.Chests) { if (Object.op_Implicit((Object)(object)chest)) { Chest(chest); } } foreach (Smelter machine in Plugin.Machines) { if (Object.op_Implicit((Object)(object)machine)) { Machine(machine); } } Component[] array = Stations.All.ToArray(); foreach (Component val in array) { if (Object.op_Implicit((Object)(object)val)) { Stations.Ports(val); } } } internal static IEnumerable Attached(PipeNode node, PipeKind kind) { if (!node.Ready) { yield break; } HashSet seen = new HashSet(); int count = (node.Terminal ? 1 : node.Ends.Length); for (int i = 0; i < count; i++) { if (node.Terminal && node.Kind != kind) { break; } Vector3 position = (node.Terminal ? node.Dock : ((Component)node).transform.TransformPoint(node.Ends[i])); Vector3 normal = (node.Terminal ? (-((Component)node).transform.forward) : ((Component)node).transform.TransformDirection(node.Directions[i])); foreach (MachinePort port in SocketIndex.NearbyPorts(position)) { if (port.Kind == kind && Object.op_Implicit((Object)(object)port.Target) && Native.Valid(port.Target) && Matches(position, normal, ((Component)port).transform) && seen.Add(port)) { yield return port; } } } } internal static bool Matches(Vector3 position, Vector3 normal, Transform target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (Vector3.SqrMagnitude(position - target.position) <= 0.0324f) { return Vector3.Dot(normal, target.forward) < -0.8f; } return false; } } internal static class PipeSnapping { private sealed class TargetSocket { internal Transform Transform; internal Piece Piece; internal MachinePort Port; } private static readonly FieldInfo Ghost = AccessTools.Field(typeof(Player), "m_placementGhost"); private static readonly FieldInfo Reach = AccessTools.Field(typeof(Player), "m_maxPlaceDistance"); private static readonly FieldInfo Manual = AccessTools.Field(typeof(Player), "m_manualSnapPoint"); private static readonly FieldInfo PlaceRotation = AccessTools.Field(typeof(Player), "m_placeRotation"); private static readonly FieldInfo RotationDegrees = AccessTools.Field(typeof(Player), "m_placeRotationDegrees"); private static Transform preparedGhost; private static Transform preparedSource; private static Transform preparedTarget; private static Quaternion preparedRotation; private static int preparedFrame = -1; internal static Vector3 Aim; internal static Vector3 AimNormal; internal static Piece AimedPiece; internal static int AimFrame = -1; private static int ghostFrame = -1; private static Player ghostPlayer; private static GameObject cachedGhost; private static PipeNode cachedGhostNode; private static Container cachedGhostChest; private static bool ghostComponentsRead; internal static bool ChestDockAvailable { get { if (preparedFrame == Time.frameCount && Object.op_Implicit((Object)(object)preparedGhost) && Object.op_Implicit((Object)(object)preparedSource) && Object.op_Implicit((Object)(object)preparedTarget) && Object.op_Implicit((Object)(object)GhostObject)) { return (Object)(object)preparedGhost == (Object)(object)GhostObject.transform; } return false; } } internal static PipeNode GhostNode { get { ReadGhostComponents(); if (!Object.op_Implicit((Object)(object)cachedGhost) || !cachedGhost.activeInHierarchy) { return null; } return cachedGhostNode; } } internal static GameObject GhostObject { get { ReadGhost(); return cachedGhost; } } internal static Container GhostChest { get { ReadGhostComponents(); if (!Object.op_Implicit((Object)(object)cachedGhost) || !cachedGhost.activeInHierarchy) { return null; } return cachedGhostChest; } } private static bool AlternativePlacement(Player player) { if (ZInput.IsNonClassicFunctionality() && ZInput.IsGamepadActive()) { return player.AlternativePlacementActive; } if (!ZInput.GetButton("AltPlace")) { if (ZInput.GetButton("JoyAltPlace")) { return !ZInput.GetButton("JoyRotate"); } return false; } return true; } internal static void PrepareChest(Player player) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_00d9: Unknown result type (might be due to invalid IL or missing references) preparedFrame = -1; preparedGhost = null; preparedSource = null; preparedTarget = null; GameObject ghostObject = GhostObject; if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)ghostObject) || AimFrame != Time.frameCount || AlternativePlacement(player) || !ChestHandoff.IsChest(ghostObject.GetComponentInChildren(true))) { return; } Quaternion rotation = ghostObject.transform.rotation; try { ghostObject.transform.rotation = Quaternion.Euler(0f, (float)(int)PlaceRotation.GetValue(player) * (float)RotationDegrees.GetValue(player), 0f); if (FindChest(player, ghostObject.transform, out var source, out var target, new List())) { preparedGhost = ghostObject.transform; preparedSource = source; preparedTarget = target; preparedRotation = ghostObject.transform.rotation; preparedFrame = Time.frameCount; } } finally { ghostObject.transform.rotation = rotation; } } internal static void InvalidateGhost() { ghostFrame = -1; ghostComponentsRead = false; cachedGhost = null; cachedGhostNode = null; cachedGhostChest = null; } private static void ReadGhost() { Player localPlayer = Player.m_localPlayer; if (ghostFrame != Time.frameCount || !((Object)(object)ghostPlayer == (Object)(object)localPlayer)) { ghostFrame = Time.frameCount; ghostPlayer = localPlayer; cachedGhost = (GameObject)(Object.op_Implicit((Object)(object)localPlayer) ? /*isinst with value type is only supported in some contexts*/: null); ghostComponentsRead = false; } } private static void ReadGhostComponents() { ReadGhost(); if (!ghostComponentsRead) { ghostComponentsRead = true; cachedGhostNode = (Object.op_Implicit((Object)(object)cachedGhost) ? cachedGhost.GetComponent() : null); Container val = (Object.op_Implicit((Object)(object)cachedGhost) ? cachedGhost.GetComponentInChildren(true) : null); cachedGhostChest = (ChestHandoff.IsChest(val) ? val : null); } } internal static bool Find(Player player, Transform ghost, out Transform source, out Transform target, List pieces) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0277: 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) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Expected O, but got Unknown //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0642: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0450: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_051b: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Unknown result type (might be due to invalid IL or missing references) //IL_0524: Unknown result type (might be due to invalid IL or missing references) //IL_0529: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_0535: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Unknown result type (might be due to invalid IL or missing references) //IL_0577: Unknown result type (might be due to invalid IL or missing references) //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Unknown result type (might be due to invalid IL or missing references) //IL_05bb: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Unknown result type (might be due to invalid IL or missing references) //IL_05d7: Unknown result type (might be due to invalid IL or missing references) //IL_05d9: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_0599: Unknown result type (might be due to invalid IL or missing references) //IL_059e: Unknown result type (might be due to invalid IL or missing references) source = null; target = null; if (!Plugin.RunConnectionChecks.Value) { return false; } PipeNode node = ((Component)ghost).GetComponent(); if (!Object.op_Implicit((Object)(object)node) || AimFrame != Time.frameCount) { return false; } bool flag = node.Kind == PipeKind.Straight || node.Kind == PipeKind.ShortStraight; if (flag) { PipeDeflection.Update(ghost); } List list = new List(); foreach (MachinePort item in MachinePort.All) { if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)item.Target) && Native.Valid(item.Target) && !(Vector3.SqrMagnitude(((Component)item).transform.position - Aim) > 2.25f) && (!node.Terminal || node.Kind == item.Kind)) { list.Add(new TargetSocket { Transform = ((Component)item).transform, Piece = Native.PieceOf(item.Target), Port = item }); } } List list2 = PipeNode.All.Where((PipeNode p) => Object.op_Implicit((Object)(object)p) && p.Ready && (Object)(object)p != (Object)(object)node && Native.Valid((Component)(object)p) && Vector3.SqrMagnitude(((Component)p).transform.position - Aim) < 400f).ToList(); foreach (PipeNode item2 in list2) { Piece component = ((Component)item2).GetComponent(); if (!pieces.Contains(component)) { pieces.Add(component); } foreach (Transform item3 in ((Component)item2).transform) { Transform val = item3; if (((Object)val).name == "_snap" && Vector3.SqrMagnitude(val.position - Aim) < 1.44f) { list.Add(new TargetSocket { Transform = val, Piece = component }); } } } List list3 = new List(); ((Component)node).GetComponent().GetSnapPoints(list3); int num = (int)Manual.GetValue(player); Quaternion rotation = ghost.rotation; Quaternion rotation2 = rotation; float num2 = float.MaxValue; float num3 = (float)Reach.GetValue(player) + (float)((Component)node).GetComponent().m_extraPlacementDistance; foreach (TargetSocket item4 in list) { if (!Object.op_Implicit((Object)(object)item4.Piece) || ((Object)(object)AimedPiece != (Object)(object)item4.Piece && Vector3.Distance(Aim, item4.Transform.position) > 0.6f) || Vector3.Distance(((Character)player).GetEyePoint(), item4.Transform.position) > num3) { continue; } bool flag2 = false; if ((Object)(object)item4.Port == (Object)null) { foreach (MachinePort item5 in MachinePort.All) { if (Object.op_Implicit((Object)(object)item5) && Object.op_Implicit((Object)(object)item5.Target) && Native.Valid(item5.Target) && PortFactory.Matches(item4.Transform.position, item4.Transform.forward, ((Component)item5).transform)) { flag2 = true; } } } foreach (PipeNode item6 in list2) { if ((Object)(object)item4.Piece == (Object)(object)((Component)item6).GetComponent()) { continue; } for (int num4 = 0; num4 < item6.Ends.Length; num4++) { if (PortFactory.Matches(((Component)item6).transform.TransformPoint(item6.Ends[num4]), ((Component)item6).transform.TransformDirection(item6.Directions[num4]), item4.Transform)) { flag2 = true; } } if (item6.Terminal && PortFactory.Matches(item6.Dock, -((Component)item6).transform.forward, item4.Transform)) { flag2 = true; } } if (flag2) { continue; } for (int num5 = 0; num5 < list3.Count; num5++) { if (num >= 0 && num < list3.Count && num5 != num) { continue; } Transform val2 = list3[num5]; bool dock = ((Object)val2).name == "_fp_dock"; if (SnapGeometry.Compatible(node.Kind, dock, (Object)(object)item4.Port != (Object)null, Object.op_Implicit((Object)(object)item4.Port) ? item4.Port.Kind : PipeKind.Straight)) { Vector3 v = rotation * val2.localRotation * Vector3.forward; Rotation rotation3 = SnapGeometry.AlignOpposing(ToPoint(v), ToPoint(item4.Transform.forward)); Quaternion val3 = new Quaternion(rotation3.X, rotation3.Y, rotation3.Z, rotation3.W) * rotation; if (flag && (Object)(object)item4.Port == (Object)null) { val3 = PipeDeflection.Apply(val3, item4.Transform); } float num6 = Vector3.SqrMagnitude(item4.Transform.position - Aim) + Quaternion.Angle(rotation, val3) * 0.001f; if (!(num6 >= num2)) { num2 = num6; rotation2 = val3; source = val2; target = item4.Transform; } } } if (!pieces.Contains(item4.Piece)) { pieces.Add(item4.Piece); } } if (!Object.op_Implicit((Object)(object)target)) { return false; } ghost.rotation = rotation2; return true; } private static Point ToPoint(Vector3 v) { return new Point(v.x, v.y, v.z); } internal static bool Occupied(Transform socket, PipeNode owner) { return SocketIndex.Occupied(socket, owner); } internal static bool FindChest(Player player, Transform ghost, out Transform source, out Transform target, List pieces) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0460: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0224: 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_0209: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) source = null; target = null; if (!Plugin.RunConnectionChecks.Value) { return false; } if (ChestDockAvailable && (Object)(object)preparedGhost == (Object)(object)ghost) { source = preparedSource; target = preparedTarget; ghost.rotation = preparedRotation; Piece componentInParent = ((Component)target).GetComponentInParent(); if (Object.op_Implicit((Object)(object)componentInParent) && !pieces.Contains(componentInParent)) { pieces.Add(componentInParent); } return true; } Container chest = ((Component)ghost).GetComponentInChildren(true); if (!ChestHandoff.IsChest(chest) || AimFrame != Time.frameCount) { return false; } PortFactory.Chest(chest, preview: true); MachinePort[] array = (from p in ((Component)ghost).GetComponentsInChildren(true) where (Object)(object)p.Target == (Object)(object)chest select p).ToArray(); float num = (float)Reach.GetValue(player) + (float)((Component)ghost).GetComponent().m_extraPlacementDistance; Quaternion rotation = ghost.rotation; Quaternion rotation2 = rotation; float num2 = float.MaxValue; List list = new List(); ((Component)ghost).GetComponent().GetSnapPoints(list); int num3 = (int)Manual.GetValue(player); foreach (PipeNode item in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item) || !item.Ready || !Native.Valid((Component)(object)item) || Vector3.SqrMagnitude(((Component)item).transform.position - Aim) > 400f || (item.Terminal && item.Kind != PipeKind.Chest)) { continue; } Piece component = ((Component)item).GetComponent(); if (!pieces.Contains(component)) { pieces.Add(component); } foreach (Transform item2 in ((Component)item).transform) { Transform val = item2; if (((Object)val).name != (item.Terminal ? "_fp_dock" : "_snap") || (!SnapGeometry.UprightPort(ToPoint(val.forward)) && Mathf.Abs(val.forward.y) < 0.999f)) { continue; } float num4 = Vector3.Distance(val.position, Aim); if (num4 > 1.2f || ((Object)(object)AimedPiece != (Object)(object)component && num4 > 0.6f) || Occupied(val, item) || Vector3.Distance(((Character)player).GetEyePoint(), val.position) > num) { continue; } MachinePort[] array2 = array; foreach (MachinePort machinePort in array2) { if (num3 >= 0 && num3 < list.Count && Object.op_Implicit((Object)(object)((Component)list[num3]).GetComponent()) && (Object)(object)list[num3] != (Object)(object)((Component)machinePort).transform) { continue; } Vector3 val2 = ghost.InverseTransformDirection(((Component)machinePort).transform.forward); Vector3 val3 = rotation * val2; Vector3 val4 = -val.forward; bool flag = Mathf.Abs(val4.y) > 0.999f; if (!(flag ? (Vector3.Dot(val3, val4) < 0.999f) : (Mathf.Abs(val3.y) > 0.05f))) { float num6 = (flag ? 0f : Vector3.SignedAngle(Vector3.ProjectOnPlane(val3, Vector3.up), Vector3.ProjectOnPlane(val4, Vector3.up), Vector3.up)); Quaternion val5 = Quaternion.AngleAxis(num6, Vector3.up) * rotation; Vector3 val6 = ghost.InverseTransformPoint(((Component)machinePort).transform.position); Vector3 val7 = val.position - val5 * Vector3.Scale(val6, ghost.localScale); float num7 = num4 * num4 + Quaternion.Angle(rotation, val5) * 0.001f + Vector3.SqrMagnitude(val7 - ghost.position) * 0.02f; if (!(num7 >= num2)) { num2 = num7; rotation2 = val5; source = ((Component)machinePort).transform; target = val; } } } } } if (!Object.op_Implicit((Object)(object)target)) { return false; } ghost.rotation = rotation2; return true; } } [HarmonyPatch(typeof(Player), "SetupPlacementGhost")] internal static class ChestPreviewPortsPatch { private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } PipeSnapping.InvalidateGhost(); GameObject ghostObject = PipeSnapping.GhostObject; if (Object.op_Implicit((Object)(object)ghostObject) && ChestHandoff.IsChest(ghostObject.GetComponentInChildren(true))) { PortFactory.Chest(ghostObject.GetComponentInChildren(true), preview: true); if (!Object.op_Implicit((Object)(object)ghostObject.GetComponent())) { ghostObject.AddComponent(); } } } } [HarmonyPatch(typeof(Player), "PieceRayTest")] internal static class PipeAimPatch { private static void Postfix(Player __instance, bool __result, Vector3 __0, Vector3 __1, Piece __2) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { PipeSnapping.AimFrame = (__result ? Time.frameCount : (-1)); PipeSnapping.Aim = __0; PipeSnapping.AimedPiece = __2; PipeSnapping.AimNormal = __1; PipeSnapping.PrepareChest(__instance); } } } [HarmonyPatch(typeof(Player), "FindClosestSnapPoints")] internal static class PipeSnapPatch { private static bool Prefix(Player __instance, Transform __0, ref Transform __2, ref Transform __3, List __4, ref bool __result) { if (Object.op_Implicit((Object)(object)((Component)__0).GetComponent())) { __result = StretchPlacement.Preview(__instance, __0, out __2, out __3, __4); return false; } if (Object.op_Implicit((Object)(object)((Component)__0).GetComponent())) { __result = PipeSnapping.Find(__instance, __0, out __2, out __3, __4); return false; } if (ChestHandoff.IsChest(((Component)__0).GetComponentInChildren())) { __result = PipeSnapping.FindChest(__instance, __0, out __2, out __3, __4); return false; } return true; } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class PipeSupportPatch { private static bool RequireLevelSurface(Piece piece) { if (piece.m_notOnTiltingSurface) { if (PipeSnapping.ChestDockAvailable) { return !((Object)(object)PipeSnapping.GhostObject == (Object)(object)((Component)piece).gameObject); } return true; } return false; } private static bool CanAttach(WearNTear surface, Player player) { if (surface.m_supports) { return true; } if ((Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } if (Object.op_Implicit((Object)(object)PipeSnapping.GhostChest) && Object.op_Implicit((Object)(object)((Component)surface).GetComponent())) { return true; } if (!Object.op_Implicit((Object)(object)PipeSnapping.GhostNode)) { return false; } Component val = Stations.Find(((Component)surface).gameObject); if (!Object.op_Implicit((Object)(object)((Component)surface).GetComponent()) && !ChestHandoff.Eligible(((Component)surface).GetComponent())) { if (Object.op_Implicit((Object)(object)val)) { return Stations.Supported(val); } return false; } return true; } private static IEnumerable Transpiler(IEnumerable instructions) { FieldInfo field = AccessTools.Field(typeof(WearNTear), "m_supports"); MethodInfo replacement = AccessTools.Method(typeof(PipeSupportPatch), "CanAttach", (Type[])null, (Type[])null); FieldInfo tilt = AccessTools.Field(typeof(Piece), "m_notOnTiltingSurface"); int matches = 0; int tiltMatches = 0; foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, tilt)) { CodeInstruction call = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(PipeSupportPatch), "RequireLevelSurface", (Type[])null, (Type[])null)); call.labels.AddRange(instruction.labels); call.blocks.AddRange(instruction.blocks); yield return call; tiltMatches++; } else if (instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, field)) { CodeInstruction loadPlayer = new CodeInstruction(OpCodes.Ldarg_0, (object)null); loadPlayer.labels.AddRange(instruction.labels); loadPlayer.blocks.AddRange(instruction.blocks); yield return loadPlayer; yield return new CodeInstruction(OpCodes.Call, (object)replacement); matches++; } else { yield return instruction; } } if (matches != 1) { Plugin.Error("Pipe support hook expected one placement check, found " + matches + ". Check game/mod compatibility."); } if (tiltMatches != 1) { throw new InvalidOperationException("Chest docking expected one level-surface check, found " + tiltMatches + "."); } } } internal static class SocketIndex { private struct End { internal PipeNode Node; internal Vector3 Position; internal Vector3 Normal; } private static readonly Dictionary> ends = new Dictionary>(); private static readonly Dictionary> ports = new Dictionary>(); private static bool dirty = true; internal static void Invalidate() { dirty = true; } internal static void Clear() { ends.Clear(); ports.Clear(); dirty = true; } private static Vector3Int Cell(Vector3 p) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) return new Vector3Int(Mathf.FloorToInt(p.x), Mathf.FloorToInt(p.y), Mathf.FloorToInt(p.z)); } private static void Ensure() { //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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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) if (!dirty) { return; } dirty = false; ends.Clear(); ports.Clear(); foreach (PipeNode item in PipeNode.All) { if (Object.op_Implicit((Object)(object)item) && item.Ready && Native.Valid((Component)(object)item)) { for (int i = 0; i < item.Ends.Length; i++) { Add(item, ((Component)item).transform.TransformPoint(item.Ends[i]), ((Component)item).transform.TransformDirection(item.Directions[i])); } if (item.Terminal) { Add(item, item.Dock, -((Component)item).transform.forward); } } } foreach (MachinePort item2 in MachinePort.All) { if (Object.op_Implicit((Object)(object)item2) && !item2.Preview && Object.op_Implicit((Object)(object)item2.Target) && Native.Valid(item2.Target)) { Vector3Int key = Cell(((Component)item2).transform.position); if (!ports.TryGetValue(key, out var value)) { value = (ports[key] = new List()); } value.Add(item2); } } } private static void Add(PipeNode n, Vector3 p, Vector3 normal) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector3Int key = Cell(p); if (!ends.TryGetValue(key, out var value)) { value = (ends[key] = new List()); } value.Add(new End { Node = n, Position = p, Normal = normal }); } internal static IEnumerable NearbyPorts(Vector3 point) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Ensure(); Vector3Int c = Cell(point); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { for (int z = -1; z <= 1; z++) { if (!ports.TryGetValue(c + new Vector3Int(x, y, z), out var list)) { continue; } foreach (MachinePort p in list) { if (Object.op_Implicit((Object)(object)p)) { yield return p; } } } } } } internal static bool Occupied(Transform socket, PipeNode owner) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0013: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) Ensure(); Vector3 position = socket.position; Vector3 forward = socket.forward; foreach (MachinePort item in NearbyPorts(position)) { if (PortFactory.Matches(position, forward, ((Component)item).transform)) { return true; } } Vector3Int val = Cell(position); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { if (!ends.TryGetValue(val + new Vector3Int(i, j, k), out var value)) { continue; } foreach (End item2 in value) { if (Object.op_Implicit((Object)(object)item2.Node) && (Object)(object)item2.Node != (Object)(object)owner) { Vector3 val2 = item2.Position - position; if (((Vector3)(ref val2)).sqrMagnitude <= 0.0324f && Vector3.Dot(item2.Normal, forward) < -0.8f) { return true; } } } } } } return false; } } public static class SortingRules { public static bool Matches(string source, int quality, int world, string stored, int storedQuality, int storedWorld) { if (!string.IsNullOrEmpty(source) && source == stored && quality == storedQuality) { return world == storedWorld; } return false; } } public static class StationRules { public static int[] PlanBatch(int amount, int[] free) { if (amount <= 0) { return null; } long num = 0L; foreach (int val in free) { num += Math.Max(0, val); } if (num < amount) { return null; } int[] array = new int[free.Length]; for (int j = 0; j < free.Length; j++) { if (amount <= 0) { break; } array[j] = Math.Min(Math.Max(0, free[j]), amount); amount -= array[j]; } return array; } } internal static class Stations { internal static readonly HashSet All = new HashSet(); private static readonly MethodInfo CookGetSlot = Method(typeof(CookingStation), "GetSlot"); private static readonly MethodInfo CookSetSlot = Method(typeof(CookingStation), "SetSlot"); private static readonly MethodInfo CookAdd = Method(typeof(CookingStation), "RPC_AddItem"); private static readonly MethodInfo CookFree = Method(typeof(CookingStation), "GetFreeSlot"); private static readonly MethodInfo CookGetFuel = Method(typeof(CookingStation), "GetFuel"); private static readonly MethodInfo CookSetFuel = Method(typeof(CookingStation), "SetFuel"); private static readonly MethodInfo FermentStatus = Method(typeof(Fermenter), "GetStatus"); private static readonly MethodInfo FermentContent = Method(typeof(Fermenter), "GetContent"); private static readonly MethodInfo FermentConversion = Method(typeof(Fermenter), "GetItemConversion"); private static readonly MethodInfo FermentAdd = Method(typeof(Fermenter), "RPC_AddItem"); private static readonly MethodInfo HoneyLevel = Method(typeof(Beehive), "GetHoneyLevel"); private static readonly MethodInfo Incinerate = Method(typeof(Incinerator), "RPC_RequestIncinerate"); private static readonly FieldInfo IncineratorBusy = AccessTools.Field(typeof(Incinerator), "isInUse"); private static readonly object RawCookingStatus = Enum.ToObject(CookSetSlot.GetParameters()[3].ParameterType, 0); private static readonly MethodInfo AddAt = AccessTools.Method(typeof(Inventory), "AddItem", new Type[5] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) }, (Type[])null); internal static Component Find(GameObject go) { return (Component)(go.GetComponentInChildren() ?? go.GetComponentInChildren() ?? go.GetComponentInChildren() ?? go.GetComponentInChildren() ?? ((object)go.GetComponentInChildren()) ?? ((object)go.GetComponentInChildren())); } internal static string Name(Component s) { ZNetView val = Native.View(s); return Native.Name(Object.op_Implicit((Object)(object)val) ? ((Component)val).gameObject : s.gameObject); } internal static string Label(Component s) { //IL_0009: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) if (s is Smelter) { return ((Smelter)s).m_name; } if (s is CookingStation) { return ((CookingStation)s).m_name; } if (s is Fermenter) { return ((Fermenter)s).m_name; } if (s is Beehive) { return ((Beehive)s).m_name; } if (s is SapCollector) { return ((SapCollector)s).m_name; } return "Obliterator"; } internal static bool Supported(Component s) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)s)) { if (!(s is Smelter)) { if (!(s is CookingStation) && !(s is Fermenter) && !(s is Beehive) && !(s is SapCollector)) { return s is Incinerator; } return true; } return Automation.Supported((Smelter)s); } return false; } internal static bool CanHandoff(Component s) { Fermenter val = (Fermenter)(object)((s is Fermenter) ? s : null); if (Object.op_Implicit((Object)(object)val) && ((MonoBehaviour)val).IsInvoking("DelayedTap")) { return false; } Incinerator val2 = (Incinerator)(object)((s is Incinerator) ? s : null); if (Object.op_Implicit((Object)(object)val2)) { if (!(bool)IncineratorBusy.GetValue(val2) && Object.op_Implicit((Object)(object)val2.m_container)) { return !ChestHandoff.Busy(val2.m_container); } return false; } return true; } internal static void Register(Component s) { if (Object.op_Implicit((Object)(object)s) && Native.Valid(s) && Object.op_Implicit((Object)(object)Native.PieceOf(s)) && All.Add(s)) { Ports(s); } } internal static void Ports(Component s) { //IL_0079: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: 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_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: 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_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: 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) if (!Object.op_Implicit((Object)(object)s) || !Native.Valid(s) || !Object.op_Implicit((Object)(object)Native.PieceOf(s)) || ((Component)Native.PieceOf(s)).GetComponentsInChildren(true).Any((MachinePort p) => (Object)(object)p.Target == (Object)(object)s)) { return; } Transform transform = ((Component)Native.PieceOf(s)).transform; Bounds val = default(Bounds); ((Bounds)(ref val))..ctor(Vector3.up * 0.6f, Vector3.one); bool flag = false; Collider[] componentsInChildren = s.GetComponentsInChildren(); foreach (Collider val2 in componentsInChildren) { if (!val2.enabled || val2.isTrigger) { continue; } Bounds bounds = val2.bounds; for (int num2 = -1; num2 <= 1; num2 += 2) { for (int num3 = -1; num3 <= 1; num3 += 2) { for (int num4 = -1; num4 <= 1; num4 += 2) { Vector3 val3 = transform.InverseTransformPoint(((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)num2, (float)num3, (float)num4))); if (!flag) { val = new Bounds(val3, Vector3.zero); flag = true; } else { ((Bounds)(ref val)).Encapsulate(val3); } } } } } Vector3 center = ((Bounds)(ref val)).center; center.y = Mathf.Clamp(center.y, 0.35f, 1.5f); if (!(s is Beehive) && !(s is SapCollector)) { PortFactory.Add(s, PipeKind.Input, transform.TransformPoint(center + Vector3.left * (((Bounds)(ref val)).extents.x + 0.06f)), transform.TransformDirection(Vector3.left)); } PortFactory.Add(s, PipeKind.Output, transform.TransformPoint(center + Vector3.right * (((Bounds)(ref val)).extents.x + 0.06f)), transform.TransformDirection(Vector3.right)); Component obj = s; CookingStation val4 = (CookingStation)(object)((obj is CookingStation) ? obj : null); if (Object.op_Implicit((Object)(object)val4) && val4.m_useFuel && Object.op_Implicit((Object)(object)val4.m_fuelItem)) { PortFactory.Add(s, PipeKind.Fuel, transform.TransformPoint(center + Vector3.back * (((Bounds)(ref val)).extents.z + 0.06f)), transform.TransformDirection(Vector3.back)); } } private static MethodInfo Method(Type type, string name) { return AccessTools.Method(type, name, (Type[])null, (Type[])null); } internal static bool Ready(Component station, Container chest) { if (!Native.Owner(station) || !ChestHandoff.Access(station)) { return false; } ChestHandoff component = ((Component)chest).GetComponent(); if (Object.op_Implicit((Object)(object)component)) { return component.Ready(station); } return false; } internal static void Tick(Component s) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)s)) { return; } try { if (!Native.Valid(s)) { return; } MachineHandoff.Prepare(s); if (!Native.Owner(s) || !ChestHandoff.Access(s)) { return; } foreach (Container item in Routing.Connected(s, PipeKind.Output)) { Ready(s, item); } if (s is CookingStation) { Cook((CookingStation)s); } else if (s is Fermenter) { Ferment((Fermenter)s); } else if (s is Beehive) { Honey((Beehive)s); } else if (s is SapCollector) { Sap((SapCollector)s); } else if (s is Incinerator) { Obliterate((Incinerator)s); } } catch (Exception ex) { Plugin.Error("Station automation paused for " + Name(s) + ": " + ex); } } private static bool Store(Component s, ItemDrop prefab, int amount, bool cheated = false, bool fullDurability = false, bool crafter = false, SkillType credit = (SkillType)0) { //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Invalid comparison between Unknown and I4 //IL_0231: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)prefab) || amount <= 0 || !Native.Owner(s)) { return false; } ItemData val = prefab.m_itemData.Clone(); val.m_dropPrefab = ((Component)prefab).gameObject; val.m_stack = amount; val.m_worldLevel = (byte)Game.m_worldLevel; val.m_cheated = (cheated || Native.View(s).GetZDO().GetBool(ZDOVars.s_cheated, false)) && !PlayerProfile.s_bypassCheatChecks; if (fullDurability) { val.m_durability = val.m_shared.m_maxDurability; } if (crafter && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { val.m_crafterID = Player.m_localPlayer.GetPlayerID(); val.m_crafterName = Player.m_localPlayer.GetPlayerName(); } foreach (Container item in Routing.Connected(s, PipeKind.Output)) { if (!Ready(s, item)) { continue; } Inventory inventory = item.GetInventory(); int width = inventory.GetWidth(); int[] array = new int[width * inventory.GetHeight()]; for (int i = 0; i < array.Length; i++) { ItemData itemAt = inventory.GetItemAt(i % width, i / width); if (itemAt == null) { array[i] = val.m_shared.m_maxStackSize; } else if (!OpenChestAccess.Reserved(inventory, itemAt) && itemAt.m_shared.m_name == val.m_shared.m_name && itemAt.m_quality == val.m_quality && itemAt.m_worldLevel == val.m_worldLevel) { array[i] = Math.Max(0, itemAt.m_shared.m_maxStackSize - itemAt.m_stack); } } int[] array2 = StationRules.PlanBatch(amount, array); if (array2 == null) { continue; } ItemData val2 = val.Clone(); for (int j = 0; j < array2.Length; j++) { if (array2[j] > 0) { AddAt.Invoke(inventory, new object[5] { val2, array2[j], j % width, j / width, false }); } } if (val2.m_stack == 0) { CookingCredit.Add(item, val, ((int)credit == -1) ? 1 : amount, credit); return true; } throw new InvalidOperationException("A third-party inventory change invalidated a reserved output batch."); } return false; } private static void Cook(CookingStation s) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < s.m_slots.Length; i++) { object[] array = new object[5] { i, "", 0f, RawCookingStatus, false }; CookGetSlot.Invoke(s, array); string text = (string)array[1]; int num = Convert.ToInt32(array[3]); if (!string.IsNullOrEmpty(text) && (num == 1 || num == 2)) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text); ItemDrop prefab = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent() : null); if (Store((Component)(object)s, prefab, 1, (bool)array[4], s.m_spawnFullDurability, s.m_recordCrafter, (SkillType)((num == 1) ? ((int)s.m_skill) : 0))) { CookSetSlot.Invoke(s, new object[5] { i, "", 0f, RawCookingStatus, false }); Native.View((Component)(object)s).InvokeRPC(ZNetView.Everybody, "RPC_SetSlotVisual", new object[2] { i, "" }); } } } int num2 = 10; foreach (Container item2 in Routing.Connected((Component)(object)s, PipeKind.Input)) { if (!Ready((Component)(object)s, item2)) { continue; } Inventory inventory = item2.GetInventory(); ItemData[] array2 = inventory.GetAllItems().ToArray(); foreach (ItemData item in array2) { if (!Object.op_Implicit((Object)(object)item.m_dropPrefab) || OpenChestAccess.Reserved(inventory, item) || !s.m_conversion.Any((ItemConversion r) => Object.op_Implicit((Object)(object)r.m_from) && ((Object)((Component)r.m_from).gameObject).name == ((Object)item.m_dropPrefab).name)) { continue; } while (num2 > 0 && inventory.ContainsItem(item) && StockReserve.Available(inventory, item) > 0 && (int)CookFree.Invoke(s, null) >= 0) { string name = ((Object)item.m_dropPrefab).name; bool cheated = item.m_cheated; if (!inventory.RemoveItem(item, 1)) { break; } CookAdd.Invoke(s, new object[3] { ZNet.GetUID(), name, cheated }); num2--; } if (num2 == 0) { break; } } if (num2 == 0) { break; } } if (!s.m_useFuel || !Object.op_Implicit((Object)(object)s.m_fuelItem)) { return; } int num3 = Math.Min(10, Mathf.FloorToInt((float)s.m_maxFuel - (float)CookGetFuel.Invoke(s, null))); foreach (Container item3 in Routing.Connected((Component)(object)s, PipeKind.Fuel)) { if (num3 <= 0) { break; } if (!Ready((Component)(object)s, item3)) { continue; } Inventory inventory2 = item3.GetInventory(); ItemData[] array3 = inventory2.GetAllItems().ToArray(); foreach (ItemData val in array3) { if (Object.op_Implicit((Object)(object)val.m_dropPrefab) && !(((Object)val.m_dropPrefab).name != ((Object)((Component)s.m_fuelItem).gameObject).name) && !OpenChestAccess.Reserved(inventory2, val)) { int num5 = Math.Min(num3, StockReserve.Available(inventory2, val)); if (num5 > 0 && inventory2.RemoveItem(val, num5)) { CookSetFuel.Invoke(s, new object[1] { (float)CookGetFuel.Invoke(s, null) + (float)num5 }); num3 -= num5; } } } } } private static void Ferment(Fermenter s) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown if (((MonoBehaviour)s).IsInvoking("DelayedTap")) { return; } int num = Convert.ToInt32(FermentStatus.Invoke(s, null)); if (num == 3) { ItemConversion val = (ItemConversion)FermentConversion.Invoke(s, new object[1] { (int)FermentContent.Invoke(s, null) }); ZDO zDO = Native.View((Component)(object)s).GetZDO(); if (val == null || !Store((Component)(object)s, val.m_to, val.m_producedItems, zDO.GetBool(ZDOVars.s_cheatedQueued, false), fullDurability: false, crafter: false, (SkillType)0)) { return; } zDO.Set(ZDOVars.s_content, 0, false); zDO.Set(ZDOVars.s_startTime, 0L); zDO.Set(ZDOVars.s_cheatedQueued, false); num = 0; } if (num != 0) { return; } foreach (Container item2 in Routing.Connected((Component)(object)s, PipeKind.Input)) { if (!Ready((Component)(object)s, item2)) { continue; } Inventory inventory = item2.GetInventory(); ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData item in array) { if (Object.op_Implicit((Object)(object)item.m_dropPrefab) && !OpenChestAccess.Reserved(inventory, item) && s.m_conversion.Any((ItemConversion r) => Object.op_Implicit((Object)(object)r.m_from) && ((Object)((Component)r.m_from).gameObject).name == ((Object)item.m_dropPrefab).name)) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name); bool cheated = item.m_cheated; if (StockReserve.Available(inventory, item) > 0 && inventory.RemoveItem(item, 1)) { FermentAdd.Invoke(s, new object[3] { ZNet.GetUID(), stableHashCode, cheated }); return; } } } } } private static void Honey(Beehive s) { int num = (int)HoneyLevel.Invoke(s, null); while (num > 0) { int amount = Game.instance.ScaleDrops(s.m_honeyItem.m_itemData, 1); if (!Store((Component)(object)s, s.m_honeyItem, amount, cheated: false, fullDurability: false, crafter: false, (SkillType)(-1))) { break; } Native.View((Component)(object)s).GetZDO().Set(ZDOVars.s_level, --num, false); } } private static void Sap(SapCollector s) { ZDO zDO = Native.View((Component)(object)s).GetZDO(); int num = zDO.GetInt(ZDOVars.s_level, 0); int num2 = 10; int num3 = num; while (num > 0 && num2-- > 0 && Store((Component)(object)s, s.m_spawnItem, Game.instance.ScaleDrops(s.m_spawnItem.m_itemData, 1), cheated: false, fullDurability: false, crafter: false, (SkillType)0)) { zDO.Set(ZDOVars.s_level, --num, false); } if (num != num3) { Native.View((Component)(object)s).InvokeRPC(ZNetView.Everybody, "RPC_UpdateEffects", new object[0]); } } private static bool MoveOne(Inventory from, Inventory to, ItemData item) { if (OpenChestAccess.Reserved(from, item)) { return false; } for (int i = 0; i < to.GetHeight(); i++) { for (int j = 0; j < to.GetWidth(); j++) { ItemData itemAt = to.GetItemAt(j, i); if (itemAt == null || !OpenChestAccess.Reserved(to, itemAt)) { int stack = item.m_stack; to.MoveItemToThis(from, item, 1, j, i); if (item.m_stack < stack) { return true; } } } } return false; } private static void Obliterate(Incinerator s) { Container container = s.m_container; if (!Object.op_Implicit((Object)(object)container) || !Native.Owner((Component)(object)container) || (bool)IncineratorBusy.GetValue(s) || ChestHandoff.Busy(container)) { return; } Native.Load.Invoke(container, null); Inventory inv = container.GetInventory(); HashSet products = new HashSet(from r in s.m_conversions where Object.op_Implicit((Object)(object)r.m_result) select ((Object)((Component)r.m_result).gameObject).name); if (Object.op_Implicit((Object)(object)s.m_defaultResult)) { products.Add(((Object)((Component)s.m_defaultResult).gameObject).name); } Func func = (ItemData item) => Object.op_Implicit((Object)(object)item.m_dropPrefab) && products.Contains(((Object)item.m_dropPrefab).name); int num = 10; foreach (Container item in Routing.Connected((Component)(object)s, PipeKind.Output)) { if (!Ready((Component)(object)s, item)) { continue; } foreach (ItemData item2 in inv.GetAllItems().ToArray().Where(func)) { while (num > 0 && inv.ContainsItem(item2) && item2.m_stack > 0 && MoveOne(inv, item.GetInventory(), item2)) { num--; } } } if (inv.GetAllItems().Any(func)) { return; } List list = Routing.Connected((Component)(object)s, PipeKind.Input); List list2 = Routing.Connected((Component)(object)s, PipeKind.Output); if (list.Count == 0 || list2.Count == 0) { return; } num = 10; foreach (Container item3 in list) { if (ChestSettings.Mode(item3) != ChestFlow.SendOnly || !Ready((Component)(object)s, item3)) { continue; } Inventory inventory = item3.GetInventory(); ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData val in array) { if (!func(val)) { while (num > 0 && inventory.ContainsItem(val) && StockReserve.Available(inventory, val) > 0 && MoveOne(inventory, inv, val)) { num--; } } } } bool flag = list.Any((Container c) => ChestSettings.Mode(c) == ChestFlow.SendOnly); bool flag2 = Object.op_Implicit((Object)(object)s.m_defaultResult) && s.m_defaultCost > 0 && inv.NrOfItemsIncludingStacks() >= s.m_defaultCost; foreach (IncineratorConversion conversion in s.m_conversions) { if (Object.op_Implicit((Object)(object)conversion.m_result) && conversion.m_resultAmount > 0 && conversion.m_requirements.Count != 0) { Func predicate = (Requirement r) => Object.op_Implicit((Object)(object)r.m_resItem) && r.m_amount > 0 && inv.CountItems(r.m_resItem.m_itemData.m_shared.m_name, -1, true) >= r.m_amount; flag2 |= (conversion.m_requireOnlyOneIngredient ? conversion.m_requirements.Any(predicate) : conversion.m_requirements.All(predicate)); } } if (flag && flag2 && list2.Any((Container c) => Ready((Component)(object)s, c))) { Incinerate.Invoke(s, new object[2] { ZNet.GetUID(), Player.m_localPlayer.GetPlayerID() }); } } } [HarmonyPatch] internal static class ExtraStationRegistration { private static IEnumerable TargetMethods() { try { Type[] array = new Type[5] { typeof(CookingStation), typeof(Fermenter), typeof(Beehive), typeof(SapCollector), typeof(Incinerator) }; foreach (Type t in array) { yield return AccessTools.Method(t, "Awake", (Type[])null, (Type[])null); } } finally { } yield return AccessTools.Method(typeof(CookingStation), "UpdateCooking", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Fermenter), "SlowUpdate", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Beehive), "UpdateBees", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(SapCollector), "UpdateTick", (Type[])null, (Type[])null); } private static void Postfix(Component __instance) { Stations.Register(__instance); } } internal static class StockReserve { internal static int Available(Inventory inventory, ItemData item) { if (!Plugin.LeaveLastItem.Value || Hopper.InventoryIsHopper(inventory)) { return item.m_stack; } int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != item && allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_worldLevel == item.m_worldLevel) { num += allItem.m_stack; } } return Math.Max(0, item.m_stack - ((num <= 0) ? 1 : 0)); } internal static bool KilnAllows(string name) { if (!(name == "Wood") && (!(name == "FineWood") || !Plugin.KilnFineWood.Value)) { if (name == "RoundLog") { return Plugin.KilnCoreWood.Value; } return false; } return true; } } public sealed class StretchPipe : MonoBehaviour, IPlaced { private const string DataKey = "FP_stretch_v1"; private static readonly HashSet instances = new HashSet(); private static readonly MethodInfo SetupColliders = AccessTools.Method(typeof(WearNTear), "SetupColliders", (Type[])null, (Type[])null); private static readonly MethodInfo ClearSupport = AccessTools.Method(typeof(WearNTear), "ClearCachedSupport", (Type[])null, (Type[])null); private Transform visual; private MeshFilter filter; private PipeNode node; private Transform[] sockets; private readonly List boxes = new List(); private byte[] lastData; private Mesh ownedMesh; private bool worldApplied; internal StretchShape Shape; private void OnEnable() { instances.Add(this); } private void OnDisable() { instances.Remove(this); } private void Start() { Read(); } private void Cache() { if (!Object.op_Implicit((Object)(object)visual)) { visual = ((Component)this).transform.Find("pipe_mesh"); filter = ((Component)visual).GetComponent(); node = ((Component)this).GetComponent(); sockets = (from Transform t in (IEnumerable)((Component)this).transform where ((Object)t).name == "_snap" select t).ToArray(); boxes.AddRange(((Component)visual).GetComponentsInChildren(true)); } } internal static void PollAll() { foreach (StretchPipe instance in instances) { if (Object.op_Implicit((Object)(object)instance)) { instance.Read(); } } } private void Read() { if (!Native.Valid((Component)(object)this)) { return; } Cache(); byte[] byteArray = Native.View((Component)(object)this).GetZDO().GetByteArray("FP_stretch_v1", (byte[])null); if (byteArray == null || lastData == null || !byteArray.SequenceEqual(lastData)) { StretchShape stretchShape = StretchShape.Decode(byteArray); if (stretchShape == null) { ((Component)visual).gameObject.SetActive(false); ((Behaviour)node).enabled = false; } else { Apply(stretchShape); lastData = (byte[])byteArray.Clone(); } } } internal void Apply(StretchShape shape) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0115: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: 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) //IL_01b0: 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) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Expected O, but got Unknown bool flag = Native.Valid((Component)(object)this); if (Shape != null && Same(Shape.Start, shape.Start) && Same(Shape.End, shape.End) && Same(Shape.StartNormal, shape.StartNormal) && Same(Shape.EndNormal, shape.EndNormal) && (!flag || worldApplied)) { return; } Cache(); SweptPipeMesh data = PipeModels.StretchMesh(shape); if (!Object.op_Implicit((Object)(object)ownedMesh)) { ownedMesh = PipeMeshUpload.Create(data, "ForgePipes continuous stretch", !flag); filter.sharedMesh = ownedMesh; } else { PipeMeshUpload.Update(ownedMesh, data); } Shape = shape; ((Component)visual).gameObject.SetActive(true); node.Ends = (Vector3[])(object)new Vector3[2] { Vector(shape.Start), Vector(shape.End) }; node.Directions = (Vector3[])(object)new Vector3[2] { Vector(shape.StartNormal), Vector(shape.EndNormal) }; for (int i = 0; i < 2; i++) { sockets[i].localPosition = node.Ends[i]; sockets[i].localRotation = Quaternion.FromToRotation(Vector3.forward, node.Directions[i]); } Point[] array = shape.Sample(); PipeModels.SetCenterlines(node, new Point[1][] { array }); int[] array2 = PipeCollisionPath.Simplify(array); int num = array2.Length - 1; while (boxes.Count < num) { GameObject val = new GameObject("pipe_collider"); val.layer = ((Component)this).gameObject.layer; val.transform.SetParent(visual, false); boxes.Add(val.AddComponent()); } for (int j = 0; j < boxes.Count; j++) { if (j < num) { PipeModels.SetCollider(boxes[j], array[array2[j]], array[array2[j + 1]], 0.16f); } else { ((Collider)boxes[j]).enabled = false; } } ((Behaviour)node).enabled = true; if (flag) { worldApplied = true; Routing.Dirty = true; node.VisualRevision++; SocketIndex.Invalidate(); PipeRenderGroups.Changed(node); WearNTear component = ((Component)this).GetComponent(); SetupColliders.Invoke(component, null); ClearSupport.Invoke(component, null); } } private static bool Same(Point a, Point b) { if (a.X == b.X && a.Y == b.Y) { return a.Z == b.Z; } return false; } private void OnDestroy() { instances.Remove(this); if (Object.op_Implicit((Object)(object)ownedMesh)) { Object.Destroy((Object)(object)ownedMesh); } } public void OnPlaced() { if (StretchPlacement.Pending != null && Native.Owner((Component)(object)this)) { byte[] array = StretchPlacement.Pending.Encode(); Native.View((Component)(object)this).GetZDO().Set("FP_stretch_v1", array); Apply(StretchPlacement.Pending); lastData = array; } } internal static Point Point(Vector3 v) { return new Point(v.x, v.y, v.z); } internal static Vector3 Vector(Point p) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(p.X, p.Y, p.Z); } } internal static class StretchPlacement { private sealed class Endpoint { internal Transform Socket; internal PipeNode Node; internal Piece Piece; internal Piece AttachedMachine; internal Vector3 Position => Socket.position; internal Vector3 Normal => Socket.forward; internal bool Exists { get { if (Object.op_Implicit((Object)(object)Socket) && Object.op_Implicit((Object)(object)Piece)) { return Native.Valid((Component)(object)Piece); } return false; } } } private static Endpoint start; private static Endpoint aimed; private static GameObject sessionGhost; private static Vector3 anchoredPosition; private static Vector3 anchoredNormal; private static bool previewValid; private static string reason = "Aim at a port or free pipe end."; private static StretchShape previewShape; private static readonly Collider[] routeColliders = (Collider[])(object)new Collider[64]; private static readonly FieldInfo Reach = AccessTools.Field(typeof(Player), "m_maxPlaceDistance"); private static readonly FieldInfo RayMask = AccessTools.Field(typeof(Player), "m_placeRayMask"); private static readonly MethodInfo UpdateGhost = AccessTools.Method(typeof(Player), "UpdatePlacementGhost", (Type[])null, (Type[])null); internal static StretchShape Pending; internal static bool Active => start != null; internal static string Hint => PipeLanguage.Text(reason); internal static StretchShape CommitShape { get { if (!previewValid || !Active) { return null; } return previewShape; } } internal static void ResetPreview() { previewValid = false; previewShape = null; } internal static bool IsStretch(GameObject go) { if (Object.op_Implicit((Object)(object)go)) { return Object.op_Implicit((Object)(object)go.GetComponent()); } return false; } internal static void Cancel() { start = null; aimed = null; sessionGhost = null; previewValid = false; previewShape = null; } internal static void Tick() { if (start != null && (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Character)Player.m_localPlayer).InPlaceMode() || !Object.op_Implicit((Object)(object)sessionGhost) || (Object)(object)PipeSnapping.GhostObject != (Object)(object)sessionGhost)) { Cancel(); } } private static bool Free(Endpoint e) { if (e != null && e.Exists) { return !PipeSnapping.Occupied(e.Socket, e.Node); } return false; } private static Endpoint Pick(Player player, Endpoint exclude) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) Endpoint endpoint = null; float num = float.MaxValue; float num2 = (float)Reach.GetValue(player); List list = new List(); foreach (MachinePort item in MachinePort.All) { if (Object.op_Implicit((Object)(object)item) && !item.Preview && Object.op_Implicit((Object)(object)item.Target)) { Vector3 val = ((Component)item).transform.position - PipeSnapping.Aim; if (((Vector3)(ref val)).sqrMagnitude <= 1.69f && Native.Valid(item.Target)) { list.Add(new Endpoint { Socket = ((Component)item).transform, Piece = Native.PieceOf(item.Target) }); } } } foreach (PipeNode item2 in PipeNode.All) { if (!Object.op_Implicit((Object)(object)item2) || !((Behaviour)item2).enabled || !item2.Ready || !Native.Valid((Component)(object)item2)) { continue; } Vector3 val2 = ((Component)item2).transform.position - PipeSnapping.Aim; if (((Vector3)(ref val2)).sqrMagnitude > 400f) { continue; } foreach (Transform item3 in ((Component)item2).transform) { Transform val3 = item3; if (((Object)val3).name == "_snap") { Vector3 val4 = val3.position - PipeSnapping.Aim; if (((Vector3)(ref val4)).sqrMagnitude <= 1.69f) { list.Add(new Endpoint { Socket = val3, Node = item2, Piece = ((Component)item2).GetComponent() }); } } } } foreach (Endpoint item4 in list) { if (Object.op_Implicit((Object)(object)item4.Piece) && (exclude == null || !((Object)(object)item4.Socket == (Object)(object)exclude.Socket))) { float num3 = Vector3.Distance(item4.Position, PipeSnapping.Aim); if (!(num3 > 1.3f) && (!((Object)(object)PipeSnapping.AimedPiece != (Object)(object)item4.Piece) || !(num3 > 0.65f)) && !(Vector3.Distance(((Character)player).GetEyePoint(), item4.Position) > num2) && num3 < num && Free(item4)) { endpoint = item4; num = num3; } } } if (endpoint != null && Object.op_Implicit((Object)(object)endpoint.Node) && endpoint.Node.Terminal) { MachinePort machinePort = PortFactory.Attached(endpoint.Node, endpoint.Node.Kind).FirstOrDefault(); if (Object.op_Implicit((Object)(object)machinePort) && Object.op_Implicit((Object)(object)machinePort.Target)) { endpoint.AttachedMachine = Native.PieceOf(machinePort.Target); } } return endpoint; } internal static bool Preview(Player player, Transform ghost, out Transform source, out Transform target, List pieces) { //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_00d8: 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_018d: 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_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: 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_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) source = null; target = null; previewValid = false; previewShape = null; if (!Plugin.RunConnectionChecks.Value) { return false; } if (PipeSnapping.AimFrame != Time.frameCount) { reason = "Aim at a port, pipe end, or building surface."; return false; } if (Object.op_Implicit((Object)(object)sessionGhost) && (Object)(object)sessionGhost != (Object)(object)((Component)ghost).gameObject) { Cancel(); } sessionGhost = ((Component)ghost).gameObject; StretchPipe component = ((Component)ghost).GetComponent(); aimed = Pick(player, start); if (start == null) { if (aimed == null) { reason = "Start at a chest, furnace port, or free pipe end."; return false; } Vector3 normal = aimed.Normal; ghost.rotation = Quaternion.identity; ghost.position = aimed.Position + normal; StretchShape shape = new StretchShape(StretchPipe.Point(-normal), StretchPipe.Point(normal), StretchPipe.Point(-normal), StretchPipe.Point(normal)); component.Apply(shape); source = ((IEnumerable)ghost).Cast().First((Transform t) => ((Object)t).name == "_snap"); target = aimed.Socket; if (!pieces.Contains(aimed.Piece)) { pieces.Add(aimed.Piece); } previewValid = true; reason = "Click to anchor. Then aim and click to finish."; return true; } if (!Free(start) || Vector3.Distance(start.Position, anchoredPosition) > 0.05f || Vector3.Dot(start.Normal, anchoredNormal) < 0.99f) { Cancel(); reason = "Start connection changed. Select a new starting point."; return false; } Vector3 val = ((aimed != null) ? aimed.Position : (PipeSnapping.Aim + PipeSnapping.AimNormal * 0.18f)); Vector3 val2 = val - anchoredPosition; if (((Vector3)(ref val2)).magnitude < 0.5f || ((Vector3)(ref val2)).magnitude > 16f) { reason = "Stretch pipes must span 0.5 to 16 metres."; return false; } Vector3 val3 = (anchoredPosition + val) * 0.5f; Vector3 val4 = ((aimed != null) ? (-aimed.Normal) : ((Vector3)(ref val2)).normalized); StretchShape shape2 = new StretchShape(StretchPipe.Point(anchoredPosition - val3), StretchPipe.Point(val - val3), StretchPipe.Point(-anchoredNormal), StretchPipe.Point(val4)); ghost.position = val3; ghost.rotation = Quaternion.identity; component.Apply(shape2); source = (from Transform t in (IEnumerable)ghost where ((Object)t).name == "_snap" select t).Skip(1).First(); if (aimed != null) { target = aimed.Socket; } else { target = ghost.Find("FP_stretch_preview_target"); if (!Object.op_Implicit((Object)(object)target)) { target = new GameObject("FP_stretch_preview_target").transform; target.SetParent(ghost, false); } target.position = val; target.rotation = Quaternion.LookRotation(-val4); } if (!pieces.Contains(start.Piece)) { pieces.Add(start.Piece); } if (aimed != null && !pieces.Contains(aimed.Piece)) { pieces.Add(aimed.Piece); } previewShape = shape2; previewValid = CheckRoute(player, ghost, shape2, aimed); return true; } private static bool CheckRoute(Player player, Transform ghost, StretchShape shape, Endpoint finish) { //IL_004c: 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_008d: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_016c: 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_0190: 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) Vector3[] array = (from p in shape.Sample() select ghost.TransformPoint(StretchPipe.Vector(p))).ToArray(); int num = (int)RayMask.GetValue(player); for (int num2 = 0; num2 < array.Length; num2++) { if (Location.IsInsideNoBuildLocation(array[num2]) || !PrivateArea.CheckAccess(array[num2], 0.16f, false, true)) { reason = "The route passes through a protected building area."; return false; } if (num2 == 0) { continue; } int num3 = Physics.OverlapCapsuleNonAlloc(array[num2 - 1], array[num2], 0.14f, routeColliders, num, (QueryTriggerInteraction)1); Collider[] array2 = ((num3 == routeColliders.Length) ? Physics.OverlapCapsule(array[num2 - 1], array[num2], 0.14f, num, (QueryTriggerInteraction)1) : routeColliders); if (array2 != routeColliders) { num3 = array2.Length; } for (int num4 = 0; num4 < num3; num4++) { Collider val = array2[num4]; if (!((Component)val).transform.IsChildOf(ghost)) { Piece componentInParent = ((Component)val).GetComponentInParent(); if ((start == null || !((Object)(object)componentInParent == (Object)(object)start.Piece) || num2 > 8) && (finish == null || !((Object)(object)componentInParent == (Object)(object)finish.Piece) || num2 <= 24) && !EndpointClearance(start, componentInParent, array[num2 - 1], array[num2]) && !EndpointClearance(finish, componentInParent, array[num2 - 1], array[num2])) { reason = "Pipe route is blocked. Use shorter runs or a tee to route around it."; return false; } } } } reason = ((finish != null) ? "Click to connect as one pipe." : "Click to finish with a free endpoint for another pipe."); return true; } private static bool EndpointClearance(Endpoint endpoint, Piece obstacle, Vector3 a, Vector3 b) { //IL_001f: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (endpoint != null && Object.op_Implicit((Object)(object)endpoint.AttachedMachine) && (Object)(object)endpoint.AttachedMachine == (Object)(object)obstacle) { return EndpointClearanceRules.Allows(StretchPipe.Point(endpoint.Position), StretchPipe.Point(endpoint.Normal), StretchPipe.Point(a), StretchPipe.Point(b)); } return false; } internal static void FinishPreview(Player player, ref PlacementStatus status) { if (IsStretch(PipeSnapping.GhostObject)) { if (!previewValid) { status = (PlacementStatus)1; } PipeSnapping.GhostObject.GetComponent().SetInvalidPlacementHeightlight((int)status != 0); } } internal static bool TryClick(Player player, ref bool result) { //IL_0025: 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_0060: 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_006f: Unknown result type (might be due to invalid IL or missing references) UpdateGhost.Invoke(player, new object[1] { true }); if (!previewValid || (int)player.GetPlacementStatus() != 0) { ((Character)player).Message((MessageType)2, PipeLanguage.Text(reason), 0, (Sprite)null, false); result = false; return false; } if (start == null) { start = aimed; anchoredPosition = start.Position; anchoredNormal = start.Normal; ((Character)player).Message((MessageType)2, PipeLanguage.Text("Pipe start anchored. Aim and click to finish; switch pieces to cancel."), 0, (Sprite)null, false); result = false; return false; } return true; } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class StretchPreviewPatch { private static void Prefix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { StretchPlacement.ResetPreview(); } } private static void Postfix(Player __instance, ref PlacementStatus ___m_placementStatus) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { StretchPlacement.FinishPreview(__instance, ref ___m_placementStatus); } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class StretchClickPatch { private static bool Prefix(Player __instance, Piece __0, ref bool __result) { StretchPlacement.Pending = null; if (!StretchPlacement.IsStretch(((Component)__0).gameObject)) { return true; } if (!StretchPlacement.TryClick(__instance, ref __result)) { return false; } StretchPlacement.Pending = StretchPlacement.CommitShape; return true; } private static void Postfix(Piece __0, bool __result) { if (__result && StretchPlacement.IsStretch(((Component)__0).gameObject)) { StretchPlacement.Cancel(); } } private static void Finalizer() { StretchPlacement.Pending = null; } } public sealed class StretchShape { public const int Segments = 32; public const float MinLength = 0.5f; public const float MaxLength = 16f; public readonly Point Start; public readonly Point End; public readonly Point StartNormal; public readonly Point EndNormal; public StretchShape(Point start, Point end, Point startNormal, Point endNormal) { Start = start; End = end; StartNormal = startNormal; EndNormal = endNormal; } private static bool Finite(Point p) { if (!float.IsNaN(p.X) && !float.IsNaN(p.Y) && !float.IsNaN(p.Z) && !float.IsInfinity(p.X) && !float.IsInfinity(p.Y)) { return !float.IsInfinity(p.Z); } return false; } public bool Valid() { if (!Finite(Start) || !Finite(End) || !Finite(StartNormal) || !Finite(EndNormal)) { return false; } float num = Point.DistanceSquared(Start, End); if (num >= 0.25f && num <= 256f && Point.Dot(Start, Start) <= 144f && Point.Dot(End, End) <= 144f && Math.Abs(Point.Dot(StartNormal, StartNormal) - 1f) < 0.01f) { return Math.Abs(Point.Dot(EndNormal, EndNormal) - 1f) < 0.01f; } return false; } public Point[] Sample() { if (!Valid()) { throw new ArgumentException("Invalid stretch pipe layout."); } float s = Math.Min(3f, (float)Math.Sqrt(Point.DistanceSquared(Start, End)) * 0.3f); Point a = SnapGeometry.Subtract(Start, SnapGeometry.Scale(StartNormal, s)); Point a2 = SnapGeometry.Subtract(End, SnapGeometry.Scale(EndNormal, s)); Point[] array = new Point[33]; for (int i = 0; i <= 32; i++) { float num = (float)i / 32f; float num2 = 1f - num; ref Point reference = ref array[i]; reference = SnapGeometry.Add(SnapGeometry.Add(SnapGeometry.Scale(Start, num2 * num2 * num2), SnapGeometry.Scale(a, 3f * num2 * num2 * num)), SnapGeometry.Add(SnapGeometry.Scale(a2, 3f * num2 * num * num), SnapGeometry.Scale(End, num * num * num))); } return array; } public byte[] Encode() { if (!Valid()) { throw new ArgumentException("Invalid stretch pipe layout."); } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(1); Point[] array = new Point[4] { Start, End, StartNormal, EndNormal }; for (int i = 0; i < array.Length; i++) { Point point = array[i]; binaryWriter.Write(point.X); binaryWriter.Write(point.Y); binaryWriter.Write(point.Z); } return memoryStream.ToArray(); } public static StretchShape Decode(byte[] bytes) { if (bytes == null || bytes.Length != 52) { return null; } using MemoryStream input = new MemoryStream(bytes); using BinaryReader binaryReader = new BinaryReader(input); if (binaryReader.ReadInt32() != 1) { return null; } Point[] array = new Point[4]; for (int i = 0; i < 4; i++) { ref Point reference = ref array[i]; reference = new Point(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); } StretchShape stretchShape = new StretchShape(array[0], array[1], array[2], array[3]); return stretchShape.Valid() ? stretchShape : null; } } public sealed class SweptPipeMesh { public readonly List Vertices = new List(); public readonly List Normals = new List(); public readonly List U = new List(); public readonly List V = new List(); public readonly List Triangles = new List(); public readonly List Surface = new List(); private readonly Dictionary surfaceCopies = new Dictionary(); private static Point Add(Point a, Point b) { return new Point(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } private static Point Sub(Point a, Point b) { return new Point(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } private static Point Scale(Point p, float s) { return new Point(p.X * s, p.Y * s, p.Z * s); } private static Point Cross(Point a, Point b) { return new Point(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } private static float Length(Point p) { return (float)Math.Sqrt(Point.Dot(p, p)); } private static Point Unit(Point p) { float num = Length(p); if (num < 1E-07f) { return new Point(0f, 0f, 1f); } return Scale(p, 1f / num); } private int Vertex(Point p, Point n, float u, float v, int surface) { int count = Vertices.Count; Vertices.Add(p); Normals.Add(n); U.Add(u); V.Add(v); Surface.Add(surface); return count; } private void Face(int a, int b, int c) { Triangles.Add(a); Triangles.Add(b); Triangles.Add(c); } private int WithSurface(int index, int surface) { if (Surface[index] == surface) { return index; } long key = ((long)index << 3) | (uint)surface; if (surfaceCopies.TryGetValue(key, out var value)) { return value; } value = Vertex(Vertices[index], Normals[index], U[index], V[index], surface); surfaceCopies.Add(key, value); return value; } public void Append(SweptPipeMesh other) { int count = Vertices.Count; Vertices.AddRange(other.Vertices); Normals.AddRange(other.Normals); U.AddRange(other.U); V.AddRange(other.V); Surface.AddRange(other.Surface); foreach (int triangle in other.Triangles) { Triangles.Add(triangle + count); } } private static Point Normal(Point tangent) { Point a = ((Math.Abs(tangent.Y) < 0.8f) ? new Point(0f, 1f, 0f) : new Point(1f, 0f, 0f)); return Unit(Cross(a, tangent)); } private static float Radius(float s, float length, float radius, float start, float end, float collar) { float num = s; float num2 = start; if (length - s < s) { num = length - s; num2 = end; } if (num2 <= radius || num >= collar) { return radius; } float num3 = Math.Min(0.018f, collar * 0.2f); if (num < num3) { return num2 - 0.012f + num / num3 * 0.012f; } if (num > collar - num3) { return radius + (num2 - radius) * (collar - num) / num3; } return num2; } public static SweptPipeMesh Build(Point[] path, float radius, float startCollarRadius, float endCollarRadius, float collarLength = 0.13f, int sides = 12, Point? startTangent = null, Point? endTangent = null) { if (path == null || path.Length < 2 || sides < 3 || radius <= 0f) { throw new ArgumentException("Invalid pipe geometry."); } List list = new List(); for (int i = 0; i < path.Length; i++) { Point point = path[i]; if (float.IsNaN(point.X + point.Y + point.Z) || float.IsInfinity(point.X) || float.IsInfinity(point.Y) || float.IsInfinity(point.Z)) { throw new ArgumentException("Nonfinite pipe point."); } if (list.Count == 0 || Point.DistanceSquared(list[list.Count - 1], point) > 1E-10f) { list.Add(point); } } if (list.Count < 2) { throw new ArgumentException("Zero length pipe."); } for (int num = list.Count - 2; num > 0; num--) { Point a = Unit(Sub(list[num], list[num - 1])); Point b = Unit(Sub(list[num + 1], list[num])); if (Point.Dot(a, b) > 0.999999f) { list.RemoveAt(num); } } float[] array = new float[list.Count]; for (int j = 1; j < list.Count; j++) { array[j] = array[j - 1] + Length(Sub(list[j], list[j - 1])); } Point[] array2 = new Point[list.Count]; for (int k = 0; k < list.Count; k++) { ref Point reference = ref array2[k]; reference = ((k == 0) ? Unit(Sub(list[1], list[0])) : ((k == list.Count - 1) ? Unit(Sub(list[k], list[k - 1])) : Unit(Add(Unit(Sub(list[k], list[k - 1])), Unit(Sub(list[k + 1], list[k])))))); } if (startTangent.HasValue) { ref Point reference2 = ref array2[0]; reference2 = Unit(startTangent.Value); } if (endTangent.HasValue) { ref Point reference3 = ref array2[^1]; reference3 = Unit(endTangent.Value); } float num2 = array[^1]; float num3 = Math.Min(collarLength, num2 * 0.24f); float num4 = Math.Min(0.018f, num3 * 0.2f); SortedSet sortedSet = new SortedSet(array); if (startCollarRadius > radius) { sortedSet.Add(num4); sortedSet.Add(num3 - num4); sortedSet.Add(num3); } if (endCollarRadius > radius) { sortedSet.Add(num2 - num4); sortedSet.Add(num2 - num3 + num4); sortedSet.Add(num2 - num3); } List list2 = new List(); foreach (float item in sortedSet) { float num5 = item; if (list2.Count == 0 || num5 - list2[list2.Count - 1] > 1E-05f) { list2.Add(num5); } } SweptPipeMesh sweptPipeMesh = new SweptPipeMesh(); Point a2 = default(Point); Point tangent = default(Point); Point normal = default(Point); Point tangent2 = default(Point); Point normal2 = default(Point); int l = 0; int num6 = 0; for (int m = 0; m < list2.Count; m++) { float num7; for (num7 = list2[m]; l < list.Count - 2 && array[l + 1] < num7 - 1E-06f; l++) { } float num8 = (num7 - array[l]) / (array[l + 1] - array[l]); Point a3 = Add(list[l], Scale(Sub(list[l + 1], list[l]), num8)); Point point2 = Unit(Add(Scale(array2[l], 1f - num8), Scale(array2[l + 1], num8))); if (m == 0 && startTangent.HasValue) { point2 = Unit(startTangent.Value); } if (m == list2.Count - 1 && endTangent.HasValue) { point2 = Unit(endTangent.Value); } Point p = ((m == 0) ? Normal(point2) : Sub(a2, Scale(point2, Point.Dot(a2, point2)))); p = ((!(Length(p) < 0.001f)) ? Unit(p) : Normal(point2)); Point p2 = Unit(Cross(point2, p)); a2 = p; if (m == 0) { tangent = point2; normal = p; } tangent2 = point2; normal2 = p; float s = Radius(num7, num2, radius, startCollarRadius, endCollarRadius, num3); float num9 = Math.Max(0f, num7 - 0.001f); float num10 = Math.Min(num2, num7 + 0.001f); float s2 = (Radius(num10, num2, radius, startCollarRadius, endCollarRadius, num3) - Radius(num9, num2, radius, startCollarRadius, endCollarRadius, num3)) / (num10 - num9); int surface = (((num7 < num3 && startCollarRadius > radius) || (num7 > num2 - num3 && endCollarRadius > radius)) ? 1 : 0); int count = sweptPipeMesh.Vertices.Count; for (int n = 0; n <= sides; n++) { double num11 = (double)(n * 2) * Math.PI / (double)sides; Point point3 = Add(Scale(p, (float)Math.Cos(num11)), Scale(p2, (float)Math.Sin(num11))); sweptPipeMesh.Vertex(Add(a3, Scale(point3, s)), Unit(Sub(point3, Scale(point2, s2))), (float)n / (float)sides, num7 * 2f, surface); } if (m > 0) { int num12 = num6; int num13 = count; float num14 = (num7 + list2[m - 1]) * 0.5f; int surface2 = (((num14 < num3 && startCollarRadius > radius) || (num14 > num2 - num3 && endCollarRadius > radius)) ? 1 : 0); for (int num15 = 0; num15 < sides; num15++) { int a4 = sweptPipeMesh.WithSurface(num12 + num15, surface2); int b2 = sweptPipeMesh.WithSurface(num12 + num15 + 1, surface2); int c = sweptPipeMesh.WithSurface(num13 + num15, surface2); int num16 = sweptPipeMesh.WithSurface(num13 + num15 + 1, surface2); sweptPipeMesh.Face(a4, b2, num16); sweptPipeMesh.Face(a4, num16, c); } } num6 = count; } sweptPipeMesh.End(list[0], tangent, normal, Radius(0f, num2, radius, startCollarRadius, endCollarRadius, num3), radius * 0.7f, sides, start: true); sweptPipeMesh.End(list[list.Count - 1], tangent2, normal2, Radius(num2, num2, radius, startCollarRadius, endCollarRadius, num3), radius * 0.7f, sides, start: false); return sweptPipeMesh; } private void End(Point center, Point tangent, Point normal, float outer, float inner, int sides, bool start) { Point point = Scale(tangent, (!start) ? 1 : (-1)); Point p = Cross(tangent, normal); int count = Vertices.Count; for (int i = 0; i < 2; i++) { for (int j = 0; j <= sides; j++) { double num = (double)(j * 2) * Math.PI / (double)sides; Point p2 = Add(Scale(normal, (float)Math.Cos(num)), Scale(p, (float)Math.Sin(num))); Vertex(Add(center, Scale(p2, (i == 0) ? outer : inner)), point, (float)j / (float)sides, (float)i * 0.1f, 1); } } for (int k = 0; k < sides; k++) { int num2 = count + k; int num3 = count + sides + 1 + k; if (start) { Face(num2, num3 + 1, num2 + 1); Face(num2, num3, num3 + 1); } else { Face(num2, num2 + 1, num3 + 1); Face(num2, num3 + 1, num3); } } Point point2 = Sub(center, Scale(point, 0.06f)); int count2 = Vertices.Count; for (int l = 0; l < 2; l++) { for (int m = 0; m <= sides; m++) { double num4 = (double)(m * 2) * Math.PI / (double)sides; Point p3 = Add(Scale(normal, (float)Math.Cos(num4)), Scale(p, (float)Math.Sin(num4))); Vertex(Add((l == 0) ? center : point2, Scale(p3, inner)), Scale(p3, -1f), (float)m / (float)sides, (float)l * 0.12f, 2); } } for (int n = 0; n < sides; n++) { int num5 = count2 + n; int num6 = count2 + sides + 1 + n; if (start) { Face(num5, num6 + 1, num5 + 1); Face(num5, num6, num6 + 1); } else { Face(num5, num5 + 1, num6 + 1); Face(num5, num6 + 1, num6); } } int a = Vertex(point2, point, 0.5f, 0.5f, 2); int count3 = Vertices.Count; for (int num7 = 0; num7 <= sides; num7++) { double num8 = (double)(num7 * 2) * Math.PI / (double)sides; Point p4 = Add(Scale(normal, (float)Math.Cos(num8)), Scale(p, (float)Math.Sin(num8))); Vertex(Add(point2, Scale(p4, inner)), point, (float)num7 / (float)sides, 0f, 2); } for (int num9 = 0; num9 < sides; num9++) { if (start) { Face(a, count3 + num9 + 1, count3 + num9); } else { Face(a, count3 + num9, count3 + num9 + 1); } } } } public static class TightElbowShape { public static Point[] Path(Point turn) { List list = new List(); list.Add(new Point(0f, 0f, -0.5f)); List list2 = list; for (int i = 0; i <= 8; i++) { double num = (double)i * Math.PI / 16.0; float num2 = 0.25f * (1f - (float)Math.Cos(num)); list2.Add(new Point(turn.X * num2, turn.Y * num2, -0.25f + 0.25f * (float)Math.Sin(num))); } list2.Add(new Point(turn.X * 0.5f, turn.Y * 0.5f, 0f)); return list2.ToArray(); } } public static class EndpointClearanceRules { public static bool Allows(Point socket, Point normal, Point a, Point b) { Point point = SnapGeometry.Subtract(a, socket); Point point2 = SnapGeometry.Subtract(b, socket); if (Point.Dot(point, point) <= 0.42249995f && Point.Dot(point2, point2) <= 0.42249995f && Point.Dot(point, normal) >= -0.01f) { return Point.Dot(point2, normal) >= -0.01f; } return false; } }