using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3+680bffc7332f1c6a2517c65426a0cfb48c4aceb3")] [assembly: AssemblyProduct("ValheimFloorPlan")] [assembly: AssemblyTitle("ValheimFloorPlan")] [assembly: AssemblyVersion("1.0.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 FloorPlan { public int Cols { get; set; } public int Rows { get; set; } public List Pieces { 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("piece,")) { continue; } string[] array2 = text2.Split(new char[1] { ',' }); if (array2.Length < 4) { continue; } int rotation = 0; int num = -1; if (array2.Length > 4) { if (int.TryParse(array2[4], out var result)) { rotation = result; num = ((array2.Length > 5) ? 5 : (-1)); } else { num = 4; } } floorPlan.Pieces.Add(new FloorPlanPiece { Col = int.Parse(array2[1]), Row = int.Parse(array2[2]), Type = array2[3], Rotation = rotation, WallFace = ((num >= 0) ? ParseWallFace(array2[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 const float PLACE_DELAY = 0.05f; private const float ORIGIN_MARKER_RADIUS = 0.75f; private const float ORIGIN_MARKER_LIFT = 0.45f; 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; public const string VFP_TAG = "vfp_build"; private float _undoConfirmationExpireAt = 0f; private int _undoConfirmationPieceCount = 0; private int _undoConfirmationTerrainChunks = 0; private Coroutine _undoCountdownCoroutine = null; private Coroutine _undoRefreshCoroutine = 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_RADIUS = 75f; private readonly List _lastPlaced = 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 _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 int _previewRiskBottomCount = 0; public static FloorPlanBuilder Instance { get; private set; } public bool CanUndo => _lastPlaced.Count > 0 || TerrainSnapshot.HasSnapshot; private void Awake() { Instance = this; } public void StartPreview(string path) { //IL_0066: 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_006b: 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_0080: Expected O, but got Unknown //IL_00a0: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01da: 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_01f4: 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_020e: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0235: 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)); return; } _previewPlan = floorPlan; _previewActive = true; Player localPlayer = Player.m_localPlayer; _previewOrigin = (((Object)(object)localPlayer != (Object)null) ? GetBuildOrigin(localPlayer) : Vector3.zero); _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(0.18f, 0.05f, 0.02f, 0.98f), 0.14f, 7); _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 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) _previewActive = false; _previewPlan = null; _previewPadWalls = null; _previewOuterWalls = null; _previewOriginMarker = null; _previewRotationDeg = 0f; _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(); if ((Object)(object)_previewGo != (Object)null) { Object.Destroy((Object)(object)_previewGo); _previewGo = null; } } private void Update() { if (_previewActive && _previewPlan != null) { UpdatePreviewMode(); } } public void ToggleTearRepairMode() { } public void ToggleTerrainClipMode() { } private void UpdatePreviewMode() { //IL_0037: 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_00be: 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_022b: 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_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023f: 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_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0214: 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_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0307: 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_03f3: 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) if (_previewPlan == null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { CancelPreview(); return; } UpdatePreviewPosition(_previewOrigin); 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; previewChanged = true; ((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Rotation {_previewRotationDeg:F0}°", 0, (Sprite)null); } else if (IsPreviewKeyDown(ValheimFloorPlanPlugin.PreviewRotateRightKey)) { _previewRotationDeg = (_previewRotationDeg + num) % 360f; previewChanged = true; ((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Rotation {_previewRotationDeg:F0}°", 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) { _previewOrigin += val; previewChanged = true; ((Character)localPlayer).Message(ValheimFloorPlanPlugin.ProgressMessageType, $"ValheimFloorPlan: Origin ({_previewOrigin.x:F1}, {_previewOrigin.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; 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}° 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 != 0) ? 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 GetBuildOrigin(Player player) { //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_000f: Unknown result type (might be due to invalid IL or missing references) return GetForwardOffsetOrigin(player, ValheimFloorPlanPlugin.BuildOriginForwardOffset); } private static Vector3 GetForwardOffsetOrigin(Player player, float forwardOffset) { //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_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_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) //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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0075: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; forwardOffset = Mathf.Max(0f, forwardOffset); if (forwardOffset <= 0f) { return position; } Vector3 forward = ((Component)player).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { return position; } ((Vector3)(ref forward)).Normalize(); return position + forward * forwardOffset; } 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) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0088: 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_00af: 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) if (_previewPlan != null) { float previewRaiseDelta = Mathf.Clamp(ValheimFloorPlanPlugin.TerrainHighPointDelta, 0f, 4f); TerrainLeveler.GetPadBounds(_previewPlan, origin, out var minX, out var maxX, out var minZ, out var maxZ); TerrainLeveler.GetLeveledAreaBounds(_previewPlan, origin, out var minX2, out var maxX2, out var minZ2, out var maxZ2); SetWallRingRectangle(_previewPadWalls, origin.y, RotateBoundsCorners(origin, minX, maxX, minZ, maxZ, _previewRotationDeg), previewRaiseDelta); SetWallRingRectangle(_previewOuterWalls, origin.y, RotateBoundsCorners(origin, minX2, maxX2, minZ2, maxZ2, _previewRotationDeg), previewRaiseDelta); SetOriginMarker(_previewOriginMarker, origin.y, origin); } } 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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) 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 num = rotDeg * ((float)Math.PI / 180f); float num2 = Mathf.Cos(num); float num3 = Mathf.Sin(num); float x = origin.x; float z = origin.z; for (int i = 0; i < 4; i++) { float num4 = array[i].x - x; float num5 = array[i].y - z; array[i] = new Vector2(x + num4 * num2 + num5 * num3, z - num4 * num3 + num5 * num2); } 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 SetOriginMarker(LineRenderer? lr, float referenceY, Vector3 origin) { //IL_001d: 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_002a: 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_0061: 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_007b: 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_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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00c1: 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_00ca: 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_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_00ec: 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_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_010d: 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_0117: 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) 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; } Vector3[] array = (Vector3[])(object)new Vector3[7]; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(origin.x, num + 0.45f, origin.z); array[0] = val2 + new Vector3(-0.75f, 0f, 0f); array[1] = val2; array[2] = val2 + new Vector3(0.75f, 0f, 0f); array[3] = val2; array[4] = val2 + new Vector3(0f, 0f, 0.75f); array[5] = val2; array[6] = val2 + new Vector3(0f, 0f, -0.75f); lr.SetPositions(array); } } 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() { //IL_0098: 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) 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); return; } CountUndoStats(localPlayer, out var pieceCount, out var terrainChunkCount); if (pieceCount == 0 && terrainChunkCount == 0) { ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, "ValheimFloorPlan: Nothing to undo."); return; } _undoConfirmationPieceCount = pieceCount; _undoConfirmationTerrainChunks = terrainChunkCount; _undoConfirmationExpireAt = Time.time + 5f; if (_undoCountdownCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_undoCountdownCoroutine); } _undoCountdownCoroutine = ((MonoBehaviour)this).StartCoroutine(UndoCountdownCoroutine()); ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.ProgressMessageType, BuildUndoConfirmationMessage(5)); ValheimFloorPlanPlugin.Log.LogInfo((object)$"[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 (_undoConfirmationTerrainChunks > 0) { text += $" and restore {_undoConfirmationTerrainChunks} terrain chunk(s)"; } return text + $". Press Undo again ({secondsLeft}s remaining) to confirm."; } private void CountUndoStats(Player player, out int pieceCount, out int terrainChunkCount) { //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_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) pieceCount = 0; Vector3 position = ((Component)player).transform.position; 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") && !(Vector3.Distance(((Component)val).transform.position, position) > 75f)) { pieceCount++; } } } terrainChunkCount = TerrainSnapshot.GetSnapshotChunkCount(); } private void PerformUndo(Player player) { //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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0125: 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) Vector3 position = ((Component)player).transform.position; 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") && !(Vector3.Distance(((Component)val).transform.position, position) > 75f)) { ZNetScene.instance.Destroy(((Component)val).gameObject); num++; } } } _lastPlaced.Clear(); bool hasSnapshot = TerrainSnapshot.HasSnapshot; int snapshotChunkCount = TerrainSnapshot.GetSnapshotChunkCount(); TerrainSnapshot.Restore(); if (hasSnapshot && snapshotChunkCount > 0) { if (_undoRefreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_undoRefreshCoroutine); } _undoRefreshCoroutine = ((MonoBehaviour)this).StartCoroutine(PostUndoTerrainRefresh(position, snapshotChunkCount)); } if (!hasSnapshot) { ValheimFloorPlanPlugin.ShowWrappedMessage(ValheimFloorPlanPlugin.WarningMessageType, "ValheimFloorPlan: No terrain snapshot in this session. Undo removed pieces only."); } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[FloorPlanBuilder] Undo: removed {num} VFP pieces within {75f}m, restored {snapshotChunkCount} terrain chunks."); ((Character)player).Message((MessageType)2, $"ValheimFloorPlan: Undone ({num} pieces removed, {snapshotChunkCount} 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; } public void BuildFromFile(string path) { //IL_007e: 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)); return; } 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}"); ((MonoBehaviour)this).StartCoroutine(LevelThenPlace(floorPlan, 0f, GetBuildOrigin(localPlayer))); } 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; } ValheimFloorPlanPlugin.Log.LogInfo((object)$"Build origin: {origin} rotation={rotationDeg:F0}°"); _lastPlaced.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)); ShowBuildProgress("Final checks..."); yield return ((MonoBehaviour)this).StartCoroutine(PostBuildSpikeGuard(plan, origin, rotationDeg)); } 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 k = 0; k < array2.Length; k++) { RaycastHit val2 = array2[k]; 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 num7 = ((RaycastHit)(ref val2)).point.y - ((RaycastHit)(ref val)).point.y; if (!(num7 < 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={num7: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 = Mathf.Clamp(ValheimFloorPlanPlugin.ExternalWallHeight, 1, 4); bool useWoodStructure = ValheimFloorPlanPlugin.WallPillarMaterial == ValheimFloorPlanPlugin.StructuralMaterial.Wood; GetPlanPieceBounds(plan, out var minCol, out var maxColExclusive, out var minRow, out var maxRowExclusive); float cosR = Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); float sinR = 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; } int effectivePieceRotation = piece.Rotation; if (useWoodStructure && piece.Type == "Wall" && piece.WallFace == WallFaceMode.Inner) { effectivePieceRotation = (effectivePieceRotation + 180) % 360; } 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); float dx = ((float)piece.Col + (float)effW * 0.5f) * 1f; float dz = ((float)piece.Row + (float)effH * 0.5f) * 1f; float x = origin.x + dx * cosR + dz * sinR; float z = origin.z - dx * sinR + dz * cosR; 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); bool isExternal = IsOnPlanOuterPerimeter(piece.Col, piece.Row, effW, effH, minCol, maxColExclusive, minRow, maxRowExclusive); int stackCount = ((!(IsExternalWallOrPillarType(piece.Type) && isExternal)) ? 1 : configuredExternalWallHeight); float stackStepY = GetStackStepY(piece.Type); Quaternion rot = Quaternion.Euler(0f, (float)effectivePieceRotation + rotationDeg, 0f); Vector3 materialOffset = Vector3.zero; if (useWoodStructure && (piece.Type == "Wall" || piece.Type == "Pillar") && 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}"); } GameObject go = Object.Instantiate(prefab, stackedPos, rot); _lastPlaced.Add(go); ZNetView zNetView = go.GetComponent(); if ((Object)(object)zNetView != (Object)null) { ZDO zdo = zNetView.GetZDO(); if (zdo != null) { zdo.SetOwner(ZDOMan.GetSessionID()); zdo.Set("vfp_build", "1"); } } Piece pieceComp = go.GetComponent(); if (pieceComp != null) { pieceComp.SetCreator(player.GetPlayerID()); } placed++; if (placed % 10 == 0) { yield return (object)new WaitForSeconds(0.05f); } } processed++; if (totalPieces > 0) { int pct = Mathf.FloorToInt((float)processed * 100f / (float)totalPieces); if (pct >= nextProgressPct) { ShowBuildProgress($"Placing pieces... {processed}/{totalPieces}"); nextProgressPct += 10; } } hit = 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 static void ShowBuildProgress(string message) { ValheimFloorPlanPlugin.ShowProgressMessage(message); } 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; } if (type == "Wall") { return "wood_wall_half"; } if (type == "Pillar") { return "wood_pole_log"; } return defaultPrefab; } private static float GetStackStepY(string type) { if (type == "Wall") { return 1f; } if (type == "Pillar") { return 2f; } return 0f; } 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 Vector3 GetWoodPerimeterOffset(string pieceType, int col, int row, int effW, int effH, int minCol, int maxColExclusive, int minRow, int maxRowExclusive, float planRotationDeg) { //IL_00f0: 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_00f6: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) float num = ((pieceType == "Pillar") ? 0.15f : 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") { 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); return Quaternion.Euler(0f, planRotationDeg, 0f) * val; } 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; } } if (minCol == int.MaxValue) { minCol = 0; minRow = 0; maxColExclusive = plan.Cols; maxRowExclusive = plan.Rows; } } } public sealed class PieceDef { public readonly string Prefab; public readonly int BaseW; public readonly int BaseH; public readonly float YOffset; public PieceDef(string prefab, int baseW, int baseH, float yOffset = 0f) { Prefab = prefab; BaseW = baseW; BaseH = baseH; YOffset = yOffset; } 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) }, { "Wall", new PieceDef("stone_wall_2x1", 2, 1, 0.5f) }, { "Doorway", new PieceDef("wood_door", 2, 1, 1f) }, { "Pillar", new PieceDef("stone_pillar", 1, 1, 1f) }, { "Hearth", new PieceDef("hearth", 3, 2) } }; 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 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) GetBounds(plan, origin, 2, 0f, out var innerMinX, out var innerMaxX, out var innerMinZ, out var innerMaxZ); float cosR = Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); float sinR = 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)); float ldx = lx - origin.x; float ldz = lz - origin.z; float wx = origin.x + ldx * cosR + ldz * sinR; float wz = origin.z - ldx * sinR + ldz * cosR; 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 ix5 = 0; ix5 <= edgeStepsX; ix5++) { float lx2 = ((ix5 == edgeStepsX) ? innerMaxX : (innerMinX + (float)ix5 * edgeSampleStep)); for (int iz2 = 0; iz2 <= edgeStepsZ; iz2++) { float lz2 = ((iz2 == edgeStepsZ) ? innerMaxZ : (innerMinZ + (float)iz2 * 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 ix3 = 0; ix3 <= stepsX; ix3++) { float lx3 = ((ix3 == stepsX) ? innerMaxX : (innerMinX + (float)ix3 * levelSampleStep)); for (int iz3 = 0; iz3 <= stepsZ; iz3++) { float lz3 = ((iz3 == stepsZ) ? innerMaxZ : (innerMinZ + (float)iz3 * levelSampleStep)); float ldx2 = lx3 - origin.x; float ldz2 = lz3 - origin.z; float wx2 = origin.x + ldx2 * cosR + ldz2 * sinR; float wz2 = origin.z - ldx2 * sinR + ldz2 * cosR; 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 ix2 = 0; ix2 <= edgeStepsX; ix2++) { float lx4 = ((ix2 == edgeStepsX) ? innerMaxX : (innerMinX + (float)ix2 * edgeSampleStep)); for (int iz4 = 0; iz4 <= edgeStepsZ; iz4++) { float lz4 = ((iz4 == edgeStepsZ) ? innerMaxZ : (innerMinZ + (float)iz4 * edgeSampleStep)); if (!IsInEdgeBand(lx4, lz4, innerMinX, innerMaxX, innerMinZ, innerMaxZ, 0.6f)) { continue; } float ldx3 = lx4 - origin.x; float ldz3 = lz4 - origin.z; float wx3 = origin.x + ldx3 * cosR + ldz3 * sinR; float wz3 = origin.z - ldx3 * sinR + ldz3 * cosR; 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 pass2 = 1; pass2 <= spikePasses; pass2++) { for (int ix4 = 0; ix4 <= spikeStepsX; ix4++) { float lx5 = ((ix4 == spikeStepsX) ? sampleMaxX : (sampleMinX + (float)ix4 * spikeScanStep)); for (int iz5 = 0; iz5 <= spikeStepsZ; iz5++) { float lz5 = ((iz5 == spikeStepsZ) ? sampleMaxZ : (sampleMinZ + (float)iz5 * spikeScanStep)); float ldx4 = lx5 - origin.x; float ldz4 = lz5 - origin.z; float wx4 = origin.x + ldx4 * cosR + ldz4 * sinR; float wz4 = origin.z - ldx4 * sinR + ldz4 * cosR; 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 {pass2}/{spikePasses})"); } } } if (pass2 < 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 lowerTargetY2 = Mathf.Min(centerY, medianY + 0.03f); lowerTargetY2 = Mathf.Max(centerY - 0.45f, lowerTargetY2); 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 opCount2 = 0; Vector2[] array = passOffsets; for (int i = 0; i < array.Length; i++) { Vector2 o = array[i]; ApplySmooth(point.x + o.x, lowerTargetY2, point.z + o.y, 1.1f, modified); opCount2++; if (opCount2 % 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, lowerTargetY2, point.z + o2.y, 0.7f, modified); opCount2++; if (opCount2 % 10 == 0) { yield return null; } } yield return null; int bridgeOps = ApplyEdgeBridge(point, lowerTargetY2, slopeDir, ridgeY, troughY, modified); opCount2 += bridgeOps; if (opCount2 % 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, lowerTargetY2); float localMedianY = GetLocalMedianHeight(sx, sz, currentY, 0.6f); if (currentY <= localMedianY + 0.28f) { continue; } float cutY2 = Mathf.Min(currentY - 0.03f, localMedianY + 0.02f); cutY2 = Mathf.Max(lowerTargetY2, cutY2); if (CanLowerWithoutRaise(sx, sz, cutY2, 0.42f, currentY)) { ApplyLevel(sx, cutY2, sz, 0.42f, modified, smooth: true); cutOps++; opCount2++; if (opCount2 % 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, lowerTargetY2, point.z + o4.y, 1.05f, modified); opCount2++; if (opCount2 % 10 == 0) { yield return null; } } ValheimFloorPlanPlugin.Log.LogInfo((object)$"[TerrainLeveler] Tear repair at {point} -> centerY={centerY:F2}, medianY={medianY:F2}, lowerTargetY={lowerTargetY2:F2}, bridgeOps={bridgeOps}, cutOps={cutOps}, ops={opCount2}, 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0228: 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_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) GetBounds(plan, origin, 2, 0f, 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 cosR = Mathf.Cos(rotationDeg * ((float)Math.PI / 180f)); float sinR = 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 i = 0; i < hotspots.Count; i++) { if (hotspotPoints.Count >= 12) { break; } Vector3 position = hotspots[i].Position; bool flag = false; for (int j = 0; j < hotspotPoints.Count; j++) { Vector3 val = hotspotPoints[j]; float num5 = position.x - val.x; float num6 = position.z - val.z; if (num5 * num5 + num6 * num6 < 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_02dc: Unknown result type (might be due to invalid IL or missing references) float num7 = x1 - x0; float num8 = z1 - z0; float num9 = Mathf.Sqrt(num7 * num7 + num8 * num8); int num10 = Mathf.Max(1, Mathf.CeilToInt(num9 / 0.75f)); for (int k = 0; k <= num10; k++) { float num11 = (float)k / (float)num10; float num12 = Mathf.Lerp(x0, x1, num11); float num13 = Mathf.Lerp(z0, z1, num11); float num14 = num12 - origin.x; float num15 = num13 - origin.z; float num16 = origin.x + num14 * cosR + num15 * sinR; float num17 = origin.z - num14 * sinR + num15 * cosR; float num18 = num12 + inNx * 0.8f; float num19 = num13 + inNz * 0.8f; float num20 = num18 - origin.x; float num21 = num19 - origin.z; float x2 = origin.x + num20 * cosR + num21 * sinR; float z2 = origin.z - num20 * sinR + num21 * cosR; float num22 = num12 - inNx * 0.8f; float num23 = num13 - inNz * 0.8f; float num24 = num22 - origin.x; float num25 = num23 - origin.z; float x3 = origin.x + num24 * cosR + num25 * sinR; float z3 = origin.z - num24 * sinR + num25 * cosR; float num26 = SampleHeight(num16, num17, origin.y); float num27 = SampleHeight(x2, z2, num26); float num28 = SampleHeight(x3, z3, num26); if (num26 < edgeMinY) { edgeMinY = num26; } if (num26 > edgeMaxY) { edgeMaxY = num26; } float num29 = Mathf.Abs(num27 - num28); if (num29 > localMaxEdgeStep) { localMaxEdgeStep = num29; } float num30 = Mathf.Abs(num26 - (num27 + num28) * 0.5f); roughAccum += num30; roughCount++; if (hotspots != null) { float num31 = num29 + num30 * 1.35f; if (num31 >= 0.62f && (num29 >= 0.5f || num30 >= 0.24f)) { hotspots.Add(new EdgeRiskHotspot(new Vector3(num16, num26, num17), num31)); } } } } } 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_016d: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) 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; } } if (num == int.MaxValue) { num = 0; num3 = 0; num2 = plan.Cols; num4 = plan.Rows; } float num7 = (float)(num - pad) * 1f; float num8 = (float)(num2 + pad) * 1f; float num9 = (float)(num3 - pad) * 1f; float num10 = (float)(num4 + pad) * 1f; if (Mathf.Approximately(rotationDeg % 360f, 0f)) { minX = origin.x + num7; maxX = origin.x + num8; minZ = origin.z + num9; maxZ = origin.z + num10; return; } float num11 = rotationDeg * ((float)Math.PI / 180f); float num12 = Mathf.Cos(num11); float num13 = Mathf.Sin(num11); float[] array = new float[4] { num7, num8, num8, num7 }; float[] array2 = new float[4] { num9, num9, num10, num10 }; float num14 = float.MaxValue; float num15 = float.MinValue; float num16 = float.MaxValue; float num17 = float.MinValue; for (int i = 0; i < 4; i++) { float num18 = array[i] * num12 + array2[i] * num13; float num19 = (0f - array[i]) * num13 + array2[i] * num12; if (num18 < num14) { num14 = num18; } if (num18 > num15) { num15 = num18; } if (num19 < num16) { num16 = num19; } if (num19 > num17) { num17 = num19; } } minX = origin.x + num14; maxX = origin.x + num15; minZ = origin.z + num16; maxZ = origin.z + num17; } 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 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", "1.0.3")] public class ValheimFloorPlanPlugin : BaseUnityPlugin { internal enum StructuralMaterial { Stone, Wood } public const string PluginGUID = "com.alexdroz.valheimfloorplan"; public const string PluginName = "ValheimFloorPlan"; public const string PluginVersion = "1.0.3"; internal static ManualLogSource Log = null; private ConfigEntry _vfpFilePath = null; private ConfigEntry _buildHotkey = null; private ConfigEntry _undoHotkey = 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 _externalWallHeight = null; private ConfigEntry _wallPillarMaterial = null; private ConfigEntry _buildOriginForwardOffset = null; private ConfigEntry _previewMoveStep = null; private ConfigEntry _previewFineMoveStep = 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; 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 ExternalWallHeight { get; private set; } = 1; internal static StructuralMaterial WallPillarMaterial { get; private set; } = StructuralMaterial.Stone; internal static float BuildOriginForwardOffset { get; private set; } = 12f; internal static float PreviewMoveStep { get; private set; } = 2f; internal static float PreviewFineMoveStep { get; private set; } = 0.5f; 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)114; internal static KeyCode PreviewCancelKey { get; private set; } = (KeyCode)27; internal static KeyCode PreviewFineAdjustKey { get; private set; } = (KeyCode)304; private void Awake() { //IL_004c: 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_00d7: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Expected O, but got Unknown //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Expected O, but got Unknown //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Expected O, but got Unknown //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Expected O, but got Unknown //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Expected O, but got Unknown //IL_04f0: Unknown result type (might be due to invalid IL or missing references) //IL_04fa: Expected O, but got Unknown //IL_055c: Unknown result type (might be due to invalid IL or missing references) //IL_0566: Expected O, but got Unknown //IL_05d2: Unknown result type (might be due to invalid IL or missing references) //IL_05dc: Expected O, but got Unknown //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_0652: Expected O, but got Unknown //IL_06be: Unknown result type (might be due to invalid IL or missing references) //IL_06c8: Expected O, but got Unknown //IL_0734: Unknown result type (might be due to invalid IL or missing references) //IL_073e: Expected O, but got Unknown //IL_099a: Unknown result type (might be due to invalid IL or missing references) //IL_09ab: Unknown result type (might be due to invalid IL or missing references) //IL_09bc: Unknown result type (might be due to invalid IL or missing references) //IL_09cd: Unknown result type (might be due to invalid IL or missing references) //IL_09de: Unknown result type (might be due to invalid IL or missing references) //IL_09ef: Unknown result type (might be due to invalid IL or missing references) //IL_0a00: Unknown result type (might be due to invalid IL or missing references) //IL_0a11: Unknown result type (might be due to invalid IL or missing references) //IL_0a22: Unknown result type (might be due to invalid IL or missing references) //IL_0a57: Unknown result type (might be due to invalid IL or missing references) //IL_0a6a: Unknown result type (might be due to invalid IL or missing references) //IL_0a77: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; _vfpFilePath = ((BaseUnityPlugin)this).Config.Bind("General", "FloorPlanFile", "", "Full path to the .vfp floor plan file exported from Valheim Floor Plan Designer."); _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)."); _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); _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; _externalWallHeight = ((BaseUnityPlugin)this).Config.Bind("Building", "ExternalWallHeight", 1, new ConfigDescription("How many levels high external Wall/Pillar pieces should be stacked.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 4), Array.Empty())); _externalWallHeight.SettingChanged += delegate { ExternalWallHeight = Mathf.Clamp(_externalWallHeight.Value, 1, 4); }; ExternalWallHeight = Mathf.Clamp(_externalWallHeight.Value, 1, 4); _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); _buildOriginForwardOffset = ((BaseUnityPlugin)this).Config.Bind("General", "BuildOriginForwardOffset", 12f, new ConfigDescription("How far in front of the player (in meters) the plan origin is placed for preview/build.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 20f), Array.Empty())); _buildOriginForwardOffset.SettingChanged += delegate { BuildOriginForwardOffset = Mathf.Clamp(_buildOriginForwardOffset.Value, 10f, 20f); }; BuildOriginForwardOffset = Mathf.Clamp(_buildOriginForwardOffset.Value, 10f, 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); _previewRotateStepDeg = ((BaseUnityPlugin)this).Config.Bind("Preview", "RotateStepDegrees", 15f, new ConfigDescription("How far the preview rotates per rotate key press, in degrees.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 90f), Array.Empty())); _previewRotateStepDeg.SettingChanged += delegate { PreviewRotateStepDeg = Mathf.Clamp(_previewRotateStepDeg.Value, 1f, 90f); }; PreviewRotateStepDeg = Mathf.Clamp(_previewRotateStepDeg.Value, 1f, 90f); _previewFineRotateStepDeg = ((BaseUnityPlugin)this).Config.Bind("Preview", "FineRotateStepDegrees", 5f, new ConfigDescription("How far the preview rotates per key press while the fine-adjust key is held, in degrees.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 45f), Array.Empty())); _previewFineRotateStepDeg.SettingChanged += delegate { PreviewFineRotateStepDeg = Mathf.Clamp(_previewFineRotateStepDeg.Value, 1f, 45f); }; PreviewFineRotateStepDeg = Mathf.Clamp(_previewFineRotateStepDeg.Value, 1f, 45f); _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)114, "Preview rotation key for rotating clockwise."); _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; ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)("ValheimFloorPlan v1.0.3 loaded! " + $"Build: {_buildHotkey.Value} Undo: {_undoHotkey.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 height: {ExternalWallHeight} Wall/Pillar material: {WallPillarMaterial} Origin offset: {BuildOriginForwardOffset:F1}m Preview move: {PreviewMoveStep:F2}/{PreviewFineMoveStep:F2}m Preview rotate: {PreviewRotateStepDeg:F0}/{PreviewFineRotateStepDeg:F0}°")); } private void Update() { //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_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) KeyboardShortcut value = _buildHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { string text = _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 = _undoHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { FloorPlanBuilder.Instance.Undo(); } } 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 static StructuralMaterial ParseStructuralMaterial(string value) { if (string.Equals(value?.Trim(), "Wood", StringComparison.OrdinalIgnoreCase)) { return StructuralMaterial.Wood; } return StructuralMaterial.Stone; } } }