Decompiled source of Scrap Clean v1.2.0

plugins/ScrapClean.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.StructWrapping;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
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("ScrapClean")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+82354be9bbf2772f8290ccf7d97994d9637de857")]
[assembly: AssemblyProduct("ScrapClean")]
[assembly: AssemblyTitle("ScrapClean")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace ScrapClean;

internal static class CenterCache
{
	private sealed class Entry
	{
		internal Renderer[] Renderers;

		internal Collider[] Colliders;

		internal int ChildCount;
	}

	private const float PruneInterval = 5f;

	private const float MaxRendererExtent = 200f;

	private static readonly Dictionary<GameObject, Entry> Cache = new Dictionary<GameObject, Entry>(512);

	private static readonly List<GameObject> DeadScratch = new List<GameObject>(16);

	private static float _nextPrune;

	internal static bool TryGetRenderers(GameObject go, List<Renderer> results)
	{
		results.Clear();
		if ((Object)(object)go == (Object)null)
		{
			return false;
		}
		Renderer[] renderers = GetEntry(go).Renderers;
		for (int i = 0; i < renderers.Length; i++)
		{
			if ((Object)(object)renderers[i] != (Object)null)
			{
				results.Add(renderers[i]);
			}
		}
		return results.Count > 0;
	}

	private static Entry GetEntry(GameObject go)
	{
		int childCount = go.transform.childCount;
		if (Cache.TryGetValue(go, out var value) && value != null && value.ChildCount == childCount && !HasDeadEntry(value))
		{
			return value;
		}
		value = new Entry
		{
			Renderers = go.GetComponentsInChildren<Renderer>(true),
			Colliders = go.GetComponentsInChildren<Collider>(true),
			ChildCount = childCount
		};
		Cache[go] = value;
		return value;
	}

	private static bool HasDeadEntry(Entry entry)
	{
		for (int i = 0; i < entry.Renderers.Length; i++)
		{
			if ((Object)(object)entry.Renderers[i] == (Object)null)
			{
				return true;
			}
		}
		for (int j = 0; j < entry.Colliders.Length; j++)
		{
			if ((Object)(object)entry.Colliders[j] == (Object)null)
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsSolidGeometry(Renderer renderer)
	{
		if (renderer is ParticleSystemRenderer || renderer is TrailRenderer)
		{
			return false;
		}
		if (!(renderer is MeshRenderer) && !(renderer is SkinnedMeshRenderer) && !(renderer is LineRenderer))
		{
			return renderer is SpriteRenderer;
		}
		return true;
	}

	internal static bool TryGetBounds(GameObject go, out Bounds bounds)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		bounds = default(Bounds);
		if ((Object)(object)go == (Object)null)
		{
			return false;
		}
		PruneIfStale();
		Entry entry = GetEntry(go);
		bool flag = false;
		for (int i = 0; i < entry.Renderers.Length; i++)
		{
			Renderer val = entry.Renderers[i];
			if ((Object)(object)val == (Object)null || !IsSolidGeometry(val) || !val.enabled || !((Component)val).gameObject.activeInHierarchy)
			{
				continue;
			}
			Bounds bounds2 = val.bounds;
			Vector3 size = ((Bounds)(ref bounds2)).size;
			if (!(((Vector3)(ref size)).sqrMagnitude <= 1E-06f) && !(((Bounds)(ref bounds2)).size.x > 200f) && !(((Bounds)(ref bounds2)).size.y > 200f) && !(((Bounds)(ref bounds2)).size.z > 200f))
			{
				if (!flag)
				{
					bounds = bounds2;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(bounds2);
				}
			}
		}
		if (flag)
		{
			return true;
		}
		return TryGetColliderBounds(entry, out bounds);
	}

	private static bool TryGetColliderBounds(Entry entry, out Bounds bounds)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: 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_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		bounds = default(Bounds);
		bool flag = false;
		for (int i = 0; i < entry.Colliders.Length; i++)
		{
			Collider val = entry.Colliders[i];
			if ((Object)(object)val == (Object)null || !val.enabled || val.isTrigger || !((Component)val).gameObject.activeInHierarchy)
			{
				continue;
			}
			Bounds bounds2 = val.bounds;
			Vector3 size = ((Bounds)(ref bounds2)).size;
			if (!(((Vector3)(ref size)).sqrMagnitude <= 1E-06f))
			{
				if (!flag)
				{
					bounds = bounds2;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(bounds2);
				}
			}
		}
		return flag;
	}

	internal static Vector3 Center(GameObject go)
	{
		//IL_0009: 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_001b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)go == (Object)null)
		{
			return Vector3.zero;
		}
		if (TryGetBounds(go, out var bounds))
		{
			return ((Bounds)(ref bounds)).center;
		}
		return go.transform.position;
	}

	internal static Vector3 AimReferencePoint(GameObject go, Vector3 rayOrigin, Vector3 rayDirection)
	{
		//IL_0009: 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_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)go == (Object)null)
		{
			return Vector3.zero;
		}
		if (!TryGetBounds(go, out var bounds))
		{
			return go.transform.position;
		}
		float num = Vector3.Dot(((Bounds)(ref bounds)).center - rayOrigin, rayDirection);
		if (num < 0f)
		{
			return ((Bounds)(ref bounds)).center;
		}
		return ((Bounds)(ref bounds)).ClosestPoint(rayOrigin + rayDirection * num);
	}

	internal static Vector3 OcclusionReferencePoint(GameObject go, Vector3 rayOrigin)
	{
		//IL_0009: 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_0028: 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)
		if ((Object)(object)go == (Object)null)
		{
			return Vector3.zero;
		}
		if (!TryGetBounds(go, out var bounds))
		{
			return go.transform.position;
		}
		return ((Bounds)(ref bounds)).ClosestPoint(rayOrigin);
	}

	internal static void Forget(GameObject go)
	{
		if ((Object)(object)go != (Object)null)
		{
			Cache.Remove(go);
		}
	}

	internal static void Clear()
	{
		Cache.Clear();
		_nextPrune = 0f;
	}

	private static void PruneIfStale()
	{
		if (Time.unscaledTime < _nextPrune)
		{
			return;
		}
		_nextPrune = Time.unscaledTime + 5f;
		DeadScratch.Clear();
		foreach (KeyValuePair<GameObject, Entry> item in Cache)
		{
			if ((Object)(object)item.Key == (Object)null)
			{
				DeadScratch.Add(item.Key);
			}
		}
		for (int i = 0; i < DeadScratch.Count; i++)
		{
			Cache.Remove(DeadScratch[i]);
		}
	}
}
internal struct CleanResult
{
	internal int Removed;

	internal int Requested;

	internal int Skipped;
}
internal static class Cleaner
{
	private struct PendingTarget
	{
		internal GameObject Root;

		internal PhotonView View;

		internal Item Item;

		internal float Deadline;

		internal float ClaimAt;
	}

	private const float TakeoverTimeout = 2.5f;

	private const float FixedClaimDelay = 0.9f;

	private static readonly List<PendingTarget> Pending = new List<PendingTarget>();

	private static readonly List<PendingTarget> PendingScratch = new List<PendingTarget>();

	private static bool IsLocallyOwned(PhotonView view)
	{
		if ((Object)(object)view == (Object)null)
		{
			return false;
		}
		if (view.IsMine)
		{
			return true;
		}
		int num = LocalActorNumber();
		if (num <= 0)
		{
			return false;
		}
		if (view.OwnerActorNr == num)
		{
			return true;
		}
		if (view.CreatorActorNr == num)
		{
			if (view.OwnerActorNr != 0 && view.Owner != null)
			{
				return view.Owner.IsInactive;
			}
			return true;
		}
		return false;
	}

	private static int LocalActorNumber()
	{
		if (PhotonNetwork.LocalPlayer == null)
		{
			return -1;
		}
		return PhotonNetwork.LocalPlayer.ActorNumber;
	}

	private static bool EnsureLocalOwnership(PhotonView view)
	{
		if ((Object)(object)view == (Object)null)
		{
			return false;
		}
		if (view.IsMine)
		{
			return true;
		}
		int num = LocalActorNumber();
		if (num <= 0)
		{
			return false;
		}
		int ownerActorNr = view.OwnerActorNr;
		int controllerActorNr = view.ControllerActorNr;
		try
		{
			view.OwnerActorNr = num;
			view.ControllerActorNr = num;
		}
		catch (Exception ex)
		{
			SCLog.WarnThrottled("cleaner.own", "接管所有权失败:" + SafeName((Object)(object)view) + " :: " + ex.Message);
		}
		if (view.IsMine)
		{
			return true;
		}
		RestoreOwnership(view, ownerActorNr, controllerActorNr);
		return false;
	}

	private static void RestoreOwnership(PhotonView view, int owner, int controller)
	{
		if ((Object)(object)view == (Object)null)
		{
			return;
		}
		try
		{
			if (owner != 0)
			{
				view.OwnerActorNr = owner;
			}
			if (controller != 0)
			{
				view.ControllerActorNr = controller;
			}
		}
		catch (Exception ex)
		{
			SCLog.Verbose("还原所有权失败:" + ex.Message);
		}
	}

	internal static bool DestroyWithOwnership(PhotonView view, GameObject root)
	{
		if ((Object)(object)view == (Object)null || (Object)(object)root == (Object)null)
		{
			return false;
		}
		if (view.IsMine)
		{
			return DestroyNow(root);
		}
		int ownerActorNr = view.OwnerActorNr;
		int controllerActorNr = view.ControllerActorNr;
		if (!EnsureLocalOwnership(view))
		{
			return false;
		}
		if (DestroyNow(root))
		{
			return true;
		}
		RestoreOwnership(view, ownerActorNr, controllerActorNr);
		return false;
	}

	internal static void Tick()
	{
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		if (Pending.Count == 0)
		{
			return;
		}
		float unscaledTime = Time.unscaledTime;
		PendingScratch.Clear();
		PendingScratch.AddRange(Pending);
		Pending.Clear();
		for (int i = 0; i < PendingScratch.Count; i++)
		{
			PendingTarget item = PendingScratch[i];
			if ((Object)(object)item.Root == (Object)null || (Object)(object)item.View == (Object)null)
			{
				continue;
			}
			bool isMine = item.View.IsMine;
			if (isMine || unscaledTime >= item.ClaimAt)
			{
				if ((Object)(object)item.Item != (Object)null && ((int)item.Item.itemState != 0 || !item.Root.activeInHierarchy))
				{
					SCLog.Info("目标已被拾取,取消清理:" + SafeName((Object)(object)item.Root));
				}
				else if (DestroyWithOwnership(item.View, item.Root))
				{
					if (!isMine)
					{
						SCLog.Info("对方未响应,本机接管并清理:" + SafeName((Object)(object)item.Root));
					}
				}
				else
				{
					SCLog.WarnThrottled("cleaner.claim.failed", "接管并清理失败,放弃:" + SafeName((Object)(object)item.Root));
				}
			}
			else if (unscaledTime >= item.Deadline)
			{
				if (item.ClaimAt < float.MaxValue)
				{
					SCLog.WarnThrottled("cleaner.claim.timeout", "接管并清理超时,放弃:" + SafeName((Object)(object)item.Root));
				}
				else
				{
					SCLog.WarnThrottled("cleaner.transfer.timeout", "所有权转移超时,放弃:" + SafeName((Object)(object)item.Root));
				}
			}
			else
			{
				Pending.Add(item);
			}
		}
	}

	internal static void Reset()
	{
		Pending.Clear();
	}

	internal static bool CleanSingle(ScrapTarget target, out string failure, out bool requested)
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		failure = null;
		requested = false;
		if ((Object)(object)target.Root == (Object)null || (Object)(object)target.View == (Object)null)
		{
			failure = "目标已消失";
			return false;
		}
		if (!TargetScanner.IsStillValid(target))
		{
			failure = "目标状态已改变";
			return false;
		}
		if (IsLocallyOwned(target.View))
		{
			if (!DestroyWithOwnership(target.View, target.Root))
			{
				failure = "无法取得该物体的所有权";
				return false;
			}
			CleanCompanion(target);
			return true;
		}
		if (!ScrapCleanConfig.AllowOwnershipTakeover.Value)
		{
			failure = "该物体不属于你";
			return false;
		}
		if ((int)target.View.OwnershipTransfer != 0)
		{
			if (!RequestTakeover(target.Root, target.View, target.Item))
			{
				failure = "无法请求所有权(未连接房间)";
				return false;
			}
			CleanCompanion(target);
			requested = true;
			return true;
		}
		if (!CleanRequestRelay.RequestRemoteDelete(target.View))
		{
			failure = "无法清理该物体";
			return false;
		}
		QueueFixedClaim(target);
		CleanCompanion(target);
		requested = true;
		return true;
	}

	private static void MarkHandled(ScrapTarget target, HashSet<int> handled)
	{
		if ((Object)(object)target.View != (Object)null && target.View.ViewID != 0)
		{
			handled.Add(target.View.ViewID);
		}
		GameObject companion = target.Companion;
		if (!((Object)(object)companion == (Object)null))
		{
			PhotonView component = companion.GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && component.ViewID != 0)
			{
				handled.Add(component.ViewID);
			}
		}
	}

	private static void CleanCompanion(ScrapTarget target)
	{
		GameObject companion = target.Companion;
		if ((Object)(object)companion == (Object)null)
		{
			return;
		}
		PhotonView component = companion.GetComponent<PhotonView>();
		if (!((Object)(object)component == (Object)null))
		{
			if (IsLocallyOwned(component))
			{
				DestroyWithOwnership(component, companion);
			}
			else if (CleanRequestRelay.RequestRemoteDelete(component))
			{
				QueueFixedClaim(component, companion, null);
			}
		}
	}

	internal static CleanResult CleanArea(Vector3 center, float radius, List<ScrapTarget> targets, out string failure)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		failure = null;
		CleanResult result = default(CleanResult);
		float num = radius * radius;
		HashSet<int> hashSet = new HashSet<int>();
		for (int i = 0; i < targets.Count; i++)
		{
			ScrapTarget target = targets[i];
			if ((Object)(object)target.Root == (Object)null || (Object)(object)target.View == (Object)null || (target.View.ViewID != 0 && hashSet.Contains(target.View.ViewID)))
			{
				continue;
			}
			Vector3 val = target.Center - center;
			if (((Vector3)(ref val)).sqrMagnitude > num || !TargetScanner.IsStillValid(target))
			{
				continue;
			}
			if (IsLocallyOwned(target.View))
			{
				if (!DestroyWithOwnership(target.View, target.Root))
				{
					result.Skipped++;
					continue;
				}
				MarkHandled(target, hashSet);
				CleanCompanion(target);
				result.Removed++;
			}
			else if (!ScrapCleanConfig.AllowOwnershipTakeover.Value)
			{
				result.Skipped++;
			}
			else if ((int)target.View.OwnershipTransfer != 0)
			{
				if (!RequestTakeover(target.Root, target.View, target.Item))
				{
					result.Skipped++;
					continue;
				}
				MarkHandled(target, hashSet);
				CleanCompanion(target);
				result.Requested++;
			}
			else if (CleanRequestRelay.RequestRemoteDelete(target.View))
			{
				QueueFixedClaim(target);
				MarkHandled(target, hashSet);
				CleanCompanion(target);
				result.Requested++;
			}
			else
			{
				result.Skipped++;
			}
		}
		if (result.Removed == 0 && result.Requested == 0 && result.Skipped > 0)
		{
			failure = "圆内目标均无法清理";
		}
		return result;
	}

	internal static bool CanClean(ScrapTarget target, out string reason)
	{
		reason = null;
		if ((Object)(object)target.Root == (Object)null || (Object)(object)target.View == (Object)null)
		{
			reason = "目标已消失";
			return false;
		}
		if (IsLocallyOwned(target.View))
		{
			return true;
		}
		if (!ScrapCleanConfig.AllowOwnershipTakeover.Value)
		{
			reason = "不属于你";
			return false;
		}
		return true;
	}

	private static void QueueFixedClaim(ScrapTarget target)
	{
		QueueFixedClaim(target.View, target.Root, target.Item);
	}

	private static void QueueFixedClaim(PhotonView view, GameObject root, Item item)
	{
		if ((Object)(object)root == (Object)null || (Object)(object)view == (Object)null || !ScrapCleanConfig.AllowOwnershipTakeover.Value)
		{
			return;
		}
		float unscaledTime = Time.unscaledTime;
		for (int i = 0; i < Pending.Count; i++)
		{
			if ((Object)(object)Pending[i].Root == (Object)(object)root)
			{
				PendingTarget value = Pending[i];
				value.Deadline = unscaledTime + 2.5f;
				value.ClaimAt = unscaledTime + 0.9f;
				Pending[i] = value;
				return;
			}
		}
		Pending.Add(new PendingTarget
		{
			Root = root,
			View = view,
			Item = item,
			Deadline = unscaledTime + 2.5f,
			ClaimAt = unscaledTime + 0.9f
		});
	}

	private static bool RequestTakeover(GameObject root, PhotonView view, Item item)
	{
		if (!PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode)
		{
			return false;
		}
		int num = LocalActorNumber();
		if (num <= 0)
		{
			return false;
		}
		for (int i = 0; i < Pending.Count; i++)
		{
			if ((Object)(object)Pending[i].Root == (Object)(object)root)
			{
				PendingTarget value = Pending[i];
				value.Deadline = Time.unscaledTime + 2.5f;
				Pending[i] = value;
				return true;
			}
		}
		Pending.Add(new PendingTarget
		{
			Root = root,
			View = view,
			Item = item,
			Deadline = Time.unscaledTime + 2.5f,
			ClaimAt = float.MaxValue
		});
		try
		{
			view.TransferOwnership(num);
		}
		catch (Exception ex)
		{
			SCLog.WarnThrottled("cleaner.transfer", "请求接管所有权失败:" + SafeName((Object)(object)root) + " :: " + ex.Message);
		}
		return true;
	}

	private static bool DestroyNow(GameObject root)
	{
		if ((Object)(object)root == (Object)null)
		{
			return false;
		}
		string text = SafeName((Object)(object)root);
		try
		{
			if (PhotonNetwork.InRoom || PhotonNetwork.OfflineMode)
			{
				if (ConnectionHandler.AppQuits)
				{
					return false;
				}
				PhotonNetwork.Destroy(root);
			}
			else
			{
				root.SetActive(false);
				Object.Destroy((Object)(object)root);
			}
			CenterCache.Forget(root);
			return !root.activeInHierarchy;
		}
		catch (Exception ex)
		{
			SCLog.Warn("删除物体失败:" + text + " :: " + ex.Message);
			return false;
		}
	}

	internal static string SafeName(Object obj)
	{
		try
		{
			return (obj != (Object)null) ? obj.name : "<null>";
		}
		catch (Exception)
		{
			return "<destroyed>";
		}
	}
}
internal enum CleanModeKind
{
	Single,
	Area
}
[DefaultExecutionOrder(-300)]
internal sealed class CleanMode : MonoBehaviour
{
	private const float MinSingleRange = 1f;

	private const float MinAreaRadius = 0.5f;

	private const float MaxAreaRadius = 20f;

	private readonly List<ScrapTarget> _targets = new List<ScrapTarget>(256);

	private readonly HighlightRenderer _highlight = new HighlightRenderer();

	private readonly HashSet<string> _loggedRejects = new HashSet<string>();

	private const int MaxLoggedRejects = 2048;

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

	private CleanModeKind _mode;

	private bool _active;

	private CleanUI _ui;

	private GroundRing _ring;

	private ScrapTarget _nearest;

	private bool _hasNearest;

	private GameObject _publishedRoot;

	private int _publishedDistance = -1;

	private bool _publishedCleanable;

	private string _publishedReason;

	private string _publishedLabel;

	private int _publishedAreaCount = -1;

	private const int PublishedNone = -1;

	private const int PublishedEmpty = -2;

	private const int PublishedArea = -3;

	private bool _degraded;

	private bool _primeScanPending;

	private bool _fovWarned;

	internal bool SuppressGameplayInput { get; private set; }

	private static float HintHold()
	{
		ConfigEntry<float> toastSeconds = ScrapCleanConfig.ToastSeconds;
		if (toastSeconds == null || !(toastSeconds.Value > 0f))
		{
			return 2f;
		}
		return toastSeconds.Value;
	}

	private static float AimMaxDistance()
	{
		ConfigEntry<float> singleRange = ScrapCleanConfig.SingleRange;
		float num = ((singleRange != null && singleRange.Value > 0f) ? singleRange.Value : 100f);
		return Mathf.Max(1f, num);
	}

	private static float AreaRadiusSafe()
	{
		return Mathf.Clamp(ScrapCleanConfig.AreaRadius?.Value ?? 3f, 0.5f, 20f);
	}

	private static Vector3 PlayerCenter(Character local)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0018: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return local.Center;
		}
		catch (Exception)
		{
			return ((Component)local).transform.position;
		}
	}

	private void Awake()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		_ui = ((Component)this).gameObject.AddComponent<CleanUI>();
		GameObject val = new GameObject("ScrapCleanRingHost");
		val.transform.SetParent(((Component)this).transform, false);
		_ring = val.AddComponent<GroundRing>();
		CleanRequestRelay.Ensure(((Component)this).gameObject);
		HarmonyPatches.Register(this);
		SceneManager.sceneLoaded += OnSceneLoaded;
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		TargetScanner.Invalidate();
		CenterCache.Clear();
		CleanRequestRelay.ResetRequestCooldowns();
		_loggedRejects.Clear();
		SCLog.ResetDiagnostics();
		ResetPublishedState();
		_degraded = false;
		_primeScanPending = true;
		if (_active)
		{
			ExitMode(silent: true);
		}
	}

	private void OnDisable()
	{
		SuppressGameplayInput = false;
		_highlight.Clear();
		_ring?.SetVisible(visible: false);
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		HarmonyPatches.Register(null);
		ExitMode(silent: true);
		SuppressGameplayInput = false;
		_highlight.Clear();
		CenterCache.Clear();
		TargetScanner.Invalidate();
	}

	private void Update()
	{
		try
		{
			Tick();
		}
		catch (Exception ex)
		{
			bool flag = !_degraded;
			_degraded = true;
			if (flag)
			{
				SCLog.Error("清理模式更新出错,已退出(后续相同错误不再输出):" + ex);
			}
			try
			{
				ExitMode(silent: true);
				if (flag)
				{
					_ui?.ShowResult(success: false, "清理模式出错(详见日志)", 2f);
				}
			}
			finally
			{
				SuppressGameplayInput = false;
			}
		}
	}

	private void Tick()
	{
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		Cleaner.Tick();
		bool num = IsContextValid();
		if (!num && _active)
		{
			ExitMode(silent: true);
		}
		if (!num)
		{
			SuppressGameplayInput = false;
			_highlight.Clear();
			_ring?.SetVisible(visible: false);
			_ui?.ClearTarget();
			_ui?.HideImmediate();
			ResetPublishedState();
			return;
		}
		bool flag = IsInputBlocked(GUIManager.instance);
		if (SCInput.KeyDown(ScrapCleanConfig.ToggleKey.Value))
		{
			if (_active)
			{
				ExitMode(silent: false);
			}
			else if (!flag)
			{
				EnterMode();
			}
			return;
		}
		if (!_active)
		{
			SuppressGameplayInput = false;
			_highlight.EndFrame();
			_ring?.SetVisible(visible: false);
			_ui?.ClearTarget();
			if (_primeScanPending)
			{
				_primeScanPending = false;
				ScrapCleanWarmup.PrimeTargetScan();
			}
			return;
		}
		if (flag)
		{
			SCInput.ResetScrollTracking();
			SuppressGameplayInput = false;
			_highlight.Clear();
			_ring?.SetVisible(visible: false);
			_ui?.ClearTarget();
			_ui?.HideImmediate();
			ResetPublishedState();
			return;
		}
		SuppressGameplayInput = true;
		SCInput.SyncScrollBaselineOnFocusChange();
		HandleScroll();
		Vector3 val = PlayerCenter(Character.localCharacter);
		Vector3 val2;
		float num2;
		if (_mode == CleanModeKind.Area)
		{
			if (!TryGetAimPoint(out var point, out var normal))
			{
				_targets.Clear();
				_hasNearest = false;
				_highlight.Clear();
				_ring?.SetVisible(visible: false);
				PublishEmptyTarget("将准星对准要清理的表面");
				if (SCInput.LeftClickDown())
				{
					_ui?.ShowResult(success: false, "请将准星对准要清理的表面", 1.2f);
				}
				return;
			}
			val2 = point;
			num2 = AreaRadiusSafe();
			_ring?.Rebuild(point, num2, normal);
		}
		else
		{
			val2 = val;
			num2 = AimMaxDistance();
			_ring?.SetVisible(visible: false);
		}
		TargetScanner.Collect(val2, num2, _targets);
		HandleHighlight(val2, num2);
		if (SCInput.LeftClickDown())
		{
			HandleClick();
		}
	}

	private static bool IsInputBlocked(GUIManager gui)
	{
		if ((Object)(object)gui == (Object)null)
		{
			return true;
		}
		try
		{
			if (gui.windowBlockingInput || gui.wheelActive || GUIManager.InPauseMenu)
			{
				return true;
			}
		}
		catch (Exception ex)
		{
			SCLog.WarnThrottled("gui.blocked", "读取游戏 UI 状态失败,本次视为被 UI 占用:" + ex.Message);
			return true;
		}
		Character localCharacter = Character.localCharacter;
		if ((Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null)
		{
			if (!localCharacter.data.usingEmoteWheel)
			{
				return localCharacter.data.usingBackpackWheel;
			}
			return true;
		}
		return false;
	}

	private void HandleScroll()
	{
		int num = SCInput.ScrollNotch();
		if (num != 0)
		{
			_mode = ((_mode == CleanModeKind.Single) ? CleanModeKind.Area : CleanModeKind.Single);
			PersistMode(_mode);
			AnnounceMode(num > 0);
		}
	}

	private void AnnounceMode(bool upward)
	{
		_ui?.SetMode(_mode);
		ResetPublishedState();
		_ui?.ShowSwitch(ModeTitle(_mode), ModeAction(_mode), upward, HintHold());
	}

	private static string ModeKey(CleanModeKind mode)
	{
		if (mode != CleanModeKind.Area)
		{
			return "Single";
		}
		return "Area";
	}

	private static string ModeTitle(CleanModeKind mode)
	{
		return CleanUI.ModeLabel(mode);
	}

	private static string ModeAction(CleanModeKind mode)
	{
		return CleanUI.ModeActionText(mode);
	}

	private static CleanModeKind ParseMode(string raw)
	{
		if (!string.Equals(raw, "Area", StringComparison.OrdinalIgnoreCase))
		{
			return CleanModeKind.Single;
		}
		return CleanModeKind.Area;
	}

	private static void PersistMode(CleanModeKind mode)
	{
		ConfigEntry<string> lastMode = ScrapCleanConfig.LastMode;
		if (lastMode == null)
		{
			return;
		}
		string text = ModeKey(mode);
		if (string.Equals(lastMode.Value, text, StringComparison.Ordinal))
		{
			return;
		}
		try
		{
			lastMode.Value = text;
		}
		catch (Exception ex)
		{
			SCLog.WarnThrottled("config.persist", "写入 LastMode 失败(本次切换仍然生效):" + ex.Message);
		}
	}

	private void HandleHighlight(Vector3 queryCenter, float queryRadius)
	{
		//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_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		if (_mode == CleanModeKind.Area)
		{
			_hasNearest = false;
			_nearest = default(ScrapTarget);
			int num = 0;
			bool flag = false;
			for (int i = 0; i < _targets.Count; i++)
			{
				ScrapTarget target = _targets[i];
				if ((Object)(object)target.Root == (Object)null)
				{
					continue;
				}
				Vector3 val = target.Center - queryCenter;
				if (!(((Vector3)(ref val)).sqrMagnitude > queryRadius * queryRadius))
				{
					_highlight.Mark(target.Root);
					num++;
					if (!flag && Cleaner.CanClean(target, out var _))
					{
						flag = true;
					}
				}
			}
			_highlight.EndFrame();
			if (num != _publishedAreaCount || flag != _publishedCleanable)
			{
				_publishedAreaCount = num;
				_publishedCleanable = flag;
				_publishedRoot = null;
				_publishedDistance = -3;
				_ui?.SetTarget(CleanUI.Status((num > 0) ? ("圆内 " + num + " 个目标") : "圆内没有目标", flag));
			}
		}
		else
		{
			_hasNearest = TryFindAimedTarget(out _nearest, out var bestAngle);
			if (!_hasNearest)
			{
				_highlight.EndFrame();
				PublishEmptyTarget("将准星对准要清理的目标");
			}
			else
			{
				_highlight.Mark(_nearest.Root);
				_highlight.EndFrame();
				PublishTarget(_nearest, bestAngle);
			}
		}
	}

	private void PublishTarget(ScrapTarget target, float aimAngle)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)target.Root == (Object)null))
		{
			string reason;
			bool flag = Cleaner.CanClean(target, out reason);
			int num = Mathf.RoundToInt(Vector3.Distance(PlayerCenter(Character.localCharacter), target.Center));
			if (_publishedRoot != target.Root || _publishedDistance != num || _publishedCleanable != flag || !string.Equals(_publishedReason, reason, StringComparison.Ordinal) || !string.Equals(_publishedLabel, target.Label, StringComparison.Ordinal))
			{
				_publishedRoot = target.Root;
				_publishedDistance = num;
				_publishedCleanable = flag;
				_publishedReason = reason;
				_publishedLabel = target.Label;
				_publishedAreaCount = -1;
				_ui?.SetTarget(CleanUI.Target(target.Label, target.Kind == ScrapKind.Placed, num, flag, reason, aimAngle));
			}
		}
	}

	private void PublishEmptyTarget(string text)
	{
		if (!((Object)(object)_publishedRoot == (Object)null) || _publishedDistance != -2)
		{
			_publishedRoot = null;
			_publishedDistance = -2;
			_publishedCleanable = false;
			_publishedReason = null;
			_publishedLabel = null;
			_publishedAreaCount = -1;
			_ui?.SetTarget(CleanUI.Status(text));
		}
	}

	private void ResetPublishedState()
	{
		_publishedRoot = null;
		_publishedDistance = -1;
		_publishedCleanable = false;
		_publishedReason = null;
		_publishedLabel = null;
		_publishedAreaCount = -1;
	}

	private void HandleClick()
	{
		//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
		LogAimDiagnostics();
		if (_mode == CleanModeKind.Single)
		{
			if (!_hasNearest || (Object)(object)_nearest.Root == (Object)null || (Object)(object)_nearest.View == (Object)null)
			{
				_ui?.ShowResult(success: false, "附近没有可清理的目标", 1.2f);
				return;
			}
			if (!Cleaner.CanClean(_nearest, out var reason))
			{
				_ui?.ShowResult(success: false, reason, 1.2f);
				return;
			}
			SCLog.Info("清理尝试(单选):" + Cleaner.SafeName((Object)(object)_nearest.Root) + " 类型 " + KindName(_nearest.Kind) + " IsMine=" + _nearest.View.IsMine + " Owner=" + _nearest.View.OwnerActorNr + " Creator=" + _nearest.View.CreatorActorNr + " 本地=" + ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1)) + " 所有权 " + ((object)Unsafe.As<OwnershipOption, OwnershipOption>(ref _nearest.View.OwnershipTransfer)/*cast due to .constrained prefix*/).ToString());
			string text = Truncate(_nearest.Label, 18);
			if (!Cleaner.CleanSingle(_nearest, out var failure, out var requested))
			{
				SCLog.Warn("清理失败:" + Cleaner.SafeName((Object)(object)_nearest.Root) + " :: " + failure);
				_ui?.ShowResult(success: false, failure ?? "清理失败", 1.2f);
				return;
			}
			SCLog.Info("清理" + (requested ? "请求" : "执行") + "成功:" + Cleaner.SafeName((Object)(object)_nearest.Root));
			_ui?.ShowResult(success: true, requested ? ("已请求清理 " + text) : ("已清理 " + text), 0.9f);
			_hasNearest = false;
			_nearest = default(ScrapTarget);
			ResetPublishedState();
			return;
		}
		if (!TryGetAimPoint(out var point, out var _))
		{
			_ui?.ShowResult(success: false, "请将准星对准要清理的表面", 1.2f);
			return;
		}
		float radius = AreaRadiusSafe();
		string failure2;
		CleanResult cleanResult = Cleaner.CleanArea(point, radius, _targets, out failure2);
		int num = cleanResult.Removed + cleanResult.Requested;
		if (num == 0)
		{
			_ui?.ShowResult(success: false, failure2 ?? "圆内没有可清理的目标", 1.2f);
			return;
		}
		string text2 = "已清理 " + num + " 个目标";
		if (cleanResult.Requested > 0)
		{
			text2 = text2 + "(" + cleanResult.Requested + " 个已请求)";
		}
		if (cleanResult.Skipped > 0)
		{
			text2 = text2 + ",跳过 " + cleanResult.Skipped;
		}
		_ui?.ShowResult(success: true, text2, 1.2f);
		ResetPublishedState();
	}

	private bool TryFindAimedTarget(out ScrapTarget best, out float bestAngle)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: 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_00a5: 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)
		//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_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: 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_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		best = default(ScrapTarget);
		bestAngle = float.MaxValue;
		MainCamera instance = MainCamera.instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)((Component)instance).GetComponent<Camera>() == (Object)null)
		{
			return false;
		}
		Transform transform = ((Component)instance).transform;
		Vector3 position = transform.position;
		Vector3 forward = transform.forward;
		Vector3 origin = position + forward * 0.05f;
		float num = AimMaxDistance();
		float num2 = num * num;
		bool result = false;
		float num3 = float.MaxValue;
		for (int i = 0; i < _targets.Count; i++)
		{
			ScrapTarget scrapTarget = _targets[i];
			if ((Object)(object)scrapTarget.Root == (Object)null || (Object)(object)scrapTarget.View == (Object)null)
			{
				continue;
			}
			Vector3 val = scrapTarget.Center - position;
			if (((Vector3)(ref val)).sqrMagnitude > num2 || Vector3.Dot(forward, val) <= 0f)
			{
				continue;
			}
			bool flag;
			try
			{
				flag = MainCamera.PointWouldBeInFieldOfView(transform, scrapTarget.Center, 1.35f);
			}
			catch (Exception ex)
			{
				WarnFovOnce(ex);
				continue;
			}
			if (!flag)
			{
				continue;
			}
			Vector3 val2 = CenterCache.AimReferencePoint(scrapTarget.Root, position, forward);
			Vector3 val3 = CenterCache.OcclusionReferencePoint(scrapTarget.Root, position);
			Vector3 val4 = val2 - position;
			float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
			if (sqrMagnitude <= 0.0001f || sqrMagnitude > num2)
			{
				continue;
			}
			bool flag2;
			try
			{
				flag2 = MainCamera.PointWouldBeInFieldOfView(transform, val2, 1f);
			}
			catch (Exception ex2)
			{
				WarnFovOnce(ex2);
				continue;
			}
			if (!flag2)
			{
				LogReject(((Object)scrapTarget.Root).name, "不在视野锥内");
				continue;
			}
			if (IsOccluded(origin, val3, scrapTarget.Root.transform, out var blockerName, out var blockerDistance))
			{
				LogReject(((Object)scrapTarget.Root).name, "被遮挡", blockerName, blockerDistance, Vector3.Distance(position, val3));
				continue;
			}
			float num4 = Vector3.Angle(forward, val4);
			if (num4 < bestAngle - 0.001f || (Mathf.Abs(num4 - bestAngle) <= 0.001f && sqrMagnitude < num3))
			{
				best = scrapTarget;
				bestAngle = num4;
				num3 = sqrMagnitude;
				result = true;
			}
		}
		return result;
	}

	private static bool IsOccluded(Vector3 origin, Vector3 point, Transform target, out string blockerName, out float blockerDistance)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: 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)
		blockerName = null;
		blockerDistance = 0f;
		Vector3 val = point - origin;
		float magnitude = ((Vector3)(ref val)).magnitude;
		float num = magnitude - 0.15f;
		if (num <= 0.01f)
		{
			return false;
		}
		int num2 = Physics.RaycastNonAlloc(origin, val / magnitude, HitBuffer, num, LayerMask.op_Implicit(HelperFunctions.terrainMapMask), (QueryTriggerInteraction)1);
		float num3 = float.MaxValue;
		for (int i = 0; i < num2; i++)
		{
			Transform transform = ((RaycastHit)(ref HitBuffer[i])).transform;
			if (!((Object)(object)transform == (Object)null) && !((Object)(object)transform == (Object)(object)target) && !transform.IsChildOf(target) && ((RaycastHit)(ref HitBuffer[i])).distance < num3)
			{
				num3 = ((RaycastHit)(ref HitBuffer[i])).distance;
				blockerName = ((Object)((Component)transform).gameObject).name;
			}
		}
		if (num3 >= float.MaxValue)
		{
			return false;
		}
		blockerDistance = num3;
		return true;
	}

	private void WarnFovOnce(Exception ex)
	{
		if (!_fovWarned)
		{
			_fovWarned = true;
			SCLog.Warn("视野判定异常,已跳过该目标:" + ((ex != null) ? ex.Message : "unknown"));
		}
	}

	private void LogReject(string rootName, string reason)
	{
		LogReject(rootName, reason, null, 0f, 0f);
	}

	private void LogReject(string rootName, string reason, string blockerName, float blockerDistance, float targetDistance)
	{
		if (VerboseEnabled() && _loggedRejects.Add(rootName))
		{
			if (_loggedRejects.Count > 2048)
			{
				_loggedRejects.Clear();
			}
			string text = ((blockerName == null) ? string.Empty : (" 遮挡物 " + blockerName + " 遮挡距离 " + blockerDistance.ToString("0.0") + " 目标距离 " + targetDistance.ToString("0.0")));
			SCLog.Verbose("单选拒绝(" + reason + "):" + rootName + text);
		}
	}

	private static bool VerboseEnabled()
	{
		if (ScrapCleanConfig.VerboseLogs != null)
		{
			return ScrapCleanConfig.VerboseLogs.Value;
		}
		return false;
	}

	private void LogAimDiagnostics()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: 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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		if (!VerboseEnabled())
		{
			return;
		}
		MainCamera instance = MainCamera.instance;
		if ((Object)(object)instance == (Object)null)
		{
			SCLog.Verbose("准星诊断:MainCamera 为空。");
			return;
		}
		Transform transform = ((Component)instance).transform;
		RaycastHit val = HelperFunctions.LineCheck(transform.position, transform.position + transform.forward * AimMaxDistance(), (LayerType)0, 0f, (QueryTriggerInteraction)1);
		if ((Object)(object)((RaycastHit)(ref val)).collider == (Object)null)
		{
			SCLog.Verbose("准星诊断:射线未命中任何实体层。");
			return;
		}
		GameObject gameObject = ((Component)((RaycastHit)(ref val)).collider).gameObject;
		SCLog.Verbose("准星诊断:命中 " + ((Object)gameObject).name + " 距离 " + ((RaycastHit)(ref val)).distance.ToString("0.0") + "m 层 " + LayerMask.LayerToName(gameObject.layer) + " IsItem=" + ((Object)(object)gameObject.GetComponentInParent<Item>() != (Object)null) + " IsPhotonView=" + ((Object)(object)gameObject.GetComponentInParent<PhotonView>() != (Object)null));
	}

	private static bool TryGetAimPoint(out Vector3 point, out Vector3 normal)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_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_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		point = Vector3.zero;
		normal = Vector3.up;
		MainCamera instance = MainCamera.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return false;
		}
		Transform transform = ((Component)instance).transform;
		RaycastHit val = HelperFunctions.LineCheck(transform.position, transform.position + transform.forward * AimMaxDistance(), (LayerType)1, 0f, (QueryTriggerInteraction)1);
		if ((Object)(object)((RaycastHit)(ref val)).collider == (Object)null)
		{
			return false;
		}
		point = ((RaycastHit)(ref val)).point;
		normal = ((RaycastHit)(ref val)).normal;
		return true;
	}

	private static bool IsContextValid()
	{
		Character localCharacter = Character.localCharacter;
		if ((Object)(object)localCharacter == (Object)null)
		{
			return false;
		}
		if ((Object)(object)GUIManager.instance == (Object)null)
		{
			return false;
		}
		if (LoadingScreenHandler.loading)
		{
			return false;
		}
		if ((Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead || localCharacter.data.fullyPassedOut)
		{
			return false;
		}
		return true;
	}

	private void EnterMode()
	{
		_active = true;
		_mode = ParseMode((ScrapCleanConfig.LastMode != null) ? ScrapCleanConfig.LastMode.Value : null);
		SuppressGameplayInput = true;
		ResetPublishedState();
		_degraded = false;
		SCInput.ResetScrollTracking();
		_ui?.SetMode(_mode);
		_ui?.ShowMode(ModeTitle(_mode), ModeAction(_mode), entering: true, HintHold());
		SCLog.Info("进入清理模式(" + ((_mode == CleanModeKind.Area) ? "范围" : "单选") + ")。");
	}

	private void ExitMode(bool silent)
	{
		//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)
		bool active = _active;
		_active = false;
		SuppressGameplayInput = false;
		SCInput.ResetScrollTracking();
		_hasNearest = false;
		_nearest = default(ScrapTarget);
		ResetPublishedState();
		_targets.Clear();
		try
		{
			Cleaner.Reset();
			_highlight.Clear();
			_ring?.SetVisible(visible: false);
			_ui?.ClearTarget();
			if (active && !silent)
			{
				_ui?.ShowMode("○ 退出清理", "按 " + ((object)ScrapCleanConfig.ToggleKey.Value/*cast due to .constrained prefix*/).ToString() + " 重新进入", entering: false, HintHold());
			}
			else if (active)
			{
				_ui?.HideImmediate();
			}
		}
		finally
		{
			SuppressGameplayInput = false;
		}
		if (active)
		{
			SCLog.Info("退出清理模式。");
		}
	}

	private static string KindName(ScrapKind kind)
	{
		if (kind != ScrapKind.Dropped)
		{
			return "放置物";
		}
		return "掉落物";
	}

	private static string Truncate(string value, int max)
	{
		if (string.IsNullOrEmpty(value) || value.Length <= max)
		{
			return value;
		}
		return value.Substring(0, max) + "…";
	}
}
internal sealed class CleanRequestRelay : MonoBehaviour, IOnEventCallback
{
	private const byte EventCode = 190;

	private const byte KeyViewId = 1;

	private const byte KeyActor = 2;

	private const byte KeyMagic = 3;

	private const string MagicValue = "ScrapClean/190/1";

	private const float RequestCooldown = 2f;

	private const float InboundCooldown = 5f;

	private static CleanRequestRelay _instance;

	private static readonly Dictionary<int, float> RecentRequests = new Dictionary<int, float>();

	private static readonly Dictionary<int, float> InboundHandled = new Dictionary<int, float>();

	private static readonly List<int> ExpiredScratch = new List<int>(16);

	private bool _subscribed;

	private void OnEnable()
	{
		TrySubscribe();
	}

	private void OnDisable()
	{
		if (_subscribed)
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			_subscribed = false;
		}
	}

	private void Update()
	{
		if (!_subscribed)
		{
			TrySubscribe();
		}
	}

	private void TrySubscribe()
	{
		if (_subscribed)
		{
			return;
		}
		try
		{
			PhotonNetwork.AddCallbackTarget((object)this);
			_subscribed = true;
		}
		catch (Exception ex)
		{
			SCLog.Verbose("注册网络回调等待中:" + ex.Message);
		}
	}

	internal static CleanRequestRelay Ensure(GameObject host)
	{
		if ((Object)(object)_instance != (Object)null && (Object)(object)((Component)_instance).gameObject == (Object)(object)host)
		{
			return _instance;
		}
		_instance = host.GetComponent<CleanRequestRelay>();
		if ((Object)(object)_instance == (Object)null)
		{
			_instance = host.AddComponent<CleanRequestRelay>();
		}
		return _instance;
	}

	internal static bool RequestRemoteDelete(PhotonView view)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: 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_008b: Expected O, but got Unknown
		//IL_008b: 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_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Expected O, but got Unknown
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)view == (Object)null || view.ViewID < 1)
		{
			return false;
		}
		if (!PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode)
		{
			return false;
		}
		float unscaledTime = Time.unscaledTime;
		PruneRecentRequests(unscaledTime);
		int viewID = view.ViewID;
		if (RecentRequests.TryGetValue(viewID, out var value) && unscaledTime < value)
		{
			return true;
		}
		Hashtable val = new Hashtable();
		val.Add((byte)3, (object)"ScrapClean/190/1");
		val.Add((byte)1, (object)viewID);
		val.Add((byte)2, (object)((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0));
		Hashtable val2 = val;
		try
		{
			RaiseEventOptions val3 = new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			};
			bool num = PhotonNetwork.RaiseEvent((byte)190, (object)val2, val3, SendOptions.SendReliable);
			if (num)
			{
				RecentRequests[viewID] = unscaledTime + 2f;
			}
			return num;
		}
		catch (Exception ex)
		{
			SCLog.Warn("发送清理请求失败:" + ex.Message);
			return false;
		}
	}

	internal static void ResetRequestCooldowns()
	{
		RecentRequests.Clear();
		InboundHandled.Clear();
	}

	private static void PruneRecentRequests(float now)
	{
		if (RecentRequests.Count == 0)
		{
			return;
		}
		ExpiredScratch.Clear();
		foreach (KeyValuePair<int, float> recentRequest in RecentRequests)
		{
			if (now >= recentRequest.Value)
			{
				ExpiredScratch.Add(recentRequest.Key);
			}
		}
		for (int i = 0; i < ExpiredScratch.Count; i++)
		{
			RecentRequests.Remove(ExpiredScratch[i]);
		}
	}

	private static void PruneInbound(float now)
	{
		if (InboundHandled.Count == 0)
		{
			return;
		}
		ExpiredScratch.Clear();
		foreach (KeyValuePair<int, float> item in InboundHandled)
		{
			if (now >= item.Value)
			{
				ExpiredScratch.Add(item.Key);
			}
		}
		for (int i = 0; i < ExpiredScratch.Count; i++)
		{
			InboundHandled.Remove(ExpiredScratch[i]);
		}
	}

	public void OnEvent(EventData photonEvent)
	{
		if (photonEvent == null || photonEvent.Code != 190)
		{
			return;
		}
		try
		{
			object customData = photonEvent.CustomData;
			Hashtable val = (Hashtable)((customData is Hashtable) ? customData : null);
			if (val == null)
			{
				return;
			}
			if (!(val[(byte)3] is string text) || text != "ScrapClean/190/1")
			{
				SCLog.WarnThrottled("relay.legacy", "收到一条旧版本格式的清理请求(对方模组版本低于 1.2.0),已忽略");
			}
			else
			{
				if (!val.ContainsKey((byte)1))
				{
					return;
				}
				int num = ToInt(val[(byte)1]);
				if (num < 1)
				{
					return;
				}
				float unscaledTime = Time.unscaledTime;
				PruneInbound(unscaledTime);
				if (InboundHandled.TryGetValue(num, out var value) && unscaledTime < value)
				{
					return;
				}
				InboundHandled[num] = unscaledTime + 5f;
				int num2 = (val.ContainsKey((byte)2) ? ToInt(val[(byte)2]) : 0);
				PhotonView photonView = PhotonNetwork.GetPhotonView(num);
				if ((Object)(object)photonView == (Object)null)
				{
					return;
				}
				int num3 = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
				bool flag = photonView.IsMine;
				if (!flag && num3 > 0)
				{
					if (photonView.OwnerActorNr == num3)
					{
						flag = true;
					}
					else if (photonView.CreatorActorNr == num3)
					{
						flag = photonView.OwnerActorNr == 0 || photonView.Owner == null || photonView.Owner.IsInactive;
					}
				}
				if (flag && IsInboundTargetAllowed(photonView))
				{
					GameObject gameObject = ((Component)photonView).gameObject;
					SCLog.Info("响应清理请求,删除本地持有的物体:" + Cleaner.SafeName((Object)(object)gameObject) + "(发起者 Actor=" + num2 + ")");
					if (!Cleaner.DestroyWithOwnership(photonView, gameObject))
					{
						SCLog.WarnThrottled("relay.destroy.failed", "响应清理请求失败,已放弃:" + Cleaner.SafeName((Object)(object)gameObject));
					}
					else
					{
						DestroyCompanionRope(gameObject);
					}
				}
			}
		}
		catch (Exception ex)
		{
			SCLog.WarnThrottled("relay.event", "处理清理请求失败:" + ex.Message);
		}
	}

	private static bool IsInboundTargetAllowed(PhotonView view)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Invalid comparison between Unknown and I4
		GameObject go = ((Component)view).gameObject;
		if (!view.isRuntimeInstantiated)
		{
			SCLog.WarnThrottled("relay.reject.runtime", () => "忽略一条清理请求(非运行时实例化):" + Cleaner.SafeName((Object)(object)go));
			return false;
		}
		if ((Object)(object)((Component)view).GetComponent<Character>() != (Object)null)
		{
			SCLog.WarnThrottled("relay.reject.character", () => "忽略一条清理请求(目标是人形):" + Cleaner.SafeName((Object)(object)go));
			return false;
		}
		Item component = ((Component)view).GetComponent<Item>();
		if ((Object)(object)component != (Object)null)
		{
			if ((int)component.itemState != 0 || !go.activeInHierarchy)
			{
				SCLog.WarnThrottled("relay.reject.item", () => "忽略一条清理请求(道具不在可清理状态):" + Cleaner.SafeName((Object)(object)go));
				return false;
			}
			return true;
		}
		Rope component2 = ((Component)view).GetComponent<Rope>();
		if ((Object)(object)component2 != (Object)null && (int)component2.attachmenState == 1)
		{
			SCLog.WarnThrottled("relay.reject.spool", () => "忽略一条清理请求(目标是手持卷轴里的绳索):" + Cleaner.SafeName((Object)(object)go));
			return false;
		}
		if (!go.activeInHierarchy || !TargetScanner.IsPlacementShapedForRelay(go))
		{
			SCLog.WarnThrottled("relay.reject.shape", () => "忽略一条清理请求(目标形状未识别):" + Cleaner.SafeName((Object)(object)go));
			return false;
		}
		return true;
	}

	private static void DestroyCompanionRope(GameObject anchorRoot)
	{
		RopeAnchorWithRope component = anchorRoot.GetComponent<RopeAnchorWithRope>();
		if ((Object)(object)component == (Object)null)
		{
			return;
		}
		GameObject ropeInstance = component.ropeInstance;
		if (!((Object)(object)ropeInstance == (Object)null))
		{
			PhotonView component2 = ropeInstance.GetComponent<PhotonView>();
			if (!((Object)(object)component2 == (Object)null))
			{
				Cleaner.DestroyWithOwnership(component2, ropeInstance);
			}
		}
	}

	private static int ToInt(object value)
	{
		if (value is int)
		{
			return (int)value;
		}
		if (value is byte)
		{
			return (byte)value;
		}
		StructWrapper val = (StructWrapper)((value is StructWrapper) ? value : null);
		if (val != null)
		{
			object obj = val.Box();
			if (obj is int)
			{
				return (int)obj;
			}
			if (obj is byte)
			{
				return (byte)obj;
			}
		}
		try
		{
			return Convert.ToInt32(value);
		}
		catch (Exception)
		{
			return 0;
		}
	}
}
internal enum CleanUiToast
{
	Info,
	Success,
	Failure
}
internal struct CleanTargetInfo
{
	internal string Label;

	internal bool Placed;

	internal float Distance;

	internal bool Cleanable;

	internal string Reason;

	internal float AimAngle;

	internal bool Plain;
}
[DefaultExecutionOrder(-300)]
internal sealed class CleanUI : MonoBehaviour
{
	private enum Phase
	{
		Hidden,
		SlideIn,
		Hold,
		SlideOut
	}

	private const float RefWidth = 1920f;

	private const float RefHeight = 1080f;

	private const int CanvasSortOrder = 560;

	private const float CardTopY = -246f;

	private const float CardWidth = 620f;

	private const float CardPadTop = 8f;

	private const float CardPadBottom = 8f;

	private const float CardRowGap = 4f;

	private const float CardDividerHeight = 1f;

	private const float AccentBarWidth = 4f;

	private const float ToastAccentBarWidth = 3f;

	private const float TextInsetLeft = 22f;

	private const float TextInsetRight = 16f;

	private const float ModeColumnWidth = 300f;

	private const float ToastGapY = 16f;

	private const float ToastPadY = 8f;

	private const float ToastPadX = 18f;

	private const float ToastMinWidth = 220f;

	private const float ToastMaxWidth = 760f;

	private const float FontMode = 22f;

	private const float FontAction = 17f;

	private const float FontTarget = 20f;

	private const float FontToast = 22f;

	private const float SlideInTime = 0.16f;

	private const float SlideOutTime = 0.14f;

	private const float SlideDistance = 20f;

	private const string DefaultActionText = "滚轮切换 · 左键清理目标";

	private const string AreaActionText = "滚轮切换 · 左键清理圆内目标";

	private const int LabelMaxChars = 16;

	private const int LabelMaxCharsWithReason = 11;

	private const int ReasonMaxChars = 5;

	private const float PrimingTimeoutSeconds = 2f;

	private const string PrimeCorpus = "0123456789.,-+%/m°·×√●◎○()()::、,,  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz清理模式更新出错,已退(后续相同误不再输):详见日志将准星对要的表面请读取游戏状态失败本次视为被占用写入切换仍然生效圆内个目标没有附近可尝试单选类型地所权求执行成功跳过在野锥遮挡判定异常该物距离拒绝诊断空。射线未命中任何实体层进范围○按重掉落放置…注册网络回调等待发送收到一条旧版格方组低于忽略响应删除持起者弃处非运时例化是人形道具手卷轴里绳索识别滚轮·左键°×√●◎、偏角属你改变无法得连接房间均机管并超受检查拾消确踏板菇构建提示能字默认文显块还原转移环预热影指器使找抑制补丁安装复停路径快捷绑上限米只且此值就以;则太远玩家自己也看半点心否允许弹锚岩钉旗帜便携炉多联其他关闭秒细排问题打开创首性卡顿候扫描询继启动渲染完与材质前尾满血蘑云雾锁链魔豆藤蔓气球劣最位流刷嵌套样名包盒光照子由会沿它这绪";

	private GameObject _canvasObject;

	private CanvasGroup _canvasGroup;

	private GameObject _cardRoot;

	private RectTransform _cardRect;

	private Image _cardAccent;

	private TextMeshProUGUI _modeText;

	private TextMeshProUGUI _actionText;

	private TextMeshProUGUI _targetText;

	private GameObject _toastRoot;

	private RectTransform _toastRect;

	private CanvasGroup _toastGroup;

	private Image _toastAccent;

	private TextMeshProUGUI _toastText;

	private float _toastBaseY;

	private bool _built;

	private bool _fontWarned;

	private bool _buildFaultLogged;

	private CleanModeKind _mode;

	private Phase _phase;

	private float _phaseStart;

	private float _holdSeconds;

	private float _slideInFrom;

	private float _slideOutTo;

	private float _slideOutFrom;

	private float _alphaFrom = 1f;

	private string _pendingText;

	private CleanUiToast _pendingKind;

	private float _appliedAlpha = -1f;

	private float _appliedOffset = float.NaN;

	private bool _primingCardWasActive;

	private bool _primingToastWasActive;

	private bool _priming;

	private float _primingStartedAt;

	private bool _cardShownDuringPriming;

	internal bool CanBuild => (Object)(object)GUIManager.instance != (Object)null;

	internal bool CanPrime
	{
		get
		{
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			FontFallbackSwapper instance2 = FontFallbackSwapper.instance;
			if ((Object)(object)instance2 != (Object)null && (Object)(object)instance2.mainBaseFont != (Object)null)
			{
				return true;
			}
			if ((!((Object)(object)instance.interactNameText != (Object)null) || !((Object)(object)((TMP_Text)instance.interactNameText).font != (Object)null)) && (!((Object)(object)instance.interactPromptText != (Object)null) || !((Object)(object)((TMP_Text)instance.interactPromptText).font != (Object)null)))
			{
				if ((Object)(object)instance.itemPromptMain != (Object)null)
				{
					return (Object)(object)((TMP_Text)instance.itemPromptMain).font != (Object)null;
				}
				return false;
			}
			return true;
		}
	}

	internal bool IsShowing
	{
		get
		{
			if (_phase == Phase.Hidden && (!((Object)(object)_cardRoot != (Object)null) || !_cardRoot.activeSelf))
			{
				if ((Object)(object)_toastRoot != (Object)null)
				{
					return _toastRoot.activeSelf;
				}
				return false;
			}
			return true;
		}
	}

	private void Awake()
	{
		ScrapCleanWarmup.Attach(((Component)this).transform);
		ScrapCleanWarmup.RegisterUi(this);
	}

	private void OnDestroy()
	{
		ScrapCleanWarmup.UnregisterUi(this);
		if ((Object)(object)_canvasObject != (Object)null)
		{
			Object.Destroy((Object)(object)_canvasObject);
			_canvasObject = null;
		}
		_canvasGroup = null;
		_cardRoot = null;
		_cardRect = null;
		_cardAccent = null;
		_modeText = null;
		_actionText = null;
		_targetText = null;
		_toastRoot = null;
		_toastRect = null;
		_toastGroup = null;
		_toastAccent = null;
		_toastText = null;
	}

	private void Update()
	{
		if (_phase == Phase.Hidden)
		{
			return;
		}
		float unscaledTime = Time.unscaledTime;
		float num = unscaledTime - _phaseStart;
		switch (_phase)
		{
		case Phase.SlideIn:
		{
			float num4 = Mathf.Clamp01(num / 0.16f);
			float num5 = EaseOutCubic(num4);
			Apply(Mathf.Lerp(_slideInFrom, 0f, num5), num5);
			if (!(num4 < 1f))
			{
				_phase = Phase.Hold;
				_phaseStart = unscaledTime;
			}
			break;
		}
		case Phase.Hold:
			Apply(0f, 1f);
			if (num >= _holdSeconds)
			{
				_phase = Phase.SlideOut;
				_phaseStart = unscaledTime;
				_slideOutFrom = 0f;
				_alphaFrom = 1f;
			}
			break;
		case Phase.SlideOut:
		{
			float num2 = Mathf.Clamp01(num / 0.14f);
			float num3 = EaseInCubic(num2);
			Apply(Mathf.Lerp(_slideOutFrom, _slideOutTo, num3), Mathf.Lerp(_alphaFrom, 0f, num3));
			if (!(num2 < 1f))
			{
				if (_pendingText != null)
				{
					string pendingText = _pendingText;
					CleanUiToast pendingKind = _pendingKind;
					_pendingText = null;
					PrepareToast(pendingText, pendingKind);
					_phase = Phase.SlideIn;
					_phaseStart = unscaledTime;
				}
				else
				{
					_phase = Phase.Hidden;
					SetToastVisible(visible: false);
				}
			}
			break;
		}
		}
	}

	internal void SetMode(CleanModeKind mode)
	{
		ApplyMode(mode);
	}

	internal void SetTarget(CleanTargetInfo info)
	{
		if (!EnsureBuilt())
		{
			return;
		}
		if (string.IsNullOrEmpty(info.Label))
		{
			HideSelectionLabel();
			return;
		}
		string text = ComposeTarget(info);
		if ((Object)(object)_targetText != (Object)null && !string.Equals(((TMP_Text)_targetText).text, text, StringComparison.Ordinal))
		{
			((TMP_Text)_targetText).text = text;
		}
		ShowCard();
	}

	internal void ClearTarget()
	{
		HideSelectionLabel();
	}

	internal static CleanTargetInfo Target(string label, bool placed, float distance, bool cleanable, string reason, float aimAngle)
	{
		return new CleanTargetInfo
		{
			Label = label,
			Placed = placed,
			Distance = distance,
			Cleanable = cleanable,
			Reason = reason,
			AimAngle = aimAngle
		};
	}

	internal static CleanTargetInfo Status(string label)
	{
		return new CleanTargetInfo
		{
			Label = label,
			Plain = true
		};
	}

	internal static CleanTargetInfo Status(string label, bool good)
	{
		return new CleanTargetInfo
		{
			Label = label,
			Plain = true,
			Cleanable = good
		};
	}

	internal void SetActionHint(string text)
	{
		if (!string.IsNullOrEmpty(text) && !((Object)(object)_actionText == (Object)null))
		{
			string text2 = SafeGlyphs(text);
			if (!string.Equals(((TMP_Text)_actionText).text, text2, StringComparison.Ordinal))
			{
				((TMP_Text)_actionText).text = text2;
			}
		}
	}

	internal void ShowMode(string title, string subtitle, bool entering, float holdSeconds)
	{
		if (EnsureBuilt())
		{
			string text = title;
			if (!entering && !string.IsNullOrEmpty(subtitle))
			{
				text = title + " · " + subtitle;
			}
			else
			{
				SetActionHint(subtitle);
			}
			ShowToast(text, CleanUiToast.Info, holdSeconds, entering);
		}
	}

	internal void ShowSwitch(string title, string subtitle, bool upward, float holdSeconds)
	{
		if (EnsureBuilt())
		{
			SetActionHint(subtitle);
			ShowSwitchToast(title, CleanUiToast.Info, upward, holdSeconds);
		}
	}

	internal void ShowResult(bool success, string text, float holdSeconds)
	{
		ShowToast(text, success ? CleanUiToast.Success : CleanUiToast.Failure, holdSeconds, entering: true);
	}

	internal void HideSelectionLabel()
	{
		if ((Object)(object)_cardRoot != (Object)null && _cardRoot.activeSelf)
		{
			_cardRoot.SetActive(false);
		}
	}

	internal void HideImmediate()
	{
		_pendingText = null;
		_phase = Phase.Hidden;
		SetToastVisible(visible: false);
	}

	internal bool BeginPriming()
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		if (!EnsureBuilt())
		{
			return false;
		}
		_primingCardWasActive = _cardRoot.activeSelf;
		_primingToastWasActive = _toastRoot.activeSelf;
		_priming = true;
		_primingStartedAt = Time.unscaledTime;
		_cardShownDuringPriming = false;
		_canvasGroup.alpha = 0f;
		_cardRoot.SetActive(true);
		_toastRoot.SetActive(true);
		_toastGroup.alpha = 1f;
		_toastRect.anchoredPosition = new Vector2(0f, _toastBaseY);
		((TMP_Text)_modeText).text = ModeLabel(CleanModeKind.Single);
		((TMP_Text)_actionText).text = ModeActionText(CleanModeKind.Single);
		((TMP_Text)_targetText).text = "<color=" + ScrapCleanPalette.HexTextSecondary + ">放置物</color> <color=" + ScrapCleanPalette.HexTextPrimary + ">踏板菇</color>  <color=" + ScrapCleanPalette.HexTextSecondary + ">12.4 m</color>  <color=" + ScrapCleanPalette.HexSuccess + ">√ 可清理</color>";
		((TMP_Text)_toastText).text = "0123456789.,-+%/m°·×√●◎○()()::、,,  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz清理模式更新出错,已退(后续相同误不再输):详见日志将准星对要的表面请读取游戏状态失败本次视为被占用写入切换仍然生效圆内个目标没有附近可尝试单选类型地所权求执行成功跳过在野锥遮挡判定异常该物距离拒绝诊断空。射线未命中任何实体层进范围○按重掉落放置…注册网络回调等待发送收到一条旧版格方组低于忽略响应删除持起者弃处非运时例化是人形道具手卷轴里绳索识别滚轮·左键°×√●◎、偏角属你改变无法得连接房间均机管并超受检查拾消确踏板菇构建提示能字默认文显块还原转移环预热影指器使找抑制补丁安装复停路径快捷绑上限米只且此值就以;则太远玩家自己也看半点心否允许弹锚岩钉旗帜便携炉多联其他关闭秒细排问题打开创首性卡顿候扫描询继启动渲染完与材质前尾满血蘑云雾锁链魔豆藤蔓气球劣最位流刷嵌套样名包盒光照子由会沿它这绪";
		Canvas.ForceUpdateCanvases();
		return true;
	}

	internal void EndPriming()
	{
		bool priming = _priming;
		_priming = false;
		if ((Object)(object)_canvasGroup != (Object)null)
		{
			_canvasGroup.alpha = 1f;
		}
		if (!priming)
		{
			return;
		}
		if (_phase == Phase.Hidden)
		{
			if ((Object)(object)_toastGroup != (Object)null)
			{
				_toastGroup.alpha = 0f;
			}
			if ((Object)(object)_toastRoot != (Object)null)
			{
				_toastRoot.SetActive(_primingToastWasActive);
			}
			_appliedAlpha = -1f;
			_appliedOffset = float.NaN;
			_pendingText = null;
		}
		if ((Object)(object)_cardRoot != (Object)null)
		{
			_cardRoot.SetActive(_primingCardWasActive || _cardShownDuringPriming);
		}
		_cardShownDuringPriming = false;
		ApplyMode(_mode);
	}

	private void ShowToast(string text, CleanUiToast kind, float holdSeconds, bool entering)
	{
		if (EnsureBuilt())
		{
			PrepareToast(text, kind);
			_pendingText = null;
			_slideInFrom = (entering ? (-20f) : 20f);
			_slideOutTo = 0f - _slideInFrom;
			_holdSeconds = Mathf.Max(0.05f, holdSeconds);
			Apply(_slideInFrom, 0f);
			_phase = Phase.SlideIn;
			_phaseStart = Time.unscaledTime;
		}
	}

	private void ShowSwitchToast(string text, CleanUiToast kind, bool upward, float holdSeconds)
	{
		if (EnsureBuilt())
		{
			_slideOutTo = (upward ? 20f : (-20f));
			_slideInFrom = 0f - _slideOutTo;
			_holdSeconds = Mathf.Max(0.05f, holdSeconds);
			if (_phase == Phase.Hidden)
			{
				PrepareToast(text, kind);
				_pendingText = null;
				Apply(_slideInFrom, 0f);
				_phase = Phase.SlideIn;
				_phaseStart = Time.unscaledTime;
			}
			else
			{
				_pendingText = text;
				_pendingKind = kind;
				_slideOutFrom = (float.IsNaN(_appliedOffset) ? 0f : _appliedOffset);
				_alphaFrom = ((_appliedAlpha < 0f) ? 1f : _appliedAlpha);
				_phase = Phase.SlideOut;
				_phaseStart = Time.unscaledTime;
			}
		}
	}

	private void PrepareToast(string text, CleanUiToast kind)
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		RecoverStalledPriming();
		string text2 = SafeGlyphs(text);
		switch (kind)
		{
		case CleanUiToast.Success:
			text2 = "√ " + text2;
			break;
		case CleanUiToast.Failure:
			text2 = "× " + text2;
			break;
		}
		SetToastVisible(visible: true);
		if ((Object)(object)_toastText != (Object)null && !string.Equals(((TMP_Text)_toastText).text, text2, StringComparison.Ordinal))
		{
			((TMP_Text)_toastText).text = text2;
		}
		ApplyToastWidth(text2);
		if ((Object)(object)_toastAccent != (Object)null)
		{
			((Graphic)_toastAccent).color = (Color)(kind switch
			{
				CleanUiToast.Failure => ScrapCleanPalette.Failure, 
				CleanUiToast.Success => ScrapCleanPalette.Success, 
				_ => ScrapCleanPalette.ModeAccent(_mode), 
			});
		}
	}

	private void ApplyToastWidth(string body)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: 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_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_toastRect == (Object)null) && !((Object)(object)_toastText == (Object)null))
		{
			float num = 724f;
			float num2 = 760f;
			try
			{
				num2 = Mathf.Clamp(((TMP_Text)_toastText).GetPreferredValues(body, num, 0f).x + 36f, 220f, 760f);
			}
			catch (Exception)
			{
				num2 = 760f;
			}
			Vector2 sizeDelta = _toastRect.sizeDelta;
			if (!Mathf.Approximately(sizeDelta.x, num2))
			{
				_toastRect.sizeDelta = new Vector2(num2, sizeDelta.y);
			}
		}
	}

	private void Apply(float offsetY, float alpha)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_toastRect == (Object)null || (Object)(object)_toastGroup == (Object)null)
		{
			_phase = Phase.Hidden;
			return;
		}
		if (!Mathf.Approximately(offsetY, _appliedOffset))
		{
			_appliedOffset = offsetY;
			_toastRect.anchoredPosition = new Vector2(0f, _toastBaseY + offsetY);
		}
		if (!Mathf.Approximately(alpha, _appliedAlpha))
		{
			_appliedAlpha = alpha;
			_toastGroup.alpha = alpha;
		}
	}

	private void SetToastVisible(bool visible)
	{
		if ((Object)(object)_toastRoot != (Object)null && _toastRoot.activeSelf != visible)
		{
			_toastRoot.SetActive(visible);
		}
		if (!visible)
		{
			_appliedAlpha = -1f;
			_appliedOffset = float.NaN;
		}
	}

	private void RecoverStalledPriming()
	{
		if (_priming && !(Time.unscaledTime - _primingStartedAt < 2f))
		{
			_priming = false;
			_cardShownDuringPriming = false;
			if ((Object)(object)_canvasGroup != (Object)null)
			{
				_canvasGroup.alpha = 1f;
			}
			if ((Object)(object)_toastGroup != (Object)null)
			{
				_toastGroup.alpha = 0f;
			}
			if ((Object)(object)_toastRoot != (Object)null)
			{
				_toastRoot.SetActive(false);
			}
			if ((Object)(object)_cardRoot != (Object)null)
			{
				_cardRoot.SetActive(false);
			}
		}
	}

	private void ShowCard()
	{
		RecoverStalledPriming();
		if (!((Object)(object)_cardRoot == (Object)null))
		{
			if (_priming)
			{
				_cardShownDuringPriming = true;
			}
			if (!_cardRoot.activeSelf)
			{
				_cardRoot.SetActive(true);
			}
		}
	}

	private void ApplyMode(CleanModeKind mode)
	{
		//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_0023: 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)
		_mode = mode;
		if (!((Object)(object)_modeText == (Object)null))
		{
			Color color = ScrapCleanPalette.ModeAccent(mode);
			((Graphic)_cardAccent).color = color;
			((Graphic)_modeText).color = color;
			((TMP_Text)_modeText).text = ModeLabel(mode);
			((TMP_Text)_actionText).text = ModeActionText(mode);
		}
	}

	internal static string ModeLabel(CleanModeKind mode)
	{
		if (mode != CleanModeKind.Area)
		{
			return "● 单选清理";
		}
		return "◎ 范围清理";
	}

	internal static string ModeActionText(CleanModeKind mode)
	{
		if (mode != CleanModeKind.Area)
		{
			return "滚轮切换 · 左键清理目标";
		}
		return "滚轮切换 · 左键清理圆内目标";
	}

	private bool EnsureBuilt()
	{
		if (_built)
		{
			return true;
		}
		if (!CanBuild)
		{
			return false;
		}
		try
		{
			BuildHierarchy();
			_built = true;
			return true;
		}
		catch (Exception ex)
		{
			if (!_buildFaultLogged)
			{
				_buildFaultLogged = true;
				SCLog.Warn("构建提示 UI 失败:" + ex.Message);
			}
			if ((Object)(object)_canvasObject != (Object)null)
			{
				Object.Destroy((Object)(object)_canvasObject);
				_canvasObject = null;
			}
			_canvasGroup = null;
			_cardRoot = null;
			_cardRect = null;
			_cardAccent = null;
			_modeText = null;
			_actionText = null;
			_targetText = null;
			_toastRoot = null;
			_toastRect = null;
			_toastGroup = null;
			_toastAccent = null;
			_toastText = null;
			return false;
		}
	}

	private void BuildHierarchy()
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		TMP_FontAsset font = ResolveFont();
		float num = RowHeight(22f);
		float num2 = RowHeight(20f);
		float num3 = 8f + num + 4f + 1f + 4f + num2 + 8f;
		float toastHeight = RowHeight(22f) + 16f;
		_toastBaseY = -246f - num3 - 16f;
		_canvasObject = new GameObject("ScrapCleanCanvas");
		_canvasObject.transform.SetParent(((Component)this).transform, false);
		Canvas obj = _canvasObject.AddComponent<Canvas>();
		obj.renderMode = (RenderMode)0;
		obj.sortingOrder = 560;
		CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>();
		obj2.uiScaleMode = (ScaleMode)1;
		obj2.referenceResolution = new Vector2(1920f, 1080f);
		obj2.screenMatchMode = (ScreenMatchMode)0;
		obj2.matchWidthOrHeight = 1f;
		_canvasGroup = _canvasObject.AddComponent<CanvasGroup>();
		_canvasGroup.interactable = false;
		_canvasGroup.blocksRaycasts = false;
		BuildCard(font, num, num2, num3);
		BuildToast(font, toastHeight);
		ApplyMode(_mode);
		_cardRoot.SetActive(false);
		_toastRoot.SetActive(false);
	}

	private void BuildCard(TMP_FontAsset font, float modeRow, float targetRow, float cardHeight)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0216: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		Image val = CreateImage("Card", _canvasObject.transform, ScrapCleanPalette.CardBackground);
		_cardRoot = ((Component)val).gameObject;
		_cardRect = (RectTransform)((Component)val).transform;
		_cardRect.anchorMin = new Vector2(0.5f, 0.5f);
		_cardRect.anchorMax = new Vector2(0.5f, 0.5f);
		_cardRect.pivot = new Vector2(0.5f, 1f);
		_cardRect.anchoredPosition = new Vector2(0f, -246f);
		_cardRect.sizeDelta = new Vector2(620f, cardHeight);
		RectTransform val2 = (RectTransform)((Component)(_cardAccent = CreateImage("Accent", (Transform)(object)_cardRect, ScrapCleanPalette.ModeSingle))).transform;
		val2.anchorMin = new Vector2(0f, 0f);
		val2.anchorMax = new Vector2(0f, 1f);
		val2.pivot = new Vector2(0f, 0.5f);
		val2.anchoredPosition = Vector2.zero;
		val2.sizeDelta = new Vector2(4f, 0f);
		_modeText = CreateText("Mode", (Transform)(object)_cardRect, 22f, (TextAlignmentOptions)513, ScrapCleanPalette.ModeSingle, font, richText: false);
		PlaceTopLeft(((TMP_Text)_modeText).rectTransform, 22f, 8f, 300f, modeRow);
		float width = 282f;
		_actionText = CreateText("Action", (Transform)(object)_cardRect, 17f, (TextAlignmentOptions)516, ScrapCleanPalette.TextSecondary, font, richText: false);
		PlaceTopRight(((TMP_Text)_actionText).rectTransform, 16f, 8f, width, modeRow);
		RectTransform val3 = (RectTransform)((Component)CreateImage("Divider", (Transform)(object)_cardRect, ScrapCleanPalette.Divider)).transform;
		val3.anchorMin = new Vector2(0f, 1f);
		val3.anchorMax = new Vector2(1f, 1f);
		val3.pivot = new Vector2(0.5f, 1f);
		float num = 8f + modeRow + 4f;
		val3.offsetMin = new Vector2(22f, 0f - (num + 1f));
		val3.offsetMax = new Vector2(-16f, 0f - num);
		_targetText = CreateText("Target", (Transform)(object)_cardRect, 20f, (TextAlignmentOptions)513, ScrapCleanPalette.TextPrimary, font, richText: true);
		PlaceTopBand(((TMP_Text)_targetText).rectTransform, num + 1f + 4f, targetRow);
	}

	private void BuildToast(TMP_FontAsset font, float toastHeight)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: 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_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		Image val = CreateImage("Toast", _canvasObject.transform, ScrapCleanPalette.ToastBackground);
		_toastRoot = ((Component)val).gameObject;
		_toastRect = (RectTransform)((Component)val).transform;
		_toastRect.anchorMin = new Vector2(0.5f, 0.5f);
		_toastRect.anchorMax = new Vector2(0.5f, 0.5f);
		_toastRect.pivot = new Vector2(0.5f, 1f);
		_toastRect.anchoredPosition = new Vector2(0f, _toastBaseY);
		_toastRect.sizeDelta = new Vector2(220f, toastHeight);
		_toastGroup = _toastRoot.AddComponent<CanvasGroup>();
		_toastGroup.interactable = false;
		_toastGroup.blocksRaycasts = false;
		RectTransform val2 = (RectTransform)((Component)(_toastAccent = CreateImage("Accent", (Transform)(object)_toastRect, ScrapCleanPalette.ModeSingle))).transform;
		val2.anchorMin = new Vector2(0f, 0f);
		val2.anchorMax = new Vector2(0f, 1f);
		val2.pivot = new Vector2(0f, 0.5f);
		val2.anchoredPosition = Vector2.zero;
		val2.sizeDelta = new Vector2(3f, 0f);
		_toastText = CreateText("Text", (Transform)(object)_toastRect, 22f, (TextAlignmentOptions)514, ScrapCleanPalette.TextPrimary, font, richText: true);
		RectTransform rectTransform = ((TMP_Text)_toastText).rectTransform;
		rectTransform.anchorMin = Vector2.zero;
		rectTransform.anchorMax = Vector2.one;
		rectTransform.pivot = new Vector2(0.5f, 0.5f);
		rectTransform.offsetMin = new Vector2(18f, 0f);
		rectTransform.offsetMax = new Vector2(-18f, 0f);
	}

	private static void PlaceTopLeft(RectTransform rect, float left, float top, float width, float height)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		rect.anchorMin = new Vector2(0f, 1f);
		rect.anchorMax = new Vector2(0f, 1f);
		rect.pivot = new Vector2(0f, 1f);
		rect.anchoredPosition = new Vector2(left, 0f - top);
		rect.sizeDelta = new Vector2(width, height);
	}

	private static void PlaceTopRight(RectTransform rect, float right, float top, float width, float height)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		rect.anchorMin = new Vector2(1f, 1f);
		rect.anchorMax = new Vector2(1f, 1f);
		rect.pivot = new Vector2(1f, 1f);
		rect.anchoredPosition = new Vector2(0f - right, 0f - top);
		rect.sizeDelta = new Vector2(width, height);
	}

	private static void PlaceTopBand(RectTransform rect, float top, float height)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		rect.anchorMin = new Vector2(0f, 1f);
		rect.anchorMax = new Vector2(1f, 1f);
		rect.pivot = new Vector2(0.5f, 1f);
		rect.offsetMin = new Vector2(22f, 0f - (top + height));
		rect.offsetMax = new Vector2(-16f, 0f - top);
	}

	private static float RowHeight(float fontSize)
	{
		return Mathf.Ceil(fontSize * 1.45f);
	}

	private TMP_FontAsset ResolveFont()
	{
		FontFallbackSwapper instance = FontFallbackSwapper.instance;
		if ((Object)(object)instance != (Object)null && (Object)(object)instance.mainBaseFont != (Object)null)
		{
			return instance.mainBaseFont;
		}
		GUIManager instance2 = GUIManager.instance;
		if ((Object)(object)instance2 != (Object)null)
		{
			if ((Object)(object)instance2.interactNameText != (Object)null && (Object)(object)((TMP_Text)instance2.interactNameText).font != (Object)null)
			{
				return ((TMP_Text)instance2.interactNameText).font;
			}
			if ((Object)(object)instance2.interactPromptText != (Object)null && (Object)(object)((TMP_Text)instance2.interactPromptText).font != (Object)null)
			{
				return ((TMP_Text)instance2.interactPromptText).font;
			}
			if ((Object)(object)instance2.itemPromptMain != (Object)null && (Object)(object)((TMP_Text)instance2.itemPromptMain).font != (Object)null)
			{
				return ((TMP_Text)instance2.itemPromptMain).font;
			}
		}
		if (!_fontWarned)
		{
			_fontWarned = true;
			SCLog.Warn("未能取到游戏字体,已回退到 TMP 默认字体(中文可能显示为方块)。");
		}
		return TMP_Settings.defaultFontAsset;
	}

	private static Image CreateImage(string name, Transform parent, Color color)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) })
		{
			layer = UiLayer()
		};
		val.transform.SetParent(parent, false);
		Image val2 = val.AddComponent<Image>();
		((Graphic)val2).color = color;
		((Graphic)val2).raycastTarget = false;
		RectTransform val3 = (RectTransform)val.transform;
		((Transform)val3).localScale = Vector3.one;
		((Transform)val3).localRotation = Quaternion.identity;
		val3.anchoredPosition3D = Vector3.zero;
		return val2;
	}

	private static TextMeshProUGUI CreateText(string name, Transform parent, float size, TextAlignmentOptions alignment, Color color, TMP_FontAsset font, bool richText)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: 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_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) })
		{
			layer = UiLayer()
		};
		val.transform.SetParent(parent, false);
		TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
		if ((Object)(object)font != (Object)null)
		{
			((TMP_Text)val2).font = font;
		}
		((TMP_Text)val2).fontSize = size;
		((TMP_Text)val2).enableAutoSizing = false;
		((TMP_Text)val2).alignment = alignment;
		((Graphic)val2).color = color;
		((TMP_Text)val2).richText = richText;
		((Graphic)val2).raycastTarget = false;
		((TMP_Text)val2).margin = Vector4.zero;
		((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
		((TMP_Text)val2).overflowMode = (TextOverflowModes)0;
		RectTransform val3 = (RectTransform)val.transform;
		((Transform)val3).localScale = Vector3.one;
		((Transform)val3).localRotation = Quaternion.identity;
		val3.anchoredPosition3D = Vector3.zero;
		return val2;
	}

	private static int UiLayer()
	{
		int num = LayerMask.NameToLayer("UI");
		if (num < 0)
		{
			return 5;
		}
		return num;
	}

	private static string ComposeTarget(CleanTargetInfo info)
	{
		bool flag = !info.Plain && !info.Cleanable && !string.IsNullOrEmpty(info.Reason);
		string value = Clamp(SafeGlyphs(info.Label), flag ? 11 : 16);
		StringBuilder stringBuilder = new StringBuilder(96);
		if (!info.Plain)
		{
			stringBuilder.Append("<color=").Append(ScrapCleanPalette.HexTextSecondary).Append('>');
			stringBuilder.Append(info.Placed ? "放置物" : "掉落物");
			stringBuilder.Append("</color> ");
		}
		string value2 = ((info.Plain && !info.Cleanable) ? ScrapCleanPalette.HexTextSecondary : ScrapCleanPalette.HexTextPrimary);
		stringBuilder.Append("<color=").Append(value2).Append('>');
		stringBuilder.Append(value);
		stringBuilder.Append("</color>");
		if (!flag && info.Distance > 0.05f)
		{
			float distance = info.Distance;
			string value3 = ((Mathf.Abs(distance - Mathf.Round(distance)) < 0.05f) ? Mathf.RoundToInt(distance).ToString(CultureInfo.InvariantCulture) : distance.ToString("0.0", CultureInfo.InvariantCulture));
			stringBuilder.Append("  <color=").Append(ScrapCleanPalette.HexTextSecondary).Append('>');
			stringBuilder.Append(value3);
			stringBuilder.Append(" m</color>");
		}
		if (!info.Plain)
		{
			if (info.Cleanable)
			{
				stringBuilder.Append("  <color=").Append(ScrapCleanPalette.HexSuccess).Append(">√ 可清理</color>");
			}
			else
			{
				stringBuilder.Append("  <color=").Append(ScrapCleanPalette.HexFailure).Append(">× 不可清理</color>");
				if (!string.IsNullOrEmpty(info.Reason))
				{
					stringBuilder.Append(" <color=").Append(ScrapCleanPalette.HexTextSecondary).Append('>');
					stringBuilder.Append(Clamp(SafeGlyphs(info.Reason), 5));
					stringBuilder.Append("</color>");
				}
			}
		}
		if (info.AimAngle > 0f && ScrapCleanConfig.VerboseLogs != null && ScrapCleanConfig.VerboseLogs.Value)
		{
			stringBuilder.Append("  <color=").Append(ScrapCleanPalette.HexTextSecondary).Append(">偏角 ");
			stringBuilder.Append(Mathf.RoundToInt(info.AimAngle).ToString(CultureInfo.InvariantCulture));
			stringBuilder.Append("°</color>");
		}
		return stringBuilder.ToString();
	}

	private static string SafeGlyphs(string text)
	{
		if (string.IsNullOrEmpty(text))
		{
			return text;
		}
		char[] array = null;
		for (int i = 0; i < text.Length; i++)
		{
			char c = text[i];
			char c2 = c;
			switch (c)
			{
			case '■':
			case '▣':
			case '⬤':
				c2 = '●';
				break;
			case '✕':
			case '✖':
				c2 = '×';
				break;
			case '✓':
			case '✔':
				c2 = '√';
				break;
			case '<':
				c2 = '(';
				break;
			case '>':
				c2 = ')';
				break;
			}
			if (c2 != c)
			{
				if (array == null)
				{
					array = text.ToCharArray();
				}
				array[i] = c2;
			}
		}
		if (array != null)
		{
			return new string(array);
		}
		return text;
	}

	private static string Clamp(string text, int max)
	{
		if (string.IsNullOrEmpty(text) || text.Length <= max)
		{
			return text;
		}
		return text.Substring(0, max) + "…";
	}

	private static float EaseOutCubic(float t)
	{
		float num = 1f - t;
		return 1f - num * num * num;
	}

	private static float EaseInCubic(float t)
	{
		return t * t * t;
	}
}
internal sealed class GroundRing : MonoBehaviour
{
	private const int MaxSegments = 96;

	private const float SegmentSpacing = 0.12f;

	private const float DashRatio = 0.55f;

	private const float DashChunkLength = 0.4f;

	private const float LineWidth = 0.055f;

	private const float VerticalBand = 0.12f;

	private const float SnapRadius = 0.08f;

	private const float SnapDistance = 1.6f;

	private const float PrimeMeshExtent = 0.001f;

	private const float PrimeHostHeight = -10000f;

	private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");

	private static readonly int LegacyColorId = Shader.PropertyToID("_Color");

	private static readonly int CullId = Shader.PropertyToID("_Cull");

	private MeshFilter _filter;

	private MeshRenderer _renderer;

	private Mesh _mesh;

	private Material _material;

	private bool _materialReady;

	private bool _materialGivenUp;

	private int _materialAttempts;

	private float _nextMaterialRetry;

	private const float MaterialRetryInterval = 2f;

	private const int MaxMaterialAttempts = 12;

	private GameObject _primeHost;

	private Mesh _primeMesh;

	private int _segments = 96;

	private readonly List<Vector3> _vertices = new List<Vector3>(768);

	private readonly List<int> _triangles = new List<int>(1152);

	private readonly Vector3[] _points = (Vector3[])(object)new Vector3[97];

	private readonly bool[] _valid = new bool[97];

	private bool _sampled;

	private Vector3 _sampledCenter;

	private Vector3 _sampledNormal;

	private float _sampledRadius;

	private void Awake()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Expected O, but got Unknown
		_filter = ((Component)this).gameObject.AddComponent<MeshFilter>();
		_renderer = ((Component)this).gameObject.AddComponent<MeshRenderer>();
		ApplyRendererSettings((Renderer)(object)_renderer);
		_mesh = new Mesh
		{
			name = "ScrapCleanRing",
			indexFormat = (IndexFormat)1
		};
		_mesh.MarkDynamic();
		_filter.sharedMesh = _mesh;
		EnsureMaterial();
		((Renderer)_renderer).enabled = false;
		ScrapCleanWarmup.RegisterRing(this);
	}

	private void OnDestroy()
	{
		ScrapCleanWarmup.UnregisterRing(this);
		ReleasePrimeHost();
		if ((Object)(object)_mesh != (Object)null)
		{
			Object.Destroy((Object)(object)_mesh);
			_mesh = null;
		}
		if ((Object)(object)_material != (Object)null)
		{
			Object.Destroy((Object)(object)_material);
			_material = null;
		}
	}

	internal void SetVisible(bool visible)
	{
		if ((Object)(object)_renderer != (Object)null && ((Renderer)_renderer).enabled != visible)
		{
			((Renderer)_renderer).enabled = visible;
		}
		if (!visible)
		{
			_sampled = false;
		}
	}

	internal void Rebuild(Vector3 center, float radius, Vector3 surfaceNormal)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be du