Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Buildheim v1.2.0
plugins/Buildheim.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Managers; using Jotunn.Utils; using PlanBuild.Blueprints; using PlanBuild.Client; using SimpleJson; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Buildheim")] [assembly: AssemblyDescription("Client-only blueprint planning and hammer building for Valheim")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Buildheim")] [assembly: AssemblyCopyright("WTFPL")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.0.0")] [module: UnverifiableCode] namespace PlanBuild { [BepInPlugin("augusdogus.Buildheim", "Buildheim", "1.2.0")] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [BepInIncompatibility("marcopogo.PlanBuild")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class PlanBuildPlugin : BaseUnityPlugin { public const string PluginGUID = "augusdogus.Buildheim"; public const string PluginName = "Buildheim"; public const string PluginVersion = "1.2.0"; private ClientPlanner planner; private void Awake() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)SystemInfo.graphicsDeviceType == 4) { ((Behaviour)this).enabled = false; return; } if (!ClientPaths.TryMigrateConfig(Paths.ConfigPath, ((BaseUnityPlugin)this).Config.ConfigFilePath, out var copied, out var error)) { ((BaseUnityPlugin)this).Logger.LogError((object)error); ((Behaviour)this).enabled = false; return; } if (copied) { ((BaseUnityPlugin)this).Config.Reload(); } planner = new ClientPlanner(new ClientConfig(((BaseUnityPlugin)this).Config)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Buildheim loaded. Press End in a world to open the planner."); } private void Update() { planner?.Update(); } private void LateUpdate() { planner?.DrawProjection(); } private void OnDestroy() { planner?.Dispose(); } } } namespace PlanBuild.Blueprints { public class PieceEntry { public string line; public string name; public string category; public float posX; public float posY; public float posZ; public float rotX; public float rotY; public float rotZ; public float rotW; public string additionalInfo; public float scaleX; public float scaleY; public float scaleZ; public static PieceEntry FromBlueprint(string line) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_0098: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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) if (line.IndexOf(',') > -1) { line = line.Replace(',', '.'); } string[] array = line.Split(new char[1] { ';' }); string text = array[0]; string text2 = array[1]; float num = InvariantFloat(array[2]); float num2 = InvariantFloat(array[3]); float num3 = InvariantFloat(array[4]); float num4 = InvariantFloat(array[5]); float num5 = InvariantFloat(array[6]); float num6 = InvariantFloat(array[7]); float num7 = InvariantFloat(array[8]); Vector3 pos = default(Vector3); ((Vector3)(ref pos))..ctor(num, num2, num3); Quaternion val = new Quaternion(num4, num5, num6, num7); Quaternion normalized = ((Quaternion)(ref val)).normalized; string text3 = array[9]; if (string.IsNullOrEmpty(text3) || text3.Equals("\"\"")) { text3 = null; } else { try { text3 = SimpleJson.DeserializeObject<string>(text3); } catch { text3 = array[9]; } } Vector3 one = Vector3.one; if (array.Length > 10) { float num8 = InvariantFloat(array[10]); float num9 = InvariantFloat(array[11]); float num10 = InvariantFloat(array[12]); ((Vector3)(ref one))..ctor(num8, num9, num10); } return new PieceEntry(text, text2, pos, normalized, text3, one); } public static PieceEntry FromVBuild(string line) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_00a1: 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_00a7: Unknown result type (might be due to invalid IL or missing references) if (line.IndexOf(',') > -1) { line = line.Replace(',', '.'); } string[] array = line.Split(new char[1] { ' ' }); string text = array[0]; float num = InvariantFloat(array[1]); float num2 = InvariantFloat(array[2]); float num3 = InvariantFloat(array[3]); float num4 = InvariantFloat(array[4]); float num5 = InvariantFloat(array[5]); float num6 = InvariantFloat(array[6]); float num7 = InvariantFloat(array[7]); string text2 = "Building"; Quaternion val = new Quaternion(num, num2, num3, num4); Quaternion normalized = ((Quaternion)(ref val)).normalized; Vector3 pos = default(Vector3); ((Vector3)(ref pos))..ctor(num5, num6, num7); string empty = string.Empty; return new PieceEntry(text, text2, pos, normalized, empty, Vector3.one); } public PieceEntry(string name, string category, Vector3 pos, Quaternion rot, string additionalInfo, Vector3 scale) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_004b: 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_0065: 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_00b5: 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_00cf: Unknown result type (might be due to invalid IL or missing references) this.name = name.Split(new char[1] { '(' })[0]; this.category = category; posX = pos.x; posY = pos.y; posZ = pos.z; rotX = rot.x; rotY = rot.y; rotZ = rot.z; rotW = rot.w; this.additionalInfo = additionalInfo; if (!string.IsNullOrEmpty(this.additionalInfo)) { this.additionalInfo = string.Concat(this.additionalInfo.Split(new char[1] { ';' })); } scaleX = scale.x; scaleY = scale.y; scaleZ = scale.z; line = string.Join(";", this.name, this.category, InvariantString(posX), InvariantString(posY), InvariantString(posZ), InvariantString(rotX), InvariantString(rotY), InvariantString(rotZ), InvariantString(rotW), SimpleJson.SerializeObject((object)additionalInfo), InvariantString(scaleX), InvariantString(scaleY), InvariantString(scaleZ)); } public Vector3 GetPosition() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(posX, posY, posZ); } public Quaternion GetRotation() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(rotX, rotY, rotZ, rotW); } public Vector3 GetScale() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(scaleX, scaleY, scaleZ); } internal static float InvariantFloat(string s) { if (string.IsNullOrEmpty(s)) { return 0f; } return float.Parse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo); } internal static string InvariantString(float f) { return f.ToString(NumberFormatInfo.InvariantInfo); } } } namespace PlanBuild.Client { internal sealed class AutoBuilder { private readonly AutoBuildQueue queue = new AutoBuildQueue(); public HammerTarget Find(BlueprintProjection projection, Player player) { HammerTarget selected = null; queue.Next(projection.Pieces.Count, Time.time, delegate(int index) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) HammerTarget hammerTarget = HammerTarget.FromPiece(projection, projection.Pieces[index], player); if (hammerTarget == null || !player.m_knownRecipes.Contains(hammerTarget.Piece.m_name) || !player.IsPieceAvailable(hammerTarget.Piece) || !hammerTarget.HasInventoryResources() || ZoneSystem.instance.GetGlobalKey(hammerTarget.Piece.FreeBuildKey()) || !player.HaveRequirements(hammerTarget.Piece, (RequirementMode)0)) { return false; } selected = hammerTarget; return true; }); return selected; } public bool TryAttempt(Player player) { ItemData rightItem = ((Humanoid)player).GetRightItem(); if (rightItem != null && (!rightItem.m_shared.m_useDurability || rightItem.m_durability > 0f) && ((Character)player).HaveStamina(rightItem.m_shared.m_attack.m_attackStamina) && Time.time - player.m_lastToolUseTime > player.m_placeDelay) { return queue.TryAttempt(Time.time); } return false; } public void Reset() { queue.Reset(); } } internal sealed class AutoBuildQueue { private int nextIndex; private int? selectedIndex; private float nextAttempt; private float nextScan; public int? Next(int count, float now, Func<int, bool> eligible) { if (selectedIndex.HasValue && selectedIndex.Value < count && eligible(selectedIndex.Value)) { return selectedIndex; } selectedIndex = null; if (count <= 0 || now < nextScan) { return null; } for (int i = 0; i < count; i++) { int num = nextIndex % count; nextIndex = (num + 1) % count; if (eligible(num)) { selectedIndex = num; nextAttempt = now + 0.5f; return num; } } nextScan = now + 0.5f; return null; } public bool TryAttempt(float now) { if (!selectedIndex.HasValue || now < nextAttempt) { return false; } selectedIndex = null; return true; } public void Reset() { nextIndex = 0; selectedIndex = null; nextAttempt = 0f; nextScan = 0f; } } internal sealed class BlueprintDocument { public const int MaxPieces = 10000; public string Name { get; } public IReadOnlyList<PieceEntry> Pieces { get; } public BlueprintDocument(string name, IReadOnlyList<PieceEntry> pieces) { Name = name; Pieces = pieces; } public static bool TryParse(string name, IEnumerable<string> lines, bool vbuild, out BlueprintDocument document, out string error) { document = null; error = string.Empty; List<PieceEntry> list = new List<PieceEntry>(); bool flag = true; int num = 0; try { foreach (string line in lines) { num++; string text = line.Trim(); if (text.Length == 0) { continue; } if (text.StartsWith("#Name:")) { name = text.Substring(6).Trim(); } if (text == "#Pieces") { flag = true; } switch (text) { case "#SnapPoints": case "#Terrain": case "#Description": flag = false; break; } if (!text.StartsWith("#") && flag) { PieceEntry pieceEntry = (vbuild ? PieceEntry.FromVBuild(text) : PieceEntry.FromBlueprint(text)); if (string.IsNullOrWhiteSpace(pieceEntry.name) || !ValidTransform(pieceEntry)) { error = $"Invalid piece transform on line {num}. Fix the file and reload it."; return false; } list.Add(pieceEntry); if (list.Count > 10000) { error = $"Blueprint exceeds the {10000} piece limit. Split it into smaller blueprints."; return false; } } } } catch (Exception ex) when (ex is FormatException || ex is IndexOutOfRangeException || ex is OverflowException) { error = $"Cannot read piece on line {num}: {ex.Message} Fix the file and reload it."; return false; } if (list.Count == 0) { error = "This file contains no building pieces. Choose another blueprint."; return false; } document = new BlueprintDocument(name, list); return true; } private static bool ValidTransform(PieceEntry entry) { if (new float[10] { entry.posX, entry.posY, entry.posZ, entry.rotX, entry.rotY, entry.rotZ, entry.rotW, entry.scaleX, entry.scaleY, entry.scaleZ }.All((float x) => !float.IsNaN(x) && !float.IsInfinity(x)) && entry.scaleX > 0f && entry.scaleY > 0f) { return entry.scaleZ > 0f; } return false; } public IEnumerable<string> Serialize() { yield return "#Name:" + Name; yield return "#Pieces"; foreach (PieceEntry piece in Pieces) { yield return piece.line; } } } internal sealed class BlueprintHitTest { private readonly Vector3[] vertices; private readonly int[] triangles; public BlueprintHitTest(Vector3[] vertices, int[] triangles) { this.vertices = vertices; this.triangles = triangles; } public bool Intersect(Ray ray, out float distance) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0053: 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_0059: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0090: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00bd: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00f0: Unknown result type (might be due to invalid IL or missing references) distance = float.PositiveInfinity; for (int i = 0; i < triangles.Length; i += 3) { Vector3 val = vertices[triangles[i]]; Vector3 val2 = vertices[triangles[i + 1]] - val; Vector3 val3 = vertices[triangles[i + 2]] - val; Vector3 val4 = Vector3.Cross(((Ray)(ref ray)).direction, val3); float num = Vector3.Dot(val2, val4); if (Mathf.Abs(num) < 1E-06f) { continue; } float num2 = 1f / num; Vector3 val5 = ((Ray)(ref ray)).origin - val; float num3 = Vector3.Dot(val5, val4) * num2; if (num3 < 0f || num3 > 1f) { continue; } Vector3 val6 = Vector3.Cross(val5, val2); float num4 = Vector3.Dot(((Ray)(ref ray)).direction, val6) * num2; if (!(num4 < 0f) && !(num3 + num4 > 1f)) { float num5 = Vector3.Dot(val3, val6) * num2; if (num5 >= 0f && num5 < distance) { distance = num5; } } } return !float.IsPositiveInfinity(distance); } } internal sealed class BlueprintLayers { private readonly float[] origins; private int[] available; public float Height { get; private set; } = 2f; public int? Selected { get; private set; } public int Count => available.Length; public int Ordinal { get { if (!Selected.HasValue) { return 0; } return Array.IndexOf(available, Selected.Value) + 1; } } public BlueprintLayers(IEnumerable<float> origins) { this.origins = origins.ToArray(); available = GetAvailable(); } private int Index(float height) { return (int)Math.Floor((double)height / (double)Height); } private int[] GetAvailable() { return (from index in origins.Select(Index).Distinct() orderby index select index).ToArray(); } public bool Contains(float originHeight) { if (Selected.HasValue) { return Index(originHeight) == Selected.Value; } return true; } public void All() { Selected = null; } public void Bottom() { if (available.Length != 0) { Selected = available[0]; } } public bool Select(int? layer) { if (layer.HasValue && Array.IndexOf(available, layer.Value) < 0) { return false; } Selected = layer; return true; } public void Move(int direction) { if (available.Length != 0) { if (!Selected.HasValue) { Bottom(); return; } int num = Array.IndexOf(available, Selected.Value); num = Math.Max(0, Math.Min(available.Length - 1, num + Math.Sign(direction))); Selected = available[num]; } } public bool SetHeight(float height) { if (float.IsNaN(height) || float.IsInfinity(height) || height < 0.5f || height > 4f) { return false; } if (height == Height) { return true; } Height = height; available = GetAvailable(); if (Selected.HasValue) { Bottom(); } return true; } } internal static class BlueprintLibrary { public static bool TryList(string directory, out string[] files, out string error) { files = Array.Empty<string>(); error = string.Empty; try { Directory.CreateDirectory(directory); files = (from x in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories) where Path.GetExtension(x).Equals(".blueprint", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(x).Equals(".vbuild", StringComparison.OrdinalIgnoreCase) select x).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray(); return true; } catch (Exception ex) when (IsFileError(ex)) { error = "Cannot list blueprints in " + directory + ": " + ex.Message + " Check the directory setting and permissions."; return false; } } public static bool TryLoad(string path, out BlueprintDocument document, out string error) { document = null; error = string.Empty; try { if (new FileInfo(path).Length > 8388608) { error = "Blueprint exceeds 8 MB. Split it into smaller files before loading."; return false; } return BlueprintDocument.TryParse(Path.GetFileNameWithoutExtension(path), File.ReadLines(path), Path.GetExtension(path).Equals(".vbuild", StringComparison.OrdinalIgnoreCase), out document, out error); } catch (Exception ex) when (IsFileError(ex)) { error = "Cannot load " + Path.GetFileName(path) + ": " + ex.Message + " Check the file and try again."; return false; } } public static List<PieceEntry> CapturePieces(CaptureBounds bounds, Vector3 origin) { //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_0041: 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_004d: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) List<PieceEntry> list = new List<PieceEntry>(); foreach (Piece s_allPiece in Piece.s_allPieces) { if (!Object.op_Implicit((Object)(object)s_allPiece) || !s_allPiece.IsPlacedByPlayer()) { continue; } Vector3 position = ((Component)s_allPiece).transform.position; if (!bounds.Contains(position.x, position.y, position.z)) { continue; } ZNetView component = ((Component)s_allPiece).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { GameObject prefab = ZNetScene.instance.GetPrefab(component.GetZDO().GetPrefab()); if (Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<Piece>())) { list.Add(new PieceEntry(((Object)prefab).name, "Building", position - origin, ((Component)s_allPiece).transform.rotation, string.Empty, ((Component)s_allPiece).transform.lossyScale)); } } } return list; } public static bool TryCapture(string directory, string name, CaptureBounds bounds, Vector3 origin, out string error) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) error = string.Empty; name = name.Trim(); if (name.Length == 0 || name.Length > 80 || name.Any((char c) => !char.IsLetterOrDigit(c) && c != ' ' && c != '_' && c != '-')) { error = "Use a name of 1 to 80 letters, numbers, spaces, underscores or hyphens."; return false; } if (bounds == null || !bounds.HasVolume) { error = "Select two corners with width, height and depth. Use Alt + wheel to raise the upper corner."; return false; } List<PieceEntry> list = CapturePieces(bounds, origin); if (list.Count == 0 || list.Count > 10000) { error = $"Found {list.Count} pieces. Adjust the box to contain 1 to {10000} loaded, player-built pieces."; return false; } try { Directory.CreateDirectory(directory); using (FileStream stream = new FileStream(Path.Combine(directory, name + ".blueprint"), FileMode.CreateNew, FileAccess.Write)) { using StreamWriter streamWriter = new StreamWriter(stream); foreach (string item in new BlueprintDocument(name, list).Serialize()) { streamWriter.WriteLine(item); } } return true; } catch (Exception ex) when (IsFileError(ex)) { error = "Cannot save " + name + ": " + ex.Message + " Existing files were not overwritten. Choose a new name or check directory permissions."; return false; } } private static bool IsFileError(Exception ex) { if (!(ex is IOException) && !(ex is UnauthorizedAccessException) && !(ex is ArgumentException)) { return ex is NotSupportedException; } return true; } } internal sealed class BlueprintMaterialsHud : IDisposable { private static BlueprintMaterialsHud instance; private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.materialHud"); private readonly Func<Piece> hoveredPiece; public BlueprintMaterialsHud(Func<Piece> hoveredPiece) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown this.hoveredPiece = hoveredPiece; instance = this; harmony.PatchAll(typeof(BlueprintMaterialsHud)); } [HarmonyPostfix] [HarmonyPatch(typeof(Hud), "SetupPieceInfo")] private static void ShowBlueprintMaterials(Hud __instance, Piece piece) { if (instance != null && Object.op_Implicit((Object)(object)piece) && piece.m_repairPiece) { Piece val = instance.hoveredPiece(); if (Object.op_Implicit((Object)(object)val) && !val.m_repairPiece && !val.m_removePiece) { __instance.SetupPieceInfo(val); } } } public void Dispose() { harmony.UnpatchSelf(); instance = null; } } internal sealed class BlueprintProjection : IDisposable { internal sealed class Part { public Mesh Mesh { get; } public Matrix4x4 LocalMatrix { get; } public BlueprintHitTest HitTest { get; } public Part(Mesh mesh, Matrix4x4 matrix, BlueprintHitTest hitTest) { //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) Mesh = mesh; LocalMatrix = matrix; HitTest = hitTest; } } internal sealed class ProjectedPiece { public PieceEntry Entry { get; } public GameObject Prefab { get; } public IReadOnlyList<Part> Parts { get; } public Bounds LocalBounds { get; } public bool Completed { get; set; } public ProjectedPiece(PieceEntry entry, GameObject prefab, IReadOnlyList<Part> parts) { //IL_001d: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //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_009b: 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_00a0: 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_00ac: Unknown result type (might be due to invalid IL or missing references) Entry = entry; Prefab = prefab; Parts = parts; Bounds val = default(Bounds); bool flag = true; foreach (Part part in parts) { Bounds bounds = part.Mesh.bounds; for (int i = 0; i < 8; i++) { Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((i & 1) != 0) ? 1 : (-1)), (float)(((i & 2) != 0) ? 1 : (-1)), (float)(((i & 4) != 0) ? 1 : (-1)))); Matrix4x4 localMatrix = part.LocalMatrix; Vector3 val3 = ((Matrix4x4)(ref localMatrix)).MultiplyPoint3x4(((Bounds)(ref bounds)).center + val2); if (flag) { val = new Bounds(val3, Vector3.zero); flag = false; } else { ((Bounds)(ref val)).Encapsulate(val3); } } } LocalBounds = val; } } private readonly Material material; private readonly Material aimedMaterial; public string Name { get; } public IReadOnlyList<ProjectedPiece> Pieces { get; } public BlueprintLayers Layers { get; } public int MissingPrefabs { get; } public Vector3 Position { get; set; } public float Yaw { get; set; } public bool Enabled { get; set; } = true; public Quaternion Rotation => Quaternion.Euler(0f, Yaw, 0f); private BlueprintProjection(BlueprintDocument document, List<ProjectedPiece> pieces, int missing, Material material) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown Name = document.Name; Pieces = pieces; Layers = new BlueprintLayers(pieces.Select((ProjectedPiece piece) => piece.Entry.posY)); MissingPrefabs = missing; this.material = material; aimedMaterial = new Material(material) { color = new Color(1f, 0.85f, 0.2f, 0.3f) }; } public static bool TryCreate(BlueprintDocument document, out BlueprintProjection projection, out string error) { //IL_025d: 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_0277: 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_028e: Expected O, but got Unknown //IL_01de: 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_01ef: Unknown result type (might be due to invalid IL or missing references) projection = null; error = string.Empty; Shader prefab = Cache.GetPrefab<Shader>("Lux Lit Particles/ Bumped"); if (!Object.op_Implicit((Object)(object)prefab)) { error = "The blueprint shader is unavailable. Rejoin the world and try again."; return false; } List<ProjectedPiece> list = new List<ProjectedPiece>(); int num = 0; Dictionary<Mesh, BlueprintHitTest> dictionary = new Dictionary<Mesh, BlueprintHitTest>(); foreach (PieceEntry piece in document.Pieces) { GameObject prefab2 = ZNetScene.instance.GetPrefab(piece.name); if (!Object.op_Implicit((Object)(object)prefab2)) { num++; continue; } List<Part> list2 = new List<Part>(); HashSet<Renderer> hashSet = new HashSet<Renderer>(prefab2.GetComponentsInChildren<LODGroup>(true).SelectMany((LODGroup group) => group.GetLODs().Skip(1)).SelectMany((LOD lod) => lod.renderers)); MeshFilter[] componentsInChildren = prefab2.GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>(); if (!Object.op_Implicit((Object)(object)val.sharedMesh) || !Object.op_Implicit((Object)(object)component) || !((Renderer)component).enabled || hashSet.Contains((Renderer)(object)component) || !ActiveBelowRoot(((Component)val).transform, prefab2.transform)) { continue; } Mesh mesh = val.sharedMesh; if (!dictionary.TryGetValue(mesh, out var value)) { value = ((mesh.isReadable && Enumerable.Range(0, mesh.subMeshCount).All((int i) => (int)mesh.GetTopology(i) == 0)) ? new BlueprintHitTest(mesh.vertices, mesh.triangles) : null); dictionary.Add(mesh, value); } list2.Add(new Part(mesh, prefab2.transform.worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix, value)); } if (list2.Count == 0) { num++; } else { list.Add(new ProjectedPiece(piece, prefab2, list2)); } } if (list.Count == 0) { error = "No pieces in this blueprint can be displayed. Check its required piece mods."; return false; } Material val2 = new Material(prefab) { color = new Color(0.15f, 0.8f, 1f, 0.3f), renderQueue = 3000 }; val2.SetOverrideTag("RenderType", "Transparent"); val2.SetFloat("_ZWrite", 0f); val2.SetFloat("_SrcBlend", 5f); val2.SetFloat("_DstBlend", 10f); val2.DisableKeyword("DIRECTIONAL"); projection = new BlueprintProjection(document, list, num, val2); return true; } private static bool ActiveBelowRoot(Transform node, Transform root) { while ((Object)(object)node != (Object)(object)root) { if (!((Component)node).gameObject.activeSelf) { return false; } node = node.parent; } return true; } public Vector3 PiecePosition(ProjectedPiece piece) { //IL_0001: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) return Position + Rotation * piece.Entry.GetPosition(); } public Quaternion PieceRotation(ProjectedPiece piece) { //IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references) return Rotation * piece.Entry.GetRotation(); } public Matrix4x4 PieceMatrix(ProjectedPiece piece) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) return Matrix4x4.TRS(PiecePosition(piece), PieceRotation(piece), piece.Entry.GetScale()); } public void Draw(ProjectedPiece selected = null, ProjectedPiece aimed = null) { //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_0094: 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_009c: Unknown result type (might be due to invalid IL or missing references) Camera val = (Object.op_Implicit((Object)(object)GameCamera.instance) ? ((Component)GameCamera.instance).GetComponent<Camera>() : null); if (!Object.op_Implicit((Object)(object)val)) { return; } foreach (ProjectedPiece piece in Pieces) { if (piece.Completed || !Layers.Contains(piece.Entry.posY) || piece == selected) { continue; } Matrix4x4 val2 = PieceMatrix(piece); foreach (Part part in piece.Parts) { for (int i = 0; i < part.Mesh.subMeshCount; i++) { Graphics.DrawMesh(part.Mesh, val2 * part.LocalMatrix, (piece == aimed) ? aimedMaterial : material, 0, val, i, (MaterialPropertyBlock)null, (ShadowCastingMode)0, false); } } } } public void Dispose() { Object.Destroy((Object)(object)material); Object.Destroy((Object)(object)aimedMaterial); } } internal static class BlueprintRotation { public const float Step = 22.5f; public static float Turn(float yaw, int direction, bool coarse) { double num = (yaw % 360f + 360f) % 360f / 22.5f; int num2 = ((!coarse) ? 1 : 4); return (float)((((direction > 0) ? (Math.Floor(num + 1E-06) + (double)num2) : (Math.Ceiling(num - 1E-06) - (double)num2)) * 22.5 % 360.0 + 360.0) % 360.0); } } internal abstract class BlueprintSelection { internal sealed class Hammer : BlueprintSelection { public HammerTarget Target { get; } public Hammer(HammerTarget target) : base(target.Planned) { Target = target; } } internal sealed class Guide : BlueprintSelection { public string Reason { get; } public Guide(BlueprintProjection.ProjectedPiece planned, string reason) : base(planned) { Reason = reason; } } public BlueprintProjection.ProjectedPiece Planned { get; } private BlueprintSelection(BlueprintProjection.ProjectedPiece planned) { Planned = planned; } } internal static class BuildMaterials { public static bool HasAll(IEnumerable<KeyValuePair<string, int>> costs, Func<string, int> inventoryCount) { foreach (KeyValuePair<string, long> item in Total(costs)) { if (inventoryCount(item.Key) < item.Value) { return false; } } return true; } public static Dictionary<string, long> Missing(IEnumerable<KeyValuePair<string, int>> costs, Func<string, int> inventoryCount) { Dictionary<string, long> dictionary = new Dictionary<string, long>(); foreach (KeyValuePair<string, long> item in Total(costs)) { long num = item.Value - inventoryCount(item.Key); if (num > 0) { dictionary.Add(item.Key, num); } } return dictionary; } public static Dictionary<string, long> Total(IEnumerable<KeyValuePair<string, int>> costs) { Dictionary<string, long> dictionary = new Dictionary<string, long>(); foreach (KeyValuePair<string, int> cost in costs) { if (cost.Value > 0) { dictionary.TryGetValue(cost.Key, out var value); dictionary[cost.Key] = value + cost.Value; } } return dictionary; } } internal enum BuildMode { Guide, Assisted, Automatic } internal sealed class CaptureBounds { public float MinX { get; } public float MinY { get; } public float MinZ { get; } public float MaxX { get; } public float MaxY { get; } public float MaxZ { get; } public float Width => MaxX - MinX; public float Height => MaxY - MinY; public float Depth => MaxZ - MinZ; public bool HasVolume { get { if (Width > 0f && Height > 0f) { return Depth > 0f; } return false; } } private CaptureBounds(float ax, float ay, float az, float bx, float by, float bz) { MinX = Math.Min(ax, bx); MinY = Math.Min(ay, by); MinZ = Math.Min(az, bz); MaxX = Math.Max(ax, bx); MaxY = Math.Max(ay, by); MaxZ = Math.Max(az, bz); } public bool Contains(float x, float y, float z) { if (x >= MinX && x <= MaxX && y >= MinY && y <= MaxY && z >= MinZ) { return z <= MaxZ; } return false; } public static bool TryCreate(float ax, float ay, float az, float bx, float by, float bz, out CaptureBounds bounds) { bounds = null; float[] array = new float[6] { ax, ay, az, bx, by, bz }; foreach (float f in array) { if (float.IsNaN(f) || float.IsInfinity(f)) { return false; } } CaptureBounds captureBounds = new CaptureBounds(ax, ay, az, bx, by, bz); if (float.IsInfinity(captureBounds.Width) || float.IsInfinity(captureBounds.Height) || float.IsInfinity(captureBounds.Depth)) { return false; } bounds = captureBounds; return true; } } internal sealed class CaptureOutline : IDisposable { private Mesh mesh; private Material material; private readonly MaterialPropertyBlock color = new MaterialPropertyBlock(); public void Draw(Vector3? first, Vector3? second) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_00b1: 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_00c6: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: 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_01cd: 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_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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_0160: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: 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_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: 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_0207: 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_0216: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((!first.HasValue && !second.HasValue) || !Object.op_Implicit((Object)(object)GameCamera.instance)) { return; } if (!Object.op_Implicit((Object)(object)material)) { Shader val = Shader.Find("Hidden/Internal-Colored"); if (!Object.op_Implicit((Object)(object)val)) { return; } material = new Material(val) { renderQueue = 4000 }; material.SetInt("_SrcBlend", 5); material.SetInt("_DstBlend", 10); material.SetInt("_Cull", 0); material.SetInt("_ZWrite", 0); material.SetInt("_ZTest", 8); mesh = new Mesh { name = "Buildheim capture box" }; Vector3[] array = (Vector3[])(object)new Vector3[8]; for (int i = 0; i < array.Length; i++) { array[i] = new Vector3((float)(i & 1), (float)((i >> 1) & 1), (float)((i >> 2) & 1)); } mesh.vertices = array; mesh.SetIndices(new int[24] { 0, 1, 2, 3, 4, 5, 6, 7, 0, 2, 1, 3, 4, 6, 5, 7, 0, 4, 1, 5, 2, 6, 3, 7 }, (MeshTopology)3, 0); mesh.RecalculateBounds(); } Camera component = ((Component)GameCamera.instance).GetComponent<Camera>(); if (first.HasValue && second.HasValue) { Box(Vector3.Min(first.Value, second.Value), Vector3.Max(first.Value, second.Value) - Vector3.Min(first.Value, second.Value), new Color(0.3f, 0.9f, 1f, 0.8f), component); } if (first.HasValue) { Box(first.Value - Vector3.one * 0.12f, Vector3.one * 0.24f, Color.cyan, component); } if (second.HasValue) { Box(second.Value - Vector3.one * 0.12f, Vector3.one * 0.24f, new Color(1f, 0.65f, 0.15f), component); } } private void Box(Vector3 position, Vector3 size, Color tint, Camera camera) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_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) color.SetColor("_Color", tint); Graphics.DrawMesh(mesh, Matrix4x4.TRS(position, Quaternion.identity, size), material, 0, camera, 0, color, (ShadowCastingMode)0, false); } public void Dispose() { if (Object.op_Implicit((Object)(object)mesh)) { Object.Destroy((Object)(object)mesh); } if (Object.op_Implicit((Object)(object)material)) { Object.Destroy((Object)(object)material); } } } internal sealed class CapturePanel { private readonly ClientPlanner planner; private readonly Text corners; private readonly Text dimensions; private readonly Button save; public CapturePanel(ClientPlanner planner, Transform parent) { this.planner = planner; PlannerWidgets.Label(parent, "Capture a building", 0f, 0f, 644f, 32f, 22, heading: true); InputField name = PlannerWidgets.Input(parent, "Blueprint name", 0f, 46f, 644f); PlannerWidgets.Button(parent, "Select / edit box", 0f, 100f, 314f, planner.BeginCapture); PlannerWidgets.Button(parent, "Clear selection", 330f, 100f, 314f, planner.Selection.Clear); corners = PlannerWidgets.Label(parent, "", 0f, 150f, 644f, 58f); dimensions = PlannerWidgets.Label(parent, "", 0f, 216f, 644f, 48f, 20, heading: true); PlannerWidgets.Label(parent, "Pick opposite corners with left and right click. The wheel adjusts the selected corner; Alt changes its height. Middle click switches corners.", 0f, 274f, 644f, 60f, 17); PlannerWidgets.Label(parent, "Includes loaded, player-built pieces whose placement point is inside the box. Corner A is the blueprint origin.", 0f, 340f, 644f, 48f, 16); save = PlannerWidgets.Button(parent, "Save blueprint", 0f, 400f, 644f, delegate { planner.Capture(name.text); }); } public void Refresh() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) CaptureSelection selection = planner.Selection; corners.text = "A (cyan): " + Position(selection.First) + "\nB (orange): " + Position(selection.Second); CaptureBounds bounds; bool flag = selection.TryBounds(out bounds) && bounds.HasVolume; int num = (flag ? BlueprintLibrary.CapturePieces(bounds, selection.First.Value).Count : 0); dimensions.text = ((bounds == null) ? "Select two opposite corners to start." : $"{bounds.Width:0.0} × {bounds.Height:0.0} × {bounds.Depth:0.0} m | {num} pieces"); if (bounds != null && !flag) { Text obj = dimensions; obj.text += "\nGive the box width, height and depth."; } else if (flag && num == 0) { Text obj2 = dimensions; obj2.text += "\nMove the box around a player-built structure."; } else if (num > 10000) { Text obj3 = dimensions; obj3.text += $"\nReduce the selection to {10000} pieces."; } ((Selectable)save).interactable = flag && num > 0 && num <= 10000; } private static string Position(Vector3? point) { //IL_0016: 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_0038: Unknown result type (might be due to invalid IL or missing references) if (!point.HasValue) { return "not selected"; } return $"{point.Value.x:0.0}, {point.Value.y:0.0}, {point.Value.z:0.0}"; } } internal sealed class CaptureSelection : IDisposable { private static CaptureSelection instance; private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.capture"); private readonly Func<bool> canTakeInput; private readonly CaptureOutline outline = new CaptureOutline(); private int startedFrame; private int scrollFrame = -1; private bool firstSelected = true; public const string Hints = "Left click: corner A | Right click: corner B | Middle click: switch corner\nCtrl + wheel: forward/back | Ctrl + X + wheel: sideways | Alt + wheel: height | Shift: 1 m"; public Vector3? First { get; private set; } public Vector3? Second { get; private set; } public bool Editing { get; private set; } public string SelectedCorner { get { if (!firstSelected) { return "B"; } return "A"; } } public string Status { get; private set; } = "Aim at the ground or a building and pick two opposite corners."; private bool Active { get { if (Editing) { return canTakeInput(); } return false; } } private static bool Control { get { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } } private static bool Alt { get { if (!Input.GetKey((KeyCode)308)) { return Input.GetKey((KeyCode)307); } return true; } } public CaptureSelection(Func<bool> canTakeInput) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown this.canTakeInput = canTakeInput; instance = this; harmony.PatchAll(typeof(CaptureSelection)); } public void Begin() { Editing = true; startedFrame = Time.frameCount; } public void End() { Editing = false; } public void Clear() { End(); Vector3? first = (Second = null); First = first; } public bool TryBounds(out CaptureBounds bounds) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0068: 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_008e: Unknown result type (might be due to invalid IL or missing references) bounds = null; if (First.HasValue && Second.HasValue) { return CaptureBounds.TryCreate(First.Value.x, First.Value.y, First.Value.z, Second.Value.x, Second.Value.y, Second.Value.z, out bounds); } return false; } public void Update() { //IL_0069: 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) if (!Active || Time.frameCount == startedFrame) { return; } if (Input.GetMouseButtonDown(2)) { firstSelected = !firstSelected; } if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { firstSelected = Input.GetMouseButtonDown(0); if (TryAim(out var point)) { if (firstSelected) { First = point; } else { Second = point; } Status = "Corner " + SelectedCorner + " set. Use modifier keys and the wheel to adjust it."; } else { Status = "No surface within 100 m. Aim at the ground or a building to set a corner."; } } if (Control || Alt) { ZInput.GetMouseScrollWheel(); } } private static bool TryAim(out Vector3 point) { //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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) point = Vector3.zero; if (!Object.op_Implicit((Object)(object)GameCamera.instance)) { return false; } Camera component = ((Component)GameCamera.instance).GetComponent<Camera>(); int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" }); RaycastHit val = default(RaycastHit); if (!Physics.Raycast(component.ViewportPointToRay(new Vector3(0.5f, 0.5f)), ref val, 100f, mask, (QueryTriggerInteraction)1)) { return false; } point = ((RaycastHit)(ref val)).point; return true; } public void Draw(bool showInMenu) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (Editing || showInMenu) { Vector3? second = Second; if (Active && First.HasValue && !second.HasValue && TryAim(out var point)) { second = point; } outline.Draw(First, second); } } [HarmonyPostfix] [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] private static void Scroll(ref float __result) { //IL_0082: 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_00aa: 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_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_0101: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00d3: 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_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_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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) if (instance == null || !instance.Active || (!Control && !Alt)) { return; } float num = __result; __result = 0f; if (num == 0f || instance.scrollFrame == Time.frameCount) { return; } instance.scrollFrame = Time.frameCount; Vector3? val = (instance.firstSelected ? instance.First : instance.Second); if (val.HasValue) { Vector3 val2 = Vector3.up; if (!Alt && Object.op_Implicit((Object)(object)GameCamera.instance)) { val2 = Quaternion.Euler(0f, ((Component)GameCamera.instance).transform.eulerAngles.y, 0f) * (Input.GetKey((KeyCode)120) ? Vector3.right : Vector3.forward); } bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); Vector3 value = val.Value + val2 * Mathf.Sign(num) * (flag ? 1f : 0.1f); if (instance.firstSelected) { instance.First = value; } else { instance.Second = value; } } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "SetControls")] private static void SuppressActions(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool crouch, ref bool dodge) { if (instance != null && instance.Active && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = (dodge = false)))))); if (Control) { crouch = false; } } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static bool SuppressPlacement(Player __instance) { if (instance == null || !instance.Editing || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } __instance.m_placePressedTime = (__instance.m_removePressedTime = -9999f); if (Object.op_Implicit((Object)(object)__instance.m_placementGhost)) { __instance.m_placementGhost.SetActive(false); } if (Object.op_Implicit((Object)(object)__instance.m_placementMarkerInstance)) { __instance.m_placementMarkerInstance.SetActive(false); } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(ZInput), "GetButtonDown")] private static bool SuppressSit(string name, ref bool __result) { if (instance == null || !instance.Active || !Control || name != "Sit" || !Input.GetKey((KeyCode)120)) { return true; } __result = false; return false; } public void Dispose() { harmony.UnpatchSelf(); instance = null; outline.Dispose(); } } internal sealed class ChestObservation : IDisposable { private sealed class Subscription : IDisposable { private readonly Inventory inventory; private readonly Action refresh; public Container Container { get; } public Subscription(Container container, Action refresh) { Container = container; inventory = container.GetInventory(); this.refresh = refresh; Inventory obj = inventory; obj.m_onChanged = (Action)Delegate.Combine(obj.m_onChanged, refresh); refresh(); } public void Dispose() { Inventory obj = inventory; obj.m_onChanged = (Action)Delegate.Remove(obj.m_onChanged, refresh); } } private readonly MaterialChecklist checklist; private readonly Dictionary<string, Subscription> subscriptions = new Dictionary<string, Subscription>(); public ChestObservation(MaterialChecklist checklist) { this.checklist = checklist; } public static IEnumerable<KeyValuePair<string, int>> Items(Inventory inventory) { return from item in inventory.GetAllItems() select new KeyValuePair<string, int>(item.m_shared.m_name, item.m_stack); } public void Update() { KeyValuePair<string, Subscription>[] array = subscriptions.Where((KeyValuePair<string, Subscription> entry) => !Object.op_Implicit((Object)(object)entry.Value.Container)).ToArray(); for (int num = 0; num < array.Length; num++) { KeyValuePair<string, Subscription> keyValuePair = array[num]; keyValuePair.Value.Dispose(); subscriptions.Remove(keyValuePair.Key); } Container container = (Object.op_Implicit((Object)(object)InventoryGui.instance) ? InventoryGui.instance.m_currentContainer : null); if (!Object.op_Implicit((Object)(object)container) || !InventoryGui.IsVisible() || !container.IsOwner()) { return; } ZNetView component = ((Component)container).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return; } string key = ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString(); if (!subscriptions.ContainsKey(key)) { subscriptions.Add(key, new Subscription(container, delegate { checklist.Observe(key, Items(container.GetInventory())); })); } } public void Dispose() { foreach (Subscription value in subscriptions.Values) { value.Dispose(); } subscriptions.Clear(); checklist.ForgetChests(); } } internal sealed class ClientConfig { public string DataDirectory { get; } public ConfigEntry<string> Directory { get; } public ConfigEntry<KeyCode> ToggleKey { get; } public ConfigEntry<KeyCode> AutoBuildKey { get; } public ConfigEntry<KeyCode> PlacementKey { get; } public ConfigEntry<KeyCode> HudKey { get; } public ConfigEntry<bool> ShowHud { get; } public ClientConfig(ConfigFile config) { DataDirectory = ClientPaths.DataDirectory(Paths.ConfigPath); Directory = config.Bind<string>("Client", "Blueprint directory", Path.Combine(DataDirectory, "blueprints"), "Local directory for .blueprint and .vbuild files. Nothing is uploaded to the server."); ToggleKey = config.Bind<KeyCode>("Client", "Planner key", (KeyCode)279, "Open or close the blueprint planner."); AutoBuildKey = config.Bind<KeyCode>("Client", "Autobuild key", (KeyCode)278, "Toggle autobuild for the current blueprint while the planner is closed."); PlacementKey = config.Bind<KeyCode>("Client", "Placement key", (KeyCode)288, "Enable or disable the current placement without clearing it. Disabling hides the hologram and stops build assistance."); HudKey = config.Bind<KeyCode>("Client", "HUD key", (KeyCode)289, "Show or hide Buildheim's HUD while the planner is closed. Does not change placement or build mode."); ShowHud = config.Bind<bool>("Client", "Show HUD", true, "Show Buildheim's build status and shortcut hints. Hiding the HUD does not stop autobuild or hide the hologram."); } } internal static class ClientPaths { public static string DataDirectory(string configRoot) { string text = Path.Combine(configRoot, "Buildheim"); string text2 = Path.Combine(configRoot, "PlanBuild"); if (!Directory.Exists(text) && Directory.Exists(text2)) { return text2; } return text; } public static bool TryMigrateConfig(string configRoot, string destination, out bool copied, out string error) { copied = false; error = string.Empty; string text = Path.Combine(configRoot, "marcopogo.PlanBuild.cfg"); try { if (File.Exists(destination) || !File.Exists(text)) { return true; } File.Copy(text, destination, overwrite: false); copied = true; return true; } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { error = "Buildheim could not copy settings from " + text + " to " + destination + ": " + ex.Message + " The original settings and blueprints are preserved. Check permissions before restarting."; return false; } } } internal sealed class ClientPlanner : IDisposable { private readonly HammerAssistance assistance = new HammerAssistance(); private readonly ProjectionControls controls; private readonly ChestObservation chests; private readonly PlannerWindow view; private readonly PlacementSession session = new PlacementSession(); private ZNetScene scene; private float nextProgressCheck; public ClientConfig Config { get; } public BlueprintProjection Projection { get; private set; } public CaptureSelection Selection { get; } public bool PlacementEnabled { get { if (Projection != null) { return Projection.Enabled; } return false; } } private BlueprintProjection ActiveProjection { get { if (!PlacementEnabled || Selection.Editing) { return null; } return Projection; } } public BuildMode Mode { get; private set; } = BuildMode.Assisted; public MaterialChecklist Materials { get; } = new MaterialChecklist(); public string[] Files { get; private set; } = Array.Empty<string>(); public string Status { get; private set; } = "Choose a blueprint or select a box to capture a building."; public bool Visible { get; private set; } public string BuildStatus { get { if (!controls.Held) { return assistance.Status; } return "Positioning hologram. Release modifiers to build."; } } public bool HasHudActivity { get { if (!Selection.Editing && !controls.Held) { return assistance.AimedPiece != null; } return true; } } public Piece HoveredPiece { get { BlueprintProjection.ProjectedPiece aimedPiece = assistance.AimedPiece; if (aimedPiece == null) { return null; } return aimedPiece.Prefab.GetComponent<Piece>(); } } public ClientPlanner(ClientConfig config) { Config = config; Selection = new CaptureSelection(() => !Visible && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && ((Character)Player.m_localPlayer).TakeInput() && !Hud.IsPieceSelectionVisible()); controls = new ProjectionControls(() => (Visible || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead() || !((Character)Player.m_localPlayer).TakeInput() || Hud.IsPieceSelectionVisible()) ? null : ActiveProjection); chests = new ChestObservation(Materials); view = new PlannerWindow(this); } public void Update() { //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_019d: 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_01f5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)scene != (Object)(object)ZNetScene.instance) { SaveSession(); ResetProjection(); session.Reset(); chests.Dispose(); Selection.Clear(); SetVisible(value: false, playSound: false); view.Hide(); scene = ZNetScene.instance; } if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead()) { SaveSession(); if (Mode == BuildMode.Automatic) { Mode = BuildMode.Assisted; } Selection.End(); SetVisible(value: false, playSound: false); assistance.SetProjection(null); view.Hide(); return; } if (!session.Active && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.GetWorld() != null && Player.m_localPlayer.GetPlayerID() != 0L) { RestoreSession(); } if (Projection != null) { chests.Update(); if (Time.time >= nextProgressCheck) { if (PlacementEnabled) { ProjectionProgress.Refresh(Projection); } nextProgressCheck = Time.time + 0.5f; SaveSession(); } } if ((!Object.op_Implicit((Object)(object)Settings.instance) || !((Behaviour)Settings.instance).isActiveAndEnabled) && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus())) { if (!Visible && ((Character)Player.m_localPlayer).TakeInput()) { if (Input.GetKeyDown(Config.PlacementKey.Value)) { SetPlacementEnabled(!PlacementEnabled); } if (Input.GetKeyDown(Config.HudKey.Value)) { ToggleHud(); } if (!Selection.Editing && PlacementEnabled && Input.GetKeyDown(Config.AutoBuildKey.Value)) { SetMode((Mode == BuildMode.Automatic) ? BuildMode.Assisted : BuildMode.Automatic); } } if (Input.GetKeyDown(Config.ToggleKey.Value)) { if (Visible) { SetVisible(value: false); } else if (((Character)Player.m_localPlayer).TakeInput()) { Refresh(); SetVisible(value: true); } } if (Visible && Input.GetKeyDown((KeyCode)27)) { SetVisible(value: false); } } controls.Update(); Selection.Update(); UpdateAssistance(); view.Update(); } public void SetVisible(bool value, bool playSound = true) { if (Visible != value) { Visible = value; if (value) { Hud.HidePieceSelection(); } if (playSound) { PlannerPresentation.PlaySound(value); } if (value && Selection.Editing) { Selection.End(); view.SelectCapture(); } UpdateAssistance(); GUIManager.BlockInput(value); if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Player.m_localPlayer.m_placePressedTime = -9999f; Player.m_localPlayer.m_removePressedTime = -9999f; ZInput.ResetButtonStatus("Attack"); ZInput.ResetButtonStatus("JoyPlace"); } } } public void SetPlacementEnabled(bool enabled) { if (Projection != null && Projection.Enabled != enabled) { Projection.Enabled = enabled; if (!enabled && Mode == BuildMode.Automatic) { Mode = BuildMode.Assisted; } if (enabled) { ProjectionProgress.Refresh(Projection); } UpdateAssistance(); Status = (enabled ? "Placement enabled. Autobuild is off." : "Placement disabled. Its position, layers and checklist are preserved."); SaveSession(); } } public void SetMode(BuildMode mode) { if (PlacementEnabled) { Mode = mode; UpdateAssistance(); } } public void ToggleHud() { Config.ShowHud.Value = !Config.ShowHud.Value; } private void UpdateAssistance() { assistance.SetProjection((!Visible && !controls.Held && Mode != BuildMode.Guide) ? ActiveProjection : null, Mode); } public void DrawProjection() { if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && (Object)(object)scene == (Object)(object)ZNetScene.instance) { ActiveProjection?.Draw(assistance.PreviewPiece, assistance.AimedPiece); Selection.Draw(Visible && view.CaptureVisible); } } public void Refresh() { if (BlueprintLibrary.TryList(Config.Directory.Value, out var files, out var error)) { Files = files; } else { Status = error; } } public void OpenBlueprintFolder() { try { string fullPath = Path.GetFullPath(Config.Directory.Value); Directory.CreateDirectory(fullPath); using (Process.Start(new ProcessStartInfo(fullPath) { UseShellExecute = true, Verb = "open" })) { } Status = "Folder open requested. Add .blueprint or .vbuild files, then click Refresh."; } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException || ex is NotSupportedException || ex is Win32Exception || ex is SecurityException || ex is InvalidOperationException) { Status = "Cannot open the blueprint folder. Check the game log for its path and open it manually."; Logger.LogWarning((object)("Cannot open blueprint folder '" + Config.Directory.Value + "': " + ex.Message + " Check the directory setting, permissions, and default file manager.")); } } public void BeginCapture() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (Mode == BuildMode.Automatic) { Mode = BuildMode.Assisted; } Selection.Begin(); SetVisible(value: false); Status = $"Select corners A and B, then press {Config.ToggleKey.Value} to review and save."; } public void Capture(string name) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) string error; if (!Selection.TryBounds(out var bounds) || !Selection.First.HasValue) { Status = "Select both corners before saving a blueprint."; } else if (BlueprintLibrary.TryCapture(Config.Directory.Value, name, bounds, Selection.First.Value, out error)) { Refresh(); Selection.End(); Status = "Saved " + name + ".blueprint. Corner A defines its origin."; } else { Status = error; } } public bool Load(string file) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!BlueprintLibrary.TryLoad(file, out var document, out var error) || !BlueprintProjection.TryCreate(document, out var projection, out error)) { Status = error; return false; } ResetProjection(); session.SetDocument(document); Projection = projection; Projection.Position = ((Component)Player.m_localPlayer).transform.position; ProjectionProgress.Refresh(Projection); Status = "Blueprint loaded. Close the planner to position it with modifier keys and the wheel."; SaveSession(); return true; } public IReadOnlyList<MaterialChecklist.Row> MaterialRows(bool selectedLayer) { List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(); if (Projection != null) { foreach (BlueprintProjection.ProjectedPiece item in Projection.Pieces.Where((BlueprintProjection.ProjectedPiece piece) => !piece.Completed && (!selectedLayer || Projection.Layers.Contains(piece.Entry.posY)))) { Piece component = item.Prefab.GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)component)) { continue; } foreach (Requirement item2 in component.m_resources.Where((Requirement resource) => Object.op_Implicit((Object)(object)resource.m_resItem))) { list.Add(new KeyValuePair<string, int>(item2.m_resItem.m_itemData.m_shared.m_name, item2.GetAmount(0))); } } } return Materials.Rows(list, ChestObservation.Items(((Humanoid)Player.m_localPlayer).GetInventory())); } public void ForgetChests() { chests.Dispose(); } public void MoveToFeet() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (Projection != null) { Projection.Position = ((Component)Player.m_localPlayer).transform.position; ProjectionProgress.Refresh(Projection); } } private void RestoreSession() { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) string directory = Path.Combine(Config.DataDirectory, "placements"); if (!session.Open(directory, ZNet.instance.GetWorldUID(), Player.m_localPlayer.GetPlayerID(), out var save, out var error)) { if (error.Length > 0) { Status = error; } return; } if (!BlueprintDocument.TryParse(save.Name, save.Blueprint, vbuild: false, out var document, out error) || !BlueprintProjection.TryCreate(document, out var projection, out error)) { Status = "Saved hologram could not be restored. " + error; return; } Projection = projection; session.SetDocument(document); Projection.Position = new Vector3(save.X, save.Y, save.Z); Projection.Yaw = save.Yaw; Projection.Enabled = !save.Disabled; Projection.Layers.SetHeight(save.LayerHeight); bool flag = Projection.Layers.Select(save.Layer); Mode = ((!save.PreviewOnly) ? BuildMode.Assisted : BuildMode.Guide); string[] checkedMaterials = save.CheckedMaterials; foreach (string name in checkedMaterials) { Materials.Check(name, value: true); } ProjectionProgress.Refresh(Projection); Status = (flag ? "Saved hologram restored. Autobuild is off." : "Saved hologram restored with all layers; its saved layer was invalid. Autobuild is off."); if (!PlacementEnabled) { Status += $" Placement is disabled. Press {Config.PlacementKey.Value} or enable it in Build."; } } private void SaveSession() { if (!session.Save(Projection, Mode, Materials, out var error)) { if (Status != error) { Logger.LogError((object)error); } Status = error; } } public void Clear() { ResetProjection(); Status = (session.Clear(out var error) ? "Hologram cleared, including its saved placement." : error); } private void ResetProjection() { Mode = BuildMode.Assisted; assistance.SetProjection(null); Projection?.Dispose(); Projection = null; Materials.ClearChecks(); } public void Dispose() { SaveSession(); ResetProjection(); SetVisible(value: false, playSound: false); chests.Dispose(); view.Dispose(); assistance.Dispose(); controls.Dispose(); Selection.Dispose(); } } internal sealed class HammerAssistance : IDisposable { private static HammerAssistance instance; private static HammerTarget ghostTarget; private readonly Harmony harmony = new Harmony("augusdogus.Buildheim.hammer"); private readonly AutoBuilder autoBuilder = new AutoBuilder(); private BuildMode mode; private BlueprintProjection projection; private BlueprintSelection selection; private HammerTarget target => (selection as BlueprintSelection.Hammer)?.Target; public BlueprintProjection.ProjectedPiece AimedPiece { get { if (!Active(Player.m_localPlayer) || !((Character)Player.m_localPlayer).TakeInput() || Hud.IsPieceSelectionVisible()) { return null; } return selection?.Planned; } } public BlueprintProjection.ProjectedPiece PreviewPiece { get { if (target == null || !target.RecipeKnown || !Object.op_Implicit((Object)(object)target.Player.m_placementGhost) || !((Object)(object)target.Player.GetSelectedPiece() == (Object)(object)target.Piece)) { return null; } return target.Planned; } } public string Status { get; private set; } = "Equip a hammer and aim at a missing piece."; public bool Ready { get; private set; } public HammerAssistance() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown instance = this; try { harmony.PatchAll(typeof(HammerAssistance)); Ready = true; } catch (Exception arg) { harmony.UnpatchSelf(); Status = "Hammer assistance could not load for this game version. Holograms still work; check the BepInEx log."; Logger.LogError((object)$"Cannot patch hammer placement: {arg}"); } } public void SetProjection(BlueprintProjection value, BuildMode buildMode = BuildMode.Assisted) { if (projection != value || mode != buildMode) { projection = value; mode = buildMode; selection = null; autoBuilder.Reset(); HideGhost(Player.m_localPlayer); } } private bool Active(Player player) { if (Ready && projection != null && Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead()) { ItemData rightItem = ((Humanoid)player).GetRightItem(); object obj; if (rightItem == null) { obj = null; } else { GameObject dropPrefab = rightItem.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } return (string?)obj == "Hammer"; } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static bool SelectPiece(Player __instance, bool takeInput, out bool __state) { //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) __state = false; if (instance == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (ProjectionControls.Adjusting) { instance.selection = null; __instance.m_placePressedTime = -9999f; __instance.m_removePressedTime = -9999f; if (Object.op_Implicit((Object)(object)__instance.m_placementGhost)) { __instance.m_placementGhost.SetActive(false); } if (Object.op_Implicit((Object)(object)__instance.m_placementMarkerInstance)) { __instance.m_placementMarkerInstance.SetActive(false); } return false; } if (!instance.Active(__instance) || !takeInput || Hud.IsPieceSelectionVisible()) { instance.selection = null; return true; } if (instance.mode == BuildMode.Automatic && (ZInput.GetButton("Remove") || ZInput.GetButton("JoyRemove") || ZInput.GetButton("JoyAltKeys"))) { instance.selection = null; return true; } if (instance.mode == BuildMode.Automatic) { HammerTarget hammerTarget = instance.autoBuilder.Find(instance.projection, __instance); instance.selection = ((hammerTarget == null) ? null : new BlueprintSelection.Hammer(hammerTarget)); } else { instance.selection = HammerTarget.Find(instance.projection, __instance); } if (instance.selection is BlueprintSelection.Guide guide) { instance.Status = guide.Reason; SelectRepair(__instance); return true; } HammerTarget hammerTarget2 = instance.target; if (hammerTarget2 == null) { instance.Status = ((instance.mode == BuildMode.Automatic) ? "Waiting for a nearby buildable piece, materials or stamina." : "Aim at a missing piece within hammer reach."); SelectRepair(__instance); return true; } string text = hammerTarget2.RecipeError(); if (text != null) { instance.Status = text; SelectRepair(__instance); return true; } if (!hammerTarget2.HasInventoryResources()) { instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": missing materials in your inventory."; SelectRepair(__instance); return true; } if (ZoneSystem.instance.GetGlobalKey(hammerTarget2.Piece.FreeBuildKey()) || !__instance.HaveRequirements(hammerTarget2.Piece, (RequirementMode)0)) { instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": " + (ZoneSystem.instance.GetGlobalKey(hammerTarget2.Piece.FreeBuildKey()) ? "disable the world's free-build setting to use hammer assistance." : "check the required crafting station and recipe requirements."); SelectRepair(__instance); return true; } if ((Object)(object)__instance.GetSelectedPiece() != (Object)(object)hammerTarget2.Piece && !__instance.SetSelectedPiece(hammerTarget2.Piece)) { instance.Status = Localization.instance.Localize(hammerTarget2.Piece.m_name) + ": the hammer could not select this recipe. Reopen its build menu and try again."; SelectRepair(__instance); return true; } instance.Status = "Click to build " + Localization.instance.Localize(hammerTarget2.Piece.m_name); if (instance.mode != BuildMode.Automatic) { return true; } instance.Status = "Next: " + Localization.instance.Localize(hammerTarget2.Piece.m_name); if (!instance.autoBuilder.TryAttempt(__instance)) { return true; } __instance.UpdatePlacementGhost(false); if ((int)__instance.m_placementStatus != 0) { instance.Status = PlacementFeedback.Describe(__instance.m_placementStatus); return true; } __instance.m_placePressedTime = Time.time; __state = true; instance.Status = "Autobuilding " + Localization.instance.Localize(hammerTarget2.Piece.m_name); return true; } private static void SelectRepair(Player player) { Piece selectedPiece = player.GetSelectedPiece(); if ((selectedPiece == null || !selectedPiece.m_repairPiece) && Object.op_Implicit((Object)(object)player.m_buildPieces)) { Piece val = (from prefab in player.m_buildPieces.m_pieces where Object.op_Implicit((Object)(object)prefab) select prefab.GetComponent<Piece>()).FirstOrDefault((Func<Piece, bool>)((Piece piece) => Object.op_Implicit((Object)(object)piece) && piece.m_repairPiece)); if (Object.op_Implicit((Object)(object)val) && !player.SetSelectedPiece(val)) { Logger.LogWarning((object)"The hammer could not select Repair after clearing a blueprint recipe."); } } HideGhost(player); } private static void HideGhost(Player player) { if (Object.op_Implicit((Object)(object)player)) { player.m_placePressedTime = -9999f; if (Object.op_Implicit((Object)(object)player.m_placementGhost)) { player.m_placementGhost.SetActive(false); } if (Object.op_Implicit((Object)(object)player.m_placementMarkerInstance)) { player.m_placementMarkerInstance.SetActive(false); } } } [HarmonyTranspiler] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static IEnumerable<CodeInstruction> GuardSelectedRecipe(IEnumerable<CodeInstruction> instructions) { MethodInfo getPiece = AccessTools.Method(typeof(PieceTable), "GetSelectedPiece", (Type[])null, (Type[])null); List<CodeInstruction> list = instructions.ToList(); if (list.Count((CodeInstruction val) => CodeInstructionExtensions.Calls(val, getPiece)) != 1) { throw new InvalidOperationException("UpdatePlacement no longer reads one build recipe; assistance was disabled."); } foreach (CodeInstruction instruction in list) { yield return instruction; if (CodeInstructionExtensions.Calls(instruction, getPiece)) { yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(HammerAssistance), "BuildRecipe", (Type[])null, (Type[])null)); } } } private static Piece BuildRecipe(Piece recipe, Player player) { if (instance == null || !instance.Active(player)) { return recipe; } if (Object.op_Implicit((Object)(object)recipe) && recipe.m_repairPiece) { if (instance.selection != null) { return null; } return recipe; } HammerTarget hammerTarget = instance.target; if (hammerTarget == null || !hammerTarget.RecipeKnown || !((Object)(object)hammerTarget.Piece == (Object)(object)recipe) || hammerTarget.Planned.Completed || !instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY)) { return null; } return recipe; } [HarmonyFinalizer] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static void ClearAutoClick(Player __instance, bool __state) { if (__state) { __instance.m_placePressedTime = -9999f; } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static bool CheckBuild(Player __instance, Piece piece, ref bool __result) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (instance == null || !instance.Active(__instance)) { return true; } HammerTarget hammerTarget = instance.target; ProjectionProgress.Refresh(instance.projection); string text = null; if (hammerTarget == null || (Object)(object)hammerTarget.Piece != (Object)(object)piece || hammerTarget.Planned.Completed || !instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY)) { text = "Aim at a missing blueprint piece to build it."; } else { string text2 = hammerTarget.RecipeError(); if (text2 != null) { text = text2; } else if (ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { text = "Hammer assistance requires resource costs. Disable the world's free-build setting first."; } else { string text3 = hammerTarget.MissingMaterialsError(); if (text3 != null) { text = text3; } else if (!__instance.HaveRequirements(piece, (RequirementMode)0)) { text = "Check the required crafting station and recipe requirements."; } } } if (text == null) { return true; } __result = false; ((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null, false); return false; } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static void Built(Player __instance, bool __result) { if (__result && instance != null && instance.Active(__instance) && instance.target != null) { instance.target.Planned.Completed = true; } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static void BeginGhost(Player __instance, out HammerTarget __state) { //IL_009e: 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) __state = ghostTarget; ghostTarget = null; if (instance != null && instance.Active(__instance)) { HammerTarget hammerTarget = instance.target; if (hammerTarget != null && hammerTarget.RecipeKnown && !hammerTarget.Planned.Completed && instance.projection.Layers.Contains(hammerTarget.Planned.Entry.posY) && !((Object)(object)__instance.GetSelectedPiece() != (Object)(object)hammerTarget.Piece) && Object.op_Implicit((Object)(object)__instance.m_placementGhost)) { ghostTarget = hammerTarget; __instance.m_placementGhost.transform.SetPositionAndRotation(hammerTarget.Position, hammerTarget.Rotation); } } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static void EndGhost(Player __instance) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (instance == null || !instance.Active(__instance)) { return; } if (ghostTarget == null) { __instance.m_placementStatus = (PlacementStatus)1; if (Object.op_Implicit((Object)(object)__instance.m_placementGhost)) { __instance.m_placementGhost.SetActive(false); } return; } __instance.m_placementGhost.SetActive(true); __instance.m_placementGhost.transform.SetPositionAndRotation(ghostTarget.Position, ghostTarget.Rotation); string text = Localization.instance.Localize(ghostTarget.Piece.m_name); if ((int)__instance.m_placementStatus != 0) { instance.Status = text + ": " + PlacementFeedback.Describe(__instance.m_placementStatus); } else if (ZoneSystem.instance.GetGlobalKey(ghostTarget.Piece.FreeBuildKey())) { instance.Status = text + ": disable the world's free-build setting to use hammer assistance."; } else if (!ghostTarget.HasInventoryResources()) { instance.Status = text + ": missing materials in your inventory."; } else if (!__instance.HaveRequirements(ghostTarget.Piece, (RequirementMode)0)) { instance.Status = text + ": check the required crafting station and recipe requirements."; } } [HarmonyFinalizer] [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static void ClearGhost(HammerTarget __state) { ghostTarget = __state; } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "PieceRayTest")] private static bool BlueprintSurface(Player __instance, bool water, ref Vector3 point, ref Vector3 normal, ref Piece piece, ref Heightmap heightmap, ref Collider waterSurface, ref bool __result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (ghostTarget == null || (Object)(object)ghostTarget.Player != (Object)(object)__instance) { return true; } point = Vector3.zero; normal = Vector3.zero; piece = null; heightmap = null; waterSurface = null; __result = ghostTarget.TrySurface(water, out var hit); if (__result) { point = ((RaycastHit)(ref hit)).point; normal = ((RaycastHit)(ref hit)).normal; piece = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Piece>(); heightmap = ((Component)((RaycastHit)(ref hit)).collider).GetComponent<Heightmap>(); if (((Component)((RaycastHit)(ref hit)).collider).gameObject.layer == LayerMask.NameToLayer("Water")) { waterSurface = ((RaycastHit)(ref hit)).collider; } } return false; } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "PieceRayTest")] private static void CheckSurface(Player __instance, ref Vector3 point, ref bool __result) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (ghostTarget != null && !((Object)(object)ghostTarget.Player != (Object)(object)__instance) && __result) { if (!ghostTarget.NearSurface(point) || Vector3.Distance(((Character)__instance).m_eye.position, point) >= __instance.m_maxPlaceDistance + (float)ghostTarget.Piece.m_extraPlacementDistance) { __result = false; instance.Status = "No reachable surface touches this piece. Move closer or build its foundation first."; } else { point = ghostTarget.Position; } } } [HarmonyTranspiler] [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static IEnumerable<CodeInstruction> AlignGhost(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); MethodInfo methodInfo = AccessTools.PropertySetter(typeof(Transform), "position"); MethodInfo methodInfo2 = AccessTools.PropertySetter(typeof(Transform), "rotation"); int num = 0; int num2 = 0; foreach (CodeInstruction item in list) { if (item.operand is MethodInfo methodInfo3 && methodInfo3.DeclaringType == typeof(Transform) && (methodInfo3.Name == "SetPositionAndRotation" || methodInfo3.Name == "SetLocalPositionAndRotation" || methodInfo3.Name == "set_localPosition" || methodInfo3.Name == "set_localRotation")) { throw new InvalidOperationException("UpdatePlacementGhost uses unsupported transform writes; assistance was disabled."); } if (CodeInstructionExtensions.Calls(item, methodInfo)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(HammerAssistance), "SetPosition", (Type[])null, (Type[])null); num++; } else if (CodeInstructionExtensions.Calls(item, methodInfo2)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(HammerAssistance), "SetRotation", (Type[])null, (Type[])null); num2++; } } if (num == 0 || num2 == 0) { throw new InvalidOperationException("UpdatePlacementGhost no longer sets position and rotation as expected; assistance was disabled."); } return list; } private static void SetPosition(Transform transform, Vector3 position) { //IL_0024: 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) transform.position = ((ghostTarget != null && (Object)(object)transform == (Object)(object)ghostTarget.Player.m_placementGhost.transform) ? ghostTarget.Position : position); } private static void SetRotation(Transform transform, Quaternion rotation) { //IL_0024: 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) transform.rotation = ((ghostTarget != null && (Object)(object)transform == (Object)(object)ghostTarget.Player.m_placementGhost.transform) ? ghostTarget.Rotation : rotation); } public void Dispose() { harmony.UnpatchSelf(); projection = null; selection = null; ghostTarget = null; instance = null; } } internal sealed class HammerTarget { private enum Rejection { None, Completed, OtherLayer, NotAHammerPiece, Scaled, OutOfReach } public BlueprintProjection Projection { get; } public BlueprintProjection.ProjectedPiece Planned { get; } public Player Player { get; } public Piece Piece { get; } public Vector3 Position => Projection.PiecePosition(Planned); public Quaternion Rotation => Projection.PieceRotation(Planned); public bool RecipeKnown => Player.m_knownRecipes.Contains(Piece.m_name); private IEnumerable<KeyValuePair<string, int>> ResourceCosts => from resource in Piece.m_resources where Object.op_Implicit((Object)(object)resource.m_resItem) select new KeyValuePair<string, int>(resource.m_resItem.m_itemData.m_shared.m_name, resource.GetAmount(0)); public string RecipeError() { string text = Localization.instance.Localize(Piece.m_name); if (!RecipeKnown) { string[] array = (from resource in (from resource in Piece.m_resources where Object.op_Implicit((Object)(object)resource.m_resItem) && resource.GetAmount(0) > 0 select resource.m_resItem.m_itemData.m_shared.m_name into resource where !Player.m_knownMaterial.Contains(resource) select resource).Distinct() select Localization.instance.Localize(resource)).ToArray(); if (array.Length == 0) { return text + ": recipe locked. Discover its required materials and crafting station first."; } return text + ": recipe locked. Discover " + string.Join(", ", array) + " to unlock it."; } if (!Player.IsPieceAvailable(Piece)) { return text + ": recipe learned, but unavailable in this hammer's build menu."; } return null; } private HammerTarget(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player, Piece piece) { Projection = projection; Planned = planned; Player = player; Piece = piece; } public static HammerTarget FromPiece(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player) { Rejection rejection; return FromPiece(projection, planned, player, out rejection); } private static HammerTarget FromPiece(BlueprintProjection projection, BlueprintProjection.ProjectedPiece planned, Player player, out Rejection rejection) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_0099: 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_00a8: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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) rejection = Rejection.Completed; if (planned.Completed) { return null; } rejection = Rejection.OtherLayer; if (!projection.Layers.Contains(planned.Entry.posY)) { return null; } Piece component = planned.Prefab.GetComponent<Piece>(); rejection = Rejection.NotAHammerPiece; if (!Object.op_Implicit((Object)(object)component) || component.m_repairPiece || component.m_removePiece) { return null; } rejection = Rejection.Scaled; if (Vector3.Distance(planned.Entry.GetScale(), planned.Prefab.transform.localScale) > 0.01f) { return null; } Matrix4x4 val = projection.PieceMatrix(planned); Bounds localBounds = planned.LocalBounds; Matrix4x4 inverse = ((Matrix4x4)(ref val)).inverse; Vector3 val2 = ((Matrix4x4)(ref val)).MultiplyPoint3x4(((Bounds)(ref localBounds)).ClosestPoint(((Matrix4x4)(ref inverse)).MultiplyPoint3x4(((Character)player).m_eye.position))); rejection = Rejection.OutOfReach; if (Vector3.Distance(((Character)player).m_eye.position, val2) >= player.m_maxPlaceDistance + (float)component.m_extraPlacementDistance) { return null; } rejection = Rejection.None; retur