Decompiled source of ValheimFloorPlan v2.1.3

BepInEx\plugins\ValheimFloorPlan\ValheimFloorPlan.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("ValheimFloorPlan")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("A floor plan building tool mod for Valheim")]
[assembly: AssemblyFileVersion("2.1.3.0")]
[assembly: AssemblyInformationalVersion("2.1.3+15c96a2c25d187f4c99ed4f7963decf81ac9e3cb")]
[assembly: AssemblyProduct("ValheimFloorPlan")]
[assembly: AssemblyTitle("ValheimFloorPlan")]
[assembly: AssemblyVersion("2.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimFloorPlan
{
	public enum WallFaceMode
	{
		Default,
		Outer,
		Inner
	}
	public class FloorPlanPiece
	{
		public int Col { get; set; }

		public int Row { get; set; }

		public string Type { get; set; } = "";

		public int Rotation { get; set; }

		public WallFaceMode WallFace { get; set; } = WallFaceMode.Default;
	}
	public class FlexiWallPiece
	{
		public float X1 { get; set; }

		public float Y1 { get; set; }

		public float X2 { get; set; }

		public float Y2 { get; set; }

		public float Mx { get; set; }

		public float My { get; set; }
	}
	public class FloorPlan
	{
		public int Cols { get; set; }

		public int Rows { get; set; }

		public List<FloorPlanPiece> Pieces { get; } = new List<FloorPlanPiece>();

		public List<FlexiWallPiece> FlexiWalls { get; } = new List<FlexiWallPiece>();

		public static FloorPlan Load(string path)
		{
			FloorPlan floorPlan = new FloorPlan();
			string[] array = File.ReadAllLines(path);
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.StartsWith("cols="))
				{
					floorPlan.Cols = int.Parse(text2.Substring(5));
				}
				else if (text2.StartsWith("rows="))
				{
					floorPlan.Rows = int.Parse(text2.Substring(5));
				}
				else if (text2.StartsWith("flexiwall,") || text2.StartsWith("arcwall,"))
				{
					string[] array2 = text2.Split(new char[1] { ',' });
					if (array2.Length >= 7 && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && float.TryParse(array2[4], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) && float.TryParse(array2[5], NumberStyles.Float, CultureInfo.InvariantCulture, out var result5) && float.TryParse(array2[6], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6))
					{
						floorPlan.FlexiWalls.Add(new FlexiWallPiece
						{
							X1 = result,
							Y1 = result2,
							X2 = result3,
							Y2 = result4,
							Mx = result5,
							My = result6
						});
					}
				}
				else
				{
					if (!text2.StartsWith("piece,"))
					{
						continue;
					}
					string[] array3 = text2.Split(new char[1] { ',' });
					if (array3.Length < 4)
					{
						continue;
					}
					int rotation = 0;
					int num = -1;
					if (array3.Length > 4)
					{
						if (int.TryParse(array3[4], out var result7))
						{
							rotation = result7;
							num = ((array3.Length > 5) ? 5 : (-1));
						}
						else
						{
							num = 4;
						}
					}
					floorPlan.Pieces.Add(new FloorPlanPiece
					{
						Col = int.Parse(array3[1]),
						Row = int.Parse(array3[2]),
						Type = array3[3],
						Rotation = rotation,
						WallFace = ((num >= 0) ? ParseWallFace(array3[num]) : WallFaceMode.Default)
					});
				}
			}
			return floorPlan;
		}

		private static WallFaceMode ParseWallFace(string raw)
		{
			string text = (raw ?? string.Empty).Trim().ToLowerInvariant();
			if (text == "outer" || text == "out" || text == "o")
			{
				return WallFaceMode.Outer;
			}
			if (text == "inner" || text == "in" || text == "i")
			{
				return WallFaceMode.Inner;
			}
			return WallFaceMode.Default;
		}
	}
	public class FloorPlanBuilder : MonoBehaviour
	{
		private struct FlexiWallSegment
		{
			public float Lx;

			public float Lz;

			public float YawDeg;

			public FlexiWallSegment(float lx, float lz, float yawDeg)
			{
				Lx = lx;
				Lz = lz;
				YawDeg = yawDeg;
			}
		}

		private sealed class ScaffoldPolePoint
		{
			public readonly float T;

			public readonly Vector2 Local;

			public readonly Vector3 Pos;

			public ScaffoldPolePoint(float t, Vector2 local, Vector3 pos)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_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)
				T = t;
				Local = local;
				Pos = pos;
			}
		}

		private sealed class ScaffoldFurnitureExclusion
		{
			public readonly Vector2 Center;

			public readonly Vector2 Forward;

			public readonly Vector2 Side;

			public readonly float ForwardHalfExtent;

			public readonly float SideHalfExtent;

			public readonly float FrontClearance;

			public ScaffoldFurnitureExclusion(Vector2 center, Vector2 forward, Vector2 side, float forwardHalfExtent, float sideHalfExtent, float frontClearance)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_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)
				Center = center;
				Forward = forward;
				Side = side;
				ForwardHalfExtent = forwardHalfExtent;
				SideHalfExtent = sideHalfExtent;
				FrontClearance = frontClearance;
			}
		}

		private sealed class ScaffoldDoorSpan
		{
			public readonly float Min;

			public readonly float Max;

			public ScaffoldDoorSpan(float min, float max)
			{
				Min = min;
				Max = max;
			}
		}

		private sealed class HearthOpening
		{
			public readonly int MinCol;

			public readonly int MinRow;

			public readonly int MaxColExclusive;

			public readonly int MaxRowExclusive;

			public readonly int SourceLevel;

			public readonly string SourceType;

			public int Width => MaxColExclusive - MinCol;

			public int Height => MaxRowExclusive - MinRow;

			public HearthOpening(int minCol, int minRow, int maxColExclusive, int maxRowExclusive, int sourceLevel = 0, string sourceType = "Opening")
			{
				MinCol = minCol;
				MinRow = minRow;
				MaxColExclusive = maxColExclusive;
				MaxRowExclusive = maxRowExclusive;
				SourceLevel = sourceLevel;
				SourceType = sourceType;
			}
		}

		private const bool TESTING_ONLY = false;

		private const float PLACE_DELAY = 0.05f;

		private const float ORIGIN_MARKER_LIFT = 0.3f;

		private const float ORIGIN_MARKER_HEIGHT = 10f;

		private const float PREVIEW_EDGE_RISK_SAMPLE_INTERVAL = 0.45f;

		private const float PREVIEW_EDGE_RISK_HINT_INTERVAL = 2f;

		private const float PREVIEW_EDGE_RISK_HINT_START_DELAY = 2.5f;

		private const float PREVIEW_STEEP_RELIEF_WARN = 6f;

		private const float PREVIEW_RISK_MARKER_RADIUS = 0.45f;

		private const float PREVIEW_RISK_MARKER_LIFT = 0.18f;

		private static readonly string[] TEST_WORKBENCH_PREFABS = new string[6] { "piece_workbench", "forge", "piece_stonecutter", "piece_artisanstation", "blackforge", "piece_magetable" };

		public const string VFP_TAG = "vfp_build";

		private float _undoConfirmationExpireAt = 0f;

		private int _undoConfirmationPieceCount = 0;

		private int _undoConfirmationTerrainChunks = 0;

		private bool _undoConfirmationKeepLeveledTerrain = false;

		private Coroutine _undoCountdownCoroutine = null;

		private Coroutine _undoRefreshCoroutine = null;

		private GameObject? _undoHighlightGo = null;

		private const float UNDO_REFRESH_RADIUS = 120f;

		private const float UNDO_REFRESH_DURATION = 2.5f;

		private const float UNDO_REFRESH_INTERVAL = 0.25f;

		private const float UNDO_HIGHLIGHT_RING_RADIUS = 1.05f;

		private const float UNDO_HIGHLIGHT_RING_LIFT = 0.25f;

		private const int UNDO_HIGHLIGHT_RING_SEGMENTS = 20;

		private const float UNDO_RADIUS_ADJUST_STEP = 5f;

		private const float UNDO_CONFIRMATION_SECONDS = 5f;

		private const int UNDO_BOUNDARY_CIRCLE_SEGMENTS = 64;

		private const float UNDO_BOUNDARY_CIRCLE_LIFT = 0.3f;

		private float _undoActiveRadius = 15f;

		private Vector3 _undoCenter = Vector3.zero;

		private readonly List<GameObject> _lastPlaced = new List<GameObject>();

		private readonly List<GameObject> _groundFloorScaffoldVerticals = new List<GameObject>();

		private bool _previewActive = false;

		private FloorPlan? _previewPlan = null;

		private GameObject? _previewGo = null;

		private MeshFilter? _previewPadWalls = null;

		private MeshFilter? _previewOuterWalls = null;

		private LineRenderer? _previewOriginMarker = null;

		private float _previewRotationDeg = 0f;

		private Vector3 _previewCenter = Vector3.zero;

		private Vector3 _previewOrigin = Vector3.zero;

		private TerrainLeveler.EdgeRiskLevel _previewEdgeRisk = TerrainLeveler.EdgeRiskLevel.Low;

		private float _previewEdgeRelief = 0f;

		private float _previewEdgeIrregularity = 0f;

		private float _previewEdgeMaxStep = 0f;

		private float _previewRiskNextSampleAt = 0f;

		private float _previewRiskNextHintAt = 0f;

		private float _previewRiskHintsEnabledAt = 0f;

		private bool _previewRiskDirty = true;

		private readonly List<Vector3> _previewRiskHotspots = new List<Vector3>();

		private readonly List<LineRenderer> _previewRiskMarkers = new List<LineRenderer>();

		private readonly List<Vector3> _previewRiskRenderPoints = new List<Vector3>();

		private readonly List<MeshFilter> _previewUpperLevelRings = new List<MeshFilter>();

		private int _previewRiskBottomCount = 0;

		private const float FLEXI_TIGHT_CURVE_RADIUS = 4f;

		private const float FLEXI_TIGHT_CURVE_DENSITY = 0.5f;

		private const float FLEXI_MIN_SEGMENT_ANGLE_RAD = (float)Math.PI / 15f;

		private static float UNDO_RADIUS => ValheimFloorPlanPlugin.UndoRadius;

		public static FloorPlanBuilder Instance { get; private set; } = null;

		public bool CanUndo => _lastPlaced.Count > 0 || TerrainSnapshot.HasSnapshot;

		private void Awake()
		{
			Instance = this;
		}

		public void StartPreview(string path)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			if (_previewActive)
			{
				CancelPreview();
			}
			FloorPlan floorPlan;
			try
			{
				floorPlan = FloorPlan.Load(path);
			}
			catch (Exception ex)
			{
				ValheimFloorPlanPlugin.Log.LogError((object)("Failed to load floor plan: " + ex.Message));
				ValheimFloorPlanPlugin.ShowWrappedMessage((MessageType)2, "ValheimFloorPlan: Could not load plan '" + Path.GetFileName(path) + "' — " + ex.Message);
				return;
			}
			if (ValidateAdditionalLevelPlanFootprints(floorPlan))
			{
				_previewPlan = floorPlan;
				_previewActive = true;
				Player localPlayer = Player.m_localPlayer;
				_previewRotationDeg = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform.eulerAngles.y : (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.eulerAngles.y : 0f));
				_previewRotationDeg = SnapAngleDeg(_previewRotationDeg + 180f);
				_previewCenter = (((Object)(object)localPlayer != (Object)null) ? GetInitialBuildCenter(localPlayer, floorPlan, _previewRotationDeg) : Vector3.zero);
				_previewOrigin = GetPlacementOriginFromCenter(floorPlan, _previewCenter, _previewRotationDeg);
				_previewGo = new GameObject("VFP_Preview");
				_previewPadWalls = MakeWallRing(_previewGo, "VFP_WallsPad", new Color(1f, 1f, 1f, 0.28f));
				_previewOuterWalls = MakeWallRing(_previewGo, "VFP_WallsOuter", new Color(0.2f, 1f, 0.2f, 0.24f));
				_previewOriginMarker = MakeLine(_previewGo, new Color(1f, 0.9f, 0f, 0.98f), 0.2f, 2);
				_previewUpperLevelRings.Clear();
				if (ValheimFloorPlanPlugin.FloorPlanLevels >= 2)
				{
					_previewUpperLevelRings.Add(MakeWallRing(_previewGo, "VFP_WallsLevel2", new Color(1f, 0.25f, 0.25f, 0.32f)));
				}
				if (ValheimFloorPlanPlugin.FloorPlanLevels >= 3)
				{
					_previewUpperLevelRings.Add(MakeWallRing(_previewGo, "VFP_WallsLevel3", new Color(1f, 0.25f, 0.25f, 0.32f)));
				}
				_previewEdgeRisk = TerrainLeveler.EdgeRiskLevel.Low;
				_previewEdgeRelief = 0f;
				_previewEdgeIrregularity = 0f;
				_previewEdgeMaxStep = 0f;
				_previewRiskDirty = true;
				_previewRiskNextSampleAt = 0f;
				_previewRiskNextHintAt = Time.time + 2.5f;
				_previewRiskHintsEnabledAt = Time.time + 2.5f;
				ValheimFloorPlanPlugin.Log.LogInfo((object)($"[FloorPlanBuilder] Preview active ({floorPlan.Pieces.Count} pieces, " + $"{floorPlan.Cols}×{floorPlan.Rows} cells). {ValheimFloorPlanPlugin.PreviewConfirmKey} to build, RMB/ESC to cancel."));
				ValheimFloorPlanPlugin.ShowWrappedMessage((MessageType)2, $"ValheimFloorPlan: {ValheimFloorPlanPlugin.PreviewMoveLeftKey}/{ValheimFloorPlanPlugin.PreviewMoveRightKey}/{ValheimFloorPlanPlugin.PreviewMoveForwardKey}/{ValheimFloorPlanPlugin.PreviewMoveBackwardKey} move | {ValheimFloorPlanPlugin.PreviewRotateLeftKey}/{ValheimFloorPlanPlugin.PreviewRotateRightKey} rotate | {ValheimFloorPlanPlugin.PreviewFineAdjustKey} fine | {ValheimFloorPlanPlugin.PreviewConfirmKey} to place | RMB/{ValheimFloorPlanPlugin.PreviewCancelKey} cancel");
			}
		}

		private bool ValidateAdditionalLevelPlanFootprints(FloorPlan level1Plan)
		{
			GetPlanPieceBounds(level1Plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			if (!ValidateSingleAdditionalLevelPlan("FloorPlanFileLevel2", ValheimFloorPlanPlugin.FloorPlanFileLevel2, minCol, maxColExclusive, minRow, maxRowExclusive))
			{
				return false;
			}
			if (!ValidateSingleAdditionalLevelPlan("FloorPlanFileLevel3", ValheimFloorPlanPlugin.FloorPlanFileLevel3, minCol, maxColExclusive, minRow, maxRowExclusive))
			{
				return false;
			}
			return true;
		}

		private bool ValidateSingleAdditionalLevelPlan(string configKey, string path, int l1MinCol, int l1MaxColExclusive, int l1MinRow, int l1MaxRowExclusive)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			string text = (path ?? string.Empty).Trim();
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			FloorPlan plan;
			try
			{
				plan = FloorPlan.Load(text);
			}
			catch (Exception ex)
			{
				ValheimFloorPlanPlugin.Log.LogError((object)("[" + configKey + "] Failed to load '" + text + "': " + ex.Message));
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: " + configKey + " could not be loaded. " + Path.GetFileName(text) + " (" + ex.Message + ")");
				return false;
			}
			GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			if (minCol < l1MinCol || maxColExclusive > l1MaxColExclusive || minRow < l1MinRow || maxRowExclusive > l1MaxRowExclusive)
			{
				ValheimFloorPlanPlugin.Log.LogWarning((object)("[" + configKey + "] Footprint must fit inside Level 1 footprint. " + $"Level1=[col {l1MinCol}..{l1MaxColExclusive}, row {l1MinRow}..{l1MaxRowExclusive}] " + $"Candidate=[col {minCol}..{maxColExclusive}, row {minRow}..{maxRowExclusive}] " + "Path='" + text + "'"));
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: " + configKey + " footprint must fit inside Level 1 plan. Smaller offset layouts are allowed, but they cannot extend beyond Level 1 bounds.");
				return false;
			}
			ValheimFloorPlanPlugin.Log.LogInfo((object)("[" + configKey + "] Footprint validated inside Level 1. " + $"Candidate=[col {minCol}..{maxColExclusive}, row {minRow}..{maxRowExclusive}] " + "Path='" + text + "'"));
			return true;
		}

		private static MeshFilter MakeWallRing(GameObject parent, string name, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent.transform, false);
			MeshFilter val2 = val.AddComponent<MeshFilter>();
			MeshRenderer val3 = val.AddComponent<MeshRenderer>();
			((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val3).receiveShadows = false;
			Material val4 = new Material(Shader.Find("Sprites/Default"));
			val4.color = color;
			((Renderer)val3).sharedMaterial = val4;
			Mesh val5 = new Mesh
			{
				name = name + "_Mesh"
			};
			val5.vertices = (Vector3[])(object)new Vector3[16];
			val5.uv = (Vector2[])(object)new Vector2[16];
			for (int i = 0; i < 16; i++)
			{
				int num = i % 4;
				Vector2[] uv = val5.uv;
				int num2 = i;
				if (1 == 0)
				{
				}
				Vector2 val6 = (Vector2)(num switch
				{
					0 => new Vector2(0f, 0f), 
					1 => new Vector2(1f, 0f), 
					2 => new Vector2(1f, 1f), 
					_ => new Vector2(0f, 1f), 
				});
				if (1 == 0)
				{
				}
				uv[num2] = val6;
			}
			val5.triangles = new int[48]
			{
				0, 1, 2, 0, 2, 3, 2, 1, 0, 3,
				2, 0, 4, 5, 6, 4, 6, 7, 6, 5,
				4, 7, 6, 4, 8, 9, 10, 8, 10, 11,
				10, 9, 8, 11, 10, 8, 12, 13, 14, 12,
				14, 15, 14, 13, 12, 15, 14, 12
			};
			val5.RecalculateNormals();
			val2.sharedMesh = val5;
			return val2;
		}

		private static LineRenderer MakeLine(GameObject parent, Color color, float width, int positionCount = 5)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("VFP_Line");
			val.transform.SetParent(parent.transform, false);
			LineRenderer val2 = val.AddComponent<LineRenderer>();
			val2.useWorldSpace = true;
			val2.loop = false;
			val2.positionCount = positionCount;
			val2.widthMultiplier = width;
			((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val2).receiveShadows = false;
			((Renderer)val2).sharedMaterial = new Material(Shader.Find("Sprites/Default"));
			val2.startColor = color;
			val2.endColor = color;
			return val2;
		}

		private void CancelPreview()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			_previewActive = false;
			_previewPlan = null;
			_previewPadWalls = null;
			_previewOuterWalls = null;
			_previewOriginMarker = null;
			_previewRotationDeg = 0f;
			_previewCenter = Vector3.zero;
			_previewOrigin = Vector3.zero;
			_previewEdgeRisk = TerrainLeveler.EdgeRiskLevel.Low;
			_previewEdgeRelief = 0f;
			_previewEdgeIrregularity = 0f;
			_previewEdgeMaxStep = 0f;
			_previewRiskDirty = true;
			_previewRiskNextSampleAt = 0f;
			_previewRiskNextHintAt = 0f;
			_previewRiskHintsEnabledAt = 0f;
			_previewRiskHotspots.Clear();
			_previewRiskRenderPoints.Clear();
			_previewRiskMarkers.Clear();
			_previewUpperLevelRings.Clear();
			if ((Object)(object)_previewGo != (Object)null)
			{
				Object.Destroy((Object)(object)_previewGo);
				_previewGo = null;
			}
		}

		private void Update()
		{
			if (_previewActive && _previewPlan != null)
			{
				UpdatePreviewMode();
			}
			if (_undoConfirmationExpireAt > Time.time)
			{
				UpdateUndoConfirmationInput();
			}
		}

		public void ToggleTearRepairMode()
		{
		}

		public void ToggleTerrainClipMode()
		{
		}

		private void UpdatePreviewMode()
		{
			//IL_0037: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: 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_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_0450: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
			if (_previewPlan == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				CancelPreview();
				return;
			}
			UpdatePreviewPosition(_previewOrigin, _previewCenter);
			bool previewChanged = false;
			bool flag = IsFineAdjustHeld();
			float num = (flag ? ValheimFloorPlanPlugin.PreviewFineRotateStepDeg : ValheimFloorPlanPlugin.PreviewRotateStepDeg);
			float num2 = (flag ? ValheimFloorPlanPlugin.PreviewFineMoveStep : ValheimFloorPlanPlugin.PreviewMoveStep);
			if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewRotateLeftKey))
			{
				_previewRotationDeg = (_previewRotationDeg - num + 360f) % 360f;
				if (!flag)
				{
					_previewRotationDeg = SnapAngleDeg(_previewRotationDeg);
				}
				_previewOrigin = GetPlacementOriginFromCenter(_previewPlan, _previewCenter, _previewRotationDeg);
				previewChanged = true;
				((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Rotation {_previewRotationDeg:F1}°", 0, (Sprite)null);
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewRotateRightKey))
			{
				_previewRotationDeg = (_previewRotationDeg + num) % 360f;
				if (!flag)
				{
					_previewRotationDeg = SnapAngleDeg(_previewRotationDeg);
				}
				_previewOrigin = GetPlacementOriginFromCenter(_previewPlan, _previewCenter, _previewRotationDeg);
				previewChanged = true;
				((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Rotation {_previewRotationDeg:F1}°", 0, (Sprite)null);
			}
			Vector3 forward = Vector3.forward;
			Vector3 right = Vector3.right;
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				forward = ((Component)main).transform.forward;
				forward.y = 0f;
				if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f)
				{
					((Vector3)(ref forward)).Normalize();
					((Vector3)(ref right))..ctor(forward.z, 0f, 0f - forward.x);
				}
				else
				{
					forward = Vector3.forward;
					right = Vector3.right;
				}
			}
			Vector3 val = Vector3.zero;
			if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveForwardKey))
			{
				val = forward * num2;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveBackwardKey))
			{
				val = -forward * num2;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveRightKey))
			{
				val = right * num2;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveLeftKey))
			{
				val = -right * num2;
			}
			if (val != Vector3.zero)
			{
				_previewCenter += val;
				_previewOrigin = GetPlacementOriginFromCenter(_previewPlan, _previewCenter, _previewRotationDeg);
				previewChanged = true;
				((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Center ({_previewCenter.x:F1}, {_previewCenter.z:F1})", 0, (Sprite)null);
			}
			UpdatePreviewEdgeRisk(localPlayer, previewChanged);
			if (Input.GetMouseButtonDown(1) || IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewCancelKey))
			{
				CancelPreview();
				((Character)localPlayer).Message((MessageType)2, "ValheimFloorPlan: Build cancelled.", 0, (Sprite)null);
				return;
			}
			bool flag2 = (Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus();
			if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewConfirmKey) && !flag2)
			{
				FloorPlan previewPlan = _previewPlan;
				float previewRotationDeg = _previewRotationDeg;
				Vector3 previewOrigin = _previewOrigin;
				Vector3 previewCenter = _previewCenter;
				TerrainLeveler.EdgeRiskLevel previewEdgeRisk = _previewEdgeRisk;
				float previewEdgeRelief = _previewEdgeRelief;
				float previewEdgeMaxStep = _previewEdgeMaxStep;
				float previewEdgeIrregularity = _previewEdgeIrregularity;
				bool flag3 = previewEdgeRelief >= 6f;
				if (previewEdgeRisk == TerrainLeveler.EdgeRiskLevel.High || flag3)
				{
					ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: Final warning before build. " + $"Edge risk={previewEdgeRisk}, relief={previewEdgeRelief:F1}m, step={previewEdgeMaxStep:F2}m. " + "Terracing or downhill tears may occur.");
				}
				CancelPreview();
				ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Build confirmed by key {ValheimFloorPlanPlugin.PreviewConfirmKey}. Rotation={previewRotationDeg:F0}°  center={previewCenter}  origin={previewOrigin}  edgeRisk={previewEdgeRisk}  edgeRelief={previewEdgeRelief:F2}  irregularity={previewEdgeIrregularity:F2}  maxEdgeStep={previewEdgeMaxStep:F2}");
				((MonoBehaviour)this).StartCoroutine(LevelThenPlace(previewPlan, previewRotationDeg, previewOrigin));
			}
		}

		private void UpdatePreviewEdgeRisk(Player player, bool previewChanged)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			if (_previewPlan == null)
			{
				return;
			}
			if (previewChanged)
			{
				_previewRiskDirty = true;
			}
			if (!_previewRiskDirty && Time.time < _previewRiskNextSampleAt)
			{
				return;
			}
			TerrainLeveler.EdgeRiskLevel previewEdgeRisk = _previewEdgeRisk;
			_previewEdgeRisk = TerrainLeveler.EvaluateEdgeRisk(_previewPlan, _previewOrigin, _previewRotationDeg, out _previewEdgeRelief, out _previewEdgeIrregularity, out _previewEdgeMaxStep, _previewRiskHotspots);
			_previewRiskBottomCount = BuildPreviewRiskRenderPoints(_previewRiskHotspots, _previewRiskRenderPoints);
			UpdatePreviewRiskMarkers(_previewEdgeRisk, _previewRiskRenderPoints, _previewRiskBottomCount);
			_previewRiskDirty = false;
			_previewRiskNextSampleAt = Time.time + 0.45f;
			bool flag = _previewEdgeRisk != TerrainLeveler.EdgeRiskLevel.Low;
			if ((!(Time.time < _previewRiskHintsEnabledAt) || flag) && (previewChanged || _previewEdgeRisk != previewEdgeRisk || Time.time >= _previewRiskNextHintAt))
			{
				if (_previewEdgeRisk == TerrainLeveler.EdgeRiskLevel.High || _previewEdgeRisk == TerrainLeveler.EdgeRiskLevel.Medium)
				{
					string text = ((_previewEdgeRisk == TerrainLeveler.EdgeRiskLevel.High) ? $"Edge risk HIGH: uneven boundary terrain may cause tears/spikes. Try nudging or rotating before build. step={_previewEdgeMaxStep:F2}m, relief={_previewEdgeRelief:F1}m" : $"Edge risk MEDIUM: some boundary irregularity detected. Small origin/rotation adjustments may improve results. step={_previewEdgeMaxStep:F2}m, relief={_previewEdgeRelief:F1}m");
					ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: " + text);
				}
				_previewRiskNextHintAt = Time.time + 2f;
			}
		}

		private int BuildPreviewRiskRenderPoints(List<Vector3> hotspots, List<Vector3> output)
		{
			//IL_0054: 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_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			output.Clear();
			if (_previewPlan == null)
			{
				return 0;
			}
			float num = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainHighPointDelta, 0f, 4f);
			if (hotspots.Count == 0)
			{
				return 0;
			}
			for (int i = 0; i < hotspots.Count; i++)
			{
				output.Add(hotspots[i]);
			}
			int count = output.Count;
			TerrainLeveler.GetLeveledAreaBounds(_previewPlan, _previewOrigin, out var minX, out var maxX, out var minZ, out var maxZ);
			float num2 = _previewRotationDeg * ((float)Math.PI / 180f);
			float num3 = Mathf.Cos(num2);
			float num4 = Mathf.Sin(num2);
			float[] array = new float[4] { minX, maxX, maxX, minX };
			float[] array2 = new float[4] { minZ, minZ, maxZ, maxZ };
			float num5 = float.MinValue;
			float y = _previewOrigin.y;
			RaycastHit val = default(RaycastHit);
			for (int j = 0; j < 4; j++)
			{
				float num6 = array[j] - _previewOrigin.x;
				float num7 = array2[j] - _previewOrigin.z;
				float num8 = _previewOrigin.x + num6 * num3 + num7 * num4;
				float num9 = _previewOrigin.z - num6 * num4 + num7 * num3;
				if (Physics.Raycast(new Vector3(num8, y + 300f, num9), Vector3.down, ref val, 600f, 2048) && ((RaycastHit)(ref val)).point.y > num5)
				{
					num5 = ((RaycastHit)(ref val)).point.y;
				}
			}
			float num10 = ((num5 == float.MinValue) ? y : num5) + 0.3f + num;
			float num11 = maxZ - _previewOrigin.z;
			float[] array3 = new float[3] { 0.25f, 0.5f, 0.75f };
			for (int k = 0; k < array3.Length; k++)
			{
				float num12 = Mathf.Lerp(minX, maxX, array3[k]);
				float num13 = num12 - _previewOrigin.x;
				float num14 = _previewOrigin.x + num13 * num3 + num11 * num4;
				float num15 = _previewOrigin.z - num13 * num4 + num11 * num3;
				output.Add(new Vector3(num14, num10, num15));
			}
			return count;
		}

		private void UpdatePreviewRiskMarkers(TerrainLeveler.EdgeRiskLevel risk, List<Vector3> hotspots, int bottomCount)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_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_0111: 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_00bc: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			int num = ((risk != TerrainLeveler.EdgeRiskLevel.Low) ? Mathf.Min(hotspots.Count, 24) : 0);
			EnsureRiskMarkerCount(num);
			RaycastHit val3 = default(RaycastHit);
			Vector3 val4 = default(Vector3);
			for (int i = 0; i < _previewRiskMarkers.Count; i++)
			{
				LineRenderer val = _previewRiskMarkers[i];
				if (i >= num)
				{
					((Renderer)val).enabled = false;
					continue;
				}
				((Renderer)val).enabled = true;
				val.startColor = ((risk == TerrainLeveler.EdgeRiskLevel.High) ? new Color(1f, 0.22f, 0.12f, 0.95f) : new Color(1f, 0.72f, 0.18f, 0.92f));
				val.endColor = val.startColor;
				Vector3 val2 = hotspots[i];
				float y;
				if (i < bottomCount)
				{
					y = val2.y;
					if (Physics.Raycast(new Vector3(val2.x, val2.y + 300f, val2.z), Vector3.down, ref val3, 600f, 2048))
					{
						y = ((RaycastHit)(ref val3)).point.y;
					}
					y += 0.18f;
				}
				else
				{
					y = val2.y;
				}
				((Vector3)(ref val4))..ctor(val2.x, y, val2.z);
				float num2 = 0.45f;
				val.positionCount = 5;
				val.SetPosition(0, val4 + new Vector3(0f - num2, 0f, 0f));
				val.SetPosition(1, val4 + new Vector3(0f, 0f, num2));
				val.SetPosition(2, val4 + new Vector3(num2, 0f, 0f));
				val.SetPosition(3, val4 + new Vector3(0f, 0f, 0f - num2));
				val.SetPosition(4, val4 + new Vector3(0f - num2, 0f, 0f));
			}
		}

		private void EnsureRiskMarkerCount(int count)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_previewGo == (Object)null))
			{
				while (_previewRiskMarkers.Count < count)
				{
					LineRenderer val = MakeLine(_previewGo, new Color(1f, 0.72f, 0.18f, 0.92f), 0.06f);
					val.loop = false;
					_previewRiskMarkers.Add(val);
				}
			}
		}

		private static Vector3 GetInitialBuildCenter(Player player, FloorPlan? plan, float rotationDeg)
		{
			//IL_0007: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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)
			Vector3 position = ((Component)player).transform.position;
			Vector3 buildForward = GetBuildForward(player);
			if (((Vector3)(ref buildForward)).sqrMagnitude < 0.0001f)
			{
				return position;
			}
			float forwardHalfExtent = GetForwardHalfExtent(plan, rotationDeg, buildForward);
			float num = Mathf.Max(0f, ValheimFloorPlanPlugin.BuildOriginForwardOffset);
			return position + buildForward * (forwardHalfExtent + num);
		}

		private static Vector3 GetBuildForward(Player player)
		{
			//IL_0025: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Vector3 result = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform.forward : ((Component)player).transform.forward);
			result.y = 0f;
			if (((Vector3)(ref result)).sqrMagnitude >= 0.0001f)
			{
				((Vector3)(ref result)).Normalize();
			}
			return result;
		}

		private static float GetForwardHalfExtent(FloorPlan? plan, float rotationDeg, Vector3 worldForward)
		{
			//IL_00d9: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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)
			if (plan == null)
			{
				return 0f;
			}
			GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			float outerPerimeterDelta = TerrainLeveler.GetOuterPerimeterDelta();
			float num = (float)minCol * 1f - outerPerimeterDelta;
			float num2 = (float)maxColExclusive * 1f + outerPerimeterDelta;
			float num3 = (float)minRow * 1f - outerPerimeterDelta;
			float num4 = (float)maxRowExclusive * 1f + outerPerimeterDelta;
			float num5 = (num + num2) * 0.5f;
			float num6 = (num3 + num4) * 0.5f;
			float num7 = 0f;
			float[] array = new float[4] { num, num2, num2, num };
			float[] array2 = new float[4] { num3, num3, num4, num4 };
			for (int i = 0; i < 4; i++)
			{
				float localX = array[i] - num5;
				float localZ = array2[i] - num6;
				Vector2 val = PieceMap.TransformLocalXZ(localX, localZ, rotationDeg);
				float num8 = val.x * worldForward.x + val.y * worldForward.z;
				if (num8 > num7)
				{
					num7 = num8;
				}
			}
			return Mathf.Max(0f, num7);
		}

		private static bool IsPreviewKeyDown(KeyCode key)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			return (int)key != 0 && Input.GetKeyDown(key);
		}

		private static bool IsFineAdjustHeld()
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			//IL_003a: 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)
			KeyCode previewFineAdjustKey = ValheimFloorPlanPlugin.PreviewFineAdjustKey;
			if ((int)previewFineAdjustKey == 304 || (int)previewFineAdjustKey == 303)
			{
				return Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			}
			return (int)previewFineAdjustKey != 0 && Input.GetKey(previewFineAdjustKey);
		}

		private void UpdatePreviewPosition(Vector3 origin, Vector3 center)
		{
			//IL_0030: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			if (_previewPlan == null)
			{
				return;
			}
			float previewRaiseDelta = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainHighPointDelta, 0f, 4f);
			TerrainLeveler.GetPadBounds(_previewPlan, origin, out var minX, out var maxX, out var minZ, out var maxZ, _previewRotationDeg);
			TerrainLeveler.GetLeveledAreaBounds(_previewPlan, origin, out var minX2, out var maxX2, out var minZ2, out var maxZ2, _previewRotationDeg);
			SetWallRingRectangle(_previewPadWalls, origin.y, (Vector2[])(object)new Vector2[4]
			{
				new Vector2(minX, minZ),
				new Vector2(maxX, minZ),
				new Vector2(maxX, maxZ),
				new Vector2(minX, maxZ)
			}, previewRaiseDelta);
			SetWallRingRectangle(_previewOuterWalls, origin.y, (Vector2[])(object)new Vector2[4]
			{
				new Vector2(minX2, minZ2),
				new Vector2(maxX2, minZ2),
				new Vector2(maxX2, maxZ2),
				new Vector2(minX2, maxZ2)
			}, previewRaiseDelta);
			SetOriginMarker(_previewOriginMarker, center.y, center);
			if (_previewUpperLevelRings.Count > 0)
			{
				Vector2[] corners = (Vector2[])(object)new Vector2[4]
				{
					new Vector2(minX, minZ),
					new Vector2(maxX, minZ),
					new Vector2(maxX, maxZ),
					new Vector2(minX, maxZ)
				};
				float y = origin.y;
				RaycastHit val = default(RaycastHit);
				if (Physics.Raycast(new Vector3(origin.x, origin.y + 300f, origin.z), Vector3.down, ref val, 600f, 2048))
				{
					y = ((RaycastHit)(ref val)).point.y;
				}
				if (_previewUpperLevelRings.Count >= 1)
				{
					SetWallRingAtHeight(_previewUpperLevelRings[0], y + (float)ValheimFloorPlanPlugin.ScaffoldingFloorHeight, corners);
				}
				if (_previewUpperLevelRings.Count >= 2)
				{
					SetWallRingAtHeight(_previewUpperLevelRings[1], y + (float)ValheimFloorPlanPlugin.ScaffoldingFloorHeight + (float)ValheimFloorPlanPlugin.ScaffoldingFloorHeight2, corners);
				}
			}
		}

		private static Vector3 GetPlacementOriginFromCenter(FloorPlan? plan, Vector3 center, float rotationDeg)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_005d: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (plan == null)
			{
				return center;
			}
			GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			float localX = (float)(minCol + maxColExclusive) * 0.5f * 1f;
			float localZ = (float)(minRow + maxRowExclusive) * 0.5f * 1f;
			Vector2 val = PieceMap.TransformLocalXZ(localX, localZ, rotationDeg);
			return new Vector3(center.x - val.x, center.y, center.z - val.y);
		}

		private static Vector2[] RotateBoundsCorners(Vector3 origin, float minX, float maxX, float minZ, float maxZ, float rotDeg)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_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)
			//IL_00ad: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			Vector2[] array = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(minX, minZ),
				new Vector2(maxX, minZ),
				new Vector2(maxX, maxZ),
				new Vector2(minX, maxZ)
			};
			if (Mathf.Approximately(rotDeg % 360f, 0f))
			{
				return array;
			}
			float x = origin.x;
			float z = origin.z;
			for (int i = 0; i < 4; i++)
			{
				float localX = array[i].x - x;
				float localZ = array[i].y - z;
				Vector2 val = PieceMap.TransformLocalXZ(localX, localZ, rotDeg);
				array[i] = new Vector2(x + val.x, z + val.y);
			}
			return array;
		}

		private static void SetWallRingRectangle(MeshFilter? mf, float referenceY, Vector2[] corners, float previewRaiseDelta)
		{
			//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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mf == (Object)null || (Object)(object)mf.sharedMesh == (Object)null)
			{
				return;
			}
			float num = referenceY + 300f;
			float[] array = new float[4];
			float num2 = float.MaxValue;
			float num3 = float.MinValue;
			RaycastHit val = default(RaycastHit);
			for (int i = 0; i < 4; i++)
			{
				float num4 = referenceY;
				if (Physics.Raycast(new Vector3(corners[i].x, num, corners[i].y), Vector3.down, ref val, 600f, 2048))
				{
					num4 = ((RaycastHit)(ref val)).point.y;
				}
				array[i] = num4;
				if (num4 < num2)
				{
					num2 = num4;
				}
				if (num4 > num3)
				{
					num3 = num4;
				}
			}
			float num5 = num2 + 0.06f;
			float num6 = num3 + 0.3f + Mathf.Max(0f, previewRaiseDelta);
			if (num6 - num5 < 0.75f)
			{
				num6 = num5 + 0.75f;
			}
			Mesh sharedMesh = mf.sharedMesh;
			Vector3[] vertices = sharedMesh.vertices;
			for (int j = 0; j < 4; j++)
			{
				int num7 = (j + 1) % 4;
				int num8 = j * 4;
				vertices[num8] = new Vector3(corners[j].x, num5, corners[j].y);
				vertices[num8 + 1] = new Vector3(corners[num7].x, num5, corners[num7].y);
				vertices[num8 + 2] = new Vector3(corners[num7].x, num6, corners[num7].y);
				vertices[num8 + 3] = new Vector3(corners[j].x, num6, corners[j].y);
			}
			sharedMesh.vertices = vertices;
			sharedMesh.RecalculateBounds();
			sharedMesh.RecalculateNormals();
		}

		private static void SetWallRingAtHeight(MeshFilter? mf, float floorY, Vector2[] corners, float ringHeight = 0.45f)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)mf == (Object)null) && !((Object)(object)mf.sharedMesh == (Object)null))
			{
				float num = floorY - 0.05f;
				float num2 = floorY + ringHeight;
				Mesh sharedMesh = mf.sharedMesh;
				Vector3[] vertices = sharedMesh.vertices;
				for (int i = 0; i < 4; i++)
				{
					int num3 = (i + 1) % 4;
					int num4 = i * 4;
					vertices[num4] = new Vector3(corners[i].x, num, corners[i].y);
					vertices[num4 + 1] = new Vector3(corners[num3].x, num, corners[num3].y);
					vertices[num4 + 2] = new Vector3(corners[num3].x, num2, corners[num3].y);
					vertices[num4 + 3] = new Vector3(corners[i].x, num2, corners[i].y);
				}
				sharedMesh.vertices = vertices;
				sharedMesh.RecalculateBounds();
				sharedMesh.RecalculateNormals();
			}
		}

		private static void SetOriginMarker(LineRenderer? lr, float referenceY, Vector3 origin)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)lr == (Object)null))
			{
				float num = referenceY;
				float num2 = referenceY + 300f;
				RaycastHit val = default(RaycastHit);
				if (Physics.Raycast(new Vector3(origin.x, num2, origin.z), Vector3.down, ref val, 600f, 2048))
				{
					num = ((RaycastHit)(ref val)).point.y;
				}
				lr.positionCount = 2;
				lr.SetPosition(0, new Vector3(origin.x, num + 0.3f, origin.z));
				lr.SetPosition(1, new Vector3(origin.x, num + 0.3f + 10f, origin.z));
			}
		}

		private static void SetLinePositions(LineRenderer? lr, Vector3 from, Vector3 to)
		{
			//IL_0018: 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)
			if (!((Object)(object)lr == (Object)null))
			{
				lr.positionCount = 2;
				lr.SetPosition(0, from);
				lr.SetPosition(1, to);
			}
		}

		public void Undo(bool keepLeveledTerrain = false)
		{
			//IL_0098: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				ValheimFloorPlanPlugin.Log.LogWarning((object)"[FloorPlanBuilder] No local player for Undo.");
				return;
			}
			if (_undoConfirmationExpireAt > Time.time)
			{
				_undoConfirmationExpireAt = 0f;
				if (_undoCountdownCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine);
				}
				_undoCountdownCoroutine = null;
				PerformUndo(localPlayer, _undoConfirmationKeepLeveledTerrain);
				_undoConfirmationKeepLeveledTerrain = false;
				return;
			}
			_undoActiveRadius = UNDO_RADIUS;
			_undoCenter = ((Component)localPlayer).transform.position;
			CountUndoStats(localPlayer, _undoActiveRadius, _undoCenter, out var pieceCount, out var terrainChunkCount);
			if (pieceCount == 0 && terrainChunkCount == 0)
			{
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, "ValheimFloorPlan: Nothing to undo.");
				return;
			}
			_undoConfirmationPieceCount = pieceCount;
			_undoConfirmationTerrainChunks = terrainChunkCount;
			_undoConfirmationKeepLeveledTerrain = keepLeveledTerrain;
			_undoConfirmationExpireAt = Time.time + 5f;
			if (_undoCountdownCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine);
			}
			_undoCountdownCoroutine = ((MonoBehaviour)this).StartCoroutine(UndoCountdownCoroutine());
			ShowUndoHighlights(localPlayer);
			ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, BuildUndoConfirmationMessage(5));
			ValheimFloorPlanPlugin.Log.LogInfo((object)(keepLeveledTerrain ? $"[FloorPlanBuilder] Undo confirmation pending (keep terrain): {pieceCount} pieces, snapshot chunks={terrainChunkCount}." : $"[FloorPlanBuilder] Undo confirmation pending: {pieceCount} pieces, {terrainChunkCount} terrain chunks."));
		}

		private string BuildUndoConfirmationMessage(int secondsLeft)
		{
			string text = $"ValheimFloorPlan: Confirm Undo? Will remove {_undoConfirmationPieceCount} piece(s)";
			if (_undoConfirmationKeepLeveledTerrain)
			{
				text = ((_undoConfirmationTerrainChunks <= 0) ? (text + " and keep leveled terrain") : (text + $" and keep leveled terrain (discard {_undoConfirmationTerrainChunks} snapshot chunk(s))"));
			}
			else if (_undoConfirmationTerrainChunks > 0)
			{
				text += $" and restore {_undoConfirmationTerrainChunks} terrain chunk(s)";
			}
			text += $" within {_undoActiveRadius:F0}m horizontal radius (+/- to adjust)";
			return text + $". Arrow keys to move circle center | Press Undo key again ({secondsLeft}s remaining) to confirm, or RMB/Esc to cancel.";
		}

		private void CountUndoStats(Player player, float radius, Vector3 center, out int pieceCount, out int terrainChunkCount)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			pieceCount = 0;
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					ZDO zDO = val.GetZDO();
					if (zDO != null && !(zDO.GetString("vfp_build", "") != "1") && IsWithinHorizontalRadius(((Component)val).transform.position, center, radius))
					{
						pieceCount++;
					}
				}
			}
			terrainChunkCount = TerrainSnapshot.GetSnapshotChunkCount();
		}

		private void PerformUndo(Player player, bool keepLeveledTerrain)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			ClearUndoHighlights();
			int num = 0;
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					ZDO zDO = val.GetZDO();
					if (zDO != null && !(zDO.GetString("vfp_build", "") != "1") && IsWithinHorizontalRadius(((Component)val).transform.position, _undoCenter, _undoActiveRadius))
					{
						ZNetScene.instance.Destroy(((Component)val).gameObject);
						num++;
					}
				}
			}
			_lastPlaced.Clear();
			bool hasSnapshot = TerrainSnapshot.HasSnapshot;
			int snapshotChunkCount = TerrainSnapshot.GetSnapshotChunkCount();
			int num2 = 0;
			int num3 = 0;
			if (keepLeveledTerrain)
			{
				if (hasSnapshot)
				{
					num3 = snapshotChunkCount;
					TerrainSnapshot.Clear();
				}
			}
			else
			{
				num2 = snapshotChunkCount;
				TerrainSnapshot.Restore();
				if (hasSnapshot && num2 > 0)
				{
					if (_undoRefreshCoroutine != null)
					{
						((MonoBehaviour)this).StopCoroutine(_undoRefreshCoroutine);
					}
					_undoRefreshCoroutine = ((MonoBehaviour)this).StartCoroutine(PostUndoTerrainRefresh(_undoCenter, num2));
				}
				if (!hasSnapshot)
				{
					ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: No terrain snapshot in this session. Undo removed pieces only.");
				}
			}
			if (keepLeveledTerrain)
			{
				ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Undo(keep terrain): removed {num} VFP pieces within {_undoActiveRadius:F0}m from center {_undoCenter}, discarded {num3} terrain snapshot chunks.");
				((Character)player).Message((MessageType)2, string.Format("ValheimFloorPlan: Undone ({0} pieces removed, leveled terrain kept{1}).", num, (num3 > 0) ? $", {num3} snapshot chunks discarded" : ""), 0, (Sprite)null);
			}
			else
			{
				ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Undo: removed {num} VFP pieces within {_undoActiveRadius:F0}m from center {_undoCenter}, restored {num2} terrain chunks.");
				((Character)player).Message((MessageType)2, $"ValheimFloorPlan: Undone ({num} pieces removed, {num2} terrain chunks restored).", 0, (Sprite)null);
			}
		}

		private IEnumerator PostUndoTerrainRefresh(Vector3 center, int restoredChunks)
		{
			//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)
			float elapsed = 0f;
			int passes = 0;
			int touched = 0;
			for (; elapsed < 2.5f; elapsed += 0.25f)
			{
				Heightmap[] hmaps = Object.FindObjectsOfType<Heightmap>() ?? Array.Empty<Heightmap>();
				int passTouched = 0;
				Heightmap[] array = hmaps;
				foreach (Heightmap hmap in array)
				{
					if (!((Object)(object)hmap == (Object)null) && !(Vector3.Distance(((Component)hmap).transform.position, center) > 120f))
					{
						hmap.Poke(false);
						passTouched++;
					}
				}
				passes++;
				touched = passTouched;
				yield return (object)new WaitForSeconds(0.25f);
			}
			ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Post-undo refresh complete: {passes} passes, {touched} nearby heightmaps touched, restoredChunks={restoredChunks}.");
			_undoRefreshCoroutine = null;
		}

		private IEnumerator UndoCountdownCoroutine()
		{
			float nextUpdateAt = Time.time + 1f;
			while (_undoConfirmationExpireAt > Time.time)
			{
				if (Time.time >= nextUpdateAt)
				{
					float remainingSeconds = _undoConfirmationExpireAt - Time.time;
					ValheimFloorPlanPlugin.ShowWrappedMessage(text: BuildUndoConfirmationMessage((int)Mathf.Ceil(remainingSeconds)), messageType: ValheimFloorPlanPlugin.ProgressMessageType);
					nextUpdateAt = Time.time + 1f;
				}
				yield return null;
			}
			_undoCountdownCoroutine = null;
			_undoConfirmationExpireAt = 0f;
			_undoConfirmationKeepLeveledTerrain = false;
			ClearUndoHighlights();
		}

		private void ShowUndoHighlights(Player player)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: 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_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			ClearUndoHighlights();
			_undoHighlightGo = new GameObject("VFP_UndoHighlight");
			Color color = default(Color);
			((Color)(ref color))..ctor(1f, 0.18f, 0.12f, 0.95f);
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			RaycastHit val2 = default(RaycastHit);
			foreach (ZNetView val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ZDO zDO = val.GetZDO();
				if (zDO != null && !(zDO.GetString("vfp_build", "") != "1") && IsWithinHorizontalRadius(((Component)val).transform.position, _undoCenter, _undoActiveRadius))
				{
					Vector3 position = ((Component)val).transform.position;
					float y = position.y;
					if (Physics.Raycast(new Vector3(position.x, position.y + 20f, position.z), Vector3.down, ref val2, 40f, 2048))
					{
						y = ((RaycastHit)(ref val2)).point.y;
					}
					y += 0.25f;
					LineRenderer val3 = MakeLine(_undoHighlightGo, color, 0.09f, 20);
					val3.loop = true;
					for (int j = 0; j < 20; j++)
					{
						float num = (float)j * (float)Math.PI * 2f / 20f;
						val3.SetPosition(j, new Vector3(position.x + Mathf.Cos(num) * 1.05f, y, position.z + Mathf.Sin(num) * 1.05f));
					}
				}
			}
			LineRenderer val4 = MakeLine(_undoHighlightGo, new Color(1f, 0.65f, 0f, 0.92f), 0.15f, 64);
			val4.loop = true;
			RaycastHit val5 = default(RaycastHit);
			for (int k = 0; k < 64; k++)
			{
				float num2 = (float)k * (float)Math.PI * 2f / 64f;
				float num3 = _undoCenter.x + Mathf.Cos(num2) * _undoActiveRadius;
				float num4 = _undoCenter.z + Mathf.Sin(num2) * _undoActiveRadius;
				float y2 = _undoCenter.y;
				if (Physics.Raycast(new Vector3(num3, _undoCenter.y + 300f, num4), Vector3.down, ref val5, 600f, 2048))
				{
					y2 = ((RaycastHit)(ref val5)).point.y;
				}
				val4.SetPosition(k, new Vector3(num3, y2 + 0.3f, num4));
			}
		}

		private void ClearUndoHighlights()
		{
			if ((Object)(object)_undoHighlightGo != (Object)null)
			{
				Object.Destroy((Object)(object)_undoHighlightGo);
				_undoHighlightGo = null;
			}
		}

		private void CancelUndoConfirmation(Player player)
		{
			if (_undoCountdownCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine);
				_undoCountdownCoroutine = null;
			}
			_undoConfirmationExpireAt = 0f;
			_undoConfirmationKeepLeveledTerrain = false;
			ClearUndoHighlights();
			((Character)player).Message((MessageType)2, "ValheimFloorPlan: Undo cancelled.", 0, (Sprite)null);
		}

		private void UpdateUndoConfirmationInput()
		{
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: 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_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (Input.GetMouseButtonDown(1) || Input.GetKeyDown((KeyCode)27))
			{
				CancelUndoConfirmation(localPlayer);
				return;
			}
			bool flag = Input.GetKeyDown((KeyCode)43) || Input.GetKeyDown((KeyCode)61) || Input.GetKeyDown((KeyCode)270);
			bool flag2 = Input.GetKeyDown((KeyCode)45) || Input.GetKeyDown((KeyCode)269);
			if (flag || flag2)
			{
				float num = _undoActiveRadius + (flag ? 5f : (-5f));
				_undoActiveRadius = Mathf.Clamp(num, 5f, 150f);
				ValheimFloorPlanPlugin.SetUndoRadius(_undoActiveRadius);
				CountUndoStats(localPlayer, _undoActiveRadius, _undoCenter, out var pieceCount, out var terrainChunkCount);
				_undoConfirmationPieceCount = pieceCount;
				_undoConfirmationTerrainChunks = terrainChunkCount;
				ShowUndoHighlights(localPlayer);
				_undoConfirmationExpireAt = Time.time + 5f;
				if (_undoCountdownCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine);
				}
				_undoCountdownCoroutine = ((MonoBehaviour)this).StartCoroutine(UndoCountdownCoroutine());
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, BuildUndoConfirmationMessage(5));
				return;
			}
			float num2 = (IsFineAdjustHeld() ? ValheimFloorPlanPlugin.PreviewFineMoveStep : ValheimFloorPlanPlugin.PreviewMoveStep);
			Vector3 forward = Vector3.forward;
			Vector3 right = Vector3.right;
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				forward = ((Component)main).transform.forward;
				forward.y = 0f;
				if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f)
				{
					((Vector3)(ref forward)).Normalize();
					((Vector3)(ref right))..ctor(forward.z, 0f, 0f - forward.x);
				}
				else
				{
					forward = Vector3.forward;
					right = Vector3.right;
				}
			}
			Vector3 val = Vector3.zero;
			bool flag3 = false;
			if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveForwardKey))
			{
				val = forward * num2;
				flag3 = true;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveBackwardKey))
			{
				val = -forward * num2;
				flag3 = true;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveRightKey))
			{
				val = right * num2;
				flag3 = true;
			}
			else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewMoveLeftKey))
			{
				val = -right * num2;
				flag3 = true;
			}
			if (flag3)
			{
				_undoCenter += val;
				CountUndoStats(localPlayer, _undoActiveRadius, _undoCenter, out var pieceCount2, out var terrainChunkCount2);
				_undoConfirmationPieceCount = pieceCount2;
				_undoConfirmationTerrainChunks = terrainChunkCount2;
				ShowUndoHighlights(localPlayer);
				_undoConfirmationExpireAt = Time.time + 5f;
				if (_undoCountdownCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine);
				}
				_undoCountdownCoroutine = ((MonoBehaviour)this).StartCoroutine(UndoCountdownCoroutine());
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, BuildUndoConfirmationMessage(5));
			}
		}

		public void BuildFromFile(string path)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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)
			FloorPlan floorPlan;
			try
			{
				floorPlan = FloorPlan.Load(path);
			}
			catch (Exception ex)
			{
				ValheimFloorPlanPlugin.Log.LogError((object)("Failed to load floor plan: " + ex.Message));
				ValheimFloorPlanPlugin.ShowWrappedMessage((MessageType)2, "ValheimFloorPlan: Could not load plan '" + Path.GetFileName(path) + "' — " + ex.Message);
				return;
			}
			if (ValidateAdditionalLevelPlanFootprints(floorPlan))
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					ValheimFloorPlanPlugin.Log.LogError((object)"No local player found.");
					return;
				}
				ValheimFloorPlanPlugin.Log.LogInfo((object)$"Building floor plan: {floorPlan.Pieces.Count} pieces from {path}");
				float num = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform.eulerAngles.y : ((Component)localPlayer).transform.eulerAngles.y);
				num = SnapAngleDeg(num + 180f);
				Vector3 initialBuildCenter = GetInitialBuildCenter(localPlayer, floorPlan, num);
				Vector3 placementOriginFromCenter = GetPlacementOriginFromCenter(floorPlan, initialBuildCenter, num);
				((MonoBehaviour)this).StartCoroutine(LevelThenPlace(floorPlan, num, placementOriginFromCenter));
			}
		}

		private IEnumerator LevelThenPlace(FloorPlan plan, float rotationDeg, Vector3 origin)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Player player = Player.m_localPlayer;
			if ((Object)(object)player == (Object)null)
			{
				ValheimFloorPlanPlugin.Log.LogError((object)"No local player found.");
				yield break;
			}
			float snappedRotationDeg = SnapAngleDeg(rotationDeg);
			if (Mathf.Abs(Mathf.DeltaAngle(rotationDeg, snappedRotationDeg)) > 0.01f)
			{
				ValheimFloorPlanPlugin.Log.LogInfo((object)$"Build rotation snapped: {rotationDeg:F1}° -> {snappedRotationDeg:F1}° (step={ValheimFloorPlanPlugin.BuildRotationSnapDegrees:F1}°)");
			}
			rotationDeg = snappedRotationDeg;
			ValheimFloorPlanPlugin.Log.LogInfo((object)$"Build origin: {origin}  rotation={rotationDeg:F1}°");
			_lastPlaced.Clear();
			_groundFloorScaffoldVerticals.Clear();
			TerrainLeveler.GetSnapshotBounds(plan, origin, out var sMinX, out var sMaxX, out var sMinZ, out var sMaxZ, rotationDeg);
			TerrainSnapshot.Capture(sMinX, sMaxX, sMinZ, sMaxZ, origin.y);
			if (!TerrainSnapshot.HasSnapshot)
			{
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: Warning - terrain snapshot capture failed. Undo may remove pieces without restoring terrain.");
			}
			((Character)player).Message((MessageType)2, "Clearing rocks...", 0, (Sprite)null);
			ClearRocksInPad(plan, origin, rotationDeg);
			((Character)player).Message((MessageType)2, "Leveling terrain...", 0, (Sprite)null);
			yield return ((MonoBehaviour)this).StartCoroutine(TerrainLeveler.LevelForPlan(plan, origin, rotationDeg));
			((Character)player).Message((MessageType)2, "Waiting for terrain physics...", 0, (Sprite)null);
			ShowBuildProgress("Waiting for terrain physics...");
			TerrainLeveler.GetPadBounds(plan, origin, out var padMinX, out var padMaxX, out var padMinZ, out var padMaxZ, rotationDeg);
			yield return ((MonoBehaviour)this).StartCoroutine(WaitForTerrainPhysics(padMinX, padMaxX, padMinZ, padMaxZ, TerrainLeveler.TargetLevelY));
			((Character)player).Message((MessageType)2, "Placing floor plan pieces...", 0, (Sprite)null);
			ShowBuildProgress($"Placing pieces... 0/{plan.Pieces.Count}");
			yield return ((MonoBehaviour)this).StartCoroutine(PlacePieces(plan, origin, rotationDeg));
			if (ValheimFloorPlanPlugin.RoofScaffolding)
			{
				ShowBuildProgress("Placing roof scaffolding...");
				yield return ((MonoBehaviour)this).StartCoroutine(PlaceRoofScaffolding(plan, origin, rotationDeg));
			}
			yield return ((MonoBehaviour)this).StartCoroutine(PlaceAdditionalLevelLayouts(plan, origin, rotationDeg));
			ShowBuildProgress("Final checks...");
			yield return ((MonoBehaviour)this).StartCoroutine(PostBuildSpikeGuard(plan, origin, rotationDeg));
			if (!ValheimFloorPlanPlugin.DisableWelcomePost)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(PlaceCenterSignage(plan, origin, rotationDeg));
			}
		}

		private IEnumerator PlaceAdditionalLevelLayouts(FloorPlan level1Plan, Vector3 origin, float rotationDeg)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			int configuredLevels = Mathf.Clamp(ValheimFloorPlanPlugin.FloorPlanLevels, 1, 3);
			if (configuredLevels <= 1)
			{
				yield break;
			}
			List<HearthOpening> blockedChimneyShaftOpenings = BuildHearthOpenings(level1Plan);
			List<HearthOpening> blockedStaircaseShaftOpenings = BuildStaircaseOpenings(level1Plan, 1);
			string level2Path = (ValheimFloorPlanPlugin.FloorPlanFileLevel2 ?? string.Empty).Trim();
			string level3Path = (ValheimFloorPlanPlugin.FloorPlanFileLevel3 ?? string.Empty).Trim();
			if (!ValheimFloorPlanPlugin.RoofScaffolding || !ValheimFloorPlanPlugin.ScaffoldingFloors)
			{
				ValheimFloorPlanPlugin.Log.LogWarning((object)"[UpperLevels] FloorPlanLevels > 1 requires RoofScaffolding and ScaffoldingFloors. Upper-level placement skipped.");
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: FloorPlanLevels > 1 requires RoofScaffolding and ScaffoldingFloors.");
				yield break;
			}
			GetPlanPieceBounds(level1Plan, out var l1MinCol, out var l1MaxColExclusive, out var l1MinRow, out var l1MaxRowExclusive);
			if (configuredLevels >= 2)
			{
				FloorPlan level2Plan;
				if (string.IsNullOrEmpty(level2Path))
				{
					ValheimFloorPlanPlugin.Log.LogWarning((object)"[UpperLevels] FloorPlanLevels is 2+ but FloorPlanFileLevel2 is empty. Level 2 placement skipped.");
					ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: FloorPlanLevels is 2+, but FloorPlanFileLevel2 is not set.");
				}
				else if (TryLoadValidatedAdditionalLevelPlan("FloorPlanFileLevel2", level2Path, l1MinCol, l1MaxColExclusive, l1MinRow, l1MaxRowExclusive, out level2Plan))
				{
					yield return ((MonoBehaviour)this).StartCoroutine(PlaceUpperLevelPieces(level2Plan, level1Plan, origin, rotationDeg, 2, blockedChimneyShaftOpenings, blockedStaircaseShaftOpenings));
				}
			}
			if (configuredLevels >= 3)
			{
				FloorPlan level3Plan;
				if (string.IsNullOrEmpty(level3Path))
				{
					ValheimFloorPlanPlugin.Log.LogWarning((object)"[UpperLevels] FloorPlanLevels is 3 but FloorPlanFileLevel3 is empty. Level 3 placement skipped.");
					ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: FloorPlanLevels is 3, but FloorPlanFileLevel3 is not set.");
				}
				else if (TryLoadValidatedAdditionalLevelPlan("FloorPlanFileLevel3", level3Path, l1MinCol, l1MaxColExclusive, l1MinRow, l1MaxRowExclusive, out level3Plan))
				{
					yield return ((MonoBehaviour)this).StartCoroutine(PlaceUpperLevelPieces(level3Plan, level1Plan, origin, rotationDeg, 3, blockedChimneyShaftOpenings, blockedStaircaseShaftOpenings));
				}
			}
		}

		private bool TryLoadValidatedAdditionalLevelPlan(string configKey, string path, int l1MinCol, int l1MaxColExclusive, int l1MinRow, int l1MaxRowExclusive, out FloorPlan? levelPlan)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			levelPlan = null;
			string text = (path ?? string.Empty).Trim();
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			try
			{
				levelPlan = FloorPlan.Load(text);
			}
			catch (Exception ex)
			{
				ValheimFloorPlanPlugin.Log.LogError((object)("[" + configKey + "] Failed to load '" + text + "': " + ex.Message));
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: " + configKey + " could not be loaded. " + Path.GetFileName(text) + " (" + ex.Message + ")");
				return false;
			}
			GetPlanPieceBounds(levelPlan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			if (minCol < l1MinCol || maxColExclusive > l1MaxColExclusive || minRow < l1MinRow || maxRowExclusive > l1MaxRowExclusive)
			{
				ValheimFloorPlanPlugin.Log.LogWarning((object)("[" + configKey + "] Footprint must fit inside Level 1 footprint. " + $"Level1=[col {l1MinCol}..{l1MaxColExclusive}, row {l1MinRow}..{l1MaxRowExclusive}] " + $"Candidate=[col {minCol}..{maxColExclusive}, row {minRow}..{maxRowExclusive}] " + "Path='" + text + "'"));
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: " + configKey + " footprint must fit inside Level 1 plan.");
				levelPlan = null;
				return false;
			}
			return true;
		}

		private IEnumerator PlaceUpperLevelPieces(FloorPlan upperPlan, FloorPlan level1Plan, Vector3 origin, float rotationDeg, int targetLevelNumber, List<HearthOpening> blockedChimneyShaftOpenings, List<HearthOpening> blockedStaircaseShaftOpenings)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			int floorIndex = targetLevelNumber - 2;
			int scaffoldLevels = Mathf.Clamp(ValheimFloorPlanPlugin.ScaffoldingLevels, 1, 3);
			if (floorIndex < 0 || floorIndex >= scaffoldLevels)
			{
				ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber} layout provided but ScaffoldingLevels={scaffoldLevels} does not provide that floor. Skipped.");
				ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, $"ValheimFloorPlan: Level {targetLevelNumber} layout skipped because matching scaffold floor is not available.");
				yield break;
			}
			float levelDeckY = GetDeckYForScaffoldLevel(floorIndex);
			bool useWoodStructure = ValheimFloorPlanPlugin.WallPillarMaterial == ValheimFloorPlanPlugin.StructuralMaterial.Wood;
			int layoutLevelIndex = Mathf.Clamp(targetLevelNumber - 1, 0, 2);
			int configuredExternalWallHeight = ValheimFloorPlanPlugin.GetExternalWallHeightForLevel(layoutLevelIndex);
			GetPlanPieceBounds(level1Plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive);
			int total = upperPlan.Pieces.Count;
			int placed = 0;
			int skipped = 0;
			int skippedPerimeterRule = 0;
			int skippedStaircaseOverlap = 0;
			int skippedStaircaseFurnitureOverlap = 0;
			int skippedStaircaseBuildFailed = 0;
			int skippedStaircaseReachMode = 0;
			int skippedChimneyShaftOverlap = 0;
			int skippedStaircaseShaftOverlap = 0;
			int skippedHearthPerimeterBuffer = 0;
			int skippedHearthLowerOverlap = 0;
			int skippedHearthSpacing = 0;
			int skippedHearthFurnitureOverlap = 0;
			int skippedFurnitureShaftOverlap = 0;
			List<HearthOpening> blockedUpperHearthOpenings = new List<HearthOpening>();
			List<HearthOpening> blockedUpperFurnitureOpenings = new List<HearthOpening>();
			List<HearthOpening> upperHearthPlacementHistory = new List<HearthOpening>();
			List<HearthOpening> placedUpperHearthOpenings = new List<HearthOpening>();
			List<string> clashReportLines = new List<string>();
			List<FloorPlanPiece> orderedPieces = new List<FloorPlanPiece>(upperPlan.Pieces.Count);
			for (int i = 0; i < upperPlan.Pieces.Count; i++)
			{
				FloorPlanPiece p = upperPlan.Pieces[i];
				if (p.Type == "Staircase")
				{
					orderedPieces.Add(p);
				}
			}
			for (int j = 0; j < upperPlan.Pieces.Count; j++)
			{
				FloorPlanPiece p2 = upperPlan.Pieces[j];
				if (p2.Type == "Hearth")
				{
					orderedPieces.Add(p2);
				}
			}
			for (int k = 0; k < upperPlan.Pieces.Count; k++)
			{
				FloorPlanPiece p3 = upperPlan.Pieces[k];
				if (p3.Type != "Staircase" && p3.Type != "Hearth")
				{
					orderedPieces.Add(p3);
				}
			}
			ShowBuildProgress($"Placing Level {targetLevelNumber} pieces... 0/{total}");
			foreach (FloorPlanPiece piece in orderedPieces)
			{
				PieceDef def = PieceMap.GetDef(piece.Type);
				if (def == null)
				{
					skipped++;
					continue;
				}
				if (piece.Type == "Floor2x2" || piece.Type == "Floor1x1")
				{
					skipped++;
					continue;
				}
				int effectivePieceRotation = piece.Rotation;
				int effW = def.EffW(effectivePieceRotation);
				int effH = def.EffH(effectivePieceRotation);
				int pieceMaxColExclusive = piece.Col + effW;
				int pieceMaxRowExclusive = piece.Row + effH;
				if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, blockedChimneyShaftOpenings))
				{
					HearthOpening overlap = FindFirstOverlappingOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, blockedChimneyShaftOpenings, 0);
					string overlapLabel = DescribeHearthOpening(overlap);
					ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: piece '{piece.Type}' at ({piece.Col},{piece.Row}) overlaps chimney shaft {overlapLabel} and was skipped.");
					clashReportLines.Add($"{piece.Type} ({piece.Col},{piece.Row}) vs shaft {overlapLabel}");
					skippedChimneyShaftOverlap++;
					skipped++;
					continue;
				}
				List<HearthOpening> applicableStairShaftOpenings = GetApplicableStaircaseShaftOpenings(blockedStaircaseShaftOpenings, targetLevelNumber);
				if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, applicableStairShaftOpenings))
				{
					HearthOpening overlap2 = FindFirstOverlappingOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, applicableStairShaftOpenings, 0);
					string overlapLabel2 = DescribeHearthOpening(overlap2);
					ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: piece '{piece.Type}' at ({piece.Col},{piece.Row}) overlaps staircase shaft {overlapLabel2} and was skipped.");
					clashReportLines.Add($"{piece.Type} ({piece.Col},{piece.Row}) vs stair-shaft {overlapLabel2}");
					skippedStaircaseShaftOverlap++;
					skipped++;
					continue;
				}
				bool isExternal = IsOnPlanOuterPerimeter(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive);
				bool allowPerimeterPlacement = piece.Type == "Wall" || piece.Type == "Pillar" || piece.Type == "Doorway";
				if (isExternal && !allowPerimeterPlacement)
				{
					ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: piece '{piece.Type}' at ({piece.Col},{piece.Row}) touches Level 1 outer perimeter and was skipped (internal-only rule).");
					skippedPerimeterRule++;
					skipped++;
					continue;
				}
				if (piece.Type == "Staircase")
				{
					int stairMaxColExclusive = piece.Col + effW;
					int stairMaxRowExclusive = piece.Row + effH;
					if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, stairMaxColExclusive, stairMaxRowExclusive, blockedUpperFurnitureOpenings))
					{
						HearthOpening furnitureOverlap = FindFirstOverlappingOpening(piece.Col, piece.Row, stairMaxColExclusive, stairMaxRowExclusive, blockedUpperFurnitureOpenings, 0);
						ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Staircase at ({piece.Col},{piece.Row}) overlaps furniture {DescribeHearthOpening(furnitureOverlap)} and was skipped.");
						clashReportLines.Add($"Stair ({piece.Col},{piece.Row}) vs {DescribeHearthOpening(furnitureOverlap)}");
						skippedStaircaseFurnitureOverlap++;
						skipped++;
						continue;
					}
					if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, stairMaxColExclusive, stairMaxRowExclusive, blockedUpperHearthOpenings))
					{
						HearthOpening overlap3 = FindFirstOverlappingOpening(piece.Col, piece.Row, stairMaxColExclusive, stairMaxRowExclusive, blockedUpperHearthOpenings, 0);
						string overlapLabel3 = DescribeHearthOpening(overlap3);
						ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Staircase at ({piece.Col},{piece.Row}) overlaps {overlapLabel3} and was skipped.");
						clashReportLines.Add($"Stair ({piece.Col},{piece.Row}) vs {overlapLabel3}");
						skippedStaircaseOverlap++;
						skipped++;
						continue;
					}
					float staircaseTargetTopY = GetUpperLevelStaircaseTargetTopY(levelDeckY, floorIndex, scaffoldLevels);
					if (staircaseTargetTopY <= levelDeckY + 0.01f)
					{
						ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Staircase at ({piece.Col},{piece.Row}) skipped by StaircaseReachMode={ValheimFloorPlanPlugin.StaircaseReachMode} (no higher scaffold level available).");
						clashReportLines.Add($"Stair ({piece.Col},{piece.Row}) blocked by StaircaseReachMode={ValheimFloorPlanPlugin.StaircaseReachMode}");
						skippedStaircaseReachMode++;
						skipped++;
						continue;
					}
					int staircasePlaced = PlaceStaircaseComposite(piece, def, origin, rotationDeg, "woodiron_pole", "wood_beam", 2f, Player.m_localPlayer, levelDeckY, staircaseTargetTopY);
					if (staircasePlaced <= 0)
					{
						skippedStaircaseBuildFailed++;
						skipped++;
						continue;
					}
					placed += staircasePlaced;
					HearthOpening staircaseOpenin