Decompiled source of Sinka v1.0.0

plugins/Sinka/Sinka.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Snap points for chests, fences and anything else that will not line up.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+0acc3c7d875ae07c18a9149ec689d9ae170f3e7c")]
[assembly: AssemblyProduct("Sinka")]
[assembly: AssemblyTitle("Sinka")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Sinka
{
	internal static class Buildable
	{
		private static ObjectDB _builtFrom;

		private static HashSet<string> _names;

		private const int MaxWaitFrames = 900;

		private static int _waited;

		private static bool _gaveUp;

		public static bool Ready()
		{
			if (!SinkaConfig.BuildablePiecesOnly.Value)
			{
				return true;
			}
			if (Build())
			{
				return true;
			}
			if (_gaveUp)
			{
				return true;
			}
			if (++_waited < 900)
			{
				return false;
			}
			_gaveUp = true;
			SinkaPlugin.Log.LogWarning((object)("No piece tables found after " + 900 + " frames, so BuildablePiecesOnly cannot be applied. Snapping everything with a Piece component instead, which includes dungeon loot chests and pots."));
			return true;
		}

		public static bool Includes(GameObject prefab)
		{
			if (!SinkaConfig.BuildablePiecesOnly.Value)
			{
				return true;
			}
			if (_names == null)
			{
				return true;
			}
			return _names.Contains(((Object)prefab).name);
		}

		private static bool Build()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_items == null || instance.m_items.Count == 0)
			{
				return false;
			}
			if ((Object)(object)_builtFrom == (Object)(object)instance && _names != null)
			{
				return true;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			int num = 0;
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
				{
					continue;
				}
				PieceTable buildPieces = component.m_itemData.m_shared.m_buildPieces;
				if ((Object)(object)buildPieces == (Object)null || buildPieces.m_pieces == null)
				{
					continue;
				}
				num++;
				foreach (GameObject piece in buildPieces.m_pieces)
				{
					if ((Object)(object)piece != (Object)null)
					{
						hashSet.Add(((Object)piece).name);
					}
				}
			}
			if (num == 0 || hashSet.Count == 0)
			{
				return false;
			}
			_names = hashSet;
			_builtFrom = instance;
			SinkaPlugin.Log.LogInfo((object)("Buildable set: " + hashSet.Count + " piece(s) across " + num + " piece table(s)."));
			return true;
		}
	}
	internal static class SinkaConfig
	{
		private const string DefaultFences = "wood_fence, piece_sharpstakes, piece_stakewall_blackwood, piece_dvergr_sharpstakes, piece_dvergr_stake_wall";

		public static ConfigEntry<bool> SnapContainers;

		public static ConfigEntry<bool> SnapFences;

		public static ConfigEntry<bool> SnapUnsnappedPieces;

		public static ConfigEntry<bool> BuildablePiecesOnly;

		public static ConfigEntry<string> FencePrefabs;

		public static ConfigEntry<string> ExcludePrefabs;

		public static ConfigEntry<string> PointOverrides;

		public static ConfigEntry<float> Gap;

		public static ConfigEntry<float> FenceLadderStep;

		public static ConfigEntry<float> FenceLadderBelow;

		public static ConfigEntry<bool> Verbose;

		private static HashSet<string> _fences;

		private static HashSet<string> _excluded;

		private static Dictionary<string, Vector3[]> _overrides;

		public static void Bind(ConfigFile config)
		{
			SnapContainers = config.Bind<bool>("Snapping", "SnapContainers", true, "Snap anything buildable that holds items. Matched on components, so modded chests are covered without naming them.");
			SnapFences = config.Bind<bool>("Snapping", "SnapFences", true, "Snap the fences and stake walls listed in FencePrefabs.");
			SnapUnsnappedPieces = config.Bind<bool>("Snapping", "SnapUnsnappedPieces", false, "Snap every buildable piece the game never gave snap points to. Catches modded pieces for free, but also chairs, banners and item stands, where snapping tends to fight you rather than help.");
			BuildablePiecesOnly = config.Bind<bool>("Snapping", "BuildablePiecesOnly", true, "Only snap pieces that actually appear in a build menu. Off means anything with a Piece component qualifies, which includes dungeon loot chests and pots you cannot place and probably do not want to snap to.");
			FencePrefabs = config.Bind<string>("Snapping", "FencePrefabs", "wood_fence, piece_sharpstakes, piece_stakewall_blackwood, piece_dvergr_sharpstakes, piece_dvergr_stake_wall", "Comma-separated prefab names treated as fences. Names that do not exist are reported in the log at startup.");
			ExcludePrefabs = config.Bind<string>("Snapping", "ExcludePrefabs", "", "Comma-separated prefab names to leave alone, whatever else matches.");
			PointOverrides = config.Bind<string>("Snapping", "PointOverrides", "", "Exact snap points for named prefabs, replacing anything derived. Format:\n#   prefab: x,y,z | x,y,z ; other_prefab: x,y,z\n# Semicolons separate prefabs, a colon follows the name, pipes separate\n# points, commas separate the three coordinates of one point. Naming a\n# prefab here is enough to get it snapped - it does not also have to be a\n# container or a listed fence. Decimals must use a dot, never a comma,\n# because a comma already separates coordinates. Gap and the fence ladder\n# do not apply: a point given here is used exactly as written.");
			Gap = config.Bind<float>("Snapping", "Gap", 0f, "Metres of space left between two chained pieces. 0 places them flush. Negative values are ignored - overlapping pieces just clip.");
			FenceLadderStep = config.Bind<float>("Snapping", "FenceLadderStep", 0.2f, "Vertical spacing of the snap points up each end of a fence, in metres. Smaller follows sloping ground more closely and costs more points per piece. 0 turns the ladder off and gives fences plain corners like a chest.");
			FenceLadderBelow = config.Bind<float>("Snapping", "FenceLadderBelow", 0.2f, "How far below its own base a fence's lowest rung sits, in metres. This is what lets the next panel step down rather than only up.");
			Verbose = config.Bind<bool>("Diagnostics", "Verbose", false, "Log the measured footprint of every piece that gets snap points, and the colliders it was measured from.");
		}

		public static bool IsFence(string prefabName)
		{
			if (_fences == null)
			{
				_fences = Split(FencePrefabs.Value);
			}
			return _fences.Contains(prefabName);
		}

		public static bool IsExcluded(string prefabName)
		{
			if (_excluded == null)
			{
				_excluded = Split(ExcludePrefabs.Value);
			}
			if (_excluded.Count > 0)
			{
				return _excluded.Contains(prefabName);
			}
			return false;
		}

		public static IEnumerable<string> ConfiguredFences()
		{
			if (_fences == null)
			{
				_fences = Split(FencePrefabs.Value);
			}
			return _fences;
		}

		public static bool HasPointOverride(string prefabName)
		{
			if (Overrides().Count > 0)
			{
				return Overrides().ContainsKey(prefabName);
			}
			return false;
		}

		public static Vector3[] PointOverride(string prefabName)
		{
			if (!Overrides().TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		public static IEnumerable<string> ConfiguredOverrides()
		{
			return Overrides().Keys;
		}

		private static Dictionary<string, Vector3[]> Overrides()
		{
			if (_overrides != null)
			{
				return _overrides;
			}
			_overrides = new Dictionary<string, Vector3[]>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(PointOverrides.Value))
			{
				return _overrides;
			}
			string[] array = PointOverrides.Value.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.IndexOf(':');
				if (num <= 0)
				{
					Warn("PointOverrides entry has no 'prefab:' part and was ignored: " + text);
					continue;
				}
				string text2 = text.Substring(0, num).Trim();
				Vector3[] array2 = ParsePoints(text.Substring(num + 1), text2);
				if (text2.Length != 0 && array2 != null)
				{
					_overrides[text2] = array2;
				}
			}
			return _overrides;
		}

		private static Vector3[] ParsePoints(string text, string prefabName)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			string[] array = text.Split(new char[1] { '|' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					string[] array2 = text2.Split(new char[1] { ',' });
					float result;
					float result2;
					float result3;
					if (array2.Length != 3)
					{
						Warn("PointOverrides: " + prefabName + " has a point that is not three numbers and was ignored: " + text2);
					}
					else if (!float.TryParse(array2[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result) || !float.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result2) || !float.TryParse(array2[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
					{
						Warn("PointOverrides: " + prefabName + " has a point that does not parse as numbers and was ignored: " + text2);
					}
					else
					{
						list.Add(new Vector3(result, result2, result3));
					}
				}
			}
			if (list.Count != 0)
			{
				return list.ToArray();
			}
			Warn("PointOverrides: " + prefabName + " listed no usable points, so it is snapped the derived way instead.");
			return null;
		}

		private static void Warn(string message)
		{
			if (SinkaPlugin.Log != null)
			{
				SinkaPlugin.Log.LogWarning((object)message);
			}
		}

		private static HashSet<string> Split(string value)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(value))
			{
				return hashSet;
			}
			string[] array = value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}
	}
	[BepInPlugin("ezomic.valheim.sinka", "Sinka", "1.0.0")]
	public class SinkaPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.sinka";

		public const string PluginName = "Sinka";

		public const string PluginVersion = "1.0.0";

		public const string PluginAuthor = "Robbin Thijssen";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SinkaConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("ezomic.valheim.sinka");
			_harmony.PatchAll(typeof(ScenePatches));
			Log.LogInfo((object)"Sinka 1.0.0 by Robbin Thijssen - ready.");
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			if (!((Object)(object)ZNetScene.instance == (Object)null))
			{
				SnapPoints.Apply();
			}
		}
	}
	internal static class ScenePatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static void AddSnapPointsOnScene()
		{
			SnapPoints.Apply();
		}
	}
	internal static class SnapPoints
	{
		private const string Tag = "snappoint";

		private static ZNetScene _appliedTo;

		private static int _pieceMask;

		private const int MaxRungs = 24;

		private static int PieceMask
		{
			get
			{
				if (_pieceMask == 0)
				{
					_pieceMask = LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" });
				}
				return _pieceMask;
			}
		}

		public static bool Apply()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_appliedTo == (Object)(object)instance)
			{
				return true;
			}
			if (!Buildable.Ready())
			{
				return false;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			List<string> list = new List<string>();
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null || !Eligible(prefab))
				{
					continue;
				}
				if (HasSnapPoints(prefab.transform))
				{
					num2++;
					continue;
				}
				try
				{
					Vector3[] array = SinkaConfig.PointOverride(((Object)prefab).name);
					bool flag = array == null && UseLadder(prefab);
					if ((array != null) ? AddExact(prefab, array) : (flag ? AddLadder(prefab) : AddCorners(prefab)))
					{
						num++;
						if (array != null)
						{
							num4++;
						}
						else if (flag)
						{
							num3++;
						}
					}
				}
				catch (Exception ex)
				{
					SinkaPlugin.Log.LogWarning((object)("Could not snap " + ((Object)prefab).name + ": " + ex.Message));
				}
				if (!ReachableAsTarget(prefab))
				{
					list.Add(((Object)prefab).name);
				}
			}
			if (list.Count > 0)
			{
				SinkaPlugin.Log.LogWarning((object)("These have snap points, but none of their colliders are on the piece or piece_nonsolid layer, which is where the game looks for something to snap to: " + string.Join(", ", list.ToArray()) + ". Placing one still snaps it to its neighbours; what will not work is snapping anything to it once it is standing. That is the piece's own layer setup, not something this mod will change for it."));
			}
			_appliedTo = instance;
			SinkaPlugin.Log.LogInfo((object)("Added snap points to " + num + " piece(s): " + num3 + " fence ladder, " + num4 + " from PointOverrides, " + (num - num3 - num4) + " corners. " + num2 + " already had their own."));
			ReportMissingNames();
			return true;
		}

		private static bool ReachableAsTarget(GameObject prefab)
		{
			bool flag = false;
			Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (!val.isTrigger)
				{
					flag = true;
					if ((PieceMask & (1 << ((Component)val).gameObject.layer)) != 0)
					{
						return true;
					}
				}
			}
			return !flag;
		}

		private static bool AddExact(GameObject prefab, Vector3[] points)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < points.Length; i++)
			{
				Create(prefab, "snap_custom" + (i + 1), points[i]);
			}
			if (SinkaConfig.Verbose.Value)
			{
				SinkaPlugin.Log.LogInfo((object)(((Object)prefab).name + ": " + points.Length + " point(s) from PointOverrides"));
			}
			return true;
		}

		private static void ReportMissingNames()
		{
			if (SinkaConfig.SnapFences.Value)
			{
				Report("FencePrefabs", SinkaConfig.ConfiguredFences());
			}
			Report("PointOverrides", SinkaConfig.ConfiguredOverrides());
		}

		private static void Report(string setting, IEnumerable<string> names)
		{
			List<string> list = new List<string>();
			foreach (string name in names)
			{
				if ((Object)(object)ZNetScene.instance.GetPrefab(name) == (Object)null)
				{
					list.Add(name);
				}
			}
			if (list.Count != 0)
			{
				SinkaPlugin.Log.LogWarning((object)(setting + " names that match no prefab: " + string.Join(", ", list.ToArray())));
			}
		}

		private static bool Eligible(GameObject prefab)
		{
			if ((Object)(object)prefab.GetComponent<Piece>() == (Object)null)
			{
				return false;
			}
			if ((Object)(object)prefab.GetComponent<Ship>() != (Object)null)
			{
				return false;
			}
			if (SinkaConfig.IsExcluded(((Object)prefab).name))
			{
				return false;
			}
			bool flag = SinkaConfig.HasPointOverride(((Object)prefab).name);
			if (!flag && !Buildable.Includes(prefab))
			{
				return false;
			}
			if (flag)
			{
				return true;
			}
			if (SinkaConfig.SnapContainers.Value && (Object)(object)prefab.GetComponent<Container>() != (Object)null)
			{
				return true;
			}
			if (SinkaConfig.SnapFences.Value && SinkaConfig.IsFence(((Object)prefab).name))
			{
				return true;
			}
			return SinkaConfig.SnapUnsnappedPieces.Value;
		}

		private static bool HasSnapPoints(Transform root)
		{
			for (int i = 0; i < root.childCount; i++)
			{
				if (((Component)root.GetChild(i)).CompareTag("snappoint"))
				{
					return true;
				}
			}
			return false;
		}

		private static bool AddCorners(GameObject prefab)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: 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_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_00a8: 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_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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if (!Footprint(prefab, out var bounds))
			{
				return false;
			}
			float num = Mathf.Max(0f, SinkaConfig.Gap.Value) * 0.5f;
			Vector3 val = ((Bounds)(ref bounds)).extents + new Vector3(num, num, num);
			int[] array = new int[2] { -1, 1 };
			foreach (int num2 in array)
			{
				int[] array2 = new int[2] { -1, 1 };
				foreach (int num3 in array2)
				{
					int[] array3 = new int[2] { -1, 1 };
					foreach (int num4 in array3)
					{
						Vector3 localPosition = ((Bounds)(ref bounds)).center + new Vector3(val.x * (float)num2, val.y * (float)num3, val.z * (float)num4);
						string text = ((num3 < 0) ? "bottom" : "top") + "-" + ((num4 < 0) ? "back" : "front") + "-" + ((num2 < 0) ? "left" : "right");
						Create(prefab, "snap_" + text, localPosition);
					}
				}
			}
			if (SinkaConfig.Verbose.Value)
			{
				ManualLogSource log = SinkaPlugin.Log;
				string[] obj = new string[5]
				{
					((Object)prefab).name,
					": footprint ",
					null,
					null,
					null
				};
				Vector3 val2 = ((Bounds)(ref bounds)).size;
				obj[2] = ((Vector3)(ref val2)).ToString("F2");
				obj[3] = " centred ";
				val2 = ((Bounds)(ref bounds)).center;
				obj[4] = ((Vector3)(ref val2)).ToString("F2");
				log.LogInfo((object)string.Concat(obj));
			}
			return true;
		}

		private static bool UseLadder(GameObject prefab)
		{
			if (SinkaConfig.SnapFences.Value && SinkaConfig.FenceLadderStep.Value > 0f)
			{
				return SinkaConfig.IsFence(((Object)prefab).name);
			}
			return false;
		}

		private static bool AddLadder(GameObject prefab)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			if (!Footprint(prefab, out var bounds))
			{
				return false;
			}
			bool flag = ((Bounds)(ref bounds)).size.x >= ((Bounds)(ref bounds)).size.z;
			float reach = (flag ? ((Bounds)(ref bounds)).extents.x : ((Bounds)(ref bounds)).extents.z) + Mathf.Max(0f, SinkaConfig.Gap.Value) * 0.5f;
			float value = SinkaConfig.FenceLadderStep.Value;
			float num = ((Bounds)(ref bounds)).min.y - Mathf.Max(0f, SinkaConfig.FenceLadderBelow.Value);
			int num2 = Mathf.FloorToInt((((Bounds)(ref bounds)).max.y - num) / value) + 1;
			bool flag2 = num2 > 24;
			if (flag2)
			{
				num2 = 24;
			}
			for (int i = 0; i < num2; i++)
			{
				AddRung(prefab, bounds, flag, reach, num + (float)i * value);
			}
			float num3 = num + (float)(num2 - 1) * value;
			if (!flag2 && ((Bounds)(ref bounds)).max.y - num3 > value * 0.25f)
			{
				AddRung(prefab, bounds, flag, reach, ((Bounds)(ref bounds)).max.y);
			}
			Vector3 val;
			if (flag2)
			{
				ManualLogSource log = SinkaPlugin.Log;
				string[] obj = new string[10]
				{
					((Object)prefab).name,
					" is ",
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				val = ((Bounds)(ref bounds)).size;
				obj[2] = val.y.ToString("F2");
				obj[3] = "m tall and FenceLadderStep is ";
				obj[4] = value.ToString();
				obj[5] = "m, which wants more than ";
				obj[6] = 24.ToString();
				obj[7] = " rungs. The ladder stops at ";
				obj[8] = num3.ToString("F2");
				obj[9] = "m - raise the step to cover the whole piece.";
				log.LogWarning((object)string.Concat(obj));
			}
			if (SinkaConfig.Verbose.Value)
			{
				ManualLogSource log2 = SinkaPlugin.Log;
				string[] obj2 = new string[9]
				{
					((Object)prefab).name,
					": footprint ",
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				val = ((Bounds)(ref bounds)).size;
				obj2[2] = ((Vector3)(ref val)).ToString("F2");
				obj2[3] = " centred ";
				val = ((Bounds)(ref bounds)).center;
				obj2[4] = ((Vector3)(ref val)).ToString("F2");
				obj2[5] = ", ladder of ";
				obj2[6] = num2.ToString();
				obj2[7] = " along ";
				obj2[8] = (flag ? "x" : "z");
				log2.LogInfo((object)string.Concat(obj2));
			}
			return true;
		}

		private static void AddRung(GameObject prefab, Bounds bounds, bool alongX, float reach, float y)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			float[] array = new float[2] { -1f, 1f };
			foreach (float num in array)
			{
				Vector3 localPosition = (alongX ? new Vector3(((Bounds)(ref bounds)).center.x + reach * num, y, ((Bounds)(ref bounds)).center.z) : new Vector3(((Bounds)(ref bounds)).center.x, y, ((Bounds)(ref bounds)).center.z + reach * num));
				string text = (alongX ? ((num < 0f) ? "left" : "right") : ((num < 0f) ? "back" : "front"));
				Create(prefab, "snap_" + text + "-y" + y.ToString("F2"), localPosition);
			}
		}

		private static void Create(GameObject prefab, string name, Vector3 localPosition)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				tag = "snappoint"
			};
			val.transform.SetParent(prefab.transform, false);
			val.transform.localPosition = localPosition;
		}

		private static bool Footprint(GameObject prefab, out Bounds bounds)
		{
			//IL_0001: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			bool flag = false;
			Transform transform = prefab.transform;
			Transform val = LiveGeometry(prefab);
			Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren)
			{
				if (!Skip(val2, transform) && LocalBounds(val2, out var local))
				{
					Bounds val3 = ToRoot(transform, ((Component)val2).transform, local);
					if (!flag)
					{
						bounds = val3;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val3);
					}
					if (SinkaConfig.Verbose.Value)
					{
						ManualLogSource log = SinkaPlugin.Log;
						string text = PathTo(((Component)val2).transform, transform);
						Vector3 size = ((Bounds)(ref val3)).size;
						log.LogInfo((object)("    " + text + " " + ((Vector3)(ref size)).ToString("F2")));
					}
				}
			}
			if (flag)
			{
				return true;
			}
			MeshFilter[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val4 in componentsInChildren2)
			{
				if (!((Object)(object)val4.sharedMesh == (Object)null) && !IsDisabled(((Component)val4).transform, transform))
				{
					Bounds val5 = ToRoot(transform, ((Component)val4).transform, val4.sharedMesh.bounds);
					if (!flag)
					{
						bounds = val5;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val5);
					}
				}
			}
			return flag;
		}

		private static Transform LiveGeometry(GameObject prefab)
		{
			WearNTear component = prefab.GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.m_new == (Object)null || (Object)(object)component.m_new == (Object)(object)prefab)
			{
				return prefab.transform;
			}
			return component.m_new.transform;
		}

		private static bool Skip(Collider collider, Transform root)
		{
			if (collider.isTrigger)
			{
				return true;
			}
			if (IsDisabled(((Component)collider).transform, root))
			{
				return true;
			}
			Rigidbody attachedRigidbody = collider.attachedRigidbody;
			if ((Object)(object)attachedRigidbody != (Object)null && (Object)(object)((Component)attachedRigidbody).transform != (Object)(object)root)
			{
				return true;
			}
			return false;
		}

		private static bool IsDisabled(Transform transform, Transform root)
		{
			Transform val = transform;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				if (!((Component)val).gameObject.activeSelf)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static string PathTo(Transform transform, Transform root)
		{
			string text = ((Object)transform).name;
			Transform parent = transform.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}

		private static bool LocalBounds(Collider collider, out Bounds local)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			BoxCollider val = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
			if (val == null)
			{
				MeshCollider val2 = (MeshCollider)(object)((collider is MeshCollider) ? collider : null);
				if (val2 == null)
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
					if (val3 != null)
					{
						float num = val3.radius * 2f;
						local = new Bounds(val3.center, new Vector3(num, Mathf.Max(val3.height, num), num));
						return true;
					}
					SphereCollider val4 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
					if (val4 != null)
					{
						local = new Bounds(val4.center, Vector3.one * val4.radius * 2f);
						return true;
					}
				}
				else if ((Object)(object)val2.sharedMesh != (Object)null)
				{
					local = val2.sharedMesh.bounds;
					return true;
				}
				local = default(Bounds);
				return false;
			}
			local = new Bounds(val.center, val.size);
			return true;
		}

		private static Bounds ToRoot(Transform root, Transform child, Bounds local)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_001a: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0074: 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_007c: 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)
			Vector3 center = ((Bounds)(ref local)).center;
			Vector3 extents = ((Bounds)(ref local)).extents;
			Bounds result = default(Bounds);
			((Bounds)(ref result))..ctor(root.InverseTransformPoint(child.TransformPoint(center)), Vector3.zero);
			for (int i = 0; i < 8; i++)
			{
				Vector3 val = center + new Vector3(((i & 1) == 0) ? (0f - extents.x) : extents.x, ((i & 2) == 0) ? (0f - extents.y) : extents.y, ((i & 4) == 0) ? (0f - extents.z) : extents.z);
				((Bounds)(ref result)).Encapsulate(root.InverseTransformPoint(child.TransformPoint(val)));
			}
			return result;
		}
	}
}