Decompiled source of BetterStability v1.0.0

plugins/BetterStability.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BetterStability.Core;
using BetterStability.Dev;
using BetterStability.Gameplay;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BetterStability
{
	public static class Cfg
	{
		public static ConfigEntry<bool> UseTiers;

		public static readonly Dictionary<Tier, ConfigEntry<string>> TierText = new Dictionary<Tier, ConfigEntry<string>>();

		public static ConfigEntry<string> TierOverrides;

		public static ConfigEntry<bool> Anchoring;

		public static ConfigEntry<float> FullHoldShare;

		public static ConfigEntry<float> MinDepth;

		public static ConfigEntry<string> HostTiers;

		public static ConfigEntry<bool> ShowSupport;

		private static readonly TierValues[] _values = new TierValues[Strength.All.Length];

		private static readonly string[] _valueText = new string[Strength.All.Length];

		private static string _overrideText;

		private static string _hostText;

		private static Dictionary<string, Tier> _overrides = new Dictionary<string, Tier>(StringComparer.OrdinalIgnoreCase);

		private static readonly bool[] _hosts = new bool[Strength.All.Length];

		public static Dictionary<string, Tier> Overrides
		{
			get
			{
				string text = TierOverrides?.Value ?? "";
				if (text != _overrideText)
				{
					List<string> list = new List<string>();
					_overrides = Strength.ParseOverrides(text, list);
					_overrideText = text;
					if (list.Count > 0)
					{
						ManualLogSource log = BetterStabilityPlugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("TierOverrides: ignored " + string.Join(", ", list) + " (expected prefab:Tier)"));
						}
					}
				}
				return _overrides;
			}
		}

		public static event Action Changed;

		public static void Bind(ConfigFile c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			UseTiers = c.Bind<bool>("1 - Materials", "UseTiers", true, new ConfigDescription("Use BetterStability's material values below instead of the game's own. Off: every material holds as in vanilla (anchoring still works).", (AcceptableValueBase)null, new object[1] { val }));
			Tier[] all = Strength.All;
			for (int i = 0; i < all.Length; i++)
			{
				Tier tier = all[i];
				ConfigEntry<string> val2 = c.Bind<string>("1 - Materials", tier.ToString(), Strength.Default(tier).ToString(), new ConfigDescription(TierDescription(tier), (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
				{
					IsAdminOnly = true,
					Order = 0 - tier
				} }));
				TierText[tier] = val2;
				val2.SettingChanged += delegate
				{
					Cfg.Changed?.Invoke();
				};
			}
			TierOverrides = c.Bind<string>("1 - Materials", "TierOverrides", "", new ConfigDescription("Give single pieces another material as \"prefab:Tier\", comma separated, for example \"woodiron_beam:Flametal, piece_mymod_wall:BlackMarble\". Tiers: " + string.Join(", ", Strength.All) + ".", (AcceptableValueBase)null, new object[1] { val }));
			Anchoring = c.Bind<bool>("2 - Anchoring", "Anchoring", true, new ConfigDescription("Pieces set into rock, the ground or masonry hold by it: deep enough in rock or a foundation wall they stand as on the ground; in higher masonry they take that wall's support without loss over distance.", (AcceptableValueBase)null, new object[1] { val }));
			FullHoldShare = c.Bind<float>("2 - Anchoring", "FullHoldShare", 0.33f, new ConfigDescription("Part of a piece that must sit inside the rock or wall for a full hold: 0.33 means a third (a 2 m beam needs about 0.7 m inside). Less holds in proportion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1] { val }));
			MinDepth = c.Bind<float>("2 - Anchoring", "MinDepth", 0.25f, new ConfigDescription("A piece reaching less than this many metres into rock or a wall is not anchored at all (it only touches, as in vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { val }));
			HostTiers = c.Bind<string>("2 - Anchoring", "HostTiers", "Stone, BlackMarble, Grausten, Ancient", new ConfigDescription("Materials that hold a piece set into them, comma separated. Rock, the ground and fixed world structures always hold.", (AcceptableValueBase)null, new object[1] { val }));
			ShowSupport = c.Bind<bool>("3 - Client", "ShowSupport", true, "With the hammer in hand, the piece you look at shows its material, its support in percent and how it is anchored.");
			UseTiers.SettingChanged += delegate
			{
				Cfg.Changed?.Invoke();
			};
			Anchoring.SettingChanged += delegate
			{
				Cfg.Changed?.Invoke();
			};
			ConfigEntry<string>[] array = new ConfigEntry<string>[2] { TierOverrides, HostTiers };
			for (int i = 0; i < array.Length; i++)
			{
				array[i].SettingChanged += delegate
				{
					Cfg.Changed?.Invoke();
				};
			}
			ConfigEntry<float>[] array2 = new ConfigEntry<float>[2] { FullHoldShare, MinDepth };
			for (int i = 0; i < array2.Length; i++)
			{
				array2[i].SettingChanged += delegate
				{
					Cfg.Changed?.Invoke();
				};
			}
		}

		public static void RaiseChanged()
		{
			Cfg.Changed?.Invoke();
		}

		private static string TierDescription(Tier t)
		{
			TierValues tierValues = Strength.Vanilla(t);
			object obj;
			if (t != Tier.Flametal)
			{
				TierValues tierValues2 = tierValues;
				obj = "vanilla " + tierValues2.ToString();
			}
			else
			{
				obj = "vanilla files flametal pieces under Iron";
			}
			string arg = (string)obj;
			return $"{t}: max support, min support, loss per metre upwards, loss per metre sideways ({arg}). " + "A grounded piece has max; each link to a neighbour keeps 1 - loss × distance of its support; below min it breaks.";
		}

		public static TierValues Values(Tier t)
		{
			ConfigEntry<string> value;
			string text = (TierText.TryGetValue(t, out value) ? value.Value : null);
			if ((object)text != _valueText[(int)t])
			{
				_valueText[(int)t] = text;
				if (!Strength.TryParse(text, out _values[(int)t]))
				{
					_values[(int)t] = Strength.Default(t);
					ManualLogSource log = BetterStabilityPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"{t}: could not read \"{text}\" (expected max, min, up, out); using {_values[(int)t]}.");
					}
				}
			}
			return _values[(int)t];
		}

		public static bool IsHost(Tier t)
		{
			string text = HostTiers?.Value ?? "";
			if (text != _hostText)
			{
				_hostText = text;
				Array.Clear(_hosts, 0, _hosts.Length);
				string[] array = text.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text2 in array)
				{
					if (Enum.TryParse<Tier>(text2.Trim(), ignoreCase: true, out var result) && Enum.IsDefined(typeof(Tier), result))
					{
						_hosts[(int)result] = true;
						continue;
					}
					ManualLogSource log = BetterStabilityPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("HostTiers: unknown material " + text2));
					}
				}
			}
			return _hosts[(int)t];
		}
	}
	public static class Lang
	{
		private static readonly (string key, string en, string de)[] Texts = new(string, string, string)[21]
		{
			("bs_tier_wood", "Wood", "Holz"),
			("bs_tier_stone", "Stone", "Stein"),
			("bs_tier_iron", "Iron", "Eisen"),
			("bs_tier_corewood", "Core wood", "Kernholz"),
			("bs_tier_blackmarble", "Black marble", "Schwarzmarmor"),
			("bs_tier_grausten", "Grausten", "Grausten"),
			("bs_tier_ancient", "Ancient stone", "Uraltes Gestein"),
			("bs_tier_ice", "Ice", "Eis"),
			("bs_tier_timberwood", "Timber", "Bauholz"),
			("bs_tier_flametal", "Flametal", "Flammenmetall"),
			("bs_hover_support", "{0} · support {1} %", "{0} · Halt {1} %"),
			("bs_hover_support_full", "{0} · full support", "{0} · voller Halt"),
			("bs_host_ground", "the ground", "den Boden"),
			("bs_host_rock", "rock", "den Fels"),
			("bs_hover_bedrock", "Set {0} m into {1} – as firm as the ground", "{0} m tief in {1} gesetzt – fest wie der Boden"),
			("bs_hover_foundation", "Set {0} m into a foundation wall – as firm as the ground", "{0} m tief in eine Grundmauer gesetzt – fest wie der Boden"),
			("bs_hover_masonry", "Set {0} m into {1} – carried by the wall", "{0} m tief in {1} gesetzt – die Mauer trägt mit"),
			("bs_host_foundation", "a foundation wall", "eine Grundmauer"),
			("bs_hover_set", "Set {0} m into {1}", "{0} m tief in {1} gesetzt"),
			("bs_hover_partial", "Holds {0} % – {1} m in would hold fully", "Hält zu {0} % – {1} m tief hielte es ganz"),
			("bs_hover_shallow", "Only {0} m in {1} – too shallow to hold", "Nur {0} m in {1} – zu flach, um zu halten")
		};

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			(string, string, string)[] texts = Texts;
			for (int i = 0; i < texts.Length; i++)
			{
				(string, string, string) tuple = texts[i];
				string item = tuple.Item1;
				string item2 = tuple.Item2;
				string item3 = tuple.Item3;
				dictionary[item] = item2;
				dictionary2[item] = item3;
			}
			Pieces.AddTexts(dictionary, dictionary2);
			string text = "English";
			localization.AddTranslation(ref text, dictionary);
			text = "German";
			localization.AddTranslation(ref text, dictionary2);
		}

		public static string T(string token, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + token) : token);
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				return text;
			}
		}

		public static string L(string token)
		{
			if (Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}

		public static string TierName(Tier t)
		{
			return T("bs_tier_" + t.ToString().ToLowerInvariant());
		}
	}
	public static class Pieces
	{
		public static void Init()
		{
		}

		public static void AddTexts(Dictionary<string, string> en, Dictionary<string, string> de)
		{
		}
	}
	[BepInPlugin("zap.betterstability", "Better Stability", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class BetterStabilityPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "zap.betterstability";

		public const string PluginName = "Better Stability";

		public const string PluginVersion = "1.0.0";

		private Harmony _harmony;

		public static BetterStabilityPlugin Instance { get; private set; }

		public static string PluginDir { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static string AssetPath(string fileName)
		{
			return Path.Combine(PluginDir ?? "", "Assets", fileName);
		}

		private void Awake()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			Lang.Register();
			Pieces.Init();
			_harmony = new Harmony("zap.betterstability");
			_harmony.PatchAll(typeof(BetterStabilityPlugin).Assembly);
			SelfTest.TryStart(((Component)this).gameObject);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace BetterStability.Gameplay
{
	public struct Anchor
	{
		public HostKind Kind;

		public float Share;

		public float Depth;

		public float Length;

		public WearNTear Host;

		public Collider Collider;

		public Collider[] Rocks;

		public string Name;
	}
	public static class Anchors
	{
		[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
		private static class HoldByAnchor
		{
			private static bool Prefix(WearNTear __instance, out float __state)
			{
				__state = 0f;
				_updating = __instance;
				if (Stability.Bypass || !Cfg.Anchoring.Value || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid())
				{
					return true;
				}
				try
				{
					PieceInfo pieceInfo = Stability.Info(__instance);
					if (Time.time - pieceInfo.LastHold > 30f)
					{
						pieceInfo.AnchorDirty = true;
						pieceInfo.GroundedDirty = true;
					}
					pieceInfo.LastHold = Time.time;
					float max = Stability.ValuesOf(__instance).Max;
					__state = Hold(__instance, pieceInfo, max, out var _);
					if (__state >= max - 0.001f && !__instance.m_clearCachedSupport)
					{
						SetSupport(__instance, max);
						return false;
					}
				}
				catch (Exception ex)
				{
					BetterStabilityPlugin.Log.LogWarning((object)("Anchor check failed on " + ((Object)__instance).name + ": " + ex.Message));
					__state = 0f;
				}
				return true;
			}

			private static void Postfix(WearNTear __instance, float __state)
			{
				_updating = null;
				if (!(__state <= 0f) && !((Object)(object)__instance == (Object)null) && !(__state <= __instance.m_support))
				{
					SetSupport(__instance, Mathf.Min(__state, Stability.ValuesOf(__instance).Max));
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				_updating = null;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "ClearCachedSupport")]
		private static class MeasureAgain
		{
			private static void Postfix(WearNTear __instance)
			{
				if (!_damage && !((Object)(object)__instance == (Object)(object)_updating) && Stability.TryGetInfo(__instance, out var info))
				{
					info.GroundedDirty = true;
					if (!_digging || info.Grounded || info.Anchors.Exists((Anchor a) => a.Kind == HostKind.Bedrock))
					{
						info.AnchorDirty = true;
					}
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_HealthChanged")]
		private static class NotOnDamage
		{
			private static void Prefix()
			{
				_damage = true;
			}

			private static void Finalizer()
			{
				_damage = false;
			}
		}

		[HarmonyPatch(typeof(Heightmap), "Regenerate")]
		private static class OnlyGroundOnDigging
		{
			private static void Prefix()
			{
				_digging = true;
			}

			private static void Finalizer()
			{
				_digging = false;
			}
		}

		private const float SampleStep = 0.25f;

		private const int MaxPerAxis = 8;

		private const int MaxSamples = 400;

		private const int MaxMeasuresPerFrame = 12;

		private static readonly Collider[] Hits = (Collider[])(object)new Collider[256];

		private static readonly List<Collider> RockHits = new List<Collider>(4);

		private static int _budgetFrame = -1;

		private static int _budgetUsed;

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

		private static readonly List<Collider> Candidates = new List<Collider>(32);

		private static readonly List<WearNTear> CandidateHosts = new List<WearNTear>(32);

		private static readonly Dictionary<WearNTear, int> MasonryCount = new Dictionary<WearNTear, int>();

		private static readonly List<bool> SampleInRock = new List<bool>(512);

		private static readonly List<WearNTear> SampleHost = new List<WearNTear>(512);

		private static readonly Dictionary<WearNTear, Collider> MasonryCollider = new Dictionary<WearNTear, Collider>();

		private static int _mask = -1;

		private static int _terrainLayer = -1;

		private static WearNTear _updating;

		public static int MeasureCount;

		public static double MeasureSeconds;

		private static readonly List<float> Levels = new List<float>(512);

		private static readonly Dictionary<long, float> GroundCache = new Dictionary<long, float>();

		private static int _groundMask = -1;

		private static bool _damage;

		private static bool _digging;

		private static int Mask
		{
			get
			{
				if (_mask == -1)
				{
					_mask = LayerMask.GetMask(new string[5] { "piece", "Default", "static_solid", "Default_small", "terrain" });
					_terrainLayer = LayerMask.NameToLayer("terrain");
				}
				return _mask;
			}
		}

		public static void Measure(WearNTear w, PieceInfo info)
		{
			long timestamp = Stopwatch.GetTimestamp();
			try
			{
				MeasureNow(w, info);
			}
			finally
			{
				MeasureCount++;
				MeasureSeconds += (double)(Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency;
			}
		}

		private static void MeasureNow(WearNTear w, PieceInfo info)
		{
			//IL_00bf: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			info.AnchorDirty = false;
			info.MeasuredAt = Time.time;
			info.Anchors.Clear();
			Samples.Clear();
			SampleInRock.Clear();
			SampleHost.Clear();
			Candidates.Clear();
			CandidateHosts.Clear();
			MasonryCount.Clear();
			MasonryCollider.Clear();
			RockHits.Clear();
			GroundCache.Clear();
			Collider[] array = OwnColliders(w);
			bool flag = false;
			int mask = Mask;
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (!Solid(val))
				{
					continue;
				}
				AddSamples(val);
				Bounds bounds = val.bounds;
				flag = true;
				int num = Physics.OverlapBoxNonAlloc(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents + Vector3.one * 0.05f, Hits, Quaternion.identity, mask);
				for (int j = 0; j < num; j++)
				{
					Collider val2 = Hits[j];
					if (!((Object)(object)val2 == (Object)null) && Solid(val2) && ((Component)val2).gameObject.layer != _terrainLayer && Array.IndexOf(array, val2) < 0 && !Candidates.Contains(val2))
					{
						WearNTear componentInParent = ((Component)val2).GetComponentInParent<WearNTear>();
						if (!((Object)(object)componentInParent != (Object)null) || (!((Object)(object)componentInParent == (Object)(object)w) && componentInParent.m_supports && Cfg.IsHost(Stability.TierOf(componentInParent))))
						{
							Candidates.Add(val2);
							CandidateHosts.Add(componentInParent);
						}
					}
				}
			}
			if (!flag || Samples.Count == 0)
			{
				return;
			}
			if (Samples.Count > 400)
			{
				float num2 = (float)Samples.Count / 400f;
				for (int k = 0; k < 400; k++)
				{
					Samples[k] = Samples[(int)((float)k * num2)];
				}
				Samples.RemoveRange(400, Samples.Count - 400);
			}
			int num3 = 0;
			Collider val3 = null;
			string text = null;
			foreach (Vector3 sample in Samples)
			{
				bool flag2 = false;
				WearNTear val4 = null;
				if (GroundAt(sample, out var height) && sample.y < height - 0.02f)
				{
					flag2 = true;
					text = text ?? "ground";
				}
				for (int l = 0; l < Candidates.Count; l++)
				{
					Collider val5 = Candidates[l];
					if (!Inside(val5, sample))
					{
						continue;
					}
					WearNTear val6 = CandidateHosts[l];
					if ((Object)(object)val6 == (Object)null)
					{
						if (!RockHits.Contains(val5))
						{
							RockHits.Add(val5);
						}
						if (!flag2)
						{
							flag2 = true;
							val3 = val3 ?? val5;
							text = ((text == null || text == "ground") ? "rock" : text);
						}
					}
					else if (!((Object)(object)val4 == (Object)(object)val6))
					{
						MasonryCount.TryGetValue(val6, out var value);
						MasonryCount[val6] = value + 1;
						if (!MasonryCollider.ContainsKey(val6))
						{
							MasonryCollider[val6] = val5;
						}
						val4 = val6;
					}
				}
				if (flag2)
				{
					num3++;
				}
				SampleInRock.Add(flag2);
				SampleHost.Add(val4);
			}
			float num4 = Samples.Count;
			if (num3 > 0)
			{
				Reach((int index) => SampleInRock[index], out var depth, out var length);
				info.Anchors.Add(new Anchor
				{
					Kind = HostKind.Bedrock,
					Share = (float)num3 / num4,
					Depth = depth,
					Length = length,
					Collider = val3,
					Rocks = RockHits.ToArray(),
					Name = text
				});
			}
			foreach (KeyValuePair<WearNTear, int> item in MasonryCount)
			{
				float num5 = (float)item.Value / num4;
				if (Stability.TryGetInfo(item.Key, out var info2))
				{
					int num6 = info2.Anchors.FindIndex((Anchor a) => (Object)(object)a.Host == (Object)(object)w);
					if (num6 >= 0)
					{
						if (info2.Anchors[num6].Share >= num5)
						{
							continue;
						}
						info2.Anchors.RemoveAt(num6);
					}
				}
				WearNTear key = item.Key;
				Reach((int index) => (Object)(object)SampleHost[index] == (Object)(object)key, out var depth2, out var length2);
				info.Anchors.Add(new Anchor
				{
					Kind = HostKind.Masonry,
					Share = num5,
					Depth = depth2,
					Length = length2,
					Host = item.Key,
					Collider = MasonryCollider[item.Key],
					Name = (((Component)item.Key).GetComponent<Piece>()?.m_name ?? Utils.GetPrefabName(((Component)item.Key).gameObject))
				});
			}
		}

		private static void Reach(Func<int, bool> inside, out float depth, out float length)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.zero;
			Vector3 val2 = Vector3.zero;
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < Samples.Count; i++)
			{
				if (inside(i))
				{
					val += Samples[i];
					num++;
				}
				else
				{
					val2 += Samples[i];
					num2++;
				}
			}
			if (num == 0)
			{
				depth = 0f;
				length = 0f;
				return;
			}
			val /= (float)num;
			Vector3 val3 = ((num2 > 0) ? (val - val2 / (float)num2) : Vector3.down);
			if (((Vector3)(ref val3)).sqrMagnitude < 1E-06f)
			{
				val3 = Vector3.down;
			}
			((Vector3)(ref val3)).Normalize();
			float num3 = float.MaxValue;
			float num4 = float.MinValue;
			float num5 = float.MaxValue;
			float num6 = float.MinValue;
			Levels.Clear();
			for (int j = 0; j < Samples.Count; j++)
			{
				float num7 = Vector3.Dot(Samples[j], val3);
				Levels.Add(num7);
				num5 = Mathf.Min(num5, num7);
				num6 = Mathf.Max(num6, num7);
				if (inside(j))
				{
					num3 = Mathf.Min(num3, num7);
					num4 = Mathf.Max(num4, num7);
				}
			}
			Levels.Sort();
			float num8 = 0.25f;
			for (int k = 1; k < Levels.Count; k++)
			{
				float num9 = Levels[k] - Levels[k - 1];
				if (num9 > 0.02f && num9 < num8)
				{
					num8 = num9;
				}
			}
			depth = num4 - num3 + num8;
			length = num6 - num5 + num8;
		}

		public static bool IsGrounded(WearNTear w)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			PieceInfo pieceInfo = Stability.Info(w);
			if (!pieceInfo.GroundedDirty && (!pieceInfo.Grounded || !Gone(pieceInfo.GroundedBy)))
			{
				return pieceInfo.Grounded;
			}
			pieceInfo.GroundedDirty = false;
			pieceInfo.Grounded = false;
			pieceInfo.GroundedBy = null;
			GroundCache.Clear();
			Collider[] array = OwnColliders(w);
			int mask = Mask;
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (!Solid(val))
				{
					continue;
				}
				Bounds bounds = val.bounds;
				if (GroundAt(((Bounds)(ref bounds)).center, out var height) && ((Bounds)(ref bounds)).min.y < height - 0.02f)
				{
					pieceInfo.Grounded = true;
					return true;
				}
				int num = OverlapLikeTheGame(val, mask);
				for (int j = 0; j < num; j++)
				{
					Collider val2 = Hits[j];
					if (!((Object)(object)val2 == (Object)null) && Solid(val2) && Array.IndexOf(array, val2) < 0 && (((Component)val2).gameObject.layer == _terrainLayer || (Object)(object)((Component)val2).GetComponentInParent<WearNTear>() == (Object)null))
					{
						pieceInfo.Grounded = true;
						if (((Component)val2).gameObject.layer != _terrainLayer)
						{
							pieceInfo.GroundedBy = val2;
						}
						return true;
					}
				}
			}
			return false;
		}

		private static int OverlapLikeTheGame(Collider c, int mask)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_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_0054: 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)
			BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null);
			if (val != null)
			{
				Transform transform = ((Component)val).transform;
				Vector3 val2 = Vector3.Scale(transform.lossyScale, val.size) + Vector3.one * 0.3f;
				return Physics.OverlapBoxNonAlloc(transform.position + transform.TransformVector(val.center), val2 * 0.5f, Hits, transform.rotation, mask);
			}
			Bounds bounds = c.bounds;
			return Physics.OverlapBoxNonAlloc(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents + Vector3.one * 0.15f, Hits, Quaternion.identity, mask);
		}

		private static Collider[] OwnColliders(WearNTear w)
		{
			if (w.m_colliders == null)
			{
				w.SetupColliders();
			}
			return w.m_colliders ?? Array.Empty<Collider>();
		}

		private static bool GroundAt(Vector3 p, out float height)
		{
			//IL_0000: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			long key = ((long)Mathf.RoundToInt(p.x * 20f) << 32) ^ (uint)Mathf.RoundToInt(p.z * 20f);
			if (GroundCache.TryGetValue(key, out height))
			{
				return !float.IsNaN(height);
			}
			if (_groundMask == -1)
			{
				_groundMask = LayerMask.GetMask(new string[1] { "terrain" });
			}
			RaycastHit val = default(RaycastHit);
			bool flag = Physics.Raycast(new Vector3(p.x, 6000f, p.z), Vector3.down, ref val, 10000f, _groundMask);
			height = (flag ? ((RaycastHit)(ref val)).point.y : float.NaN);
			GroundCache[key] = height;
			return flag;
		}

		private static bool Solid(Collider c)
		{
			if ((Object)(object)c != (Object)null && c.enabled && ((Component)c).gameObject.activeInHierarchy && !c.isTrigger)
			{
				return (Object)(object)c.attachedRigidbody == (Object)null;
			}
			return false;
		}

		private static bool Gone(Collider c)
		{
			if (c != null)
			{
				if (Object.op_Implicit((Object)(object)c) && c.enabled)
				{
					return !((Component)c).gameObject.activeInHierarchy;
				}
				return true;
			}
			return false;
		}

		private static void AddSamples(Collider c)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null);
			if (val != null)
			{
				Transform transform = ((Component)val).transform;
				Vector3 val2 = Vector3.Scale(transform.lossyScale, val.size);
				int num = Count(val2.x);
				int num2 = Count(val2.y);
				int num3 = Count(val2.z);
				Vector3 val3 = default(Vector3);
				for (int i = 0; i < num; i++)
				{
					for (int j = 0; j < num2; j++)
					{
						for (int k = 0; k < num3; k++)
						{
							((Vector3)(ref val3))..ctor(((float)i + 0.5f) / (float)num - 0.5f, ((float)j + 0.5f) / (float)num2 - 0.5f, ((float)k + 0.5f) / (float)num3 - 0.5f);
							Samples.Add(transform.TransformPoint(val.center + Vector3.Scale(val3, val.size)));
						}
					}
				}
				return;
			}
			Bounds bounds = c.bounds;
			int num4 = Count(((Bounds)(ref bounds)).size.x);
			int num5 = Count(((Bounds)(ref bounds)).size.y);
			int num6 = Count(((Bounds)(ref bounds)).size.z);
			MeshCollider val4 = (MeshCollider)(object)((c is MeshCollider) ? c : null);
			bool flag = val4 == null || val4.convex;
			for (int l = 0; l < num4; l++)
			{
				for (int m = 0; m < num5; m++)
				{
					for (int n = 0; n < num6; n++)
					{
						Vector3 val5 = ((Bounds)(ref bounds)).min + Vector3.Scale(((Bounds)(ref bounds)).size, new Vector3(((float)l + 0.5f) / (float)num4, ((float)m + 0.5f) / (float)num5, ((float)n + 0.5f) / (float)num6));
						bool num7;
						if (!flag)
						{
							num7 = Inside(c, val5);
						}
						else
						{
							Vector3 val6 = c.ClosestPoint(val5) - val5;
							num7 = ((Vector3)(ref val6)).sqrMagnitude < 0.0001f;
						}
						if (num7)
						{
							Samples.Add(val5);
						}
					}
				}
			}
		}

		private static int Count(float size)
		{
			return Mathf.Clamp(Mathf.RoundToInt(size / 0.25f), 1, 8);
		}

		private static bool Inside(Collider c, Vector3 p)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00c2: Unknown result type (might be due to invalid IL or missing references)
			MeshCollider val = (MeshCollider)(object)((c is MeshCollider) ? c : null);
			Bounds bounds;
			Vector3 val2;
			if (val == null || val.convex)
			{
				bounds = c.bounds;
				if (((Bounds)(ref bounds)).Contains(p))
				{
					val2 = c.ClosestPoint(p) - p;
					return ((Vector3)(ref val2)).sqrMagnitude < 0.0001f;
				}
				return false;
			}
			bounds = c.bounds;
			if (!((Bounds)(ref bounds)).Contains(p))
			{
				return false;
			}
			bool queriesHitBackfaces = Physics.queriesHitBackfaces;
			Physics.queriesHitBackfaces = true;
			try
			{
				bounds = c.bounds;
				val2 = ((Bounds)(ref bounds)).size;
				float num = ((Vector3)(ref val2)).magnitude + 1f;
				RaycastHit val3 = default(RaycastHit);
				RaycastHit val4 = default(RaycastHit);
				return c.Raycast(new Ray(p, Vector3.up), ref val3, num) && ((RaycastHit)(ref val3)).normal.y > 0f && c.Raycast(new Ray(p, Vector3.down), ref val4, num) && ((RaycastHit)(ref val4)).normal.y < 0f;
			}
			finally
			{
				Physics.queriesHitBackfaces = queriesHitBackfaces;
			}
		}

		public static float Hold(WearNTear w, PieceInfo info, float ownMax, out Anchor best, bool anyTime = false)
		{
			best = default(Anchor);
			if (!Cfg.Anchoring.Value)
			{
				return 0f;
			}
			bool flag = Lost(info);
			if (info.AnchorDirty || flag)
			{
				if (anyTime || Budget())
				{
					Measure(w, info);
				}
				else if (flag)
				{
					info.Anchors.Clear();
				}
			}
			float num = 0f;
			float value = Cfg.FullHoldShare.Value;
			float value2 = Cfg.MinDepth.Value;
			foreach (Anchor anchor in info.Anchors)
			{
				HostKind hostKind = anchor.Kind;
				float num2 = 0f;
				if ((Object)(object)anchor.Host != (Object)null)
				{
					if ((Object)(object)anchor.Host.m_nview == (Object)null || !anchor.Host.m_nview.IsValid())
					{
						continue;
					}
					num2 = anchor.Host.GetSupport();
					hostKind = ((!IsGrounded(anchor.Host)) ? HostKind.Masonry : HostKind.Foundation);
					if (hostKind == HostKind.Masonry && Stability.TryGetInfo(anchor.Host, out var info2) && info2.AnchorSupport > 0f && num2 <= info2.AnchorSupport + 0.5f)
					{
						continue;
					}
				}
				float quality = AnchorRule.Quality(anchor.Share, anchor.Depth, value, value2);
				float num3 = AnchorRule.Support(hostKind, quality, ownMax, num2);
				if (num3 > 0f && AnchorRule.Better(hostKind, num3, best.Kind, num))
				{
					best = anchor;
					best.Kind = hostKind;
					num = num3;
				}
			}
			info.AnchorSupport = num;
			return num;
		}

		private static bool Lost(PieceInfo info)
		{
			foreach (Anchor anchor in info.Anchors)
			{
				if (Gone(anchor.Collider) || (anchor.Host != null && (!Object.op_Implicit((Object)(object)anchor.Host) || !anchor.Host.m_nview.IsValid())))
				{
					return true;
				}
				if (anchor.Rocks == null)
				{
					continue;
				}
				Collider[] rocks = anchor.Rocks;
				for (int i = 0; i < rocks.Length; i++)
				{
					if (Gone(rocks[i]))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool Budget()
		{
			if (_budgetFrame != Time.frameCount)
			{
				_budgetFrame = Time.frameCount;
				_budgetUsed = 0;
			}
			if (_budgetUsed >= 12)
			{
				return false;
			}
			_budgetUsed++;
			return true;
		}

		private static void SetSupport(WearNTear w, float support)
		{
			if (!w.m_support.Equals(support))
			{
				w.m_support = support;
				ZDO zDO = w.m_nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set(ZDOVars.s_support, support);
				}
			}
		}
	}
	public sealed class PieceInfo
	{
		public Tier Tier;

		public bool TierKnown;

		public bool AnchorDirty = true;

		public float MeasuredAt;

		public float LastHold = -1000f;

		public readonly List<Anchor> Anchors = new List<Anchor>(2);

		public float AnchorSupport;

		public bool Grounded;

		public bool GroundedDirty = true;

		public Collider GroundedBy;
	}
	public static class Stability
	{
		[HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")]
		private static class TierValuesForPiece
		{
			private static bool Prefix(WearNTear __instance, ref float maxSupport, ref float minSupport, ref float horizontalLoss, ref float verticalLoss)
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return true;
				}
				TierValues tierValues = ValuesOf(__instance);
				maxSupport = tierValues.Max;
				minSupport = tierValues.Min;
				verticalLoss = tierValues.Up;
				horizontalLoss = tierValues.Out;
				return false;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
		private static class ForgetPiece
		{
			private static void Postfix(WearNTear __instance)
			{
				Infos.Remove(((Object)__instance).GetInstanceID());
			}
		}

		[HarmonyPatch(typeof(Game), "Start")]
		private static class FreshWorld
		{
			private static void Postfix()
			{
				TierByPrefab.Clear();
				Infos.Clear();
			}
		}

		private static readonly Dictionary<int, PieceInfo> Infos;

		private static readonly Dictionary<string, Tier> TierByPrefab;

		public static bool Bypass;

		static Stability()
		{
			Infos = new Dictionary<int, PieceInfo>();
			TierByPrefab = new Dictionary<string, Tier>();
			Cfg.Changed += OnConfigChanged;
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				OnConfigChanged();
			};
		}

		public static PieceInfo Info(WearNTear w)
		{
			int instanceID = ((Object)w).GetInstanceID();
			if (!Infos.TryGetValue(instanceID, out var value))
			{
				value = new PieceInfo();
				Infos[instanceID] = value;
			}
			return value;
		}

		public static bool TryGetInfo(WearNTear w, out PieceInfo info)
		{
			return Infos.TryGetValue(((Object)w).GetInstanceID(), out info);
		}

		public static Tier TierOf(WearNTear w)
		{
			PieceInfo pieceInfo = Info(w);
			if (!pieceInfo.TierKnown)
			{
				pieceInfo.Tier = ResolveTier(w);
				pieceInfo.TierKnown = true;
			}
			return pieceInfo.Tier;
		}

		public static TierValues ValuesOf(WearNTear w)
		{
			Tier tier = TierOf(w);
			if (Bypass || !Cfg.UseTiers.Value)
			{
				return Strength.Vanilla((tier == Tier.Flametal) ? Tier.Iron : tier);
			}
			return Cfg.Values(tier);
		}

		private static Tier ResolveTier(WearNTear w)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected I4, but got Unknown
			string prefabName = Utils.GetPrefabName(((Component)w).gameObject);
			if (Cfg.Overrides.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			if (TierByPrefab.TryGetValue(prefabName, out var value2))
			{
				return value2;
			}
			Tier tier = (Tier)w.m_materialType;
			if (!Enum.IsDefined(typeof(Tier), tier))
			{
				tier = Tier.Wood;
			}
			Piece component = ((Component)w).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null && component.m_resources != null && tier == Tier.Iron)
			{
				Requirement[] resources = component.m_resources;
				foreach (Requirement val in resources)
				{
					if ((((Object)(object)val?.m_resItem != (Object)null) ? ((Object)((Component)val.m_resItem).gameObject).name : "").StartsWith("Flametal", StringComparison.OrdinalIgnoreCase))
					{
						tier = Tier.Flametal;
						break;
					}
				}
			}
			TierByPrefab[prefabName] = tier;
			return tier;
		}

		public static void OnConfigChanged()
		{
			TierByPrefab.Clear();
			foreach (PieceInfo value in Infos.Values)
			{
				value.TierKnown = false;
				value.AnchorDirty = true;
				value.GroundedDirty = true;
			}
			foreach (WearNTear allInstance in WearNTear.GetAllInstances())
			{
				if ((Object)(object)allInstance != (Object)null)
				{
					allInstance.ClearCachedSupport();
				}
			}
		}
	}
	public static class SupportHover
	{
		[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
		private static class ShowUnderCrosshair
		{
			private static void Postfix(Hud __instance, Player player)
			{
				try
				{
					if (_broken || !Cfg.ShowSupport.Value || (Object)(object)player == (Object)null || (Object)(object)__instance.m_hoverName == (Object)null)
					{
						return;
					}
					Piece hoveringPiece = player.GetHoveringPiece();
					if ((Object)(object)hoveringPiece == (Object)null)
					{
						return;
					}
					WearNTear component = ((Component)hoveringPiece).GetComponent<WearNTear>();
					if (!((Object)(object)component == (Object)null))
					{
						string text = Describe(component);
						if (!string.IsNullOrEmpty(text))
						{
							string text2 = ((TMP_Text)__instance.m_hoverName).text;
							((TMP_Text)__instance.m_hoverName).text = (string.IsNullOrEmpty(text2) ? text : (text2 + "\n" + text));
						}
					}
				}
				catch (Exception ex)
				{
					BetterStabilityPlugin.Log.LogWarning((object)("Support hover failed, switched off until restart: " + ex));
					_broken = true;
				}
			}
		}

		private static bool _broken;

		private static WearNTear _piece;

		private static PieceInfo _info = new PieceInfo();

		public static string Describe(WearNTear w)
		{
			if ((Object)(object)w == (Object)null || (Object)(object)w.m_nview == (Object)null || !w.m_nview.IsValid())
			{
				return "";
			}
			Tier t = Stability.TierOf(w);
			TierValues tierValues = Stability.ValuesOf(w);
			float support = w.GetSupport();
			string text = ((support >= tierValues.Max - 0.001f) ? Lang.T("bs_hover_support_full", Lang.TierName(t)) : Lang.T("bs_hover_support", Lang.TierName(t), Mathf.FloorToInt(Mathf.Clamp01(support / tierValues.Max) * 100f)));
			if (!Cfg.Anchoring.Value)
			{
				return text;
			}
			if (_piece != w || Time.time - _info.MeasuredAt > 1f)
			{
				_piece = w;
				_info = new PieceInfo();
			}
			PieceInfo info = _info;
			Anchors.Hold(w, info, tierValues.Max, out var best, anyTime: true);
			if (best.Kind == HostKind.None)
			{
				Anchor a = default(Anchor);
				foreach (Anchor anchor in info.Anchors)
				{
					if (anchor.Kind != HostKind.Bedrock && anchor.Depth > a.Depth)
					{
						a = anchor;
					}
				}
				if (a.Depth >= 0.05f)
				{
					return text + "\n<color=#c0c0c0>" + Lang.T("bs_hover_shallow", Metres(a.Depth), HostName(a)) + "</color>";
				}
				return text;
			}
			float num = AnchorRule.Quality(best.Share, best.Depth, Cfg.FullHoldShare.Value, Cfg.MinDepth.Value);
			if (num < 0.999f)
			{
				if (best.Kind == HostKind.Bedrock)
				{
					return text;
				}
				float m = best.Length * Cfg.FullHoldShare.Value;
				return text + "\n<color=#ffd98a>" + Lang.T("bs_hover_set", Metres(best.Depth), HostName(best)) + "</color>\n<color=#c0c0c0>" + Lang.T("bs_hover_partial", Mathf.RoundToInt(num * 100f), Metres(m)) + "</color>";
			}
			return text + "\n<color=#ffd98a>" + best.Kind switch
			{
				HostKind.Bedrock => Lang.T("bs_hover_bedrock", Metres(best.Depth), HostName(best)), 
				HostKind.Foundation => Lang.T("bs_hover_foundation", Metres(best.Depth)), 
				_ => Lang.T("bs_hover_masonry", Metres(best.Depth), HostName(best)), 
			} + "</color>";
		}

		private static string HostName(Anchor a)
		{
			if (a.Kind == HostKind.Bedrock)
			{
				return Lang.T((a.Name == "ground") ? "bs_host_ground" : "bs_host_rock");
			}
			if (a.Kind == HostKind.Foundation)
			{
				return Lang.T("bs_host_foundation");
			}
			return Lang.L(a.Name ?? "");
		}

		public static string Metres(float m)
		{
			bool num = Localization.instance != null && Localization.instance.GetSelectedLanguage() == "German";
			string text = m.ToString("0.0", CultureInfo.InvariantCulture);
			if (!num)
			{
				return text;
			}
			return text.Replace('.', ',');
		}
	}
}
namespace BetterStability.Dev
{
	public class SelfTest : MonoBehaviour
	{
		private sealed class Row
		{
			public Tier Tier;

			public string Prefab;

			public Func<int, WearNTear> Place;

			public Func<int, bool> Clear = (int _) => true;

			public readonly List<WearNTear> Pieces = new List<WearNTear>();

			public bool Done;

			public bool Capped;

			public int Standing;

			public int Expected;
		}

		private string _out;

		private readonly List<string> _report = new List<string>();

		private static readonly (Tier tier, string prefab)[] Columns = new(Tier, string)[8]
		{
			(Tier.Wood, "wood_pole2"),
			(Tier.CoreWood, "wood_pole_log"),
			(Tier.Timberwood, "stave_pole_2m"),
			(Tier.Stone, "stone_pillar"),
			(Tier.Iron, "woodiron_pole"),
			(Tier.BlackMarble, "blackmarble_2x2x2"),
			(Tier.Grausten, "Piece_grausten_wall_2x2"),
			(Tier.Flametal, "Piece_flametal_pillar")
		};

		private static readonly (Tier tier, string prefab)[] Beams = new(Tier, string)[8]
		{
			(Tier.Wood, "wood_beam"),
			(Tier.CoreWood, "wood_wall_log"),
			(Tier.Timberwood, "stave_beam_2m"),
			(Tier.Stone, "stone_floor_2x2"),
			(Tier.Iron, "woodiron_beam"),
			(Tier.BlackMarble, "blackmarble_floor"),
			(Tier.Grausten, "Piece_grausten_floor_2x2"),
			(Tier.Flametal, "Piece_flametal_beam")
		};

		private int _pass;

		private int _fail;

		private readonly List<Vector3> _taken = new List<Vector3>();

		private WearNTear Place(string prefab, Vector3 pos, Quaternion rot)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			if ((Object)(object)prefab2 == (Object)null)
			{
				Note("place: missing prefab " + prefab);
				return null;
			}
			WearNTear component = Object.Instantiate<GameObject>(prefab2, pos, rot).GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				component.OnPlaced();
			}
			return component;
		}

		private static bool Alive(WearNTear w)
		{
			if ((Object)(object)w != (Object)null && (Object)(object)w.m_nview != (Object)null)
			{
				return w.m_nview.IsValid();
			}
			return false;
		}

		private static float SupportOf(WearNTear w)
		{
			if (!Alive(w))
			{
				return 0f;
			}
			return w.GetSupport();
		}

		private static float Percent(WearNTear w)
		{
			if (!Alive(w))
			{
				return 0f;
			}
			return 100f * w.GetSupport() / Stability.ValuesOf(w).Max;
		}

		private IEnumerator Settle(IList<WearNTear> pieces, float quiet = 4f, float limit = 120f)
		{
			float end = Time.realtimeSinceStartup + limit;
			float stableSince = Time.realtimeSinceStartup;
			string last = "";
			while (Time.realtimeSinceStartup < end)
			{
				yield return (object)new WaitForSecondsRealtime(0.5f);
				string text = string.Join(",", pieces.Select((WearNTear p) => (!Alive(p)) ? "x" : Mathf.RoundToInt(p.GetSupport()).ToString()));
				if (text != last)
				{
					last = text;
					stableSince = Time.realtimeSinceStartup;
				}
				else if (Time.realtimeSinceStartup - stableSince >= quiet)
				{
					yield break;
				}
			}
			Note("settle: still changing after " + limit + " s");
		}

		private static void Clear(IEnumerable<WearNTear> pieces)
		{
			foreach (WearNTear piece in pieces)
			{
				if (Alive(piece))
				{
					piece.m_nview.Destroy();
				}
			}
		}

		private static List<WearNTear> PiecesNear(Vector3 c, float r)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return (from w in WearNTear.GetAllInstances()
				where Alive(w) && Vector3.Distance(((Component)w).transform.position, c) < r
				select w).ToList();
		}

		private static Bounds SolidBounds(GameObject go)
		{
			//IL_0002: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			Bounds result = default(Bounds);
			bool flag = false;
			Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (!val.isTrigger)
				{
					if (!flag)
					{
						result = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val.bounds);
					}
				}
			}
			return result;
		}

		public static void TryStart(GameObject host)
		{
			string environmentVariable = Environment.GetEnvironmentVariable("STABILITY_SELFTEST");
			if (string.IsNullOrEmpty(environmentVariable))
			{
				return;
			}
			Application.runInBackground = true;
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			bool flag = false;
			for (int i = 0; i < commandLineArgs.Length - 1; i++)
			{
				if (commandLineArgs[i] == "-savedir" && !string.IsNullOrEmpty(commandLineArgs[i + 1]))
				{
					Directory.CreateDirectory(commandLineArgs[i + 1]);
					Utils.SetSaveDataPath(commandLineArgs[i + 1]);
					flag = true;
				}
			}
			if (!flag)
			{
				BetterStabilityPlugin.Log.LogError((object)"Self test needs -savedir <scratch folder>; not starting it.");
			}
			else
			{
				host.AddComponent<SelfTest>()._out = environmentVariable;
			}
		}

		private static bool Flag(string name)
		{
			return (Environment.GetEnvironmentVariable("STABILITY_SELFTEST_" + name) ?? "") == "1";
		}

		private void Start()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			Directory.CreateDirectory(_out);
			Application.logMessageReceived += new LogCallback(OnLog);
			((MonoBehaviour)this).StartCoroutine(Run());
		}

		private void OnLog(string condition, string stackTrace, LogType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type == 4 || (int)type == 0)
			{
				_report.Add($"[{type}] {condition}\n{stackTrace}");
			}
		}

		private IEnumerator Run()
		{
			float timeout = Time.realtimeSinceStartup + 600f;
			while (((Object)(object)FejdStartup.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(4f);
			if (Flag("WORLD"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(EnterWorld()));
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					if (Flag("RESEARCH"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Research()));
					}
					bool all = Flag("ALL");
					if (Flag("DE"))
					{
						Localization.instance.SetLanguage("German");
					}
					Player.m_localPlayer.SetGodMode(true);
					Player.m_localPlayer.SetGhostMode(true);
					EnvMan.instance.m_debugTimeOfDay = true;
					EnvMan.instance.m_debugTime = 0.38f;
					if (all || Flag("ANCHOR"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Anchoring()));
					}
					if (all || Flag("FLOAT"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(NoFloating()));
					}
					if (all || Flag("TOWERS"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Towers()));
					}
					if (all || Flag("OVERHANG"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Overhang()));
					}
					if (all || Flag("CLIFF"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Cliff()));
					}
					_report.Add($"anchor cost: {Anchors.MeasureCount} measurings, {Anchors.MeasureSeconds * 1000.0:0.0} ms in all " + $"({((Anchors.MeasureCount > 0) ? (Anchors.MeasureSeconds * 1000000.0 / (double)Anchors.MeasureCount) : 0.0):0} µs each)");
				}
			}
			_report.Add($"RESULT pass {_pass} fail {_fail}");
			Flush();
			Application.logMessageReceived -= new LogCallback(OnLog);
			yield return (object)new WaitForSecondsRealtime(0.5f);
			Application.Quit();
		}

		private IEnumerator Safe(IEnumerator inner)
		{
			while (true)
			{
				object current;
				try
				{
					if (!inner.MoveNext())
					{
						break;
					}
					current = inner.Current;
				}
				catch (Exception ex)
				{
					_report.Add("step failed: " + ex);
					_fail++;
					break;
				}
				yield return current;
			}
		}

		private void Flush()
		{
			File.WriteAllLines(Path.Combine(_out, "report.txt"), _report);
		}

		private IEnumerator Research()
		{
			ZNetScene instance = ZNetScene.instance;
			List<GameObject> list = (from p in instance.m_prefabs.Concat(instance.m_namedPrefabs.Values)
				where (Object)(object)p != (Object)null
				group p by ((Object)p).name into g
				select g.First() into p
				orderby ((Object)p).name
				select p).ToList();
			_report.Add("research: prefabs " + list.Count);
			HashSet<string> hammerPieces = new HashSet<string>();
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer");
			PieceTable val = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces);
			if ((Object)(object)val != (Object)null)
			{
				foreach (GameObject item in val.m_pieces.Where((GameObject p) => (Object)(object)p != (Object)null))
				{
					hammerPieces.Add(((Object)item).name);
					Piece component = item.GetComponent<Piece>();
					WearNTear component2 = item.GetComponent<WearNTear>();
					string text = ((component?.m_resources == null) ? "" : string.Join(" ", from r in component.m_resources
						where (Object)(object)r?.m_resItem != (Object)null
						select ((Object)r.m_resItem).name + ":" + r.m_amount));
					string text2 = string.Join(" ", (from c in item.GetComponentsInChildren<Collider>(true)
						where !c.isTrigger
						select c).Select(Shape));
					List<string> report = _report;
					string[] array = new string[6];
					object[] obj = new object[4]
					{
						((Object)item).name,
						Lang.L(component?.m_name),
						component?.m_category,
						null
					};
					object obj2;
					if (component == null)
					{
						obj2 = null;
					}
					else
					{
						CraftingStation craftingStation = component.m_craftingStation;
						obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null);
					}
					obj[3] = obj2;
					array[0] = string.Format("HAMMER {0} '{1}' cat {2} station {3} ", obj);
					array[1] = (((Object)(object)component2 == (Object)null) ? "noWNT" : $"mat {component2.m_materialType} hp {component2.m_health} supports {component2.m_supports} com {component2.m_comOffset} ");
					array[2] = "| ";
					array[3] = text;
					array[4] = " | ";
					array[5] = text2;
					report.Add(string.Concat(array));
				}
			}
			_report.Add("hammer pieces: " + hammerPieces.Count);
			foreach (IGrouping<MaterialType, GameObject> item2 in from p in list
				where !hammerPieces.Contains(((Object)p).name) && (Object)(object)p.GetComponent<WearNTear>() != (Object)null
				group p by p.GetComponent<WearNTear>().m_materialType)
			{
				_report.Add($"WORLD {item2.Key} ({item2.Count()}): " + string.Join(" ", item2.Select((GameObject p) => ((Object)p).name).Take(60)));
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				foreach (ZoneLocation item3 in ZoneSystem.instance.m_locations.Where((ZoneLocation l) => l != null && l.m_enable && l.m_prefabName != null && (l.m_prefabName.IndexOf("tower", StringComparison.OrdinalIgnoreCase) >= 0 || l.m_prefabName.IndexOf("seal", StringComparison.OrdinalIgnoreCase) >= 0)))
				{
					_report.Add($"LOCATION {item3.m_prefabName} biome {item3.m_biome} quantity {item3.m_quantity}");
				}
			}
			foreach (GameObject item4 in ObjectDB.instance.m_items.Where((GameObject i) => (Object)(object)i != (Object)null && (((Object)i).name.IndexOf("Flametal", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Marble", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Grausten", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Iron", StringComparison.OrdinalIgnoreCase) >= 0)))
			{
				_report.Add("ITEM " + ((Object)item4).name + " '" + Lang.L(item4.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name) + "'");
			}
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			WorldGenerator instance2 = WorldGenerator.instance;
			int num = 0;
			for (float num2 = 50f; num2 < 3000f; num2 += 25f)
			{
				if (num >= 25)
				{
					break;
				}
				for (int num3 = 0; num3 < 48; num3++)
				{
					if (num >= 25)
					{
						break;
					}
					float num4 = (float)num3 * (float)Math.PI * 2f / 48f;
					float num5 = position.x + Mathf.Cos(num4) * num2;
					float num6 = position.z + Mathf.Sin(num4) * num2;
					float height = instance2.GetHeight(num5, num6);
					if (height < ZoneSystem.instance.m_waterLevel + 3f)
					{
						continue;
					}
					float num7 = 0f;
					float num8 = 0f;
					for (int num9 = 0; num9 < 8; num9++)
					{
						float num10 = (float)num9 * (float)Math.PI / 4f;
						float height2 = instance2.GetHeight(num5 + Mathf.Cos(num10) * 3f, num6 + Mathf.Sin(num10) * 3f);
						if ((height2 - height) / 3f > num7)
						{
							num7 = (height2 - height) / 3f;
							num8 = num10 * 57.29578f;
						}
					}
					if (!(num7 < 1.6f))
					{
						num++;
						_report.Add($"STEEP ({num5:0},{height:0.0},{num6:0}) slope {num7:0.00} uphill-dir {num8:0} biome {instance2.GetBiome(num5, num6, 0.02f, false)} dist {num2:0}");
					}
				}
			}
			Flush();
			yield break;
		}

		private static string Shape(Collider c)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null);
			if (val == null)
			{
				MeshCollider val2 = (MeshCollider)(object)((c is MeshCollider) ? c : null);
				Bounds bounds;
				if (val2 != null)
				{
					string obj = (val2.convex ? "C" : "");
					bounds = ((Collider)val2).bounds;
					return "mesh" + obj + Fmt(((Bounds)(ref bounds)).size);
				}
				string name = ((object)c).GetType().Name;
				bounds = c.bounds;
				return name + Fmt(((Bounds)(ref bounds)).size);
			}
			return "box" + Fmt(Vector3.Scale(val.size, ((Component)val).transform.lossyScale)) + "@" + Fmt(((Component)val).transform.position + ((Component)val).transform.TransformVector(val.center) - ((Component)val).transform.root.position);
		}

		private static string Fmt(Vector3 v)
		{
			//IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"({v.x:0.##},{v.y:0.##},{v.z:0.##})";
		}

		private IEnumerator EnterWorld()
		{
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_report.Add("world: no FejdStartup");
				yield break;
			}
			PlayerProfile val = new PlayerProfile("stabilitytester", (FileSource)2);
			val.SetName("Mason");
			val.m_firstSpawn = false;
			val.Save();
			Game.SetProfile("stabilitytester", (FileSource)2);
			World val2 = new World("StabilityTest", "HQ7GnTiegh")
			{
				m_fileSource = (FileSource)2
			};
			val2.SaveWorldFWLData(DateTime.Now);
			instance.m_world = val2;
			instance.m_startingWorld = true;
			ZNet.SetServer(true, false, false, val2.m_name, "", val2);
			ZNet.ResetServerHost();
			instance.TransitionToMainScene();
			_report.Add("world: loading " + val2.m_name + " seed " + val2.m_seedName);
			float timeout = Time.realtimeSinceStartup + 300f;
			while (((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting()) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				_report.Add("world: player never spawned");
				yield break;
			}
			yield return (object)new WaitForSecondsRealtime(6f);
			_report.Add("world: spawned at " + ((object)((Component)Player.m_localPlayer).transform.position/*cast due to .constrained prefix*/).ToString());
		}

		private void UseVanilla(bool vanilla)
		{
			Stability.Bypass = vanilla;
			Stability.OnConfigChanged();
		}

		private TierValues ValuesFor(Tier t, bool vanilla)
		{
			if (!vanilla)
			{
				return Cfg.Values(t);
			}
			return Strength.Vanilla((t == Tier.Flametal) ? Tier.Iron : t);
		}

		private IEnumerator Grow(List<Row> rows, int maxPieces)
		{
			float limit = Time.realtimeSinceStartup + 900f;
			for (int k = 0; k < maxPieces; k++)
			{
				if (!(Time.realtimeSinceStartup < limit))
				{
					break;
				}
				List<(Row row, WearNTear w)> added = new List<(Row, WearNTear)>();
				foreach (Row row2 in rows)
				{
					if (row2.Done)
					{
						continue;
					}
					if (!row2.Clear(k))
					{
						row2.Done = true;
						row2.Capped = true;
						continue;
					}
					WearNTear val = row2.Place(k);
					if ((Object)(object)val == (Object)null)
					{
						row2.Done = true;
						continue;
					}
					row2.Pieces.Add(val);
					added.Add((row2, val));
				}
				if (added.Count == 0)
				{
					break;
				}
				float until = Time.realtimeSinceStartup + 6f;
				while (Time.realtimeSinceStartup < until && added.Any(((Row row, WearNTear w) a) => Alive(a.w) && !Checked(a.w)))
				{
					yield return null;
				}
				yield return (object)new WaitForSecondsRealtime(0.4f);
				foreach (var item in added)
				{
					var (row, _) = item;
					if (!Alive(item.w) || row.Pieces.Take(row.Pieces.Count - 1).Any((WearNTear x) => !Alive(x)))
					{
						row.Done = true;
					}
				}
			}
			yield return ((MonoBehaviour)this).StartCoroutine(Settle(rows.SelectMany((Row r) => r.Pieces).ToList(), 6f, 240f));
			foreach (Row row3 in rows)
			{
				row3.Standing = row3.Pieces.TakeWhile(Alive).Count();
				if (!row3.Done)
				{
					row3.Capped = true;
				}
			}
		}

		private static bool Checked(WearNTear w)
		{
			if (w.m_supportColliders.Count <= 0 && !(w.m_support < Stability.ValuesOf(w).Max - 0.01f))
			{
				if (Stability.TryGetInfo(w, out var info))
				{
					return !info.AnchorDirty;
				}
				return false;
			}
			return true;
		}

		private IEnumerator Towers()
		{
			Player localPlayer = Player.m_localPlayer;
			Vector3 site = FlatSpot(((Component)localPlayer).transform.position);
			ClearArea(site + new Vector3(17f, 0f, -30f), 95f);
			Stand(localPlayer, site + new Vector3(-12f, 0f, -14f));
			yield return (object)new WaitForSecondsRealtime(2f);
			Dictionary<(Tier, bool), float> heights = new Dictionary<(Tier, bool), float>();
			bool[] array = new bool[2] { true, false };
			foreach (bool vanilla in array)
			{
				UseVanilla(vanilla);
				List<Row> rows = new List<Row>();
				Dictionary<Row, float> steps = new Dictionary<Row, float>();
				for (int j = 0; j < Columns.Length; j++)
				{
					(Tier, string) tuple = Columns[j];
					Tier item = tuple.Item1;
					string prefab = tuple.Item2;
					Bounds shape = LocalShape(prefab);
					float step = ((Bounds)(ref shape)).size.y;
					Vector3 at = Ground(site + new Vector3((float)j * 5f, 0f, 0f));
					Row row = new Row
					{
						Tier = item,
						Prefab = prefab,
						Expected = Mathf.RoundToInt(Strength.ColumnHeight(ValuesFor(item, vanilla), step) / step),
						Place = (int k) => Place(prefab, at + Vector3.up * ((float)k * step - ((Bounds)(ref shape)).min.y - 0.2f), Quaternion.identity)
					};
					rows.Add(row);
					steps[row] = step;
				}
				yield return ((MonoBehaviour)this).StartCoroutine(Grow(rows, 130));
				string text = (vanilla ? "vanilla" : "mod");
				foreach (Row item3 in rows)
				{
					float num = (float)item3.Standing * steps[item3];
					heights[(item3.Tier, vanilla)] = num;
					Note(string.Format("tower {0} {1} ({2}): {3} pieces = {4}{5:0.#} m (formula {6})", text, item3.Tier, item3.Prefab, item3.Standing, item3.Capped ? "≥ " : "", num, item3.Expected));
					Check(Mathf.Abs(item3.Standing - item3.Expected) <= 1 || (item3.Capped && item3.Standing >= item3.Expected), $"tower {text} {item3.Tier}: {item3.Standing} pieces stand, the formula says {item3.Expected} (±1)");
				}
				if (!vanilla)
				{
					Vector3 val = Ground(site + new Vector3(17.5f, 0f, 0f));
					yield return ((MonoBehaviour)this).StartCoroutine(MainShot("01_towers", Above(val + Vector3.up * 60f, 180f, 135f, 22f), 60f));
				}
				Clear(rows.SelectMany((Row r) => r.Pieces));
				yield return (object)new WaitForSecondsRealtime(2f);
			}
			(Tier, string)[] columns = Columns;
			for (int num2 = 0; num2 < columns.Length; num2++)
			{
				Tier item2 = columns[num2].Item1;
				Check(heights[(item2, false)] >= heights[(item2, true)], $"tower {item2}: mod {heights[(item2, false)]:0} m ≥ vanilla {heights[(item2, true)]:0} m");
			}
			Check(heights[(Tier.BlackMarble, false)] >= 3f * heights[(Tier.BlackMarble, true)], "black marble builds at least 3 × as high as in vanilla");
			Check(heights[(Tier.BlackMarble, false)] > heights[(Tier.Iron, false)], "black marble builds higher than iron");
			Check(heights[(Tier.Grausten, false)] > heights[(Tier.BlackMarble, false)], "grausten builds higher than black marble");
			Check(heights[(Tier.Flametal, false)] > heights[(Tier.Grausten, false)], "flametal builds higher than grausten");
			Check(heights[(Tier.Flametal, false)] >= 2f * heights[(Tier.Iron, false)], "flametal builds at least twice as high as iron");
			UseVanilla(vanilla: false);
		}

		private IEnumerator Overhang()
		{
			Player localPlayer = Player.m_localPlayer;
			Vector3 origin = FlatSpot(((Component)localPlayer).transform.position) + new Vector3(-20f, 0f, -20f);
			ClearArea(origin + new Vector3(40f, 0f, 21f), 100f);
			Stand(localPlayer, origin + new Vector3(-8f, 0f, -6f));
			Dictionary<(Tier, bool), int> reach = new Dictionary<(Tier, bool), int>();
			Dictionary<(Tier, bool), bool> capped = new Dictionary<(Tier, bool), bool>();
			bool[] array = new bool[2] { true, false };
			foreach (bool vanilla in array)
			{
				UseVanilla(vanilla);
				ClearArea(origin + new Vector3(40f, 0f, 21f), 100f);
				yield return (object)new WaitForSecondsRealtime(1f);
				List<GameObject> plinths = new List<GameObject>();
				List<Row> rows = new List<Row>();
				for (int j = 0; j < Beams.Length; j++)
				{
					(Tier, string) tuple = Beams[j];
					Tier tier = tuple.Item1;
					string prefab = tuple.Item2;
					Vector3 foot = Ground(origin + new Vector3(0f, 0f, (float)j * 6f));
					float num = foot.y + 25f;
					plinths.Add(Plinth(new Vector3(foot.x, num - 13.5f, foot.z), new Vector3(2f, 27f, 2f)));
					float y = num - 1.5f;
					Bounds world;
					Row item = new Row
					{
						Tier = tier,
						Prefab = prefab,
						Expected = 1 + Mathf.RoundToInt(Strength.Overhang(ValuesFor(tier, vanilla)) / 2f),
						Place = (int k) => PlaceAligned(prefab, new Vector3(foot.x + 1f - 0.05f + 1f + (float)k * 2f, y, foot.z), Vector3.right, out world),
						Clear = (int k) => RowClear(new Vector3(foot.x + 2f + (float)k * 2f, y, foot.z), tier, k)
					};
					rows.Add(item);
				}
				yield return ((MonoBehaviour)this).StartCoroutine(Grow(rows, 80));
				string text = (vanilla ? "vanilla" : "mod");
				foreach (Row item3 in rows)
				{
					reach[(item3.Tier, vanilla)] = item3.Standing;
					if (!vanilla && (item3.Tier == Tier.Grausten || item3.Tier == Tier.BlackMarble))
					{
						int[] array2 = new int[4]
						{
							0,
							1,
							3,
							item3.Pieces.Count - 1
						};
						foreach (int num3 in array2)
						{
							if (num3 >= 0 && num3 < item3.Pieces.Count)
							{
								Dump($"{item3.Tier} slab {num3}", item3.Pieces[num3]);
							}
						}
					}
					capped[(item3.Tier, vanilla)] = item3.Capped;
					Note(string.Format("overhang {0} {1} ({2}): {3} pieces = {4}{5} m out from the rock (formula {6})", text, item3.Tier, item3.Prefab, item3.Standing, item3.Capped ? "≥ " : "", item3.Standing * 2, item3.Expected));
					bool flag = item3.Tier == Tier.Stone || item3.Tier == Tier.BlackMarble || item3.Tier == Tier.Grausten;
					Check(item3.Standing >= item3.Expected - 1 && (flag || item3.Capped || item3.Standing <= item3.Expected + 1), string.Format("overhang {0} {1}: {2} pieces, the formula says {3} ({4})", text, item3.Tier, item3.Standing, item3.Expected, flag ? "at least" : "±1"));
				}
				if (!vanilla)
				{
					yield return ((MonoBehaviour)this).StartCoroutine(MainShot("02_overhang", Above(Ground(origin + new Vector3(14f, 0f, 21f)) + Vector3.up * 20f, 205f, 50f, -12f), 60f));
				}
				Clear(rows.SelectMany((Row r) => r.Pieces));
				foreach (GameObject item4 in plinths)
				{
					Object.Destroy((Object)(object)item4);
				}
				yield return (object)new WaitForSecondsRealtime(2f);
			}
			(Tier, string)[] beams = Beams;
			for (int num2 = 0; num2 < beams.Length; num2++)
			{
				Tier item2 = beams[num2].Item1;
				Check(reach[(item2, false)] >= reach[(item2, true)], $"overhang {item2}: mod {reach[(item2, false)]} ≥ vanilla {reach[(item2, true)]}");
			}
			Check(reach[(Tier.Flametal, false)] > reach[(Tier.Iron, false)] || (capped[(Tier.Flametal, false)] && reach[(Tier.Flametal, false)] >= reach[(Tier.Iron, false)]), "flametal reaches farther than iron");
			Check(reach[(Tier.Stone, false)] <= 2, "stone still does not reach out (masonry does not bend)");
			UseVanilla(vanilla: false);
		}

		private IEnumerator Anchoring()
		{
			Player localPlayer = Player.m_localPlayer;
			Vector3 a0 = FlatSpot(((Component)localPlayer).transform.position);
			ClearArea(a0, 45f);
			Stand(localPlayer, a0 + new Vector3(-3f, 0f, 7f));
			Dictionary<string, float> results = new Dictionary<string, float>();
			bool[] array = new bool[2] { true, false };
			foreach (bool vanilla in array)
			{
				UseVanilla(vanilla);
				string mode = (vanilla ? "vanilla" : "mod");
				Vector3 g = Ground(a0);
				List<WearNTear> all = new List<WearNTear>();
				WearNTear wall = Place("stone_wall_4x2", g + Vector3.up * 0.8f, Quaternion.identity);
				WearNTear upper = Place("stone_wall_4x2", g + Vector3.up * 2.8f, Quaternion.identity);
				float num = g.z + 0.5f;
				Bounds world;
				WearNTear deep = PlaceAligned("woodiron_beam", new Vector3(g.x - 1.2f, g.y + 1.3f, num - 0.7f + 1f), Vector3.forward, out world);
				WearNTear shallow = PlaceAligned("woodiron_beam", new Vector3(g.x + 1.2f, g.y + 1.3f, num - 0.3f + 1f), Vector3.forward, out world);
				WearNTear high = PlaceAligned("woodiron_beam", new Vector3(g.x, g.y + 3.3f, num - 0.7f + 1f), Vector3.forward, out world);
				WearNTear corbel = PlaceAligned("stone_floor_2x2", new Vector3(g.x, g.y + 1.2f, g.z - 0.5f + 0.7f - 1f), Vector3.forward, out world);
				all.AddRange((IEnumerable<WearNTear>)(object)new WearNTear[6] { wall, upper, deep, shallow, high, corbel });
				yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f));
				(string, WearNTear)[] array2 = new(string, WearNTear)[6]
				{
					("wall", wall),
					("upper", upper),
					("deep", deep),
					("shallow", shallow),
					("high", high),
					("corbel", corbel)
				};
				for (int j = 0; j < array2.Length; j++)
				{
					var (text, w) = array2[j];
					Dump(mode + " " + text, w);
				}
				results[mode + " deep"] = Percent(deep);
				results[mode + " shallow"] = Percent(shallow);
				results[mode + " high"] = SupportOf(high);
				results[mode + " upper"] = SupportOf(upper);
				results[mode + " corbel"] = (Alive(corbel) ? Percent(corbel) : (-1f));
				Note($"anchor {mode}: wall {Percent(wall):0}% upper {Percent(upper):0}% ({SupportOf(upper):0}) | iron deep {Percent(deep):0}% shallow {Percent(shallow):0}% " + string.Format("in upper course {0:0}% ({1:0}) | stone corbel {2}", Percent(high), SupportOf(high), Alive(corbel) ? (Percent(corbel).ToString("0") + "%") : "fell"));
				if (!vanilla)
				{
					Note("hover deep: " + Plain(SupportHover.Describe(deep)));
					Note("hover shallow: " + Plain(SupportHover.Describe(shallow)));
					Note("hover high: " + Plain(SupportHover.Describe(high)));
					Note("hover corbel: " + Plain(SupportHover.Describe(corbel)));
					Note("hover wall: " + Plain(SupportHover.Describe(wall)));
					Check(SupportHover.Describe(deep).Contains(Lang.T("bs_host_foundation")), "deep beam: hover names the foundation wall");
					Check(SupportHover.Describe(shallow).Contains(Lang.T("bs_hover_partial").Substring(0, 5)), "shallow beam: hover tells how much it holds");
				}
				if (!vanilla)
				{
					Note($"anchor cost so far: {Anchors.MeasureCount} measurings, {Anchors.MeasureSeconds * 1000.0:0.0} ms in all");
					yield return ((MonoBehaviour)this).StartCoroutine(MainShot("03_anchors", Above(g + new Vector3(0f, 1.6f, 0.6f), 35f, 8.5f, 1.2f), 50f));
					yield return ((MonoBehaviour)this).StartCoroutine(HammerShot("04_hover_deep", deep));
					yield return ((MonoBehaviour)this).StartCoroutine(HammerShot("05_hover_shallow", shallow));
				}
				if (!vanilla)
				{
					wall.m_nview.Destroy();
					yield return ((MonoBehaviour)this).StartCoroutine(Settle(all.Where(Alive).ToList(), 6f));
					array2 = new(string, WearNTear)[5]
					{
						("upper", upper),
						("deep", deep),
						("shallow", shallow),
						("high", high),
						("corbel", corbel)
					};
					for (int j = 0; j < array2.Length; j++)
					{
						var (text2, w2) = array2[j];
						if (Alive(w2))
						{
							Dump("after wall " + text2, w2);
						}
					}
					Check(!Alive(deep) && !Alive(shallow) && !Alive(corbel), $"wall removed: the beams and the corbel set into it fall (deep {Alive(deep)}, shallow {Alive(shallow)}, corbel {Alive(corbel)}; must be False)");
					Check(!Alive(upper) && !Alive(high), $"wall removed: the course above and its beam fall (upper {Alive(upper)}, beam {Alive(high)}; must be False)");
				}
				Clear(all);
				yield return (object)new WaitForSecondsRealtime(2f);
			}
			Check(results["mod deep"] >= 99.9f, string.Format("iron beam a third deep in a foundation wall: full support ({0:0} %; vanilla {1:0} %)", results["mod deep"], results["vanilla deep"]));
			Check(results["vanilla deep"] < 99.9f, string.Format("vanilla gives it less ({0:0} %)", results["vanilla deep"]));
			Check(results["mod shallow"] < 99.9f && results["mod shallow"] >= results["vanilla shallow"] - 0.5f, string.Format("shallow beam: not full, but never worse than vanilla ({0:0} % vs {1:0} %)", results["mod shallow"], results["vanilla shallow"]));
			float num2 = results["mod upper"] * 0.97f;
			Check(Mathf.Abs(results["mod high"] - Mathf.Min(num2, Cfg.Values(Tier.Iron).Max)) <= 0.02f * num2 || results["mod high"] >= num2, string.Format("beam in the upper course takes that wall's support ({0:0} vs wall {1:0} × 0.97)", results["mod high"], results["mod upper"]));
			Check(results["mod corbel"] >= 99.9f, string.Format("stone corbel set into the wall: full support ({0:0} %; vanilla {1})", results["mod corbel"], (results["vanilla corbel"] < 0f) ? "fell" : (results["vanilla corbel"].ToString("0") + " %")));
			UseVanilla(vanilla: false);
		}

		private IEnumerator NoFloating()
		{
			Player localPlayer = Player.m_localPlayer;
			Vector3 val = Ground(FlatSpot(((Component)localPlayer).transform.position));
			ClearArea(val, 25f);
			Stand(localPlayer, val + new Vector3(-6f, 0f, 6f));
			UseVanilla(vanilla: false);
			GameObject rock = Plinth(val + Vector3.up * 1.5f, new Vector3(2f, 5f, 2f));
			float num = val.y + 4f;
			WearNTear a = Place("stone_wall_2x1", new Vector3(val.x, num + 0.5f, val.z), Quaternion.identity);
			Bounds world;
			WearNTear beam = PlaceAligned("woodiron_beam", new Vector3(val.x + 0.3f, num + 0.5f, val.z + 0.5f - 0.7f + 1f), Vector3.forward, out world);
			WearNTear b = Place("stone_wall_2x1", new Vector3(val.x + 0.3f, num + 0.65f + 0.5f, val.z + 1.2f), Quaternion.identity);
			WearNTear s1 = PlaceAligned("stone_floor_2x2", new Vector3(val.x - 0.3f, num + 1.5f, val.z - 1.2f), Vector3.right, out world);
			WearNTear s2 = PlaceAligned("stone_floor_2x2", new Vector3(val.x - 0.3f + 1.4f, num + 1.6f, val.z - 1.2f), Vector3.right, out world);
			List<WearNTear> all = new List<WearNTear> { a, beam, b, s1, s2 };
			yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 5f, 90f));
			Note($"before: wall {Percent(a):0}% beam {Percent(beam):0}% wall on beam {Alive(b)} slabs {Alive(s1)}/{Alive(s2)}");
			Check(Alive(a) && Alive(beam) && Percent(beam) >= 99.9f, "on the rock: the wall is a foundation, its beam has full support");
			Object.Destroy((Object)(object)rock);
			float t0 = Time.realtimeSinceStartup;
			yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f, 180f));
			Note(string.Format("after the rock went ({0:0} s): alive {1}", Time.realtimeSinceStartup - t0, string.Join(" ", all.Select((WearNTear w) => (!Alive(w)) ? "no" : "yes"))));
			foreach (WearNTear item in all.Where(Alive))
			{
				Dump("floating", item);
			}
			Check(all.All((WearNTear w) => !Alive(w)), "rock gone: nothing holds itself up in the air (all must have fallen)");
			Clear(all);
		}

		private IEnumerator Cliff()
		{
			Player p = Player.m_localPlayer;
			Vector3 facePoint = Vector3.zero;
			Vector3 uphill = Vector3.zero;
			string kind = "";
			foreach (var (at, dir) in CliffCandidates(((Component)p).transform.position))
			{
				Stand(p, at - dir * 8f);
				float until = Time.realtimeSinceStartup + 60f;
				while (!GroundLoaded(at) && Time.realtimeSinceStartup < until)
				{
					yield return null;
				}
				yield return (object)new WaitForSecondsRealtime(4f);
				if (FindRockFace(at, out var hit, out var into, out var name))
				{
					facePoint = hit;
					uphill = into;
					kind = "rock " + name;
					break;
				}
				Vector3 val = Ground(at - dir * 2f);
				float num = Ground(at + dir * 2f).y - val.y;
				Note($"cliff candidate {at:F0}: no rock face; ground rises {num:0.0} m over 4 m");
				if (!(num >= 2.5f) || !(kind == ""))
				{
					continue;
				}
				for (float num2 = 0f; num2 < 6f; num2 += 0.05f)
				{
					Vector3 val2 = Ground(val + dir * num2);
					if (val2.y >= val.y + 1.8f)
					{
						facePoint = val2;
						break;
					}
				}
				uphill = dir;
				kind = $"ground slope ({Mathf.Atan2(num, 4f) * 57.29578f:0}°)";
			}
			Note("cliff: building against " + kind);
			if (uphill == Vector3.zero)
			{
				Check(ok: false, "cliff: no steep rock face found near the start");
				yield break;
			}
			Vector3 val3 = Vector3.Cross(Vector3.up, uphill);
			Stand(p, facePoint - uphill * 9f + val3 * 4f);
			float loaded = Time.realtimeSinceStartup + 60f;
			while (!GroundLoaded(facePoint) && Time.realtimeSinceStartup < loaded)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(3f);
			ClearArea(facePoint, 40f);
			float y = facePoint.y;
			Note($"cliff: face at {facePoint}, ground 1 m in front {Ground(facePoint - uphill).y - y:+0.0;-0.0} m, 1 m behind {Ground(facePoint + uphill).y - y:+0.0;-0.0} m");
			yield return ((MonoBehaviour)this).StartCoroutine(BuildAgainst(facePoint, uphill, kind, "06_cliff", "07_hover_rock", slopeWall: true));
			yield return ((MonoBehaviour)this).StartCoroutine(RockFace());
			UseVanilla(vanilla: false);
		}

		private IEnumerator RockFace()
		{
			Player localPlayer = Player.m_localPlayer;
			GameObject pf = (from g in ZNetScene.instance.m_prefabs
				where (Object)(object)g != (Object)null && ((Object)g).name.IndexOf("rock", StringComparison.OrdinalIgnoreCase) >= 0 && (Object)(object)g.GetComponentInChildren<MineRock>() == (Object)null && (Object)(object)g.GetComponentInChildren<MineRock5>() == (Object)null && (Object)(object)g.GetComponentInChildren<Destructible>() == (Object)null && (Object)(object)g.GetComponentInChildren<WearNTear>() == (Object)null && (Object)(object)g.GetComponentInChildren<Pickable>() == (Object)null
				select (g: g, size: RockSize(g)) into x
				where x.size.y >= 5f && Mathf.Min(x.size.x, x.size.z) >= 5f
				orderby ((Vector3)(ref x.size)).magnitude
				select x.g).FirstOrDefault();
			if ((Object)(object)pf == (Object)null)
			{
				Check(ok: false, "rock face: no fixed rock prefab with a concave collider found");
				yield break;
			}
			Vector3 site = FlatSpot(((Component)localPlayer).transform.position);
			Stand(localPlayer, site + new Vector3(-14f, 0f, 0f));
			float loaded = Time.realtimeSinceStartup + 60f;
			while (!GroundLoaded(site) && Time.realtimeSinceStartup < loaded)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(3f);
			ClearArea(site, 35f);
			GameObject rock = Object.Instantiate<GameObject>(pf, Ground(site) + Vector3.down * 1f, Quaternion.identity);
			yield return (object)new WaitForSecondsRealtime(1f);
			Note($"rock face: spawned {((Object)pf).name}, size {RockSize(pf):F1}");
			if (!FindRockFace(site, out var hit, out var into, out var name, 4.5f))
			{
				Check(ok: false, "rock face: no steep side on " + ((Object)pf).name);
				yield break;
			}
			yield return ((MonoBehaviour)this).StartCoroutine(BuildAgainst(hit, into, "rock " + name, "08_rock", "09_hover_rock", slopeWall: false));
			ZNetView component = rock.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				component.Destroy();
			}
			else
			{
				Object.Destroy((Object)(object)rock);
			}
		}

		private static Vector3 RockSize(GameObject g)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_006a: 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)
			Vector3 result = Vector3.zero;
			MeshCollider[] componentsInChildren = g.GetComponentsInChildren<MeshCollider>(true);
			foreach (MeshCollider val in componentsInChildren)
			{
				if (!val.convex && !((Collider)val).isTrigger && !((Object)(object)val.sharedMesh == (Object)null))
				{
					Bounds bounds = val.sharedMesh.bounds;
					Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).size, ((Component)val).transform.lossyScale);
					if (((Vector3)(ref val2)).magnitude > ((Vector3)(ref result)).magnitude)
					{
						result = val2;
					}
				}
			}
			return result;
		}

		private IEnumerator BuildAgainst(Vector3 facePoint, Vector3 uphill, string kind, string shot, string hoverShot, bool slopeWall)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Vector3 side = Vector3.Cross(Vector3.up, uphill);
			bool[] array = new bool[2] { true, false };
			foreach (bool vanilla in array)
			{
				UseVanilla(vanilla);
				string mode = (vanilla ? "vanilla" : "mod");
				List<WearNTear> all = new List<WearNTear>();
				List<WearNTear> inner = new List<WearNTear>();
				float[] array2 = new float[2] { -1f, 1f };
				Bounds world;
				foreach (float num in array2)
				{
					Vector3 val = facePoint + side * num + Vector3.up * 0.05f;
					WearNTear item = PlaceAligned("woodiron_beam", val - uphill * 0.19999999f, -uphill, out world);
					WearNTear item2 = PlaceAligned("woodiron_beam", val - uphill * 2.2f, -uphill, out world);
					inner.Add(item);
					all.Add(item);
					all.Add(item2);
				}
				Quaternion rot = Quaternion.LookRotation(-uphill);
				for (int k = 0; k < 2; k++)
				{
					Vector3 pos = facePoint - uphill * (1.6f + (float)k * 2f) + Vector3.up * 0.25f;
					all.Add(Place("wood_floor", pos, rot));
				}
				Vector3 pos2 = facePoint - uphill * 4.6f + Vector3.up * 1.3f;
				all.Add(Place("woodwall", pos2, rot));
				all.Add(Place("woodwall", facePoint - uphill * 3.6f + side * 1f + Vector3.up * 1.3f, Quaternion.LookRotation(side)));
				all.Add(Place("woodwall", facePoint - uphill * 3.6f - side * 1f + Vector3.up * 1.3f, Quaternion.LookRotation(side)));
				all.RemoveAll((WearNTear w) => (Object)(object)w == (Object)null);
				Vector3 at = facePoint + side * 5f;
				WearNTear val2 = ((!slopeWall) ? null : Place("stone_wall_2x1", Ground(at) + Vector3.up * 0.3f - uphill * 0.2f, Quaternion.LookRotation(side)));
				WearNTear swBeam = (((Object)(object)val2 == (Object)null) ? null : PlaceAligned("woodiron_beam", ((Component)val2).transform.position - uphill * 0.8f + Vector3.up * 0.2f, -uphill, out world));
				all.Add(val2);
				all.Add(swBeam);
				all.RemoveAll((WearNTear w) => (Object)(object)w == (Object)null);
				yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f));
				int num2 = all.Count(Alive);
				Note(string.Format("{0} {1}: {2} of {3} pieces stand; rock beams {4}, ", kind, mode, num2, all.Count, string.Join(" ", inner.Select((WearNTear w) => Percent(w).ToString("0") + "%"))) + (slopeWall ? $"beam in the stone wall {Percent(swBeam):0}%" : ""));
				if (!vanilla)
				{
					Check(num2 == all.Count, $"{kind}: house on beams set into it, everything stands ({num2} of {all.Count})");
					Check(inner.All((WearNTear w) => Percent(w) >= 99.9f), kind + ": beams set into it have full support");
					if (slopeWall)
					{
						Check(Percent(swBeam) >= 99.9f, $"beam in a stone wall set into the slope: full support ({Percent(swBeam):0} %)");
					}
					Note("hover rock beam: " + Plain(SupportHover.Describe(inner[0])));
					string text = Lang.T(kind.StartsWith("rock") ? "bs_host_rock" : "bs_host_ground");
					Check(SupportHover.Describe(inner[0]).Contains(text), "rock beam: hover says it is set into " + text);
					EnvMan.instance.m_debugTimeOfDay = true;
					EnvMan.instance.m_debugTime = 0.62f;
					yield return (object)new WaitForSecondsRealtime(1f);
					Vector3 target = facePoint - uphill * 2.5f + Vector3.up * 1.2f;
					Quaternion val3 = Quaternion.LookRotation(-uphill);
					float bearing = ((Quaternion)(ref val3)).eulerAngles.y + 35f;
					yield return ((MonoBehaviour)this).StartCoroutine(MainShot(shot, Above(target, bearing, 10f, 1.5f), 58f));
					yield return ((MonoBehaviour)this).StartCoroutine(HammerShot(hoverShot, inner[0]));
				}
				Clear(all);
				yield return (object)new WaitForSecondsRealtime(2f);
			}
			UseVanilla(vanilla: false);
		}

		private bool FindRockFace(Vector3 at, out Vector3 hit, out Vector3 into, out string name, float height = 3f)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: 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_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: 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_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)