Decompiled source of PieceReplacer v1.1.12

plugins/PieceReplacer/PieceReplacer.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Ainzworth.PieceReplacer.Config;
using Ainzworth.PieceReplacer.Replacement;
using Ainzworth.PieceReplacer.UI;
using Ainzworth.PieceReplacer.Utils;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("PieceReplacer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.12.0")]
[assembly: AssemblyInformationalVersion("1.1.12+21fa852600457b3ea92595699bb3b5d930b81f67")]
[assembly: AssemblyProduct("PieceReplacer")]
[assembly: AssemblyTitle("PieceReplacer")]
[assembly: AssemblyVersion("1.1.12.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ainzworth.PieceReplacer
{
	[BepInPlugin("uk.ainzworth.valheim.piecereplacer", "PieceReplacer", "1.1.12")]
	public sealed class PieceReplacerPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "uk.ainzworth.valheim.piecereplacer";

		public const string PluginName = "PieceReplacer";

		public const string PluginVersion = "1.1.12";

		private Harmony? _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			ReplacementRegistry.Reload(ModConfig.ReplacementGroups.Value);
			ModConfig.ReplacementGroups.SettingChanged += delegate
			{
				ReplacementRegistry.Reload(ModConfig.ReplacementGroups.Value);
			};
			ModConfig.AutoCompatibility.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			ModConfig.RequireSameCategory.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			ModConfig.RequireSameUsageTags.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			ModConfig.SnapPointTolerance.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			ModConfig.AllowBoundsFallback.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			ModConfig.BoundsTolerance.SettingChanged += delegate
			{
				PieceCompatibility.ClearCache();
			};
			_harmony = new Harmony("uk.ainzworth.valheim.piecereplacer");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PieceReplacer 1.1.12 loaded.");
		}

		private void Update()
		{
			BlockerHighlight.Tick();
		}

		private void OnGUI()
		{
			BatchReplacementHud.Draw();
		}

		private void OnDestroy()
		{
			BlockerHighlight.Clear();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			ReplacementRuntime.Clear();
			BatchReplacementRuntime.Clear();
		}
	}
}
namespace Ainzworth.PieceReplacer.Utils
{
	internal static class PieceTargeting
	{
		private static readonly int PieceMask = LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" });

		private static readonly FieldInfo? MaxPlaceDistanceField = AccessTools.Field(typeof(Player), "m_maxPlaceDistance");

		internal static Piece? FindAimedPiece(Player player, Func<Piece, bool>? predicate = null)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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_0061: 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_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_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			Piece hoveringPiece = PlayerPrivateAccess.GetHoveringPiece(player);
			if (Object.op_Implicit((Object)(object)hoveringPiece) && !PieceUtils.IsPlacementGhost(player, hoveringPiece) && (predicate == null || predicate(hoveringPiece)))
			{
				return hoveringPiece;
			}
			Camera main = Camera.main;
			Vector3 position;
			Vector3 forward;
			if (Object.op_Implicit((Object)(object)main))
			{
				position = ((Component)main).transform.position;
				forward = ((Component)main).transform.forward;
			}
			else
			{
				if (!Object.op_Implicit((Object)(object)((Character)player).m_eye))
				{
					return null;
				}
				position = ((Character)player).m_eye.position;
				forward = ((Character)player).m_eye.forward;
			}
			float num = 5f;
			try
			{
				if (MaxPlaceDistanceField?.GetValue(player) is float num2 && num2 > 0f)
				{
					num = num2;
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Could not read m_maxPlaceDistance: " + ex.Message));
				}
			}
			float num3 = Mathf.Max(2f, num + 1f);
			RaycastHit[] source = Physics.RaycastAll(position, forward, num3, PieceMask, (QueryTriggerInteraction)1);
			float? num4 = null;
			HashSet<int> hashSet = new HashSet<int>();
			foreach (RaycastHit item in source.OrderBy((RaycastHit h) => ((RaycastHit)(ref h)).distance))
			{
				RaycastHit current = item;
				if (!Object.op_Implicit((Object)(object)((RaycastHit)(ref current)).collider))
				{
					continue;
				}
				Piece componentInParent = ((Component)((RaycastHit)(ref current)).collider).GetComponentInParent<Piece>();
				if (Object.op_Implicit((Object)(object)componentInParent) && !PieceUtils.IsPlacementGhost(player, componentInParent) && hashSet.Add(((Object)componentInParent).GetInstanceID()))
				{
					float valueOrDefault = num4.GetValueOrDefault();
					if (!num4.HasValue)
					{
						valueOrDefault = ((RaycastHit)(ref current)).distance;
						num4 = valueOrDefault;
					}
					if (((RaycastHit)(ref current)).distance - num4.Value > 0.3f)
					{
						break;
					}
					if (predicate == null || predicate(componentInParent))
					{
						return componentInParent;
					}
				}
			}
			return null;
		}
	}
	internal static class PieceUtils
	{
		internal static string PrefabName(Piece piece)
		{
			return Utils.GetPrefabName(((Component)piece).gameObject);
		}

		internal static bool IsPlacementGhost(Player player, Piece piece)
		{
			GameObject placementGhost = PlayerPrivateAccess.GetPlacementGhost(player);
			if (!Object.op_Implicit((Object)(object)placementGhost))
			{
				return false;
			}
			Transform transform = ((Component)piece).transform;
			Transform transform2 = placementGhost.transform;
			if (!((Object)(object)transform == (Object)(object)transform2))
			{
				return transform.IsChildOf(transform2);
			}
			return true;
		}
	}
	internal static class PlayerPrivateAccess
	{
		private static readonly FieldInfo? PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly FieldInfo? HoveringField = AccessTools.Field(typeof(Player), "m_hovering");

		private static readonly MethodInfo? GetHoveringPieceMethod = AccessTools.Method(typeof(Player), "GetHoveringPiece", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo? GetHoverObjectMethod = AccessTools.Method(typeof(Player), "GetHoverObject", Type.EmptyTypes, (Type[])null);

		internal static GameObject? GetPlacementGhost(Player player)
		{
			try
			{
				object? obj = PlacementGhostField?.GetValue(player);
				return (GameObject?)((obj is GameObject) ? obj : null);
			}
			catch (Exception ex)
			{
				LogReflectionFailure("m_placementGhost", ex);
				return null;
			}
		}

		internal static Piece? GetHoveringPiece(Player player)
		{
			try
			{
				if (GetHoveringPieceMethod != null)
				{
					object? obj = GetHoveringPieceMethod.Invoke(player, null);
					Piece val = (Piece)((obj is Piece) ? obj : null);
					if (val != null && Object.op_Implicit((Object)(object)val))
					{
						return val;
					}
				}
			}
			catch (Exception ex)
			{
				LogReflectionFailure("GetHoveringPiece", ex);
			}
			GameObject hoverObject = GetHoverObject(player);
			if (!Object.op_Implicit((Object)(object)hoverObject))
			{
				return null;
			}
			return hoverObject.GetComponentInParent<Piece>();
		}

		internal static GameObject? GetHoverObject(Player player)
		{
			try
			{
				if (GetHoverObjectMethod != null)
				{
					object obj = GetHoverObjectMethod.Invoke(player, null);
					GameObject val = (GameObject)((obj is GameObject) ? obj : null);
					if (val != null)
					{
						return val;
					}
					Component val2 = (Component)((obj is Component) ? obj : null);
					if (val2 != null)
					{
						return val2.gameObject;
					}
				}
				object? obj2 = HoveringField?.GetValue(player);
				return (GameObject?)((obj2 is GameObject) ? obj2 : null);
			}
			catch (Exception ex)
			{
				LogReflectionFailure("GetHoverObject/m_hovering", ex);
				return null;
			}
		}

		private static void LogReflectionFailure(string member, Exception ex)
		{
			if (ModConfig.DebugLogging.Value)
			{
				PieceReplacerPlugin.Log.LogWarning((object)("Could not access Player." + member + ": " + ex.Message));
			}
		}
	}
	internal static class VanillaAccess
	{
		private static readonly MethodInfo? CheckCanRemovePieceMethod = AccessTools.Method(typeof(Player), "CheckCanRemovePiece", new Type[1] { typeof(Piece) }, (Type[])null);

		internal static bool CanRemoveForReplacement(Player player, Piece piece, out string reason)
		{
			//IL_0026: 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)
			reason = string.Empty;
			if (!Object.op_Implicit((Object)(object)piece) || !piece.m_canBeRemoved)
			{
				reason = "Dieses Bauteil kann nicht entfernt werden.";
				return false;
			}
			if (Location.IsInsideNoBuildLocation(((Component)piece).transform.position))
			{
				reason = "$msg_nobuildzone";
				return false;
			}
			if (!PrivateArea.CheckAccess(((Component)piece).transform.position, 0f, true, false))
			{
				reason = "$msg_privatezone";
				return false;
			}
			if (ModConfig.RequireRemovalPermission.Value && CheckCanRemovePieceMethod != null)
			{
				try
				{
					object obj = CheckCanRemovePieceMethod.Invoke(player, new object[1] { piece });
					if (obj is bool && !(bool)obj)
					{
						reason = "Das Bauteil kann unter den aktuellen Vanilla-Regeln nicht entfernt werden.";
						return false;
					}
				}
				catch (Exception arg)
				{
					PieceReplacerPlugin.Log.LogError((object)$"CheckCanRemovePiece failed: {arg}");
					reason = "Entfernungsprüfung fehlgeschlagen.";
					return false;
				}
			}
			if (!Object.op_Implicit((Object)(object)((Component)piece).GetComponent<WearNTear>()))
			{
				reason = "Das Ziel besitzt kein WearNTear und wird von V0.1 nicht unterstützt.";
				return false;
			}
			return true;
		}

		internal static void ShowCenterMessage(Player player, string message)
		{
			try
			{
				((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Could not show message '" + message + "': " + ex.Message));
				}
			}
		}
	}
}
namespace Ainzworth.PieceReplacer.UI
{
	internal static class BatchReplacementHud
	{
		private static GUIStyle? _boxStyle;

		private static GUIStyle? _labelStyle;

		internal static void Draw()
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.Enabled.Value && ModConfig.ShowBatchHud.Value && ModConfig.IsBatchModeHeld())
			{
				BatchReplacementRuntime.State current = BatchReplacementRuntime.Current;
				if (current != null)
				{
					EnsureStyles();
					string text = BuildText(current);
					int num = 3 + current.Cost.Lines.Count + ((!string.IsNullOrEmpty(current.Error)) ? 1 : 0);
					float num2 = Mathf.Min(520f, (float)Screen.width - 30f);
					float num3 = Mathf.Clamp(54f + (float)num * 21f, 105f, 220f);
					float num4 = ((float)Screen.width - num2) * 0.5f;
					float num5 = (float)Screen.height - num3 - 135f;
					Rect val = default(Rect);
					((Rect)(ref val))..ctor(num4, Mathf.Max(10f, num5), num2, num3);
					GUI.Box(val, GUIContent.none, _boxStyle);
					GUI.Label(new Rect(((Rect)(ref val)).x + 14f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 28f, ((Rect)(ref val)).height - 20f), text, _labelStyle);
				}
			}
		}

		private static string BuildText(BatchReplacementRuntime.State state)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<b>AREA REPLACE</b>  ");
			stringBuilder.Append(state.Pieces.Count);
			stringBuilder.Append((state.Pieces.Count == 1) ? " piece" : " pieces");
			stringBuilder.AppendLine();
			stringBuilder.Append(state.SourceDisplayName);
			stringBuilder.Append("  →  ");
			stringBuilder.AppendLine(state.SelectedDisplayName);
			if (!string.IsNullOrEmpty(state.Error))
			{
				stringBuilder.Append("<color=#ff7070>");
				stringBuilder.Append(Localize(state.Error));
				stringBuilder.AppendLine("</color>");
			}
			if (state.Cost.FreeBuild)
			{
				stringBuilder.AppendLine("<color=#8dff9b>Free build — no materials required</color>");
			}
			else
			{
				foreach (BatchMaterialCost.Line line in state.Cost.Lines)
				{
					string value = (line.Enough ? "#d9e4d1" : "#ff7070");
					stringBuilder.Append("<color=");
					stringBuilder.Append(value);
					stringBuilder.Append('>');
					stringBuilder.Append(line.DisplayName);
					stringBuilder.Append(": ");
					stringBuilder.Append(line.ExternalSupply ? (">=" + line.Required) : line.Available.ToString());
					stringBuilder.Append(" / ");
					stringBuilder.Append(line.Required);
					if (line.ExternalSupply)
					{
						stringBuilder.Append(" (including containers)");
					}
					if (!line.Enough)
					{
						stringBuilder.Append("  (missing ");
						stringBuilder.Append(line.Required - line.Available);
						stringBuilder.Append(')');
					}
					stringBuilder.AppendLine("</color>");
				}
			}
			stringBuilder.Append(state.CanExecute ? "<color=#8dff9b>Left click to replace the complete surface</color>" : "<color=#ff7070>Replacement blocked</color>");
			return stringBuilder.ToString();
		}

		private static string Localize(string value)
		{
			try
			{
				return (Localization.instance != null) ? Localization.instance.Localize(value) : value;
			}
			catch
			{
				return value;
			}
		}

		private static void EnsureStyles()
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0036: Expected O, but got Unknown
			//IL_0040: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (_boxStyle == null || _labelStyle == null)
			{
				_boxStyle = new GUIStyle(GUI.skin.box)
				{
					padding = new RectOffset(10, 10, 10, 10)
				};
				_labelStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)0,
					fontSize = 16,
					richText = true,
					wordWrap = true
				};
				_labelStyle.normal.textColor = Color.white;
			}
		}
	}
}
namespace Ainzworth.PieceReplacer.Replacement
{
	internal sealed class BatchMaterialCost
	{
		internal sealed class Line
		{
			internal string ItemToken = string.Empty;

			internal string DisplayName = string.Empty;

			internal int Required;

			internal int Available;

			internal bool ExternalSupply;

			internal bool Enough
			{
				get
				{
					if (Available < Required)
					{
						return ExternalSupply;
					}
					return true;
				}
			}
		}

		internal bool FreeBuild { get; private set; }

		internal List<Line> Lines { get; } = new List<Line>();

		internal bool CanAfford
		{
			get
			{
				if (!FreeBuild)
				{
					return Lines.TrueForAll((Line line) => line.Enough);
				}
				return true;
			}
		}

		internal static BatchMaterialCost Calculate(Player player, Piece selectedPiece, int pieceCount)
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Expected O, but got Unknown
			BatchMaterialCost batchMaterialCost = new BatchMaterialCost
			{
				FreeBuild = IsFreeBuild(player, selectedPiece)
			};
			if (batchMaterialCost.FreeBuild || !Object.op_Implicit((Object)(object)selectedPiece) || selectedPiece.m_resources == null)
			{
				return batchMaterialCost;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			Dictionary<string, ItemDrop> dictionary2 = new Dictionary<string, ItemDrop>(StringComparer.Ordinal);
			Requirement[] resources = selectedPiece.m_resources;
			checked
			{
				foreach (Requirement val in resources)
				{
					if (val != null && Object.op_Implicit((Object)(object)val.m_resItem))
					{
						string name = val.m_resItem.m_itemData.m_shared.m_name;
						int amount = val.GetAmount(0);
						if (amount > 0)
						{
							int num = amount * Mathf.Max(1, pieceCount);
							dictionary[name] = (dictionary.TryGetValue(name, out var value) ? (value + num) : num);
							dictionary2[name] = val.m_resItem;
						}
					}
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					string displayName = item.Key;
					try
					{
						if (Localization.instance != null)
						{
							displayName = Localization.instance.Localize(item.Key);
						}
					}
					catch
					{
					}
					int num2 = inventory.CountItems(item.Key, -1, true);
					bool externalSupply = false;
					if (num2 < item.Value)
					{
						Requirement[] resources2 = selectedPiece.m_resources;
						try
						{
							selectedPiece.m_resources = (Requirement[])(object)new Requirement[1]
							{
								new Requirement
								{
									m_resItem = dictionary2[item.Key],
									m_amount = item.Value
								}
							};
							externalSupply = player.HaveRequirements(selectedPiece, (RequirementMode)0);
						}
						finally
						{
							selectedPiece.m_resources = resources2;
						}
					}
					batchMaterialCost.Lines.Add(new Line
					{
						ItemToken = item.Key,
						DisplayName = displayName,
						Required = item.Value,
						Available = num2,
						ExternalSupply = externalSupply
					});
				}
				return batchMaterialCost;
			}
		}

		internal string MissingSummary()
		{
			List<string> list = new List<string>();
			foreach (Line line in Lines)
			{
				if (!line.Enough)
				{
					list.Add($"{line.DisplayName} {line.Required - line.Available}");
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return string.Empty;
		}

		private static bool IsFreeBuild(Player player, Piece piece)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (player.PlacementCostDisabled)
			{
				return true;
			}
			try
			{
				return Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey());
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class BatchReplacementRuntime
	{
		internal sealed class State
		{
			internal Player Player;

			internal Piece SelectedPiece;

			internal Piece SeedPiece;

			internal string SelectedPrefab = string.Empty;

			internal string SourcePrefab = string.Empty;

			internal string GroupName = string.Empty;

			internal string SourceDisplayName = string.Empty;

			internal string SelectedDisplayName = string.Empty;

			internal List<Piece> Pieces = new List<Piece>();

			internal BatchMaterialCost Cost = new BatchMaterialCost();

			internal string Error = string.Empty;

			internal bool LimitExceeded;

			internal List<Piece> Blockers = new List<Piece>();

			internal int ScannedFrame;

			internal bool CanExecute
			{
				get
				{
					if (Pieces.Count > 0 && !LimitExceeded && string.IsNullOrEmpty(Error))
					{
						return Cost.CanAfford;
					}
					return false;
				}
			}
		}

		private const int RescanIntervalFrames = 10;

		private static State? _current;

		internal static State? Current => _current;

		internal static void Refresh(Player player)
		{
			if (!ModConfig.IsBatchModeHeld() || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				Clear();
				return;
			}
			Piece selected = player.GetSelectedPiece();
			if (!Object.op_Implicit((Object)(object)selected) || selected.m_repairPiece)
			{
				Clear();
				return;
			}
			string groupName2;
			Piece val = PieceTargeting.FindAimedPiece(player, (Piece candidate) => ReplacementRegistry.CanReplace(candidate, selected, out groupName2));
			if (!Object.op_Implicit((Object)(object)val))
			{
				Clear();
				return;
			}
			string text = PieceUtils.PrefabName(selected);
			string text2 = PieceUtils.PrefabName(val);
			if (!ReplacementRegistry.CanReplace(val, selected, out string groupName))
			{
				Clear();
				return;
			}
			if (_current != null && (Object)(object)_current.Player == (Object)(object)player && (Object)(object)_current.SeedPiece == (Object)(object)val && (Object)(object)_current.SelectedPiece == (Object)(object)selected && _current.SelectedPrefab == text && Time.frameCount - _current.ScannedFrame < 10)
			{
				_current.Cost = BatchMaterialCost.Calculate(player, selected, _current.Pieces.Count);
				return;
			}
			int value = ModConfig.MaxBatchPieces.Value;
			bool limitExceeded;
			string error;
			List<Piece> list = ConnectedSurfaceFinder.Find(val, value, out limitExceeded, out error);
			State state = new State
			{
				Player = player,
				SelectedPiece = selected,
				SeedPiece = val,
				SelectedPrefab = text,
				SourcePrefab = text2,
				GroupName = (groupName ?? string.Empty),
				SourceDisplayName = LocalizePieceName(val),
				SelectedDisplayName = LocalizePieceName(selected),
				Pieces = list,
				LimitExceeded = limitExceeded,
				Error = error,
				ScannedFrame = Time.frameCount
			};
			if (list.Count > 0 && string.IsNullOrEmpty(state.Error))
			{
				ValidateBatch(player, selected, list, state);
			}
			state.Cost = BatchMaterialCost.Calculate(player, selected, list.Count);
			_current = state;
			if (ModConfig.DebugLogging.Value)
			{
				PieceReplacerPlugin.Log.LogDebug((object)($"Batch candidate: {text2} -> {text}, pieces={list.Count}, " + $"affordable={state.Cost.CanAfford}, error='{state.Error}'."));
			}
		}

		internal static bool IsCandidateFor(Player player)
		{
			if (_current != null && (Object)(object)_current.Player == (Object)(object)player && Object.op_Implicit((Object)(object)_current.SeedPiece))
			{
				return Object.op_Implicit((Object)(object)_current.SelectedPiece);
			}
			return false;
		}

		internal static bool MatchesPlacement(Player player, Piece placementPiece)
		{
			if (IsCandidateFor(player) && _current != null && Object.op_Implicit((Object)(object)placementPiece))
			{
				return string.Equals(PieceUtils.PrefabName(placementPiece), _current.SelectedPrefab, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		internal static bool Execute(Player player, Piece placementPiece)
		{
			//IL_00c6: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			_current = null;
			Refresh(player);
			State current = _current;
			if (current == null || !MatchesPlacement(player, placementPiece))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(current.Error))
			{
				BlockerHighlight.Show(current.Blockers);
				VanillaAccess.ShowCenterMessage(player, current.Error);
				return false;
			}
			current.Cost = BatchMaterialCost.Calculate(player, placementPiece, current.Pieces.Count);
			if (!current.Cost.CanAfford)
			{
				string text = current.Cost.MissingSummary();
				VanillaAccess.ShowCenterMessage(player, string.IsNullOrEmpty(text) ? "Not enough materials." : ("Missing: " + text));
				return false;
			}
			GameObject placementGhost = PlayerPrivateAccess.GetPlacementGhost(player);
			if (!Object.op_Implicit((Object)(object)placementGhost) || !placementGhost.activeInHierarchy)
			{
				VanillaAccess.ShowCenterMessage(player, "Placement preview is not available.");
				return false;
			}
			Quaternion val = placementGhost.transform.rotation * Quaternion.Inverse(((Component)current.SeedPiece).transform.rotation);
			List<Piece> list = new List<Piece>(current.Pieces);
			try
			{
				if (!current.Cost.FreeBuild)
				{
					player.ConsumeResources(placementPiece.m_resources, 0, -1, list.Count);
				}
				foreach (Piece item in list)
				{
					if (!Object.op_Implicit((Object)(object)item))
					{
						throw new InvalidOperationException("A surface piece disappeared before the batch could be placed.");
					}
					Quaternion val2 = val * ((Component)item).transform.rotation;
					Vector3 correction;
					Vector3 alignedPosition = ReplacementAlignment.GetAlignedPosition(item, placementPiece, val2, out correction);
					if (ModConfig.DebugLogging.Value && ((Vector3)(ref correction)).sqrMagnitude > 1E-06f)
					{
						PieceReplacerPlugin.Log.LogDebug((object)("Batch pivot alignment " + PieceUtils.PrefabName(item) + " -> " + current.SelectedPrefab + ": " + $"correction={correction:F4}, magnitude={((Vector3)(ref correction)).magnitude:F4}m."));
					}
					player.PlacePiece(placementPiece, alignedPosition, val2, false, false);
				}
				bool freeBuild = current.Cost.FreeBuild;
				foreach (Piece item2 in list)
				{
					if (Object.op_Implicit((Object)(object)item2))
					{
						WearNTear component = ((Component)item2).GetComponent<WearNTear>();
						if (!Object.op_Implicit((Object)(object)component))
						{
							PieceReplacerPlugin.Log.LogWarning((object)("Batch replacement left old piece '" + PieceUtils.PrefabName(item2) + "' because WearNTear was missing."));
						}
						else
						{
							component.Remove(freeBuild);
						}
					}
				}
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogInfo((object)$"Batch replaced {list.Count} '{current.SourcePrefab}' with '{current.SelectedPrefab}'.");
				}
				Clear();
				ReplacementRuntime.Clear();
				return true;
			}
			catch (Exception arg)
			{
				PieceReplacerPlugin.Log.LogError((object)$"Batch replacement failed: {arg}");
				VanillaAccess.ShowCenterMessage(player, "Batch replacement failed. See BepInEx log for details.");
				Clear();
				return false;
			}
		}

		internal static void Clear()
		{
			_current = null;
		}

		private static void ValidateBatch(Player player, Piece selected, List<Piece> pieces, State state)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			foreach (Piece piece in pieces)
			{
				if (!Object.op_Implicit((Object)(object)piece))
				{
					state.Error = "A connected piece is no longer available.";
					break;
				}
				if (!VanillaAccess.CanRemoveForReplacement(player, piece, out string reason))
				{
					state.Blockers.Add(piece);
					if (string.IsNullOrEmpty(state.Error))
					{
						state.Error = reason;
					}
				}
				else if (Object.op_Implicit((Object)(object)selected.m_craftingStation) && !Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(selected.m_craftingStation.m_name, ((Component)piece).transform.position)))
				{
					state.Blockers.Add(piece);
					if (string.IsNullOrEmpty(state.Error))
					{
						state.Error = "$msg_missingstation";
					}
				}
			}
		}

		private static string LocalizePieceName(Piece piece)
		{
			string text = piece.m_name;
			try
			{
				if (Localization.instance != null)
				{
					text = Localization.instance.Localize(text);
				}
			}
			catch
			{
			}
			return text;
		}
	}
	internal static class BlockerHighlight
	{
		private static readonly List<GameObject> Markers = new List<GameObject>();

		private static Material? _material;

		private static float _until;

		internal static void Show(IEnumerable<Piece> pieces)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: 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_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: 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_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: 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_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			Clear();
			Shader val = Shader.Find("Sprites/Default");
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = Shader.Find("Unlit/Color");
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			_material = new Material(val);
			_material.color = Color.red;
			Bounds bounds = default(Bounds);
			foreach (Piece piece in pieces)
			{
				if (!Object.op_Implicit((Object)(object)piece))
				{
					continue;
				}
				((Bounds)(ref bounds))..ctor(((Component)piece).transform.position, Vector3.one * 0.25f);
				bool flag = true;
				Collider[] componentsInChildren = ((Component)piece).GetComponentsInChildren<Collider>();
				foreach (Collider val2 in componentsInChildren)
				{
					if (Object.op_Implicit((Object)(object)val2) && val2.enabled && !val2.isTrigger)
					{
						if (flag)
						{
							bounds = val2.bounds;
							flag = false;
						}
						else
						{
							((Bounds)(ref bounds)).Encapsulate(val2.bounds);
						}
					}
				}
				((Bounds)(ref bounds)).Expand(0.08f);
				Vector3 min = ((Bounds)(ref bounds)).min;
				Vector3 max = ((Bounds)(ref bounds)).max;
				Vector3[] array = (Vector3[])(object)new Vector3[8]
				{
					new Vector3(min.x, min.y, min.z),
					new Vector3(max.x, min.y, min.z),
					new Vector3(max.x, min.y, max.z),
					new Vector3(min.x, min.y, max.z),
					new Vector3(min.x, max.y, min.z),
					new Vector3(max.x, max.y, min.z),
					new Vector3(max.x, max.y, max.z),
					new Vector3(min.x, max.y, max.z)
				};
				int[] array2 = new int[16]
				{
					0, 1, 2, 3, 0, 4, 5, 1, 5, 6,
					2, 6, 7, 3, 7, 4
				};
				GameObject val3 = new GameObject("PieceReplacer blocked piece");
				Markers.Add(val3);
				LineRenderer val4 = val3.AddComponent<LineRenderer>();
				((Renderer)val4).sharedMaterial = _material;
				val4.useWorldSpace = true;
				float startWidth = (val4.endWidth = 0.045f);
				val4.startWidth = startWidth;
				Color startColor = (val4.endColor = Color.red);
				val4.startColor = startColor;
				val4.positionCount = array2.Length;
				for (int j = 0; j < array2.Length; j++)
				{
					val4.SetPosition(j, array[array2[j]]);
				}
			}
			_until = Time.unscaledTime + 4f;
		}

		internal static void Tick()
		{
			if (Object.op_Implicit((Object)(object)_material) && (Time.unscaledTime >= _until || !Object.op_Implicit((Object)(object)Player.m_localPlayer)))
			{
				Clear();
			}
		}

		internal static void Clear()
		{
			foreach (GameObject marker in Markers)
			{
				if (Object.op_Implicit((Object)(object)marker))
				{
					Object.Destroy((Object)(object)marker);
				}
			}
			Markers.Clear();
			if (Object.op_Implicit((Object)(object)_material))
			{
				Object.Destroy((Object)(object)_material);
			}
			_material = null;
		}
	}
	internal static class ConnectedSurfaceFinder
	{
		private static readonly int PieceMask = LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" });

		internal static List<Piece> Find(Piece seed, int maxPieces, out bool limitExceeded, out string error)
		{
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: 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)
			List<Piece> list = new List<Piece>();
			limitExceeded = false;
			error = string.Empty;
			if (!Object.op_Implicit((Object)(object)seed))
			{
				error = "No replacement target selected.";
				return list;
			}
			Vector3 worldNormal;
			bool flag = PieceCompatibility.TryGetSurfaceNormal(seed, out worldNormal);
			string b = PieceUtils.PrefabName(seed);
			int num = ((maxPieces <= 0) ? int.MaxValue : maxPieces);
			float num2 = Mathf.Max(0.01f, ModConfig.BatchPlaneTolerance.Value);
			float tolerance = Mathf.Max(0.005f, ModConfig.BatchConnectionTolerance.Value);
			Queue<Piece> queue = new Queue<Piece>();
			HashSet<int> hashSet = new HashSet<int>();
			queue.Enqueue(seed);
			hashSet.Add(((Object)seed).GetInstanceID());
			while (queue.Count > 0)
			{
				Piece val = queue.Dequeue();
				list.Add(val);
				foreach (Piece item in FindNearbyPieces(val))
				{
					if (!Object.op_Implicit((Object)(object)item) || (Object)(object)item == (Object)(object)val)
					{
						continue;
					}
					int instanceID = ((Object)item).GetInstanceID();
					if (hashSet.Contains(instanceID) || !string.Equals(PieceUtils.PrefabName(item), b, StringComparison.OrdinalIgnoreCase) || (flag && !PieceCompatibility.TryGetSurfaceNormal(item, out var _)))
					{
						continue;
					}
					PieceCompatibility.TryGetSurfaceNormal(item, out var worldNormal3);
					if (flag && Mathf.Abs(Vector3.Dot(worldNormal, worldNormal3)) < 0.995f)
					{
						continue;
					}
					float num3 = Mathf.Abs(Vector3.Dot(((Component)item).transform.position - ((Component)seed).transform.position, worldNormal));
					if ((!flag || !(num3 > num2)) && AreDirectlyConnected(val, item, tolerance, flag))
					{
						hashSet.Add(instanceID);
						if (list.Count + queue.Count >= num)
						{
							limitExceeded = true;
							error = $"Connected surface exceeds the configured limit of {num} pieces.";
							return list;
						}
						queue.Enqueue(item);
					}
				}
			}
			return list;
		}

		private static IEnumerable<Piece> FindNearbyPieces(Piece piece)
		{
			if (!TryGetWorldBounds(piece, out var bounds))
			{
				((Bounds)(ref bounds))..ctor(((Component)piece).transform.position, Vector3.one * 2f);
			}
			float num = Mathf.Max(((Bounds)(ref bounds)).size.x, Mathf.Max(((Bounds)(ref bounds)).size.y, ((Bounds)(ref bounds)).size.z)) * 1.4f + 0.3f;
			Collider[] array = Physics.OverlapSphere(((Bounds)(ref bounds)).center, Mathf.Max(num, 1.5f), PieceMask, (QueryTriggerInteraction)2);
			HashSet<int> seen = new HashSet<int>();
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Piece componentInParent = ((Component)val).GetComponentInParent<Piece>();
				if (Object.op_Implicit((Object)(object)componentInParent))
				{
					int instanceID = ((Object)componentInParent).GetInstanceID();
					if (seen.Add(instanceID))
					{
						yield return componentInParent;
					}
				}
			}
		}

		private static bool AreDirectlyConnected(Piece a, Piece b, float tolerance, bool planar)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] worldSnapPoints = PieceCompatibility.GetWorldSnapPoints(a);
			Vector3[] worldSnapPoints2 = PieceCompatibility.GetWorldSnapPoints(b);
			if (worldSnapPoints.Length != 0 && worldSnapPoints2.Length != 0)
			{
				Vector3[] array;
				if (!ModConfig.IncludeDiagonalConnections.Value)
				{
					Bounds a2 = default(Bounds);
					((Bounds)(ref a2))..ctor(((Component)a).transform.InverseTransformPoint(worldSnapPoints[0]), Vector3.zero);
					Bounds b2 = default(Bounds);
					((Bounds)(ref b2))..ctor(((Component)a).transform.InverseTransformPoint(worldSnapPoints2[0]), Vector3.zero);
					array = worldSnapPoints;
					foreach (Vector3 val in array)
					{
						((Bounds)(ref a2)).Encapsulate(((Component)a).transform.InverseTransformPoint(val));
					}
					array = worldSnapPoints2;
					foreach (Vector3 val2 in array)
					{
						((Bounds)(ref b2)).Encapsulate(((Component)a).transform.InverseTransformPoint(val2));
					}
					if (!HasSideContact(a2, b2, tolerance))
					{
						return false;
					}
				}
				float num = tolerance * tolerance;
				bool[] array2 = new bool[worldSnapPoints2.Length];
				int num2 = 0;
				array = worldSnapPoints;
				foreach (Vector3 val3 in array)
				{
					int num3 = -1;
					float num4 = float.MaxValue;
					for (int j = 0; j < worldSnapPoints2.Length; j++)
					{
						if (!array2[j])
						{
							Vector3 val4 = val3 - worldSnapPoints2[j];
							float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
							if (sqrMagnitude <= num && sqrMagnitude < num4)
							{
								num3 = j;
								num4 = sqrMagnitude;
							}
						}
					}
					if (num3 >= 0)
					{
						array2[num3] = true;
						num2++;
					}
				}
				int num5 = ((!planar || ModConfig.IncludeDiagonalConnections.Value) ? 1 : Math.Min(2, Math.Min(worldSnapPoints.Length, worldSnapPoints2.Length)));
				return num2 >= num5;
			}
			if (TryGetWorldBounds(a, out var bounds) && TryGetWorldBounds(b, out var bounds2))
			{
				if (!ModConfig.IncludeDiagonalConnections.Value && !HasSideContact(bounds, bounds2, tolerance))
				{
					return false;
				}
				((Bounds)(ref bounds)).Expand(tolerance * 2f);
				((Bounds)(ref bounds2)).Expand(tolerance * 2f);
				return ((Bounds)(ref bounds)).Intersects(bounds2);
			}
			return false;
		}

		internal static bool HasSideContact(Bounds a, Bounds b, float tolerance)
		{
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_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_0061: 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)
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < 3; i++)
			{
				Vector3 val = ((Bounds)(ref a)).max;
				float num3 = ((Vector3)(ref val))[i];
				val = ((Bounds)(ref b)).max;
				float num4 = Mathf.Min(num3, ((Vector3)(ref val))[i]);
				val = ((Bounds)(ref a)).min;
				float num5 = ((Vector3)(ref val))[i];
				val = ((Bounds)(ref b)).min;
				float num6 = num4 - Mathf.Max(num5, ((Vector3)(ref val))[i]);
				if (num6 < 0f - tolerance)
				{
					return false;
				}
				val = ((Bounds)(ref a)).size;
				if (((Vector3)(ref val))[i] > tolerance)
				{
					num++;
				}
				if (num6 > tolerance)
				{
					num2++;
				}
			}
			return num2 >= Mathf.Max(0, num - 1);
		}

		private static bool TryGetWorldBounds(Piece piece, out Bounds bounds)
		{
			//IL_0001: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			Renderer[] componentsInChildren = ((Component)piece).GetComponentsInChildren<Renderer>(true);
			bool flag = false;
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					if (!flag)
					{
						bounds = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val.bounds);
					}
				}
			}
			if (flag)
			{
				return true;
			}
			Collider[] componentsInChildren2 = ((Component)piece).GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					if (!flag)
					{
						bounds = val2.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val2.bounds);
					}
				}
			}
			return flag;
		}
	}
	internal static class PieceCompatibility
	{
		private sealed class Descriptor
		{
			internal string Prefab = string.Empty;

			internal string Category = string.Empty;

			internal ulong UsageTags;

			internal string FunctionalSignature = string.Empty;

			internal Vector3[] SnapPoints = Array.Empty<Vector3>();

			internal Bounds? LocalBounds;
		}

		private sealed class Vector3ToleranceComparer : IEqualityComparer<Vector3>
		{
			private readonly float _tolerance;

			internal Vector3ToleranceComparer(float tolerance)
			{
				_tolerance = tolerance;
			}

			public bool Equals(Vector3 a, Vector3 b)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return VectorApproximately(a, b, _tolerance);
			}

			public int GetHashCode(Vector3 value)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				int num = Mathf.RoundToInt(value.x / _tolerance);
				int num2 = Mathf.RoundToInt(value.y / _tolerance);
				int num3 = Mathf.RoundToInt(value.z / _tolerance);
				return ((17 * 31 + num) * 31 + num2) * 31 + num3;
			}
		}

		private static readonly Dictionary<string, Descriptor> Cache = new Dictionary<string, Descriptor>(StringComparer.OrdinalIgnoreCase);

		private static readonly FieldInfo? CategoryField = AccessTools.Field(typeof(Piece), "m_category");

		private static readonly FieldInfo? UsageTagsField = AccessTools.Field(typeof(Piece), "m_usage") ?? AccessTools.Field(typeof(Piece), "m_usageTags");

		private static readonly MethodInfo[] GetSnapPointMethods = (from method in typeof(Piece).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
			where string.Equals(method.Name, "GetSnapPoints", StringComparison.Ordinal)
			select method).ToArray();

		private static readonly HashSet<string> FunctionalComponentNames = new HashSet<string>(StringComparer.Ordinal)
		{
			"Door", "Container", "Ship", "TeleportWorld", "Bed", "Chair", "ItemStand", "ArmorStand", "Smelter", "CookingStation",
			"Fireplace", "CraftingStation", "StationExtension", "Fermenter", "Beehive", "Windmill", "SapCollector", "ShieldGenerator", "Turret", "Trap",
			"PrivateArea", "Sign", "Incinerator", "Vagon", "MineRock5"
		};

		internal static void ClearCache()
		{
			Cache.Clear();
		}

		internal static Vector3[] GetWorldSnapPoints(Piece piece)
		{
			if (!Object.op_Implicit((Object)(object)piece))
			{
				return Array.Empty<Vector3>();
			}
			string prefab = PieceUtils.PrefabName(piece);
			return GetDescriptor(piece, prefab).SnapPoints.Select((Vector3 point) => ((Component)piece).transform.TransformPoint(point)).ToArray();
		}

		internal static Vector3[] GetLocalSnapPoints(Piece piece)
		{
			if (!Object.op_Implicit((Object)(object)piece))
			{
				return Array.Empty<Vector3>();
			}
			string prefab = PieceUtils.PrefabName(piece);
			return GetDescriptor(piece, prefab).SnapPoints.ToArray();
		}

		internal static bool TryGetSurfaceNormal(Piece piece, out Vector3 worldNormal)
		{
			//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_0049: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			worldNormal = Vector3.zero;
			if (!Object.op_Implicit((Object)(object)piece))
			{
				return false;
			}
			string prefab = PieceUtils.PrefabName(piece);
			Descriptor descriptor = GetDescriptor(piece, prefab);
			if (descriptor.FunctionalSignature.Length != 0)
			{
				return false;
			}
			Vector3 val;
			if (TryGetSnapPlane(descriptor.SnapPoints, out var normal))
			{
				val = ((Component)piece).transform.TransformDirection(normal);
				worldNormal = ((Vector3)(ref val)).normalized;
				return ((Vector3)(ref worldNormal)).sqrMagnitude > 0.9f;
			}
			if (!descriptor.LocalBounds.HasValue)
			{
				return false;
			}
			Bounds value = descriptor.LocalBounds.Value;
			Vector3 size = ((Bounds)(ref value)).size;
			float num = Mathf.Abs(size.x);
			float num2 = Mathf.Abs(size.y);
			float num3 = Mathf.Abs(size.z);
			float num4;
			float num5;
			Vector3 val2;
			if (num <= num2 && num <= num3)
			{
				num4 = num;
				num5 = Mathf.Min(num2, num3);
				val2 = Vector3.right;
			}
			else if (num2 <= num && num2 <= num3)
			{
				num4 = num2;
				num5 = Mathf.Min(num, num3);
				val2 = Vector3.up;
			}
			else
			{
				num4 = num3;
				num5 = Mathf.Min(num, num2);
				val2 = Vector3.forward;
			}
			if (num5 <= 0.001f || num4 > num5 * 0.6f)
			{
				return false;
			}
			val = ((Component)piece).transform.TransformDirection(val2);
			worldNormal = ((Vector3)(ref val)).normalized;
			return ((Vector3)(ref worldNormal)).sqrMagnitude > 0.9f;
		}

		internal static bool AreCompatible(Piece source, Piece target, out string? compatibilityName)
		{
			//IL_018c: 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_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			compatibilityName = null;
			if (!ModConfig.AutoCompatibility.Value || !Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)target))
			{
				return false;
			}
			string text = PieceUtils.PrefabName(source);
			string text2 = PieceUtils.PrefabName(target);
			if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			Descriptor descriptor = GetDescriptor(source, text);
			Descriptor descriptor2 = GetDescriptor(target, text2);
			if (ModConfig.RequireSameCategory.Value && descriptor.Category.Length > 0 && descriptor2.Category.Length > 0 && !string.Equals(descriptor.Category, descriptor2.Category, StringComparison.Ordinal))
			{
				return false;
			}
			if (ModConfig.RequireSameUsageTags.Value && descriptor.UsageTags != 0L && descriptor2.UsageTags != 0L && descriptor.UsageTags != descriptor2.UsageTags)
			{
				return false;
			}
			if (!string.Equals(descriptor.FunctionalSignature, descriptor2.FunctionalSignature, StringComparison.Ordinal))
			{
				return false;
			}
			float tolerance = Mathf.Max(0.001f, ModConfig.SnapPointTolerance.Value);
			if (descriptor.SnapPoints.Length >= 2 || descriptor2.SnapPoints.Length >= 2)
			{
				if (descriptor.SnapPoints.Length != descriptor2.SnapPoints.Length || descriptor.SnapPoints.Length < 2)
				{
					return false;
				}
				if (!PointSetsMatch(descriptor.SnapPoints, descriptor2.SnapPoints, tolerance))
				{
					return false;
				}
				compatibilityName = $"auto:snap:{descriptor.SnapPoints.Length}";
				return true;
			}
			if (!ModConfig.AllowBoundsFallback.Value || !descriptor.LocalBounds.HasValue || !descriptor2.LocalBounds.HasValue)
			{
				return false;
			}
			float tolerance2 = Mathf.Max(0.001f, ModConfig.BoundsTolerance.Value);
			Bounds value = descriptor.LocalBounds.Value;
			Bounds value2 = descriptor2.LocalBounds.Value;
			if (!VectorApproximately(((Bounds)(ref value)).center, ((Bounds)(ref value2)).center, tolerance2) || !VectorApproximately(((Bounds)(ref value)).size, ((Bounds)(ref value2)).size, tolerance2))
			{
				return false;
			}
			compatibilityName = "auto:bounds";
			return true;
		}

		private static Descriptor GetDescriptor(Piece piece, string prefab)
		{
			//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_011b: 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)
			if (Cache.TryGetValue(prefab, out Descriptor value))
			{
				return value;
			}
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefab) : null);
			Piece val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Piece>() : null);
			if (Object.op_Implicit((Object)(object)val2))
			{
				piece = val2;
			}
			Descriptor descriptor = new Descriptor
			{
				Prefab = prefab,
				Category = ReadCategory(piece),
				UsageTags = ReadUsageTags(piece),
				FunctionalSignature = ReadFunctionalSignature(piece),
				SnapPoints = ReadSnapPoints(piece),
				LocalBounds = ReadLocalMeshBounds(piece)
			};
			if (Object.op_Implicit((Object)(object)val2))
			{
				Cache[prefab] = descriptor;
			}
			if (ModConfig.DebugLogging.Value)
			{
				ManualLogSource log = PieceReplacerPlugin.Log;
				string text = $"Descriptor {prefab}: category={descriptor.Category}, usage=0x{descriptor.UsageTags:X}, ";
				string text2 = $"functional={descriptor.FunctionalSignature}, snaps={descriptor.SnapPoints.Length}, ";
				object obj;
				if (!descriptor.LocalBounds.HasValue)
				{
					obj = "none";
				}
				else
				{
					Bounds value2 = descriptor.LocalBounds.Value;
					obj = ((object)((Bounds)(ref value2)).size/*cast due to .constrained prefix*/).ToString();
				}
				log.LogDebug((object)(text + text2 + "bounds=" + (string?)obj));
			}
			return descriptor;
		}

		private static string ReadCategory(Piece piece)
		{
			try
			{
				return (CategoryField?.GetValue(piece))?.ToString() ?? string.Empty;
			}
			catch (Exception ex)
			{
				LogFailure("m_category", piece, ex);
				return string.Empty;
			}
		}

		private static ulong ReadUsageTags(Piece piece)
		{
			try
			{
				object obj = UsageTagsField?.GetValue(piece);
				if (obj == null)
				{
					return 0uL;
				}
				return Convert.ToUInt64(obj, CultureInfo.InvariantCulture);
			}
			catch (Exception ex)
			{
				LogFailure("m_usage", piece, ex);
				return 0uL;
			}
		}

		private static string ReadFunctionalSignature(Piece piece)
		{
			try
			{
				string[] value = (from component in ((Component)piece).GetComponents<Component>()
					where Object.op_Implicit((Object)(object)component)
					select ((object)component).GetType().Name into name
					where FunctionalComponentNames.Contains(name)
					select name).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string name) => name, StringComparer.Ordinal).ToArray();
				return string.Join("+", value);
			}
			catch (Exception ex)
			{
				LogFailure("functional components", piece, ex);
				return string.Empty;
			}
		}

		private static Vector3[] ReadSnapPoints(Piece piece)
		{
			List<Transform> list = new List<Transform>();
			foreach (MethodInfo item in GetSnapPointMethods.OrderBy((MethodInfo m) => m.GetParameters().Length))
			{
				try
				{
					ParameterInfo[] parameters = item.GetParameters();
					object obj;
					if (parameters.Length == 0)
					{
						obj = item.Invoke(piece, null);
						goto IL_00e0;
					}
					if (parameters.Length == 1 && typeof(IList<Transform>).IsAssignableFrom(parameters[0].ParameterType))
					{
						List<Transform> list2 = new List<Transform>();
						obj = item.Invoke(piece, new object[1] { list2 });
						list.AddRange(list2.Where((Transform t) => Object.op_Implicit((Object)(object)t)));
						goto IL_00e0;
					}
					goto end_IL_004e;
					IL_00e0:
					if (obj is IEnumerable enumerable)
					{
						foreach (object item2 in enumerable)
						{
							Transform val = (Transform)((item2 is Transform) ? item2 : null);
							if (val != null && Object.op_Implicit((Object)(object)val))
							{
								list.Add(val);
								continue;
							}
							Component val2 = (Component)((item2 is Component) ? item2 : null);
							if (val2 != null && Object.op_Implicit((Object)(object)val2))
							{
								list.Add(val2.transform);
								continue;
							}
							GameObject val3 = (GameObject)((item2 is GameObject) ? item2 : null);
							if (val3 != null && Object.op_Implicit((Object)(object)val3))
							{
								list.Add(val3.transform);
							}
						}
					}
					if (list.Count > 0)
					{
						break;
					}
					end_IL_004e:;
				}
				catch (Exception ex)
				{
					if (ModConfig.DebugLogging.Value)
					{
						PieceReplacerPlugin.Log.LogDebug((object)$"GetSnapPoints variant '{item}' failed for {PieceUtils.PrefabName(piece)}: {ex.Message}");
					}
				}
			}
			if (list.Count == 0)
			{
				try
				{
					list.AddRange(from transform in ((Component)piece).GetComponentsInChildren<Transform>(true)
						where Object.op_Implicit((Object)(object)transform) && (Object)(object)transform != (Object)(object)((Component)piece).transform && ((Object)transform).name.IndexOf("snap", StringComparison.OrdinalIgnoreCase) >= 0
						select transform);
				}
				catch (Exception ex2)
				{
					LogFailure("snap-point hierarchy", piece, ex2);
				}
			}
			Transform root = ((Component)piece).transform;
			return (from point in (from point in list
					where Object.op_Implicit((Object)(object)point)
					select root.InverseTransformPoint(point.position)).Distinct(new Vector3ToleranceComparer(0.001f))
				orderby point.x, point.y, point.z
				select point).ToArray();
		}

		private static Bounds? ReadLocalMeshBounds(Piece piece)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_00f9: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MeshFilter[] componentsInChildren = ((Component)piece).GetComponentsInChildren<MeshFilter>(true);
				if (componentsInChildren.Length == 0)
				{
					return null;
				}
				bool flag = false;
				Bounds value = default(Bounds);
				Transform transform = ((Component)piece).transform;
				MeshFilter[] array = componentsInChildren;
				Vector3 val2 = default(Vector3);
				foreach (MeshFilter val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.sharedMesh))
					{
						continue;
					}
					Bounds bounds = val.sharedMesh.bounds;
					Vector3 min = ((Bounds)(ref bounds)).min;
					Vector3 max = ((Bounds)(ref bounds)).max;
					for (int j = 0; j < 2; j++)
					{
						for (int k = 0; k < 2; k++)
						{
							for (int l = 0; l < 2; l++)
							{
								((Vector3)(ref val2))..ctor((j == 0) ? min.x : max.x, (k == 0) ? min.y : max.y, (l == 0) ? min.z : max.z);
								Vector3 val3 = ((Component)val).transform.TransformPoint(val2);
								Vector3 val4 = transform.InverseTransformPoint(val3);
								if (!flag)
								{
									((Bounds)(ref value))..ctor(val4, Vector3.zero);
									flag = true;
								}
								else
								{
									((Bounds)(ref value)).Encapsulate(val4);
								}
							}
						}
					}
				}
				return flag ? new Bounds?(value) : ((Bounds?)null);
			}
			catch (Exception ex)
			{
				LogFailure("mesh bounds", piece, ex);
				return null;
			}
		}

		internal static bool TryGetSnapPlane(Vector3[] points, out Vector3 normal)
		{
			//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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_00ab: 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_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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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)
			normal = Vector3.zero;
			if (points.Length < 3)
			{
				return false;
			}
			Vector3 val = points[0];
			Vector3 val2 = Vector3.zero;
			Vector3[] array = points;
			foreach (Vector3 val3 in array)
			{
				Vector3 val4 = val3 - val;
				if (((Vector3)(ref val4)).sqrMagnitude > ((Vector3)(ref val2)).sqrMagnitude)
				{
					val2 = val3 - val;
				}
			}
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				return false;
			}
			Vector3 val5 = Vector3.zero;
			array = points;
			foreach (Vector3 val6 in array)
			{
				Vector3 val7 = Vector3.Cross(val2, val6 - val);
				if (((Vector3)(ref val7)).sqrMagnitude > ((Vector3)(ref val5)).sqrMagnitude)
				{
					val5 = val7;
				}
			}
			if (((Vector3)(ref val5)).magnitude / ((Vector3)(ref val2)).magnitude < 0.1f)
			{
				return false;
			}
			normal = ((Vector3)(ref val5)).normalized;
			array = points;
			for (int i = 0; i < array.Length; i++)
			{
				if (Mathf.Abs(Vector3.Dot(array[i] - val, normal)) > 0.025f)
				{
					normal = Vector3.zero;
					return false;
				}
			}
			return true;
		}

		private static bool PointSetsMatch(Vector3[] a, Vector3[] b, float tolerance)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			bool[] array = new bool[b.Length];
			float num = tolerance * tolerance;
			foreach (Vector3 val in a)
			{
				int num2 = -1;
				float num3 = float.MaxValue;
				for (int j = 0; j < b.Length; j++)
				{
					if (!array[j])
					{
						Vector3 val2 = val - b[j];
						float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
						if (sqrMagnitude <= num && sqrMagnitude < num3)
						{
							num3 = sqrMagnitude;
							num2 = j;
						}
					}
				}
				if (num2 < 0)
				{
					return false;
				}
				array[num2] = true;
			}
			return true;
		}

		private static bool VectorApproximately(Vector3 a, Vector3 b, float tolerance)
		{
			//IL_0000: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Abs(a.x - b.x) <= tolerance && Mathf.Abs(a.y - b.y) <= tolerance)
			{
				return Mathf.Abs(a.z - b.z) <= tolerance;
			}
			return false;
		}

		private static void LogFailure(string member, Piece piece, Exception ex)
		{
			if (ModConfig.DebugLogging.Value)
			{
				PieceReplacerPlugin.Log.LogWarning((object)("Could not inspect " + member + " on '" + PieceUtils.PrefabName(piece) + "': " + ex.Message));
			}
		}
	}
	internal static class ReplacementAlignment
	{
		private const float MaximumReasonablePivotCorrection = 10f;

		internal static Vector3 GetAlignedPosition(Piece sourcePiece, Piece replacementPiece, Quaternion replacementRotation, out Vector3 correction)
		{
			//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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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)
			correction = Vector3.zero;
			if (!Object.op_Implicit((Object)(object)sourcePiece) || !Object.op_Implicit((Object)(object)replacementPiece))
			{
				if (!Object.op_Implicit((Object)(object)sourcePiece))
				{
					return Vector3.zero;
				}
				return ((Component)sourcePiece).transform.position;
			}
			Vector3 position = ((Component)sourcePiece).transform.position;
			Vector3[] worldSnapPoints = PieceCompatibility.GetWorldSnapPoints(sourcePiece);
			Vector3[] localSnapPoints = PieceCompatibility.GetLocalSnapPoints(replacementPiece);
			if (worldSnapPoints.Length == 0 || localSnapPoints.Length == 0)
			{
				return position;
			}
			Vector3 val = Average(worldSnapPoints);
			Vector3 val2 = Average(localSnapPoints);
			Vector3 val3 = val - replacementRotation * val2;
			if (!IsFinite(val3))
			{
				return position;
			}
			correction = val3 - position;
			if (((Vector3)(ref correction)).magnitude > 10f)
			{
				correction = Vector3.zero;
				return position;
			}
			return val3;
		}

		private static Vector3 Average(Vector3[] points)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.zero;
			for (int i = 0; i < points.Length; i++)
			{
				val += points[i];
			}
			return val / (float)points.Length;
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: 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_001a: 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_0034: 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)
			if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z))
			{
				return !float.IsInfinity(value.z);
			}
			return false;
		}
	}
	internal sealed class ReplacementGroup
	{
		private readonly HashSet<string> _prefabs;

		internal string Name { get; }

		internal ReplacementGroup(string name, IEnumerable<string> prefabs)
		{
			Name = name;
			_prefabs = new HashSet<string>(prefabs, StringComparer.OrdinalIgnoreCase);
		}

		internal bool Contains(string prefabName)
		{
			return _prefabs.Contains(prefabName);
		}
	}
	internal static class ReplacementRegistry
	{
		private static readonly Dictionary<string, ReplacementGroup> GroupByPrefab = new Dictionary<string, ReplacementGroup>(StringComparer.OrdinalIgnoreCase);

		internal static void Reload(string definition)
		{
			GroupByPrefab.Clear();
			PieceCompatibility.ClearCache();
			if (string.IsNullOrWhiteSpace(definition))
			{
				PieceReplacerPlugin.Log.LogInfo((object)"No explicit replacement groups configured; automatic compatibility remains available.");
				return;
			}
			string[] array = definition.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.IndexOf(':');
				string text2;
				string text3;
				if (num >= 0)
				{
					text2 = text.Substring(0, num).Trim();
					text3 = text.Substring(num + 1);
				}
				else
				{
					text2 = $"group-{GroupByPrefab.Count + 1}";
					text3 = text;
				}
				string[] array2 = (from p in text3.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
					select p.Trim() into p
					where p.Length > 0
					select p).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
				if (array2.Length < 2)
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Ignoring replacement group '" + text2 + "' because it contains fewer than two prefabs."));
					continue;
				}
				ReplacementGroup value = new ReplacementGroup(text2, array2);
				string[] array3 = array2;
				foreach (string text4 in array3)
				{
					if (GroupByPrefab.TryGetValue(text4, out ReplacementGroup value2))
					{
						PieceReplacerPlugin.Log.LogWarning((object)("Prefab '" + text4 + "' occurs in both '" + value2.Name + "' and '" + text2 + "'. '" + text2 + "' wins."));
					}
					GroupByPrefab[text4] = value;
				}
			}
			PieceReplacerPlugin.Log.LogInfo((object)($"Loaded {GroupByPrefab.Values.Distinct().Count()} explicit replacement group(s) covering {GroupByPrefab.Count} prefab(s)." + " Automatic structural compatibility is evaluated for all other pieces."));
		}

		internal static bool CanReplace(Piece source, Piece target, out string? groupName)
		{
			string text = PieceUtils.PrefabName(source);
			string text2 = PieceUtils.PrefabName(target);
			groupName = null;
			if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (GroupByPrefab.TryGetValue(text, out ReplacementGroup value) && GroupByPrefab.TryGetValue(text2, out ReplacementGroup value2) && value == value2)
			{
				groupName = value.Name;
				return true;
			}
			if (VanillaStructuralCompatibility.AreCompatible(text, text2, out groupName))
			{
				return true;
			}
			return PieceCompatibility.AreCompatible(source, target, out groupName);
		}
	}
	internal static class ReplacementRuntime
	{
		internal sealed class State
		{
			internal Player Player;

			internal Piece SelectedPiece;

			internal Piece TargetPiece;

			internal string SelectedPrefab = string.Empty;

			internal string TargetPrefab = string.Empty;

			internal string GroupName = string.Empty;

			internal int UpdatedFrame;
		}

		private sealed class CommitSnapshot
		{
			internal Player Player;

			internal Piece OldPiece;

			internal string OldPrefab = string.Empty;

			internal string NewPrefab = string.Empty;

			internal Vector3 Position;

			internal Quaternion Rotation;

			internal HashSet<int> ExistingMatchingInstances = new HashSet<int>();
		}

		private const float VerificationPositionTolerance = 0.2f;

		private const float VerificationRotationToleranceDegrees = 5f;

		private const int VerificationAttempts = 4;

		private const int RemovalVerificationAttempts = 60;

		private const float PlacementStabilizationSeconds = 0.1f;

		private const float SupportIsolationSeconds = 0.05f;

		private const float PostRemovalVerificationSeconds = 0.05f;

		private static readonly MethodInfo? WearNTearUpdateSupportMethod = typeof(WearNTear).GetMethod("UpdateSupport", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static State? _current;

		private static Piece? _pendingRemoval;

		private static Player? _pendingPlayer;

		private static string _pendingSelectedPrefab = string.Empty;

		private static string _pendingTargetPrefab = string.Empty;

		private static Vector3 _pendingPlacementPosition;

		private static Quaternion _pendingPlacementRotation;

		private static HashSet<int> _pendingExistingMatchingInstances = new HashSet<int>();

		private static bool _hasPendingPlacementTransform;

		private static bool _verificationInProgress;

		internal static State? Current => _current;

		internal static void Refresh(Player player)
		{
			if (!ModConfig.IsModifierHeld() || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				ClearCurrent();
				return;
			}
			Piece selected = player.GetSelectedPiece();
			if (!Object.op_Implicit((Object)(object)selected) || selected.m_repairPiece)
			{
				ClearCurrent();
				return;
			}
			string groupName2;
			Piece val = PieceTargeting.FindAimedPiece(player, (Piece candidate) => ReplacementRegistry.CanReplace(candidate, selected, out groupName2));
			if (!Object.op_Implicit((Object)(object)val))
			{
				ClearCurrent();
				return;
			}
			string text = PieceUtils.PrefabName(selected);
			string text2 = PieceUtils.PrefabName(val);
			if (!ReplacementRegistry.CanReplace(val, selected, out string groupName))
			{
				ClearCurrent();
				return;
			}
			_current = new State
			{
				Player = player,
				SelectedPiece = selected,
				TargetPiece = val,
				SelectedPrefab = text,
				TargetPrefab = text2,
				GroupName = (groupName ?? string.Empty),
				UpdatedFrame = Time.frameCount
			};
			if (ModConfig.DebugLogging.Value)
			{
				PieceReplacerPlugin.Log.LogDebug((object)("Replace candidate: " + text2 + " -> " + text + " [" + groupName + "]"));
			}
		}

		internal static bool IsCompatibleFor(Player player)
		{
			if (_current != null && (Object)(object)_current.Player == (Object)(object)player && Object.op_Implicit((Object)(object)_current.TargetPiece))
			{
				return Object.op_Implicit((Object)(object)_current.SelectedPiece);
			}
			return false;
		}

		internal static bool MatchesPlacement(Player player, Piece placementPiece)
		{
			if (!IsCompatibleFor(player) || _current == null)
			{
				return false;
			}
			return PieceUtils.PrefabName(placementPiece) == _current.SelectedPrefab;
		}

		internal static bool BeginCommit(Player player, Piece placementPiece)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: 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)
			if (_verificationInProgress)
			{
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogWarning((object)"Replacement commit ignored because the previous placement is still being verified.");
				}
				return false;
			}
			if (!MatchesPlacement(player, placementPiece) || _current == null)
			{
				return false;
			}
			Piece targetPiece = _current.TargetPiece;
			if (!VanillaAccess.CanRemoveForReplacement(player, targetPiece, out string reason))
			{
				VanillaAccess.ShowCenterMessage(player, reason);
				return false;
			}
			GameObject placementGhost = PlayerPrivateAccess.GetPlacementGhost(player);
			if (!Object.op_Implicit((Object)(object)placementGhost) || !placementGhost.activeInHierarchy)
			{
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogWarning((object)"Replacement commit aborted because the placement ghost is missing/inactive.");
				}
				return false;
			}
			_pendingPlacementPosition = placementGhost.transform.position;
			_pendingPlacementRotation = placementGhost.transform.rotation;
			_hasPendingPlacementTransform = true;
			_pendingSelectedPrefab = _current.SelectedPrefab;
			_pendingTargetPrefab = _current.TargetPrefab;
			_pendingPlayer = player;
			_pendingRemoval = targetPiece;
			_pendingExistingMatchingInstances = CaptureExistingMatchingInstances(player, _pendingSelectedPrefab, _pendingPlacementPosition, _pendingPlacementRotation, targetPiece);
			if (ModConfig.DebugLogging.Value)
			{
				Vector3 eulerAngles = ((Quaternion)(ref _pendingPlacementRotation)).eulerAngles;
				PieceReplacerPlugin.Log.LogDebug((object)("Replacement commit begin: target='" + _pendingTargetPrefab + "', replacement='" + _pendingSelectedPrefab + "', " + $"pos={_pendingPlacementPosition:F3}, rot=({eulerAngles.x:F2},{eulerAngles.y:F2},{eulerAngles.z:F2}), " + $"preexistingMatches={_pendingExistingMatchingInstances.Count}."));
			}
			return true;
		}

		internal static bool TryGetPendingPlacementTransform(Player player, Piece placementPiece, out Vector3 position, out Quaternion rotation)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			rotation = Quaternion.identity;
			if (!_hasPendingPlacementTransform || (Object)(object)_pendingPlayer != (Object)(object)player || !Object.op_Implicit((Object)(object)placementPiece) || string.IsNullOrEmpty(_pendingSelectedPrefab))
			{
				return false;
			}
			if (PieceUtils.PrefabName(placementPiece) != _pendingSelectedPrefab)
			{
				return false;
			}
			position = _pendingPlacementPosition;
			rotation = _pendingPlacementRotation;
			return true;
		}

		internal static void CompleteCommit(Player player, bool placementSucceeded)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			Piece pendingRemoval = _pendingRemoval;
			bool flag = (Object)(object)_pendingPlayer == (Object)(object)player;
			CommitSnapshot commitSnapshot = new CommitSnapshot
			{
				Player = player,
				OldPiece = pendingRemoval,
				OldPrefab = _pendingTargetPrefab,
				NewPrefab = _pendingSelectedPrefab,
				Position = _pendingPlacementPosition,
				Rotation = _pendingPlacementRotation,
				ExistingMatchingInstances = new HashSet<int>(_pendingExistingMatchingInstances)
			};
			if (ModConfig.DebugLogging.Value)
			{
				PieceReplacerPlugin.Log.LogDebug((object)($"Replacement TryPlacePiece result: success={placementSucceeded}, " + string.Format("pendingPlayer={0}, oldPiece={1}, ", flag, Object.op_Implicit((Object)(object)pendingRemoval) ? PieceUtils.PrefabName(pendingRemoval) : "<missing>") + "replacement='" + commitSnapshot.NewPrefab + "'."));
			}
			ClearPending();
			if (placementSucceeded && flag && Object.op_Implicit((Object)(object)pendingRemoval) && !string.IsNullOrEmpty(commitSnapshot.NewPrefab))
			{
				_verificationInProgress = true;
				((MonoBehaviour)player).StartCoroutine(VerifyPlacementThenRemove(commitSnapshot));
			}
		}

		internal static void CancelCommit()
		{
			ClearPending();
		}

		internal static void Clear()
		{
			ClearCurrent();
			ClearPending();
		}

		private static IEnumerator VerifyPlacementThenRemove(CommitSnapshot snapshot)
		{
			Piece placedPiece = null;
			try
			{
				for (int attempt = 1; attempt <= 4; attempt++)
				{
					yield return null;
					placedPiece = FindPlacedReplacement(snapshot);
					if (Object.op_Implicit((Object)(object)placedPiece))
					{
						if (ModConfig.DebugLogging.Value)
						{
							Quaternion rotation = ((Component)placedPiece).transform.rotation;
							Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
							PieceReplacerPlugin.Log.LogDebug((object)($"Replacement spawn verification succeeded on attempt {attempt}: " + $"instance={((Object)placedPiece).GetInstanceID()}, prefab='{PieceUtils.PrefabName(placedPiece)}', " + $"pos={((Component)placedPiece).transform.position:F3}, rot=({eulerAngles.x:F2},{eulerAngles.y:F2},{eulerAngles.z:F2})."));
						}
						break;
					}
					if (ModConfig.DebugLogging.Value)
					{
						PieceReplacerPlugin.Log.LogDebug((object)($"Replacement spawn verification attempt {attempt}/{4} found no new " + "'" + snapshot.NewPrefab + "' instance at the captured transform."));
					}
				}
				if (Object.op_Implicit((Object)(object)placedPiece))
				{
					float stableUntil = Time.realtimeSinceStartup + 0.1f;
					while (Time.realtimeSinceStartup < stableUntil)
					{
						yield return null;
						if (!IsValidPlacedReplacement(snapshot, placedPiece))
						{
							PieceReplacerPlugin.Log.LogWarning((object)("Replacement instance '" + snapshot.NewPrefab + "' disappeared during the network stabilization window. Original '" + snapshot.OldPrefab + "' was kept."));
							placedPiece = null;
							break;
						}
					}
				}
				if (!Object.op_Implicit((Object)(object)placedPiece))
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Replacement aborted safely: vanilla reported placement success for '" + snapshot.NewPrefab + "', but no matching placed instance could be verified. Original '" + snapshot.OldPrefab + "' was kept."));
					VanillaAccess.ShowCenterMessage(snapshot.Player, "Replacement failed; original piece was kept.");
					_verificationInProgress = false;
					yield break;
				}
				if (!Object.op_Implicit((Object)(object)snapshot.OldPiece))
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Replacement '" + snapshot.NewPrefab + "' was verified, but original '" + snapshot.OldPrefab + "' disappeared before removal."));
					_verificationInProgress = false;
					ClearCurrent();
					yield break;
				}
				WearNTear wearNTear = ((Component)snapshot.OldPiece).GetComponent<WearNTear>();
				if (!Object.op_Implicit((Object)(object)wearNTear))
				{
					PieceReplacerPlugin.Log.LogWarning((object)("Replacement '" + snapshot.NewPrefab + "' was verified but old piece '" + snapshot.OldPrefab + "' no longer has WearNTear; old piece was not removed."));
					_verificationInProgress = false;
					yield break;
				}
				Collider[] oldColliders = ((Component)snapshot.OldPiece).GetComponentsInChildren<Collider>(true);
				bool[] oldColliderStates = new bool[oldColliders.Length];
				for (int i = 0; i < oldColliders.Length; i++)
				{
					Collider val = oldColliders[i];
					oldColliderStates[i] = Object.op_Implicit((Object)(object)val) && val.enabled;
					if (Object.op_Implicit((Object)(object)val) && val.enabled)
					{
						val.enabled = false;
					}
				}
				WearNTear replacementWearNTear = ((Component)placedPiece).GetComponent<WearNTear>();
				float isolationUntil = Time.realtimeSinceStartup + 0.05f;
				while (Time.realtimeSinceStartup < isolationUntil)
				{
					TryRefreshSupport(replacementWearNTear);
					yield return null;
					if (!IsValidPlacedReplacement(snapshot, placedPiece))
					{
						RestoreColliders(oldColliders, oldColliderStates);
						PieceReplacerPlugin.Log.LogWarning((object)("Replacement aborted safely: '" + snapshot.NewPrefab + "' did not survive without support from the original '" + snapshot.OldPrefab + "'. Original piece was kept."));
						VanillaAccess.ShowCenterMessage(snapshot.Player, "Replacement failed stability check; original piece was kept.");
						ClearCurrent();
						yield break;
					}
				}
				bool blockDrop = snapshot.Player.PlacementCostDisabled;
				wearNTear.Remove(blockDrop);
				bool oldRemoved = false;
				for (int attempt = 1; attempt <= 60; attempt++)
				{
					yield return null;
					if (!Object.op_Implicit((Object)(object)snapshot.OldPiece))
					{
						oldRemoved = true;
						break;
					}
				}
				if (!oldRemoved)
				{
					RestoreColliders(oldColliders, oldColliderStates);
					PieceReplacerPlugin.Log.LogWarning((object)("Replacement rollback: new '" + snapshot.NewPrefab + "' was placed, but original '" + snapshot.OldPrefab + "' did not disappear after its removal request."));
					if (Object.op_Implicit((Object)(object)placedPiece))
					{
						WearNTear component = ((Component)placedPiece).GetComponent<WearNTear>();
						if (Object.op_Implicit((Object)(object)component))
						{
							component.Remove(blockDrop);
						}
					}
					VanillaAccess.ShowCenterMessage(snapshot.Player, "Replacement failed; original piece was kept.");
					ClearCurrent();
					yield break;
				}
				TryRefreshSupport(replacementWearNTear);
				float postRemovalUntil = Time.realtimeSinceStartup + 0.05f;
				while (Time.realtimeSinceStartup < postRemovalUntil)
				{
					yield return null;
					TryRefreshSupport(replacementWearNTear);
					if (!IsValidPlacedReplacement(snapshot, placedPiece))
					{
						PieceReplacerPlugin.Log.LogError((object)("Replacement '" + snapshot.NewPrefab + "' disappeared after original '" + snapshot.OldPrefab + "' was removed. This indicates an external/networked destruction after commit."));
						VanillaAccess.ShowCenterMessage(snapshot.Player, "Replacement vanished after server commit; see log.");
						ClearCurrent();
						yield break;
					}
				}
				if (ModConfig.DebugLogging.Value)
				{
					PieceReplacerPlugin.Log.LogInfo((object)("Replaced '" + snapshot.OldPrefab + "' with verified '" + snapshot.NewPrefab + "' " + $"(instance={((Object)placedPiece).GetInstanceID()})."));
				}
				ClearCurrent();
			}
			finally
			{
				_verificationInProgress = false;
			}
		}

		private static HashSet<int> CaptureExistingMatchingInstances(Player player, string prefabName, Vector3 position, Quaternion rotation, Piece oldPiece)
		{
			//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)
			HashSet<int> hashSet = new HashSet<int>();
			Piece[] array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
			foreach (Piece val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)oldPiece) && !PieceUtils.IsPlacementGhost(player, val) && string.Equals(PieceUtils.PrefabName(val), prefabName, StringComparison.OrdinalIgnoreCase) && TransformMatches(((Component)val).transform, position, rotation))
				{
					hashSet.Add(((Object)val).GetInstanceID());
				}
			}
			return hashSet;
		}

		private static Piece? FindPlacedReplacement(CommitSnapshot snapshot)
		{
			Piece[] array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
			foreach (Piece val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)snapshot.OldPiece) && !PieceUtils.IsPlacementGhost(snapshot.Player, val) && !snapshot.ExistingMatchingInstances.Contains(((Object)val).GetInstanceID()) && IsValidPlacedReplacement(snapshot, val))
				{
					return val;
				}
			}
			return null;
		}

		private static bool IsValidPlacedReplacement(CommitSnapshot snapshot, Piece piece)
		{
			//IL_0058: 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)
			if (!O