Decompiled source of SonicWorldTools v0.1.1

SonicWorldTools.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SonicWorldTools.Config;
using SonicWorldTools.Core;
using SonicWorldTools.Install;
using SonicWorldTools.Integration;
using SonicWorldTools.Modules.PieceRepeater;
using SonicWorldTools.Modules.PrecisionAreaPipette;
using SonicWorldTools.Modules.ScatterProps;
using SonicWorldTools.Refinement;
using SonicWorldTools.Rendering;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SonicWorldTools")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+5c2b30426f6fc51221cf4e4e294ad664c4ba4fed")]
[assembly: AssemblyProduct("SonicWorldTools")]
[assembly: AssemblyTitle("SonicWorldTools")]
[assembly: AssemblyVersion("0.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 SonicWorldTools
{
	[BepInPlugin("com.sonicdm.valheim.sonicworldtools", "SonicWorldTools", "0.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class SonicWorldToolsPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.sonicdm.valheim.sonicworldtools";

		public const string PluginName = "SonicWorldTools";

		public const string PluginVersion = "0.1.1";

		internal static ManualLogSource ModLogger;

		private static SonicWorldToolsPlugin _instance;

		private PrecisionAreaPipetteController _pipetteController;

		private PieceRepeaterController _pieceRepeaterController;

		private readonly List<Action> _generatedToolUnhooks = new List<Action>();

		private Harmony _harmony;

		private bool _infinityHammerInitialized;

		private float _nextInfinityHammerProbeTime;

		private void Awake()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			_instance = this;
			ModLogger = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			ToolInput.RegisterOwnedMouse2Button();
			PrefabPlacementMetadata.Load(Paths.ConfigPath, ModLogger);
			_harmony = new Harmony("com.sonicdm.valheim.sonicworldtools");
			_harmony.PatchAll();
			ToolActionGate.RegisterClaim(ToolPlayerAction.Remove, "line-tool-sample", PieceRepeaterController.OwnsMiddleClick);
			ToolActionGate.RegisterClaim(ToolPlayerAction.Remove, "scatter-sample", ScatterPropsCommand.ShouldSuppressMiddleClick);
			HookConfigHotReload();
			if (PluginConfig.Enabled != null && PluginConfig.Enabled.Value && PluginConfig.InstallInfinityHammerTools != null && PluginConfig.InstallInfinityHammerTools.Value)
			{
				ToolInstallerBridge.EnsureInstalled();
			}
			TryInitializeInfinityHammerIntegration();
			ModLogger.LogInfo((object)"SonicWorldTools 0.1.0 loaded.");
		}

		private void Update()
		{
			ToolRuntime.BeginFrame();
			ToolInput.CaptureFrame();
			if (!_infinityHammerInitialized && Time.unscaledTime >= _nextInfinityHammerProbeTime)
			{
				_nextInfinityHammerProbeTime = Time.unscaledTime + 1f;
				TryInitializeInfinityHammerIntegration();
			}
			ToolIdentityTracker.Tick(_pipetteController);
			ScatterPropsCommand.Tick();
			_pieceRepeaterController?.Tick();
			PrefabPlacementMetadata.EnsureCatalog();
			_pipetteController?.Tick();
		}

		private void OnGUI()
		{
			_pipetteController?.DrawHud();
		}

		private void TryInitializeInfinityHammerIntegration()
		{
			if (!_infinityHammerInitialized && InfinityHammerBridge.TryDetect(out var _, out var _))
			{
				_infinityHammerInitialized = true;
				DependencyDetector.Run();
				TerrainToolIconOverrides.TryInstall(_harmony);
				ScatterPropsCommand.Register();
				PrefabGeometryDumpCommand.Register();
				PrefabPlacementMetadata.RegisterRefreshCommand();
				_pieceRepeaterController = _pieceRepeaterController ?? new PieceRepeaterController();
				ToolSessionCoordinator.Register(_pieceRepeaterController);
				ToolInputRouter.RegisterEarlyUpdateHandler("line-tool-settings", delegate
				{
					PieceRepeaterController.TryConsumeSettingsHotkey();
				});
				PieceRepeaterCommand.Register();
				PieceRepeaterController.InitializeKeyHints();
				TryRegisterPrecisionAreaPipette();
			}
		}

		private void TryRegisterPrecisionAreaPipette()
		{
			if (!PluginConfig.IsPrecisionAreaPipetteEnabled())
			{
				UnregisterPrecisionAreaPipette();
				ModLogger.LogInfo((object)"Precision Area Pipette disabled by config.");
				return;
			}
			if (!InfinityHammerBridge.TryDetect(out var _, out var _))
			{
				ModLogger.LogDebug((object)"Infinity Hammer temporarily unavailable; preserving Precision Select controller.");
				return;
			}
			if (_pipetteController == null)
			{
				_pipetteController = new PrecisionAreaPipetteController();
			}
			ToolSessionCoordinator.Register(_pipetteController);
			PrecisionAreaPipetteCommand.Register();
		}

		private void UnregisterPrecisionAreaPipette()
		{
			if (_pipetteController != null)
			{
				_pipetteController.Deactivate();
			}
			ToolSessionCoordinator.Unregister(_pipetteController);
			PrecisionAreaPipetteController.ClearInstance();
			_pipetteController = null;
		}

		private void HookConfigHotReload()
		{
			if (PluginConfig.Enabled != null)
			{
				PluginConfig.Enabled.SettingChanged += OnIntegrationSettingChanged;
			}
			if (PluginConfig.InstallInfinityHammerTools != null)
			{
				PluginConfig.InstallInfinityHammerTools.SettingChanged += OnIntegrationSettingChanged;
			}
			if (PluginConfig.PrecisionAreaPipetteEnabled != null)
			{
				PluginConfig.PrecisionAreaPipetteEnabled.SettingChanged += OnPipetteSettingChanged;
			}
			HookPreviewSetting(PluginConfig.ShowBox);
			HookPreviewSetting(PluginConfig.ShowCornerMarkers);
			HookPreviewSetting(PluginConfig.ShowHighlights);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.RefineModifierKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.AddPieceKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.ExcludePieceKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PickUpModifierKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PickUpKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.DismantleModifierKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PermanentDeleteModifierKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.SelectionRemovalKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterSampleKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterWaypointKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterPlaceModifier);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterSpacingModifier);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterLevelModifier);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterJointModifier);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterSettingsHotkey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.PieceRepeaterOffsetEditorModifier);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.ScatterSampleKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.ScatterClearModifier);
			HookGeneratedToolSetting<float>(PluginConfig.TerrainPathInitialHalfWidth);
			HookGeneratedToolSetting<float>(PluginConfig.TerrainPathEdgeSmoothness);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.TerrainPathFinishKey);
			HookGeneratedToolSetting<KeyCode>(PluginConfig.TerrainPathSmoothKey);
			HookGeneratedToolSetting<float>(PluginConfig.LowerTerrainInitialHeight);
			HookGeneratedToolSetting<float>(PluginConfig.LowerTerrainSmoothness);
			HookGeneratedToolSetting<float>(PluginConfig.ScatterInitialRadius);
		}

		private void OnGeneratedToolSettingChanged(object sender, EventArgs e)
		{
			ToolInstallerBridge.EnsureInstalled();
			PieceRepeaterController.RefreshKeyHints();
		}

		private void HookGeneratedToolSetting<T>(ConfigEntry<T> entry)
		{
			if (entry != null)
			{
				entry.SettingChanged += OnGeneratedToolSettingChanged;
				_generatedToolUnhooks.Add(delegate
				{
					entry.SettingChanged -= OnGeneratedToolSettingChanged;
				});
			}
		}

		private void HookPreviewSetting(ConfigEntry<bool> entry)
		{
			if (entry != null)
			{
				entry.SettingChanged += OnPreviewOrHudSettingChanged;
			}
		}

		private void OnIntegrationSettingChanged(object sender, EventArgs e)
		{
			bool flag = PluginConfig.Enabled != null && PluginConfig.Enabled.Value;
			bool flag2 = PluginConfig.InstallInfinityHammerTools != null && PluginConfig.InstallInfinityHammerTools.Value;
			if (flag && flag2)
			{
				ToolInstallerBridge.EnsureInstalled();
			}
			else
			{
				ToolInstallerBridge.OnIntegrationDisabled();
			}
			TryRegisterPrecisionAreaPipette();
		}

		private void OnPipetteSettingChanged(object sender, EventArgs e)
		{
			TryRegisterPrecisionAreaPipette();
		}

		private void OnPreviewOrHudSettingChanged(object sender, EventArgs e)
		{
			PrecisionAreaPipettePreview.Clear();
		}

		private void UnhookPreviewSetting(ConfigEntry<bool> entry)
		{
			if (entry != null)
			{
				entry.SettingChanged -= OnPreviewOrHudSettingChanged;
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
			if (PluginConfig.Enabled != null)
			{
				PluginConfig.Enabled.SettingChanged -= OnIntegrationSettingChanged;
			}
			if (PluginConfig.InstallInfinityHammerTools != null)
			{
				PluginConfig.InstallInfinityHammerTools.SettingChanged -= OnIntegrationSettingChanged;
			}
			if (PluginConfig.PrecisionAreaPipetteEnabled != null)
			{
				PluginConfig.PrecisionAreaPipetteEnabled.SettingChanged -= OnPipetteSettingChanged;
			}
			UnhookPreviewSetting(PluginConfig.ShowBox);
			UnhookPreviewSetting(PluginConfig.ShowCornerMarkers);
			UnhookPreviewSetting(PluginConfig.ShowHighlights);
			foreach (Action generatedToolUnhook in _generatedToolUnhooks)
			{
				generatedToolUnhook();
			}
			_generatedToolUnhooks.Clear();
			UnregisterPrecisionAreaPipette();
			ToolSessionCoordinator.Reset();
			ToolActionGate.Reset();
			ToolInputRouter.Reset();
			ToolRuntime.Reset();
			PrefabPlacementMetadata.Shutdown();
			_pieceRepeaterController?.Shutdown();
			_pieceRepeaterController = null;
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
			}
		}
	}
}
namespace SonicWorldTools.Rendering
{
	internal static class BoxRenderer
	{
		private const string RootName = "SonicWorldTools_BoxRenderer";

		private static readonly Color BoxColor = new Color(0.25f, 0.85f, 1f, 0.75f);

		private static readonly Color CornerAColor = new Color(0.2f, 1f, 0.45f, 0.9f);

		private static readonly Color CornerBColor = new Color(1f, 0.85f, 0.2f, 0.9f);

		private static readonly Color CornerCColor = new Color(1f, 0.45f, 0.85f, 0.9f);

		private static GameObject _root;

		private static LineRenderer[] _edges;

		private static LineRenderer[] _markerA;

		private static LineRenderer[] _markerB;

		private static LineRenderer[] _markerC;

		private static readonly Vector3[] CornerBuffer = (Vector3[])(object)new Vector3[8];

		private static Material _sharedMaterial;

		internal static void DrawBox(Vector3 cornerA, Vector3 cornerB)
		{
			//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)
			DrawBox(SelectionVolume.FromCorners(cornerA, cornerB));
		}

		internal static void DrawBox(SelectionVolume volume)
		{
			if (!IsBoxEnabled())
			{
				HideBox();
				return;
			}
			Ensure();
			volume.GetWorldCorners(CornerBuffer);
			SetEdgePositionsFromCorners(CornerBuffer);
			SetEdgesVisible(visible: true);
		}

		internal static void DrawCornerA(Vector3? worldPosition)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsMarkersEnabled() || !worldPosition.HasValue)
			{
				SetMarkerVisible(_markerA, visible: false);
				return;
			}
			Ensure();
			DrawCross(_markerA, worldPosition.Value, CornerAColor);
			SetMarkerVisible(_markerA, visible: true);
		}

		internal static void DrawCornerB(Vector3? worldPosition)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsMarkersEnabled() || !worldPosition.HasValue)
			{
				SetMarkerVisible(_markerB, visible: false);
				return;
			}
			Ensure();
			DrawCross(_markerB, worldPosition.Value, CornerBColor);
			SetMarkerVisible(_markerB, visible: true);
		}

		internal static void DrawCornerC(Vector3? worldPosition)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsMarkersEnabled() || !worldPosition.HasValue)
			{
				SetMarkerVisible(_markerC, visible: false);
				return;
			}
			Ensure();
			DrawCross(_markerC, worldPosition.Value, CornerCColor);
			SetMarkerVisible(_markerC, visible: true);
		}

		internal static void Clear()
		{
			HideBox();
			SetMarkerVisible(_markerA, visible: false);
			SetMarkerVisible(_markerB, visible: false);
			SetMarkerVisible(_markerC, visible: false);
		}

		internal static void Dispose()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			Clear();
			if ((Object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_edges = null;
			_markerA = null;
			_markerB = null;
			_markerC = null;
			if ((Object)_sharedMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_sharedMaterial);
				_sharedMaterial = null;
			}
		}

		private static bool IsBoxEnabled()
		{
			if (PluginConfig.ShowBox != null)
			{
				return PluginConfig.ShowBox.Value;
			}
			return true;
		}

		private static bool IsMarkersEnabled()
		{
			if (PluginConfig.ShowCornerMarkers != null)
			{
				return PluginConfig.ShowCornerMarkers.Value;
			}
			return true;
		}

		private static void HideBox()
		{
			SetEdgesVisible(visible: false);
		}

		private static void Ensure()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00d2: 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_0104: 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_0095: 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_009f: Expected O, but got Unknown
			if ((Object)_root != (Object)null && _edges != null)
			{
				return;
			}
			_root = new GameObject("SonicWorldTools_BoxRenderer");
			Object.DontDestroyOnLoad((Object)(object)_root);
			((Object)_root).hideFlags = (HideFlags)61;
			if ((Object)_sharedMaterial == (Object)null)
			{
				Shader val = Shader.Find("Sprites/Default");
				if ((Object)val == (Object)null)
				{
					val = Shader.Find("Unlit/Color");
				}
				_sharedMaterial = (((Object)val != (Object)null) ? new Material(val) : new Material(Shader.Find("Hidden/InternalErrorShader")));
				((Object)_sharedMaterial).hideFlags = (HideFlags)61;
			}
			_edges = (LineRenderer[])(object)new LineRenderer[12];
			for (int i = 0; i < 12; i++)
			{
				_edges[i] = CreateLine("Edge_" + i, BoxColor, 0.04f);
			}
			_markerA = CreateCross("CornerA", CornerAColor);
			_markerB = CreateCross("CornerB", CornerBColor);
			_markerC = CreateCross("CornerC", CornerCColor);
			SetEdgesVisible(visible: false);
			SetMarkerVisible(_markerA, visible: false);
			SetMarkerVisible(_markerB, visible: false);
			SetMarkerVisible(_markerC, visible: false);
		}

		private static LineRenderer[] CreateCross(string name, Color color)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			LineRenderer[] array = (LineRenderer[])(object)new LineRenderer[3];
			for (int i = 0; i < 3; i++)
			{
				array[i] = CreateLine(name + "_" + i, color, 0.06f);
			}
			return array;
		}

		private static LineRenderer CreateLine(string name, Color color, float width)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(_root.transform, false);
			LineRenderer val2 = val.AddComponent<LineRenderer>();
			((Renderer)val2).sharedMaterial = _sharedMaterial;
			val2.startColor = color;
			val2.endColor = color;
			val2.widthMultiplier = width;
			val2.positionCount = 2;
			val2.useWorldSpace = true;
			((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val2).receiveShadows = false;
			val2.loop = false;
			val2.numCapVertices = 2;
			((Renderer)val2).enabled = false;
			return val2;
		}

		private static void DrawCross(LineRenderer[] lines, Vector3 center, Color color)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_003b: 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_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_0050: 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_005f: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_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)
			if (lines != null)
			{
				SetSegment(lines[0], center + Vector3.left * 0.35f, center + Vector3.right * 0.35f, color);
				SetSegment(lines[1], center + Vector3.down * 0.35f, center + Vector3.up * 0.35f, color);
				SetSegment(lines[2], center + Vector3.back * 0.35f, center + Vector3.forward * 0.35f, color);
			}
		}

		private static void SetSegment(LineRenderer lr, Vector3 a, Vector3 b, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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)
			if (!((Object)lr == (Object)null))
			{
				lr.startColor = color;
				lr.endColor = color;
				lr.SetPosition(0, a);
				lr.SetPosition(1, b);
				((Renderer)lr).enabled = true;
			}
		}

		private static void SetEdgesVisible(bool visible)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (_edges == null)
			{
				return;
			}
			for (int i = 0; i < _edges.Length; i++)
			{
				if ((Object)_edges[i] != (Object)null)
				{
					((Renderer)_edges[i]).enabled = visible;
				}
			}
		}

		private static void SetMarkerVisible(LineRenderer[] lines, bool visible)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			if (lines == null)
			{
				return;
			}
			for (int i = 0; i < lines.Length; i++)
			{
				if ((Object)lines[i] != (Object)null)
				{
					((Renderer)lines[i]).enabled = visible;
				}
			}
		}

		private static void SetEdgePositionsFromCorners(Vector3[] c)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_00b0: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: 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)
			SetSegment(_edges[0], c[0], c[1], BoxColor);
			SetSegment(_edges[1], c[1], c[2], BoxColor);
			SetSegment(_edges[2], c[2], c[3], BoxColor);
			SetSegment(_edges[3], c[3], c[0], BoxColor);
			SetSegment(_edges[4], c[4], c[5], BoxColor);
			SetSegment(_edges[5], c[5], c[6], BoxColor);
			SetSegment(_edges[6], c[6], c[7], BoxColor);
			SetSegment(_edges[7], c[7], c[4], BoxColor);
			SetSegment(_edges[8], c[0], c[4], BoxColor);
			SetSegment(_edges[9], c[1], c[5], BoxColor);
			SetSegment(_edges[10], c[2], c[6], BoxColor);
			SetSegment(_edges[11], c[3], c[7], BoxColor);
		}
	}
	internal static class ObjectHighlightRenderer
	{
		private static readonly Color BaseColor = new Color(0.45f, 0.78f, 0.95f, 1f);

		private static readonly Color AddedColor = new Color(0.45f, 0.92f, 0.55f, 1f);

		private static readonly Color ExcludedColor = new Color(0.95f, 0.42f, 0.38f, 1f);

		private static readonly Color HoverColor = new Color(0.98f, 0.88f, 0.4f, 1f);

		private static readonly List<WearNTear> _highlighted = new List<WearNTear>(128);

		private static readonly HashSet<int> _highlightedIds = new HashSet<int>();

		private static readonly HashSet<int> _activeTintIds = new HashSet<int>();

		private static readonly HashSet<int> _frameTintIds = new HashSet<int>();

		private static readonly Dictionary<int, GameObject> _tintRoots = new Dictionary<int, GameObject>();

		internal static void SetHighlighted(IEnumerable<GameObject> roots)
		{
			SetVisualStates(roots, null, null, null);
		}

		internal static void SetVisualStates(IEnumerable<GameObject> baseRoots, IEnumerable<GameObject> addedRoots, IEnumerable<GameObject> excludedRoots, GameObject hoverRoot)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			_highlighted.Clear();
			_highlightedIds.Clear();
			_frameTintIds.Clear();
			if (!IsHighlightsEnabled())
			{
				ClearTints();
				return;
			}
			CollectAndTint(baseRoots, BaseColor);
			CollectAndTint(addedRoots, AddedColor);
			CollectAndTint(excludedRoots, ExcludedColor);
			if ((Object)hoverRoot != (Object)null)
			{
				ApplyRoot(hoverRoot, HoverColor);
			}
			PruneStaleTints();
		}

		internal static void Tick()
		{
		}

		internal static void Clear()
		{
			_highlighted.Clear();
			_highlightedIds.Clear();
			ClearTints();
		}

		private static void CollectAndTint(IEnumerable<GameObject> roots, Color color)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (roots == null)
			{
				return;
			}
			foreach (GameObject root in roots)
			{
				ApplyRoot(root, color);
			}
		}

		private static void ApplyRoot(GameObject root, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)root == (Object)null)
			{
				return;
			}
			WearNTear val = root.GetComponent<WearNTear>();
			if ((Object)val == (Object)null)
			{
				val = root.GetComponentInChildren<WearNTear>();
			}
			GameObject go = (((Object)val != (Object)null) ? ((Component)val).gameObject : root);
			if ((Object)val != (Object)null)
			{
				int instanceID = ((Object)val).GetInstanceID();
				if (_highlightedIds.Add(instanceID))
				{
					_highlighted.Add(val);
				}
			}
			ApplyMaterialTint(go, color);
		}

		private static void ApplyMaterialTint(GameObject go, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)go == (Object)null)
			{
				return;
			}
			try
			{
				MaterialMan instance = MaterialMan.instance;
				if (!((Object)instance == (Object)null))
				{
					instance.SetValue<Color>(go, ShaderProps._EmissionColor, color * 0.22f, false);
					instance.SetValue<Color>(go, ShaderProps._Color, Color.Lerp(Color.white, color, 0.45f), false);
					int instanceID = ((Object)go).GetInstanceID();
					_frameTintIds.Add(instanceID);
					_tintRoots[instanceID] = go;
				}
			}
			catch
			{
			}
		}

		private static void PruneStaleTints()
		{
			if (_activeTintIds.Count > 0)
			{
				List<int> list = null;
				foreach (int activeTintId in _activeTintIds)
				{
					if (!_frameTintIds.Contains(activeTintId))
					{
						if (list == null)
						{
							list = new List<int>();
						}
						list.Add(activeTintId);
					}
				}
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						ResetTracked(list[i]);
					}
				}
			}
			_activeTintIds.Clear();
			foreach (int frameTintId in _frameTintIds)
			{
				_activeTintIds.Add(frameTintId);
			}
		}

		private static void ClearTints()
		{
			if (_activeTintIds.Count == 0 && _tintRoots.Count == 0)
			{
				_frameTintIds.Clear();
				return;
			}
			List<int> list = new List<int>(_activeTintIds);
			for (int i = 0; i < list.Count; i++)
			{
				ResetTracked(list[i]);
			}
			_activeTintIds.Clear();
			_frameTintIds.Clear();
			_tintRoots.Clear();
		}

		private static void ResetTracked(int instanceId)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			if (!_tintRoots.TryGetValue(instanceId, out var value))
			{
				_activeTintIds.Remove(instanceId);
				return;
			}
			_tintRoots.Remove(instanceId);
			_activeTintIds.Remove(instanceId);
			if ((Object)value == (Object)null)
			{
				return;
			}
			try
			{
				MaterialMan instance = MaterialMan.instance;
				if (!((Object)instance == (Object)null))
				{
					instance.ResetValue(value, ShaderProps._Color);
					instance.ResetValue(value, ShaderProps._EmissionColor);
				}
			}
			catch
			{
			}
		}

		private static bool IsHighlightsEnabled()
		{
			if (PluginConfig.ShowHighlights != null)
			{
				return PluginConfig.ShowHighlights.Value;
			}
			return true;
		}
	}
}
namespace SonicWorldTools.Refinement
{
	internal static class RefinementInput
	{
		internal readonly struct FrameInput
		{
			internal readonly bool ModifierHeld;

			internal readonly bool AddDown;

			internal readonly bool ExcludeDown;

			internal readonly bool HasHoverPiece;

			internal readonly Piece HoverPiece;

			internal FrameInput(bool modifierHeld, bool addDown, bool excludeDown, Piece hoverPiece)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Expected O, but got Unknown
				ModifierHeld = modifierHeld;
				AddDown = addDown;
				ExcludeDown = excludeDown;
				HasHoverPiece = (Object)hoverPiece != (Object)null;
				HoverPiece = hoverPiece;
			}
		}

		internal static FrameInput Sample()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			if (ToolRuntime.TextInputBlocked)
			{
				return default(FrameInput);
			}
			KeyCode val = (KeyCode)((PluginConfig.RefineModifierKey != null) ? ((int)PluginConfig.RefineModifierKey.Value) : 0);
			bool flag = (int)val <= 0 || PrecisionAreaPipetteInput.WasRefineModifierHeld();
			Piece hoverPiece = TryGetHoverPiece();
			bool addDown = flag && PrecisionAreaPipetteInput.WasAddPieceDown();
			bool excludeDown = flag && PrecisionAreaPipetteInput.WasExcludePieceDown();
			return new FrameInput(flag, addDown, excludeDown, hoverPiece);
		}

		private static Piece TryGetHoverPiece()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			Player localPlayer = Player.m_localPlayer;
			if ((Object)localPlayer == (Object)null)
			{
				return null;
			}
			return localPlayer.GetHoveringPiece();
		}
	}
	public sealed class SelectionRefinement
	{
		private readonly HashSet<string> _base = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _added = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _excluded = new HashSet<string>(StringComparer.Ordinal);

		public IReadOnlyCollection<string> BaseSelection => _base;

		public IReadOnlyCollection<string> AddedSelection => _added;

		public IReadOnlyCollection<string> ExcludedSelection => _excluded;

		public void ClearAll()
		{
			_base.Clear();
			_added.Clear();
			_excluded.Clear();
		}

		public void ResetRefinement()
		{
			_added.Clear();
			_excluded.Clear();
		}

		public void SetBase(IEnumerable<string> keys)
		{
			_base.Clear();
			_added.Clear();
			_excluded.Clear();
			AddKeys(_base, keys);
		}

		public void ReplaceBase(IEnumerable<string> keys)
		{
			_base.Clear();
			AddKeys(_base, keys);
		}

		public void Add(string key)
		{
			if (!string.IsNullOrEmpty(key))
			{
				_added.Add(key);
				_excluded.Remove(key);
			}
		}

		public void Exclude(string key)
		{
			if (!string.IsNullOrEmpty(key))
			{
				_excluded.Add(key);
				_added.Remove(key);
			}
		}

		public bool ContainsEffective(string key)
		{
			if (string.IsNullOrEmpty(key) || _excluded.Contains(key))
			{
				return false;
			}
			if (!_base.Contains(key))
			{
				return _added.Contains(key);
			}
			return true;
		}

		public HashSet<string> GetEffectiveSelection()
		{
			HashSet<string> hashSet = new HashSet<string>(_base, StringComparer.Ordinal);
			foreach (string item in _added)
			{
				hashSet.Add(item);
			}
			foreach (string item2 in _excluded)
			{
				hashSet.Remove(item2);
			}
			return hashSet;
		}

		public void DropKeys(IEnumerable<string> keys)
		{
			if (keys == null)
			{
				return;
			}
			foreach (string key in keys)
			{
				if (!string.IsNullOrEmpty(key))
				{
					_base.Remove(key);
					_added.Remove(key);
					_excluded.Remove(key);
				}
			}
		}

		public void PruneRefinementsToValid(IEnumerable<string> validKeys)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			AddKeys(hashSet, validKeys);
			PruneSet(_added, hashSet);
			PruneSet(_excluded, hashSet);
		}

		private static void AddKeys(HashSet<string> target, IEnumerable<string> keys)
		{
			if (keys == null)
			{
				return;
			}
			foreach (string key in keys)
			{
				if (!string.IsNullOrEmpty(key))
				{
					target.Add(key);
				}
			}
		}

		private static void PruneSet(HashSet<string> set, HashSet<string> valid)
		{
			if (set.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (string item in set)
			{
				if (!valid.Contains(item))
				{
					list.Add(item);
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				set.Remove(list[i]);
			}
		}
	}
}
namespace SonicWorldTools.Modules.ScatterProps
{
	internal static class ScatterPropsCommand
	{
		private readonly struct SampledPrefab
		{
			internal readonly string Prefab;

			internal readonly Vector3 Scale;

			internal SampledPrefab(string prefab, Vector3 scale)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				Prefab = prefab;
				Scale = scale;
			}
		}

		private sealed class RaycastHitDistanceComparer : IComparer<RaycastHit>
		{
			internal static readonly RaycastHitDistanceComparer Instance = new RaycastHitDistanceComparer();

			public int Compare(RaycastHit left, RaycastHit right)
			{
				return ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance);
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__HandleCommand;
		}

		internal const string CommandName = "sonic_scatter";

		private const float DefaultRadius = 3f;

		private const int ObjectsPerWheelNotch = 1;

		private const string CountDescriptionMarker = "\nAmount per click: ";

		private const float MaxSampleRayDistance = 80f;

		private static RaycastHit[] HitBuffer = (RaycastHit[])(object)new RaycastHit[64];

		private static Piece _countDescriptionPiece;

		private static int _countDescriptionValue = -1;

		private static bool _countDescriptionInitialized;

		private static readonly List<SampledPrefab> Samples = new List<SampledPrefab>();

		private static bool _registered;

		private static int MaximumSamples => Mathf.Clamp(PluginConfig.ScatterMaxSamples?.Value ?? 8, 1, 32);

		private static int TotalObjects => Mathf.Clamp(PluginConfig.ScatterObjectsPerClick?.Value ?? 20, 1, 200);

		private static string SampleLabel
		{
			get
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				ConfigEntry<KeyCode> scatterSampleKey = PluginConfig.ScatterSampleKey;
				return PluginConfig.FormatKeyLabel((KeyCode)((scatterSampleKey == null) ? 325 : ((int)scatterSampleKey.Value)));
			}
		}

		internal static void Register()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__HandleCommand;
				if (obj == null)
				{
					ConsoleEvent val = HandleCommand;
					<>O.<0>__HandleCommand = val;
					obj = (object)val;
				}
				new ConsoleCommand("sonic_scatter", "Sample networked props with the configured Scatter Props key; hold the clear modifier to empty the list.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
		}

		internal unsafe static void Tick()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			bool inBuildMode = ToolRuntime.InBuildMode;
			string name;
			bool flag = ToolRuntime.TryGetSelectedToolName(out name);
			bool flag2 = Object.op_Implicit((Object)(object)localPlayer) && inBuildMode && flag && string.Equals(name, "Scatter Props", StringComparison.OrdinalIgnoreCase);
			bool flag3 = ToolInput.WasMouseButtonDown(2);
			KeyCode val = (KeyCode)((PluginConfig.ScatterSampleKey != null) ? ((int)PluginConfig.ScatterSampleKey.Value) : 0);
			bool flag4 = (int)val != 0 && ToolInput.WasBoundKeyOrMouseDown(val);
			if (flag3 && PluginConfig.IsVerboseLogging())
			{
				bool infinityHammerEquipped = ToolRuntime.InfinityHammerEquipped;
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("[Scatter] MMB gate: player=" + (Object.op_Implicit((Object)(object)localPlayer) ? "local" : "none") + " buildMode=" + inBuildMode + " IHEquipped=" + infinityHammerEquipped + " selected='" + (flag ? name : "<none>") + "' active=" + flag2 + " textBlocked=" + ToolRuntime.TextInputBlocked + " sampleKey=" + ((object)(*(KeyCode*)(&val))/*cast due to .constrained prefix*/).ToString() + " sampleKeyDown=" + flag4 + "."));
				}
			}
			if (!flag2)
			{
				_countDescriptionPiece = null;
				_countDescriptionValue = -1;
				_countDescriptionInitialized = false;
			}
			else
			{
				if (ToolRuntime.TextInputBlocked)
				{
					return;
				}
				float mouseScrollY = ToolInput.MouseScrollY;
				if (Mathf.Abs(mouseScrollY) > 0.01f && ToolInput.WasModifierHeld((KeyCode)308))
				{
					int num = ((mouseScrollY > 0f) ? 1 : (-1));
					int num2 = Mathf.Clamp(PluginConfig.ScatterObjectsPerClick.Value + num, 1, 200);
					if (num2 != PluginConfig.ScatterObjectsPerClick.Value)
					{
						PluginConfig.ScatterObjectsPerClick.Value = num2;
						NotifyCenter("Scatter amount: " + num2 + " per click.");
					}
					RefreshCountDescription();
					return;
				}
				if (!_countDescriptionInitialized || _countDescriptionValue != TotalObjects)
				{
					RefreshCountDescription();
				}
				if (flag4)
				{
					string prefab;
					Vector3 scale;
					if ((int)PluginConfig.ScatterClearModifier.Value != 0 && ToolInput.WasModifierHeld(PluginConfig.ScatterClearModifier.Value))
					{
						Samples.Clear();
						Notify("Scatter sample list cleared.");
					}
					else if (!TryGetAimedPrefab(localPlayer, out prefab, out scale))
					{
						Notify("Aim at a networked prop and press " + SampleLabel + " to sample it.");
					}
					else
					{
						ToggleSample(prefab, scale);
					}
				}
			}
		}

		private static void RefreshCountDescription()
		{
			if (!InfinityHammerBridge.TryGetActiveToolSelectionPiece("Scatter Props", out var piece) || !Object.op_Implicit((Object)(object)piece))
			{
				_countDescriptionPiece = null;
				_countDescriptionValue = -1;
				_countDescriptionInitialized = false;
			}
			else
			{
				if ((Object)(object)piece == (Object)(object)_countDescriptionPiece && _countDescriptionValue == TotalObjects)
				{
					return;
				}
				string text = piece.m_description ?? string.Empty;
				int num = text.IndexOf("\nAmount per click: ", StringComparison.Ordinal);
				if (num >= 0)
				{
					text = text.Substring(0, num);
				}
				piece.m_description = text + "\nAmount per click: " + TotalObjects;
				_countDescriptionPiece = piece;
				_countDescriptionValue = TotalObjects;
				_countDescriptionInitialized = true;
				try
				{
					MethodInfo method = typeof(Hud).GetMethod("SetupPieceInfo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Piece) }, null);
					if (Object.op_Implicit((Object)(object)Hud.instance) && method != null)
					{
						method.Invoke(Hud.instance, new object[1] { piece });
					}
				}
				catch (Exception ex)
				{
					LogVerbose("Could not refresh Scatter Props count in the IH tool panel: " + ex.Message);
				}
			}
		}

		internal static bool ShouldSuppressMiddleClick(Player player)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && ToolRuntime.InBuildMode && !ToolRuntime.TextInputBlocked && (int)PluginConfig.ScatterSampleKey.Value == 325 && ToolInput.WasMouseButtonHeld(2) && ToolRuntime.TryGetSelectedToolName(out var name))
			{
				return string.Equals(name, "Scatter Props", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool TryGetAimedPrefab(Player player, out string prefab, out Vector3 scale)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: 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_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_046d: Unknown result type (might be due to invalid IL or missing references)
			prefab = null;
			scale = Vector3.one;
			Piece hoveringPiece = player.GetHoveringPiece();
			bool flag = PluginConfig.IsVerboseLogging();
			if (flag)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("[Scatter] Sample hover target: " + (Object.op_Implicit((Object)(object)hoveringPiece) ? GetHierarchyPath(((Component)hoveringPiece).transform) : "<none>") + "."));
				}
			}
			if (Object.op_Implicit((Object)(object)hoveringPiece) && !IsOwnedByLocalPlayer(((Component)hoveringPiece).gameObject, player))
			{
				ZNetView val = ((Component)hoveringPiece).GetComponent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)val))
				{
					val = ((Component)hoveringPiece).GetComponentInParent<ZNetView>();
				}
				if (Object.op_Implicit((Object)(object)val) && !IsOwnedByLocalPlayer(((Component)val).gameObject, player))
				{
					prefab = ResolvePrefabId(val);
					if (!string.IsNullOrWhiteSpace(prefab))
					{
						scale = ((Component)hoveringPiece).transform.lossyScale;
						LogVerbose("[Scatter] Sample target from Player.GetHoveringPiece(): '" + ((Object)hoveringPiece).name + "' resolved to prefab '" + prefab + "' at scale " + FormatScale(scale) + ".");
						return true;
					}
				}
			}
			GameCamera instance = GameCamera.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return false;
			}
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(((Component)instance).transform.position, ((Component)instance).transform.forward);
			int num = -5;
			int num2;
			for (num2 = Physics.RaycastNonAlloc(val2, HitBuffer, 80f, num, (QueryTriggerInteraction)1); num2 == HitBuffer.Length; num2 = Physics.RaycastNonAlloc(val2, HitBuffer, 80f, num, (QueryTriggerInteraction)1))
			{
				HitBuffer = (RaycastHit[])(object)new RaycastHit[HitBuffer.Length * 2];
			}
			Array.Sort(HitBuffer, 0, num2, RaycastHitDistanceComparer.Instance);
			if (flag)
			{
				ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogInfo((object)("[Scatter] Sample ray origin=" + FormatVector(((Ray)(ref val2)).origin) + " direction=" + FormatVector(((Ray)(ref val2)).direction) + " mask=" + num + " hitCount=" + num2 + "."));
				}
				for (int i = 0; i < num2; i++)
				{
					RaycastHit val3 = HitBuffer[i];
					if (Object.op_Implicit((Object)(object)((RaycastHit)(ref val3)).collider))
					{
						ZNetView componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<ZNetView>();
						ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
						if (modLogger3 != null)
						{
							modLogger3.LogInfo((object)("[Scatter] RayHit[" + i + "] distance=" + ((RaycastHit)(ref val3)).distance.ToString("F2", CultureInfo.InvariantCulture) + " point=" + FormatVector(((RaycastHit)(ref val3)).point) + " layer='" + LayerMask.LayerToName(((Component)((RaycastHit)(ref val3)).collider).gameObject.layer) + "' collider='" + GetHierarchyPath(((Component)((RaycastHit)(ref val3)).collider).transform) + "' prefab='" + Utils.GetPrefabName(((Component)((RaycastHit)(ref val3)).collider).gameObject) + "' znetview='" + (Object.op_Implicit((Object)(object)componentInParent) ? GetHierarchyPath(((Component)componentInParent).transform) : "<none>") + "'."));
						}
					}
				}
			}
			for (int j = 0; j < num2; j++)
			{
				RaycastHit val4 = HitBuffer[j];
				if (!Object.op_Implicit((Object)(object)((RaycastHit)(ref val4)).collider) || IsOwnedByLocalPlayer(((Component)((RaycastHit)(ref val4)).collider).gameObject, player))
				{
					continue;
				}
				ZNetView componentInParent2 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)componentInParent2))
				{
					LogVerbose("[Scatter] Sample ray stopped at nearest non-player collider '" + ((Object)((RaycastHit)(ref val4)).collider).name + "' (" + Utils.GetPrefabName(((Component)((RaycastHit)(ref val4)).collider).gameObject) + ") without a ZNetView; farther objects were not considered.");
					return false;
				}
				if (!IsOwnedByLocalPlayer(((Component)componentInParent2).gameObject, player))
				{
					prefab = ResolvePrefabId(componentInParent2);
					if (!string.IsNullOrWhiteSpace(prefab))
					{
						scale = ((Component)componentInParent2).transform.lossyScale;
						LogVerbose("[Scatter] Sample target '" + ((Object)((RaycastHit)(ref val4)).collider).name + "' resolved to prefab '" + prefab + "' at scale " + FormatScale(scale) + ".");
						return true;
					}
					LogVerbose("[Scatter] Sample ray stopped at nearest networked collider '" + ((Object)((RaycastHit)(ref val4)).collider).name + "' because its prefab could not be resolved; farther objects were not considered.");
					return false;
				}
			}
			return false;
		}

		private static string GetHierarchyPath(Transform transform)
		{
			string text = string.Empty;
			while (Object.op_Implicit((Object)(object)transform))
			{
				text = (string.IsNullOrEmpty(text) ? ((Object)transform).name : (((Object)transform).name + "/" + text));
				transform = transform.parent;
			}
			return text;
		}

		private static bool IsOwnedByLocalPlayer(GameObject target, Player player)
		{
			if (!Object.op_Implicit((Object)(object)target) || !Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			Transform val = target.transform;
			while (Object.op_Implicit((Object)(object)val))
			{
				if ((Object)(object)val == (Object)(object)((Component)player).transform)
				{
					return true;
				}
				if ((Object)(object)((Component)val).GetComponent<Player>() == (Object)(object)player)
				{
					return true;
				}
				if ((Object)(object)((Component)val).GetComponent<Character>() == (Object)(object)player)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static string ResolvePrefabId(ZNetView view)
		{
			try
			{
				ZDO zDO = view.GetZDO();
				if (zDO != null && Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab());
					if (Object.op_Implicit((Object)(object)prefab))
					{
						return Utils.GetPrefabName(prefab);
					}
				}
			}
			catch (Exception ex)
			{
				LogVerbose("[Scatter] ZDO prefab lookup fell back to object name: " + ex.Message);
			}
			return Utils.GetPrefabName(((Component)view).gameObject);
		}

		private static void HandleCommand(ConsoleEventArgs args)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 2)
			{
				Notify(args, "Scatter Props: click to scatter (" + TotalObjects + " props); Alt+wheel changes amount; radius wheel follows Infinity Hammer's bind; " + SampleLabel + " samples; " + PluginConfig.FormatKeyLabel(PluginConfig.ScatterClearModifier.Value) + "+" + SampleLabel + " clears.");
				return;
			}
			switch (args[1].ToLowerInvariant())
			{
			case "sample":
				Sample(args);
				break;
			case "clear":
				Samples.Clear();
				Notify(args, "Scatter samples cleared.");
				break;
			case "place":
				Place(args);
				break;
			default:
				Notify(args, "Scatter Props: unknown action '" + args[1] + "'.");
				break;
			}
		}

		private static void Sample(ConsoleEventArgs args)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 3 || string.IsNullOrWhiteSpace(args[2]))
			{
				Notify(args, "Aim at a prop to sample it.");
			}
			else
			{
				ToggleSample(args[2], Vector3.one);
			}
		}

		private static void ToggleSample(string prefab, Vector3 scale)
		{
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			int num = Samples.FindIndex((SampledPrefab s) => string.Equals(s.Prefab, prefab, StringComparison.OrdinalIgnoreCase));
			if (num >= 0)
			{
				Samples.RemoveAt(num);
				LogVerbose("[Scatter] Removed sample '" + prefab + "'; palette now has " + Samples.Count + " sample(s).");
				Notify("Removed " + prefab + " from Scatter Props (" + Samples.Count + " sampled).");
			}
			else if (Samples.Count >= MaximumSamples)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogWarning((object)("[Scatter] Could not add sample '" + prefab + "'; palette is at its " + MaximumSamples + " type limit."));
				}
				Notify("Scatter Props holds up to " + MaximumSamples + " prop types. Press " + SampleLabel + " on a sampled prop to remove it.");
			}
			else
			{
				Samples.Add(new SampledPrefab(prefab, scale));
				LogVerbose("[Scatter] Added sample '" + prefab + "' at scale " + FormatScale(scale) + "; palette now has " + Samples.Count + " sample(s).");
				Notify("Sampled " + prefab + " at scale " + FormatScale(scale) + " (" + Samples.Count + "/" + MaximumSamples + "). Press " + SampleLabel + " again to remove.");
			}
		}

		private static void Place(ConsoleEventArgs args)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 3)
			{
				Notify(args, "Scatter Props needs a target position.");
				return;
			}
			string[] array = args[2].Split(',');
			if (array.Length != 2 || !TryCoordinate(array[0], out var result) || !TryCoordinate(array[1], out var result2))
			{
				Notify(args, "Scatter Props received an invalid target position.");
				return;
			}
			float radius = ReadRadius(args);
			if (Samples.Count == 0)
			{
				Player localPlayer = Player.m_localPlayer;
				if (!Object.op_Implicit((Object)(object)localPlayer) || !TryGetAimedPrefab(localPlayer, out var prefab, out var scale))
				{
					Notify(args, "Sample props with " + SampleLabel + ", or aim at a prop before clicking to scatter it.");
				}
				else
				{
					Spawn(args.Context, prefab, scale, TotalObjects, result, result2, radius);
				}
				return;
			}
			int[] array2 = new int[Samples.Count];
			for (int i = 0; i < TotalObjects; i++)
			{
				array2[Random.Range(0, Samples.Count)]++;
			}
			for (int j = 0; j < Samples.Count; j++)
			{
				int num = array2[j];
				if (num > 0)
				{
					Spawn(args.Context, Samples[j].Prefab, Samples[j].Scale, num, result, result2, radius);
				}
			}
		}

		private static void Spawn(Terminal context, string prefab, Vector3 scale, int amount, float x, float z, float radius)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			string text = "spawn_object " + prefab + " amount=" + amount.ToString(CultureInfo.InvariantCulture) + " from=" + x.ToString(CultureInfo.InvariantCulture) + "," + z.ToString(CultureInfo.InvariantCulture) + " radius=" + radius.ToString(CultureInfo.InvariantCulture) + " rot=0-360 scale=" + FormatScale(scale);
			LogVerbose("[Scatter] Running: " + text);
			context.TryRunCommand(text, false, false);
		}

		private static void LogVerbose(string message)
		{
			if (PluginConfig.IsVerboseLogging())
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)message);
				}
			}
		}

		private static string FormatScale(Vector3 scale)
		{
			return scale.x.ToString(CultureInfo.InvariantCulture) + "," + scale.z.ToString(CultureInfo.InvariantCulture) + "," + scale.y.ToString(CultureInfo.InvariantCulture);
		}

		private static string FormatVector(Vector3 value)
		{
			//IL_000a: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			return string.Format(CultureInfo.InvariantCulture, "({0:0.##},{1:0.##},{2:0.##})", value.x, value.y, value.z);
		}

		private static float ReadRadius(ConsoleEventArgs args)
		{
			for (int i = 3; i < args.Length; i++)
			{
				if (args[i].StartsWith("radius=", StringComparison.OrdinalIgnoreCase) && TryCoordinate(args[i].Substring("radius=".Length), out var result) && result > 0f)
				{
					return result;
				}
			}
			return 3f;
		}

		private static bool TryCoordinate(string value, out float result)
		{
			if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) && !float.IsNaN(result))
			{
				return !float.IsInfinity(result);
			}
			return false;
		}

		private static void Notify(ConsoleEventArgs args, string message)
		{
			args.Context.AddString(message);
			Notify(message);
		}

		private static void Notify(string message)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)1, message, 0, (Sprite)null, false);
			}
		}

		private static void NotifyCenter(string message)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
	}
}
namespace SonicWorldTools.Modules.PrecisionAreaPipette
{
	internal static class PrecisionAreaPipetteCommand
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__3_0;

			public static ConsoleEvent <>9__3_1;

			internal void <Register>b__3_0(ConsoleEventArgs args)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("[PipetteCommand] 'sonic_precision_pipette' invoked; enabled=" + PluginConfig.IsPrecisionAreaPipetteEnabled() + " controller=" + (PrecisionAreaPipetteController.Instance != null) + "."));
				}
				if (!PluginConfig.IsPrecisionAreaPipetteEnabled())
				{
					ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)"SonicWorldTools / Precision Area Pipette disabled; ignored.");
					}
					return;
				}
				PrecisionAreaPipetteController instance = PrecisionAreaPipetteController.Instance;
				if (instance == null)
				{
					ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
					if (modLogger3 != null)
					{
						modLogger3.LogWarning((object)"Precision Area Pipette controller unavailable (Infinity Hammer required).");
					}
				}
				else
				{
					args.Context.TryRunCommand("tool Precision Select", false, false);
				}
			}

			internal void <Register>b__3_1(ConsoleEventArgs _)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("[PipetteCommand] 'sonic_precision_pipette_pickup' invoked; enabled=" + PluginConfig.IsPrecisionAreaPipetteEnabled() + " controller=" + (PrecisionAreaPipetteController.Instance != null) + "."));
				}
				if (!PluginConfig.IsPrecisionAreaPipetteEnabled())
				{
					ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)"SonicWorldTools / Precision Area Pipette disabled; ignored.");
					}
					return;
				}
				PrecisionAreaPipetteController instance = PrecisionAreaPipetteController.Instance;
				if (instance == null)
				{
					ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
					if (modLogger3 != null)
					{
						modLogger3.LogWarning((object)"Precision Area Pipette controller unavailable (Infinity Hammer required).");
					}
				}
				else
				{
					instance.TryPickUpSelection();
				}
			}
		}

		internal const string CommandName = "sonic_precision_pipette";

		internal const string PickUpCommandName = "sonic_precision_pipette_pickup";

		private static bool _registered;

		internal static void Register()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_007a: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			_registered = true;
			object obj = <>c.<>9__3_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
					if (modLogger != null)
					{
						modLogger.LogInfo((object)("[PipetteCommand] 'sonic_precision_pipette' invoked; enabled=" + PluginConfig.IsPrecisionAreaPipetteEnabled() + " controller=" + (PrecisionAreaPipetteController.Instance != null) + "."));
					}
					if (!PluginConfig.IsPrecisionAreaPipetteEnabled())
					{
						ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
						if (modLogger2 != null)
						{
							modLogger2.LogInfo((object)"SonicWorldTools / Precision Area Pipette disabled; ignored.");
						}
					}
					else
					{
						PrecisionAreaPipetteController instance = PrecisionAreaPipetteController.Instance;
						if (instance == null)
						{
							ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
							if (modLogger3 != null)
							{
								modLogger3.LogWarning((object)"Precision Area Pipette controller unavailable (Infinity Hammer required).");
							}
						}
						else
						{
							args.Context.TryRunCommand("tool Precision Select", false, false);
						}
					}
				};
				<>c.<>9__3_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("sonic_precision_pipette", "SonicWorldTools: activate Precision Area Pipette", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__3_1;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate
				{
					ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
					if (modLogger != null)
					{
						modLogger.LogInfo((object)("[PipetteCommand] 'sonic_precision_pipette_pickup' invoked; enabled=" + PluginConfig.IsPrecisionAreaPipetteEnabled() + " controller=" + (PrecisionAreaPipetteController.Instance != null) + "."));
					}
					if (!PluginConfig.IsPrecisionAreaPipetteEnabled())
					{
						ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
						if (modLogger2 != null)
						{
							modLogger2.LogInfo((object)"SonicWorldTools / Precision Area Pipette disabled; ignored.");
						}
					}
					else
					{
						PrecisionAreaPipetteController instance = PrecisionAreaPipetteController.Instance;
						if (instance == null)
						{
							ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
							if (modLogger3 != null)
							{
								modLogger3.LogWarning((object)"Precision Area Pipette controller unavailable (Infinity Hammer required).");
							}
						}
						else
						{
							instance.TryPickUpSelection();
						}
					}
				};
				<>c.<>9__3_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("sonic_precision_pipette_pickup", "SonicWorldTools: hand locked selection to Infinity Hammer (copy ghost)", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
	internal enum PrecisionAreaPipettePhase
	{
		Idle,
		AwaitingCornerA,
		AwaitingCornerB,
		AwaitingCornerC,
		Locked
	}
	internal sealed class PrecisionAreaPipetteController : IToolSession
	{
		private const string SessionKey = "precision-select";

		private const float CandidateRefreshInterval = 0.05f;

		private const float QueryPadding = 2f;

		private static readonly List<Piece> PieceBuffer = new List<Piece>(256);

		private static readonly List<SelectionCandidate> CandidateBuffer = new List<SelectionCandidate>(256);

		private readonly SelectionState _state = new SelectionState();

		private readonly Dictionary<string, GameObject> _keyToRoot = new Dictionary<string, GameObject>(256);

		private Vector3? _cornerA;

		private Vector3? _cornerB;

		private Vector3? _cornerC;

		private float _candidateTimer;

		private string _hudLine = string.Empty;

		private string _hudNotice = string.Empty;

		private float _hudNoticeExpiresAt;

		private GUIStyle _hudTitleStyle;

		private GUIStyle _hudBodyStyle;

		private Texture2D _hudPanelTex;

		private GameObject _hoverRoot;

		private bool _awaitPrimaryRelease;

		private int _placeModeGraceFrames;

		private int _notInPlaceFrames;

		private int _suppressPauseMenuFrames;

		string IToolSession.SessionId => "precision-select";

		bool IToolSession.IsSessionActive => IsActive;

		internal static PrecisionAreaPipetteController Instance { get; private set; }

		internal bool IsActive { get; private set; }

		internal bool HasCancellableSelection
		{
			get
			{
				if (!IsActive)
				{
					return false;
				}
				if (Phase != PrecisionAreaPipettePhase.AwaitingCornerA)
				{
					return true;
				}
				return _cornerA.HasValue;
			}
		}

		internal bool ShouldSuppressPauseMenu
		{
			get
			{
				if (!IsActive)
				{
					return false;
				}
				if (_suppressPauseMenuFrames > 0)
				{
					return true;
				}
				if (HasCancellableSelection)
				{
					return ToolInput.WasKeyDown((KeyCode)27);
				}
				return false;
			}
		}

		internal PrecisionAreaPipettePhase Phase { get; private set; }

		internal SelectionState State => _state;

		internal Vector3? CornerA => _cornerA;

		internal Vector3? CornerB => _cornerB;

		internal Vector3? CornerC => _cornerC;

		void IToolSession.StopSession(string reason)
		{
			Deactivate(reason);
		}

		bool IToolSession.ShouldSuppressPlayerAction(ToolPlayerAction action, Player player)
		{
			if (!IsActive || (Object)(object)player != (Object)(object)Player.m_localPlayer || !ToolRuntime.InBuildMode || !ToolRuntime.InfinityHammerEquipped)
			{
				return false;
			}
			switch (action)
			{
			case ToolPlayerAction.PauseMenu:
				return ShouldSuppressPauseMenu;
			default:
				return action == ToolPlayerAction.Interact;
			case ToolPlayerAction.BuildPlacement:
			case ToolPlayerAction.Attack:
			case ToolPlayerAction.Remove:
				return true;
			}
		}

		internal IReadOnlyCollection<string> GetEffectiveSelectionKeys()
		{
			return _state.GetEffectiveSelection();
		}

		internal PrecisionAreaPipetteController()
		{
			Instance = this;
			Phase = PrecisionAreaPipettePhase.Idle;
		}

		internal void Activate()
		{
			Activate("command");
		}

		internal void Activate(string source)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			if (IsActive)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("Precision Area Pipette Activate(" + source + ") ignored — already active phase=" + Phase.ToString() + "."));
				}
				return;
			}
			if (!ToolSessionCoordinator.Acquire(this))
			{
				ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogWarning((object)"Precision Select could not acquire the world-input session.");
				}
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			bool flag = (Object)localPlayer != (Object)null && ((Character)localPlayer).InPlaceMode();
			bool flag2 = false;
			try
			{
				flag2 = Hud.IsPieceSelectionVisible();
			}
			catch
			{
			}
			IsActive = true;
			_hudNotice = string.Empty;
			Phase = PrecisionAreaPipettePhase.AwaitingCornerA;
			_awaitPrimaryRelease = true;
			_placeModeGraceFrames = 180;
			_notInPlaceFrames = 0;
			TryHidePieceSelectionUi();
			ClearSelectionVisuals();
			UpdateHudText();
			ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
			if (modLogger3 != null)
			{
				modLogger3.LogInfo((object)("Precision Area Pipette active via " + source + " place=" + flag + " pieceMenu=" + flag2 + " grace=" + _placeModeGraceFrames + " awaitRelease=" + _awaitPrimaryRelease + "."));
			}
		}

		internal void Deactivate()
		{
			Deactivate("unspecified");
		}

		internal void Deactivate(string source)
		{
			if (IsActive || Phase != PrecisionAreaPipettePhase.Idle)
			{
				PrecisionAreaPipettePhase phase = Phase;
				bool hasValue = _cornerA.HasValue;
				bool hasValue2 = _cornerB.HasValue;
				bool hasValue3 = _cornerC.HasValue;
				IsActive = false;
				ToolSessionCoordinator.Release(this);
				_hudNotice = string.Empty;
				Phase = PrecisionAreaPipettePhase.Idle;
				_awaitPrimaryRelease = false;
				_placeModeGraceFrames = 0;
				_notInPlaceFrames = 0;
				ClearSelectionVisuals();
				_hudLine = string.Empty;
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("Precision Area Pipette inactive (" + source + ") wasPhase=" + phase.ToString() + " cornerA=" + hasValue + " cornerB=" + hasValue2 + " cornerC=" + hasValue3 + "."));
				}
			}
		}

		internal bool TryPickUpSelection()
		{
			if (!IsActive || Phase != PrecisionAreaPipettePhase.Locked)
			{
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"[SonicWorldTools] Pick-up ignored — lock a selection first.");
				}
				return false;
			}
			HashSet<string> effectiveSelection = _state.GetEffectiveSelection();
			if (effectiveSelection.Count == 0)
			{
				_hudLine = "Precision Area Pipette\nNothing selected to pick up.";
				ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogWarning((object)"[SonicWorldTools] Pick-up ignored — effective selection is empty.");
				}
				return false;
			}
			if (PluginConfig.IsDryRunSelection())
			{
				ManualLogSource modLogger3 = SonicWorldToolsPlugin.ModLogger;
				if (modLogger3 != null)
				{
					modLogger3.LogInfo((object)("[SonicWorldTools] DryRunSelection: would hand off " + effectiveSelection.Count + " object(s); skipping Infinity Hammer hand-off."));
				}
				LogVerbose("Base selection = " + _state.BaseSelection.Count);
				LogVerbose("Added manually = " + _state.AddedSelection.Count);
				LogVerbose("Excluded manually = " + _state.ExcludedSelection.Count);
				LogVerbose("Effective selection = " + effectiveSelection.Count);
				_hudLine = "Precision Area Pipette\nDry-run pick-up (" + effectiveSelection.Count + " objects) — no hand-off.";
				return false;
			}
			List<ZNetView> resolvedViews;
			List<string> droppedKeys;
			string userMessage;
			bool flag = InfinityHammerBridge.TryHandOffCopy(effectiveSelection, out resolvedViews, out droppedKeys, out userMessage);
			if (droppedKeys.Count > 0)
			{
				_state.DropKeys(droppedKeys);
				for (int i = 0; i < droppedKeys.Count; i++)
				{
					_keyToRoot.Remove(droppedKeys[i]);
				}
				LogVerbose("Dropped unloaded before hand-off = " + droppedKeys.Count + "; Effective selection = " + _state.GetEffectiveSelection().Count);
			}
			if (!flag)
			{
				if (!string.IsNullOrEmpty(userMessage))
				{
					_hudLine = userMessage;
					_hudNotice = userMessage;
					_hudNoticeExpiresAt = Time.unscaledTime + 6f;
				}
				if (string.IsNullOrEmpty(userMessage))
				{
					UpdateHudText();
				}
				return false;
			}
			LogVerbose("Hand-off views = " + resolvedViews.Count);
			Deactivate();
			return true;
		}

		internal void Tick()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			if (!IsActive)
			{
				_suppressPauseMenuFrames = 0;
				return;
			}
			if (_suppressPauseMenuFrames > 0)
			{
				_suppressPauseMenuFrames--;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)localPlayer == (Object)null)
			{
				return;
			}
			if (!((Character)localPlayer).InPlaceMode())
			{
				if (_placeModeGraceFrames > 0)
				{
					_placeModeGraceFrames--;
					DrawPreviewWithAim(null);
					UpdateHudText();
					return;
				}
				_notInPlaceFrames++;
				int num = ((Phase == PrecisionAreaPipettePhase.Locked) ? 90 : 20);
				if (_notInPlaceFrames < num)
				{
					DrawPreviewWithAim(null);
					UpdateHudText();
				}
				else
				{
					Deactivate("left-place-mode");
				}
				return;
			}
			_notInPlaceFrames = 0;
			if (_placeModeGraceFrames > 0)
			{
				_placeModeGraceFrames--;
			}
			if (_awaitPrimaryRelease)
			{
				if (PrecisionAreaPipetteInput.IsPrimaryCornerHeld())
				{
					DrawPreviewWithAim(null);
					UpdateHudText();
					return;
				}
				_awaitPrimaryRelease = false;
			}
			PrecisionAreaPipetteInput.FrameInput frameInput = PrecisionAreaPipetteInput.Sample();
			RefinementInput.FrameInput refine = default(RefinementInput.FrameInput);
			if (Phase == PrecisionAreaPipettePhase.Locked)
			{
				refine = RefinementInput.Sample();
			}
			_hoverRoot = ResolveHoverRoot(refine);
			if (frameInput.CancelDown)
			{
				if (HasCancellableSelection)
				{
					_suppressPauseMenuFrames = 3;
				}
				CancelSelection();
				return;
			}
			if (frameInput.ResetCornersDown)
			{
				ResetCorners();
				return;
			}
			if (Phase == PrecisionAreaPipettePhase.Locked && frameInput.ResetRefinementDown)
			{
				ResetRefinement();
				return;
			}
			if (Phase == PrecisionAreaPipettePhase.Locked && frameInput.PickUpDown)
			{
				TryPickUpSelection();
				return;
			}
			if (Phase == PrecisionAreaPipettePhase.Locked && PrecisionAreaPipetteInput.WasPermanentDeleteSelectionDown())
			{
				TryRemoveLockedSelection(SelectionRemoval.RemovalKind.PermanentDelete);
				return;
			}
			if (Phase == PrecisionAreaPipettePhase.Locked && PrecisionAreaPipetteInput.WasDismantleSelectionDown())
			{
				TryRemoveLockedSelection(SelectionRemoval.RemovalKind.DismantleRefunds);
				return;
			}
			bool flag = false;
			if (Phase == PrecisionAreaPipettePhase.Locked)
			{
				flag = TryHandleRefinement(refine);
			}
			if (!flag && frameInput.SetCornerDown && frameInput.HasAimPoint)
			{
				if (Phase == PrecisionAreaPipettePhase.Locked)
				{
					_hudLine = "Precision Area Pipette" + FormatCountDimsLine() + "\nLocked — Home resets corners, Esc cancels\n(LMB does not resize)";
				}
				else
				{
					OnSetCorner(frameInput.AimPoint);
				}
			}
			else if (!flag && frameInput.HasAimPoint)
			{
				if (Phase == PrecisionAreaPipettePhase.AwaitingCornerB)
				{
					_cornerB = frameInput.AimPoint;
				}
				else if (Phase == PrecisionAreaPipettePhase.AwaitingCornerC)
				{
					_cornerC = frameInput.AimPoint;
				}
			}
			_candidateTimer -= Time.unscaledDeltaTime;
			bool flag2 = _candidateTimer <= 0f;
			if (flag2)
			{
				_candidateTimer = 0.05f;
			}
			if (Phase == PrecisionAreaPipettePhase.AwaitingCornerC && _cornerA.HasValue && _cornerB.HasValue && _cornerC.HasValue && flag2)
			{
				RecomputeFromThreeCorners(_cornerA.Value, _cornerB.Value, _cornerC.Value);
			}
			else if (Phase == PrecisionAreaPipettePhase.Locked && _state.HasCorners && flag2)
			{
				RefreshCandidateMap(_state.Volume);
			}
			Vector3? aimPoint = (frameInput.HasAimPoint ? new Vector3?(frameInput.AimPoint) : ((Vector3?)null));
			PrecisionAreaPipettePreview.Draw(Phase, _cornerA, _cornerB, _cornerC, _state, _keyToRoot, _hoverRoot, aimPoint);
			PrecisionAreaPipettePreview.TickHighlights();
			UpdateHudText();
		}

		private void DrawPreviewWithAim(GameObject hoverRoot)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			Vector3? aimPoint = null;
			if (PrecisionAreaPipetteInput.TryGetAimPoint(out var worldPoint))
			{
				aimPoint = worldPoint;
			}
			PrecisionAreaPipettePreview.Draw(Phase, _cornerA, _cornerB, _cornerC, _state, _keyToRoot, hoverRoot, aimPoint);
			PrecisionAreaPipettePreview.TickHighlights();
		}

		internal void DrawHud()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			if (IsActive && !string.IsNullOrEmpty(_hudLine))
			{
				EnsureHudStyles();
				string text = _hudLine;
				if (!string.IsNullOrEmpty(_hudNotice) && Time.unscaledTime < _hudNoticeExpiresAt)
				{
					text = text + "\n" + _hudNotice;
				}
				else
				{
					_hudNotice = string.Empty;
				}
				string[] array = text.Split('\n');
				float num = _hudTitleStyle.CalcHeight(new GUIContent(array[0]), 396f);
				float num2 = 0f;
				for (int i = 1; i < array.Length; i++)
				{
					num2 += _hudBodyStyle.CalcHeight(new GUIContent(array[i]), 396f) + 2f;
				}
				float num3 = 24f + num + num2 + 4f;
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(14f, 14f, 420f, num3);
				Color color = GUI.color;
				GUI.color = new Color(0.04f, 0.06f, 0.09f, 0.82f);
				GUI.DrawTexture(val, (Texture)(object)_hudPanelTex);
				GUI.color = color;
				float num4 = ((Rect)(ref val)).y + 12f;
				GUI.Label(new Rect(((Rect)(ref val)).x + 12f, num4, ((Rect)(ref val)).width - 24f, num), array[0], _hudTitleStyle);
				num4 += num + 4f;
				for (int j = 1; j < array.Length; j++)
				{
					float num5 = _hudBodyStyle.CalcHeight(new GUIContent(array[j]), 396f);
					GUI.Label(new Rect(((Rect)(ref val)).x + 12f, num4, ((Rect)(ref val)).width - 24f, num5), array[j], _hudBodyStyle);
					num4 += num5 + 2f;
				}
			}
		}

		private void EnsureHudStyles()
		{
			//IL_002c: 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_0039: 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_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_0070: Expected O, but got Unknown
			//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_0090: 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_009e: 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_00c7: Expected O, but got Unknown
			if ((Object)(object)_hudPanelTex == (Object)null)
			{
				_hudPanelTex = Texture2D.whiteTexture;
			}
			if (_hudTitleStyle == null)
			{
				GUIStyle val = new GUIStyle(GUI.skin.label)
				{
					fontSize = 17,
					fontStyle = (FontStyle)1,
					wordWrap = true
				};
				val.normal.textColor = new Color(0.95f, 0.97f, 1f, 1f);
				_hudTitleStyle = val;
			}
			if (_hudBodyStyle == null)
			{
				GUIStyle val2 = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14,
					fontStyle = (FontStyle)0,
					wordWrap = true
				};
				val2.normal.textColor = new Color(0.82f, 0.88f, 0.94f, 1f);
				_hudBodyStyle = val2;
			}
		}

		internal static void ClearInstance()
		{
			if (Instance != null)
			{
				Instance.Deactivate();
				PrecisionAreaPipettePreview.Dispose();
				Instance = null;
			}
		}

		private bool TryHandleRefinement(RefinementInput.FrameInput refine)
		{
			if (!refine.AddDown && !refine.ExcludeDown)
			{
				return false;
			}
			if (!refine.HasHoverPiece)
			{
				return true;
			}
			if (!TryGetPieceKey(refine.HoverPiece, out var key, out var root))
			{
				return true;
			}
			_keyToRoot[key] = root;
			if (refine.AddDown)
			{
				if (_state.ContainsEffective(new SelectionObjectId(key)))
				{
					LogVerbose("Refine add ignored — already effective: " + key);
					return true;
				}
				_state.Add(new SelectionObjectId(key));
				LogVerbose("Added manually = " + _state.AddedSelection.Count + "; Effective selection = " + _state.GetEffectiveSelection().Count);
				return true;
			}
			if (!_state.ContainsEffective(new SelectionObjectId(key)))
			{
				LogVerbose("Refine exclude ignored — not effective: " + key);
				return true;
			}
			_state.Exclude(new SelectionObjectId(key));
			LogVerbose("Excluded manually = " + _state.ExcludedSelection.Count + "; Effective selection = " + _state.GetEffectiveSelection().Count);
			return true;
		}

		private GameObject ResolveHoverRoot(RefinementInput.FrameInput refine)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (Phase != PrecisionAreaPipettePhase.Locked && Phase != PrecisionAreaPipettePhase.AwaitingCornerC)
			{
				return null;
			}
			Piece val = null;
			if (refine.HasHoverPiece)
			{
				val = refine.HoverPiece;
			}
			else
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)localPlayer != (Object)null)
				{
					val = localPlayer.GetHoveringPiece();
				}
			}
			if ((Object)val == (Object)null)
			{
				return null;
			}
			return ((Component)val).gameObject;
		}

		private void OnSetCorner(Vector3 aimPoint)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			_placeModeGraceFrames = Math.Max(_placeModeGraceFrames, 180);
			_notInPlaceFrames = 0;
			switch (Phase)
			{
			case PrecisionAreaPipettePhase.AwaitingCornerA:
			{
				_cornerA = aimPoint;
				_cornerB = null;
				_cornerC = null;
				if (IsPersistRefinementsEnabled())
				{
					_state.ClearVolumeKeepRefinements();
				}
				else
				{
					_state.Clear();
				}
				_keyToRoot.Clear();
				Phase = PrecisionAreaPipettePhase.AwaitingCornerB;
				ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogInfo((object)("Precision Area Pipette Corner A = " + FormatVec(aimPoint)));
				}
				LogVerbose("Corner A = " + FormatVec(aimPoint));
				break;
			}
			case PrecisionAreaPipettePhase.AwaitingCornerB:
			{
				_cornerB = aimPoint;
				_cornerC = null;
				Phase = PrecisionAreaPipettePhase.AwaitingCornerC;
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("Precision Area Pipette Corner B = " + FormatVec(aimPoint) + " (width edge); set Corner C for depth/height."));
				}
				LogVerbose("Corner B = " + FormatVec(aimPoint));
				break;
			}
			case PrecisionAreaPipettePhase.AwaitingCornerC:
				_cornerC = aimPoint;
				RecomputeFromThreeCorners(_cornerA.Value, _cornerB.Value, _cornerC.Value);
				Phase = PrecisionAreaPipettePhase.Locked;
				LogSelectionLock(aimPoint);
				break;
			}
		}

		private void LogSelectionLock(Vector3 cornerC)
		{
			//IL_0053: 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_015f: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			SelectionVolume volume = _state.Volume;
			int count = _state.GetEffectiveSelection().Count;
			int count2 = CandidateBuffer.Count;
			int count3 = _keyToRoot.Count;
			ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogInfo((object)("Precision Area Pipette Corner C = " + FormatVec(cornerC) + "; selected = " + count + " / candidates = " + count2 + " (mapped roots = " + count3 + "); oriented = " + volume.IsOriented + "; size = " + volume.SizeX.ToString("0.0") + "m × " + volume.SizeZ.ToString("0.0") + "m × " + volume.SizeY.ToString("0.0") + "m (W×D×H)"));
			}
			int num = 0;
			foreach (string item in _state.BaseSelection)
			{
				if (_keyToRoot.TryGetValue(item, out var value) && !((Object)value == (Object)null))
				{
					ManualLogSource modLogger2 = SonicWorldToolsPlugin.ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)("[SonicWorldTools] Selected sample: " + ((Object)value).name));
					}
					num++;
					if (num >= 8)
					{
						break;
					}
				}
			}
			LogVerbose("Corner C = " + FormatVec(cornerC));
			LogVerbose("Base selection = " + _state.BaseSelection.Count);
			LogVerbose("Added manually = " + _state.AddedSelection.Count);
			LogVerbose("Excluded manually = " + _state.ExcludedSelection.Count);
			LogVerbose("Effective selection = " + count);
		}

		private void TryRemoveLockedSelection(SelectionRemoval.RemovalKind kind)
		{
			if (!IsActive || Phase != PrecisionAreaPipettePhase.Locked || !_state.HasCorners)
			{
				return;
			}
			HashSet<string> effectiveSelection = _state.GetEffectiveSelection();
			if (effectiveSelection.Count == 0)
			{
				_hudLine = "Precision Area Pipette\nNothing selected to remove.";
				return;
			}
			int requested;
			int skipped;
			string summary;
			bool flag = SelectionRemoval.TryRemoveEffectiveSelection(effectiveSelection, _keyToRoot, kind, out requested, out skipped, out summary);
			ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogInfo((object)("[SonicWorldTools] " + summary + ((WorldEditUndoBridge.IsAvailable && !PluginConfig.IsDryRunSelection()) ? " (WEC undo)" : string.Empty)));
			}
			if (!flag && requested == 0)
			{
				_hudLine = "Precision Area Pipette\n" + summary;
				return;
			}
			ClearSelectionVisuals();
			Phase = PrecisionAreaPipettePhase.AwaitingCornerA;
			string text = ((WorldEditUndoBridge.IsAvailable && !PluginConfig.IsDryRunSelection()) ? "\nUndo: IH/WEC undo bind (often Keypad7)" : string.Empty);
			_hudLine = "Precision Area Pipette\n" + summary + text + "\nSet Corner A";
			if (skipped > 0)
			{
				LogVerbose("Removal skipped = " + skipped);
			}
		}

		private void CancelSelection()
		{
			ClearSelectionVisuals();
			Phase = PrecisionAreaPipettePhase.AwaitingCornerA;
			_hudLine = "Precision Area Pipette — set Corner A";
			ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogInfo((object)"Precision Area Pipette selection cancelled.");
			}
		}

		private void ResetCorners()
		{
			ClearSelectionVisuals(IsPersistRefinementsEnabled());
			Phase = PrecisionAreaPipettePhase.AwaitingCornerA;
			_hudLine = (IsPersistRefinementsEnabled() ? "Precision Area Pipette — corners reset (refinements kept); set Corner A" : "Precision Area Pipette — corners reset; set Corner A");
			ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogInfo((object)("Precision Area Pipette corners reset" + (IsPersistRefinementsEnabled() ? " (refinements kept)." : ".")));
			}
		}

		private static bool IsPersistRefinementsEnabled()
		{
			if (PluginConfig.PersistRefinementsAfterResize != null)
			{
				return PluginConfig.PersistRefinementsAfterResize.Value;
			}
			return true;
		}

		private void ResetRefinement()
		{
			if (Phase == PrecisionAreaPipettePhase.Locked && _state.HasCorners)
			{
				_state.ResetRefinement();
				LogVerbose("Added manually = 0");
				LogVerbose("Excluded manually = 0");
				LogVerbose("Effective selection = " + _state.GetEffectiveSelection().Count);
				ManualLogSource modLogger = SonicWorldToolsPlugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"Precision Area Pipette refinement reset.");
				}
				UpdateHudText();
			}
		}

		private void ClearSelectionVisuals(bool keepRefinements = false)
		{
			_cornerA = null;
			_cornerB = null;
			_cornerC = null;
			if (keepRefinements)
			{
				_state.ClearVolumeKeepRefinements();
			}
			else
			{
				_state.Clear();
			}
			_keyToRoot.Clear();
			_candidateTimer = 0f;
			_hoverRoot = null;
			PrecisionAreaPipettePreview.Clear();
		}

		private void RecomputeFromThreeCorners(Vector3 a, Vector3 b, Vector3 c)
		{
			//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)
			//IL_0002: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			SelectionVolume volume = SelectionVolume.FromThreePoints(a, b, c).Expanded(PluginConfig.GetSelectionPadding());
			CollectCandidates(volume);
			AppendRefinementPiecesToCandidates();
			bool persistRefinements = IsPersistRefinementsEnabled();
			_state.SetThreeCorners(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z, CandidateBuffer, persistRefinements);
			EnsureRefinementRootsMapped();
		}

		private void AppendRefinementPiecesToCandidates()
		{
			AppendKeysAsCandidates(_state.AddedSelection);
			AppendKeysAsCandidates(_state.ExcludedSelection);
		}

		private void AppendKeysAsCandidates(IReadOnlyCollection<string> keys)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (keys == null || keys.Count == 0)
			{
				return;
			}
			foreach (string key in keys)
			{
				if (string.IsNullOrEmpty(key) || _keyToRoot.ContainsKey(key) || !TryResolveKeyToRoot(key, out var root) || (Object)root == (Object)null)
				{
					continue;
				}
				ZNetView component = root.GetComponent<ZNetView>();
				if (!((Object)component == (Object)null) && component.IsValid())
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null)
					{
						Vector3 position = zDO.GetPosition();
						Vector3 canonicalWorldPosition = WorldEditBridge.GetCanonicalWorldPosition(position);
						CandidateBuffer.Add(new SelectionCandidate(new SelectionObjectId(key), canonicalWorldPosition.x, canonicalWorldPosition.y, canonicalWorldPosition.z));
						_keyToRoot[key] = root;
					}
				}
			}
		}

		private void RefreshCandidateMap(SelectionVolume volume)
		{
			CollectCandidates(volume);
			EnsureRefinementRootsMapped();
		}

		private void CollectCandidates(SelectionVolume volume)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0050: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			CandidateBuffer.Clear();
			_keyToRoot.Clear();
			PieceBuffer.Clear();
			Vector3 val = (volume.Min + volume.Max) * 0.5f;
			Vector3 val2 = volume.Max - volume.Min;
			float num = ((Vector3)(ref val2)).magnitude * 0.5f + 2f;
			if (num < 2f)
			{
				num = 2f;
			}
			Piece.GetAllPiecesInRadius(val, num, PieceBuffer);
			for (int i = 0; i < PieceBuffer.Count; i++)
			{
				Piece val3 = PieceBuffer[i];
				if ((Object)val3 == (Object)null)
				{
					continue;
				}
				ZNetView component = ((Component)val3).GetComponent<ZNetView>();
				if (!((Object)component == (Object)null) && component.IsValid())
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null)
					{
						Vector3 position = zDO.GetPosition();
						Vector3 canonicalWorldPosition = WorldEditBridge.GetCanonicalWorldPosition(position);
						string key = ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
						CandidateBuffer.Add(new SelectionCandidate(new SelectionObjectId(key), canonicalWorldPosition.x, canonicalWorldPosition.y, canonicalWorldPosition.z));
						_keyToRoot[key] = ((Component)val3).gameObject;
					}
				}
			}
		}

		private void EnsureRefinementRootsMapped()
		{
			MapMissingKeys(_state.AddedSelection);
			MapMissingKeys(_state.ExcludedSelection);
		}

		private void MapMissingKeys(IReadOnlyCollection<string> keys)
		{
			if (keys == null || keys.Count == 0)
			{
				return;
			}
			foreach (string key in keys)
			{
				if (!_keyToRoot.ContainsKey(key) && TryResolveKeyToRoot(key, out var root))
				{
					_keyToRoot[key] = root;
				}
			}
		}

		private static bool TryResolveKeyToRoot(string key, out GameObject root)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or mis