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 Pieces { get; } = new List(); public List FlexiWalls { get; } = new List(); 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 _lastPlaced = new List(); private readonly List _groundFloorScaffoldVerticals = new List(); 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 _previewRiskHotspots = new List(); private readonly List _previewRiskMarkers = new List(); private readonly List _previewRiskRenderPoints = new List(); private readonly List _previewUpperLevelRings = new List(); 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(); MeshRenderer val3 = val.AddComponent(); ((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(); 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 hotspots, List 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 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((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((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() ?? Array.Empty(); 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((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 blockedChimneyShaftOpenings = BuildHearthOpenings(level1Plan); List 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 blockedChimneyShaftOpenings, List 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 blockedUpperHearthOpenings = new List(); List blockedUpperFurnitureOpenings = new List(); List upperHearthPlacementHistory = new List(); List placedUpperHearthOpenings = new List(); List clashReportLines = new List(); List orderedPieces = new List(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 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 staircaseOpening = new HearthOpening(piece.Col, piece.Row, stairMaxColExclusive, stairMaxRowExclusive, targetLevelNumber, "Staircase"); blockedUpperHearthOpenings.Add(staircaseOpening); blockedStaircaseShaftOpenings.Add(staircaseOpening); List staircaseOpenings = new List { staircaseOpening }; RemoveInterferingUpperDeckPieces(staircaseOpenings, origin, rotationDeg, levelDeckY + 0.75f, staircaseTargetTopY + 0.75f); RemoveInterferingUpperScaffoldBeamPieces(staircaseOpenings, origin, rotationDeg, levelDeckY + 0.5f, staircaseTargetTopY + 0.75f); continue; } bool isUpperHearth = piece.Type == "Hearth"; bool isFurniturePiece = IsFurnitureType(piece.Type); if (isFurniturePiece && !isUpperHearth && DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, blockedUpperHearthOpenings)) { HearthOpening overlap4 = FindFirstOverlappingOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, blockedUpperHearthOpenings, 0); ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Furniture '{piece.Type}' at ({piece.Col},{piece.Row}) overlaps shaft {DescribeHearthOpening(overlap4)} and was skipped."); clashReportLines.Add($"{piece.Type} ({piece.Col},{piece.Row}) vs {DescribeHearthOpening(overlap4)}"); skippedFurnitureShaftOverlap++; skipped++; continue; } if (isUpperHearth) { int hearthMaxColExclusive = piece.Col + effW; int hearthMaxRowExclusive = piece.Row + effH; if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, blockedUpperFurnitureOpenings)) { HearthOpening furnitureOverlap2 = FindFirstOverlappingOpening(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, blockedUpperFurnitureOpenings, 0); ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Hearth at ({piece.Col},{piece.Row}) overlaps furniture {DescribeHearthOpening(furnitureOverlap2)} and was skipped."); clashReportLines.Add($"Hearth ({piece.Col},{piece.Row}) vs {DescribeHearthOpening(furnitureOverlap2)}"); skippedHearthFurnitureOverlap++; skipped++; continue; } if (!IsFootprintInsideBoundsWithMargin(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, minCol, maxColExclusive, minRow, maxRowExclusive, 1)) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Hearth at ({piece.Col},{piece.Row}) violates 1-cell perimeter buffer and was skipped."); skippedHearthPerimeterBuffer++; skipped++; continue; } if (DoesFootprintOverlapAnyOpening(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, blockedUpperHearthOpenings)) { HearthOpening overlap5 = FindFirstOverlappingOpening(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, blockedUpperHearthOpenings, 0); string overlapLabel4 = DescribeHearthOpening(overlap5); ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Hearth at ({piece.Col},{piece.Row}) overlaps {overlapLabel4} and was skipped."); clashReportLines.Add($"Hearth ({piece.Col},{piece.Row}) vs {overlapLabel4}"); skippedHearthLowerOverlap++; skipped++; continue; } if (DoesFootprintOverlapAnyOpeningWithMargin(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, upperHearthPlacementHistory, 1)) { HearthOpening overlap6 = FindFirstOverlappingOpening(piece.Col, piece.Row, hearthMaxColExclusive, hearthMaxRowExclusive, upperHearthPlacementHistory, 1); string overlapLabel5 = DescribeHearthOpening(overlap6); ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: Hearth at ({piece.Col},{piece.Row}) violates spacing near {overlapLabel5} and was skipped."); clashReportLines.Add($"Hearth ({piece.Col},{piece.Row}) near {overlapLabel5}"); skippedHearthSpacing++; skipped++; continue; } } string prefabName = ResolvePrefabName(piece.Type, def.Prefab, useWoodStructure); ZNetScene instance = ZNetScene.instance; GameObject prefab = ((instance != null) ? instance.GetPrefab(prefabName) : null); if ((Object)(object)prefab == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Level {targetLevelNumber}: prefab '{prefabName}' not found for '{piece.Type}' — skipped."); skipped++; continue; } if (useWoodStructure && piece.Type == "Wall" && isExternal) { effectivePieceRotation = GetExteriorWallRotation(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, effectivePieceRotation); effW = def.EffW(effectivePieceRotation); effH = def.EffH(effectivePieceRotation); } if (useWoodStructure && piece.Type == "Wall" && piece.WallFace == WallFaceMode.Inner && !isExternal) { effectivePieceRotation = (effectivePieceRotation + 180) % 360; effW = def.EffW(effectivePieceRotation); effH = def.EffH(effectivePieceRotation); } Vector3 pieceCenter = PieceMap.TransformPlanPoint(localX: ((float)piece.Col + (float)effW * 0.5f) * 1f, localZ: ((float)piece.Row + (float)effH * 0.5f) * 1f, origin: origin, y: levelDeckY, rotationDeg: rotationDeg); Vector3 pos = new Vector3(pieceCenter.x, levelDeckY + def.YOffset, pieceCenter.z); float pieceYaw = PieceMap.TransformLocalYaw(effectivePieceRotation + def.RotationOffset, rotationDeg); Quaternion rot = Quaternion.Euler(0f, pieceYaw, 0f); Vector3 materialOffset = Vector3.zero; if (((useWoodStructure && (piece.Type == "Wall" || piece.Type == "Pillar")) || piece.Type == "Doorway") && isExternal) { materialOffset = GetWoodPerimeterOffset(piece.Type, piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, rotationDeg); } bool centerWorkbench = piece.Type == "Workbench"; int stackCount = ((!(IsExternalWallOrPillarType(piece.Type) && isExternal)) ? 1 : configuredExternalWallHeight); float stackStepY = GetStackStepY(piece.Type); for (int l = 0; l < stackCount; l++) { Vector3 stackedPos = new Vector3(pos.x, pos.y + stackStepY * (float)l, pos.z) + materialOffset; GameObject spawnedGo = SpawnRegisteredPiece(prefab, stackedPos, rot, Player.m_localPlayer, centerWorkbench); if (isUpperHearth && l == stackCount - 1) { placed += PlaceHearthBeamRing(piece.Col, piece.Row, effW, effH, origin, rotationDeg, spawnedGo, Player.m_localPlayer); placed += PlaceHearthSupportBeam(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, pos.y, Player.m_localPlayer); } placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } if (isUpperHearth) { HearthOpening placedOpening = new HearthOpening(piece.Col, piece.Row, piece.Col + effW, piece.Row + effH, targetLevelNumber, "Hearth"); blockedUpperHearthOpenings.Add(placedOpening); blockedChimneyShaftOpenings.Add(placedOpening); upperHearthPlacementHistory.Add(placedOpening); placedUpperHearthOpenings.Add(placedOpening); } if (isFurniturePiece) { blockedUpperFurnitureOpenings.Add(new HearthOpening(piece.Col, piece.Row, pieceMaxColExclusive, pieceMaxRowExclusive, targetLevelNumber, piece.Type)); } } if (placedUpperHearthOpenings.Count > 0) { placed += PlaceUpperLevelHearthChimneyStacks(level1Plan, placedUpperHearthOpenings, floorIndex, levelDeckY, origin, rotationDeg, Player.m_localPlayer); } PieceDef ulFwDef = PieceMap.GetDef("FlexiWall"); if (ulFwDef != null && upperPlan.FlexiWalls.Count > 0) { string ulFwPrefabName = ResolvePrefabName("FlexiWall", ulFwDef.Prefab, useWoodStructure); ZNetScene instance2 = ZNetScene.instance; GameObject ulFwPrefab = ((instance2 != null) ? instance2.GetPrefab(ulFwPrefabName) : null); if ((Object)(object)ulFwPrefab != (Object)null) { int configuredInternalWallHeight = ValheimFloorPlanPlugin.GetInternalWallHeightForLevel(layoutLevelIndex); float ulFwStepY = GetStackStepY("FlexiWall"); float ulFwBaseY = levelDeckY + ulFwDef.YOffset; foreach (FlexiWallPiece fw in upperPlan.FlexiWalls) { int ulFwStackCount = (IsFlexiWallOnPerimeter(fw, upperPlan, minCol, maxColExclusive, minRow, maxRowExclusive) ? configuredExternalWallHeight : configuredInternalWallHeight); List segments = ComputeFlexiWallSegments(fw, 1f, densifyTightCurve: true); for (int segIdx = 0; segIdx < segments.Count; segIdx++) { FlexiWallSegment seg = segments[segIdx]; Vector3 segCenter = PieceMap.TransformPlanPoint(origin, seg.Lx, seg.Lz, origin.y, rotationDeg); float ulFwYaw = PieceMap.TransformLocalYaw(0f - seg.YawDeg + (float)ulFwDef.RotationOffset, rotationDeg); Quaternion ulFwRot = Quaternion.Euler(0f, ulFwYaw, 0f); float tanRad = seg.YawDeg * ((float)Math.PI / 180f); Vector2 worldTan = PieceMap.TransformLocalXZ(Mathf.Cos(tanRad), Mathf.Sin(tanRad), rotationDeg); for (int s = 0; s < ulFwStackCount; s++) { float brickShift = ((segIdx > 0 && s % 2 != 0) ? (-0.5f) : 0f); Vector3 fwPos = new Vector3(segCenter.x + worldTan.x * brickShift, ulFwBaseY + ulFwStepY * (float)s, segCenter.z + worldTan.y * brickShift); SpawnRegisteredPiece(ulFwPrefab, fwPos, ulFwRot, Player.m_localPlayer); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } if (segIdx != segments.Count - 1) { continue; } for (int m = 1; m < ulFwStackCount; m += 2) { Vector3 extraPos = new Vector3(segCenter.x, ulFwBaseY + ulFwStepY * (float)m, segCenter.z); SpawnRegisteredPiece(ulFwPrefab, extraPos, ulFwRot, Player.m_localPlayer); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } } } } List summaryParts = new List(); if (skippedPerimeterRule > 0) { summaryParts.Add($"{skippedPerimeterRule} perimeter/internal-only"); } if (skippedStaircaseOverlap > 0) { summaryParts.Add($"{skippedStaircaseOverlap} staircase overlap"); } if (skippedStaircaseFurnitureOverlap > 0) { summaryParts.Add($"{skippedStaircaseFurnitureOverlap} staircase furniture-overlap"); } if (skippedStaircaseBuildFailed > 0) { summaryParts.Add($"{skippedStaircaseBuildFailed} staircase build-failed"); } if (skippedStaircaseReachMode > 0) { summaryParts.Add($"{skippedStaircaseReachMode} staircase reach-mode"); } if (skippedChimneyShaftOverlap > 0) { summaryParts.Add($"{skippedChimneyShaftOverlap} chimney-shaft overlap"); } if (skippedStaircaseShaftOverlap > 0) { summaryParts.Add($"{skippedStaircaseShaftOverlap} staircase-shaft overlap"); } if (skippedHearthPerimeterBuffer > 0) { summaryParts.Add($"{skippedHearthPerimeterBuffer} hearth perimeter-buffer"); } if (skippedHearthLowerOverlap > 0) { summaryParts.Add($"{skippedHearthLowerOverlap} hearth lower-overlap"); } if (skippedHearthSpacing > 0) { summaryParts.Add($"{skippedHearthSpacing} hearth spacing"); } if (skippedHearthFurnitureOverlap > 0) { summaryParts.Add($"{skippedHearthFurnitureOverlap} hearth furniture-overlap"); } if (skippedFurnitureShaftOverlap > 0) { summaryParts.Add($"{skippedFurnitureShaftOverlap} furniture shaft-overlap"); } if (summaryParts.Count > 0) { ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, string.Format("ValheimFloorPlan: Level {0} skips — {1}.", targetLevelNumber, string.Join(", ", summaryParts))); } if (clashReportLines.Count > 0) { yield return ((MonoBehaviour)this).StartCoroutine(PlaceUpperLevelClashSignage(level1Plan, origin, rotationDeg, targetLevelNumber, clashReportLines)); } ShowBuildProgress($"Placing Level {targetLevelNumber} pieces... done ({placed}/{total})"); ValheimFloorPlanPlugin.Log.LogInfo((object)$"[UpperLevels] Level {targetLevelNumber} placement complete: {placed} placed, {skipped} skipped, deckY={levelDeckY:F2}."); } private int PlaceUpperLevelHearthChimneyStacks(FloorPlan level1Plan, List upperHearthOpenings, int floorIndex, float upperDeckY, Vector3 origin, float rotationDeg, Player player) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("wood_wall_half") : null); ZNetScene instance2 = ZNetScene.instance; GameObject wall1Prefab = ((instance2 != null) ? instance2.GetPrefab("wood_wall_1x1") : null); ZNetScene instance3 = ZNetScene.instance; GameObject roofPrefab = ((instance3 != null) ? instance3.GetPrefab("wood_roof") : null); ZNetScene instance4 = ZNetScene.instance; GameObject val2 = ((instance4 != null) ? instance4.GetPrefab("woodiron_pole") : null); if ((Object)(object)val == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[UpperLevels] Could not place Hearth chimney stack because prefab 'wood_wall_half' was not found."); return 0; } int num = 0; int num2 = Mathf.Clamp(ValheimFloorPlanPlugin.ScaffoldingLevels, 1, 3); float num3 = upperDeckY + 3f; float num4 = upperDeckY; for (int i = floorIndex + 1; i < num2; i++) { float num5 = GetDeckYForScaffoldLevel(i) - 0.14f; if (num5 <= num4 + 0.01f) { continue; } num += PlaceHearthChimneyLevel(upperHearthOpenings, origin, rotationDeg, num4, num5, num3, val, wall1Prefab, roofPrefab, player); if ((Object)(object)val2 != (Object)null) { float num6 = num5 - num4; float y = num4 + num6 * 0.5f; foreach (HearthOpening upperHearthOpening in upperHearthOpenings) { int[] array = new int[2] { upperHearthOpening.MinCol, upperHearthOpening.MaxColExclusive }; int[] array2 = new int[2] { upperHearthOpening.MinRow, upperHearthOpening.MaxRowExclusive }; int[] array3 = array; foreach (int num7 in array3) { int[] array4 = array2; foreach (int num8 in array4) { Vector3 center = PieceMap.TransformPlanPoint(origin, num7, num8, y, rotationDeg); num += SpawnScaffoldColumn(val2, center, Quaternion.Euler(0f, rotationDeg, 0f), player, num6, 2f); } } } } num4 = num5; } float deckYForScaffoldLevel = GetDeckYForScaffoldLevel(num2 - 1); float num9 = deckYForScaffoldLevel; if (ValheimFloorPlanPlugin.RoofStyle == ValheimFloorPlanPlugin.RoofStyleOption.Gable) { GetPlanPieceBounds(level1Plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive); float num10 = (float)(maxColExclusive - minCol) * 1f; float num11 = (float)(maxRowExclusive - minRow) * 1f; float num12 = 0.5f * Mathf.Min(num10, num11); num9 = deckYForScaffoldLevel + Mathf.Tan(0.4537856f) * num12; } float num13 = Mathf.Max(num4, num3); float num14 = Mathf.Max(num13 + 2f, num9 + 1f); num += PlaceHearthChimneyTop(upperHearthOpenings, origin, rotationDeg, num13, num14, val, wall1Prefab, roofPrefab, player); RemoveInterferingUpperDeckPieces(upperHearthOpenings, origin, rotationDeg, num3, num14 + 0.75f); RemoveInterferingUpperRoofPieces(upperHearthOpenings, origin, rotationDeg, num13, num9 + 0.6f); return num; } private void RemoveInterferingUpperDeckPieces(List openings, Vector3 origin, float rotationDeg, float minDeckY, float maxDeckY) { //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) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) if (openings == null || openings.Count == 0) { return; } for (int num = _lastPlaced.Count - 1; num >= 0; num--) { GameObject val = _lastPlaced[num]; if ((Object)(object)val == (Object)null) { _lastPlaced.RemoveAt(num); } else { string text = ((((Object)val).name == null) ? string.Empty : ((Object)val).name.ToLowerInvariant()); if (text.Contains("wood_floor") || text.Contains("floor_1x1") || text.Contains("roof_top")) { Vector3 position = val.transform.position; if (!(position.y < minDeckY - 0.5f) && !(position.y > maxDeckY + 0.5f)) { Vector2 val2 = PieceMap.TransformLocalXZ(position.x - origin.x, position.z - origin.z, rotationDeg); bool flag = false; for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; if (val2.x >= (float)hearthOpening.MinCol - 0.2f && val2.x <= (float)hearthOpening.MaxColExclusive + 0.2f && val2.y >= (float)hearthOpening.MinRow - 0.2f && val2.y <= (float)hearthOpening.MaxRowExclusive + 0.2f) { flag = true; break; } } if (flag) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val); } else { Object.Destroy((Object)(object)val); } _lastPlaced.RemoveAt(num); } } } } } } private void RemoveInterferingUpperRoofPieces(List openings, Vector3 origin, float rotationDeg, float minRoofY, float maxRoofY) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if (openings == null || openings.Count == 0) { return; } for (int num = _lastPlaced.Count - 1; num >= 0; num--) { GameObject val = _lastPlaced[num]; if ((Object)(object)val == (Object)null) { _lastPlaced.RemoveAt(num); } else { string text = ((((Object)val).name == null) ? string.Empty : ((Object)val).name.ToLowerInvariant()); if (text.Contains("roof") || text.Contains("thatch")) { Vector3 position = val.transform.position; if (!(position.y < minRoofY - 1.75f) && !(position.y > maxRoofY)) { Vector2 val2 = PieceMap.TransformLocalXZ(position.x - origin.x, position.z - origin.z, rotationDeg); bool flag = false; for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; if (val2.x >= (float)hearthOpening.MinCol - 0.2f && val2.x <= (float)hearthOpening.MaxColExclusive + 0.2f && val2.y >= (float)hearthOpening.MinRow - 0.2f && val2.y <= (float)hearthOpening.MaxRowExclusive + 0.2f) { flag = true; break; } } if (flag) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val); } else { Object.Destroy((Object)(object)val); } _lastPlaced.RemoveAt(num); } } } } } } private void RemoveInterferingUpperScaffoldBeamPieces(List openings, Vector3 origin, float rotationDeg, float minBeamY, float maxBeamY) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_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) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0138: 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_0166: Unknown result type (might be due to invalid IL or missing references) if (openings == null || openings.Count == 0) { return; } for (int num = _lastPlaced.Count - 1; num >= 0; num--) { GameObject val = _lastPlaced[num]; if ((Object)(object)val == (Object)null) { _lastPlaced.RemoveAt(num); } else { string text = ((((Object)val).name == null) ? string.Empty : ((Object)val).name.ToLowerInvariant()); if (text.Contains("woodiron_beam") || text.Contains("iron_beam")) { Vector3 position = val.transform.position; if (!(position.y < minBeamY - 0.45f) && !(position.y > maxBeamY + 0.45f)) { Vector2 val2 = PieceMap.TransformLocalXZ(position.x - origin.x, position.z - origin.z, rotationDeg); bool flag = false; for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; if (val2.x >= (float)hearthOpening.MinCol - 0.25f && val2.x <= (float)hearthOpening.MaxColExclusive + 0.25f && val2.y >= (float)hearthOpening.MinRow - 0.25f && val2.y <= (float)hearthOpening.MaxRowExclusive + 0.25f) { flag = true; break; } } if (flag) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val); } else { Object.Destroy((Object)(object)val); } _lastPlaced.RemoveAt(num); } } } } } } private static float GetDeckYForScaffoldLevel(int levelIndex) { float num = TerrainLeveler.TargetLevelY; for (int i = 0; i <= levelIndex; i++) { num += Mathf.Max(2f, (float)ValheimFloorPlanPlugin.GetScaffoldingFloorHeightForLevel(i)); } return num + 0.14f; } private static float SnapAngleDeg(float angleDeg) { float num = Mathf.Clamp(ValheimFloorPlanPlugin.BuildRotationSnapDegrees, 0f, 90f); angleDeg = NormalizeAngleDeg(angleDeg); if (num <= 0.001f) { return angleDeg; } return NormalizeAngleDeg(Mathf.Round(angleDeg / num) * num); } private static float NormalizeAngleDeg(float angleDeg) { angleDeg %= 360f; if (angleDeg < 0f) { angleDeg += 360f; } return angleDeg; } private IEnumerator PlaceCenterSignage(FloorPlan plan, 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) float signageRotationDeg = rotationDeg - 180f; Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null) { yield break; } ZNetScene instance = ZNetScene.instance; GameObject polePrefab = ((instance != null) ? instance.GetPrefab("wood_pole_log_4") : null); ZNetScene instance2 = ZNetScene.instance; GameObject signPrefab = ((instance2 != null) ? instance2.GetPrefab("sign") : null); if ((Object)(object)polePrefab == (Object)null || (Object)(object)signPrefab == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[Signage] Prefab 'wood_pole_log_4' or 'sign' not found — signage skipped."); yield break; } int minCol = int.MaxValue; int maxColExcl = int.MinValue; int minRow = int.MaxValue; int maxRowExcl = int.MinValue; foreach (FloorPlanPiece piece in plan.Pieces) { PieceDef def = PieceMap.GetDef(piece.Type); int effW = def?.EffW(piece.Rotation) ?? 1; int effH = def?.EffH(piece.Rotation) ?? 1; if (piece.Col < minCol) { minCol = piece.Col; } if (piece.Col + effW > maxColExcl) { maxColExcl = piece.Col + effW; } if (piece.Row < minRow) { minRow = piece.Row; } if (piece.Row + effH > maxRowExcl) { maxRowExcl = piece.Row + effH; } } if (minCol == int.MaxValue) { minCol = 0; maxColExcl = plan.Cols; minRow = 0; maxRowExcl = plan.Rows; } float localCX = (float)(minCol + maxColExcl) * 0.5f * 1f; float localCZ = (float)(minRow + maxRowExcl) * 0.5f * 1f; Vector3 signCenter = PieceMap.TransformPlanPoint(origin, localCX, localCZ, origin.y, rotationDeg); float wx = signCenter.x; float wz = signCenter.z; float terrainY = TerrainLeveler.TargetLevelY; RaycastHit hit = default(RaycastHit); if (Physics.Raycast(new Vector3(wx, terrainY + 300f, wz), Vector3.down, ref hit, 600f, 2048)) { terrainY = ((RaycastHit)(ref hit)).point.y; } float signageRad = signageRotationDeg * ((float)Math.PI / 180f); float signageSinR = Mathf.Sin(signageRad); float signageCosR = Mathf.Cos(signageRad); SpawnScaffoldPole(polePrefab, new Vector3(wx, terrainY + 2f, wz), Quaternion.Euler(0f, signageRotationDeg, 0f), player); yield return (object)new WaitForSeconds(0.05f); string[] signTexts = new string[4] { "Welcome", "If you like this mod please", "give it a \ud83d\udc4d at the", "Thunderstore Mods Site. Thx" }; float signTopY = terrainY + 4f - 0.4f; float signOX = (0f - signageSinR) * 0.3f; float signOZ = (0f - signageCosR) * 0.3f; Quaternion signRot = Quaternion.Euler(0f, signageRotationDeg + 180f, 0f); for (int i = 0; i < signTexts.Length; i++) { float signY = signTopY - (float)i * 0.6f; Vector3 signPos = new Vector3(wx + signOX, signY, wz + signOZ); GameObject signGo = Object.Instantiate(signPrefab, signPos, signRot); _lastPlaced.Add(signGo); WearNTear wnt = signGo.GetComponent(); if ((Object)(object)wnt != (Object)null) { wnt.m_noSupportWear = true; } ZNetView znv = signGo.GetComponent(); if ((Object)(object)znv != (Object)null) { ZDO zdo = znv.GetZDO(); if (zdo != null) { zdo.SetOwner(ZDOMan.GetSessionID()); zdo.Set("vfp_build", "1"); zdo.Set("text", signTexts[i]); } } Piece component = signGo.GetComponent(); if (component != null) { component.SetCreator(player.GetPlayerID()); } yield return (object)new WaitForSeconds(0.05f); } ValheimFloorPlanPlugin.Log.LogInfo((object)"[FloorPlanBuilder] Placed centre signage pole + 4 signs."); } private void ClearRocksInPad(FloorPlan plan, Vector3 origin, float rotationDeg = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027d: 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_050e: Unknown result type (might be due to invalid IL or missing references) //IL_0513: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Unknown result type (might be due to invalid IL or missing references) //IL_0698: Unknown result type (might be due to invalid IL or missing references) //IL_069a: Unknown result type (might be due to invalid IL or missing references) //IL_0529: Unknown result type (might be due to invalid IL or missing references) //IL_05e7: Unknown result type (might be due to invalid IL or missing references) //IL_05ec: Unknown result type (might be due to invalid IL or missing references) //IL_05ee: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_05f8: Unknown result type (might be due to invalid IL or missing references) //IL_0602: Unknown result type (might be due to invalid IL or missing references) //IL_0634: Unknown result type (might be due to invalid IL or missing references) //IL_060c: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_0484: Unknown result type (might be due to invalid IL or missing references) TerrainLeveler.GetLeveledAreaBounds(plan, origin, out var minX, out var maxX, out var minZ, out var maxZ, rotationDeg); int num = 0; HashSet hashSet = new HashSet(); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((minX + maxX) * 0.5f, origin.y, (minZ + maxZ) * 0.5f); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor((maxX - minX) * 0.5f, 100f, (maxZ - minZ) * 0.5f); Bounds val3 = default(Bounds); ((Bounds)(ref val3))..ctor(val, new Vector3(maxX - minX, 400f, maxZ - minZ)); Collider[] array = Physics.OverlapBox(val, val2, Quaternion.identity, -1, (QueryTriggerInteraction)2); foreach (Collider val4 in array) { if ((Object)(object)val4 == (Object)null) { continue; } MineRock5 componentInParent = ((Component)val4).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && hashSet.Add(((Component)componentInParent).gameObject)) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing MineRock5 '{((Object)componentInParent).name}' at {((Component)componentInParent).transform.position}"); ZNetScene.instance.Destroy(((Component)componentInParent).gameObject); num++; continue; } MineRock componentInParent2 = ((Component)val4).GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null && hashSet.Add(((Component)componentInParent2).gameObject)) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing MineRock '{((Object)componentInParent2).name}' at {((Component)componentInParent2).transform.position}"); ZNetScene.instance.Destroy(((Component)componentInParent2).gameObject); num++; continue; } Destructible componentInParent3 = ((Component)val4).GetComponentInParent(); if ((Object)(object)componentInParent3 != (Object)null && hashSet.Add(((Component)componentInParent3).gameObject) && IsRockLikeName(((Object)componentInParent3).name) && (Object)(object)((Component)componentInParent3).GetComponent() == (Object)null) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing rock-like Destructible '{((Object)componentInParent3).name}' at {((Component)componentInParent3).transform.position}"); ZNetScene.instance.Destroy(((Component)componentInParent3).gameObject); num++; } } Renderer[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Renderer val5 in array2) { if ((Object)(object)val5 == (Object)null || !val5.enabled) { continue; } Bounds bounds = val5.bounds; if (!((Bounds)(ref bounds)).Intersects(val3)) { continue; } GameObject val6 = (((Object)(object)((Component)val5).transform.root != (Object)null) ? ((Component)((Component)val5).transform.root).gameObject : ((Component)val5).gameObject); if (hashSet.Add(val6) && !((Object)(object)val6.GetComponentInChildren() != (Object)null)) { string text = ((Object)val6).name.ToLowerInvariant(); bool flag = HasAnyComponentNamed(val6, "MineRock", "MineRock5", "Destructible", "StaticPhysics", "TerrainModifier", "LocationProxy"); bool flag2 = text.Contains("rock") || text.Contains("stone") || text.Contains("cliff") || text.Contains("spike") || text.Contains("obelisk") || text.Contains("monolith"); bool flag3 = text.Contains("pickable") || text.Contains("flint") || text.Contains("branch") || text.Contains("mushroom") || text.Contains("thistle") || text.Contains("berry"); bounds = val5.bounds; float y = ((Bounds)(ref bounds)).size.y; bounds = val5.bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = val5.bounds; float num2 = Mathf.Max(x, ((Bounds)(ref bounds)).size.z); bool flag4 = y >= 2f && num2 >= 0.8f; if ((flag2 || (flag && flag4)) && !flag3) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing renderer blocker '{((Object)val6).name}' at {val6.transform.position}"); ZNetScene.instance.Destroy(val6); num++; } } } MineRock5[] array3 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (MineRock5 val7 in array3) { if (!((Object)(object)val7 == (Object)null) && hashSet.Add(((Component)val7).gameObject)) { Vector3 position = ((Component)val7).transform.position; if (position.x >= minX && position.x <= maxX && position.z >= minZ && position.z <= maxZ) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing MineRock5 '{((Object)val7).name}' at {position}"); ZNetScene.instance.Destroy(((Component)val7).gameObject); num++; } } } MineRock[] array4 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (MineRock val8 in array4) { if (!((Object)(object)val8 == (Object)null) && hashSet.Add(((Component)val8).gameObject)) { Vector3 position2 = ((Component)val8).transform.position; if (position2.x >= minX && position2.x <= maxX && position2.z >= minZ && position2.z <= maxZ) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Removing MineRock '{((Object)val8).name}' at {position2}"); ZNetScene.instance.Destroy(((Component)val8).gameObject); num++; } } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] ClearRocksInPad: {num} rock(s) removed."); if (num == 0) { LogAreaBlockers(val, val2); } } private IEnumerator PostBuildSpikeGuard(FloorPlan plan, Vector3 origin, float rotationDeg = 0f) { //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) TerrainLeveler.GetLeveledAreaBounds(plan, origin, out var minX, out var maxX, out var minZ, out var maxZ, rotationDeg); int totalRemoved = 0; for (int i = 0; i < 4; i++) { totalRemoved += RemoveTallBlockersAboveTerrain(minX, maxX, minZ, maxZ, TerrainLeveler.TargetLevelY); if (i < 3) { yield return (object)new WaitForSeconds(0.75f); } } if (totalRemoved > 0) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] PostBuildSpikeGuard removed {totalRemoved} blocker(s)."); } else { ValheimFloorPlanPlugin.Log.LogInfo((object)"[FloorPlanBuilder] PostBuildSpikeGuard found no blockers."); } } private int RemoveTallBlockersAboveTerrain(float minX, float maxX, float minZ, float maxZ, float referenceY) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) int num = 0; HashSet hashSet = new HashSet(); int num2 = Mathf.CeilToInt((maxX - minX) / 0.5f); int num3 = Mathf.CeilToInt((maxZ - minZ) / 0.5f); float num4 = referenceY + 300f; RaycastHit val = default(RaycastHit); for (int i = 0; i <= num2; i++) { float num5 = ((i == num2) ? maxX : (minX + (float)i * 0.5f)); for (int j = 0; j <= num3; j++) { float num6 = ((j == num3) ? maxZ : (minZ + (float)j * 0.5f)); if (!Physics.Raycast(new Vector3(num5, num4, num6), Vector3.down, ref val, 600f, 2048)) { continue; } RaycastHit[] array = Physics.RaycastAll(new Vector3(num5, num4, num6), Vector3.down, 600f); if (array == null || array.Length == 0) { continue; } Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref b)).point.y.CompareTo(((RaycastHit)(ref a)).point.y)); RaycastHit[] array2 = array; for (int num7 = 0; num7 < array2.Length; num7++) { RaycastHit val2 = array2[num7]; if ((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) { continue; } GameObject val3 = (((Object)(object)((Component)((RaycastHit)(ref val2)).collider).transform.root != (Object)null) ? ((Component)((Component)((RaycastHit)(ref val2)).collider).transform.root).gameObject : ((Component)((RaycastHit)(ref val2)).collider).gameObject); if ((Object)(object)val3 == (Object)null || (Object)(object)val3.GetComponentInChildren() != (Object)null) { continue; } float num8 = ((RaycastHit)(ref val2)).point.y - ((RaycastHit)(ref val)).point.y; if (!(num8 < 0.8f)) { string text = ((Object)val3).name.ToLowerInvariant(); bool flag = text.Contains("pickable") || text.Contains("flint") || text.Contains("branch") || text.Contains("mushroom") || text.Contains("thistle") || text.Contains("berry"); bool flag2 = text.Contains("rock") || text.Contains("stone") || text.Contains("cliff") || text.Contains("spike") || text.Contains("obelisk") || text.Contains("monolith"); bool flag3 = HasAnyComponentNamed(val3, "MineRock", "MineRock5", "Destructible", "StaticPhysics", "TerrainModifier", "LocationProxy"); if ((flag2 || flag3) && !flag && hashSet.Add(val3)) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[FloorPlanBuilder] PostBuildSpikeGuard removing '{((Object)val3).name}' protrusion={num8:F2}m at {val3.transform.position}"); } } break; } } } foreach (GameObject item in hashSet) { ZNetScene.instance.Destroy(item); num++; } return num; } private static bool IsRockLikeName(string name) { if (string.IsNullOrEmpty(name)) { return false; } string text = name.ToLowerInvariant(); return text.Contains("rock") || text.Contains("stone") || text.Contains("boulder") || text.Contains("cliff"); } private static bool HasAnyComponentNamed(GameObject root, params string[] names) { HashSet hashSet = new HashSet(names); Component[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && hashSet.Contains(((object)val).GetType().Name)) { return true; } } return false; } private static void LogAreaBlockers(Vector3 center, Vector3 halfExtents) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); List list = new List(); Bounds val = default(Bounds); ((Bounds)(ref val))..ctor(center, new Vector3(halfExtents.x * 2f, halfExtents.y * 2f, halfExtents.z * 2f)); Collider[] array = Physics.OverlapBox(center, halfExtents, Quaternion.identity, -1, (QueryTriggerInteraction)2); foreach (Collider val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } GameObject val3 = (((Object)(object)((Component)val2).transform.root != (Object)null) ? ((Component)((Component)val2).transform.root).gameObject : ((Component)val2).gameObject); if (!hashSet.Add(val3)) { continue; } bool flag = false; string name = ((Object)val3).name; string text = name.ToLowerInvariant(); if (text.Contains("rock") || text.Contains("stone") || text.Contains("cliff") || text.Contains("location")) { flag = true; } Component[] componentsInChildren = val3.GetComponentsInChildren(true); HashSet hashSet2 = new HashSet(); Component[] array2 = componentsInChildren; foreach (Component val4 in array2) { if ((Object)(object)val4 == (Object)null) { continue; } string name2 = ((object)val4).GetType().Name; switch (name2) { default: if (!(name2 == "LocationProxy")) { continue; } break; case "MineRock": case "MineRock5": case "Destructible": case "StaticPhysics": case "TerrainModifier": break; } hashSet2.Add(name2); flag = true; } if (flag) { string text2 = ((hashSet2.Count > 0) ? string.Join(",", hashSet2) : "none"); list.Add($"{name} @ {val3.transform.position} layer={val3.layer} tags={text2}"); } } Renderer[] array3 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Renderer val5 in array3) { if ((Object)(object)val5 == (Object)null || !val5.enabled) { continue; } Bounds bounds = val5.bounds; if (!((Bounds)(ref bounds)).Intersects(val)) { continue; } GameObject val6 = (((Object)(object)((Component)val5).transform.root != (Object)null) ? ((Component)((Component)val5).transform.root).gameObject : ((Component)val5).gameObject); if (!hashSet.Add(val6)) { continue; } string text3 = ((Object)val6).name.ToLowerInvariant(); bool flag2 = text3.Contains("rock") || text3.Contains("stone") || text3.Contains("cliff") || text3.Contains("spike") || text3.Contains("obelisk") || text3.Contains("monolith"); Component[] componentsInChildren2 = val6.GetComponentsInChildren(true); HashSet hashSet3 = new HashSet(); Component[] array4 = componentsInChildren2; foreach (Component val7 in array4) { if ((Object)(object)val7 == (Object)null) { continue; } string name3 = ((object)val7).GetType().Name; switch (name3) { default: if (!(name3 == "LocationProxy")) { continue; } break; case "MineRock": case "MineRock5": case "Destructible": case "StaticPhysics": case "TerrainModifier": break; } hashSet3.Add(name3); flag2 = true; } if (flag2) { string text4 = ((hashSet3.Count > 0) ? string.Join(",", hashSet3) : "none"); list.Add($"{((Object)val6).name} @ {val6.transform.position} layer={val6.layer} tags={text4} (renderer)"); } } if (list.Count == 0) { ValheimFloorPlanPlugin.Log.LogInfo((object)"[FloorPlanBuilder] ClearRocksInPad diagnostics: no rock/location-like roots found in leveled area."); return; } int num = Mathf.Min(15, list.Count); ValheimFloorPlanPlugin.Log.LogWarning((object)$"[FloorPlanBuilder] ClearRocksInPad diagnostics: {list.Count} candidate blocker root(s) in leveled area. Showing {num}:"); for (int m = 0; m < num; m++) { ValheimFloorPlanPlugin.Log.LogWarning((object)("[FloorPlanBuilder] " + list[m])); } } private IEnumerator WaitForTerrainPhysics(float minX, float maxX, float minZ, float maxZ, float targetY) { float midX = (minX + maxX) * 0.5f; float midZ = (minZ + maxZ) * 0.5f; float rayY = targetY + 300f; Vector3[] probes = (Vector3[])(object)new Vector3[9] { new Vector3(minX, rayY, minZ), new Vector3(midX, rayY, minZ), new Vector3(maxX, rayY, minZ), new Vector3(minX, rayY, midZ), new Vector3(midX, rayY, midZ), new Vector3(maxX, rayY, midZ), new Vector3(minX, rayY, maxZ), new Vector3(midX, rayY, maxZ), new Vector3(maxX, rayY, maxZ) }; float elapsed = 0f; bool firstLog = true; float nextProgressAt = 2f; RaycastHit hit = default(RaycastHit); for (; elapsed < 30f; elapsed += 0.25f) { bool allReady = true; float worstDelta = 0f; StringBuilder sb = (firstLog ? new StringBuilder($"[FloorPlanBuilder] Physics collider probes (targetY={targetY:F2}): ") : null); Vector3[] array = probes; for (int i = 0; i < array.Length; i++) { Vector3 p = array[i]; if (Physics.Raycast(p, Vector3.down, ref hit, 600f, 2048)) { float delta = Mathf.Abs(((RaycastHit)(ref hit)).point.y - targetY); if (delta > worstDelta) { worstDelta = delta; } if (delta > 0.3f) { allReady = false; } sb?.Append($"({p.x:F0},{p.z:F0})={((RaycastHit)(ref hit)).point.y:F2} "); } else { allReady = false; sb?.Append($"({p.x:F0},{p.z:F0})=MISS "); } hit = default(RaycastHit); } if (firstLog) { ValheimFloorPlanPlugin.Log.LogInfo((object)sb.ToString()); firstLog = false; } if (allReady) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Physics collider ready after {elapsed:F1}s (worst delta {worstDelta:F2}m)."); ShowBuildProgress("Waiting for terrain physics... done"); yield break; } if (elapsed >= nextProgressAt) { ShowBuildProgress($"Waiting for terrain physics... {elapsed:F0}s"); nextProgressAt += 2f; } yield return (object)new WaitForSeconds(0.25f); } ValheimFloorPlanPlugin.Log.LogWarning((object)$"[FloorPlanBuilder] Physics collider did not settle within {30f:F0}s — placing anyway."); ShowBuildProgress("Waiting for terrain physics... timeout, placing anyway"); } private IEnumerator PlacePieces(FloorPlan plan, Vector3 origin, float rotationDeg = 0f) { //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) Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null) { ValheimFloorPlanPlugin.Log.LogError((object)"No local player found during placement."); yield break; } int placed = 0; int skipped = 0; Vector3 firstPos = Vector3.zero; int totalPieces = plan.Pieces.Count; int processed = 0; int nextProgressPct = 10; int configuredExternalWallHeight = ValheimFloorPlanPlugin.GetExternalWallHeightForLevel(0); bool useWoodStructure = ValheimFloorPlanPlugin.WallPillarMaterial == ValheimFloorPlanPlugin.StructuralMaterial.Wood; GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive); Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); Mathf.Sin(rotationDeg * ((float)Math.PI / 180f)); RaycastHit hit = default(RaycastHit); foreach (FloorPlanPiece piece in plan.Pieces) { PieceDef def = PieceMap.GetDef(piece.Type); if (def == null) { ValheimFloorPlanPlugin.Log.LogWarning((object)("Unknown piece type '" + piece.Type + "' — skipped.")); skipped++; continue; } if (piece.Type == "Staircase") { int staircasePlaced = PlaceStaircaseComposite(piece, def, origin, rotationDeg, "woodiron_pole", "wood_beam", 2f, player); if (staircasePlaced == 0) { skipped++; } else { placed += staircasePlaced; } processed++; if (totalPieces > 0) { int pct = Mathf.FloorToInt((float)processed * 100f / (float)totalPieces); if (pct >= nextProgressPct) { ShowBuildProgress($"Placing pieces... {processed}/{totalPieces}"); nextProgressPct += 10; } } continue; } int effectivePieceRotation = piece.Rotation; string prefabName = ResolvePrefabName(piece.Type, def.Prefab, useWoodStructure); ZNetScene instance = ZNetScene.instance; GameObject prefab = ((instance != null) ? instance.GetPrefab(prefabName) : null); if ((Object)(object)prefab == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)("Prefab '" + prefabName + "' not found in ZNetScene — skipped.")); skipped++; continue; } int effW = def.EffW(effectivePieceRotation); int effH = def.EffH(effectivePieceRotation); bool isExternal = IsOnPlanOuterPerimeter(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive); if (useWoodStructure && piece.Type == "Wall" && isExternal) { effectivePieceRotation = GetExteriorWallRotation(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, effectivePieceRotation); effW = def.EffW(effectivePieceRotation); effH = def.EffH(effectivePieceRotation); } if (useWoodStructure && piece.Type == "Wall" && piece.WallFace == WallFaceMode.Inner && !isExternal) { effectivePieceRotation = (effectivePieceRotation + 180) % 360; effW = def.EffW(effectivePieceRotation); effH = def.EffH(effectivePieceRotation); } Vector3 pieceCenter = PieceMap.TransformPlanPoint(localX: ((float)piece.Col + (float)effW * 0.5f) * 1f, localZ: ((float)piece.Row + (float)effH * 0.5f) * 1f, origin: origin, y: origin.y, rotationDeg: rotationDeg); float x = pieceCenter.x; float z = pieceCenter.z; float terrainY = TerrainLeveler.TargetLevelY; if (Physics.Raycast(new Vector3(x, TerrainLeveler.TargetLevelY + 300f, z), Vector3.down, ref hit, 600f, 2048)) { terrainY = ((RaycastHit)(ref hit)).point.y; } float y = terrainY + def.YOffset; Vector3 pos = new Vector3(x, y, z); int stackCount = ((!(IsExternalWallOrPillarType(piece.Type) && isExternal)) ? 1 : configuredExternalWallHeight); float stackStepY = GetStackStepY(piece.Type); float pieceYaw = PieceMap.TransformLocalYaw(effectivePieceRotation + def.RotationOffset, rotationDeg); Quaternion rot = Quaternion.Euler(0f, pieceYaw, 0f); Vector3 materialOffset = Vector3.zero; if (((useWoodStructure && (piece.Type == "Wall" || piece.Type == "Pillar")) || piece.Type == "Doorway") && isExternal) { materialOffset = GetWoodPerimeterOffset(piece.Type, piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, rotationDeg); } for (int i = 0; i < stackCount; i++) { Vector3 stackedPos = new Vector3(pos.x, pos.y + stackStepY * (float)i, pos.z) + materialOffset; if (placed == 0) { firstPos = stackedPos; ValheimFloorPlanPlugin.Log.LogInfo((object)$"First piece: type={piece.Type} prefab={prefabName} pos={stackedPos}"); } bool centerWorkbench = piece.Type == "Workbench"; GameObject spawnedGo = SpawnRegisteredPiece(prefab, stackedPos, rot, player, centerWorkbench); if (piece.Type == "Hearth" && i == stackCount - 1) { placed += PlaceHearthBeamRing(piece.Col, piece.Row, effW, effH, origin, rotationDeg, spawnedGo, player); placed += PlaceHearthSupportBeam(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, y, player); } placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } processed++; if (totalPieces > 0) { int pct2 = Mathf.FloorToInt((float)processed * 100f / (float)totalPieces); if (pct2 >= nextProgressPct) { ShowBuildProgress($"Placing pieces... {processed}/{totalPieces}"); nextProgressPct += 10; } } hit = default(RaycastHit); } PieceDef fwDef = PieceMap.GetDef("FlexiWall"); if (fwDef != null && plan.FlexiWalls.Count > 0) { string fwPrefabName = ResolvePrefabName("FlexiWall", fwDef.Prefab, useWoodStructure); ZNetScene instance2 = ZNetScene.instance; GameObject fwPrefab = ((instance2 != null) ? instance2.GetPrefab(fwPrefabName) : null); if ((Object)(object)fwPrefab != (Object)null) { int configuredInternalWallHeight = ValheimFloorPlanPlugin.GetInternalWallHeightForLevel(0); float fwStepY = GetStackStepY("FlexiWall"); RaycastHit fwHit = default(RaycastHit); foreach (FlexiWallPiece fw in plan.FlexiWalls) { int fwStackCount = (IsFlexiWallOnPerimeter(fw, plan, minCol, maxColExclusive, minRow, maxRowExclusive) ? configuredExternalWallHeight : configuredInternalWallHeight); List segments = ComputeFlexiWallSegments(fw, 1f, densifyTightCurve: true); for (int segIdx = 0; segIdx < segments.Count; segIdx++) { FlexiWallSegment seg = segments[segIdx]; Vector3 segCenter = PieceMap.TransformPlanPoint(origin, seg.Lx, seg.Lz, origin.y, rotationDeg); float fwTerrainY = TerrainLeveler.TargetLevelY; if (Physics.Raycast(new Vector3(segCenter.x, TerrainLeveler.TargetLevelY + 300f, segCenter.z), Vector3.down, ref fwHit, 600f, 2048)) { fwTerrainY = ((RaycastHit)(ref fwHit)).point.y; } float fwY = fwTerrainY + fwDef.YOffset; float fwYaw = PieceMap.TransformLocalYaw(0f - seg.YawDeg + (float)fwDef.RotationOffset, rotationDeg); Quaternion fwRot = Quaternion.Euler(0f, fwYaw, 0f); float tanRad = seg.YawDeg * ((float)Math.PI / 180f); Vector2 worldTan = PieceMap.TransformLocalXZ(Mathf.Cos(tanRad), Mathf.Sin(tanRad), rotationDeg); for (int s = 0; s < fwStackCount; s++) { float brickShift = ((segIdx > 0 && s % 2 != 0) ? (-0.5f) : 0f); Vector3 fwPos = new Vector3(segCenter.x + worldTan.x * brickShift, fwY + fwStepY * (float)s, segCenter.z + worldTan.y * brickShift); if (placed == 0) { firstPos = fwPos; ValheimFloorPlanPlugin.Log.LogInfo((object)$"First piece (FlexiWall): prefab={fwPrefabName} pos={fwPos}"); } SpawnRegisteredPiece(fwPrefab, fwPos, fwRot, player); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } if (segIdx == segments.Count - 1) { for (int j = 1; j < fwStackCount; j += 2) { Vector3 extraPos = new Vector3(segCenter.x, fwY + fwStepY * (float)j, segCenter.z); SpawnRegisteredPiece(fwPrefab, extraPos, fwRot, player); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } fwHit = default(RaycastHit); } } } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"Floor plan complete: {placed} placed, {skipped} skipped."); ValheimFloorPlanPlugin.Log.LogInfo((object)$"First piece was at: {firstPos} — player was at: {origin}"); ShowBuildProgress($"Placing pieces... done ({placed}/{totalPieces})"); ((Character)player).Message((MessageType)2, $"Floor plan built: {placed} pieces placed, {skipped} skipped. Check log for position info.", 0, (Sprite)null); } private int PlaceStaircaseComposite(FloorPlanPiece piece, PieceDef def, Vector3 origin, float rotationDeg, string polePrefabName, string stepPrefabName, float unused_flightRise, Player player, float? startBaseYOverride = null, float? targetTopYOverride = null) { //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Unknown result type (might be due to invalid IL or missing references) //IL_04bd: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Unknown result type (might be due to invalid IL or missing references) //IL_04fa: Unknown result type (might be due to invalid IL or missing references) //IL_05de: Unknown result type (might be due to invalid IL or missing references) //IL_05e3: Unknown result type (might be due to invalid IL or missing references) //IL_05e5: Unknown result type (might be due to invalid IL or missing references) //IL_05f4: Unknown result type (might be due to invalid IL or missing references) //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_0600: Unknown result type (might be due to invalid IL or missing references) //IL_06db: Unknown result type (might be due to invalid IL or missing references) //IL_06e0: Unknown result type (might be due to invalid IL or missing references) //IL_0702: Unknown result type (might be due to invalid IL or missing references) //IL_0713: Unknown result type (might be due to invalid IL or missing references) //IL_0718: Unknown result type (might be due to invalid IL or missing references) //IL_071d: Unknown result type (might be due to invalid IL or missing references) //IL_071f: Unknown result type (might be due to invalid IL or missing references) //IL_073d: Unknown result type (might be due to invalid IL or missing references) //IL_074e: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) //IL_075a: Unknown result type (might be due to invalid IL or missing references) float staircaseStepRise = ValheimFloorPlanPlugin.StaircaseStepRise; float staircaseStepAngleDeg = ValheimFloorPlanPlugin.StaircaseStepAngleDeg; float staircaseStepRadius = ValheimFloorPlanPlugin.StaircaseStepRadius; string[] array = new string[7] { "Meadows", "Black Forest", "Swamp", "Mountains", "Plains", "Mistlands", "Ashlands" }; string[] array2 = new string[7] { "#8BC34A", "#2E7D32", "#6D4C41", "#B0BEC5", "#FBC02D", "#7E57C2", "#E64A19" }; ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return 0; } GameObject val = instance.GetPrefab(polePrefabName) ?? instance.GetPrefab("woodiron_pole") ?? instance.GetPrefab("wood_pole_log") ?? instance.GetPrefab("wood_pole2") ?? instance.GetPrefab("wood_pole"); GameObject val2 = instance.GetPrefab(stepPrefabName) ?? instance.GetPrefab("wood_beam") ?? instance.GetPrefab("wood_beam_2") ?? instance.GetPrefab("wood_beam_1") ?? instance.GetPrefab("wood_pole2") ?? instance.GetPrefab("wood_pole") ?? instance.GetPrefab("woodiron_beam_26") ?? instance.GetPrefab("wood_floor_1x1") ?? instance.GetPrefab("wood_floor"); GameObject val3 = instance.GetPrefab("sign") ?? instance.GetPrefab("piece_sign"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)("[Staircase] Prefab '" + polePrefabName + "' or '" + stepPrefabName + "' not found in ZNetScene — skipped.")); return 0; } int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); float num3 = ((float)piece.Col + (float)num * 0.5f) * 1f; float num4 = ((float)piece.Row + (float)num2 * 0.5f) * 1f; Vector3 val4 = PieceMap.TransformPlanPoint(origin, num3, num4, TerrainLeveler.TargetLevelY, rotationDeg); float num5 = TerrainLeveler.TargetLevelY; RaycastHit val5 = default(RaycastHit); if (Physics.Raycast(new Vector3(val4.x, TerrainLeveler.TargetLevelY + 300f, val4.z), Vector3.down, ref val5, 600f, 2048)) { num5 = ((RaycastHit)(ref val5)).point.y; } float num6 = Mathf.Max(num5, startBaseYOverride ?? num5); float num7 = targetTopYOverride ?? (num5 + GetStaircaseTargetRise()); if (num7 <= num6 + 0.01f) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[Staircase] Skipped staircase at ({piece.Col},{piece.Row}): targetTopY={num7:F2} <= startY={num6:F2}."); return 0; } Quaternion rot = Quaternion.Euler(0f, PieceMap.TransformLocalYaw(0f, rotationDeg), 0f); float[] array3 = null; if (ValheimFloorPlanPlugin.ScaffoldingFloors) { int num8 = Mathf.Clamp(ValheimFloorPlanPlugin.ScaffoldingLevels, 1, 3); array3 = new float[num8]; float num9 = num5; for (int i = 0; i < num8; i++) { num9 += Mathf.Max(2f, (float)ValheimFloorPlanPlugin.GetScaffoldingFloorHeightForLevel(i)); array3[i] = num9 + 0.14f; } } int num10 = 0; float num11 = num6; float num12 = 4f; bool flag = true; for (; num11 < num7 - 0.1f; num11 += num12) { Vector3 pos = PieceMap.TransformPlanPoint(origin, num3, num4, num11, rotationDeg); GameObject go = SpawnRegisteredPiece(val, pos, rot, player); num10++; if (flag) { flag = false; if (TryGetObjectBoundsTopY(go, out var topY)) { num12 = Mathf.Max(0.5f, topY - num11); } } } float num13 = (270f - (float)piece.Rotation + 90f + 360f) % 360f; float num14 = num13; float num15 = num6 + -0.15f; float num16 = num13 * ((float)Math.PI / 180f); float num17 = Mathf.Cos(num16) * staircaseStepRadius; float num18 = Mathf.Sin(num16) * staircaseStepRadius; Vector3 val6 = PieceMap.TransformPlanPoint(origin, num3 + num17, num4 + num18, TerrainLeveler.TargetLevelY, rotationDeg); float num19 = num5; RaycastHit val7 = default(RaycastHit); if (Physics.Raycast(new Vector3(val6.x, TerrainLeveler.TargetLevelY + 300f, val6.z), Vector3.down, ref val7, 600f, 2048)) { num19 = ((RaycastHit)(ref val7)).point.y; } num15 = Mathf.Max(num15, num19 - 0.05f); int j = 0; if (array3 != null) { for (; j < array3.Length && num6 > array3[j] - 0.01f; j++) { } } int num20 = 0; int num21 = 0; while (num15 <= num7 + staircaseStepRise) { if (array3 != null && j < array3.Length && num15 >= array3[j] - staircaseStepRise * 0.6f) { num15 = array3[j]; j++; } float num22 = num14 * ((float)Math.PI / 180f); float num23 = Mathf.Cos(num22) * staircaseStepRadius; float num24 = Mathf.Sin(num22) * staircaseStepRadius; float localYawDeg = Mathf.Atan2(num23, num24) * 57.29578f + 90f; Quaternion rot2 = Quaternion.Euler(0f, PieceMap.TransformLocalYaw(localYawDeg, rotationDeg), 0f); Vector3 pos2 = PieceMap.TransformPlanPoint(origin, num3 + num23, num4 + num24, num15, rotationDeg); SpawnRegisteredPiece(val2, pos2, rot2, player); num10++; bool flag2 = num15 >= num7 - 0.01f; bool flag3 = num20 % 2 == 0 || flag2; if ((Object)(object)val3 != (Object)null && flag3) { float num25 = num3 + Mathf.Cos(num22) * (staircaseStepRadius + 1f); float num26 = num4 + Mathf.Sin(num22) * (staircaseStepRadius + 1f); float num27 = Mathf.Atan2(Mathf.Cos(num22), Mathf.Sin(num22)) * 57.29578f; float num28 = num27 + 180f + -10f; float num29 = num28 * ((float)Math.PI / 180f); float localX = num25 + Mathf.Cos(num29) * 0.5f; float localZ = num26 - Mathf.Sin(num29) * 0.5f; float num30 = PieceMap.TransformLocalYaw(num28, rotationDeg); Quaternion rot3 = Quaternion.Euler(0f, num30, 0f); string stairSignFloorLabel = GetStairSignFloorLabel(j, flag2); int num31 = num21 % array.Length; string content = array[num31]; string color = array2[num31]; Vector3 pos3 = PieceMap.TransformPlanPoint(origin, localX, localZ, num15 + staircaseStepRise + 0.375f, rotationDeg); GameObject signGo = SpawnRegisteredPiece(val3, pos3, rot3, player); num10++; SetSignText(signGo, color, content); Vector3 pos4 = PieceMap.TransformPlanPoint(origin, localX, localZ, num15 + staircaseStepRise + 0.725f, rotationDeg); GameObject signGo2 = SpawnRegisteredPiece(val3, pos4, rot3, player); num10++; SetSignText(signGo2, color, stairSignFloorLabel); num21++; } if (num15 >= num7 - 0.01f) { break; } num14 += staircaseStepAngleDeg; num15 += staircaseStepRise; num20++; } return num10; } private static string GetStairSignFloorLabel(int deckIdx, bool isRoofStep) { if (isRoofStep) { return "Roof / Attic"; } if (deckIdx <= 0) { return "Ground Floor"; } return deckIdx switch { 1 => "Floor One", 2 => "Floor Two", _ => "Floor " + deckIdx, }; } private static void SetSignText(GameObject? signGo, string color, string content) { if (!((Object)(object)signGo == (Object)null)) { ZNetView component = signGo.GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val != null) { val.Set("text", "" + content + ""); } } } private static bool TryGetObjectBoundsTopY(GameObject go, out float topY) { //IL_0005: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) bool flag = false; Bounds val = default(Bounds); Collider[] componentsInChildren = go.GetComponentsInChildren(); foreach (Collider val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && val2.enabled) { if (!flag) { val = val2.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val2.bounds); } } } if (!flag) { Renderer[] componentsInChildren2 = go.GetComponentsInChildren(); foreach (Renderer val3 in componentsInChildren2) { if (!((Object)(object)val3 == (Object)null) && val3.enabled) { if (!flag) { val = val3.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val3.bounds); } } } } if (!flag) { topY = go.transform.position.y; return false; } topY = ((Bounds)(ref val)).max.y; return true; } private static HearthOpening? FindFirstOverlappingOpening(int minCol, int minRow, int maxColExclusive, int maxRowExclusive, List openings, int marginCells) { for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; bool flag = maxColExclusive > hearthOpening.MinCol - marginCells && minCol < hearthOpening.MaxColExclusive + marginCells; bool flag2 = maxRowExclusive > hearthOpening.MinRow - marginCells && minRow < hearthOpening.MaxRowExclusive + marginCells; if (flag && flag2) { return hearthOpening; } } return null; } private static string DescribeHearthOpening(HearthOpening? opening) { if (opening == null) { return "another blocked opening"; } if (opening.SourceLevel > 0) { return $"L{opening.SourceLevel} {opening.SourceType} ({opening.MinCol},{opening.MinRow})"; } return $"opening ({opening.MinCol},{opening.MinRow})"; } private IEnumerator PlaceUpperLevelClashSignage(FloorPlan level1Plan, Vector3 origin, float rotationDeg, int targetLevelNumber, List clashLines) { //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) Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null || clashLines.Count == 0) { yield break; } ZNetScene instance = ZNetScene.instance; GameObject polePrefab = ((instance != null) ? instance.GetPrefab("wood_pole_log_4") : null); ZNetScene instance2 = ZNetScene.instance; GameObject signPrefab = ((instance2 != null) ? instance2.GetPrefab("sign") : null); if ((Object)(object)polePrefab == (Object)null || (Object)(object)signPrefab == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[UpperLevels] Clash signage skipped because prefab 'wood_pole_log_4' or 'sign' was not found."); yield break; } GetPlanPieceBounds(level1Plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive); float signageRotationDeg = rotationDeg - 180f; float signageRad = signageRotationDeg * ((float)Math.PI / 180f); float signageSinR = Mathf.Sin(signageRad); float signageCosR = Mathf.Cos(signageRad); float southX = 0f - signageSinR; float southZ = 0f - signageCosR; float eastX = 0f - signageCosR; float eastZ = signageSinR; float localCX = (float)(minCol + maxColExclusive) * 0.5f * 1f; float localCZ = (float)(minRow + maxRowExclusive) * 0.5f * 1f; float halfPlanDepth = (float)(maxRowExclusive - minRow) * 1f * 0.5f; Vector3 planCenter = PieceMap.TransformPlanPoint(origin, localCX, localCZ, origin.y, rotationDeg); float terrainY = TerrainLeveler.TargetLevelY; RaycastHit anchorHit = default(RaycastHit); if (Physics.Raycast(new Vector3(planCenter.x, terrainY + 300f, planCenter.z), Vector3.down, ref anchorHit, 600f, 2048)) { terrainY = ((RaycastHit)(ref anchorHit)).point.y; } float signOX = signageSinR * -0.3f; float signOZ = signageCosR * -0.3f; Quaternion poleRot = Quaternion.Euler(0f, signageRotationDeg, 0f); Quaternion signRot = Quaternion.Euler(0f, signageRotationDeg + 180f, 0f); List signLines = new List { $"L{targetLevelNumber} clashes ({clashLines.Count})" }; for (int i = 0; i < clashLines.Count; i++) { signLines.Add(clashLines[i]); } if (signLines.Count > 6) { int overflow = signLines.Count - 5; signLines = signLines.GetRange(0, 5); signLines.Add($"+{overflow} more (log)"); } float rowDist = halfPlanDepth + 1.5f; float levelOffset = (float)(targetLevelNumber - 2) * 2.4f; float wx = planCenter.x + southX * rowDist + eastX * levelOffset; float wz = planCenter.z + southZ * rowDist + eastZ * levelOffset; float poleTerrainY = terrainY; RaycastHit poleHit = default(RaycastHit); if (Physics.Raycast(new Vector3(wx, terrainY + 300f, wz), Vector3.down, ref poleHit, 600f, 2048)) { poleTerrainY = ((RaycastHit)(ref poleHit)).point.y; } SpawnScaffoldPole(polePrefab, new Vector3(wx, poleTerrainY + 2f, wz), poleRot, player); yield return (object)new WaitForSeconds(0.05f); float signTopY = poleTerrainY + 4f - 0.35f; for (int j = 0; j < signLines.Count; j++) { float signY = signTopY - (float)j * 0.58f; Vector3 signPos = new Vector3(wx + signOX, signY, wz + signOZ); GameObject signGo = SpawnRegisteredPiece(signPrefab, signPos, signRot, player); string signColor = ((j == 0) ? "orange" : "white"); SetSignText(signGo, signColor, signLines[j]); yield return (object)new WaitForSeconds(0.05f); } ValheimFloorPlanPlugin.Log.LogWarning((object)$"[UpperLevels] Placed clash signage for Level {targetLevelNumber}: {clashLines.Count} clash entry(s), 1 pole, {signLines.Count} sign(s)."); } private static Vector2 StairDirectionFromRotation(int rotation) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) int num = (rotation % 360 + 360) % 360; num = (num + 45) / 90 * 90; return (Vector2)((num % 360) switch { 0 => new Vector2(0f, 1f), 90 => new Vector2(1f, 0f), 180 => new Vector2(0f, -1f), _ => new Vector2(-1f, 0f), }); } private static int StairRotationFromDirection(Vector2 dir) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) if (Mathf.Abs(dir.x) > Mathf.Abs(dir.y)) { return (dir.x >= 0f) ? 90 : 270; } return (!(dir.y >= 0f)) ? 180 : 0; } private static GameObject? ResolveStaircasePrefab(string role, string preferredName, string[] fallbackNames, string[] tokenHints) { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return null; } GameObject prefab = instance.GetPrefab(preferredName); if ((Object)(object)prefab != (Object)null) { return prefab; } foreach (string text in fallbackNames) { if (!(text == preferredName)) { GameObject prefab2 = instance.GetPrefab(text); if ((Object)(object)prefab2 != (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)("[Staircase] Using fallback " + role + " prefab '" + text + "' (missing '" + preferredName + "').")); return prefab2; } } } GameObject val = FindPrefabByTokenHints(instance, tokenHints); if ((Object)(object)val != (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)("[Staircase] Using discovered " + role + " prefab '" + ((Object)val).name + "' (missing '" + preferredName + "').")); } return val; } private static GameObject? FindPrefabByTokenHints(ZNetScene scene, string[] tokenHints) { if ((Object)(object)scene == (Object)null || scene.m_prefabs == null || tokenHints == null || tokenHints.Length == 0) { return null; } for (int i = 0; i < scene.m_prefabs.Count; i++) { GameObject val = scene.m_prefabs[i]; if ((Object)(object)val == (Object)null) { continue; } string text = ((((Object)val).name == null) ? string.Empty : ((Object)val).name.ToLowerInvariant()); bool flag = true; foreach (string value in tokenHints) { if (!string.IsNullOrEmpty(value) && !text.Contains(value)) { flag = false; break; } } if (flag) { return val; } } for (int k = 0; k < scene.m_prefabs.Count; k++) { GameObject val2 = scene.m_prefabs[k]; if ((Object)(object)val2 == (Object)null) { continue; } string text2 = ((((Object)val2).name == null) ? string.Empty : ((Object)val2).name.ToLowerInvariant()); foreach (string value2 in tokenHints) { if (!string.IsNullOrEmpty(value2) && text2.Contains(value2)) { return val2; } } } return null; } private static int GetTopClimbableFloorIndex(int scaffoldLevels) { return ValheimFloorPlanPlugin.OpenTop ? (scaffoldLevels - 2) : (scaffoldLevels - 1); } private static float GetStaircaseTargetRise() { if (ValheimFloorPlanPlugin.ScaffoldingFloors) { int num = Mathf.Clamp(ValheimFloorPlanPlugin.ScaffoldingLevels, 1, 3); int topClimbableFloorIndex = GetTopClimbableFloorIndex(num); int num2 = Mathf.Min((ValheimFloorPlanPlugin.StaircaseReachMode != ValheimFloorPlanPlugin.StaircaseReachModeOption.AllTheWay) ? 1 : num, topClimbableFloorIndex + 1); if (num2 <= 0) { return 0f; } float num3 = 0f; for (int i = 0; i < num2; i++) { num3 += Mathf.Max(2f, (float)ValheimFloorPlanPlugin.GetScaffoldingFloorHeightForLevel(i)); } return Mathf.Max(2f, num3); } return 4f; } private static float GetUpperLevelStaircaseTargetTopY(float levelDeckY, int floorIndex, int scaffoldLevels) { int topClimbableFloorIndex = GetTopClimbableFloorIndex(scaffoldLevels); if (ValheimFloorPlanPlugin.StaircaseReachMode == ValheimFloorPlanPlugin.StaircaseReachModeOption.AllTheWay) { return (topClimbableFloorIndex >= 0) ? GetDeckYForScaffoldLevel(topClimbableFloorIndex) : levelDeckY; } int num = floorIndex + 1; if (num > topClimbableFloorIndex) { return levelDeckY; } return GetDeckYForScaffoldLevel(num); } private void RemoveInterferingUpperFloorPieces(Vector3 origin, float rotationDeg, float landingACol, float landingARow, float landingBCol, float landingBRow, float targetY) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0113: 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_00f9: 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_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) Vector3 val = PieceMap.TransformPlanPoint(origin, landingACol * 1f, landingARow * 1f, targetY, rotationDeg); Vector3 val2 = PieceMap.TransformPlanPoint(origin, landingBCol * 1f, landingBRow * 1f, targetY, rotationDeg); for (int num = _lastPlaced.Count - 1; num >= 0; num--) { GameObject val3 = _lastPlaced[num]; if ((Object)(object)val3 == (Object)null) { _lastPlaced.RemoveAt(num); } else { string text = ((Object)val3).name.ToLowerInvariant(); if (text.Contains("wood_floor") || text.Contains("floor_1x1")) { Vector3 position = val3.transform.position; if (!(Mathf.Abs(position.y - targetY) > 0.35f) && ((Mathf.Abs(position.x - val.x) <= 0.45f && Mathf.Abs(position.z - val.z) <= 0.45f) || (Mathf.Abs(position.x - val2.x) <= 0.45f && Mathf.Abs(position.z - val2.z) <= 0.45f))) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val3); } else { Object.Destroy((Object)(object)val3); } _lastPlaced.RemoveAt(num); } } } } } private static void CenterPieceOnRenderedBoundsXZ(GameObject go, Vector3 desiredCenter) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_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_0116: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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_00a6: 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_00c8: 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_0212: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) if (!TryGetBoundsCenterXZ(go, includeColliders: true, includeRenderers: false, out Vector3 center, out string source) && !TryGetBoundsCenterXZ(go, includeColliders: false, includeRenderers: true, out center, out source)) { return; } float num = center.x - go.transform.position.x; float num2 = center.z - go.transform.position.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 > 5f) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[WorkbenchTest] skipped recenter prefab={((Object)go).name} source={source} sourceDistance={num3:F3} actual=({center.x:F2}, {center.z:F2}) spawn=({go.transform.position.x:F2}, {go.transform.position.z:F2})"); return; } Vector3 position = go.transform.position; position.x += desiredCenter.x - center.x; position.z += desiredCenter.z - center.z; float num4 = position.x - go.transform.position.x; float num5 = position.z - go.transform.position.z; if (Mathf.Abs(num4) > 0.001f || Mathf.Abs(num5) > 0.001f) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[WorkbenchTest] recentered prefab={((Object)go).name} source={source} dx={num4:F3} dz={num5:F3} desired=({desiredCenter.x:F2}, {desiredCenter.z:F2}) actual=({center.x:F2}, {center.z:F2})"); } go.transform.position = position; } private static bool TryGetBoundsCenterXZ(GameObject go, bool includeColliders, bool includeRenderers, out Vector3 center, out string source) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_0131: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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) bool flag = false; Bounds val = default(Bounds); if (includeColliders) { Collider[] componentsInChildren = go.GetComponentsInChildren(); foreach (Collider val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && val2.enabled) { if (!flag) { val = val2.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val2.bounds); } } } if (flag) { center = ((Bounds)(ref val)).center; source = "collider"; return true; } } if (includeRenderers) { Renderer[] componentsInChildren2 = go.GetComponentsInChildren(); foreach (Renderer val3 in componentsInChildren2) { if (!((Object)(object)val3 == (Object)null) && val3.enabled) { if (!flag) { val = val3.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val3.bounds); } } } if (flag) { center = ((Bounds)(ref val)).center; source = "renderer"; return true; } } center = go.transform.position; source = "none"; return false; } private static void ShowBuildProgress(string message) { ValheimFloorPlanPlugin.ShowProgressMessage(message); } private static string ResolveWorkbenchTestPrefab(int variantIndex) { if (TEST_WORKBENCH_PREFABS.Length == 0) { return "piece_workbench"; } int num = Mathf.Abs(variantIndex) % TEST_WORKBENCH_PREFABS.Length; return TEST_WORKBENCH_PREFABS[num]; } private static bool IsExternalWallOrPillarType(string type) { return type == "Wall" || type == "Pillar"; } private static string ResolvePrefabName(string type, string defaultPrefab, bool useWoodStructure) { if (!useWoodStructure) { return defaultPrefab; } return type switch { "Wall" => "wood_wall_half", "FlexiWall" => "wood_wall_quarter", "Pillar" => "wood_pole_log", _ => defaultPrefab, }; } private static float GetStackStepY(string type) { if (type == "Wall" || type == "FlexiWall") { return 1f; } if (type == "Pillar") { return 2f; } return 0f; } private static int FlexiArcSegmentCount(float radius, float totalAngleAbs, float segWidth, bool densifyTightCurve) { float num = segWidth / radius; if (densifyTightCurve && radius < 4f) { float num2 = Mathf.Max((float)Math.PI / 15f, segWidth * radius / 8f); num = Mathf.Min(num, num2); } return Mathf.Max(1, Mathf.RoundToInt(totalAngleAbs / num)); } private static List ComputeFlexiWallSegments(FlexiWallPiece fw, float segWidth, bool densifyTightCurve = false) { List list = new List(); float num = fw.X2 - fw.X1; float num2 = fw.Y2 - fw.Y1; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 < 0.02f) { float num4 = (fw.X1 + fw.Mx) * 0.5f; float num5 = (fw.Y1 + fw.My) * 0.5f; float num6 = Mathf.Sqrt((fw.X1 - num4) * (fw.X1 - num4) + (fw.Y1 - num5) * (fw.Y1 - num5)); if (num6 < 0.01f) { return list; } float num7 = (float)Math.PI * 2f; float num8 = Mathf.Atan2(fw.Y1 - num5, fw.X1 - num4); int num9 = FlexiArcSegmentCount(num6, num7, segWidth, densifyTightCurve); for (int i = 0; i < num9; i++) { float num10 = ((float)i + 0.5f) / (float)num9; float num11 = num8 + num10 * num7; list.Add(new FlexiWallSegment(num4 + num6 * Mathf.Cos(num11), num5 + num6 * Mathf.Sin(num11), Mathf.Atan2(Mathf.Cos(num11), 0f - Mathf.Sin(num11)) * 57.29578f)); } return list; } float num12 = (fw.Mx - fw.X1) * num2 - (fw.My - fw.Y1) * num; if (Mathf.Abs(num12) / num3 < 0.05f) { int num13 = Mathf.Max(1, Mathf.RoundToInt(num3 / segWidth)); float num14 = num / num3; float num15 = num2 / num3; float yawDeg = Mathf.Atan2(num15, num14) * 57.29578f; for (int j = 0; j < num13; j++) { float num16 = ((float)j + 0.5f) / (float)num13; list.Add(new FlexiWallSegment(fw.X1 + num16 * num, fw.Y1 + num16 * num2, yawDeg)); } return list; } float x = fw.X1; float y = fw.Y1; float mx = fw.Mx; float my = fw.My; float x2 = fw.X2; float y2 = fw.Y2; float num17 = 2f * (x * (my - y2) + mx * (y2 - y) + x2 * (y - my)); if (Mathf.Abs(num17) < 1E-06f) { return list; } float num18 = x * x + y * y; float num19 = mx * mx + my * my; float num20 = x2 * x2 + y2 * y2; float num21 = (num18 * (my - y2) + num19 * (y2 - y) + num20 * (y - my)) / num17; float num22 = (num18 * (x2 - mx) + num19 * (x - x2) + num20 * (mx - x)) / num17; float num23 = Mathf.Sqrt((x - num21) * (x - num21) + (y - num22) * (y - num22)); float num24 = Mathf.Atan2(y - num22, x - num21); float num25 = Mathf.Atan2(y2 - num22, x2 - num21); float num26 = Mathf.Atan2(my - num22, mx - num21); float num27 = (float)Math.PI * 2f; float num28 = ((num25 - num24) % num27 + num27) % num27; float num29 = ((num26 - num24) % num27 + num27) % num27; bool flag = num29 < num28; float num30 = (flag ? num28 : (0f - (num27 - num28))); int num31 = FlexiArcSegmentCount(num23, Mathf.Abs(num30), segWidth, densifyTightCurve); for (int k = 0; k < num31; k++) { float num32 = ((float)k + 0.5f) / (float)num31; float num33 = num24 + num32 * num30; float lx = num21 + num23 * Mathf.Cos(num33); float lz = num22 + num23 * Mathf.Sin(num33); float num34 = (flag ? (0f - Mathf.Sin(num33)) : Mathf.Sin(num33)); float num35 = (flag ? Mathf.Cos(num33) : (0f - Mathf.Cos(num33))); list.Add(new FlexiWallSegment(lx, lz, Mathf.Atan2(num35, num34) * 57.29578f)); } return list; } private static bool IsOnPlanOuterPerimeter(int col, int row, int effW, int effH, int minCol, int maxColExclusive, int minRow, int maxRowExclusive) { return col <= minCol || row <= minRow || col + effW >= maxColExclusive || row + effH >= maxRowExclusive; } private static bool IsFlexiWallOnPerimeter(FlexiWallPiece fw, FloorPlan plan, int minCol, int maxColExclusive, int minRow, int maxRowExclusive) { float[] array = new float[3] { fw.X1, fw.X2, fw.Mx }; float[] array2 = new float[3] { fw.Y1, fw.Y2, fw.My }; for (int i = 0; i < 3; i++) { if (array[i] <= (float)minCol + 0.5f) { return true; } if (array[i] >= (float)maxColExclusive - 0.5f) { return true; } if (array2[i] <= (float)minRow + 0.5f) { return true; } if (array2[i] >= (float)maxRowExclusive - 0.5f) { return true; } } float num = Mathf.Sqrt((fw.X2 - fw.X1) * (fw.X2 - fw.X1) + (fw.Y2 - fw.Y1) * (fw.Y2 - fw.Y1)); if (num < 0.02f) { float num2 = (fw.X1 + fw.Mx) * 0.5f; float num3 = (fw.Y1 + fw.My) * 0.5f; float num4 = Mathf.Sqrt((fw.X1 - num2) * (fw.X1 - num2) + (fw.Y1 - num3) * (fw.Y1 - num3)); if (num2 - num4 <= (float)minCol + 0.5f) { return true; } if (num2 + num4 >= (float)maxColExclusive - 0.5f) { return true; } if (num3 - num4 <= (float)minRow + 0.5f) { return true; } if (num3 + num4 >= (float)maxRowExclusive - 0.5f) { return true; } } List list = ComputeFlexiWallSegments(fw, 1f); if (list.Count == 0) { return true; } int[] array3 = new int[3] { list.Count / 4, list.Count / 2, list.Count * 3 / 4 }; int[] array4 = array3; foreach (int num5 in array4) { int index = Mathf.Clamp(num5, 0, list.Count - 1); FlexiWallSegment flexiWallSegment = list[index]; float num6 = flexiWallSegment.YawDeg * ((float)Math.PI / 180f); float num7 = 0f - Mathf.Sin(num6); float num8 = Mathf.Cos(num6); bool flag = IsPointOverFloor(flexiWallSegment.Lx + num7 * 1f, flexiWallSegment.Lz + num8 * 1f, plan); bool flag2 = IsPointOverFloor(flexiWallSegment.Lx - num7 * 1f, flexiWallSegment.Lz - num8 * 1f, plan); if (!(flag && flag2)) { return true; } } return false; } private static bool IsPointOverFloor(float x, float z, FloorPlan plan) { foreach (FloorPlanPiece piece in plan.Pieces) { if (piece.Type != "Floor2x2" && piece.Type != "Floor1x1") { continue; } PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); if (x >= (float)piece.Col && x < (float)(piece.Col + num) && z >= (float)piece.Row && z < (float)(piece.Row + num2)) { return true; } } } return false; } private static int GetExteriorWallRotation(int col, int row, int effW, int effH, int minCol, int maxColExclusive, int minRow, int maxRowExclusive, int fallbackRotation) { if (effW == 1) { if (col <= minCol) { return 270; } if (col + effW >= maxColExclusive) { return 90; } } else if (effH == 1) { if (row <= minRow) { return 180; } if (row + effH >= maxRowExclusive) { return 0; } } return fallbackRotation; } private static Vector3 GetWoodPerimeterOffset(string pieceType, int col, int row, int effW, int effH, int minCol, int maxColExclusive, int minRow, int maxRowExclusive, float planRotationDeg) { //IL_00f4: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_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_0125: Unknown result type (might be due to invalid IL or missing references) float num = ((pieceType == "Pillar") ? 0.4f : 0.35f); float num2 = 0f; float num3 = 0f; if (col <= minCol) { num2 -= 1f; } if (col + effW >= maxColExclusive) { num2 += 1f; } if (row <= minRow) { num3 -= 1f; } if (row + effH >= maxRowExclusive) { num3 += 1f; } if (pieceType == "Wall" || pieceType == "Doorway") { if (effH == 1) { num2 = 0f; } else { num3 = 0f; } } if (num2 == 0f && num3 == 0f) { return Vector3.zero; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(num2 * num, 0f, num3 * num); Vector2 val2 = PieceMap.TransformLocalXZ(val.x, val.z, planRotationDeg); return new Vector3(val2.x, 0f, val2.y); } private static void GetPlanPieceBounds(FloorPlan plan, out int minCol, out int maxColExclusive, out int minRow, out int maxRowExclusive) { minCol = int.MaxValue; maxColExclusive = int.MinValue; minRow = int.MaxValue; maxRowExclusive = int.MinValue; foreach (FloorPlanPiece piece in plan.Pieces) { int num = 1; int num2 = 1; PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { num = def.EffW(piece.Rotation); num2 = def.EffH(piece.Rotation); } if (piece.Col < minCol) { minCol = piece.Col; } if (piece.Col + num > maxColExclusive) { maxColExclusive = piece.Col + num; } if (piece.Row < minRow) { minRow = piece.Row; } if (piece.Row + num2 > maxRowExclusive) { maxRowExclusive = piece.Row + num2; } } foreach (FlexiWallPiece flexiWall in plan.FlexiWalls) { float num3 = Mathf.Min(flexiWall.X1, Mathf.Min(flexiWall.X2, flexiWall.Mx)); float num4 = Mathf.Max(flexiWall.X1, Mathf.Max(flexiWall.X2, flexiWall.Mx)); float num5 = Mathf.Min(flexiWall.Y1, Mathf.Min(flexiWall.Y2, flexiWall.My)); float num6 = Mathf.Max(flexiWall.Y1, Mathf.Max(flexiWall.Y2, flexiWall.My)); int num7 = Mathf.FloorToInt(num3); int num8 = Mathf.CeilToInt(num4); int num9 = Mathf.FloorToInt(num5); int num10 = Mathf.CeilToInt(num6); if (num7 < minCol) { minCol = num7; } if (num8 > maxColExclusive) { maxColExclusive = num8; } if (num9 < minRow) { minRow = num9; } if (num10 > maxRowExclusive) { maxRowExclusive = num10; } } if (minCol == int.MaxValue) { minCol = 0; minRow = 0; maxColExclusive = plan.Cols; maxRowExclusive = plan.Rows; } } private IEnumerator PlaceRoofScaffolding(FloorPlan plan, 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) ValheimFloorPlanPlugin.RefreshScaffoldingRules(); Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null) { yield break; } ZNetScene instance = ZNetScene.instance; GameObject vertPrefab = ((instance != null) ? instance.GetPrefab("woodiron_pole") : null); ZNetScene instance2 = ZNetScene.instance; GameObject horizPrefab = ((instance2 != null) ? instance2.GetPrefab("woodiron_beam") : null); ZNetScene instance3 = ZNetScene.instance; GameObject floor2Prefab = ((instance3 != null) ? instance3.GetPrefab("wood_floor") : null); ZNetScene instance4 = ZNetScene.instance; GameObject floor1Prefab = ((instance4 != null) ? instance4.GetPrefab("wood_floor_1x1") : null); ZNetScene instance5 = ZNetScene.instance; GameObject roofTopPrefab = ((instance5 != null) ? instance5.GetPrefab("wood_roof_top") : null); ZNetScene instance6 = ZNetScene.instance; GameObject topLowerRoofPrefab = ((instance6 != null) ? instance6.GetPrefab("wood_roof") : null); ZNetScene instance7 = ZNetScene.instance; GameObject topLowerSupportPrefab = ((instance7 != null) ? instance7.GetPrefab("woodiron_beam_26") : null); ZNetScene instance8 = ZNetScene.instance; GameObject chimneyWall2Prefab = ((instance8 != null) ? instance8.GetPrefab("wood_wall_half") : null); ZNetScene instance9 = ZNetScene.instance; GameObject chimneyWall1Prefab = ((instance9 != null) ? instance9.GetPrefab("wood_wall_1x1") : null); ZNetScene instance10 = ZNetScene.instance; GameObject chimneyRoofPrefab = ((instance10 != null) ? instance10.GetPrefab("wood_roof") : null); if ((Object)(object)vertPrefab == (Object)null || (Object)(object)horizPrefab == (Object)null || (Object)(object)floor2Prefab == (Object)null || (Object)(object)floor1Prefab == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[Scaffolding] Prefab 'woodiron_pole', 'woodiron_beam', 'wood_floor', or 'wood_floor_1x1' not found in ZNetScene — roof scaffolding skipped."); yield break; } GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive); float lMinX = (float)minCol * 1f; float lMaxX = (float)maxColExclusive * 1f; float lMinZ = (float)minRow * 1f; float lMaxZ = (float)maxRowExclusive * 1f; float width = lMaxX - lMinX; float depth = lMaxZ - lMinZ; float perimeter = 2f * (width + depth); int scaffoldLevels = Mathf.Clamp(ValheimFloorPlanPlugin.ScaffoldingLevels, 1, 3); bool useTransverseScaffoldingBeams = ValheimFloorPlanPlugin.GetEffectiveTransverseScaffoldingBeams(scaffoldLevels); bool useLongitudinalScaffoldingBeams = ValheimFloorPlanPlugin.GetEffectiveLongitudinalScaffoldingBeams(scaffoldLevels); float[] scaffoldFloorHeights = new float[scaffoldLevels]; for (int level = 0; level < scaffoldLevels; level++) { scaffoldFloorHeights[level] = Mathf.Max(2f, (float)ValheimFloorPlanPlugin.GetScaffoldingFloorHeightForLevel(level)); } float scaffoldBaseY = TerrainLeveler.TargetLevelY; float localCenterX = (lMinX + lMaxX) * 0.5f; float localCenterZ = (lMinZ + lMaxZ) * 0.5f; Vector3 centerWorld = PieceMap.TransformPlanPoint(origin, localCenterX, localCenterZ, scaffoldBaseY + scaffoldFloorHeights[0] * 0.5f, rotationDeg); List poleParams = new List { 0f, width, width + depth, 2f * width + depth }; List doorJambParams = new List(); List doorEdgeSpans = new List(); List blockedLongitudinalLocalXs = new List(); List blockedTransverseLocalZs = new List(); List doorCenters = new List(); foreach (FloorPlanPiece piece in plan.Pieces) { if (piece.Type != "Doorway") { continue; } PieceDef def = PieceMap.GetDef(piece.Type); if (def == null) { continue; } int effW = def.EffW(piece.Rotation); int effH = def.EffH(piece.Rotation); bool onSouth = piece.Row <= minRow; bool onNorth = piece.Row + effH >= maxRowExclusive; bool onEast = piece.Col + effW >= maxColExclusive; bool onWest = piece.Col <= minCol; int edgeCount = (onSouth ? 1 : 0) + (onNorth ? 1 : 0) + (onEast ? 1 : 0) + (onWest ? 1 : 0); if (edgeCount == 1) { doorCenters.Add(new Vector2(((float)piece.Col + (float)effW * 0.5f) * 1f, ((float)piece.Row + (float)effH * 0.5f) * 1f)); float tLeft = -1f; float tRight = -1f; if (onSouth) { tLeft = (float)(piece.Col - minCol) * 1f; tRight = (float)(piece.Col + effW - minCol) * 1f; blockedLongitudinalLocalXs.Add(new ScaffoldDoorSpan((float)(piece.Col - minCol) * 1f, (float)(piece.Col + effW - minCol) * 1f)); } else if (onNorth) { tLeft = width + depth + (float)(maxColExclusive - piece.Col - effW) * 1f; tRight = width + depth + (float)(maxColExclusive - piece.Col) * 1f; blockedLongitudinalLocalXs.Add(new ScaffoldDoorSpan((float)(piece.Col - minCol) * 1f, (float)(piece.Col + effW - minCol) * 1f)); } else if (onEast) { tLeft = width + (float)(piece.Row + effH - minRow) * 1f; tRight = width + (float)(piece.Row - minRow) * 1f; blockedTransverseLocalZs.Add(new ScaffoldDoorSpan((float)(piece.Row - minRow) * 1f, (float)(piece.Row + effH - minRow) * 1f)); } else if (onWest) { tLeft = 2f * width + depth + (float)(maxRowExclusive - piece.Row - effH) * 1f; tRight = 2f * width + depth + (float)(maxRowExclusive - piece.Row) * 1f; blockedTransverseLocalZs.Add(new ScaffoldDoorSpan((float)(piece.Row - minRow) * 1f, (float)(piece.Row + effH - minRow) * 1f)); } if (tLeft >= 0f && tLeft <= perimeter) { poleParams.Add(tLeft); doorJambParams.Add(tLeft); } if (tRight >= 0f && tRight <= perimeter) { poleParams.Add(tRight); doorJambParams.Add(tRight); } if (tLeft >= 0f && tLeft <= perimeter && tRight >= 0f && tRight <= perimeter) { float spanMin = Mathf.Min(tLeft, tRight); float spanMax = Mathf.Max(tLeft, tRight); doorEdgeSpans.Add(new ScaffoldDoorSpan(spanMin, spanMax)); } } } poleParams.Sort(); ScaffoldDedup(poleParams, 0.5f); doorJambParams.Sort(); ScaffoldDedup(doorJambParams, 0.5f); poleParams.Sort(); List supportFurnitureExclusions = BuildScaffoldFurnitureExclusions(plan); List hearthOpenings = BuildHearthOpenings(plan); float chimneyStartY = scaffoldBaseY + 3f; List edgeJoinParams = new List(); float[] cornerT = new float[4] { 0f, width, width + depth, 2f * width + depth }; int[] edgeFrom = new int[4] { 0, 1, 2, 3 }; int[] edgeTo = new int[4] { 1, 2, 3, 0 }; for (int e = 0; e < 4; e++) { Vector2 aLocal = ScaffoldParamToLocal(cornerT[edgeFrom[e]], lMinX, lMaxX, lMinZ, lMaxZ, perimeter); Vector2 bLocal = ScaffoldParamToLocal(cornerT[edgeTo[e]], lMinX, lMaxX, lMinZ, lMaxZ, perimeter); float edgeDist = Vector2.Distance(aLocal, bLocal); for (float joinDist = 4f; joinDist < edgeDist - 0.05f; joinDist += 4f) { float joinT = cornerT[edgeFrom[e]] + joinDist; if (joinT >= perimeter) { joinT -= perimeter; } if (!IsNearAnyParam(joinT, poleParams, 0.25f) && !IsNearAnyParam(joinT, edgeJoinParams, 0.25f) && !IsWithinAnyDoorSpan(joinT, doorEdgeSpans, 0.25f)) { edgeJoinParams.Add(joinT); } } } if (edgeJoinParams.Count > 0) { poleParams.AddRange(edgeJoinParams); poleParams.Sort(); ScaffoldDedup(poleParams, 0.5f); } List> fwArcChains = new List>(); List fwPoleLocals = new List(); foreach (FlexiWallPiece fw in plan.FlexiWalls) { List segs = ComputeFlexiWallSegments(fw, 1f); if (segs.Count == 0 || !IsFlexiWallOnPerimeter(fw, plan, minCol, maxColExclusive, minRow, maxRowExclusive)) { continue; } fwArcChains.Add(segs); Vector2 startPt = new Vector2(segs[0].Lx, segs[0].Lz); if (!IsNearAnyPole(startPt, fwPoleLocals, 1.4f)) { fwPoleLocals.Add(startPt); } float accumulated = 0f; for (int si = 1; si < segs.Count; si++) { float step = Mathf.Sqrt((segs[si].Lx - segs[si - 1].Lx) * (segs[si].Lx - segs[si - 1].Lx) + (segs[si].Lz - segs[si - 1].Lz) * (segs[si].Lz - segs[si - 1].Lz)); accumulated += step; if (accumulated >= 3.9f) { Vector2 candidate = new Vector2(segs[si].Lx, segs[si].Lz); if (!IsNearAnyPole(candidate, fwPoleLocals, 1.4f)) { fwPoleLocals.Add(candidate); } accumulated = 0f; } } Vector2 endPt = new Vector2(segs[segs.Count - 1].Lx, segs[segs.Count - 1].Lz); if (!IsNearAnyPole(endPt, fwPoleLocals, 1.4f)) { fwPoleLocals.Add(endPt); } } List fwArcPolygon = null; if (fwArcChains.Count > 0) { fwArcPolygon = BuildArcPerimeterPolygon(fwArcChains); } int placed = 0; float currentLevelBaseY = scaffoldBaseY; for (int i = 0; i < scaffoldLevels; i++) { float scaffoldFloorHeight = scaffoldFloorHeights[i]; float levelTopY = currentLevelBaseY + scaffoldFloorHeight; float deckY = levelTopY + 0.14f; List deckOpenings = BuildScaffoldDeckOpenings(plan, deckY, chimneyStartY); bool useFwScaffolding = fwArcChains.Count > 0; bool isTopmostLevel = i == scaffoldLevels - 1; bool noRoofTopLevel = isTopmostLevel && ValheimFloorPlanPlugin.OpenTop; List occupiedPoleLocals = new List(poleParams.Count + 32); if (!useFwScaffolding) { for (int pi = 0; pi < poleParams.Count; pi++) { occupiedPoleLocals.Add(ScaffoldParamToLocal(poleParams[pi], lMinX, lMaxX, lMinZ, lMaxZ, perimeter)); } } occupiedPoleLocals.Add(new Vector2(localCenterX, localCenterZ)); if (useFwScaffolding) { foreach (Vector2 fwLocal in fwPoleLocals) { occupiedPoleLocals.Add(fwLocal); } } if (!noRoofTopLevel && !useFwScaffolding) { foreach (float t in poleParams) { Vector2 local = ScaffoldParamToLocal(t, lMinX, lMaxX, lMinZ, lMaxZ, perimeter); Vector3 polePos = PieceMap.TransformPlanPoint(origin, local.x, local.y, currentLevelBaseY + scaffoldFloorHeight * 0.5f, rotationDeg); placed += SpawnScaffoldColumn(vertPrefab, polePos, Quaternion.Euler(0f, rotationDeg, 0f), player, scaffoldFloorHeight, 2f); if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } else if (!noRoofTopLevel) { foreach (Vector2 fwLocal2 in fwPoleLocals) { Vector3 fwPolePos = PieceMap.TransformPlanPoint(origin, fwLocal2.x, fwLocal2.y, currentLevelBaseY + scaffoldFloorHeight * 0.5f, rotationDeg); placed += SpawnScaffoldColumn(vertPrefab, fwPolePos, Quaternion.Euler(0f, rotationDeg, 0f), player, scaffoldFloorHeight, 2f); if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } if (!noRoofTopLevel) { Vector3 centerPolePos = PieceMap.TransformPlanPoint(origin, localCenterX, localCenterZ, currentLevelBaseY + scaffoldFloorHeight * 0.5f, rotationDeg); placed += SpawnScaffoldColumn(vertPrefab, centerPolePos, Quaternion.Euler(0f, rotationDeg, 0f), player, scaffoldFloorHeight, 2f); if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } if (!noRoofTopLevel && !useFwScaffolding) { Vector3[] cornerTops = (Vector3[])(object)new Vector3[4]; for (int ci = 0; ci < 4; ci++) { Vector2 cl = ScaffoldParamToLocal(cornerT[ci], lMinX, lMaxX, lMinZ, lMaxZ, perimeter); cornerTops[ci] = PieceMap.TransformPlanPoint(origin, cl.x, cl.y, levelTopY, rotationDeg); } for (int j = 0; j < 4; j++) { Vector2 localEdgeA = ScaffoldParamToLocal(cornerT[edgeFrom[j]], lMinX, lMaxX, lMinZ, lMaxZ, perimeter); Vector2 localEdgeB = ScaffoldParamToLocal(cornerT[edgeTo[j]], lMinX, lMaxX, lMinZ, lMaxZ, perimeter); Vector3 cA = cornerTops[edgeFrom[j]]; Vector3 cB = cornerTops[edgeTo[j]]; float edgeDx = cB.x - cA.x; float edgeDz = cB.z - cA.z; float edgeDist2 = Mathf.Sqrt(edgeDx * edgeDx + edgeDz * edgeDz); if (edgeDist2 < 0.1f) { continue; } Vector3 dir = new Vector3(edgeDx / edgeDist2, 0f, edgeDz / edgeDist2); float beamY = (cA.y + cB.y) * 0.5f; Quaternion beamRot = Quaternion.Euler(0f, Mathf.Atan2(0f - dir.z, dir.x) * 57.29578f, 0f); int nFull = Mathf.FloorToInt(edgeDist2 / 2f); float remainder = edgeDist2 - (float)nFull * 2f; for (int b = 0; b < nFull; b++) { float centerDist = (float)b * 2f + 1f - 0.04f; float t2 = Mathf.Clamp01(centerDist / edgeDist2); Vector2 localCenter = Vector2.Lerp(localEdgeA, localEdgeB, t2); if (!IsInsideAnyHearthOpening(localCenter.x, localCenter.y, deckOpenings)) { Vector3 center = cA + dir * ((float)b * 2f + 1f - 0.04f); center.y = beamY; SpawnScaffoldPole(horizPrefab, center, beamRot, player); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } if (!(remainder > 0.05f)) { continue; } float centerDist2 = edgeDist2 - 0.96f; float t3 = Mathf.Clamp01(centerDist2 / edgeDist2); Vector2 localCenter2 = Vector2.Lerp(localEdgeA, localEdgeB, t3); if (!IsInsideAnyHearthOpening(localCenter2.x, localCenter2.y, deckOpenings)) { Vector3 center2 = cB - dir * 0.96f; center2.y = beamY; SpawnScaffoldPole(horizPrefab, center2, beamRot, player); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } } else if (!noRoofTopLevel) { foreach (List chain in fwArcChains) { for (int k = 0; k < chain.Count - 1; k += 2) { int nextJoint = Mathf.Min(k + 2, chain.Count - 1); FlexiWallSegment sA = chain[k]; FlexiWallSegment sC = chain[nextJoint]; float localCX = (sA.Lx + sC.Lx) * 0.5f; float localCZ = (sA.Lz + sC.Lz) * 0.5f; float chDX = sC.Lx - sA.Lx; float chDZ = sC.Lz - sA.Lz; float chLen = Mathf.Sqrt(chDX * chDX + chDZ * chDZ); if (!(chLen < 0.01f)) { Vector2 worldCh = PieceMap.TransformLocalXZ(chDX / chLen, chDZ / chLen, rotationDeg); Quaternion fwBeamRot = Quaternion.Euler(0f, Mathf.Atan2(0f - worldCh.y, worldCh.x) * 57.29578f, 0f); Vector3 fwBeamCenter = PieceMap.TransformPlanPoint(origin, localCX, localCZ, levelTopY, rotationDeg); SpawnScaffoldPole(horizPrefab, fwBeamCenter, fwBeamRot, player); placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } } } if (!noRoofTopLevel && useFwScaffolding && fwArcChains.Count > 1) { List arcEndptLocals = new List(fwArcChains.Count * 2); for (int fi = 0; fi < fwArcChains.Count; fi++) { List ch = fwArcChains[fi]; arcEndptLocals.Add(new Vector2(ch[0].Lx, ch[0].Lz)); arcEndptLocals.Add(new Vector2(ch[ch.Count - 1].Lx, ch[ch.Count - 1].Lz)); } for (int ai = 0; ai < arcEndptLocals.Count; ai++) { for (int aj = ai + 1; aj < arcEndptLocals.Count; aj++) { if (ai / 2 == aj / 2) { continue; } Vector2 localA = arcEndptLocals[ai]; Vector2 localB = arcEndptLocals[aj]; float gapDist = Vector2.Distance(localA, localB); if (!(gapDist < 0.5f) && !(gapDist > 4f)) { placed += PlaceScaffoldBeamSpan(pA: PieceMap.TransformPlanPoint(origin, localA.x, localA.y, levelTopY, rotationDeg), pB: PieceMap.TransformPlanPoint(origin, localB.x, localB.y, levelTopY, rotationDeg), localA: localA, localB: localB, horizPrefab: horizPrefab, vertPrefab: vertPrefab, player: player, doorCenters: doorCenters, supportFurnitureExclusions: supportFurnitureExclusions, occupiedPoleLocals: occupiedPoleLocals, blockedOpenings: deckOpenings); if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } } } if (!noRoofTopLevel && useFwScaffolding) { Vector2 centerLocal = new Vector2(localCenterX, localCenterZ); Vector3 centerTopPos = PieceMap.TransformPlanPoint(origin, localCenterX, localCenterZ, levelTopY, rotationDeg); foreach (Vector2 fwLocal3 in fwPoleLocals) { placed += PlaceScaffoldBeamSpan(pA: PieceMap.TransformPlanPoint(origin, fwLocal3.x, fwLocal3.y, levelTopY, rotationDeg), localA: fwLocal3, localB: centerLocal, pB: centerTopPos, horizPrefab: horizPrefab, vertPrefab: vertPrefab, player: player, doorCenters: doorCenters, supportFurnitureExclusions: supportFurnitureExclusions, occupiedPoleLocals: occupiedPoleLocals, blockedOpenings: deckOpenings); if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } } if (!noRoofTopLevel && !useFwScaffolding && useTransverseScaffoldingBeams) { placed += PlaceTransverseBeams(poleParams, width, depth, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, doorJambParams, blockedTransverseLocalZs, deckOpenings, doorCenters, supportFurnitureExclusions, occupiedPoleLocals, origin, rotationDeg, levelTopY, horizPrefab, vertPrefab, player); } if (!noRoofTopLevel && !useFwScaffolding && useLongitudinalScaffoldingBeams) { placed += PlaceLongitudinalBeams(poleParams, width, depth, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, doorJambParams, blockedLongitudinalLocalXs, deckOpenings, doorCenters, supportFurnitureExclusions, occupiedPoleLocals, origin, rotationDeg, levelTopY, horizPrefab, vertPrefab, player); } if (ValheimFloorPlanPlugin.ScaffoldingFloors && !noRoofTopLevel) { float fwRidgeSouthZ = -1f; float fwRidgeNorthZ = -1f; if (useFwScaffolding) { float bestS = float.MaxValue; float bestN = float.MinValue; foreach (List chain2 in fwArcChains) { foreach (FlexiWallSegment seg in chain2) { if (Mathf.Abs(seg.Lx - localCenterX) < 2f) { if (seg.Lz < bestS) { bestS = seg.Lz; } if (seg.Lz > bestN) { bestN = seg.Lz; } } } } if (bestS < float.MaxValue) { fwRidgeSouthZ = bestS; } if (bestN > float.MinValue) { fwRidgeNorthZ = bestN; } } placed += PlaceScaffoldLevelFloorDeck(minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, deckY, floor2Prefab, floor1Prefab, roofTopPrefab, topLowerRoofPrefab, topLowerSupportPrefab, vertPrefab, horizPrefab, levelTopY, isTopmostLevel, deckOpenings, player, fwRidgeSouthZ, fwRidgeNorthZ, fwArcPolygon); } if (hearthOpenings.Count > 0 && (Object)(object)chimneyWall2Prefab != (Object)null) { placed += PlaceHearthChimneyLevel(hearthOpenings, origin, rotationDeg, currentLevelBaseY, levelTopY, chimneyStartY, chimneyWall2Prefab, chimneyWall1Prefab, chimneyRoofPrefab, player); float cornerPoleCenter = currentLevelBaseY + scaffoldFloorHeight * 0.5f; foreach (HearthOpening opening in hearthOpenings) { int[] cornerCols = new int[2] { opening.MinCol, opening.MaxColExclusive }; int[] cornerRows = new int[2] { opening.MinRow, opening.MaxRowExclusive }; int[] array = cornerCols; foreach (int cc in array) { int[] array2 = cornerRows; foreach (int cr in array2) { Vector3 cPos = PieceMap.TransformPlanPoint(origin, cc, cr, cornerPoleCenter, rotationDeg); placed += SpawnScaffoldColumn(vertPrefab, cPos, Quaternion.Euler(0f, rotationDeg, 0f), player, scaffoldFloorHeight, 2f); } } } } currentLevelBaseY = levelTopY; } if (hearthOpenings.Count > 0 && (Object)(object)chimneyWall2Prefab != (Object)null) { float topDeckY = GetDeckYForScaffoldLevel(scaffoldLevels - 1); float roofApexY = topDeckY; if (ValheimFloorPlanPlugin.RoofStyle == ValheimFloorPlanPlugin.RoofStyleOption.Gable) { float halfSpan = 0.5f * Mathf.Min(width, depth); roofApexY = topDeckY + Mathf.Tan(0.4537856f) * halfSpan; } float chimneyTopY = Mathf.Max(currentLevelBaseY + 2f, roofApexY + 1f); placed += PlaceHearthChimneyTop(hearthOpenings, origin, rotationDeg, currentLevelBaseY, chimneyTopY, chimneyWall2Prefab, chimneyWall1Prefab, chimneyRoofPrefab, player); RemoveInterferingUpperDeckPieces(hearthOpenings, origin, rotationDeg, chimneyStartY, chimneyTopY + 0.75f); RemoveInterferingUpperRoofPieces(hearthOpenings, origin, rotationDeg, currentLevelBaseY, roofApexY + 0.6f); } PruneGroundFloorScaffoldVerticals(doorCenters, centerWorld, origin, rotationDeg); ValheimFloorPlanPlugin.Log.LogInfo((object)$"[Scaffolding] Placed {placed} roof scaffolding pieces ({poleParams.Count + 1} vertical columns per level across {scaffoldLevels} scaffold levels)."); } private int PlaceScaffoldLevelFloorDeck(int minCol, int maxColExclusive, int minRow, int maxRowExclusive, Vector3 origin, float rotationDeg, float deckY, GameObject floor2Prefab, GameObject floor1Prefab, GameObject? roofTopPrefab, GameObject? topLowerRoofPrefab, GameObject? topLowerSupportPrefab, GameObject? gableApexPolePrefab, GameObject? gableApexRidgeBeamPrefab, float gableApexPoleBaseY, bool useRoofTop, List hearthOpenings, Player player, float ridgeSouthEdgeZ = -1f, float ridgeNorthEdgeZ = -1f, List? arcPolygon = null) { //IL_00c0: 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_006a: 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) bool flag = useRoofTop && ValheimFloorPlanPlugin.RoofStyle == ValheimFloorPlanPlugin.RoofStyleOption.Flat && (Object)(object)roofTopPrefab != (Object)null; if (useRoofTop && ValheimFloorPlanPlugin.RoofStyle == ValheimFloorPlanPlugin.RoofStyleOption.Gable && (Object)(object)topLowerRoofPrefab != (Object)null) { int num = 0; if (ValheimFloorPlanPlugin.UseGableFloorUnderlay()) { num += PlaceScaffoldFlatDeckTiles(minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, deckY, floor2Prefab, floor1Prefab, hearthOpenings, player, arcPolygon); } return num + PlaceTopScaffoldGableRoof(minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, deckY, topLowerRoofPrefab, topLowerSupportPrefab, gableApexPolePrefab, gableApexRidgeBeamPrefab, gableApexPoleBaseY, new List(), player, ridgeSouthEdgeZ, ridgeNorthEdgeZ, arcPolygon); } if (flag) { return PlaceTopScaffoldFlatRidgeRoof(minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, deckY, roofTopPrefab, floor1Prefab, hearthOpenings, player, arcPolygon); } return PlaceScaffoldFlatDeckTiles(minCol, maxColExclusive, minRow, maxRowExclusive, origin, rotationDeg, deckY, floor2Prefab, floor1Prefab, hearthOpenings, player, arcPolygon); } private int PlaceScaffoldFlatDeckTiles(int minCol, int maxColExclusive, int minRow, int maxRowExclusive, Vector3 origin, float rotationDeg, float deckY, GameObject floor2Prefab, GameObject floor1Prefab, List hearthOpenings, Player player, List? arcPolygon = null) { //IL_000f: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_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_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) int num = 0; Quaternion rot = Quaternion.Euler(0f, rotationDeg, 0f); for (int i = minRow; i < maxRowExclusive; i += 2) { for (int j = minCol; j < maxColExclusive; j += 2) { if (j + 1 < maxColExclusive && i + 1 < maxRowExclusive && !IsBlockedByHearthOpening(j, i, hearthOpenings) && !IsBlockedByHearthOpening(j + 1, i, hearthOpenings) && !IsBlockedByHearthOpening(j, i + 1, hearthOpenings) && !IsBlockedByHearthOpening(j + 1, i + 1, hearthOpenings)) { float localX = ((float)j + 1f) * 1f; float localZ = ((float)i + 1f) * 1f; if (arcPolygon == null || IsInsideArcPolygon(localX, localZ, arcPolygon)) { Vector3 pos = PieceMap.TransformPlanPoint(origin, localX, localZ, deckY, rotationDeg); SpawnRegisteredPiece(floor2Prefab, pos, rot, player); num++; continue; } } int num2 = Mathf.Min(i + 2, maxRowExclusive); int num3 = Mathf.Min(j + 2, maxColExclusive); for (int k = i; k < num2; k++) { for (int l = j; l < num3; l++) { if (!IsBlockedByHearthOpening(l, k, hearthOpenings)) { float localX2 = ((float)l + 0.5f) * 1f; float localZ2 = ((float)k + 0.5f) * 1f; if (arcPolygon == null || IsInsideOrNearArcPolygon(localX2, localZ2, 0.5f, arcPolygon)) { Vector3 pos2 = PieceMap.TransformPlanPoint(origin, localX2, localZ2, deckY, rotationDeg); SpawnRegisteredPiece(floor1Prefab, pos2, rot, player); num++; } } } } } } return num; } private int PlaceTopScaffoldFlatRidgeRoof(int minCol, int maxColExclusive, int minRow, int maxRowExclusive, Vector3 origin, float rotationDeg, float deckY, GameObject ridgeRoofPrefab, GameObject fallbackFloor1Prefab, List hearthOpenings, Player player, List? arcPolygon = null) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) int num = 0; bool flag = maxColExclusive - minCol >= maxRowExclusive - minRow; Quaternion rot = Quaternion.Euler(0f, flag ? rotationDeg : (rotationDeg + 90f), 0f); Quaternion rot2 = Quaternion.Euler(0f, rotationDeg, 0f); for (int i = minRow; i < maxRowExclusive; i += 2) { for (int j = minCol; j < maxColExclusive; j += 2) { if (j + 1 < maxColExclusive && i + 1 < maxRowExclusive && !IsBlockedByHearthOpening(j, i, hearthOpenings) && !IsBlockedByHearthOpening(j + 1, i, hearthOpenings) && !IsBlockedByHearthOpening(j, i + 1, hearthOpenings) && !IsBlockedByHearthOpening(j + 1, i + 1, hearthOpenings)) { float localX = ((float)j + 1f) * 1f; float localZ = ((float)i + 1f) * 1f; if (arcPolygon == null || IsInsideArcPolygon(localX, localZ, arcPolygon)) { Vector3 pos = PieceMap.TransformPlanPoint(origin, localX, localZ, deckY, rotationDeg); SpawnRegisteredPiece(ridgeRoofPrefab, pos, rot, player); num++; continue; } } int num2 = Mathf.Min(i + 2, maxRowExclusive); int num3 = Mathf.Min(j + 2, maxColExclusive); for (int k = i; k < num2; k++) { for (int l = j; l < num3; l++) { if (!IsBlockedByHearthOpening(l, k, hearthOpenings)) { float localX2 = ((float)l + 0.5f) * 1f; float localZ2 = ((float)k + 0.5f) * 1f; if (arcPolygon == null || IsInsideOrNearArcPolygon(localX2, localZ2, 0.5f, arcPolygon)) { Vector3 pos2 = PieceMap.TransformPlanPoint(origin, localX2, localZ2, deckY, rotationDeg); SpawnRegisteredPiece(fallbackFloor1Prefab, pos2, rot2, player); num++; } } } } } } return num; } private int PlaceTopScaffoldGableRoof(int minCol, int maxColExclusive, int minRow, int maxRowExclusive, Vector3 origin, float rotationDeg, float roofBaseY, GameObject roofPrefab, GameObject? supportPrefab, GameObject? apexPolePrefab, GameObject? apexRidgeBeamPrefab, float apexPoleBaseY, List hearthOpenings, Player player, float ridgeSouthEdgeZ = -1f, float ridgeNorthEdgeZ = -1f, List? arcPolygon = null) { //IL_007a: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_00fd: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0346: 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) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_039b: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) int num = 0; bool flag = false; float num2 = 0.4537856f; if (flag) { float num3 = minRow; float num4 = maxRowExclusive; float num5 = (float)(minRow + maxRowExclusive) * 0.5f; float localX = (float)(minCol + maxColExclusive) * 0.5f; float num6 = Mathf.Abs(num5 - num3); float num7 = roofBaseY + Mathf.Tan(num2) * num6; for (int i = minCol; i < maxColExclusive; i += 2) { int num8 = Mathf.Min(2, maxColExclusive - i); float num9 = (float)i + (float)num8 * 0.5f; num += PlaceSlopedRoofRun(new Vector2(num9, num3), new Vector2(num9, num5), roofBaseY, num7, 180f, 2f, supportPrefab, roofPrefab, origin, rotationDeg, hearthOpenings, player); num += PlaceSlopedRoofRun(new Vector2(num9, num4), new Vector2(num9, num5), roofBaseY, num7, 0f, 2f, supportPrefab, roofPrefab, origin, rotationDeg, hearthOpenings, player); } num += PlaceGableApexSupportColumnIfClear(localX, num3, apexPoleBaseY, num7, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); num += PlaceGableApexSupportColumnIfClear(localX, num4, apexPoleBaseY, num7, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); num += PlaceGableApexSupportColumnIfClear(localX, num5, apexPoleBaseY, num7, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); return num + PlaceGableApexRidgePoleSpan(new Vector2((float)minCol, num5), new Vector2((float)maxColExclusive, num5), num7, origin, rotationDeg, apexRidgeBeamPrefab, hearthOpenings, player); } float num10 = (float)(minCol + maxColExclusive) * 0.5f; float num11 = ((ridgeSouthEdgeZ >= 0f) ? ridgeSouthEdgeZ : ((float)minRow)); float num12 = ((ridgeNorthEdgeZ >= 0f) ? ridgeNorthEdgeZ : ((float)maxRowExclusive)); float localZ = (float)(minRow + maxRowExclusive) * 0.5f; float num13 = minCol; float num14 = maxColExclusive; float num15 = Mathf.Abs(num10 - num13); float num16 = roofBaseY + Mathf.Tan(num2) * num15; for (int j = minRow; j < maxRowExclusive; j += 2) { int num17 = Mathf.Min(2, maxRowExclusive - j); float num18 = (float)j + (float)num17 * 0.5f; float num19 = num13; float num20 = num14; bool flag2 = true; bool flag3 = true; if (arcPolygon != null) { flag2 = TryGetArcBoundaryX(num18, arcPolygon, findMin: true, out var boundaryX) && boundaryX < num10; if (flag2) { num19 = boundaryX; } flag3 = TryGetArcBoundaryX(num18, arcPolygon, findMin: false, out boundaryX) && boundaryX > num10; if (flag3) { num20 = boundaryX; } if (flag2 && num10 - num19 < 2f) { flag2 = false; } if (flag3 && num20 - num10 < 2f) { flag3 = false; } } float num21 = roofBaseY; float num22 = roofBaseY; if (arcPolygon != null) { if (flag2) { num21 = Mathf.Lerp(roofBaseY, num16, Mathf.Clamp01((num19 - num13) / num15)); } if (flag3) { num22 = Mathf.Lerp(roofBaseY, num16, Mathf.Clamp01((num14 - num20) / num15)); } } if (flag2) { if (num21 > apexPoleBaseY + 0.1f) { num += PlaceGableApexSupportColumnIfClear(num19, num18, apexPoleBaseY, num21, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); } num += PlaceSlopedRoofRun(new Vector2(num19, num18), new Vector2(num10, num18), num21, num16, 270f, 2f, supportPrefab, roofPrefab, origin, rotationDeg, hearthOpenings, player); } if (flag3) { if (num22 > apexPoleBaseY + 0.1f) { num += PlaceGableApexSupportColumnIfClear(num20, num18, apexPoleBaseY, num22, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); } num += PlaceSlopedRoofRun(new Vector2(num20, num18), new Vector2(num10, num18), num22, num16, 90f, 2f, supportPrefab, roofPrefab, origin, rotationDeg, hearthOpenings, player); } } num += PlaceGableApexSupportColumnIfClear(num10, num11, apexPoleBaseY, num16, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); num += PlaceGableApexSupportColumnIfClear(num10, num12, apexPoleBaseY, num16, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); num += PlaceGableApexSupportColumnIfClear(num10, localZ, apexPoleBaseY, num16, origin, rotationDeg, apexPolePrefab, hearthOpenings, player); return num + PlaceGableApexRidgePoleSpan(new Vector2(num10, num11), new Vector2(num10, num12), num16, origin, rotationDeg, apexRidgeBeamPrefab, hearthOpenings, player); } private int PlaceGableApexRidgePoleSpan(Vector2 startLocal, Vector2 endLocal, float ridgeY, Vector3 origin, float rotationDeg, GameObject? beamPrefab, List blockedOpenings, Player player) { //IL_0017: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_011d: 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_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: 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_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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: 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_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: 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_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)beamPrefab == (Object)null) { return 0; } Vector3 val = PieceMap.TransformPlanPoint(origin, startLocal.x, startLocal.y, ridgeY - 0.06f, rotationDeg); Vector3 val2 = PieceMap.TransformPlanPoint(origin, endLocal.x, endLocal.y, ridgeY - 0.06f, rotationDeg); float num = val2.x - val.x; float num2 = val2.z - val.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 < 0.1f) { return 0; } int num4 = 0; Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(num / num3, 0f, num2 / num3); Quaternion rot = Quaternion.Euler(0f, Mathf.Atan2(0f - val3.z, val3.x) * 57.29578f, 0f); int num5 = Mathf.FloorToInt(num3 / 2f); float num6 = num3 - (float)num5 * 2f; for (int i = 0; i < num5; i++) { float num7 = (float)i * 2f + 1f - 0.04f; float num8 = Mathf.Clamp01(num7 / num3); Vector2 val4 = Vector2.Lerp(startLocal, endLocal, num8); if (!IsInsideAnyHearthOpening(val4.x, val4.y, blockedOpenings)) { Vector3 pos = val + val3 * ((float)i * 2f + 1f - 0.04f); SpawnScaffoldPole(beamPrefab, pos, rot, player); num4++; } } if (num6 > 0.05f) { float num9 = num3 - 0.96f; float num10 = Mathf.Clamp01(num9 / num3); Vector2 val5 = Vector2.Lerp(startLocal, endLocal, num10); if (IsInsideAnyHearthOpening(val5.x, val5.y, blockedOpenings)) { return num4; } Vector3 pos2 = val2 - val3 * 0.96f; SpawnScaffoldPole(beamPrefab, pos2, rot, player); num4++; } return num4; } private int PlaceGableApexSupportColumnIfClear(float localX, float localZ, float baseY, float apexY, Vector3 origin, float rotationDeg, GameObject? polePrefab, List hearthOpenings, Player player) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)polePrefab == (Object)null || apexY <= baseY + 0.01f) { return 0; } if (IsInsideAnyHearthOpening(localX, localZ, hearthOpenings)) { return 0; } float num = apexY - baseY; float num2 = 1.92f; int num3 = Mathf.Max(1, Mathf.CeilToInt(num / num2)); Quaternion rot = Quaternion.Euler(0f, rotationDeg, 0f); float num4 = baseY + 1f; int num5 = 0; for (int i = 0; i < num3; i++) { Vector3 pos = PieceMap.TransformPlanPoint(origin, localX, localZ, num4 + (float)i * num2, rotationDeg); SpawnScaffoldPole(polePrefab, pos, rot, player); num5++; } return num5; } private int PlaceSlopedRoofRun(Vector2 startLocal, Vector2 ridgeLocal, float startY, float ridgeY, float localYaw, float segmentLength, GameObject? supportPrefab, GameObject roofPrefab, Vector3 origin, float rotationDeg, List hearthOpenings, Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0069: 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_007e: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00d5: 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) Vector2 val = ridgeLocal - startLocal; float magnitude = ((Vector2)(ref val)).magnitude; if (magnitude < 0.01f) { return 0; } float num = ridgeY - startY; float num2 = Mathf.Sqrt(magnitude * magnitude + num * num); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / segmentLength)); int num4 = 0; for (int i = 0; i < num3; i++) { float num5 = ((float)i + 0.5f) / (float)num3; Vector2 val2 = Vector2.Lerp(startLocal, ridgeLocal, num5); float num6 = Mathf.Lerp(startY, ridgeY, num5); Vector2 normalized = ((Vector2)(ref val)).normalized; Vector2 val3 = val2 + normalized * 0.12f; float localY = num6 + -0.08f; num4 += PlaceTopSupportPieceIfClear(val2.x, val2.y, num6, localYaw, origin, rotationDeg, supportPrefab, hearthOpenings, player); num4 += PlaceTopRoofPieceIfClear(val3.x, val3.y, localY, localYaw, origin, rotationDeg, roofPrefab, hearthOpenings, player); } return num4; } private int PlaceHearthBeamRing(int minCol, int minRow, int widthCells, int heightCells, Vector3 origin, float rotationDeg, GameObject? hearthGo, Player player) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_00f7: Unknown result type (might be due to invalid IL or missing references) if (widthCells <= 0 || heightCells <= 0) { return 0; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("wood_wall_log") : null); if ((Object)(object)val == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[HearthBeams] Prefab 'wood_wall_log' not found - hearth ring skipped."); return 0; } float worldY = (worldY = (((Object)(object)hearthGo != (Object)null) ? (hearthGo.transform.position.y + 0.06f) : (TerrainLeveler.TargetLevelY + 0.06f))); float num = minCol; float num2 = minCol + widthCells; float num3 = minRow; float num4 = minRow + heightCells; int num5 = 0; num5 += PlaceBeamRun(num, num3, num2, num3, worldY, origin, rotationDeg, val, player); num5 += PlaceBeamRun(num, num4, num2, num4, worldY, origin, rotationDeg, val, player); num5 += PlaceBeamRun(num, num3, num, num4, worldY, origin, rotationDeg, val, player); return num5 + PlaceBeamRun(num2, num3, num2, num4, worldY, origin, rotationDeg, val, player); } private int PlaceHearthSupportBeam(int hearthCol, int hearthRow, int effW, int effH, int minCol, int maxColExclusive, int minRow, int maxRowExclusive, Vector3 origin, float rotationDeg, float beamY, Player player) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("woodiron_beam") : null); if ((Object)(object)val == (Object)null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[HearthSupport] woodiron_beam prefab not found — support beam skipped."); return 0; } float num = (float)hearthCol + (float)effW * 0.5f; float num2 = (float)hearthRow + (float)effH * 0.5f; float num3 = num - (float)minCol; float num4 = (float)maxColExclusive - num; float num5 = num2 - (float)minRow; float num6 = (float)maxRowExclusive - num2; float num7 = Mathf.Min(num3, num4); float num8 = Mathf.Min(num5, num6); float startLocalZ; float endLocalZ; float startLocalX; float endLocalX; if (num7 <= num8) { startLocalZ = (endLocalZ = num2); if (num3 <= num4) { startLocalX = minCol; endLocalX = hearthCol + effW; } else { startLocalX = maxColExclusive; endLocalX = hearthCol; } } else { startLocalX = (endLocalX = num); if (num5 <= num6) { startLocalZ = minRow; endLocalZ = hearthRow + effH; } else { startLocalZ = maxRowExclusive; endLocalZ = hearthRow; } } return PlaceBeamRun(startLocalX, startLocalZ, endLocalX, endLocalZ, beamY, origin, rotationDeg, val, player); } private int PlaceBeamRun(float startLocalX, float startLocalZ, float endLocalX, float endLocalZ, float worldY, Vector3 origin, float rotationDeg, GameObject beamPrefab, Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = PieceMap.TransformPlanPoint(origin, startLocalX, startLocalZ, worldY, rotationDeg); Vector3 val2 = PieceMap.TransformPlanPoint(origin, endLocalX, endLocalZ, worldY, rotationDeg); float num = val2.x - val.x; float num2 = val2.z - val.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 < 0.1f) { return 0; } int num4 = 0; Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(num / num3, 0f, num2 / num3); Quaternion rot = Quaternion.Euler(0f, Mathf.Atan2(0f - val3.z, val3.x) * 57.29578f, 0f); int num5 = Mathf.FloorToInt(num3 / 2f); float num6 = num3 - (float)num5 * 2f; for (int i = 0; i < num5; i++) { Vector3 pos = val + val3 * ((float)i * 2f + 1f - 0.04f); pos.y = worldY; SpawnScaffoldPole(beamPrefab, pos, rot, player); num4++; } if (num6 > 0.05f) { Vector3 pos2 = val2 - val3 * 0.96f; pos2.y = worldY; SpawnScaffoldPole(beamPrefab, pos2, rot, player); num4++; } return num4; } private int PlaceTopRoofPieceIfClear(float localX, float localZ, float localY, float localYaw, Vector3 origin, float rotationDeg, GameObject? prefab, List hearthOpenings, Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || IsInsideAnyHearthOpening(localX, localZ, hearthOpenings)) { return 0; } return PlaceChimneyRoofPiece(localX, localZ, localY, localYaw, origin, rotationDeg, prefab, player); } private int PlaceTopSupportPieceIfClear(float localX, float localZ, float localY, float localYaw, Vector3 origin, float rotationDeg, GameObject? prefab, List hearthOpenings, Player player) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || IsInsideAnyHearthOpening(localX, localZ, hearthOpenings)) { return 0; } return PlaceChimneyRoofPiece(localX, localZ, localY, localYaw + 270f, origin, rotationDeg, prefab, player); } private int PlaceHearthChimneyLevel(List hearthOpenings, Vector3 origin, float rotationDeg, float levelBaseY, float levelTopY, float chimneyStartY, GameObject wall2Prefab, GameObject? wall1Prefab, GameObject? roofPrefab, Player player) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) int num = 0; float num2 = Mathf.Max(levelBaseY, chimneyStartY); if (num2 >= levelTopY - 0.01f) { return 0; } for (int i = 0; i < hearthOpenings.Count; i++) { HearthOpening hearthOpening = hearthOpenings[i]; HearthOpening insetOpening; HearthOpening hearthOpening2 = (TryInsetOpening(hearthOpening, out insetOpening) ? insetOpening : hearthOpening); bool flag = num2 <= chimneyStartY + 0.01f; float num3 = num2; if (!(num3 >= levelTopY - 0.01f)) { int num4 = Mathf.Max(1, Mathf.CeilToInt(levelTopY - num3)); for (int j = 0; j < num4; j++) { float wallY = num3 + 0.5f + (float)j; num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MinRow, alongX: true, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MaxRowExclusive, alongX: true, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MinCol, alongX: false, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MaxColExclusive, alongX: false, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); } } } return num; } private int PlaceChimneyWallRun(int startCell, int spanCells, int boundaryCell, bool alongX, float wallY, Vector3 origin, float rotationDeg, GameObject wall2Prefab, GameObject? wall1Prefab, Player player) { //IL_0071: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) int num = 0; int num2; for (int i = 0; i < spanCells; i += num2) { bool flag = spanCells - i == 1 && (Object)(object)wall1Prefab != (Object)null; num2 = (flag ? 1 : Mathf.Min(2, spanCells - i)); float num3 = (float)(startCell + i) + (float)num2 * 0.5f; GameObject prefab = (GameObject)(flag ? ((object)wall1Prefab) : ((object)wall2Prefab)); float localX = (alongX ? num3 : ((float)boundaryCell)); float localZ = (alongX ? ((float)boundaryCell) : num3); float localYawDeg = (alongX ? 0f : 90f); Vector3 pos = PieceMap.TransformPlanPoint(origin, localX, localZ, wallY, rotationDeg); Quaternion rot = Quaternion.Euler(0f, PieceMap.TransformLocalYaw(localYawDeg, rotationDeg), 0f); SpawnRegisteredPiece(prefab, pos, rot, player); num++; } return num; } private int PlaceHearthChimneyTop(List hearthOpenings, Vector3 origin, float rotationDeg, float chimneyBaseY, float chimneyTopY, GameObject wall2Prefab, GameObject? wall1Prefab, GameObject? roofPrefab, Player player) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) int num = 0; int num2 = Mathf.Max(1, Mathf.CeilToInt(chimneyTopY - chimneyBaseY)); for (int i = 0; i < hearthOpenings.Count; i++) { HearthOpening hearthOpening = hearthOpenings[i]; HearthOpening hearthOpening2 = hearthOpening; if (TryInsetOpening(hearthOpening, out HearthOpening insetOpening)) { hearthOpening2 = insetOpening; } bool flag = hearthOpening2.Width >= hearthOpening2.Height; for (int j = 0; j < num2; j++) { float wallY = chimneyBaseY + 0.5f + (float)j; num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MinRow, alongX: true, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MaxRowExclusive, alongX: true, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MinCol, alongX: false, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MaxColExclusive, alongX: false, wallY, origin, rotationDeg, wall2Prefab, wall1Prefab, player); } float wallY2 = chimneyTopY + 0.5f; if (flag) { num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MinCol, alongX: false, wallY2, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinRow, hearthOpening2.Height, hearthOpening2.MaxColExclusive, alongX: false, wallY2, origin, rotationDeg, wall2Prefab, wall1Prefab, player); } else { num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MinRow, alongX: true, wallY2, origin, rotationDeg, wall2Prefab, wall1Prefab, player); num += PlaceChimneyWallRun(hearthOpening2.MinCol, hearthOpening2.Width, hearthOpening2.MaxRowExclusive, alongX: true, wallY2, origin, rotationDeg, wall2Prefab, wall1Prefab, player); } num += PlaceHearthChimneyRoofCap(hearthOpening2, flag, origin, rotationDeg, chimneyTopY + 1f, roofPrefab, player, invertSlope: false); } return num; } private int PlaceHearthChimneyRoofCap(HearthOpening opening, bool closeWestEastSides, Vector3 origin, float rotationDeg, float roofBaseY, GameObject? roofPrefab, Player player, bool invertSlope) { //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) int num = 0; if ((Object)(object)roofPrefab != (Object)null) { if (closeWestEastSides) { float localX = (float)opening.MinCol + 1f; float localX2 = (float)opening.MaxColExclusive - 1f; for (int i = opening.MinRow; i < opening.MaxRowExclusive; i += 2) { int num2 = Mathf.Min(2, opening.MaxRowExclusive - i); float localZ = (float)i + (float)num2 * 0.5f; float localYaw = (invertSlope ? 90f : 270f); float localYaw2 = (invertSlope ? 270f : 90f); num += PlaceChimneyRoofPiece(localX, localZ, roofBaseY, localYaw, origin, rotationDeg, roofPrefab, player); num += PlaceChimneyRoofPiece(localX2, localZ, roofBaseY, localYaw2, origin, rotationDeg, roofPrefab, player); } } else { float localZ2 = (float)opening.MinRow + 1f; float localZ3 = (float)opening.MaxRowExclusive - 1f; for (int j = opening.MinCol; j < opening.MaxColExclusive; j += 2) { int num3 = Mathf.Min(2, opening.MaxColExclusive - j); float localX3 = (float)j + (float)num3 * 0.5f; float localYaw3 = (invertSlope ? 0f : 180f); float localYaw4 = (invertSlope ? 180f : 0f); num += PlaceChimneyRoofPiece(localX3, localZ2, roofBaseY, localYaw3, origin, rotationDeg, roofPrefab, player); num += PlaceChimneyRoofPiece(localX3, localZ3, roofBaseY, localYaw4, origin, rotationDeg, roofPrefab, player); } } } return num; } private int PlaceChimneyRoofPiece(float localX, float localZ, float roofY, float localYaw, Vector3 origin, float rotationDeg, GameObject roofPrefab, Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) Vector3 pos = PieceMap.TransformPlanPoint(origin, localX, localZ, roofY, rotationDeg); Quaternion rot = Quaternion.Euler(0f, PieceMap.TransformLocalYaw(localYaw, rotationDeg), 0f); SpawnRegisteredPiece(roofPrefab, pos, rot, player); return 1; } private static List BuildHearthOpenings(FloorPlan plan) { List list = new List(); foreach (FloorPlanPiece piece in plan.Pieces) { if (!(piece.Type != "Hearth")) { PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); list.Add(new HearthOpening(piece.Col, piece.Row, piece.Col + num, piece.Row + num2, 1, "Hearth")); } } } return list; } private static bool IsFurnitureType(string pieceType) { return pieceType == "Bed" || pieceType == "Workbench" || pieceType == "Hearth"; } private static List BuildFurnitureOpenings(FloorPlan plan, int sourceLevel) { List list = new List(); foreach (FloorPlanPiece piece in plan.Pieces) { if (IsFurnitureType(piece.Type)) { PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); list.Add(new HearthOpening(piece.Col, piece.Row, piece.Col + num, piece.Row + num2, sourceLevel, piece.Type)); } } } return list; } private static List BuildStaircaseOpenings(FloorPlan plan, int sourceLevel) { List list = new List(); foreach (FloorPlanPiece piece in plan.Pieces) { if (!(piece.Type != "Staircase")) { PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); list.Add(new HearthOpening(piece.Col, piece.Row, piece.Col + num, piece.Row + num2, sourceLevel, "Staircase")); } } } return list; } private static List BuildScaffoldDeckOpenings(FloorPlan plan) { List list = BuildHearthOpenings(plan); foreach (FloorPlanPiece piece in plan.Pieces) { if (!(piece.Type != "Staircase")) { PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); list.Add(new HearthOpening(piece.Col, piece.Row, piece.Col + num, piece.Row + num2, 1, "Staircase")); } } } return list; } private static List BuildScaffoldDeckOpenings(FloorPlan plan, float deckY, float chimneyStartY) { List list = new List(); float num = chimneyStartY - 3f; float num2 = num + GetStaircaseTargetRise(); bool flag = deckY >= chimneyStartY + 1f - 0.01f; foreach (FloorPlanPiece piece in plan.Pieces) { if (piece.Type != "Hearth") { continue; } PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num3 = def.EffW(piece.Rotation); int num4 = def.EffH(piece.Rotation); HearthOpening hearthOpening = new HearthOpening(piece.Col, piece.Row, piece.Col + num3, piece.Row + num4); if (flag && TryInsetOpening(hearthOpening, out HearthOpening insetOpening)) { list.Add(insetOpening); } else { list.Add(hearthOpening); } } } foreach (FloorPlanPiece piece2 in plan.Pieces) { if (!(piece2.Type != "Staircase") && !(deckY > num2 + 0.2f)) { PieceDef def2 = PieceMap.GetDef(piece2.Type); if (def2 != null) { int num5 = def2.EffW(piece2.Rotation); int num6 = def2.EffH(piece2.Rotation); list.Add(new HearthOpening(piece2.Col, piece2.Row, piece2.Col + num5, piece2.Row + num6, 0, "Staircase")); } } } return list; } private static List GetApplicableStaircaseShaftOpenings(List openings, int targetLevelNumber) { List list = new List(openings.Count); for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; if (!string.Equals(hearthOpening.SourceType, "Staircase", StringComparison.OrdinalIgnoreCase)) { list.Add(hearthOpening); } else if (IsStaircaseOpeningBlockingLevel(hearthOpening.SourceLevel, targetLevelNumber)) { list.Add(hearthOpening); } } return list; } private static bool IsStaircaseOpeningBlockingLevel(int sourceLevel, int targetLevelNumber) { if (targetLevelNumber <= sourceLevel) { return false; } if (ValheimFloorPlanPlugin.StaircaseReachMode == ValheimFloorPlanPlugin.StaircaseReachModeOption.AllTheWay) { return true; } return targetLevelNumber == sourceLevel + 1; } private static bool IsBlockedByHearthOpening(int col, int row, List hearthOpenings) { for (int i = 0; i < hearthOpenings.Count; i++) { HearthOpening hearthOpening = hearthOpenings[i]; if (col >= hearthOpening.MinCol && col < hearthOpening.MaxColExclusive && row >= hearthOpening.MinRow && row < hearthOpening.MaxRowExclusive) { return true; } } return false; } private static bool TryInsetOpening(HearthOpening opening, out HearthOpening insetOpening) { insetOpening = opening; return false; } private static bool IsInsideAnyHearthOpening(float localX, float localZ, List hearthOpenings) { for (int i = 0; i < hearthOpenings.Count; i++) { HearthOpening hearthOpening = hearthOpenings[i]; if (localX >= (float)hearthOpening.MinCol && localX <= (float)hearthOpening.MaxColExclusive && localZ >= (float)hearthOpening.MinRow && localZ <= (float)hearthOpening.MaxRowExclusive) { return true; } } return false; } private static bool DoesFootprintOverlapAnyOpening(int minCol, int minRow, int maxColExclusive, int maxRowExclusive, List openings) { for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; bool flag = maxColExclusive > hearthOpening.MinCol && minCol < hearthOpening.MaxColExclusive; bool flag2 = maxRowExclusive > hearthOpening.MinRow && minRow < hearthOpening.MaxRowExclusive; if (flag && flag2) { return true; } } return false; } private static bool DoesFootprintOverlapAnyOpeningWithMargin(int minCol, int minRow, int maxColExclusive, int maxRowExclusive, List openings, int marginCells) { for (int i = 0; i < openings.Count; i++) { HearthOpening hearthOpening = openings[i]; bool flag = maxColExclusive > hearthOpening.MinCol - marginCells && minCol < hearthOpening.MaxColExclusive + marginCells; bool flag2 = maxRowExclusive > hearthOpening.MinRow - marginCells && minRow < hearthOpening.MaxRowExclusive + marginCells; if (flag && flag2) { return true; } } return false; } private static bool IsFootprintInsideBoundsWithMargin(int minCol, int minRow, int maxColExclusive, int maxRowExclusive, int boundsMinCol, int boundsMaxColExclusive, int boundsMinRow, int boundsMaxRowExclusive, int marginCells) { return minCol >= boundsMinCol + marginCells && minRow >= boundsMinRow + marginCells && maxColExclusive <= boundsMaxColExclusive - marginCells && maxRowExclusive <= boundsMaxRowExclusive - marginCells; } private int PlaceTransverseBeams(List poleParams, float width, float depth, float lMinX, float lMaxX, float lMinZ, float lMaxZ, float perimeter, List doorJambParams, List blockedTransverseLocalZs, List blockedOpenings, List doorCenters, List supportFurnitureExclusions, List occupiedPoleLocals, Vector3 origin, float rotationDeg, float levelTopY, GameObject horizPrefab, GameObject vertPrefab, Player player) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) int num = 0; List list = new List(); List list2 = new List(); for (int i = 0; i < poleParams.Count; i++) { float num2 = poleParams[i]; if (IsNearAnyParam(num2, doorJambParams, 0.25f)) { continue; } if (num2 > width && num2 < width + depth) { ScaffoldPolePoint scaffoldPolePoint = BuildScaffoldPolePoint(num2, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, origin, rotationDeg, levelTopY); if (!IsWithinAnyDoorSpan(scaffoldPolePoint.Local.y, blockedTransverseLocalZs, 0.25f)) { list.Add(scaffoldPolePoint); } } else if (num2 > 2f * width + depth && num2 < perimeter) { ScaffoldPolePoint scaffoldPolePoint2 = BuildScaffoldPolePoint(num2, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, origin, rotationDeg, levelTopY); if (!IsWithinAnyDoorSpan(scaffoldPolePoint2.Local.y, blockedTransverseLocalZs, 0.25f)) { list2.Add(scaffoldPolePoint2); } } } bool[] array = new bool[list.Count]; for (int j = 0; j < list2.Count; j++) { float y = list2[j].Local.y; int num3 = -1; float num4 = float.MaxValue; for (int k = 0; k < list.Count; k++) { if (!array[k]) { float num5 = Mathf.Abs(list[k].Local.y - y); if (num5 < num4) { num4 = num5; num3 = k; } } } if (num3 >= 0 && !(num4 > 0.25f)) { array[num3] = true; num += PlaceScaffoldBeamSpan(list2[j].Local, list[num3].Local, list2[j].Pos, list[num3].Pos, horizPrefab, vertPrefab, player, doorCenters, supportFurnitureExclusions, occupiedPoleLocals, blockedOpenings); } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[Scaffolding] Transverse beams: connected {num} pieces across {list2.Count} west and {list.Count} east intermediate poles."); return num; } private int PlaceLongitudinalBeams(List poleParams, float width, float depth, float lMinX, float lMaxX, float lMinZ, float lMaxZ, float perimeter, List doorJambParams, List blockedLongitudinalLocalXs, List blockedOpenings, List doorCenters, List supportFurnitureExclusions, List occupiedPoleLocals, Vector3 origin, float rotationDeg, float levelTopY, GameObject horizPrefab, GameObject vertPrefab, Player player) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: 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_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) int num = 0; List list = new List(); List list2 = new List(); for (int i = 0; i < poleParams.Count; i++) { float num2 = poleParams[i]; if (IsNearAnyParam(num2, doorJambParams, 0.25f)) { continue; } if (num2 > 0f && num2 < width) { ScaffoldPolePoint scaffoldPolePoint = BuildScaffoldPolePoint(num2, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, origin, rotationDeg, levelTopY); if (!IsWithinAnyDoorSpan(scaffoldPolePoint.Local.x, blockedLongitudinalLocalXs, 0.25f)) { list.Add(scaffoldPolePoint); } } else if (num2 > width + depth && num2 < 2f * width + depth) { ScaffoldPolePoint scaffoldPolePoint2 = BuildScaffoldPolePoint(num2, lMinX, lMaxX, lMinZ, lMaxZ, perimeter, origin, rotationDeg, levelTopY); if (!IsWithinAnyDoorSpan(scaffoldPolePoint2.Local.x, blockedLongitudinalLocalXs, 0.25f)) { list2.Add(scaffoldPolePoint2); } } } bool[] array = new bool[list2.Count]; for (int j = 0; j < list.Count; j++) { float x = list[j].Local.x; int num3 = -1; float num4 = float.MaxValue; for (int k = 0; k < list2.Count; k++) { if (!array[k]) { float num5 = Mathf.Abs(list2[k].Local.x - x); if (num5 < num4) { num4 = num5; num3 = k; } } } if (num3 >= 0 && !(num4 > 0.25f)) { array[num3] = true; num += PlaceScaffoldBeamSpan(list[j].Local, list2[num3].Local, list[j].Pos, list2[num3].Pos, horizPrefab, vertPrefab, player, doorCenters, supportFurnitureExclusions, occupiedPoleLocals, blockedOpenings); } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[Scaffolding] Longitudinal beams: connected {num} pieces across {list.Count} south and {list2.Count} north intermediate poles."); return num; } private ScaffoldPolePoint BuildScaffoldPolePoint(float t, float lMinX, float lMaxX, float lMinZ, float lMaxZ, float perimeter, Vector3 origin, float rotationDeg, float levelTopY) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) Vector2 val = ScaffoldParamToLocal(t, lMinX, lMaxX, lMinZ, lMaxZ, perimeter); Vector3 pos = PieceMap.TransformPlanPoint(origin, val.x, val.y, levelTopY, rotationDeg); return new ScaffoldPolePoint(t, val, pos); } private int PlaceScaffoldBeamSpan(Vector2 localA, Vector2 localB, Vector3 pA, Vector3 pB, GameObject horizPrefab, GameObject vertPrefab, Player player, List doorCenters, List supportFurnitureExclusions, List occupiedPoleLocals, List blockedOpenings) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_00de: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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_016a: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019c: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) float num = pB.x - pA.x; float num2 = pB.z - pA.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 < 0.1f) { return 0; } int num4 = 0; Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(num / num3, 0f, num2 / num3); float y = (pA.y + pB.y) * 0.5f; Quaternion rot = Quaternion.Euler(0f, Mathf.Atan2(0f - val.z, val.x) * 57.29578f, 0f); int num5 = Mathf.FloorToInt(num3 / 2f); float num6 = num3 - (float)num5 * 2f; for (int i = 0; i < num5; i++) { float num7 = (float)i * 2f + 1f - 0.04f; float num8 = Mathf.Clamp01(num7 / num3); Vector2 val2 = Vector2.Lerp(localA, localB, num8); if (!IsInsideAnyHearthOpening(val2.x, val2.y, blockedOpenings)) { Vector3 pos = pA + val * num7; pos.y = y; SpawnScaffoldPole(horizPrefab, pos, rot, player); num4++; } } if (num6 > 0.05f) { float num9 = num3 - 0.96f; float num10 = Mathf.Clamp01(num9 / num3); Vector2 val3 = Vector2.Lerp(localA, localB, num10); if (!IsInsideAnyHearthOpening(val3.x, val3.y, blockedOpenings)) { Vector3 pos2 = pB - val * 0.96f; pos2.y = y; SpawnScaffoldPole(horizPrefab, pos2, rot, player); num4++; } } return num4; } private static Vector2 ScaffoldParamToLocal(float t, float minX, float maxX, float minZ, float maxZ, float perimeter) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) float num = maxX - minX; float num2 = maxZ - minZ; t = (t % perimeter + perimeter) % perimeter; if (t <= num) { return new Vector2(minX + t, minZ); } t -= num; if (t <= num2) { return new Vector2(maxX, minZ + t); } t -= num2; if (t <= num) { return new Vector2(maxX - t, maxZ); } t -= num; return new Vector2(minX, maxZ - t); } private static void ScaffoldDedup(List poles, float minDist) { for (int num = poles.Count - 1; num > 0; num--) { if (poles[num] - poles[num - 1] < minDist) { poles.RemoveAt(num); } } } private static bool IsNearAnyParam(float t, List values, float tolerance) { for (int i = 0; i < values.Count; i++) { if (Mathf.Abs(values[i] - t) <= tolerance) { return true; } } return false; } private static bool IsNearAnyDoor(Vector2 local, List doorCenters, float radius) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) float num = radius * radius; for (int i = 0; i < doorCenters.Count; i++) { Vector2 val = local - doorCenters[i]; if (((Vector2)(ref val)).sqrMagnitude <= num) { return true; } } return false; } private static bool IsNearAnyPole(Vector2 local, List poleLocals, float minDistance) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) float num = minDistance * minDistance; for (int i = 0; i < poleLocals.Count; i++) { Vector2 val = local - poleLocals[i]; if (((Vector2)(ref val)).sqrMagnitude < num) { return true; } } return false; } private static List BuildScaffoldFurnitureExclusions(FloorPlan plan) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_00f3: 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_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Vector2 center = default(Vector2); Vector2 side = default(Vector2); foreach (FloorPlanPiece piece in plan.Pieces) { if (!(piece.Type != "Workbench") || !(piece.Type != "Bed") || !(piece.Type != "Hearth")) { PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { int num = def.EffW(piece.Rotation); int num2 = def.EffH(piece.Rotation); ((Vector2)(ref center))..ctor(((float)piece.Col + (float)num * 0.5f) * 1f, ((float)piece.Row + (float)num2 * 0.5f) * 1f); Vector2 scaffoldFrontDirection = GetScaffoldFrontDirection(piece.Rotation); ((Vector2)(ref side))..ctor(0f - scaffoldFrontDirection.y, scaffoldFrontDirection.x); bool flag = Mathf.Abs(scaffoldFrontDirection.x) > 0.5f; float forwardHalfExtent = (float)(flag ? num : num2) * 0.5f * 1f; float sideHalfExtent = (float)(flag ? num2 : num) * 0.5f * 1f + 0.35f; list.Add(new ScaffoldFurnitureExclusion(center, scaffoldFrontDirection, side, forwardHalfExtent, sideHalfExtent, 4f)); } } } return list; } private static Vector2 GetScaffoldFrontDirection(int rotation) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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) return (Vector2)(((rotation % 360 + 360) % 360) switch { 90 => new Vector2(-1f, 0f), 180 => new Vector2(0f, -1f), 270 => new Vector2(1f, 0f), _ => new Vector2(0f, 1f), }); } private static bool IsInsideAnyScaffoldFurnitureExclusion(Vector2 local, List exclusions) { //IL_0011: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < exclusions.Count; i++) { ScaffoldFurnitureExclusion scaffoldFurnitureExclusion = exclusions[i]; Vector2 val = local - scaffoldFurnitureExclusion.Center; float num = Vector2.Dot(val, scaffoldFurnitureExclusion.Forward); float num2 = Mathf.Abs(Vector2.Dot(val, scaffoldFurnitureExclusion.Side)); bool flag = Mathf.Abs(num) <= scaffoldFurnitureExclusion.ForwardHalfExtent && num2 <= scaffoldFurnitureExclusion.SideHalfExtent; bool flag2 = num > scaffoldFurnitureExclusion.ForwardHalfExtent && num <= scaffoldFurnitureExclusion.ForwardHalfExtent + scaffoldFurnitureExclusion.FrontClearance && num2 <= scaffoldFurnitureExclusion.SideHalfExtent; if (flag || flag2) { return true; } } return false; } private static bool IsWithinHorizontalRadius(Vector3 position, Vector3 center, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) float num = position.x - center.x; float num2 = position.z - center.z; float num3 = Mathf.Max(0f, radius); return num * num + num2 * num2 <= num3 * num3; } private static bool IsWithinAnyDoorSpan(float t, List spans, float tolerance) { for (int i = 0; i < spans.Count; i++) { if (t >= spans[i].Min - tolerance && t <= spans[i].Max + tolerance) { return true; } } return false; } private void PruneGroundFloorScaffoldVerticals(List doorCenters, Vector3 centerWorld, Vector3 origin, float rotationDeg) { //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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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) //IL_0123: Unknown result type (might be due to invalid IL or missing references) if (_groundFloorScaffoldVerticals.Count == 0) { return; } float num = Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); float num2 = Mathf.Sin(rotationDeg * ((float)Math.PI / 180f)); List list = new List(doorCenters.Count); for (int i = 0; i < doorCenters.Count; i++) { Vector2 val = doorCenters[i]; float num3 = origin.x + val.x * num + val.y * num2; float num4 = origin.z - val.x * num2 + val.y * num; list.Add(new Vector3(num3, centerWorld.y, num4)); } int num5 = 0; for (int num6 = _groundFloorScaffoldVerticals.Count - 1; num6 >= 0; num6--) { GameObject val2 = _groundFloorScaffoldVerticals[num6]; if ((Object)(object)val2 == (Object)null) { _groundFloorScaffoldVerticals.RemoveAt(num6); } else { bool flag = false; if (!flag) { for (int j = 0; j < list.Count; j++) { if (IsWithinHorizontalRadius(val2.transform.position, list[j], 4.25f)) { flag = true; break; } } } if (flag) { _lastPlaced.Remove(val2); _groundFloorScaffoldVerticals.RemoveAt(num6); if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val2); } else { Object.Destroy((Object)(object)val2); } num5++; } } } if (num5 > 0) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[Scaffolding] Pruned {num5} ground-floor vertical poles near doors after scaffolding completed."); } } private int SpawnScaffoldColumn(GameObject prefab, Vector3 center, Quaternion rot, Player player, float columnHeight, float segmentHeight) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(1, Mathf.RoundToInt(columnHeight / segmentHeight)); float num2 = center.y - columnHeight * 0.5f + segmentHeight * 0.5f; Vector3 pos = default(Vector3); for (int i = 0; i < num; i++) { ((Vector3)(ref pos))..ctor(center.x, num2 + (float)i * segmentHeight, center.z); SpawnScaffoldPole(prefab, pos, rot, player); } return num; } private GameObject SpawnRegisteredPiece(GameObject prefab, Vector3 pos, Quaternion rot, Player player, bool centerOnRenderedBoundsXZ = false) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(prefab, pos, rot); ZNetView component = val.GetComponent(); ZDO val2 = null; if ((Object)(object)component != (Object)null) { val2 = component.GetZDO(); if (centerOnRenderedBoundsXZ) { CenterPieceOnRenderedBoundsXZ(val, pos); if (val2 != null) { val2.SetPosition(val.transform.position); } } if (val2 != null) { val2.SetOwner(ZDOMan.GetSessionID()); val2.Set("vfp_build", "1"); } } _lastPlaced.Add(val); Piece component2 = val.GetComponent(); if (component2 != null) { component2.SetCreator(player.GetPlayerID()); } return val; } private GameObject SpawnScaffoldPole(GameObject prefab, Vector3 pos, Quaternion rot, Player player) { //IL_0003: 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 SpawnRegisteredPiece(prefab, pos, rot, player); } private static List BuildArcPerimeterPolygon(List> chains) { //IL_0289: Unknown result type (might be due to invalid IL or missing references) if (chains.Count == 0) { return new List(); } List> list = new List>(chains); List list2 = new List(chains.Count * 20); foreach (FlexiWallSegment item in list[0]) { list2.Add(item); } list.RemoveAt(0); while (list.Count > 0) { FlexiWallSegment flexiWallSegment = list2[list2.Count - 1]; float lx = flexiWallSegment.Lx; float lz = flexiWallSegment.Lz; int index = 0; bool flag = false; float num = float.MaxValue; for (int i = 0; i < list.Count; i++) { List list3 = list[i]; float num2 = Mathf.Sqrt((list3[0].Lx - lx) * (list3[0].Lx - lx) + (list3[0].Lz - lz) * (list3[0].Lz - lz)); float num3 = Mathf.Sqrt((list3[list3.Count - 1].Lx - lx) * (list3[list3.Count - 1].Lx - lx) + (list3[list3.Count - 1].Lz - lz) * (list3[list3.Count - 1].Lz - lz)); if (num2 < num) { num = num2; index = i; flag = false; } if (num3 < num) { num = num3; index = i; flag = true; } } List list4 = list[index]; list.RemoveAt(index); if (flag) { for (int num4 = list4.Count - 1; num4 >= 0; num4--) { list2.Add(list4[num4]); } continue; } foreach (FlexiWallSegment item2 in list4) { list2.Add(item2); } } List list5 = new List(list2.Count); foreach (FlexiWallSegment item3 in list2) { list5.Add(new Vector2(item3.Lx, item3.Lz)); } return list5; } private static bool IsInsideArcPolygon(float localX, float localZ, List polygon) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (polygon == null || polygon.Count < 3) { return false; } int count = polygon.Count; bool flag = false; int num = 0; int index = count - 1; while (num < count) { float x = polygon[num].x; float y = polygon[num].y; float x2 = polygon[index].x; float y2 = polygon[index].y; if (y > localZ != y2 > localZ && localX < (x2 - x) * (localZ - y) / (y2 - y) + x) { flag = !flag; } index = num++; } return flag; } private static bool IsInsideOrNearArcPolygon(float localX, float localZ, float margin, List polygon) { return IsInsideArcPolygon(localX, localZ, polygon) || IsInsideArcPolygon(localX - margin, localZ, polygon) || IsInsideArcPolygon(localX + margin, localZ, polygon) || IsInsideArcPolygon(localX, localZ - margin, polygon) || IsInsideArcPolygon(localX, localZ + margin, polygon); } private static bool TryGetArcBoundaryX(float localZ, List polygon, bool findMin, out float boundaryX) { //IL_0029: 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_005b: 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) boundaryX = (findMin ? float.MaxValue : float.MinValue); bool flag = false; int count = polygon.Count; int num = 0; int index = count - 1; while (num < count) { float y = polygon[num].y; float y2 = polygon[index].y; if (y > localZ != y2 > localZ) { float x = polygon[num].x; float x2 = polygon[index].x; float num2 = x + (localZ - y) / (y2 - y) * (x2 - x); if (!flag) { boundaryX = num2; flag = true; } else if (findMin && num2 < boundaryX) { boundaryX = num2; } else if (!findMin && num2 > boundaryX) { boundaryX = num2; } } index = num++; } return flag; } } public sealed class PieceDef { public readonly string Prefab; public readonly int BaseW; public readonly int BaseH; public readonly float YOffset; public readonly int RotationOffset; public PieceDef(string prefab, int baseW, int baseH, float yOffset = 0f, int rotationOffset = 0) { Prefab = prefab; BaseW = baseW; BaseH = baseH; YOffset = yOffset; RotationOffset = rotationOffset; } public int EffW(int rotation) { return (rotation == 90 || rotation == 270) ? BaseH : BaseW; } public int EffH(int rotation) { return (rotation == 90 || rotation == 270) ? BaseW : BaseH; } } public static class PieceMap { public const float CELL_SIZE = 1f; private static readonly Dictionary Map = new Dictionary { { "Floor2x2", new PieceDef("wood_floor", 2, 2) }, { "Floor1x1", new PieceDef("wood_floor_1x1", 1, 1) }, { "Bed", new PieceDef("bed", 2, 4, 0f, 180) }, { "Staircase", new PieceDef("wood_floor_1x1", 4, 4) }, { "Workbench", new PieceDef("piece_workbench", 4, 4) }, { "Wall", new PieceDef("stone_wall_2x1", 2, 1, 0.5f) }, { "FlexiWall", new PieceDef("stone_wall_1x1", 1, 1, 0.5f) }, { "Doorway", new PieceDef("wood_door", 2, 1, 1f) }, { "Pillar", new PieceDef("stone_pillar", 1, 1, 1f) }, { "Hearth", new PieceDef("hearth", 4, 3) } }; public static Vector2 TransformLocalXZ(float localX, float localZ, float rotationDeg) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) float num = rotationDeg * ((float)Math.PI / 180f); float num2 = Mathf.Cos(num); float num3 = Mathf.Sin(num); return new Vector2((0f - localX) * num2 + localZ * num3, localX * num3 + localZ * num2); } public static Vector3 TransformPlanPoint(Vector3 origin, float localX, float localZ, float y, float rotationDeg) { //IL_0005: 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_000b: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Vector2 val = TransformLocalXZ(localX, localZ, rotationDeg); return new Vector3(origin.x + val.x, y, origin.z + val.y); } public static float TransformLocalYaw(float localYawDeg, float planRotationDeg) { return NormalizeAngleDeg(planRotationDeg - localYawDeg); } private static float NormalizeAngleDeg(float angleDeg) { angleDeg %= 360f; if (angleDeg < 0f) { angleDeg += 360f; } return angleDeg; } public static PieceDef? GetDef(string vfpType) { PieceDef value; return Map.TryGetValue(vfpType, out value) ? value : null; } public static string? GetPrefab(string vfpType) { return GetDef(vfpType)?.Prefab; } } public static class TerrainLeveler { public enum EdgeRiskLevel { Low, Medium, High } private struct EdgeRiskHotspot { public Vector3 Position; public float Score; public EdgeRiskHotspot(Vector3 position, float score) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) Position = position; Score = score; } } private const float PRE_SAMPLE_STEP = 0.5f; private const float LEVEL_SAMPLE_STEP = 0.5f; private const float EDGE_LEVEL_SAMPLE_STEP = 0.25f; private const float EDGE_BAND_WIDTH = 0.6f; private const float EDGE_LEVEL_RADIUS = 1.8f; private const float EDGE_RAISE_EPSILON = 0.03f; private const float STAGE_RAISE_EPSILON = 0.02f; private const float SPIKE_SCAN_STEP = 0.5f; private const float SPIKE_LEVEL_RADIUS = 1.5f; private const float SPIKE_TOLERANCE = 0.2f; private const float TEAR_REPAIR_SAMPLE_RADIUS = 1.2f; private const float TEAR_REPAIR_MAIN_RADIUS = 1.1f; private const float TEAR_REPAIR_BLEND_RADIUS = 0.7f; private const float TEAR_REPAIR_SECOND_BLEND_RADIUS = 1.05f; private const float TEAR_REPAIR_MAX_LOWER = 0.45f; private const float TEAR_REPAIR_SPIKE_THRESHOLD = 0.28f; private const float TEAR_REPAIR_CUT_RADIUS = 0.42f; private const float TEAR_REPAIR_RAISE_TOLERANCE = 0.01f; private const float TEAR_REPAIR_MIN_RELIEF = 0.08f; private const float TEAR_REPAIR_EDGE_RADIUS = 0.48f; private const float TEAR_REPAIR_EDGE_LENGTH = 1.25f; private const float TEAR_REPAIR_EDGE_STEP = 0.32f; private const float TEAR_REPAIR_EDGE_MAX_RAISE = 0.14f; private const float TEAR_REPAIR_EDGE_MEDIAN_HEADROOM = 0.08f; private const float TERRAIN_CLIP_HEIGHT_TOLERANCE = 0.02f; private const int INNER_PAD = 2; private const float WARN_RAISE = 6f; private const int OPS_PER_FRAME = 10; private static float LEVEL_RADIUS => Mathf.Clamp(ValheimFloorPlanPlugin.TerrainStampRadius, 3f, 6f); public static float TargetLevelY { get; private set; } = 0f; public static float RecommendedPlacementWait { get; private set; } = 2f; public static float GetOuterPerimeterDelta() { return 2f + LEVEL_RADIUS; } public static IEnumerator LevelForPlan(FloorPlan plan, Vector3 origin, float rotationDeg = 0f) { //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) GetLocalBounds(plan, 2, out var innerMinX, out var innerMaxX, out var innerMinZ, out var innerMaxZ); Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); Mathf.Sin(rotationDeg * ((float)Math.PI / 180f)); bool axisAligned = Mathf.Approximately(rotationDeg % 90f, 0f); float derivedStep = Mathf.Min(0.5f, LEVEL_RADIUS * 0.17f); float preSampleStep = (axisAligned ? Mathf.Min(0.5f, LEVEL_RADIUS * 0.17f) : 0.25f); float levelSampleStep = (axisAligned ? derivedStep : Mathf.Min(0.25f, derivedStep)); float spikeScanStep = (axisAligned ? 0.5f : 0.25f); float sampleMinX = innerMinX - LEVEL_RADIUS; float sampleMaxX = innerMaxX + LEVEL_RADIUS; float sampleMinZ = innerMinZ - LEVEL_RADIUS; float sampleMaxZ = innerMaxZ + LEVEL_RADIUS; float maxY = float.MinValue; float minY = float.MaxValue; int preStepsX = Mathf.CeilToInt((sampleMaxX - sampleMinX) / preSampleStep); int preStepsZ = Mathf.CeilToInt((sampleMaxZ - sampleMinZ) / preSampleStep); for (int ix = 0; ix <= preStepsX; ix++) { float lx = ((ix == preStepsX) ? sampleMaxX : (sampleMinX + (float)ix * preSampleStep)); for (int iz = 0; iz <= preStepsZ; iz++) { float lz = ((iz == preStepsZ) ? sampleMaxZ : (sampleMinZ + (float)iz * preSampleStep)); Vector3 samplePos = PieceMap.TransformPlanPoint(origin, lx, lz, origin.y, rotationDeg); float wx = samplePos.x; float wz = samplePos.z; float h = SampleHeight(wx, wz, origin.y); if (h > maxY) { maxY = h; } if (h < minY) { minY = h; } } } if (maxY == float.MinValue) { maxY = origin.y; minY = origin.y; } float range = maxY - minY; float highPointDelta = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainHighPointDelta, 0f, 4f); float targetY = (TargetLevelY = maxY + highPointDelta); if (range > 6f) { ValheimFloorPlanPlugin.Log.LogWarning((object)($"[TerrainLeveler] Footprint height range {range:F1}m — steep ground," + " cliff-edge tears may appear on the downhill side.")); ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, $"ValheimFloorPlan: Steep terrain warning. Height range is {range:F1}m. " + "Terracing or downhill tears may occur."); } ValheimFloorPlanPlugin.Log.LogInfo((object)($"[TerrainLeveler] Raising pad to Y={targetY:F2} (maxY={maxY:F2} + delta={highPointDelta:F2}) minY={minY:F2} range={range:F1}m" + $" rotation={rotationDeg:F0}°" + $" inner(local)=[{innerMinX:F1}..{innerMaxX:F1}] x [{innerMinZ:F1}..{innerMaxZ:F1}]")); int totalPasses = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainLevelPasses, 1, 5); bool stagedRaise = ValheimFloorPlanPlugin.TerrainUseStagedRaise; float raiseStepHeight = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainRaiseStepHeight, 0.15f, 1.5f); int maxRaiseStages = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainMaxRaiseStages, 1, 16); bool skipSatisfiedCenterStamps = ValheimFloorPlanPlugin.TerrainSkipSatisfiedCenterStamps; float totalRaiseHeight = targetY - minY; int stageCount = 1; if (stagedRaise && totalRaiseHeight > 0.02f) { stageCount = Mathf.Clamp(Mathf.CeilToInt(totalRaiseHeight / raiseStepHeight), 1, maxRaiseStages); } float stageHeight = ((stageCount > 0) ? (totalRaiseHeight / (float)stageCount) : totalRaiseHeight); int stepsX = Mathf.CeilToInt((innerMaxX - innerMinX) / levelSampleStep); int stepsZ = Mathf.CeilToInt((innerMaxZ - innerMinZ) / levelSampleStep); float edgeSampleStep = Mathf.Min(levelSampleStep, 0.25f); int edgeStepsX = Mathf.CeilToInt((innerMaxX - innerMinX) / edgeSampleStep); int edgeStepsZ = Mathf.CeilToInt((innerMaxZ - innerMinZ) / edgeSampleStep); int edgePointsPerPass = 0; for (int i = 0; i <= edgeStepsX; i++) { float lx2 = ((i == edgeStepsX) ? innerMaxX : (innerMinX + (float)i * edgeSampleStep)); for (int j = 0; j <= edgeStepsZ; j++) { float lz2 = ((j == edgeStepsZ) ? innerMaxZ : (innerMinZ + (float)j * edgeSampleStep)); if (IsInEdgeBand(lx2, lz2, innerMinX, innerMaxX, innerMinZ, innerMaxZ, 0.6f)) { edgePointsPerPass++; } } } int ops = 0; HashSet modified = new HashSet(); int totalLevelOps = stageCount * totalPasses * ((stepsX + 1) * (stepsZ + 1) + edgePointsPerPass); int nextLevelPct = 5; float nextLevelHeartbeatAt = Time.time + 1.5f; int spinnerIndex = 0; string[] spinnerFrames = new string[4] { "|", "/", "-", "\\" }; ValheimFloorPlanPlugin.Log.LogInfo((object)($"[TerrainLeveler] Running {totalPasses} leveling pass(es) across {stageCount} raise stage(s) " + $"(range={range:F1}m, totalRaise={totalRaiseHeight:F1}m, staged={stagedRaise}, stageStep={raiseStepHeight:F2}m).")); ShowProgress($"Leveling terrain... 0% ({totalPasses} pass(es), {stageCount} stage(s))"); for (int stage = 1; stage <= stageCount; stage++) { float stageTargetY = ((stage == stageCount) ? targetY : Mathf.Min(targetY, minY + stageHeight * (float)stage)); for (int pass = 1; pass <= totalPasses; pass++) { float effectiveEdgeRadius = Mathf.Min(1.8f, LEVEL_RADIUS); for (int k = 0; k <= stepsX; k++) { float lx3 = ((k == stepsX) ? innerMaxX : (innerMinX + (float)k * levelSampleStep)); for (int l = 0; l <= stepsZ; l++) { float lz3 = ((l == stepsZ) ? innerMaxZ : (innerMinZ + (float)l * levelSampleStep)); Vector3 levelPos = PieceMap.TransformPlanPoint(origin, lx3, lz3, origin.y, rotationDeg); float wx2 = levelPos.x; float wz2 = levelPos.z; float h2 = SampleHeight(wx2, wz2, stageTargetY); if (!skipSatisfiedCenterStamps || h2 < stageTargetY - 0.02f) { ApplyLevel(wx2, stageTargetY, wz2, LEVEL_RADIUS, modified); } ops++; if (totalLevelOps > 0) { int pct = Mathf.FloorToInt((float)ops * 100f / (float)totalLevelOps); bool emitProgress = false; if (pct >= nextLevelPct) { for (; nextLevelPct <= pct; nextLevelPct += 5) { } emitProgress = true; } if (Time.time >= nextLevelHeartbeatAt) { nextLevelHeartbeatAt = Time.time + 1.5f; spinnerIndex = (spinnerIndex + 1) % spinnerFrames.Length; emitProgress = true; } if (emitProgress) { int shownPct = Mathf.Clamp(pct, 0, 99); ShowProgress($"Leveling terrain... {shownPct}% {spinnerFrames[spinnerIndex]} " + $"(stage {stage}/{stageCount}, pass {pass}/{totalPasses})"); } } if (ops % 10 == 0) { yield return null; } } } for (int m = 0; m <= edgeStepsX; m++) { float lx4 = ((m == edgeStepsX) ? innerMaxX : (innerMinX + (float)m * edgeSampleStep)); for (int n = 0; n <= edgeStepsZ; n++) { float lz4 = ((n == edgeStepsZ) ? innerMaxZ : (innerMinZ + (float)n * edgeSampleStep)); if (!IsInEdgeBand(lx4, lz4, innerMinX, innerMaxX, innerMinZ, innerMaxZ, 0.6f)) { continue; } Vector3 edgePos = PieceMap.TransformPlanPoint(origin, lx4, lz4, origin.y, rotationDeg); float wx3 = edgePos.x; float wz3 = edgePos.z; float h3 = SampleHeight(wx3, wz3, stageTargetY); if (h3 >= stageTargetY - 0.03f) { continue; } ApplyLevel(wx3, stageTargetY, wz3, effectiveEdgeRadius, modified); ops++; if (totalLevelOps > 0) { int pct2 = Mathf.FloorToInt((float)ops * 100f / (float)totalLevelOps); bool emitProgress2 = false; if (pct2 >= nextLevelPct) { for (; nextLevelPct <= pct2; nextLevelPct += 5) { } emitProgress2 = true; } if (Time.time >= nextLevelHeartbeatAt) { nextLevelHeartbeatAt = Time.time + 1.5f; spinnerIndex = (spinnerIndex + 1) % spinnerFrames.Length; emitProgress2 = true; } if (emitProgress2) { int shownPct2 = Mathf.Clamp(pct2, 0, 99); ShowProgress($"Leveling terrain... {shownPct2}% {spinnerFrames[spinnerIndex]} " + $"(stage {stage}/{stageCount}, pass {pass}/{totalPasses})"); } } if (ops % 10 == 0) { yield return null; } } } if (pass < totalPasses) { yield return (object)new WaitForSeconds(0.1f); } } if (stage < stageCount) { yield return (object)new WaitForSeconds(0.08f); } } int spikePasses = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainSpikeCleanupPasses, 1, 5); int spikeOps = 0; int spikeStepsX = Mathf.CeilToInt((sampleMaxX - sampleMinX) / spikeScanStep); int spikeStepsZ = Mathf.CeilToInt((sampleMaxZ - sampleMinZ) / spikeScanStep); float nextCleanupProgressAt = Time.time + 1.5f; for (int num2 = 1; num2 <= spikePasses; num2++) { for (int num3 = 0; num3 <= spikeStepsX; num3++) { float lx5 = ((num3 == spikeStepsX) ? sampleMaxX : (sampleMinX + (float)num3 * spikeScanStep)); for (int num4 = 0; num4 <= spikeStepsZ; num4++) { float lz5 = ((num4 == spikeStepsZ) ? sampleMaxZ : (sampleMinZ + (float)num4 * spikeScanStep)); Vector3 spikePos = PieceMap.TransformPlanPoint(localX: lx5 - origin.x, localZ: lz5 - origin.z, origin: origin, y: origin.y, rotationDeg: rotationDeg); float wx4 = spikePos.x; float wz4 = spikePos.z; float h4 = SampleHeight(wx4, wz4, targetY); if (h4 > targetY + 0.2f) { ApplyLevel(wx4, targetY, wz4, 1.5f, modified); spikeOps++; if (spikeOps % 10 == 0) { yield return null; } } if (Time.time >= nextCleanupProgressAt) { nextCleanupProgressAt = Time.time + 1.5f; ShowProgress($"Leveling terrain... cleanup (pass {num2}/{spikePasses})"); } } } if (num2 < spikePasses) { yield return (object)new WaitForSeconds(0.05f); } } if (spikeOps > 0) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainLeveler] Spike suppression applied: {spikeOps} ops across {spikePasses} pass(es)."); ShowProgress("Leveling terrain... final cleanup"); } else { ValheimFloorPlanPlugin.Log.LogInfo((object)"[TerrainLeveler] Spike suppression: no residual peaks detected."); } RecommendedPlacementWait = Mathf.Max(2f, (float)modified.Count * 0.5f); ValheimFloorPlanPlugin.Log.LogInfo((object)($"[TerrainLeveler] Leveling done: {totalPasses} passes, {ops} ops, " + $"{modified.Count} chunks. Placement wait: {RecommendedPlacementWait:F1}s.")); ShowProgress("Leveling terrain... done"); } public static bool IsRepairTargetValid(Vector3 point, out string reason) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) float referenceY = SampleHeight(point.x, point.z, point.y); Vector2 slopeDir; float ridgeY; float troughY; List list = CollectRepairSamples(point, referenceY, out slopeDir, out ridgeY, out troughY); if (list.Count == 0) { reason = "Unable to sample terrain."; return false; } list.Sort(); float num = list[0]; float num2 = list[list.Count - 1]; float num3 = num2 - num; if (num3 < 0.08f) { reason = "Area is too flat — no tear detected here."; return false; } reason = string.Empty; return true; } public static IEnumerator RepairTearAtPoint(Vector3 point) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) HashSet modified = new HashSet(); float centerY = SampleHeight(point.x, point.z, point.y); Vector2 slopeDir; float ridgeY; float troughY; List samples = CollectRepairSamples(point, centerY, out slopeDir, out ridgeY, out troughY); samples.Sort(); float medianY = ((samples.Count > 0) ? samples[samples.Count / 2] : centerY); float lowerTargetY = Mathf.Min(centerY, medianY + 0.03f); lowerTargetY = Mathf.Max(centerY - 0.45f, lowerTargetY); Vector2[] passOffsets = (Vector2[])(object)new Vector2[5] { new Vector2(0f, 0f), new Vector2(0.55f, 0f), new Vector2(-0.55f, 0f), new Vector2(0f, 0.55f), new Vector2(0f, -0.55f) }; int opCount = 0; Vector2[] array = passOffsets; for (int i = 0; i < array.Length; i++) { Vector2 o = array[i]; ApplySmooth(point.x + o.x, lowerTargetY, point.z + o.y, 1.1f, modified); opCount++; if (opCount % 10 == 0) { yield return null; } } yield return null; Vector2[] array2 = passOffsets; for (int j = 0; j < array2.Length; j++) { Vector2 o2 = array2[j]; ApplySmooth(point.x + o2.x, lowerTargetY, point.z + o2.y, 0.7f, modified); opCount++; if (opCount % 10 == 0) { yield return null; } } yield return null; int bridgeOps = ApplyEdgeBridge(point, lowerTargetY, slopeDir, ridgeY, troughY, modified); opCount += bridgeOps; if (opCount % 10 == 0) { yield return null; } yield return null; int cutOps = 0; Vector2[] array3 = passOffsets; for (int k = 0; k < array3.Length; k++) { Vector2 o3 = array3[k]; float sx = point.x + o3.x; float sz = point.z + o3.y; float currentY = SampleHeight(sx, sz, lowerTargetY); float localMedianY = GetLocalMedianHeight(sx, sz, currentY, 0.6f); if (currentY <= localMedianY + 0.28f) { continue; } float cutY = Mathf.Min(currentY - 0.03f, localMedianY + 0.02f); cutY = Mathf.Max(lowerTargetY, cutY); if (CanLowerWithoutRaise(sx, sz, cutY, 0.42f, currentY)) { ApplyLevel(sx, cutY, sz, 0.42f, modified, smooth: true); cutOps++; opCount++; if (opCount % 10 == 0) { yield return null; } } } yield return null; Vector2[] array4 = passOffsets; for (int l = 0; l < array4.Length; l++) { Vector2 o4 = array4[l]; ApplySmooth(point.x + o4.x, lowerTargetY, point.z + o4.y, 1.05f, modified); opCount++; if (opCount % 10 == 0) { yield return null; } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainLeveler] Tear repair at {point} -> centerY={centerY:F2}, medianY={medianY:F2}, lowerTargetY={lowerTargetY:F2}, bridgeOps={bridgeOps}, cutOps={cutOps}, ops={opCount}, chunks={modified.Count}."); } public static IEnumerator ClipTerrainCircle(Vector3 center, float targetY, float radius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) radius = Mathf.Clamp(radius, 1f, 12f); float digRadius = Mathf.Clamp(radius * 0.12f, 0.34f, 0.9f); float step = Mathf.Clamp(digRadius * 0.85f, 0.3f, 0.85f); float medianProbeRadius = Mathf.Clamp(radius * 0.14f, 0.45f, 1.25f); float r2 = radius * radius; int totalSamples = 0; for (float x = center.x - radius; x <= center.x + radius + 0.01f; x += step) { for (float z = center.z - radius; z <= center.z + radius + 0.01f; z += step) { float dx = x - center.x; float dz = z - center.z; if (dx * dx + dz * dz <= r2) { totalSamples++; } } } if (totalSamples == 0) { yield break; } ShowProgress("Repairing terrain... 0%"); HashSet modified = new HashSet(); int processed = 0; int lowerOps = 0; int candidateOps = 0; int skippedByNoRaise = 0; int skippedBySpacing = 0; int nextPct = 20; for (int pass = 0; pass < 3; pass++) { float passDigRadius = ((pass == 0) ? digRadius : Mathf.Max(0.28f, digRadius * 0.82f)); float passStep = ((pass == 0) ? step : Mathf.Max(0.28f, step * 0.9f)); float minStampSpacing = Mathf.Max(0.62f, passDigRadius * 1.8f); float minStampSpacingSqr = minStampSpacing * minStampSpacing; List passStampCenters = new List(128); for (float x2 = center.x - radius; x2 <= center.x + radius + 0.01f; x2 += passStep) { for (float z2 = center.z - radius; z2 <= center.z + radius + 0.01f; z2 += passStep) { float dx2 = x2 - center.x; float dz2 = z2 - center.z; if (dx2 * dx2 + dz2 * dz2 > r2) { continue; } float currentY = SampleHeight(x2, z2, targetY); float localMedianY = GetLocalMedianHeight(x2, z2, currentY, medianProbeRadius); float desiredY = Mathf.Min(targetY, localMedianY + 0.06f); float outlier = currentY - localMedianY; bool aboveCap = currentY > targetY + 0.02f; bool highOutlier = currentY > desiredY + 0.02f && outlier > 0.14f; if (aboveCap || highOutlier) { bool tooCloseToExisting = false; for (int i = 0; i < passStampCenters.Count; i++) { Vector2 p = passStampCenters[i]; float px = x2 - p.x; float pz = z2 - p.y; if (px * px + pz * pz < minStampSpacingSqr) { tooCloseToExisting = true; break; } } if (tooCloseToExisting) { skippedBySpacing++; continue; } candidateOps++; float guardRadius = Mathf.Clamp(passDigRadius * 0.6f, 0.24f, passDigRadius); if (CanLowerWithoutRaise(x2, z2, desiredY, guardRadius, currentY, 0.08f)) { ApplySpikeCollapse(x2, z2, currentY, desiredY, localMedianY, passDigRadius, modified); lowerOps++; passStampCenters.Add(new Vector2(x2, z2)); } else { skippedByNoRaise++; } } if (pass == 0) { processed++; int pct = Mathf.FloorToInt((float)processed * 100f / (float)totalSamples); if (pct >= nextPct) { ShowProgress($"Repairing terrain... {nextPct}%"); nextPct += 20; } } if ((processed + lowerOps) % 10 == 0) { yield return null; } } } yield return null; } ShowProgress((lowerOps > 0) ? "Repairing terrain... done" : "Repairing terrain... no repairs needed"); ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainLeveler] Terrain repair-disc at {center} -> targetY={targetY:F2}, radius={radius:F2}, digRadius={digRadius:F2}, probeRadius={medianProbeRadius:F2}, passes={3}, candidates={candidateOps}, skippedBySpacing={skippedBySpacing}, skippedByNoRaise={skippedByNoRaise}, lowerOps={lowerOps}, chunks={modified.Count}."); } public static float SampleTerrainHeight(float x, float z, float referenceY = 0f) { return SampleHeight(x, z, referenceY); } public static EdgeRiskLevel EvaluateEdgeRisk(FloorPlan plan, Vector3 origin, float rotationDeg, out float edgeRelief, out float irregularity, out float maxEdgeStep, List? hotspotPoints = null) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0244: 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) GetLocalBounds(plan, 2, out var minX, out var maxX, out var minZ, out var maxZ); float num = minX - LEVEL_RADIUS; float num2 = maxX + LEVEL_RADIUS; float num3 = minZ - LEVEL_RADIUS; float num4 = maxZ + LEVEL_RADIUS; float num5 = Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); float num6 = Mathf.Sin(rotationDeg * ((float)Math.PI / 180f)); float edgeMinY = float.MaxValue; float edgeMaxY = float.MinValue; float roughAccum = 0f; int roughCount = 0; float localMaxEdgeStep = 0f; List hotspots = ((hotspotPoints != null) ? new List(64) : null); SampleEdgeRiskLine(num, num3, num, num4, 1f, 0f); SampleEdgeRiskLine(num2, num3, num2, num4, -1f, 0f); SampleEdgeRiskLine(num, num3, num2, num3, 0f, 1f); SampleEdgeRiskLine(num, num4, num2, num4, 0f, -1f); if (edgeMaxY == float.MinValue || edgeMinY == float.MaxValue || roughCount == 0) { edgeRelief = 0f; irregularity = 0f; maxEdgeStep = 0f; return EdgeRiskLevel.Low; } edgeRelief = edgeMaxY - edgeMinY; irregularity = roughAccum / (float)roughCount; maxEdgeStep = localMaxEdgeStep; if (hotspotPoints != null) { hotspotPoints.Clear(); if (hotspots != null && hotspots.Count > 0) { hotspots.Sort((EdgeRiskHotspot a, EdgeRiskHotspot b) => b.Score.CompareTo(a.Score)); for (int num7 = 0; num7 < hotspots.Count; num7++) { if (hotspotPoints.Count >= 12) { break; } Vector3 position = hotspots[num7].Position; bool flag = false; for (int num8 = 0; num8 < hotspotPoints.Count; num8++) { Vector3 val = hotspotPoints[num8]; float num9 = position.x - val.x; float num10 = position.z - val.z; if (num9 * num9 + num10 * num10 < 2.5600002f) { flag = true; break; } } if (!flag) { hotspotPoints.Add(position); } } } } if (maxEdgeStep >= 1.4f || irregularity >= 0.55f || edgeRelief >= 5f) { return EdgeRiskLevel.High; } if (maxEdgeStep >= 0.9f || irregularity >= 0.32f || edgeRelief >= 3f) { return EdgeRiskLevel.Medium; } return EdgeRiskLevel.Low; void SampleEdgeRiskLine(float x0, float z0, float x1, float z1, float inNx, float inNz) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0186: 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) float num11 = x1 - x0; float num12 = z1 - z0; float num13 = Mathf.Sqrt(num11 * num11 + num12 * num12); int num14 = Mathf.Max(1, Mathf.CeilToInt(num13 / 0.75f)); for (int i = 0; i <= num14; i++) { float num15 = (float)i / (float)num14; float num16 = Mathf.Lerp(x0, x1, num15); float num17 = Mathf.Lerp(z0, z1, num15); float localX = num16 - origin.x; float localZ = num17 - origin.z; Vector3 val2 = PieceMap.TransformPlanPoint(origin, localX, localZ, origin.y, rotationDeg); float x2 = val2.x; float z2 = val2.z; float num18 = num16 + inNx * 0.8f; float num19 = num17 + inNz * 0.8f; float localX2 = num18 - origin.x; float localZ2 = num19 - origin.z; Vector3 val3 = PieceMap.TransformPlanPoint(origin, localX2, localZ2, origin.y, rotationDeg); float x3 = val3.x; float z3 = val3.z; float num20 = num16 - inNx * 0.8f; float num21 = num17 - inNz * 0.8f; float localX3 = num20 - origin.x; float localZ3 = num21 - origin.z; Vector3 val4 = PieceMap.TransformPlanPoint(origin, localX3, localZ3, origin.y, rotationDeg); float x4 = val4.x; float z4 = val4.z; float num22 = SampleHeight(x2, z2, origin.y); float num23 = SampleHeight(x3, z3, num22); float num24 = SampleHeight(x4, z4, num22); if (num22 < edgeMinY) { edgeMinY = num22; } if (num22 > edgeMaxY) { edgeMaxY = num22; } float num25 = Mathf.Abs(num23 - num24); if (num25 > localMaxEdgeStep) { localMaxEdgeStep = num25; } float num26 = Mathf.Abs(num22 - (num23 + num24) * 0.5f); roughAccum += num26; roughCount++; if (hotspots != null) { float num27 = num25 + num26 * 1.35f; if (num27 >= 0.62f && (num25 >= 0.5f || num26 >= 0.24f)) { hotspots.Add(new EdgeRiskHotspot(new Vector3(x2, num22, z2), num27)); } } } } } public static void GetPadBounds(FloorPlan plan, Vector3 origin, out float minX, out float maxX, out float minZ, out float maxZ, float rotationDeg = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) GetBounds(plan, origin, 2, rotationDeg, out minX, out maxX, out minZ, out maxZ); } public static void GetLeveledAreaBounds(FloorPlan plan, Vector3 origin, out float minX, out float maxX, out float minZ, out float maxZ, float rotationDeg = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) GetBounds(plan, origin, 2, rotationDeg, out minX, out maxX, out minZ, out maxZ); minX -= LEVEL_RADIUS; maxX += LEVEL_RADIUS; minZ -= LEVEL_RADIUS; maxZ += LEVEL_RADIUS; } public static void GetSnapshotBounds(FloorPlan plan, Vector3 origin, out float minX, out float maxX, out float minZ, out float maxZ, float rotationDeg = 0f) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) int pad = 2 + Mathf.CeilToInt(LEVEL_RADIUS) + 4; GetBounds(plan, origin, pad, rotationDeg, out minX, out maxX, out minZ, out maxZ); } private static void GetBounds(FloorPlan plan, Vector3 origin, int pad, float rotationDeg, out float minX, out float maxX, out float minZ, out float maxZ) { //IL_006e: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) GetLocalBounds(plan, pad, out var minX2, out var maxX2, out var minZ2, out var maxZ2); float[] array = new float[4] { minX2, maxX2, maxX2, minX2 }; float[] array2 = new float[4] { minZ2, minZ2, maxZ2, maxZ2 }; float num = float.MaxValue; float num2 = float.MinValue; float num3 = float.MaxValue; float num4 = float.MinValue; for (int i = 0; i < 4; i++) { Vector2 val = PieceMap.TransformLocalXZ(array[i], array2[i], rotationDeg); float x = val.x; float y = val.y; if (x < num) { num = x; } if (x > num2) { num2 = x; } if (y < num3) { num3 = y; } if (y > num4) { num4 = y; } } minX = origin.x + num; maxX = origin.x + num2; minZ = origin.z + num3; maxZ = origin.z + num4; } private static void GetLocalBounds(FloorPlan plan, int pad, out float minX, out float maxX, out float minZ, out float maxZ) { int num = int.MaxValue; int num2 = int.MinValue; int num3 = int.MaxValue; int num4 = int.MinValue; foreach (FloorPlanPiece piece in plan.Pieces) { int num5 = 1; int num6 = 1; PieceDef def = PieceMap.GetDef(piece.Type); if (def != null) { num5 = def.EffW(piece.Rotation); num6 = def.EffH(piece.Rotation); } if (piece.Col < num) { num = piece.Col; } if (piece.Col + num5 > num2) { num2 = piece.Col + num5; } if (piece.Row < num3) { num3 = piece.Row; } if (piece.Row + num6 > num4) { num4 = piece.Row + num6; } } foreach (FlexiWallPiece flexiWall in plan.FlexiWalls) { int num7 = Mathf.FloorToInt(Mathf.Min(flexiWall.X1, Mathf.Min(flexiWall.X2, flexiWall.Mx))); int num8 = Mathf.CeilToInt(Mathf.Max(flexiWall.X1, Mathf.Max(flexiWall.X2, flexiWall.Mx))); int num9 = Mathf.FloorToInt(Mathf.Min(flexiWall.Y1, Mathf.Min(flexiWall.Y2, flexiWall.My))); int num10 = Mathf.CeilToInt(Mathf.Max(flexiWall.Y1, Mathf.Max(flexiWall.Y2, flexiWall.My))); if (num7 < num) { num = num7; } if (num8 > num2) { num2 = num8; } if (num9 < num3) { num3 = num9; } if (num10 > num4) { num4 = num10; } } if (num == int.MaxValue) { num = 0; num3 = 0; num2 = plan.Cols; num4 = plan.Rows; } minX = (float)(num - pad) * 1f; maxX = (float)(num2 + pad) * 1f; minZ = (float)(num3 - pad) * 1f; maxZ = (float)(num4 + pad) * 1f; } private static void ApplyLevel(float x, float y, float z, float radius, HashSet modified, bool smooth = false) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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_010e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("VFP_LevelOp"); val.transform.position = new Vector3(x, y, z); TerrainOp val2 = val.AddComponent(); val2.m_settings.m_level = true; val2.m_settings.m_levelRadius = radius; val2.m_settings.m_smooth = smooth; Vector3[] array = (Vector3[])(object)new Vector3[9] { new Vector3(x, y, z), new Vector3(x - radius, y, z - radius), new Vector3(x + radius, y, z - radius), new Vector3(x - radius, y, z + radius), new Vector3(x + radius, y, z + radius), new Vector3(x - radius, y, z), new Vector3(x + radius, y, z), new Vector3(x, y, z - radius), new Vector3(x, y, z + radius) }; HashSet hashSet = new HashSet(); Vector3[] array2 = array; foreach (Vector3 val3 in array2) { TerrainComp val4 = TerrainComp.FindTerrainCompiler(val3); if ((Object)(object)val4 != (Object)null) { hashSet.Add(val4); } } foreach (TerrainComp item in hashSet) { TerrainSnapshot.EnsureCaptured(item); item.ApplyOperation(val2); modified.Add(item); } Object.Destroy((Object)(object)val); } private static void ApplySmooth(float x, float y, float z, float radius, HashSet modified) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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) GameObject val = new GameObject("VFP_SmoothOp"); val.transform.position = new Vector3(x, y, z); TerrainOp val2 = val.AddComponent(); val2.m_settings.m_level = false; val2.m_settings.m_smooth = true; val2.m_settings.m_smoothRadius = radius; Vector3[] array = (Vector3[])(object)new Vector3[9] { new Vector3(x, y, z), new Vector3(x - radius, y, z - radius), new Vector3(x + radius, y, z - radius), new Vector3(x - radius, y, z + radius), new Vector3(x + radius, y, z + radius), new Vector3(x - radius, y, z), new Vector3(x + radius, y, z), new Vector3(x, y, z - radius), new Vector3(x, y, z + radius) }; HashSet hashSet = new HashSet(); Vector3[] array2 = array; foreach (Vector3 val3 in array2) { TerrainComp val4 = TerrainComp.FindTerrainCompiler(val3); if ((Object)(object)val4 != (Object)null) { hashSet.Add(val4); } } foreach (TerrainComp item in hashSet) { TerrainSnapshot.EnsureCaptured(item); item.ApplyOperation(val2); modified.Add(item); } Object.Destroy((Object)(object)val); } private static void ApplySpikeCollapse(float x, float z, float currentY, float desiredY, float localMedianY, float radius, HashSet modified) { float radius2 = Mathf.Clamp(radius * 3.1f, 1.1f, 2.6f); ApplySmooth(x, currentY, z, radius2, modified); float num = currentY - localMedianY; if (num > 0.32f || currentY > desiredY + 0.25f) { float num2 = Mathf.Min(desiredY, localMedianY + 0.06f); num2 = Mathf.Min(num2, currentY - 0.05f); float radius3 = Mathf.Clamp(radius * 2.35f, 0.95f, 2.1f); ApplyLevel(x, num2, z, radius3, modified, smooth: true); } } private static float SampleHeight(float x, float z, float referenceY = 0f) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) float result = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(new Vector3(x, referenceY, z), ref result)) { return result; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(new Vector3(x, referenceY + 200f, z), Vector3.down, ref val, 500f, 2048)) { return ((RaycastHit)(ref val)).point.y; } return referenceY; } private static List CollectRepairSamples(Vector3 point, float referenceY, out Vector2 slopeDir, out float ridgeY, out float troughY) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_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_005e: 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_0074: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_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) //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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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_0150: 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_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) List list = new List(16); Vector2[] array = (Vector2[])(object)new Vector2[9] { new Vector2(0f, 0f), new Vector2(1.2f, 0f), new Vector2(-1.2f, 0f), new Vector2(0f, 1.2f), new Vector2(0f, -1.2f), new Vector2(0.85f, 0.85f), new Vector2(0.85f, -0.85f), new Vector2(-0.85f, 0.85f), new Vector2(-0.85f, -0.85f) }; slopeDir = Vector2.zero; ridgeY = float.MinValue; troughY = float.MaxValue; Vector2 val = Vector2.zero; Vector2 val2 = Vector2.zero; Vector2[] array2 = array; foreach (Vector2 val3 in array2) { float num = SampleHeight(point.x + val3.x, point.z + val3.y, referenceY); list.Add(num); if (num > ridgeY) { ridgeY = num; val = val3; } if (num < troughY) { troughY = num; val2 = val3; } } Vector2 val4 = val2 - val; if (((Vector2)(ref val4)).sqrMagnitude > 0.0001f) { slopeDir = ((Vector2)(ref val4)).normalized; } else { slopeDir = Vector2.right; } return list; } private static int ApplyEdgeBridge(Vector3 point, float anchorY, Vector2 slopeDir, float ridgeY, float troughY, HashSet modified) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0f, ridgeY - troughY); if (num < 0.08f) { return 0; } float num2 = Mathf.Clamp(num * 0.45f, 0.14f, 0.55f); float num3 = anchorY - num2; int num4 = Mathf.Clamp(Mathf.CeilToInt(3.90625f), 2, 6); int num5 = 0; for (int i = 0; i <= num4; i++) { float num6 = (float)i / (float)num4; float num7 = num6 * 1.25f; float x = point.x + slopeDir.x * num7; float z = point.z + slopeDir.y * num7; float num8 = Mathf.Lerp(anchorY, num3, num6); float num9 = SampleHeight(x, z, anchorY); float localMedianHeight = GetLocalMedianHeight(x, z, num9, 0.45f); float num10 = num9 + 0.14f; float num11 = localMedianHeight + 0.08f; num8 = Mathf.Min(num8, num10); num8 = Mathf.Min(num8, num11); num8 = Mathf.Min(num8, anchorY + 0.04f); float radius = Mathf.Lerp(0.408f, 0.48f, num6); ApplyLevel(x, num8, z, radius, modified, smooth: true); num5++; } return num5; } private static float GetLocalMedianHeight(float x, float z, float referenceY, float radius) { List list = new List(9) { SampleHeight(x, z, referenceY), SampleHeight(x + radius, z, referenceY), SampleHeight(x - radius, z, referenceY), SampleHeight(x, z + radius, referenceY), SampleHeight(x, z - radius, referenceY), SampleHeight(x + radius, z + radius, referenceY), SampleHeight(x + radius, z - radius, referenceY), SampleHeight(x - radius, z + radius, referenceY), SampleHeight(x - radius, z - radius, referenceY) }; list.Sort(); return list[list.Count / 2]; } private static bool CanLowerWithoutRaise(float x, float z, float targetY, float radius, float referenceY, float raiseTolerance = 0.01f) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_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_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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) Vector2[] array = (Vector2[])(object)new Vector2[9] { new Vector2(0f, 0f), new Vector2(0f - radius, 0f - radius), new Vector2(radius, 0f - radius), new Vector2(0f - radius, radius), new Vector2(radius, radius), new Vector2(0f - radius, 0f), new Vector2(radius, 0f), new Vector2(0f, 0f - radius), new Vector2(0f, radius) }; for (int i = 0; i < array.Length; i++) { float num = SampleHeight(x + array[i].x, z + array[i].y, referenceY); if (targetY > num + raiseTolerance) { return false; } } return true; } private static void ShowProgress(string message) { ValheimFloorPlanPlugin.ShowProgressMessage(message); } private static bool IsInEdgeBand(float x, float z, float minX, float maxX, float minZ, float maxZ, float bandWidth) { return x - minX <= bandWidth || maxX - x <= bandWidth || z - minZ <= bandWidth || maxZ - z <= bandWidth; } } public static class TerrainSnapshot { private struct ChunkState { public TerrainComp Comp; public float[] LevelDelta; public bool[] ModifiedHeight; public bool HadLevelDelta; public bool HadModifiedHeight; } private static readonly FieldInfo _levelDeltaField = typeof(TerrainComp).GetField("m_levelDelta", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _modifiedHeightField = typeof(TerrainComp).GetField("m_modifiedHeight", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _nviewField = typeof(TerrainComp).GetField("m_nview", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _saveMethod = typeof(TerrainComp).GetMethod("Save", BindingFlags.Instance | BindingFlags.NonPublic); private const float TERRAIN_CHUNK_SIZE = 64f; private const float TERRAIN_CHUNK_HALF = 32f; private static readonly List _saved = new List(); private static readonly HashSet _savedIds = new HashSet(); private static int _initialCaptureCount = 0; private static int _onDemandCaptureCount = 0; public static bool HasSnapshot => _saved.Count > 0; public static void Capture(float minX, float maxX, float minZ, float maxZ, float referenceY) { //IL_00b0: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_0170: Unknown result type (might be due to invalid IL or missing references) _saved.Clear(); _savedIds.Clear(); _initialCaptureCount = 0; _onDemandCaptureCount = 0; if (_levelDeltaField == null || _modifiedHeightField == null || _saveMethod == null) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[TerrainSnapshot] Reflection fields not found — undo will only remove pieces."); return; } HashSet seen = new HashSet(); TerrainComp[] array = Object.FindObjectsOfType() ?? Array.Empty(); int num = array.Length; int num2 = 0; int num3 = 0; TerrainComp[] array2 = array; foreach (TerrainComp val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 position = ((Component)val).transform.position; if (OverlapsXZ(position.x - 32f, position.x + 32f, position.z - 32f, position.z + 32f, minX, maxX, minZ, maxZ)) { num2++; TrySaveChunk(val, seen); } } } _initialCaptureCount = _saved.Count; float num4 = 8f; float[] array3 = new float[3] { referenceY, referenceY + 128f, referenceY - 128f }; for (float num5 = minX; num5 <= maxX + 0.01f; num5 += num4) { for (float num6 = minZ; num6 <= maxZ + 0.01f; num6 += num4) { for (int j = 0; j < array3.Length; j++) { TerrainComp val2 = TerrainComp.FindTerrainCompiler(new Vector3(num5, array3[j], num6)); if (!((Object)(object)val2 == (Object)null)) { num3++; TrySaveChunk(val2, seen); } } } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainSnapshot] Captured {_saved.Count} terrain chunk(s)."); if (_initialCaptureCount > 0 || (_initialCaptureCount == 0 && _saved.Count > 0)) { ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainSnapshot] Breakdown: {_initialCaptureCount} from initial broad scan, {_saved.Count - _initialCaptureCount} from fallback probes."); } if (_saved.Count == 0) { ValheimFloorPlanPlugin.Log.LogWarning((object)$"[TerrainSnapshot] Capture found no terrain chunks. loaded={num}, overlap={num2}, probeHits={num3}, bounds=([{minX:F1}..{maxX:F1}] x [{minZ:F1}..{maxZ:F1}]), refY={referenceY:F1}"); } } public static int GetSnapshotChunkCount() { return _saved.Count; } public static void Clear() { int count = _saved.Count; _saved.Clear(); _savedIds.Clear(); _initialCaptureCount = 0; _onDemandCaptureCount = 0; ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainSnapshot] Discarded {count} terrain snapshot chunk(s) without restore."); } public static void EnsureCaptured(TerrainComp tc) { if (!((Object)(object)tc == (Object)null) && !(_levelDeltaField == null) && !(_modifiedHeightField == null)) { int instanceID = ((Object)tc).GetInstanceID(); if (!_savedIds.Contains(instanceID)) { float[] array = _levelDeltaField.GetValue(tc) as float[]; bool[] array2 = _modifiedHeightField.GetValue(tc) as bool[]; _saved.Add(new ChunkState { Comp = tc, LevelDelta = ((array != null) ? ((float[])array.Clone()) : Array.Empty()), ModifiedHeight = ((array2 != null) ? ((bool[])array2.Clone()) : Array.Empty()), HadLevelDelta = (array != null), HadModifiedHeight = (array2 != null) }); _savedIds.Add(instanceID); _onDemandCaptureCount++; ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainSnapshot] On-demand capture #{_onDemandCaptureCount}: added chunk #{instanceID} (total now: {_saved.Count})"); } } } public static void Restore() { if (_saved.Count == 0) { ValheimFloorPlanPlugin.Log.LogWarning((object)"[TerrainSnapshot] Nothing to restore."); return; } ValheimFloorPlanPlugin.Log.LogInfo((object)($"[TerrainSnapshot] Restore: {_saved.Count} total chunks " + $"({_initialCaptureCount} initial + {_onDemandCaptureCount} on-demand)")); int num = 0; foreach (ChunkState item in _saved) { if ((Object)(object)item.Comp == (Object)null) { continue; } try { _levelDeltaField.SetValue(item.Comp, (item.HadLevelDelta && item.LevelDelta != null) ? ((float[])item.LevelDelta.Clone()) : null); _modifiedHeightField.SetValue(item.Comp, (item.HadModifiedHeight && item.ModifiedHeight != null) ? ((bool[])item.ModifiedHeight.Clone()) : null); _saveMethod.Invoke(item.Comp, null); Heightmap val = ((Component)item.Comp).GetComponent() ?? ((Component)item.Comp).GetComponentInParent(); if ((Object)(object)val != (Object)null) { val.Poke(false); } num++; } catch (Exception ex) { ValheimFloorPlanPlugin.Log.LogError((object)$"[TerrainSnapshot] Failed to restore chunk #{((Object)item.Comp).GetInstanceID()}: {ex.Message}"); } } _saved.Clear(); _savedIds.Clear(); ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainSnapshot] Restored {num} terrain chunk(s)."); } private static void TrySaveChunk(TerrainComp tc, HashSet seen) { int instanceID = ((Object)tc).GetInstanceID(); if (seen.Add(instanceID) && !_savedIds.Contains(instanceID)) { float[] array = _levelDeltaField.GetValue(tc) as float[]; bool[] array2 = _modifiedHeightField.GetValue(tc) as bool[]; _saved.Add(new ChunkState { Comp = tc, LevelDelta = ((array != null) ? ((float[])array.Clone()) : Array.Empty()), ModifiedHeight = ((array2 != null) ? ((bool[])array2.Clone()) : Array.Empty()), HadLevelDelta = (array != null), HadModifiedHeight = (array2 != null) }); _savedIds.Add(instanceID); } } private static bool OverlapsXZ(float aMinX, float aMaxX, float aMinZ, float aMaxZ, float bMinX, float bMaxX, float bMinZ, float bMaxZ) { if (aMaxX < bMinX || aMinX > bMaxX) { return false; } if (aMaxZ < bMinZ || aMinZ > bMaxZ) { return false; } return true; } } [BepInPlugin("com.alexdroz.valheimfloorplan", "ValheimFloorPlan", "2.1.3")] public class ValheimFloorPlanPlugin : BaseUnityPlugin { internal enum StructuralMaterial { Stone, Wood } internal enum RoofStyleOption { Gable, Flat } internal enum RoofScaffoldingGableFlooringOption { RoofWithFloorUnderlay, RoofOnly } internal enum StaircaseReachModeOption { ToTheNextLevelOnly, AllTheWay } [Serializable] private sealed class PresetBundleSettings { public int FloorPlanLevels = 1; public float UndoRadius = 15f; public string ProgressMessagePosition = "CenterLeft"; public string WarningMessagePosition = "TopLeft"; public int ScaffoldingLevels = 1; public int ScaffoldingFloorHeight = 4; public int ScaffoldingFloorHeight2 = 4; public int ScaffoldingFloorHeight3 = 4; public bool RoofScaffolding; public string RoofStyle = "Gable"; public bool OpenTop; public string RoofScaffoldingGableFlooring = "RoofWithFloorUnderlay"; public bool ScaffoldingFloors; public bool TransverseScaffoldingBeams; public bool LongitudinalScaffoldingBeams; public int ExternalWallHeightLevel1 = 1; public int ExternalWallHeightLevel2 = 1; public int ExternalWallHeightLevel3 = 1; public int InternalWallHeightLevel1 = 1; public int InternalWallHeightLevel2 = 1; public int InternalWallHeightLevel3 = 1; public string WallPillarMaterial = "Stone"; public bool DisableWelcomePost; public string StaircaseReachMode = "ToTheNextLevelOnly"; public float StaircaseStepRise = 0.16f; public float StaircaseStepAngleDeg = 15f; public float StaircaseStepRadius = 0.75f; public float BuildOriginForwardOffset; public float MoveStep = 2f; public float FineMoveStep = 0.5f; public float BuildRotationSnapDegrees = 90f; public float RotateStepDegrees = 90f; public float FineRotateStepDegrees = 22.5f; public int TerrainLevelPasses = 2; public int TerrainSpikeCleanupPasses = 2; public float TerrainStampRadius = 3f; public float TerrainHighPointDelta; public bool TerrainUseStagedRaise; public float TerrainRaiseStepHeight = 0.5f; public int TerrainMaxRaiseStages = 1; public bool TerrainSkipSatisfiedCenterStamps = true; } public const string PluginGUID = "com.alexdroz.valheimfloorplan"; public const string PluginName = "ValheimFloorPlan"; public const string PluginVersion = "2.1.3"; internal static ManualLogSource Log = null; private ConfigEntry _vfpDirectory = null; private ConfigEntry _vfpFilePath = null; private ConfigEntry _vfpFilePathLevel2 = null; private ConfigEntry _vfpFilePathLevel3 = null; private ConfigEntry _floorPlanLevels = null; private ConfigEntry _buildHotkey = null; private ConfigEntry _undoHotkey = null; private ConfigEntry _undoKeepTerrainHotkey = null; private ConfigEntry _exportBundleHotkey = null; private ConfigEntry _importBundleHotkey = null; private ConfigEntry _bundleName = null; private ConfigEntry _undoRadius = null; private ConfigEntry _progressMessagePosition = null; private ConfigEntry _warningMessagePosition = null; private ConfigEntry _terrainLevelPasses = null; private ConfigEntry _terrainSpikeCleanupPasses = null; private ConfigEntry _terrainStampRadius = null; private ConfigEntry _terrainHighPointDelta = null; private ConfigEntry _terrainUseStagedRaise = null; private ConfigEntry _terrainRaiseStepHeight = null; private ConfigEntry _terrainMaxRaiseStages = null; private ConfigEntry _terrainSkipSatisfiedCenterStamps = null; private ConfigEntry _externalWallHeightLevel1 = null; private ConfigEntry _externalWallHeightLevel2 = null; private ConfigEntry _externalWallHeightLevel3 = null; private ConfigEntry _internalWallHeightLevel1 = null; private ConfigEntry _internalWallHeightLevel2 = null; private ConfigEntry _internalWallHeightLevel3 = null; private ConfigEntry _wallPillarMaterial = null; private ConfigEntry _staircaseReachMode = null; private ConfigEntry _staircaseStepRise = null; private ConfigEntry _staircaseStepAngleDeg = null; private ConfigEntry _staircaseStepRadius = null; private ConfigEntry _roofScaffolding = null; private ConfigEntry _roofStyle = null; private ConfigEntry _openTop = null; private ConfigEntry _roofScaffoldingGableFlooring = null; private ConfigEntry _scaffoldingLevels = null; private ConfigEntry _scaffoldingFloorHeight = null; private ConfigEntry _scaffoldingFloorHeight2 = null; private ConfigEntry _scaffoldingFloorHeight3 = null; private ConfigEntry _scaffoldingFloors = null; private ConfigEntry _transverseScaffoldingBeams = null; private ConfigEntry _longitudinalScaffoldingBeams = null; private ConfigEntry _disableWelcomePost = null; private ConfigEntry _buildOriginForwardOffset = null; private ConfigEntry _previewMoveStep = null; private ConfigEntry _previewFineMoveStep = null; private ConfigEntry _buildRotationSnapDeg = null; private ConfigEntry _previewRotateStepDeg = null; private ConfigEntry _previewFineRotateStepDeg = null; private ConfigEntry _previewMoveForwardKey = null; private ConfigEntry _previewMoveBackwardKey = null; private ConfigEntry _previewMoveLeftKey = null; private ConfigEntry _previewMoveRightKey = null; private ConfigEntry _previewConfirmKey = null; private ConfigEntry _previewRotateLeftKey = null; private ConfigEntry _previewRotateRightKey = null; private ConfigEntry _previewCancelKey = null; private ConfigEntry _previewFineAdjustKey = null; private bool _applyingScaffoldingRules; private bool _bundleImportSelectionActive; private float _bundleImportSelectionExpireAt; private int _bundleImportSelectionIndex; private string[] _bundleImportSelectionNames = Array.Empty(); private int _bundleImportLastShownSecond = -1; private const float BundleImportSelectionTimeoutSeconds = 5f; private const string BundleExtension = ".vpfset"; private const string LegacyBundleExtension = ".vfpset"; internal static ValheimFloorPlanPlugin Instance { get; private set; } = null; internal static MessageType ProgressMessageType { get; private set; } = (MessageType)2; internal static MessageType WarningMessageType { get; private set; } = (MessageType)1; internal static int TerrainLevelPasses { get; private set; } = 2; internal static int TerrainSpikeCleanupPasses { get; private set; } = 2; internal static float TerrainStampRadius { get; private set; } = 3f; internal static float TerrainHighPointDelta { get; private set; } = 0f; internal static bool TerrainUseStagedRaise { get; private set; } = false; internal static float TerrainRaiseStepHeight { get; private set; } = 0.5f; internal static int TerrainMaxRaiseStages { get; private set; } = 1; internal static bool TerrainSkipSatisfiedCenterStamps { get; private set; } = true; internal static int ExternalWallHeightLevel1 { get; private set; } = 1; internal static int ExternalWallHeightLevel2 { get; private set; } = 1; internal static int ExternalWallHeightLevel3 { get; private set; } = 1; internal static int InternalWallHeightLevel1 { get; private set; } = 1; internal static int InternalWallHeightLevel2 { get; private set; } = 1; internal static int InternalWallHeightLevel3 { get; private set; } = 1; internal static StructuralMaterial WallPillarMaterial { get; private set; } = StructuralMaterial.Stone; internal static StaircaseReachModeOption StaircaseReachMode { get; private set; } = StaircaseReachModeOption.ToTheNextLevelOnly; internal static float StaircaseStepRise { get; private set; } = 0.16f; internal static float StaircaseStepAngleDeg { get; private set; } = 15f; internal static float StaircaseStepRadius { get; private set; } = 0.75f; internal static bool RoofScaffolding { get; private set; } = false; internal static RoofStyleOption RoofStyle { get; private set; } = RoofStyleOption.Gable; internal static bool OpenTop { get; private set; } = false; internal static RoofScaffoldingGableFlooringOption RoofScaffoldingGableFlooring { get; private set; } = RoofScaffoldingGableFlooringOption.RoofWithFloorUnderlay; internal static int ScaffoldingLevels { get; private set; } = 1; internal static int ScaffoldingFloorHeight { get; private set; } = 4; internal static int ScaffoldingFloorHeight2 { get; private set; } = 4; internal static int ScaffoldingFloorHeight3 { get; private set; } = 4; internal static bool ScaffoldingFloors { get; private set; } = false; internal static bool TransverseScaffoldingBeams { get; private set; } = false; internal static bool LongitudinalScaffoldingBeams { get; private set; } = false; internal static bool DisableWelcomePost { get; private set; } = false; internal static float BuildOriginForwardOffset { get; private set; } = 0f; internal static float PreviewMoveStep { get; private set; } = 2f; internal static float PreviewFineMoveStep { get; private set; } = 0.5f; internal static float BuildRotationSnapDegrees { get; private set; } = 90f; internal static float PreviewRotateStepDeg { get; private set; } = 15f; internal static float PreviewFineRotateStepDeg { get; private set; } = 5f; internal static KeyCode PreviewMoveForwardKey { get; private set; } = (KeyCode)273; internal static KeyCode PreviewMoveBackwardKey { get; private set; } = (KeyCode)274; internal static KeyCode PreviewMoveLeftKey { get; private set; } = (KeyCode)276; internal static KeyCode PreviewMoveRightKey { get; private set; } = (KeyCode)275; internal static KeyCode PreviewConfirmKey { get; private set; } = (KeyCode)101; internal static KeyCode PreviewRotateLeftKey { get; private set; } = (KeyCode)113; internal static KeyCode PreviewRotateRightKey { get; private set; } = (KeyCode)103; internal static KeyCode PreviewCancelKey { get; private set; } = (KeyCode)27; internal static KeyCode PreviewFineAdjustKey { get; private set; } = (KeyCode)304; internal static float UndoRadius { get; private set; } = 15f; internal static int FloorPlanLevels { get; private set; } = 1; internal static string FloorPlanDirectory { get; private set; } = string.Empty; internal static string FloorPlanFileLevel2 { get; private set; } = string.Empty; internal static string FloorPlanFileLevel3 { get; private set; } = string.Empty; private void Awake() { //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_01d3: 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_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Expected O, but got Unknown //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Expected O, but got Unknown //IL_0495: Unknown result type (might be due to invalid IL or missing references) //IL_049f: Expected O, but got Unknown //IL_04ea: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Expected O, but got Unknown //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Expected O, but got Unknown //IL_05e1: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Expected O, but got Unknown //IL_061b: Unknown result type (might be due to invalid IL or missing references) //IL_0625: Expected O, but got Unknown //IL_0661: Unknown result type (might be due to invalid IL or missing references) //IL_066b: Expected O, but got Unknown //IL_07cc: Unknown result type (might be due to invalid IL or missing references) //IL_07d6: Expected O, but got Unknown //IL_0911: Unknown result type (might be due to invalid IL or missing references) //IL_091b: Expected O, but got Unknown //IL_0973: Unknown result type (might be due to invalid IL or missing references) //IL_097d: Expected O, but got Unknown //IL_09d5: Unknown result type (might be due to invalid IL or missing references) //IL_09df: Expected O, but got Unknown //IL_0a37: Unknown result type (might be due to invalid IL or missing references) //IL_0a41: Expected O, but got Unknown //IL_0a99: Unknown result type (might be due to invalid IL or missing references) //IL_0aa3: Expected O, but got Unknown //IL_0afb: Unknown result type (might be due to invalid IL or missing references) //IL_0b05: Expected O, but got Unknown //IL_0b7c: Unknown result type (might be due to invalid IL or missing references) //IL_0b86: Expected O, but got Unknown //IL_0c3e: Unknown result type (might be due to invalid IL or missing references) //IL_0c48: Expected O, but got Unknown //IL_0caa: Unknown result type (might be due to invalid IL or missing references) //IL_0cb4: Expected O, but got Unknown //IL_0d20: Unknown result type (might be due to invalid IL or missing references) //IL_0d2a: Expected O, but got Unknown //IL_0d96: Unknown result type (might be due to invalid IL or missing references) //IL_0da0: Expected O, but got Unknown //IL_0e0c: Unknown result type (might be due to invalid IL or missing references) //IL_0e16: Expected O, but got Unknown //IL_0e82: Unknown result type (might be due to invalid IL or missing references) //IL_0e8c: Expected O, but got Unknown //IL_0ef8: Unknown result type (might be due to invalid IL or missing references) //IL_0f02: Expected O, but got Unknown //IL_0f79: Unknown result type (might be due to invalid IL or missing references) //IL_0f83: Expected O, but got Unknown //IL_0fd3: Unknown result type (might be due to invalid IL or missing references) //IL_0fdd: Expected O, but got Unknown //IL_102d: Unknown result type (might be due to invalid IL or missing references) //IL_1037: Expected O, but got Unknown //IL_1165: Unknown result type (might be due to invalid IL or missing references) //IL_116c: Invalid comparison between Unknown and I4 //IL_12a7: Unknown result type (might be due to invalid IL or missing references) //IL_12b8: Unknown result type (might be due to invalid IL or missing references) //IL_12c9: Unknown result type (might be due to invalid IL or missing references) //IL_12da: Unknown result type (might be due to invalid IL or missing references) //IL_12eb: Unknown result type (might be due to invalid IL or missing references) //IL_12fc: Unknown result type (might be due to invalid IL or missing references) //IL_130d: Unknown result type (might be due to invalid IL or missing references) //IL_131e: Unknown result type (might be due to invalid IL or missing references) //IL_132f: Unknown result type (might be due to invalid IL or missing references) //IL_135d: Unknown result type (might be due to invalid IL or missing references) //IL_1367: Expected O, but got Unknown //IL_13bf: Unknown result type (might be due to invalid IL or missing references) //IL_13c9: Expected O, but got Unknown //IL_142d: Unknown result type (might be due to invalid IL or missing references) //IL_1437: Expected O, but got Unknown //IL_14a3: Unknown result type (might be due to invalid IL or missing references) //IL_14ad: Expected O, but got Unknown //IL_1563: Unknown result type (might be due to invalid IL or missing references) //IL_156d: Expected O, but got Unknown //IL_15ce: Unknown result type (might be due to invalid IL or missing references) //IL_15d8: Expected O, but got Unknown //IL_1682: Unknown result type (might be due to invalid IL or missing references) //IL_1695: Unknown result type (might be due to invalid IL or missing references) //IL_16a8: Unknown result type (might be due to invalid IL or missing references) //IL_16b5: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; _vfpDirectory = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanDirectory", "", "Optional base folder for .vfp files and bundles. When set, FloorPlanFile/Level2/Level3 values are treated as filenames relative to this folder unless they are already absolute paths. Leave empty to use full paths in each file field (legacy behaviour)."); _vfpDirectory.SettingChanged += delegate { FloorPlanDirectory = (_vfpDirectory.Value ?? string.Empty).Trim(); FloorPlanFileLevel2 = ResolveVfpPath((_vfpFilePathLevel2.Value ?? string.Empty).Trim()); FloorPlanFileLevel3 = ResolveVfpPath((_vfpFilePathLevel3.Value ?? string.Empty).Trim()); }; FloorPlanDirectory = (_vfpDirectory.Value ?? string.Empty).Trim(); _vfpFilePath = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanFile", "", "Full path to the .vfp floor plan file exported from Valheim Floor Plan Designer. If FloorPlanDirectory is set, a bare filename is sufficient."); _vfpFilePathLevel2 = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanFileLevel2", "", "Optional full path to the Level 2 .vfp floor plan file. When set, its footprint must fit within the Level 1 plan footprint."); _vfpFilePathLevel2.SettingChanged += delegate { FloorPlanFileLevel2 = ResolveVfpPath((_vfpFilePathLevel2.Value ?? string.Empty).Trim()); }; FloorPlanFileLevel2 = ResolveVfpPath((_vfpFilePathLevel2.Value ?? string.Empty).Trim()); _vfpFilePathLevel3 = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanFileLevel3", "", "Optional path to the Level 3 .vfp floor plan file. If FloorPlanDirectory is set, a bare filename is sufficient. When set, its footprint must fit within the Level 1 plan footprint."); _vfpFilePathLevel3.SettingChanged += delegate { FloorPlanFileLevel3 = ResolveVfpPath((_vfpFilePathLevel3.Value ?? string.Empty).Trim()); }; FloorPlanFileLevel3 = ResolveVfpPath((_vfpFilePathLevel3.Value ?? string.Empty).Trim()); _floorPlanLevels = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanLevels", 1, new ConfigDescription("How many layout levels to build (1-3). Level 1 is FloorPlanFile, Level 2 adds FloorPlanFileLevel2, Level 3 adds FloorPlanFileLevel3. Values above 1 enforce multi-level scaffolding requirements.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 3), Array.Empty())); _floorPlanLevels.SettingChanged += delegate { ApplyScaffoldingRules(); }; FloorPlanLevels = Mathf.Clamp(_floorPlanLevels.Value, 1, 3); _buildHotkey = ((BaseUnityPlugin)this).Config.Bind("General", "BuildHotkey", new KeyboardShortcut((KeyCode)289, Array.Empty()), "Hotkey to build the floor plan at your current position."); _undoHotkey = ((BaseUnityPlugin)this).Config.Bind("General", "UndoHotkey", new KeyboardShortcut((KeyCode)290, Array.Empty()), "Hotkey to undo the last floor plan build (removes pieces and restores terrain)."); _undoKeepTerrainHotkey = ((BaseUnityPlugin)this).Config.Bind("General", "UndoKeepTerrainHotkey", new KeyboardShortcut((KeyCode)290, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Hotkey for undo-without-terrain-restore mode (removes pieces and discards the current terrain snapshot so leveled terrain remains). Default: Ctrl+F9."); _bundleName = ((BaseUnityPlugin)this).Config.Bind("Preset Bundles", "BundleName", "MyBuild", "Preset bundle file name (without extension) used by bundle export/import hotkeys."); _exportBundleHotkey = ((BaseUnityPlugin)this).Config.Bind("Preset Bundles", "ExportBundleHotkey", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Exports current floor plan files + non-key config settings into a .vfpset bundle. Key mappings are intentionally excluded."); _importBundleHotkey = ((BaseUnityPlugin)this).Config.Bind("Preset Bundles", "ImportBundleHotkey", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Starts timed bundle selection mode. Right/Left arrows choose bundle, Enter imports, Escape cancels."); _undoRadius = ((BaseUnityPlugin)this).Config.Bind("General", "UndoRadius", 15f, new ConfigDescription("Search radius in metres around the player when scanning for VFP pieces to remove on Undo.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 150f), Array.Empty())); _undoRadius.SettingChanged += delegate { UndoRadius = Mathf.Clamp(_undoRadius.Value, 5f, 150f); }; UndoRadius = Mathf.Clamp(_undoRadius.Value, 5f, 150f); _progressMessagePosition = ((BaseUnityPlugin)this).Config.Bind("General", "ProgressMessagePosition", "CenterLeft", "HUD slot for build-progress messages. Uses Valheim MessageHud positions. Examples: Center, TopLeft, TopRight. 'CenterLeft' is accepted as an alias and maps to Center."); _progressMessagePosition.SettingChanged += delegate { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ProgressMessageType = ParseProgressMessageType(_progressMessagePosition.Value); }; ProgressMessageType = ParseProgressMessageType(_progressMessagePosition.Value); _warningMessagePosition = ((BaseUnityPlugin)this).Config.Bind("General", "WarningMessagePosition", "TopLeft", "HUD slot for slope/build warning messages. Uses Valheim MessageHud positions. Examples: Center, TopLeft, TopRight. 'CenterLeft' is accepted as an alias and maps to Center."); _warningMessagePosition.SettingChanged += delegate { //IL_000b: Unknown result type (might be due to invalid IL or missing references) WarningMessageType = ParseProgressMessageType(_warningMessagePosition.Value); }; WarningMessageType = ParseProgressMessageType(_warningMessagePosition.Value); _scaffoldingLevels = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "ScaffoldingLevels", 1, new ConfigDescription("How many stacked 4m scaffolding levels to build when RoofScaffolding is enabled. 1 builds only the ground level. Higher values repeat the same scaffold pattern every +4m, up to 3 levels to stay within core-wood height limits.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 3), Array.Empty())); _scaffoldingLevels.SettingChanged += delegate { ApplyScaffoldingRules(); }; ScaffoldingLevels = Mathf.Clamp(_scaffoldingLevels.Value, 1, 3); AcceptableValueList val = new AcceptableValueList(new int[3] { 2, 4, 6 }); _scaffoldingFloorHeight = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "ScaffoldingFloorHeight", 4, new ConfigDescription("Vertical height in metres for the first scaffold level. Must be a multiple of 2m so stacked wood-iron support segments line up correctly.", (AcceptableValueBase)(object)val, Array.Empty())); _scaffoldingFloorHeight.SettingChanged += delegate { ApplyScaffoldingRules(); }; ScaffoldingFloorHeight = _scaffoldingFloorHeight.Value; _scaffoldingFloorHeight2 = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "ScaffoldingFloorHeight#2", 4, new ConfigDescription("Vertical height in metres for the second scaffold level. Used when ScaffoldingLevels is 2 or 3.", (AcceptableValueBase)(object)val, Array.Empty())); _scaffoldingFloorHeight2.SettingChanged += delegate { ApplyScaffoldingRules(); }; ScaffoldingFloorHeight2 = _scaffoldingFloorHeight2.Value; _scaffoldingFloorHeight3 = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "ScaffoldingFloorHeight#3", 4, new ConfigDescription("Vertical height in metres for the third scaffold level. Used when ScaffoldingLevels is 3.", (AcceptableValueBase)(object)val, Array.Empty())); _scaffoldingFloorHeight3.SettingChanged += delegate { ApplyScaffoldingRules(); }; ScaffoldingFloorHeight3 = _scaffoldingFloorHeight3.Value; _roofScaffolding = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "RoofScaffolding", false, "When enabled, places a ring of vertical 4m log poles at each corner, adjacent to each door, and at midpoints where spacing exceeds 8m, then connects their tops with horizontal 4m log poles forming a rectangular scaffold frame."); _roofScaffolding.SettingChanged += delegate { ApplyScaffoldingRules(); }; RoofScaffolding = _roofScaffolding.Value; ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "RoofScaffoldingType", "", new ConfigDescription("Deprecated — use RoofStyle and OpenTop instead.", (AcceptableValueBase)null, Array.Empty())); string text = (val2.Value ?? "").Trim(); bool flag = !string.IsNullOrEmpty(text); ((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("Scaffolding", "RoofScaffoldingType")); _roofStyle = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "RoofStyle", "Gable", new ConfigDescription("Topmost scaffold deck shape when ScaffoldingFloors is enabled. Gable builds a pitched roof. Flat tiles the topmost level with ridge roof pieces aligned edge-to-edge along their long edges. Ignored when OpenTop is true.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Gable", "Flat" }), Array.Empty())); _roofStyle.SettingChanged += delegate { RoofStyle = ParseRoofStyle(_roofStyle.Value); }; RoofStyle = ParseRoofStyle(_roofStyle.Value); _openTop = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "OpenTop", false, "When true, the topmost scaffold level is left open to the sky. No roof deck, no vertical poles, and no ring beams are placed on the topmost level regardless of ScaffoldingFloors or RoofStyle. Staircases are clamped to the highest real deck. Hearth chimneys still extend above the open level."); _openTop.SettingChanged += delegate { OpenTop = _openTop.Value; }; OpenTop = _openTop.Value; if (flag) { if (string.Equals(text, "NoRoof", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "No Roof", StringComparison.OrdinalIgnoreCase)) { _openTop.Value = true; OpenTop = true; } else if (string.Equals(text, "Flat", StringComparison.OrdinalIgnoreCase)) { _roofStyle.Value = "Flat"; RoofStyle = RoofStyleOption.Flat; } ((BaseUnityPlugin)this).Config.Save(); Log.LogInfo((object)$"[Migration] RoofScaffoldingType={text} migrated to RoofStyle={RoofStyle}, OpenTop={OpenTop}."); } _roofScaffoldingGableFlooring = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "RoofScaffoldingGableFlooring", "RoofWithFloorUnderlay", new ConfigDescription("Top-surface behavior for Gable mode only. RoofWithFloorUnderlay places floor tiles under the top gable roof. RoofOnly places only gable roof pieces.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "RoofWithFloorUnderlay", "RoofOnly" }), Array.Empty())); _roofScaffoldingGableFlooring.SettingChanged += delegate { RoofScaffoldingGableFlooring = ParseRoofScaffoldingGableFlooring(_roofScaffoldingGableFlooring.Value); }; RoofScaffoldingGableFlooring = ParseRoofScaffoldingGableFlooring(_roofScaffoldingGableFlooring.Value); _scaffoldingFloors = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "ScaffoldingFloors", false, "When enabled (requires RoofScaffolding), builds wood floor decks across each scaffolding level."); _scaffoldingFloors.SettingChanged += delegate { ApplyScaffoldingRules(); }; ScaffoldingFloors = _scaffoldingFloors.Value; _transverseScaffoldingBeams = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "TransverseScaffoldingBeams", false, "When enabled (requires RoofScaffolding), places horizontal 4m log beams connecting vertical poles from the West edge to the East edge. Beams are placed at each vertical pole's Z position."); _transverseScaffoldingBeams.SettingChanged += delegate { ApplyScaffoldingRules(); }; TransverseScaffoldingBeams = _transverseScaffoldingBeams.Value; _longitudinalScaffoldingBeams = ((BaseUnityPlugin)this).Config.Bind("Scaffolding", "LongitudinalScaffoldingBeams", false, "When enabled (requires RoofScaffolding), places horizontal 4m log beams connecting vertical poles from the North edge to the South edge. Beams are placed at each vertical pole's X position."); _longitudinalScaffoldingBeams.SettingChanged += delegate { ApplyScaffoldingRules(); }; LongitudinalScaffoldingBeams = _longitudinalScaffoldingBeams.Value; ApplyScaffoldingRules(); _externalWallHeightLevel1 = ((BaseUnityPlugin)this).Config.Bind("Building", "ExternalWallHeightLevel1", 1, new ConfigDescription("How many levels high external Wall/Pillar pieces should be stacked for Level 1. Dynamic cap: ScaffoldingFloorHeight x 2.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _externalWallHeightLevel1.SettingChanged += delegate { ApplyScaffoldingRules(); }; ExternalWallHeightLevel1 = Mathf.Clamp(_externalWallHeightLevel1.Value, 1, 6); _externalWallHeightLevel2 = ((BaseUnityPlugin)this).Config.Bind("Building", "ExternalWallHeightLevel2", 1, new ConfigDescription("How many levels high external Wall/Pillar pieces should be stacked for Level 2. Dynamic cap: ScaffoldingFloorHeight#2 x 2.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _externalWallHeightLevel2.SettingChanged += delegate { ApplyScaffoldingRules(); }; ExternalWallHeightLevel2 = Mathf.Clamp(_externalWallHeightLevel2.Value, 1, 6); _externalWallHeightLevel3 = ((BaseUnityPlugin)this).Config.Bind("Building", "ExternalWallHeightLevel3", 1, new ConfigDescription("How many levels high external Wall/Pillar pieces should be stacked for Level 3. Dynamic cap: ScaffoldingFloorHeight#3 x 2.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _externalWallHeightLevel3.SettingChanged += delegate { ApplyScaffoldingRules(); }; ExternalWallHeightLevel3 = Mathf.Clamp(_externalWallHeightLevel3.Value, 1, 6); _internalWallHeightLevel1 = ((BaseUnityPlugin)this).Config.Bind("Building", "InternalWallHeightLevel1", 1, new ConfigDescription("How many levels high internal (non-perimeter) FlexiWall pieces should be stacked for Level 1.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _internalWallHeightLevel1.SettingChanged += delegate { InternalWallHeightLevel1 = Mathf.Clamp(_internalWallHeightLevel1.Value, 1, 6); }; InternalWallHeightLevel1 = Mathf.Clamp(_internalWallHeightLevel1.Value, 1, 6); _internalWallHeightLevel2 = ((BaseUnityPlugin)this).Config.Bind("Building", "InternalWallHeightLevel2", 1, new ConfigDescription("How many levels high internal (non-perimeter) FlexiWall pieces should be stacked for Level 2.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _internalWallHeightLevel2.SettingChanged += delegate { InternalWallHeightLevel2 = Mathf.Clamp(_internalWallHeightLevel2.Value, 1, 6); }; InternalWallHeightLevel2 = Mathf.Clamp(_internalWallHeightLevel2.Value, 1, 6); _internalWallHeightLevel3 = ((BaseUnityPlugin)this).Config.Bind("Building", "InternalWallHeightLevel3", 1, new ConfigDescription("How many levels high internal (non-perimeter) FlexiWall pieces should be stacked for Level 3.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 6), Array.Empty())); _internalWallHeightLevel3.SettingChanged += delegate { InternalWallHeightLevel3 = Mathf.Clamp(_internalWallHeightLevel3.Value, 1, 6); }; InternalWallHeightLevel3 = Mathf.Clamp(_internalWallHeightLevel3.Value, 1, 6); ApplyScaffoldingRules(); _wallPillarMaterial = ((BaseUnityPlugin)this).Config.Bind("Building", "WallPillarMaterial", "Stone", new ConfigDescription("Material used for Wall and Pillar types. Allowed: Stone, Wood.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Stone", "Wood" }), Array.Empty())); _wallPillarMaterial.SettingChanged += delegate { WallPillarMaterial = ParseStructuralMaterial(_wallPillarMaterial.Value); }; WallPillarMaterial = ParseStructuralMaterial(_wallPillarMaterial.Value); _disableWelcomePost = ((BaseUnityPlugin)this).Config.Bind("Building", "DisableWelcomePost", false, "When enabled, skips creation of the center Welcome post and signs after a build completes."); _disableWelcomePost.SettingChanged += delegate { DisableWelcomePost = _disableWelcomePost.Value; }; DisableWelcomePost = _disableWelcomePost.Value; _staircaseReachMode = ((BaseUnityPlugin)this).Config.Bind("Building", "StaircaseReachMode", "ToTheNextLevelOnly", new ConfigDescription("Controls staircase vertical reach. ToTheNextLevelOnly climbs one level at a time. AllTheWay climbs toward the top available scaffold level.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "ToTheNextLevelOnly", "AllTheWay" }), Array.Empty())); _staircaseReachMode.SettingChanged += delegate { StaircaseReachMode = ParseStaircaseReachMode(_staircaseReachMode.Value); }; StaircaseReachMode = ParseStaircaseReachMode(_staircaseReachMode.Value); _staircaseStepRise = ((BaseUnityPlugin)this).Config.Bind("Building", "StaircaseStepRise", 0.16f, new ConfigDescription("Vertical height in metres between staircase treads. Lower values produce shallower, easier-to-climb steps but increase total tread count.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 0.4f), Array.Empty())); _staircaseStepRise.SettingChanged += delegate { StaircaseStepRise = Mathf.Clamp(_staircaseStepRise.Value, 0.1f, 0.4f); }; StaircaseStepRise = Mathf.Clamp(_staircaseStepRise.Value, 0.1f, 0.4f); _staircaseStepAngleDeg = ((BaseUnityPlugin)this).Config.Bind("Building", "StaircaseStepAngleDeg", 15f, new ConfigDescription("Degrees of rotation between staircase treads. Lower values produce a wider, gentler spiral.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 30f), Array.Empty())); _staircaseStepAngleDeg.SettingChanged += delegate { StaircaseStepAngleDeg = Mathf.Clamp(_staircaseStepAngleDeg.Value, 10f, 30f); }; StaircaseStepAngleDeg = Mathf.Clamp(_staircaseStepAngleDeg.Value, 10f, 30f); _staircaseStepRadius = ((BaseUnityPlugin)this).Config.Bind("Building", "StaircaseStepRadius", 0.75f, new ConfigDescription("Distance in metres from the center pole to the midpoint of each tread. Keep at or below 1.0 to stay within the 4x4 staircase footprint.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 1f), Array.Empty())); _staircaseStepRadius.SettingChanged += delegate { StaircaseStepRadius = Mathf.Clamp(_staircaseStepRadius.Value, 0.5f, 1f); }; StaircaseStepRadius = Mathf.Clamp(_staircaseStepRadius.Value, 0.5f, 1f); _buildOriginForwardOffset = ((BaseUnityPlugin)this).Config.Bind("Preview", "BuildOriginForwardOffset", 0f, new ConfigDescription("Extra distance in metres added beyond the auto-computed player-to-build-center placement when preview starts. Usually 0 is sufficient.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 20f), Array.Empty())); _buildOriginForwardOffset.SettingChanged += delegate { BuildOriginForwardOffset = Mathf.Clamp(_buildOriginForwardOffset.Value, 0f, 20f); }; BuildOriginForwardOffset = Mathf.Clamp(_buildOriginForwardOffset.Value, 0f, 20f); _previewMoveStep = ((BaseUnityPlugin)this).Config.Bind("Preview", "MoveStep", 2f, new ConfigDescription("How far the preview origin moves per nudge key press, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 10f), Array.Empty())); _previewMoveStep.SettingChanged += delegate { PreviewMoveStep = Mathf.Clamp(_previewMoveStep.Value, 0.25f, 10f); }; PreviewMoveStep = Mathf.Clamp(_previewMoveStep.Value, 0.25f, 10f); _previewFineMoveStep = ((BaseUnityPlugin)this).Config.Bind("Preview", "FineMoveStep", 0.5f, new ConfigDescription("How far the preview origin moves per nudge key press while the fine-adjust key is held, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 5f), Array.Empty())); _previewFineMoveStep.SettingChanged += delegate { PreviewFineMoveStep = Mathf.Clamp(_previewFineMoveStep.Value, 0.05f, 5f); }; PreviewFineMoveStep = Mathf.Clamp(_previewFineMoveStep.Value, 0.05f, 5f); AcceptableValueList val3 = new AcceptableValueList(new float[3] { 22.5f, 45f, 90f }); _buildRotationSnapDeg = ((BaseUnityPlugin)this).Config.Bind("Preview - Rotation", "BuildRotationSnapDegrees", 90f, new ConfigDescription("Snaps the final build rotation to the nearest multiple of this value at preview start and build confirm. Allowed: 22.5, 45, 90.", (AcceptableValueBase)(object)val3, Array.Empty())); _buildRotationSnapDeg.SettingChanged += delegate { BuildRotationSnapDegrees = _buildRotationSnapDeg.Value; }; BuildRotationSnapDegrees = _buildRotationSnapDeg.Value; _previewRotateStepDeg = ((BaseUnityPlugin)this).Config.Bind("Preview - Rotation", "RotateStepDegrees", 90f, new ConfigDescription("How far the preview rotates per coarse rotate key press (Q / G). Allowed: 22.5, 45, 90.", (AcceptableValueBase)(object)val3, Array.Empty())); _previewRotateStepDeg.SettingChanged += delegate { PreviewRotateStepDeg = _previewRotateStepDeg.Value; }; PreviewRotateStepDeg = _previewRotateStepDeg.Value; _previewFineRotateStepDeg = ((BaseUnityPlugin)this).Config.Bind("Preview - Rotation", "FineRotateStepDegrees", 22.5f, new ConfigDescription("How far the preview rotates per key press while the fine-adjust key (Left Shift) is held. Allowed: 22.5, 45, 90.", (AcceptableValueBase)(object)val3, Array.Empty())); _previewFineRotateStepDeg.SettingChanged += delegate { PreviewFineRotateStepDeg = _previewFineRotateStepDeg.Value; }; PreviewFineRotateStepDeg = _previewFineRotateStepDeg.Value; _previewMoveForwardKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "MoveForwardKey", (KeyCode)273, "Preview nudge key for moving the origin forward relative to the camera."); _previewMoveBackwardKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "MoveBackwardKey", (KeyCode)274, "Preview nudge key for moving the origin backward relative to the camera."); _previewMoveLeftKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "MoveLeftKey", (KeyCode)276, "Preview nudge key for moving the origin left relative to the camera."); _previewMoveRightKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "MoveRightKey", (KeyCode)275, "Preview nudge key for moving the origin right relative to the camera."); _previewConfirmKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "ConfirmKey", (KeyCode)101, "Preview key that confirms build placement at the current preview position."); _previewRotateLeftKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "RotateLeftKey", (KeyCode)113, "Preview rotation key for rotating counter-clockwise."); _previewRotateRightKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "RotateRightKey", (KeyCode)103, "Preview rotation key for rotating clockwise."); if ((int)_previewRotateRightKey.Value == 116) { _previewRotateRightKey.Value = (KeyCode)103; } _previewCancelKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "CancelKey", (KeyCode)27, "Preview keyboard cancel key. Right-click always cancels too."); _previewFineAdjustKey = ((BaseUnityPlugin)this).Config.Bind("Preview", "FineAdjustKey", (KeyCode)304, "Hold this key for fine movement and fine rotation while previewing."); _previewMoveForwardKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewMoveForwardKey = _previewMoveForwardKey.Value; }; _previewMoveBackwardKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewMoveBackwardKey = _previewMoveBackwardKey.Value; }; _previewMoveLeftKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewMoveLeftKey = _previewMoveLeftKey.Value; }; _previewMoveRightKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewMoveRightKey = _previewMoveRightKey.Value; }; _previewConfirmKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewConfirmKey = _previewConfirmKey.Value; }; _previewRotateLeftKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewRotateLeftKey = _previewRotateLeftKey.Value; }; _previewRotateRightKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewRotateRightKey = _previewRotateRightKey.Value; }; _previewCancelKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewCancelKey = _previewCancelKey.Value; }; _previewFineAdjustKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) PreviewFineAdjustKey = _previewFineAdjustKey.Value; }; PreviewMoveForwardKey = _previewMoveForwardKey.Value; PreviewMoveBackwardKey = _previewMoveBackwardKey.Value; PreviewMoveLeftKey = _previewMoveLeftKey.Value; PreviewMoveRightKey = _previewMoveRightKey.Value; PreviewConfirmKey = _previewConfirmKey.Value; PreviewRotateLeftKey = _previewRotateLeftKey.Value; PreviewRotateRightKey = _previewRotateRightKey.Value; PreviewCancelKey = _previewCancelKey.Value; PreviewFineAdjustKey = _previewFineAdjustKey.Value; _terrainLevelPasses = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainLevelPasses", 2, new ConfigDescription("Number of terrain leveling passes to run before spike cleanup. Lower is faster; higher can smooth stubborn areas.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 5), Array.Empty())); _terrainLevelPasses.SettingChanged += delegate { TerrainLevelPasses = Mathf.Clamp(_terrainLevelPasses.Value, 1, 5); }; TerrainLevelPasses = Mathf.Clamp(_terrainLevelPasses.Value, 1, 5); _terrainSpikeCleanupPasses = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainSpikeCleanupPasses", 2, new ConfigDescription("Number of spike cleanup passes after leveling. Lower is faster; higher can reduce edge peaks on rough terrain.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 5), Array.Empty())); _terrainSpikeCleanupPasses.SettingChanged += delegate { TerrainSpikeCleanupPasses = Mathf.Clamp(_terrainSpikeCleanupPasses.Value, 1, 5); }; TerrainSpikeCleanupPasses = Mathf.Clamp(_terrainSpikeCleanupPasses.Value, 1, 5); _terrainStampRadius = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainStampRadius", 3f, new ConfigDescription("Radius of each terrain leveling disc stamp in metres. Controls how wide the green preview border is and how far terrain is affected beyond the build pad edge. Larger = smoother blending but wider terrain disturbance; smaller = tighter edge but may leave small gaps.", (AcceptableValueBase)(object)new AcceptableValueRange(3f, 6f), Array.Empty())); _terrainStampRadius.SettingChanged += delegate { TerrainStampRadius = Mathf.Clamp(_terrainStampRadius.Value, 3f, 6f); }; TerrainStampRadius = Mathf.Clamp(_terrainStampRadius.Value, 3f, 6f); _terrainHighPointDelta = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainHighPointDelta", 0f, new ConfigDescription("Extra height in metres added to the sampled highest point for the terrain leveling target. Final target is HighestPoint + Delta.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 4f), Array.Empty())); _terrainHighPointDelta.SettingChanged += delegate { TerrainHighPointDelta = Mathf.Clamp(_terrainHighPointDelta.Value, 0f, 4f); }; TerrainHighPointDelta = Mathf.Clamp(_terrainHighPointDelta.Value, 0f, 4f); _terrainUseStagedRaise = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainUseStagedRaise", false, "When enabled, leveling raises terrain in multiple vertical stages instead of a single full-height jump. Experimental: may help on some slopes but can worsen spikes on irregular edges."); _terrainUseStagedRaise.SettingChanged += delegate { TerrainUseStagedRaise = _terrainUseStagedRaise.Value; }; TerrainUseStagedRaise = _terrainUseStagedRaise.Value; _terrainRaiseStepHeight = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainRaiseStepHeight", 0.5f, new ConfigDescription("Maximum vertical raise per stage in metres when TerrainUseStagedRaise is enabled. Smaller values create more stages and gentler terrain transitions.", (AcceptableValueBase)(object)new AcceptableValueRange(0.15f, 1.5f), Array.Empty())); _terrainRaiseStepHeight.SettingChanged += delegate { TerrainRaiseStepHeight = Mathf.Clamp(_terrainRaiseStepHeight.Value, 0.15f, 1.5f); }; TerrainRaiseStepHeight = Mathf.Clamp(_terrainRaiseStepHeight.Value, 0.15f, 1.5f); _terrainMaxRaiseStages = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainMaxRaiseStages", 1, new ConfigDescription("Upper limit on the number of vertical raise stages when TerrainUseStagedRaise is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 16), Array.Empty())); _terrainMaxRaiseStages.SettingChanged += delegate { TerrainMaxRaiseStages = Mathf.Clamp(_terrainMaxRaiseStages.Value, 1, 16); }; TerrainMaxRaiseStages = Mathf.Clamp(_terrainMaxRaiseStages.Value, 1, 16); _terrainSkipSatisfiedCenterStamps = ((BaseUnityPlugin)this).Config.Bind("Terrain", "TerrainSkipSatisfiedCenterStamps", true, "When enabled, center leveling stamps are skipped if sampled terrain is already at/above target. Disable for exact legacy behavior (always stamp every center point each pass)."); _terrainSkipSatisfiedCenterStamps.SettingChanged += delegate { TerrainSkipSatisfiedCenterStamps = _terrainSkipSatisfiedCenterStamps.Value; }; TerrainSkipSatisfiedCenterStamps = _terrainSkipSatisfiedCenterStamps.Value; ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)("ValheimFloorPlan v2.1.3 loaded! " + $"Build: {_buildHotkey.Value} Undo: {_undoHotkey.Value} Undo(keep terrain): {_undoKeepTerrainHotkey.Value} Progress HUD: {ProgressMessageType} Terrain passes: {TerrainLevelPasses} Spike cleanup passes: {TerrainSpikeCleanupPasses} High-point delta: {TerrainHighPointDelta:F2}m Staged raise: {TerrainUseStagedRaise} ({TerrainRaiseStepHeight:F2}m, max {TerrainMaxRaiseStages}) Skip satisfied center stamps: {TerrainSkipSatisfiedCenterStamps} External wall heights: L1={ExternalWallHeightLevel1}, L2={ExternalWallHeightLevel2}, L3={ExternalWallHeightLevel3} Wall/Pillar material: {WallPillarMaterial} Staircase reach: {StaircaseReachMode} Roof scaffolding: {RoofScaffolding} (style={RoofStyle}, openTop={OpenTop}, flooring={RoofScaffoldingGableFlooring}) Scaffolding levels: {ScaffoldingLevels} Scaffolding floor height: {ScaffoldingFloorHeight}m Scaffolding floors: {ScaffoldingFloors} Transverse beams: {TransverseScaffoldingBeams} Longitudinal beams: {LongitudinalScaffoldingBeams} Origin extra offset: {BuildOriginForwardOffset:F1}m Preview move: {PreviewMoveStep:F2}/{PreviewFineMoveStep:F2}m Preview rotate: {PreviewRotateStepDeg:F0}/{PreviewFineRotateStepDeg:F0}° Build snap: {BuildRotationSnapDegrees:F1}°")); } private void Update() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) if (_bundleImportSelectionActive) { UpdateBundleImportSelection(); return; } KeyboardShortcut value = _exportBundleHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { ExportPresetBundle(); return; } value = _importBundleHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { StartBundleImportSelection(); return; } value = _buildHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { string text = ResolveVfpPath(_vfpFilePath.Value.Trim()); if (string.IsNullOrEmpty(text)) { Log.LogWarning((object)"No floor plan file configured. Set 'FloorPlanFile' in the BepInEx config."); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "ValheimFloorPlan: No .vfp file set in config!", 0, (Sprite)null); } return; } FloorPlanBuilder.Instance.StartPreview(text); } value = _undoKeepTerrainHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { FloorPlanBuilder.Instance.Undo(keepLeveledTerrain: true); return; } value = _undoHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { FloorPlanBuilder.Instance.Undo(); } } private void StartBundleImportSelection() { try { string[] availableBundleNames = GetAvailableBundleNames(); if (availableBundleNames.Length == 0) { ShowProgressMessage("No .vpfset bundles found in PresetBundles."); return; } _bundleImportSelectionNames = availableBundleNames; _bundleImportSelectionIndex = 0; _bundleImportSelectionActive = true; _bundleImportSelectionExpireAt = Time.time + 5f; _bundleImportLastShownSecond = -1; ShowBundleImportSelectionMessage(); } catch (Exception arg) { Log.LogError((object)$"[Bundles] Could not start bundle selection: {arg}"); ShowProgressMessage("Could not start bundle selection. Check BepInEx log."); } } private void UpdateBundleImportSelection() { int num = Mathf.Max(0, Mathf.CeilToInt(_bundleImportSelectionExpireAt - Time.time)); if (Time.time >= _bundleImportSelectionExpireAt) { CancelBundleImportSelection("Bundle import canceled (timeout)."); } else if (Input.GetKeyDown((KeyCode)27)) { CancelBundleImportSelection("Bundle import canceled."); } else if (Input.GetKeyDown((KeyCode)275)) { _bundleImportSelectionIndex = (_bundleImportSelectionIndex + 1) % _bundleImportSelectionNames.Length; _bundleImportSelectionExpireAt = Time.time + 5f; _bundleImportLastShownSecond = -1; ShowBundleImportSelectionMessage(); } else if (Input.GetKeyDown((KeyCode)276)) { _bundleImportSelectionIndex = (_bundleImportSelectionIndex - 1 + _bundleImportSelectionNames.Length) % _bundleImportSelectionNames.Length; _bundleImportSelectionExpireAt = Time.time + 5f; _bundleImportLastShownSecond = -1; ShowBundleImportSelectionMessage(); } else if (Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271)) { string bundleNameOverride = _bundleImportSelectionNames[_bundleImportSelectionIndex]; _bundleImportSelectionActive = false; ImportPresetBundle(bundleNameOverride); } else if (num != _bundleImportLastShownSecond) { ShowBundleImportSelectionMessage(); } } private void CancelBundleImportSelection(string reason) { _bundleImportSelectionActive = false; _bundleImportSelectionNames = Array.Empty(); _bundleImportSelectionIndex = 0; _bundleImportSelectionExpireAt = 0f; _bundleImportLastShownSecond = -1; ShowProgressMessage(reason); } private void ShowBundleImportSelectionMessage() { if (_bundleImportSelectionNames.Length != 0) { int num = Mathf.Clamp(_bundleImportSelectionIndex, 0, _bundleImportSelectionNames.Length - 1); int num2 = num + 1; int num3 = _bundleImportSelectionNames.Length; int num4 = Mathf.Max(0, Mathf.CeilToInt(_bundleImportSelectionExpireAt - Time.time)); string text = _bundleImportSelectionNames[num]; _bundleImportLastShownSecond = num4; ShowProgressMessage($"{text} : bundle {num2} of {num3}, press for next, for previous, import, cancel ({num4}s)"); } } private void ExportPresetBundle() { try { string text = ResolveVfpPath((_vfpFilePath.Value ?? string.Empty).Trim()); string text2 = ResolveVfpPath((_vfpFilePathLevel2.Value ?? string.Empty).Trim()); string text3 = ResolveVfpPath((_vfpFilePathLevel3.Value ?? string.Empty).Trim()); if (string.IsNullOrEmpty(text) || !File.Exists(text)) { ShowProgressMessage("Export failed: Level 1 FloorPlanFile is missing or not found."); return; } if (FloorPlanLevels >= 2 && (string.IsNullOrEmpty(text2) || !File.Exists(text2))) { ShowProgressMessage("Export failed: Level 2 FloorPlanFileLevel2 is missing or not found."); return; } if (FloorPlanLevels >= 3 && (string.IsNullOrEmpty(text3) || !File.Exists(text3))) { ShowProgressMessage("Export failed: Level 3 FloorPlanFileLevel3 is missing or not found."); return; } string safeBundleName = GetSafeBundleName(_bundleName.Value); string text4 = safeBundleName + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture); string bundlesDirectory = GetBundlesDirectory(); Directory.CreateDirectory(bundlesDirectory); string text5 = Path.Combine(bundlesDirectory, text4 + ".vpfset"); PresetBundleSettings s = new PresetBundleSettings { FloorPlanLevels = Mathf.Clamp(_floorPlanLevels.Value, 1, 3), UndoRadius = Mathf.Clamp(_undoRadius.Value, 5f, 150f), ProgressMessagePosition = (_progressMessagePosition.Value ?? string.Empty).Trim(), WarningMessagePosition = (_warningMessagePosition.Value ?? string.Empty).Trim(), ScaffoldingLevels = Mathf.Clamp(_scaffoldingLevels.Value, 1, 3), ScaffoldingFloorHeight = _scaffoldingFloorHeight.Value, ScaffoldingFloorHeight2 = _scaffoldingFloorHeight2.Value, ScaffoldingFloorHeight3 = _scaffoldingFloorHeight3.Value, RoofScaffolding = _roofScaffolding.Value, RoofStyle = (_roofStyle.Value ?? "Gable").Trim(), OpenTop = _openTop.Value, RoofScaffoldingGableFlooring = (_roofScaffoldingGableFlooring.Value ?? "RoofWithFloorUnderlay").Trim(), ScaffoldingFloors = _scaffoldingFloors.Value, TransverseScaffoldingBeams = _transverseScaffoldingBeams.Value, LongitudinalScaffoldingBeams = _longitudinalScaffoldingBeams.Value, ExternalWallHeightLevel1 = Mathf.Clamp(_externalWallHeightLevel1.Value, 1, 6), ExternalWallHeightLevel2 = Mathf.Clamp(_externalWallHeightLevel2.Value, 1, 6), ExternalWallHeightLevel3 = Mathf.Clamp(_externalWallHeightLevel3.Value, 1, 6), InternalWallHeightLevel1 = Mathf.Clamp(_internalWallHeightLevel1.Value, 1, 6), InternalWallHeightLevel2 = Mathf.Clamp(_internalWallHeightLevel2.Value, 1, 6), InternalWallHeightLevel3 = Mathf.Clamp(_internalWallHeightLevel3.Value, 1, 6), WallPillarMaterial = (_wallPillarMaterial.Value ?? "Stone").Trim(), DisableWelcomePost = _disableWelcomePost.Value, StaircaseReachMode = (_staircaseReachMode.Value ?? "ToTheNextLevelOnly").Trim(), StaircaseStepRise = Mathf.Clamp(_staircaseStepRise.Value, 0.1f, 0.4f), StaircaseStepAngleDeg = Mathf.Clamp(_staircaseStepAngleDeg.Value, 10f, 30f), StaircaseStepRadius = Mathf.Clamp(_staircaseStepRadius.Value, 0.5f, 1f), BuildOriginForwardOffset = Mathf.Clamp(_buildOriginForwardOffset.Value, 0f, 20f), MoveStep = Mathf.Clamp(_previewMoveStep.Value, 0.25f, 10f), FineMoveStep = Mathf.Clamp(_previewFineMoveStep.Value, 0.05f, 5f), BuildRotationSnapDegrees = _buildRotationSnapDeg.Value, RotateStepDegrees = _previewRotateStepDeg.Value, FineRotateStepDegrees = _previewFineRotateStepDeg.Value, TerrainLevelPasses = Mathf.Clamp(_terrainLevelPasses.Value, 1, 5), TerrainSpikeCleanupPasses = Mathf.Clamp(_terrainSpikeCleanupPasses.Value, 1, 5), TerrainStampRadius = Mathf.Clamp(_terrainStampRadius.Value, 3f, 6f), TerrainHighPointDelta = Mathf.Clamp(_terrainHighPointDelta.Value, 0f, 4f), TerrainUseStagedRaise = _terrainUseStagedRaise.Value, TerrainRaiseStepHeight = Mathf.Clamp(_terrainRaiseStepHeight.Value, 0.15f, 1.5f), TerrainMaxRaiseStages = Mathf.Clamp(_terrainMaxRaiseStages.Value, 1, 16), TerrainSkipSatisfiedCenterStamps = _terrainSkipSatisfiedCenterStamps.Value }; if (File.Exists(text5)) { File.Delete(text5); } using (FileStream stream = new FileStream(text5, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Create); WriteZipEntryFromFile(zipArchive, "level1.vfp", text); if (!string.IsNullOrEmpty(text2) && File.Exists(text2)) { WriteZipEntryFromFile(zipArchive, "level2.vfp", text2); } if (!string.IsNullOrEmpty(text3) && File.Exists(text3)) { WriteZipEntryFromFile(zipArchive, "level3.vfp", text3); } ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry("settings.json", CompressionLevel.Optimal); using StreamWriter streamWriter = new StreamWriter(zipArchiveEntry.Open()); streamWriter.Write(SerializeSettings(s)); } _bundleName.Value = safeBundleName; ((BaseUnityPlugin)this).Config.Save(); ShowProgressMessage("Preset bundle exported: " + text4 + ".vpfset"); Log.LogInfo((object)("[Bundles] Exported preset bundle to '" + text5 + "'. Key mappings were excluded by design.")); } catch (Exception arg) { Log.LogError((object)$"[Bundles] Export failed: {arg}"); ShowProgressMessage("Export failed. Check BepInEx log for details."); } } private void ImportPresetBundle(string? bundleNameOverride = null) { try { string safeBundleName = GetSafeBundleName(bundleNameOverride ?? _bundleName.Value); string bundlesDirectory = GetBundlesDirectory(); string text = ResolveBundlePath(bundlesDirectory, safeBundleName); if (string.IsNullOrEmpty(text) || !File.Exists(text)) { ShowProgressMessage("Import failed: " + safeBundleName + ".vpfset not found."); return; } string text2 = Path.Combine(bundlesDirectory, safeBundleName); Directory.CreateDirectory(text2); string text3 = Path.Combine(text2, "level1.vfp"); string text4 = Path.Combine(text2, "level2.vfp"); string text5 = Path.Combine(text2, "level3.vfp"); string text6 = string.Empty; using (FileStream stream = new FileStream(text, FileMode.Open, FileAccess.Read, FileShare.Read)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); bool flag = false; bool flag2 = false; bool flag3 = false; foreach (ZipArchiveEntry entry in zipArchive.Entries) { string name = entry.Name; if (string.Equals(name, "level1.vfp", StringComparison.OrdinalIgnoreCase)) { ExtractZipEntry(entry, text3); flag = true; } else if (string.Equals(name, "level2.vfp", StringComparison.OrdinalIgnoreCase)) { ExtractZipEntry(entry, text4); flag2 = true; } else if (string.Equals(name, "level3.vfp", StringComparison.OrdinalIgnoreCase)) { ExtractZipEntry(entry, text5); flag3 = true; } else if (string.Equals(name, "settings.json", StringComparison.OrdinalIgnoreCase)) { using StreamReader streamReader = new StreamReader(entry.Open()); text6 = streamReader.ReadToEnd(); } } if (!flag) { ShowProgressMessage("Import failed: bundle does not contain level1.vfp."); return; } if (string.IsNullOrWhiteSpace(text6)) { ShowProgressMessage("Import failed: bundle does not contain settings.json."); return; } if (!TryParseSettings(text6, out PresetBundleSettings settings) || settings == null) { ShowProgressMessage("Import failed: settings.json could not be parsed."); return; } _floorPlanLevels.Value = Mathf.Clamp(settings.FloorPlanLevels, 1, 3); _undoRadius.Value = Mathf.Clamp(settings.UndoRadius, 5f, 150f); _progressMessagePosition.Value = settings.ProgressMessagePosition ?? "CenterLeft"; _warningMessagePosition.Value = settings.WarningMessagePosition ?? "TopLeft"; _scaffoldingLevels.Value = Mathf.Clamp(settings.ScaffoldingLevels, 1, 3); _scaffoldingFloorHeight.Value = settings.ScaffoldingFloorHeight; _scaffoldingFloorHeight2.Value = settings.ScaffoldingFloorHeight2; _scaffoldingFloorHeight3.Value = settings.ScaffoldingFloorHeight3; _roofScaffolding.Value = settings.RoofScaffolding; _roofStyle.Value = (string.IsNullOrWhiteSpace(settings.RoofStyle) ? "Gable" : settings.RoofStyle); _openTop.Value = settings.OpenTop; _roofScaffoldingGableFlooring.Value = (string.IsNullOrWhiteSpace(settings.RoofScaffoldingGableFlooring) ? "RoofWithFloorUnderlay" : settings.RoofScaffoldingGableFlooring); _scaffoldingFloors.Value = settings.ScaffoldingFloors; _transverseScaffoldingBeams.Value = settings.TransverseScaffoldingBeams; _longitudinalScaffoldingBeams.Value = settings.LongitudinalScaffoldingBeams; _externalWallHeightLevel1.Value = Mathf.Clamp(settings.ExternalWallHeightLevel1, 1, 6); _externalWallHeightLevel2.Value = Mathf.Clamp(settings.ExternalWallHeightLevel2, 1, 6); _externalWallHeightLevel3.Value = Mathf.Clamp(settings.ExternalWallHeightLevel3, 1, 6); _internalWallHeightLevel1.Value = Mathf.Clamp(settings.InternalWallHeightLevel1, 1, 6); _internalWallHeightLevel2.Value = Mathf.Clamp(settings.InternalWallHeightLevel2, 1, 6); _internalWallHeightLevel3.Value = Mathf.Clamp(settings.InternalWallHeightLevel3, 1, 6); _wallPillarMaterial.Value = (string.IsNullOrWhiteSpace(settings.WallPillarMaterial) ? "Stone" : settings.WallPillarMaterial); _disableWelcomePost.Value = settings.DisableWelcomePost; _staircaseReachMode.Value = (string.IsNullOrWhiteSpace(settings.StaircaseReachMode) ? "ToTheNextLevelOnly" : settings.StaircaseReachMode); _staircaseStepRise.Value = Mathf.Clamp(settings.StaircaseStepRise, 0.1f, 0.4f); _staircaseStepAngleDeg.Value = Mathf.Clamp(settings.StaircaseStepAngleDeg, 10f, 30f); _staircaseStepRadius.Value = Mathf.Clamp(settings.StaircaseStepRadius, 0.5f, 1f); _buildOriginForwardOffset.Value = Mathf.Clamp(settings.BuildOriginForwardOffset, 0f, 20f); _previewMoveStep.Value = Mathf.Clamp(settings.MoveStep, 0.25f, 10f); _previewFineMoveStep.Value = Mathf.Clamp(settings.FineMoveStep, 0.05f, 5f); _buildRotationSnapDeg.Value = settings.BuildRotationSnapDegrees; _previewRotateStepDeg.Value = settings.RotateStepDegrees; _previewFineRotateStepDeg.Value = settings.FineRotateStepDegrees; _terrainLevelPasses.Value = Mathf.Clamp(settings.TerrainLevelPasses, 1, 5); _terrainSpikeCleanupPasses.Value = Mathf.Clamp(settings.TerrainSpikeCleanupPasses, 1, 5); _terrainStampRadius.Value = Mathf.Clamp(settings.TerrainStampRadius, 3f, 6f); _terrainHighPointDelta.Value = Mathf.Clamp(settings.TerrainHighPointDelta, 0f, 4f); _terrainUseStagedRaise.Value = settings.TerrainUseStagedRaise; _terrainRaiseStepHeight.Value = Mathf.Clamp(settings.TerrainRaiseStepHeight, 0.15f, 1.5f); _terrainMaxRaiseStages.Value = Mathf.Clamp(settings.TerrainMaxRaiseStages, 1, 16); _terrainSkipSatisfiedCenterStamps.Value = settings.TerrainSkipSatisfiedCenterStamps; _vfpFilePath.Value = text3; _vfpFilePathLevel2.Value = (flag2 ? text4 : string.Empty); _vfpFilePathLevel3.Value = (flag3 ? text5 : string.Empty); ApplyScaffoldingRules(); ((BaseUnityPlugin)this).Config.Save(); } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); _bundleName.Value = StripTimestampSuffix(fileNameWithoutExtension); ((BaseUnityPlugin)this).Config.Save(); ShowProgressMessage("Preset bundle imported: " + Path.GetFileName(text)); Log.LogInfo((object)("[Bundles] Imported preset bundle from '" + text + "'. Key mappings were intentionally left unchanged.")); } catch (Exception arg) { Log.LogError((object)$"[Bundles] Import failed: {arg}"); ShowProgressMessage("Import failed. Check BepInEx log for details."); } } private static void WriteZipEntryFromFile(ZipArchive archive, string entryName, string sourceFile) { ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(entryName, CompressionLevel.Optimal); using FileStream fileStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read); using Stream destination = zipArchiveEntry.Open(); fileStream.CopyTo(destination); } private static string SerializeSettings(PresetBundleSettings s) { return string.Join("\n", "FloorPlanLevels=" + s.FloorPlanLevels.ToString(CultureInfo.InvariantCulture), "UndoRadius=" + s.UndoRadius.ToString(CultureInfo.InvariantCulture), "ProgressMessagePosition=" + (s.ProgressMessagePosition ?? string.Empty), "WarningMessagePosition=" + (s.WarningMessagePosition ?? string.Empty), "ScaffoldingLevels=" + s.ScaffoldingLevels.ToString(CultureInfo.InvariantCulture), "ScaffoldingFloorHeight=" + s.ScaffoldingFloorHeight.ToString(CultureInfo.InvariantCulture), "ScaffoldingFloorHeight2=" + s.ScaffoldingFloorHeight2.ToString(CultureInfo.InvariantCulture), "ScaffoldingFloorHeight3=" + s.ScaffoldingFloorHeight3.ToString(CultureInfo.InvariantCulture), "RoofScaffolding=" + s.RoofScaffolding, "RoofStyle=" + (s.RoofStyle ?? string.Empty), "OpenTop=" + s.OpenTop, "RoofScaffoldingGableFlooring=" + (s.RoofScaffoldingGableFlooring ?? string.Empty), "ScaffoldingFloors=" + s.ScaffoldingFloors, "TransverseScaffoldingBeams=" + s.TransverseScaffoldingBeams, "LongitudinalScaffoldingBeams=" + s.LongitudinalScaffoldingBeams, "ExternalWallHeightLevel1=" + s.ExternalWallHeightLevel1.ToString(CultureInfo.InvariantCulture), "ExternalWallHeightLevel2=" + s.ExternalWallHeightLevel2.ToString(CultureInfo.InvariantCulture), "ExternalWallHeightLevel3=" + s.ExternalWallHeightLevel3.ToString(CultureInfo.InvariantCulture), "InternalWallHeightLevel1=" + s.InternalWallHeightLevel1.ToString(CultureInfo.InvariantCulture), "InternalWallHeightLevel2=" + s.InternalWallHeightLevel2.ToString(CultureInfo.InvariantCulture), "InternalWallHeightLevel3=" + s.InternalWallHeightLevel3.ToString(CultureInfo.InvariantCulture), "WallPillarMaterial=" + (s.WallPillarMaterial ?? string.Empty), "DisableWelcomePost=" + s.DisableWelcomePost, "StaircaseReachMode=" + (s.StaircaseReachMode ?? string.Empty), "StaircaseStepRise=" + s.StaircaseStepRise.ToString(CultureInfo.InvariantCulture), "StaircaseStepAngleDeg=" + s.StaircaseStepAngleDeg.ToString(CultureInfo.InvariantCulture), "StaircaseStepRadius=" + s.StaircaseStepRadius.ToString(CultureInfo.InvariantCulture), "BuildOriginForwardOffset=" + s.BuildOriginForwardOffset.ToString(CultureInfo.InvariantCulture), "MoveStep=" + s.MoveStep.ToString(CultureInfo.InvariantCulture), "FineMoveStep=" + s.FineMoveStep.ToString(CultureInfo.InvariantCulture), "BuildRotationSnapDegrees=" + s.BuildRotationSnapDegrees.ToString(CultureInfo.InvariantCulture), "RotateStepDegrees=" + s.RotateStepDegrees.ToString(CultureInfo.InvariantCulture), "FineRotateStepDegrees=" + s.FineRotateStepDegrees.ToString(CultureInfo.InvariantCulture), "TerrainLevelPasses=" + s.TerrainLevelPasses.ToString(CultureInfo.InvariantCulture), "TerrainSpikeCleanupPasses=" + s.TerrainSpikeCleanupPasses.ToString(CultureInfo.InvariantCulture), "TerrainStampRadius=" + s.TerrainStampRadius.ToString(CultureInfo.InvariantCulture), "TerrainHighPointDelta=" + s.TerrainHighPointDelta.ToString(CultureInfo.InvariantCulture), "TerrainUseStagedRaise=" + s.TerrainUseStagedRaise, "TerrainRaiseStepHeight=" + s.TerrainRaiseStepHeight.ToString(CultureInfo.InvariantCulture), "TerrainMaxRaiseStages=" + s.TerrainMaxRaiseStages.ToString(CultureInfo.InvariantCulture), "TerrainSkipSatisfiedCenterStamps=" + s.TerrainSkipSatisfiedCenterStamps); } private static bool TryParseSettings(string raw, out PresetBundleSettings settings) { settings = new PresetBundleSettings(); if (string.IsNullOrWhiteSpace(raw)) { return false; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] array = raw.Replace("\r", string.Empty).Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && !text.StartsWith("#")) { int num = text.IndexOf('='); if (num > 0) { string key = text.Substring(0, num).Trim(); string value = text.Substring(num + 1).Trim(); dictionary[key] = value; } } } settings.FloorPlanLevels = ReadInt(dictionary, "FloorPlanLevels", settings.FloorPlanLevels); settings.UndoRadius = ReadFloat(dictionary, "UndoRadius", settings.UndoRadius); settings.ProgressMessagePosition = ReadString(dictionary, "ProgressMessagePosition", settings.ProgressMessagePosition); settings.WarningMessagePosition = ReadString(dictionary, "WarningMessagePosition", settings.WarningMessagePosition); settings.ScaffoldingLevels = ReadInt(dictionary, "ScaffoldingLevels", settings.ScaffoldingLevels); settings.ScaffoldingFloorHeight = ReadInt(dictionary, "ScaffoldingFloorHeight", settings.ScaffoldingFloorHeight); settings.ScaffoldingFloorHeight2 = ReadInt(dictionary, "ScaffoldingFloorHeight2", settings.ScaffoldingFloorHeight2); settings.ScaffoldingFloorHeight3 = ReadInt(dictionary, "ScaffoldingFloorHeight3", settings.ScaffoldingFloorHeight3); settings.RoofScaffolding = ReadBool(dictionary, "RoofScaffolding", settings.RoofScaffolding); settings.RoofStyle = ReadString(dictionary, "RoofStyle", settings.RoofStyle); settings.OpenTop = ReadBool(dictionary, "OpenTop", settings.OpenTop); if (!dictionary.ContainsKey("RoofStyle") && dictionary.TryGetValue("RoofScaffoldingType", out var value2)) { if (string.Equals(value2, "NoRoof", StringComparison.OrdinalIgnoreCase) || string.Equals(value2, "No Roof", StringComparison.OrdinalIgnoreCase)) { settings.OpenTop = true; } else if (string.Equals(value2, "Flat", StringComparison.OrdinalIgnoreCase)) { settings.RoofStyle = "Flat"; } else { settings.RoofStyle = "Gable"; } } settings.RoofScaffoldingGableFlooring = ReadString(dictionary, "RoofScaffoldingGableFlooring", settings.RoofScaffoldingGableFlooring); settings.ScaffoldingFloors = ReadBool(dictionary, "ScaffoldingFloors", settings.ScaffoldingFloors); settings.TransverseScaffoldingBeams = ReadBool(dictionary, "TransverseScaffoldingBeams", settings.TransverseScaffoldingBeams); settings.LongitudinalScaffoldingBeams = ReadBool(dictionary, "LongitudinalScaffoldingBeams", settings.LongitudinalScaffoldingBeams); settings.ExternalWallHeightLevel1 = ReadInt(dictionary, "ExternalWallHeightLevel1", settings.ExternalWallHeightLevel1); settings.ExternalWallHeightLevel2 = ReadInt(dictionary, "ExternalWallHeightLevel2", settings.ExternalWallHeightLevel2); settings.ExternalWallHeightLevel3 = ReadInt(dictionary, "ExternalWallHeightLevel3", settings.ExternalWallHeightLevel3); settings.InternalWallHeightLevel1 = ReadInt(dictionary, "InternalWallHeightLevel1", settings.InternalWallHeightLevel1); settings.InternalWallHeightLevel2 = ReadInt(dictionary, "InternalWallHeightLevel2", settings.InternalWallHeightLevel2); settings.InternalWallHeightLevel3 = ReadInt(dictionary, "InternalWallHeightLevel3", settings.InternalWallHeightLevel3); settings.WallPillarMaterial = ReadString(dictionary, "WallPillarMaterial", settings.WallPillarMaterial); settings.DisableWelcomePost = ReadBool(dictionary, "DisableWelcomePost", settings.DisableWelcomePost); settings.StaircaseReachMode = ReadString(dictionary, "StaircaseReachMode", settings.StaircaseReachMode); settings.StaircaseStepRise = ReadFloat(dictionary, "StaircaseStepRise", settings.StaircaseStepRise); settings.StaircaseStepAngleDeg = ReadFloat(dictionary, "StaircaseStepAngleDeg", settings.StaircaseStepAngleDeg); settings.StaircaseStepRadius = ReadFloat(dictionary, "StaircaseStepRadius", settings.StaircaseStepRadius); settings.BuildOriginForwardOffset = ReadFloat(dictionary, "BuildOriginForwardOffset", settings.BuildOriginForwardOffset); settings.MoveStep = ReadFloat(dictionary, "MoveStep", settings.MoveStep); settings.FineMoveStep = ReadFloat(dictionary, "FineMoveStep", settings.FineMoveStep); settings.BuildRotationSnapDegrees = ReadFloat(dictionary, "BuildRotationSnapDegrees", settings.BuildRotationSnapDegrees); settings.RotateStepDegrees = ReadFloat(dictionary, "RotateStepDegrees", settings.RotateStepDegrees); settings.FineRotateStepDegrees = ReadFloat(dictionary, "FineRotateStepDegrees", settings.FineRotateStepDegrees); settings.TerrainLevelPasses = ReadInt(dictionary, "TerrainLevelPasses", settings.TerrainLevelPasses); settings.TerrainSpikeCleanupPasses = ReadInt(dictionary, "TerrainSpikeCleanupPasses", settings.TerrainSpikeCleanupPasses); settings.TerrainStampRadius = ReadFloat(dictionary, "TerrainStampRadius", settings.TerrainStampRadius); settings.TerrainHighPointDelta = ReadFloat(dictionary, "TerrainHighPointDelta", settings.TerrainHighPointDelta); settings.TerrainUseStagedRaise = ReadBool(dictionary, "TerrainUseStagedRaise", settings.TerrainUseStagedRaise); settings.TerrainRaiseStepHeight = ReadFloat(dictionary, "TerrainRaiseStepHeight", settings.TerrainRaiseStepHeight); settings.TerrainMaxRaiseStages = ReadInt(dictionary, "TerrainMaxRaiseStages", settings.TerrainMaxRaiseStages); settings.TerrainSkipSatisfiedCenterStamps = ReadBool(dictionary, "TerrainSkipSatisfiedCenterStamps", settings.TerrainSkipSatisfiedCenterStamps); return true; } private static string ResolveVfpPath(string fileConfig) { if (string.IsNullOrEmpty(fileConfig)) { return fileConfig; } if (!string.IsNullOrEmpty(FloorPlanDirectory) && !Path.IsPathRooted(fileConfig)) { return Path.Combine(FloorPlanDirectory, fileConfig); } return fileConfig; } private static string ReadString(Dictionary map, string key, string fallback) { string value; return map.TryGetValue(key, out value) ? value : fallback; } private static int ReadInt(Dictionary map, string key, int fallback) { string value; int result; return (map.TryGetValue(key, out value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) ? result : fallback; } private static float ReadFloat(Dictionary map, string key, float fallback) { string value; float result; return (map.TryGetValue(key, out value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? result : fallback; } private static bool ReadBool(Dictionary map, string key, bool fallback) { string value; bool result; return (map.TryGetValue(key, out value) && bool.TryParse(value, out result)) ? result : fallback; } private static void ExtractZipEntry(ZipArchiveEntry entry, string destinationPath) { string directoryName = Path.GetDirectoryName(destinationPath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } using Stream stream = entry.Open(); using FileStream destination = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None); stream.CopyTo(destination); } private string GetBundlesDirectory() { if (!string.IsNullOrEmpty(FloorPlanDirectory)) { return FloorPlanDirectory; } string path = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? "."; return Path.Combine(path, "PresetBundles"); } private string[] GetAvailableBundleNames() { string bundlesDirectory = GetBundlesDirectory(); if (!Directory.Exists(bundlesDirectory)) { return Array.Empty(); } string[] files = Directory.GetFiles(bundlesDirectory, "*.vpfset", SearchOption.TopDirectoryOnly); string[] files2 = Directory.GetFiles(bundlesDirectory, "*.vfpset", SearchOption.TopDirectoryOnly); List list = new List(files.Length + files2.Length); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < files.Length; i++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension) && hashSet.Add(fileNameWithoutExtension)) { list.Add(fileNameWithoutExtension); } } for (int j = 0; j < files2.Length; j++) { string fileNameWithoutExtension2 = Path.GetFileNameWithoutExtension(files2[j]); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension2) && hashSet.Add(fileNameWithoutExtension2)) { list.Add(fileNameWithoutExtension2); } } list.Sort(StringComparer.OrdinalIgnoreCase); return list.ToArray(); } private static string ResolveBundlePath(string bundlesDir, string bundleName) { string text = Path.Combine(bundlesDir, bundleName + ".vpfset"); if (File.Exists(text)) { return text; } string text2 = Path.Combine(bundlesDir, bundleName + ".vfpset"); if (File.Exists(text2)) { return text2; } return string.Empty; } private static string GetSafeBundleName(string raw) { string text = (raw ?? string.Empty).Trim(); if (string.IsNullOrEmpty(text)) { text = "MyBuild"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } return text; } private static string StripTimestampSuffix(string bundleName) { string safeBundleName = GetSafeBundleName(bundleName); if (safeBundleName.Length < 17) { return safeBundleName; } int num = safeBundleName.Length - 16; if (safeBundleName[num] != '-') { return safeBundleName; } for (int i = num + 1; i < safeBundleName.Length; i++) { if (i == num + 9) { if (safeBundleName[i] != '-') { return safeBundleName; } } else if (!char.IsDigit(safeBundleName[i])) { return safeBundleName; } } string text = safeBundleName.Substring(0, num).TrimEnd('-', ' '); return string.IsNullOrWhiteSpace(text) ? safeBundleName : text; } internal static void SetUndoRadius(float radius) { if (!((Object)(object)Instance == (Object)null)) { Instance._undoRadius.Value = Mathf.Clamp(radius, 5f, 150f); } } internal static void ShowProgressMessage(string message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ShowWrappedMessage(ProgressMessageType, "ValheimFloorPlan: " + message); } internal static void ShowWrappedMessage(MessageType messageType, string text, int maxLineLength = 72) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) string text2 = WrapHudText(text, maxLineLength); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { ((Character)localPlayer).Message(messageType, text2, 0, (Sprite)null); } } internal static string WrapHudText(string text, int maxLineLength = 72) { if (string.IsNullOrEmpty(text) || maxLineLength < 16) { return text; } string text2 = text.Replace("\r", string.Empty).Replace("\n", " "); string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return text2; } StringBuilder stringBuilder = new StringBuilder(text2.Length + 16); int num = 0; foreach (string text3 in array) { int num2 = ((num != 0) ? 1 : 0) + text3.Length; if (num > 0 && num + num2 > maxLineLength) { stringBuilder.Append('\n'); num = 0; } else if (num > 0) { stringBuilder.Append(' '); num++; } stringBuilder.Append(text3); num += text3.Length; } return stringBuilder.ToString(); } private static MessageType ParseProgressMessageType(string value) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_006b: Unknown result type (might be due to invalid IL or missing references) string text = (value ?? string.Empty).Trim(); if (string.Equals(text, "CenterLeft", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "MiddleLeft", StringComparison.OrdinalIgnoreCase)) { return (MessageType)2; } if (Enum.TryParse(text, ignoreCase: true, out MessageType result)) { return result; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Unknown ProgressMessagePosition '" + value + "'. Falling back to Center.")); } return (MessageType)2; } private void ApplyScaffoldingRules() { if (_applyingScaffoldingRules) { return; } _applyingScaffoldingRules = true; try { int num = Mathf.Clamp(_floorPlanLevels.Value, 1, 3); if (_floorPlanLevels.Value != num) { _floorPlanLevels.Value = num; } int num2 = num; int num3 = Mathf.Clamp(_scaffoldingLevels.Value, num2, 3); if (_scaffoldingLevels.Value != num3) { _scaffoldingLevels.Value = num3; } int value = _scaffoldingFloorHeight.Value; int value2 = _scaffoldingFloorHeight2.Value; int value3 = _scaffoldingFloorHeight3.Value; if (num > 1) { if (!_roofScaffolding.Value) { _roofScaffolding.Value = true; } if (!_scaffoldingFloors.Value) { _scaffoldingFloors.Value = true; } if (!_transverseScaffoldingBeams.Value) { _transverseScaffoldingBeams.Value = true; } if (!_longitudinalScaffoldingBeams.Value) { _longitudinalScaffoldingBeams.Value = true; } } FloorPlanLevels = num; ScaffoldingLevels = num3; ScaffoldingFloorHeight = value; ScaffoldingFloorHeight2 = value2; ScaffoldingFloorHeight3 = value3; RoofScaffolding = _roofScaffolding.Value; ScaffoldingFloors = _scaffoldingFloors.Value; TransverseScaffoldingBeams = _transverseScaffoldingBeams.Value; LongitudinalScaffoldingBeams = _longitudinalScaffoldingBeams.Value; if (_externalWallHeightLevel1 != null && _externalWallHeightLevel2 != null && _externalWallHeightLevel3 != null) { int num4 = Mathf.Clamp(_externalWallHeightLevel1.Value, 1, GetMaxExternalWallHeightForLevel(0, value, value2, value3)); int num5 = Mathf.Clamp(_externalWallHeightLevel2.Value, 1, GetMaxExternalWallHeightForLevel(1, value, value2, value3)); int num6 = Mathf.Clamp(_externalWallHeightLevel3.Value, 1, GetMaxExternalWallHeightForLevel(2, value, value2, value3)); if (_externalWallHeightLevel1.Value != num4) { _externalWallHeightLevel1.Value = num4; } if (_externalWallHeightLevel2.Value != num5) { _externalWallHeightLevel2.Value = num5; } if (_externalWallHeightLevel3.Value != num6) { _externalWallHeightLevel3.Value = num6; } ExternalWallHeightLevel1 = num4; ExternalWallHeightLevel2 = num5; ExternalWallHeightLevel3 = num6; } } finally { _applyingScaffoldingRules = false; } } internal static int GetScaffoldingFloorHeightForLevel(int levelIndex) { return levelIndex switch { 0 => ScaffoldingFloorHeight, 1 => ScaffoldingFloorHeight2, _ => ScaffoldingFloorHeight3, }; } internal static int GetExternalWallHeightForLevel(int levelIndex) { return Mathf.Clamp(levelIndex, 0, 2) switch { 0 => ExternalWallHeightLevel1, 1 => ExternalWallHeightLevel2, _ => ExternalWallHeightLevel3, }; } internal static int GetInternalWallHeightForLevel(int levelIndex) { return Mathf.Clamp(levelIndex, 0, 2) switch { 0 => InternalWallHeightLevel1, 1 => InternalWallHeightLevel2, _ => InternalWallHeightLevel3, }; } internal static int GetMaxExternalWallHeightForLevel(int levelIndex) { return GetMaxExternalWallHeightForLevel(levelIndex, ScaffoldingFloorHeight, ScaffoldingFloorHeight2, ScaffoldingFloorHeight3); } internal static int GetMaxExternalWallHeightForLevel(int levelIndex, int scaffoldingFloorHeight, int scaffoldingFloorHeight2, int scaffoldingFloorHeight3) { return Mathf.Max(1, Mathf.Clamp(levelIndex, 0, 2) switch { 0 => scaffoldingFloorHeight, 1 => scaffoldingFloorHeight2, _ => scaffoldingFloorHeight3, } * 2); } internal static bool GetEffectiveTransverseScaffoldingBeams(int scaffoldingLevels) { return Mathf.Clamp(scaffoldingLevels, 1, 3) > 1 || TransverseScaffoldingBeams; } internal static bool GetEffectiveLongitudinalScaffoldingBeams(int scaffoldingLevels) { return Mathf.Clamp(scaffoldingLevels, 1, 3) > 1 || LongitudinalScaffoldingBeams; } internal static bool UseGableFloorUnderlay() { return RoofStyle == RoofStyleOption.Gable && RoofScaffoldingGableFlooring == RoofScaffoldingGableFlooringOption.RoofWithFloorUnderlay; } internal static void RefreshScaffoldingRules() { Instance?.ApplyScaffoldingRules(); } private static StructuralMaterial ParseStructuralMaterial(string value) { if (string.Equals(value?.Trim(), "Wood", StringComparison.OrdinalIgnoreCase)) { return StructuralMaterial.Wood; } return StructuralMaterial.Stone; } private static RoofStyleOption ParseRoofStyle(string value) { if (string.Equals(value?.Trim(), "Flat", StringComparison.OrdinalIgnoreCase)) { return RoofStyleOption.Flat; } return RoofStyleOption.Gable; } private static RoofScaffoldingGableFlooringOption ParseRoofScaffoldingGableFlooring(string value) { if (string.Equals(value?.Trim(), "RoofOnly", StringComparison.OrdinalIgnoreCase)) { return RoofScaffoldingGableFlooringOption.RoofOnly; } return RoofScaffoldingGableFlooringOption.RoofWithFloorUnderlay; } private static StaircaseReachModeOption ParseStaircaseReachMode(string value) { if (string.Equals(value?.Trim(), "AllTheWay", StringComparison.OrdinalIgnoreCase)) { return StaircaseReachModeOption.AllTheWay; } return StaircaseReachModeOption.ToTheNextLevelOnly; } } }