Decompiled source of WoodRamps v1.0.1

WoodRamps.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("WoodRamps")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("WoodRamps")]
[assembly: AssemblyTitle("WoodRamps")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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 WoodFloors2645
{
	internal static class ConfigManager
	{
		internal static ConfigEntry<float> DynamicFriction;

		internal static ConfigEntry<float> StaticFriction;

		internal static ConfigEntry<int> WoodCost;

		internal static ConfigEntry<bool> RequireWorkbench;

		internal static void Init(ConfigFile config)
		{
			DynamicFriction = config.Bind<float>("Physics", "DynamicFriction", 0.05f, "Dynamic friction on the angled wood ramps (lower = more slippery).");
			StaticFriction = config.Bind<float>("Physics", "StaticFriction", 0.05f, "Static friction on the angled wood ramps.");
			WoodCost = config.Bind<int>("Building", "WoodCost", 2, "Wood required to place each wood ramp piece (vanilla wood floor is 2).");
			RequireWorkbench = config.Bind<bool>("Building", "RequireWorkbench", true, "If true, pieces require a nearby Workbench like vanilla wood floors.");
		}
	}
	internal static class FloorPrefabBuilder
	{
		private sealed class PlankTemplate
		{
			internal Mesh Mesh;

			internal Material[] Materials;

			internal Quaternion Rotation;

			internal Vector3 Scale;

			internal float Y;

			internal int Layer;
		}

		internal const string VisualChildName = "FloorVisual";

		internal const string ColliderChildName = "FloorCollider";

		internal const float HalfSize = 1f;

		internal static GameObject CreateAngledFloor(RampVariant variant)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(variant.PieceName, "wood_floor");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Could not clone vanilla prefab 'wood_floor'.");
				return null;
			}
			((Object)val).name = variant.PieceName;
			AngleVanillaFloor(val, variant);
			FinalizePiece(val, variant.TokenName, variant.TokenDescription);
			val.transform.localScale = Vector3.one;
			return val;
		}

		private static void AngleVanillaFloor(GameObject prefab, RampVariant variant)
		{
			//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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			StripColliders(prefab);
			ClearSnapPointChildren(prefab);
			StripChildWearNTear(prefab);
			List<Transform> list = new List<Transform>();
			for (int i = 0; i < prefab.transform.childCount; i++)
			{
				Transform child = prefab.transform.GetChild(i);
				if ((Object)(object)child != (Object)null)
				{
					list.Add(child);
				}
			}
			float angleDegrees = variant.AngleDegrees;
			float slopeLength = variant.SlopeLength;
			Quaternion val = Quaternion.Euler(angleDegrees, 0f, 0f);
			GameObject val2 = new GameObject("FloorVisual");
			val2.transform.SetParent(prefab.transform, false);
			val2.transform.localPosition = Vector3.zero;
			val2.transform.localRotation = val;
			val2.transform.localScale = Vector3.one;
			foreach (Transform item in list)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)val2.transform))
				{
					item.SetParent(val2.transform, false);
				}
			}
			EnsureVanillaMaterialsIntact(val2);
			float deckTop = BuildPlankDeck(prefab, variant);
			string text = (variant.Rotated ? ", planks across slope" : string.Empty);
			Plugin.Log.LogInfo((object)$"Wood ramp {variant.Label}: pitch {angleDegrees:0.##}° (−Z high), slope {slopeLength:0.###} m{text}.");
			LogMaterialSummary(val2, angleDegrees, variant.Rotated);
			BuildAngledCollider(prefab, val, slopeLength, deckTop);
			BuildSnapPoints(prefab, val, slopeLength, angleDegrees);
			WearNTearHelper.ConfigureAndRefresh(prefab);
			PhysicsMaterialHelper.ApplyToObject(prefab);
		}

		private static float BuildPlankDeck(GameObject prefab, RampVariant variant)
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_043e: 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_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_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: 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_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: 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_0302: 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_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: 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_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: 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_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: 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_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			WearNTear component = prefab.GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.m_new == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Plank deck skipped: wood_floor clone has no WearNTear/New group.");
				return 0.081f;
			}
			PlankTemplate plankTemplate = FindPlankTemplate(component.m_new);
			if (plankTemplate == null)
			{
				Plugin.Log.LogWarning((object)"Plank deck skipped: no plank mesh found in wood_floor New group.");
				return 0.081f;
			}
			float slopeLength = variant.SlopeLength;
			float num = 2f;
			float result = 0.081f;
			bool flag = false;
			List<GameObject> list = new List<GameObject>();
			GameObject[] array = (GameObject[])(object)new GameObject[3] { component.m_new, component.m_worn, component.m_broken };
			foreach (GameObject val in array)
			{
				if ((Object)(object)val != (Object)null && !list.Contains(val))
				{
					list.Add(val);
				}
			}
			foreach (GameObject item in list)
			{
				PlankTemplate plankTemplate2 = FindPlankTemplate(item) ?? plankTemplate;
				for (int num2 = item.transform.childCount - 1; num2 >= 0; num2--)
				{
					Object.DestroyImmediate((Object)(object)((Component)item.transform.GetChild(num2)).gameObject);
				}
				Quaternion val2 = (variant.Rotated ? (Quaternion.Euler(0f, 90f, 0f) * plankTemplate2.Rotation) : plankTemplate2.Rotation);
				int num3;
				float num4;
				float sizeX;
				float sizeZ;
				if (variant.Rotated)
				{
					num3 = Mathf.Max(1, Mathf.RoundToInt(slopeLength / 0.5f));
					num4 = slopeLength / (float)num3;
					sizeX = num;
					sizeZ = num4;
				}
				else
				{
					num3 = Mathf.Max(1, Mathf.RoundToInt(num / 0.5f));
					num4 = num / (float)num3;
					sizeX = num4;
					sizeZ = slopeLength;
				}
				Vector3 val3 = FitScale(plankTemplate2, val2, sizeX, sizeZ);
				Bounds bounds = plankTemplate2.Mesh.bounds;
				Vector3 val4 = val2 * Vector3.Scale(((Bounds)(ref bounds)).center, val3);
				for (int j = 0; j < num3; j++)
				{
					float num5 = -0.5f * (variant.Rotated ? slopeLength : num) + num4 * ((float)j + 0.5f);
					Vector3 val5 = (variant.Rotated ? new Vector3(0f, plankTemplate2.Y, num5) : new Vector3(num5, plankTemplate2.Y, 0f));
					GameObject val6 = new GameObject($"plank_{j}")
					{
						layer = plankTemplate2.Layer
					};
					val6.transform.SetParent(item.transform, false);
					val6.transform.localPosition = val5 - new Vector3(val4.x, 0f, val4.z);
					val6.transform.localRotation = val2;
					val6.transform.localScale = val3;
					val6.AddComponent<MeshFilter>().sharedMesh = plankTemplate2.Mesh;
					((Renderer)val6.AddComponent<MeshRenderer>()).sharedMaterials = plankTemplate2.Materials;
				}
				if (!flag)
				{
					Bounds bounds2 = plankTemplate2.Mesh.bounds;
					Vector3 val7 = Vector3.Scale(((Bounds)(ref bounds2)).extents, val3);
					float num6 = Mathf.Abs((val2 * new Vector3(val7.x, 0f, 0f)).y) + Mathf.Abs((val2 * new Vector3(0f, val7.y, 0f)).y) + Mathf.Abs((val2 * new Vector3(0f, 0f, val7.z)).y);
					result = plankTemplate2.Y + val4.y + num6;
					flag = true;
				}
			}
			LODGroup component2 = prefab.GetComponent<LODGroup>();
			if ((Object)(object)component2 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component2);
			}
			HashSet<MeshRenderer> hashSet = new HashSet<MeshRenderer>();
			MeshRenderer[] array2 = (MeshRenderer[])(object)new MeshRenderer[3] { component.m_snow, component.m_snowWorn, component.m_snowBroken };
			foreach (MeshRenderer val8 in array2)
			{
				if ((Object)(object)val8 != (Object)null && hashSet.Add(val8))
				{
					Vector3 localScale = ((Component)val8).transform.localScale;
					localScale.z *= slopeLength / num;
					((Component)val8).transform.localScale = localScale;
				}
			}
			return result;
		}

		private static Vector3 FitScale(PlankTemplate template, Quaternion rotation, float sizeX, float sizeZ)
		{
			//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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0024: 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_0034: 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_0051: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = template.Mesh.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			Vector3 scale = template.Scale;
			for (int i = 0; i < 3; i++)
			{
				Vector3 zero = Vector3.zero;
				((Vector3)(ref zero))[i] = 1f;
				Vector3 val = rotation * zero;
				float num = Mathf.Max(((Vector3)(ref size))[i], 0.0001f);
				if (Mathf.Abs(val.x) > 0.5f)
				{
					((Vector3)(ref scale))[i] = sizeX / num;
				}
				else if (Mathf.Abs(val.z) > 0.5f)
				{
					((Vector3)(ref scale))[i] = sizeZ / num;
				}
			}
			return scale;
		}

		private static PlankTemplate FindPlankTemplate(GameObject group)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_00ea: 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_00fa: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)group == (Object)null)
			{
				return null;
			}
			MeshRenderer[] componentsInChildren = group.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null || !((Object)val).name.StartsWith("plank", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Vector3 localScale = ((Component)val).transform.localScale;
				if (!(Mathf.Abs(localScale.x - 0.1f) > 0.03f) && !(Mathf.Abs(localScale.y - 0.5f) > 0.1f) && !(Mathf.Abs(localScale.z - 2f) > 0.2f))
				{
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null))
					{
						Transform transform = group.transform;
						return new PlankTemplate
						{
							Mesh = component.sharedMesh,
							Materials = ((Renderer)val).sharedMaterials,
							Rotation = Quaternion.Inverse(transform.rotation) * ((Component)val).transform.rotation,
							Scale = localScale,
							Y = transform.InverseTransformPoint(((Component)val).transform.position).y,
							Layer = ((Component)val).gameObject.layer
						};
					}
				}
			}
			return null;
		}

		private static void StripColliders(GameObject prefab)
		{
			Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
		}

		private static void StripChildWearNTear(GameObject prefab)
		{
			WearNTear[] componentsInChildren = prefab.GetComponentsInChildren<WearNTear>(true);
			foreach (WearNTear val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)prefab)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}

		private static void ClearSnapPointChildren(GameObject prefab)
		{
			List<GameObject> list = new List<GameObject>();
			for (int i = 0; i < prefab.transform.childCount; i++)
			{
				Transform child = prefab.transform.GetChild(i);
				if (!((Object)(object)child == (Object)null) && (((Object)child).name.StartsWith("snap_", StringComparison.Ordinal) || ((Object)child).name.StartsWith("$hud_snappoint", StringComparison.Ordinal) || ((Component)child).CompareTag("snappoint")))
				{
					list.Add(((Component)child).gameObject);
				}
			}
			Transform[] componentsInChildren = prefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)prefab.transform)
				{
					continue;
				}
				try
				{
					if (((Component)val).CompareTag("snappoint"))
					{
						list.Add(((Component)val).gameObject);
					}
				}
				catch
				{
				}
			}
			foreach (GameObject item in list)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)item);
				}
			}
		}

		private static void EnsureVanillaMaterialsIntact(GameObject visualRoot)
		{
			if ((Object)(object)visualRoot == (Object)null)
			{
				return;
			}
			MeshRenderer[] componentsInChildren = visualRoot.GetComponentsInChildren<MeshRenderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				Plugin.Log.LogWarning((object)"Angled floor has no MeshRenderers after clone.");
				return;
			}
			bool flag = false;
			MeshRenderer[] array = componentsInChildren;
			foreach (MeshRenderer val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
					if (sharedMaterials == null || sharedMaterials.Length == 0 || HasNullMaterial(sharedMaterials))
					{
						flag = true;
						break;
					}
				}
			}
			if (!flag)
			{
				return;
			}
			Material[][] array2 = CaptureVanillaWoodFloorMaterials();
			if (array2 == null || array2.Length == 0)
			{
				Plugin.Log.LogWarning((object)"Could not capture wood_floor materials from a live probe; leaving clone materials as-is.");
				return;
			}
			int num = Mathf.Min(componentsInChildren.Length, array2.Length);
			for (int j = 0; j < num; j++)
			{
				if ((Object)(object)componentsInChildren[j] != (Object)null && array2[j] != null && array2[j].Length != 0)
				{
					((Renderer)componentsInChildren[j]).sharedMaterials = array2[j];
					((Renderer)componentsInChildren[j]).SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
			Plugin.Log.LogInfo((object)$"Copied wood_floor materials onto {num} MeshRenderer(s).");
		}

		private static bool HasNullMaterial(Material[] mats)
		{
			for (int i = 0; i < mats.Length; i++)
			{
				if ((Object)(object)mats[i] == (Object)null)
				{
					return true;
				}
			}
			return false;
		}

		private static Material[][] CaptureVanillaWoodFloorMaterials()
		{
			GameObject val = null;
			try
			{
				val = ((PrefabManager.Instance != null) ? PrefabManager.Instance.GetPrefab("wood_floor") : null);
			}
			catch
			{
				val = null;
			}
			if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				val = ZNetScene.instance.GetPrefab("wood_floor");
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Material[][] array = CollectRendererMaterials(val, requireNonNull: true);
			if (array != null && array.Length != 0)
			{
				return array;
			}
			GameObject val2 = null;
			try
			{
				val2 = Object.Instantiate<GameObject>(val);
				((Object)val2).name = "WoodRamps_MatProbe_wood_floor";
				((Object)val2).hideFlags = (HideFlags)61;
				val2.SetActive(true);
				ZNetView component = val2.GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					((Behaviour)component).enabled = false;
				}
				return CollectRendererMaterials(val2, requireNonNull: true);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("wood_floor material probe failed: " + ex.Message));
				return null;
			}
			finally
			{
				if ((Object)(object)val2 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
		}

		private static Material[][] CollectRendererMaterials(GameObject root, bool requireNonNull)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			WearNTear component = root.GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.m_new != (Object)null)
			{
				Material[][] array = CollectRendererMaterialsOn(component.m_new, requireNonNull);
				if (array != null && array.Length != 0)
				{
					return array;
				}
			}
			return CollectRendererMaterialsOn(root, requireNonNull);
		}

		private static Material[][] CollectRendererMaterialsOn(GameObject root, bool requireNonNull)
		{
			List<Material[]> list = new List<Material[]>();
			MeshRenderer[] componentsInChildren = root.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string text = ((Object)((Component)val).transform).name ?? string.Empty;
				if (text.IndexOf("worn", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("broken", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					continue;
				}
				Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
				if (sharedMaterials != null && sharedMaterials.Length != 0 && (!requireNonNull || !HasNullMaterial(sharedMaterials)))
				{
					Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
					for (int j = 0; j < sharedMaterials.Length; j++)
					{
						array[j] = sharedMaterials[j];
					}
					list.Add(array);
				}
			}
			if (list.Count <= 0)
			{
				return null;
			}
			return list.ToArray();
		}

		private static void LogMaterialSummary(GameObject visualRoot, float angleDegrees, bool rotateTexture90)
		{
			if ((Object)(object)visualRoot == (Object)null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			string text = "(none)";
			string text2 = "(none)";
			MeshRenderer[] componentsInChildren = visualRoot.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				num++;
				Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				Material[] array = sharedMaterials;
				foreach (Material val2 in array)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						num2++;
						if (text == "(none)")
						{
							text = ((Object)val2).name;
							text2 = (((Object)(object)val2.shader != (Object)null) ? ((Object)val2.shader).name : "(null shader)");
						}
					}
				}
			}
			string text3 = (rotateTexture90 ? ", plank yaw 90°" : string.Empty);
			Plugin.Log.LogInfo((object)($"Wood floor {angleDegrees}° visuals{text3}: {num} MeshRenderer(s), {num2} material(s), " + "first='" + text + "' shader='" + text2 + "'."));
		}

		private static void BuildAngledCollider(GameObject prefab, Quaternion pitch, float slopeLength, float deckTop)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			float num = 0.06f;
			GameObject val = new GameObject("FloorCollider");
			val.transform.SetParent(prefab.transform, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = pitch;
			val.transform.localScale = Vector3.one;
			val.layer = prefab.layer;
			try
			{
				val.tag = "leaky";
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to tag FloorCollider as leaky: " + ex.Message));
			}
			BoxCollider obj = val.AddComponent<BoxCollider>();
			obj.size = new Vector3(2f, 0.12f, slopeLength);
			obj.center = new Vector3(0f, deckTop - num, 0f);
		}

		private static void BuildSnapPoints(GameObject prefab, Quaternion pitch, float slopeLength, float angleDegrees)
		{
			//IL_000a: 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_0035: 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_006b: 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)
			float num = 1f;
			float num2 = slopeLength * 0.5f;
			int snapCount = 0;
			AddSnap("$hud_snappoint_top 1", SnapRole.End.High, SnapRole.Lateral.L, new Vector3(num, 0f, 0f - num2));
			AddSnap("$hud_snappoint_top 2", SnapRole.End.High, SnapRole.Lateral.R, new Vector3(0f - num, 0f, 0f - num2));
			AddSnap("$hud_snappoint_bottom 1", SnapRole.End.Low, SnapRole.Lateral.L, new Vector3(num, 0f, num2));
			AddSnap("$hud_snappoint_bottom 2", SnapRole.End.Low, SnapRole.Lateral.R, new Vector3(0f - num, 0f, num2));
			Plugin.Log.LogInfo((object)$"Built {snapCount} corner snappoints for {angleDegrees:0.##}° ramp (thatch HUD names).");
			void AddSnap(string hudName, SnapRole.End end, SnapRole.Lateral lateral, Vector3 unrotatedLocal)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Expected O, but got Unknown
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				GameObject val = new GameObject(hudName);
				val.transform.SetParent(prefab.transform, false);
				val.transform.localPosition = pitch * unrotatedLocal;
				val.transform.localRotation = Quaternion.identity;
				val.transform.localScale = Vector3.one;
				SnapRole snapRole = val.AddComponent<SnapRole>();
				snapRole.SlopeEnd = end;
				snapRole.Side = lateral;
				try
				{
					val.tag = "snappoint";
					snapCount++;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Failed to tag snappoint: " + ex.Message));
				}
			}
		}

		private static void FinalizePiece(GameObject prefab, string tokenName, string tokenDescription)
		{
			Piece component = prefab.GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_name = tokenName;
				component.m_description = tokenDescription;
				component.m_groundPiece = false;
				component.m_groundOnly = false;
				component.m_cultivatedGroundOnly = false;
				component.m_vegetationGroundOnly = false;
				component.m_waterPiece = false;
				component.m_noInWater = false;
				component.m_notOnTiltingSurface = false;
				component.m_notOnFloor = false;
				component.m_notOnWood = false;
				component.m_allowAltGroundPlacement = true;
				component.m_canBeRemoved = true;
				component.m_clipGround = false;
				component.m_clipEverything = false;
				object craftingStation;
				if (!ConfigManager.RequireWorkbench.Value)
				{
					craftingStation = null;
				}
				else
				{
					PrefabManager instance = PrefabManager.Instance;
					if (instance == null)
					{
						craftingStation = null;
					}
					else
					{
						GameObject prefab2 = instance.GetPrefab("piece_workbench");
						craftingStation = ((prefab2 != null) ? prefab2.GetComponent<CraftingStation>() : null);
					}
				}
				component.m_craftingStation = (CraftingStation)craftingStation;
			}
			WearNTearHelper.ConfigureAndRefresh(prefab);
			ZNetView component2 = prefab.GetComponent<ZNetView>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_persistent = true;
			}
		}
	}
	internal static class IconHelper
	{
		private static Sprite _fallbackWoodFloorIcon;

		private static bool _fallbackResolved;

		private static readonly object RenderLock = new object();

		private static readonly Dictionary<int, Sprite> IconsByLabel = new Dictionary<int, Sprite>();

		internal static Sprite CreateAngledPieceIcon(GameObject prefab, RampVariant variant, string nameHint)
		{
			float angleDegrees = variant.AngleDegrees;
			bool rotated = variant.Rotated;
			try
			{
				Sprite val = TryLoadPngOverride(variant.Label, rotated, nameHint);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("PNG icon load failed for '" + nameHint + "': " + ex.Message));
			}
			if ((Object)(object)prefab == (Object)null)
			{
				return GetFallbackWoodFloorIcon();
			}
			lock (RenderLock)
			{
				int label = variant.Label;
				if (rotated && IconsByLabel.TryGetValue(label, out var value) && (Object)(object)value != (Object)null)
				{
					Plugin.Log.LogInfo((object)($"Icon '{nameHint}': reuse {label}° sprite (rot90 icon matches " + "non-rot; world grain yaw unchanged)."));
					return value;
				}
				Sprite val2 = RenderTopDeckIcon(prefab, angleDegrees, nameHint);
				if ((Object)(object)val2 != (Object)null && !IconsByLabel.ContainsKey(label))
				{
					IconsByLabel[label] = val2;
				}
				return val2;
			}
		}

		private static Sprite RenderTopDeckIcon(GameObject prefab, float angleDegrees, string nameHint)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: 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_0100: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			GameObject val = null;
			try
			{
				val = BuildIconOnlyClone(prefab);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Icon clone failed for '" + nameHint + "'; using fallback."));
					return GetFallbackWoodFloorIcon();
				}
				Transform val2 = val.transform.Find("FloorVisual");
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Icon visual missing for '" + nameHint + "'; using fallback."));
					return GetFallbackWoodFloorIcon();
				}
				val2.localPosition = Vector3.zero;
				val2.localRotation = Quaternion.Euler(0f - angleDegrees, 0f, 0f);
				Plugin.Log.LogInfo((object)($"Icon '{nameHint}': capture visual pitch={angleDegrees:0.##}° " + "(icon-only; world prefab untouched)."));
				Quaternion rotation = RenderManager.IsometricRotation * Quaternion.Euler(angleDegrees * 0.5f, 0f, 0f) * Quaternion.Euler(0f, 180f, 0f);
				RenderRequest val3 = new RenderRequest(val)
				{
					Rotation = rotation,
					Width = 512,
					Height = 512,
					DistanceMultiplier = 1f,
					FieldOfView = 0.5f,
					ParticleSimulationTime = -1f,
					UseCache = false
				};
				Sprite val4 = RenderManager.Instance.Render(val3);
				if ((Object)(object)val4 == (Object)null)
				{
					Plugin.Log.LogWarning((object)("RenderManager null for '" + nameHint + "'; using fallback."));
					return GetFallbackWoodFloorIcon();
				}
				if (!string.IsNullOrEmpty(nameHint))
				{
					((Object)val4).name = nameHint;
				}
				Plugin.Log.LogInfo((object)("Rendered inclined top-deck icon '" + nameHint + "' " + $"(pitch={angleDegrees:0.##}°, " + $"cam=Iso*Euler({angleDegrees * 0.5f:0.#},0,0)*Y180, " + $"dist×={1f}, {512}px)."));
				return val4;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Icon render failed for '" + nameHint + "': " + ex.Message));
				return GetFallbackWoodFloorIcon();
			}
			finally
			{
				if ((Object)(object)val != (Object)null)
				{
					try
					{
						Object.DestroyImmediate((Object)(object)val);
					}
					catch
					{
					}
				}
			}
		}

		private static Sprite TryLoadPngOverride(int label, bool rotateTexture90, string nameHint)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_00b5: 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)
			string text = BuildPngFileName(label, rotateTexture90);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			string iconsDirectory = GetIconsDirectory();
			if (string.IsNullOrEmpty(iconsDirectory))
			{
				return null;
			}
			string text2 = Path.Combine(iconsDirectory, text);
			if (!File.Exists(text2))
			{
				return null;
			}
			byte[] array = File.ReadAllBytes(text2);
			if (array == null || array.Length == 0)
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!TryLoadPngBytes(val, array))
			{
				Object.Destroy((Object)(object)val);
				Plugin.Log.LogWarning((object)("PNG override unreadable: " + text2));
				return null;
			}
			((Object)val).name = nameHint ?? Path.GetFileNameWithoutExtension(text);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = ((Object)val).name;
			Plugin.Log.LogInfo((object)$"Loaded custom icon PNG '{text2}' ({((Texture)val).width}x{((Texture)val).height}).");
			return obj;
		}

		private static bool TryLoadPngBytes(Texture2D texture, byte[] pngBytes)
		{
			try
			{
				Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule");
				if (type != null)
				{
					MethodInfo method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
					{
						typeof(Texture2D),
						typeof(byte[])
					}, null);
					if (method != null)
					{
						return (bool)method.Invoke(null, new object[2] { texture, pngBytes });
					}
				}
				MethodInfo method2 = typeof(Texture2D).GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(byte[]) }, null);
				if (method2 != null)
				{
					return (bool)method2.Invoke(texture, new object[1] { pngBytes });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("PNG decode failed: " + ex.Message));
			}
			return false;
		}

		private static string BuildPngFileName(int label, bool rotateTexture90)
		{
			string text = "wood_floor_" + label;
			if (!rotateTexture90)
			{
				return text + ".png";
			}
			return text + "_rot90.png";
		}

		private static string GetIconsDirectory()
		{
			try
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					string location = ((BaseUnityPlugin)Plugin.Instance).Info.Location;
					if (!string.IsNullOrEmpty(location))
					{
						return Path.Combine(Path.GetDirectoryName(location) ?? string.Empty, "icons");
					}
				}
			}
			catch
			{
			}
			try
			{
				string pluginPath = Paths.PluginPath;
				if (!string.IsNullOrEmpty(pluginPath))
				{
					return Path.Combine(pluginPath, "ModdedWolf-WoodRamps", "icons");
				}
			}
			catch
			{
			}
			return null;
		}

		private static GameObject BuildIconOnlyClone(GameObject prefab)
		{
			//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_005a: Unknown result type (might be due to invalid IL or missing references)
			GameObject clone = Object.Instantiate<GameObject>(prefab);
			((Object)clone).name = ((Object)prefab).name + "_IconOnly";
			((Object)clone).hideFlags = (HideFlags)61;
			clone.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
			clone.transform.localScale = Vector3.one;
			List<GameObject> wearOverlays = new List<GameObject>();
			WearNTear component = clone.GetComponent<WearNTear>();
			GameObject healthy;
			if ((Object)(object)component != (Object)null)
			{
				healthy = component.m_new;
				AddOverlay(((Object)(object)component.m_snow != (Object)null) ? ((Component)component.m_snow).gameObject : null);
				AddOverlay(((Object)(object)component.m_snowWorn != (Object)null) ? ((Component)component.m_snowWorn).gameObject : null);
				AddOverlay(((Object)(object)component.m_snowBroken != (Object)null) ? ((Component)component.m_snowBroken).gameObject : null);
				AddOverlay(component.m_worn);
				AddOverlay(component.m_broken);
				AddOverlay(component.m_wet);
			}
			foreach (GameObject item in wearOverlays)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)item);
				}
			}
			Component[] componentsInChildren = clone.GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is MeshFilter) && !(val is MeshRenderer) && !(val is SkinnedMeshRenderer))
				{
					try
					{
						Object.DestroyImmediate((Object)(object)val);
					}
					catch
					{
					}
				}
			}
			Transform val2 = clone.transform.Find("FloorVisual");
			if ((Object)(object)val2 == (Object)null)
			{
				Object.DestroyImmediate((Object)(object)clone);
				return null;
			}
			List<GameObject> list = new List<GameObject>();
			for (int j = 0; j < clone.transform.childCount; j++)
			{
				Transform child = clone.transform.GetChild(j);
				if ((Object)(object)child != (Object)null && (Object)(object)child != (Object)(object)val2)
				{
					list.Add(((Component)child).gameObject);
				}
			}
			Transform[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<Transform>(true);
			foreach (Transform val3 in componentsInChildren2)
			{
				if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3 == (Object)(object)val2))
				{
					string text = ((Object)val3).name ?? string.Empty;
					if (text.IndexOf("worn", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("broken", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("snow", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("damage", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("lod1", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("lod2", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("lod3", StringComparison.OrdinalIgnoreCase) >= 0 || text.Equals("wet", StringComparison.OrdinalIgnoreCase) || text.StartsWith("wet", StringComparison.OrdinalIgnoreCase))
					{
						list.Add(((Component)val3).gameObject);
					}
				}
			}
			foreach (GameObject item2 in list)
			{
				if ((Object)(object)item2 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)item2);
				}
			}
			MeshRenderer[] componentsInChildren3 = ((Component)val2).GetComponentsInChildren<MeshRenderer>(true);
			if (componentsInChildren3 == null || componentsInChildren3.Length == 0)
			{
				Object.DestroyImmediate((Object)(object)clone);
				return null;
			}
			MeshRenderer[] array = componentsInChildren3;
			foreach (MeshRenderer val4 in array)
			{
				if ((Object)(object)val4 != (Object)null)
				{
					((Renderer)val4).enabled = true;
					((Component)val4).gameObject.SetActive(true);
				}
			}
			clone.SetActive(true);
			return clone;
			void AddOverlay(GameObject go)
			{
				if (!((Object)(object)go == (Object)null) && !((Object)(object)go == (Object)(object)clone) && !((Object)(object)go == (Object)(object)healthy) && (!((Object)(object)healthy != (Object)null) || !healthy.transform.IsChildOf(go.transform)) && !(((Object)go).name == "FloorVisual"))
				{
					wearOverlays.Add(go);
				}
			}
		}

		private static Sprite GetFallbackWoodFloorIcon()
		{
			if (_fallbackResolved)
			{
				return _fallbackWoodFloorIcon;
			}
			_fallbackResolved = true;
			try
			{
				GameObject val = ((PrefabManager.Instance != null) ? PrefabManager.Instance.GetPrefab("wood_floor") : null);
				if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
				{
					val = ZNetScene.instance.GetPrefab("wood_floor");
				}
				Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
				_fallbackWoodFloorIcon = (((Object)(object)val2 != (Object)null) ? val2.m_icon : null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Fallback wood_floor icon failed: " + ex.Message));
				_fallbackWoodFloorIcon = null;
			}
			return _fallbackWoodFloorIcon;
		}
	}
	internal static class PhysicsMaterialHelper
	{
		private static PhysicsMaterial _sledMaterial;

		internal static PhysicsMaterial GetOrCreate()
		{
			//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_002d: 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_0048: 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_005b: Expected O, but got Unknown
			if ((Object)(object)_sledMaterial != (Object)null)
			{
				return _sledMaterial;
			}
			_sledMaterial = new PhysicsMaterial("WoodRamps_LowFriction")
			{
				dynamicFriction = ConfigManager.DynamicFriction.Value,
				staticFriction = ConfigManager.StaticFriction.Value,
				bounciness = 0f,
				frictionCombine = (PhysicsMaterialCombine)2,
				bounceCombine = (PhysicsMaterialCombine)2
			};
			return _sledMaterial;
		}

		internal static void ApplyToObject(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			PhysicsMaterial orCreate = GetOrCreate();
			Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !val.isTrigger)
				{
					val.material = orCreate;
				}
			}
		}
	}
	internal static class PieceRegistration
	{
		internal static readonly RampVariant[] Variants = new RampVariant[6]
		{
			Make(26, 1f, rotated: false, "Wood Ramp 26°", "A wood ramp that matches vanilla 26° beams and thatch roofs (2 m across, 1 m rise)."),
			Make(45, 2f, rotated: false, "Wood Ramp 45°", "A wood ramp that matches vanilla 45° beams and thatch roofs (2 m across, 2 m rise)."),
			Make(67, 4f, rotated: false, "Wood Ramp 67°", "A wood ramp that matches vanilla 67° beams and thatch roofs (2 m across, 4 m rise)."),
			Make(26, 1f, rotated: true, "Wood Ramp 26° (Rotated)", "Same slope as Wood Ramp 26°, with plank grain rotated 90°."),
			Make(45, 2f, rotated: true, "Wood Ramp 45° (Rotated)", "Same slope as Wood Ramp 45°, with plank grain rotated 90°."),
			Make(67, 4f, rotated: true, "Wood Ramp 67° (Rotated)", "Same slope as Wood Ramp 67°, with plank grain rotated 90°.")
		};

		private static readonly HashSet<string> PieceNames = BuildPieceNames();

		private static bool _registered;

		private static RampVariant Make(int label, float rise, bool rotated, string displayName, string description)
		{
			string arg = (rotated ? "_rot90" : string.Empty);
			return new RampVariant
			{
				Label = label,
				Rise = rise,
				Rotated = rotated,
				PieceName = $"piece_wood_floor_{label}{arg}",
				TokenName = $"$woodramps_ramp{label}{arg}_name",
				TokenDescription = $"$woodramps_ramp{label}{arg}_description",
				DisplayName = displayName,
				Description = description
			};
		}

		private static HashSet<string> BuildPieceNames()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			RampVariant[] variants = Variants;
			foreach (RampVariant rampVariant in variants)
			{
				hashSet.Add(rampVariant.PieceName);
			}
			return hashSet;
		}

		internal static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			RampVariant[] variants = Variants;
			foreach (RampVariant rampVariant in variants)
			{
				string text = "English";
				localization.AddTranslation(ref text, ref rampVariant.TokenName, rampVariant.DisplayName);
				text = "English";
				localization.AddTranslation(ref text, ref rampVariant.TokenDescription, rampVariant.Description);
			}
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable;
		}

		internal static void Unregister()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
		}

		private static void OnVanillaPrefabsAvailable()
		{
			if (_registered)
			{
				return;
			}
			try
			{
				bool flag = true;
				RampVariant[] variants = Variants;
				foreach (RampVariant variant in variants)
				{
					flag &= RegisterFloor(variant);
				}
				if (!flag)
				{
					Plugin.Log.LogError((object)"One or more wood ramp pieces failed to register.");
					return;
				}
				_registered = true;
				PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
				Plugin.Log.LogInfo((object)$"Registered {Variants.Length} wood ramp pieces (26°, 45°, 67° + rotated).");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to register wood ramps: {arg}");
			}
		}

		private static bool RegisterFloor(RampVariant variant)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			string pieceName = variant.PieceName;
			GameObject val = FloorPrefabBuilder.CreateAngledFloor(variant);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)("Failed to create prefab '" + pieceName + "'."));
				return false;
			}
			Sprite val2 = null;
			try
			{
				val2 = IconHelper.CreateAngledPieceIcon(val, variant, pieceName + "_icon");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Icon resolve failed for '" + pieceName + "': " + ex.Message));
				val2 = null;
			}
			PieceConfig val3 = new PieceConfig
			{
				Name = variant.TokenName,
				Description = variant.TokenDescription,
				PieceTable = PieceTables.Hammer,
				Category = PieceCategories.Building,
				AllowedInDungeons = false,
				Icon = val2
			};
			if (ConfigManager.RequireWorkbench.Value)
			{
				val3.CraftingStation = CraftingStations.Workbench;
			}
			val3.AddRequirement(new RequirementConfig("Wood", Mathf.Max(1, ConfigManager.WoodCost.Value), 0, true));
			((Object)val).name = pieceName;
			CustomPiece val4 = new CustomPiece(val, false, val3);
			if (!PieceManager.Instance.AddPiece(val4))
			{
				Plugin.Log.LogError((object)("PieceManager rejected " + pieceName + "."));
				return false;
			}
			try
			{
				Piece component = val.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null && (Object)(object)val2 != (Object)null)
				{
					component.m_icon = val2;
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("Failed to assign m_icon on '" + pieceName + "': " + ex2.Message));
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode(pieceName);
			string arg = (variant.Rotated ? ", plank yaw 90°" : string.Empty);
			Plugin.Log.LogInfo((object)($"Registered build piece '{pieceName}' at {variant.AngleDegrees:0.##}° " + $"(rise {variant.Rise} m{arg}, hash {stableHashCode})."));
			return true;
		}

		internal static bool IsAngledWoodFloor(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return false;
			}
			string item = ((Object)go).name.Replace("(Clone)", string.Empty).Trim();
			return PieceNames.Contains(item);
		}

		internal static bool IsAngledWoodFloor(Piece piece)
		{
			if ((Object)(object)piece != (Object)null)
			{
				return IsAngledWoodFloor(((Component)piece).gameObject);
			}
			return false;
		}
	}
	[BepInPlugin("ModdedWolf.WoodRamps", "Wood Ramps", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "ModdedWolf.WoodRamps";

		public const string NAME = "Wood Ramps";

		public const string VERSION = "1.0.1";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigManager.Init(((BaseUnityPlugin)this).Config);
			PieceRegistration.Register();
			_harmony = new Harmony("ModdedWolf.WoodRamps");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Wood Ramps v1.0.1 loaded.");
		}

		private void OnDestroy()
		{
			PieceRegistration.Unregister();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
		}
	}
	internal sealed class RampVariant
	{
		internal const float Run = 2f;

		internal const float PlankWidth = 0.5f;

		internal int Label;

		internal float Rise;

		internal bool Rotated;

		internal string PieceName;

		internal string TokenName;

		internal string TokenDescription;

		internal string DisplayName;

		internal string Description;

		internal float AngleDegrees => Mathf.Atan2(Rise, 2f) * 57.29578f;

		internal float SlopeLength => Mathf.Sqrt(4f + Rise * Rise);
	}
	internal sealed class SnapRole : MonoBehaviour
	{
		internal enum End
		{
			Low,
			High,
			Side,
			Mid
		}

		internal enum Lateral
		{
			L,
			C,
			R,
			None
		}

		internal End SlopeEnd;

		internal Lateral Side;

		internal static SnapRole Get(Transform t)
		{
			if (!((Object)(object)t != (Object)null))
			{
				return null;
			}
			return ((Component)t).GetComponent<SnapRole>();
		}
	}
	internal static class WearNTearHelper
	{
		private static readonly FieldInfo CollidersField = typeof(WearNTear).GetField("m_colliders", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo BoundsField = typeof(WearNTear).GetField("m_bounds", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo RenderersField = typeof(WearNTear).GetField("m_renderers", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool _copiedWoodDefaults;

		private static float _woodHealth = 400f;

		private static MaterialType _woodMaterial = (MaterialType)0;

		private static bool _woodSupports = true;

		private static bool _woodNoRoofWear = true;

		private static bool _woodNoSupportWear = true;

		private static bool _woodAshDamageImmune = false;

		private static bool _woodBurnable = true;

		private static DamageModifiers _woodDamages;

		private static bool _copiedDamages;

		private static EffectList _woodDestroyedEffect;

		private static EffectList _woodHitEffect;

		private static EffectList _woodSwitchEffect;

		private static bool _copiedEffects;

		internal static void ConfigureAndRefresh(GameObject root)
		{
			//IL_005e: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			WearNTear[] componentsInChildren = root.GetComponentsInChildren<WearNTear>(true);
			foreach (WearNTear val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)root)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
			WearNTear component = root.GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null)
			{
				ClearPropertyBlocks(root);
				return;
			}
			EnsureWoodDefaultsCached();
			component.m_materialType = _woodMaterial;
			component.m_health = ((_woodHealth > 0f) ? _woodHealth : 400f);
			component.m_supports = _woodSupports;
			component.m_noRoofWear = true;
			component.m_noSupportWear = _woodNoSupportWear;
			component.m_ashDamageImmune = _woodAshDamageImmune;
			component.m_burnable = _woodBurnable;
			component.m_forceCorrectCOMCalculation = true;
			component.m_autoCreateFragments = true;
			if (_copiedDamages)
			{
				component.m_damages = _woodDamages;
			}
			if (_copiedEffects)
			{
				if (_woodDestroyedEffect != null)
				{
					component.m_destroyedEffect = _woodDestroyedEffect;
				}
				if (_woodHitEffect != null)
				{
					component.m_hitEffect = _woodHitEffect;
				}
				if (_woodSwitchEffect != null)
				{
					component.m_switchEffect = _woodSwitchEffect;
				}
			}
			component.m_fragmentRoots = null;
			if (!IsAlive(component.m_wet))
			{
				Transform val2 = FindChildRecursive(root.transform, "wet");
				component.m_wet = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null);
			}
			if (!IsAlive(component.m_new) || !IsAlive(component.m_worn) || !IsAlive(component.m_broken))
			{
				Transform val3 = root.transform.Find("FloorVisual");
				GameObject val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : null);
				if (!IsAlive(component.m_new))
				{
					component.m_new = val4;
				}
				if (!IsAlive(component.m_worn))
				{
					component.m_worn = val4;
				}
				if (!IsAlive(component.m_broken))
				{
					component.m_broken = val4;
				}
			}
			if (RenderersField != null && RenderersField.GetValue(component) is IList list)
			{
				list.Clear();
				Transform val5 = root.transform.Find("FloorVisual");
				Renderer[] componentsInChildren2 = ((Component)(((Object)(object)val5 != (Object)null) ? val5 : root.transform)).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val6 in componentsInChildren2)
				{
					if ((Object)(object)val6 != (Object)null)
					{
						list.Add(val6);
					}
				}
			}
			if (CollidersField != null)
			{
				CollidersField.SetValue(component, null);
			}
			if (BoundsField != null)
			{
				if (BoundsField.GetValue(component) is IList list2)
				{
					list2.Clear();
				}
				else
				{
					BoundsField.SetValue(component, null);
				}
			}
			try
			{
				component.SetupColliders();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("WearNTear.SetupColliders failed: " + ex.Message));
			}
			component.m_clearCachedSupport = false;
			try
			{
				component.SetHealthVisual(1f, false);
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("WearNTear.SetHealthVisual failed: " + ex2.Message));
			}
			ClearPropertyBlocks(root);
		}

		private static bool IsAlive(GameObject go)
		{
			return (Object)(object)go != (Object)null;
		}

		private static Transform FindChildRecursive(Transform root, string name)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform child = root.GetChild(i);
				if (!((Object)(object)child == (Object)null))
				{
					if (string.Equals(((Object)child).name, name, StringComparison.OrdinalIgnoreCase))
					{
						return child;
					}
					Transform val = FindChildRecursive(child, name);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			return null;
		}

		internal static void ClearPropertyBlocks(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			Transform val = root.transform.Find("FloorVisual");
			MeshRenderer[] componentsInChildren = ((Component)(((Object)(object)val != (Object)null) ? val : root.transform)).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					((Renderer)val2).SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
		}

		private static void EnsureWoodDefaultsCached()
		{
			//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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (_copiedWoodDefaults)
			{
				return;
			}
			_copiedWoodDefaults = true;
			GameObject val = null;
			try
			{
				val = ((PrefabManager.Instance != null) ? PrefabManager.Instance.GetPrefab("wood_floor") : null);
			}
			catch
			{
				val = null;
			}
			if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				val = ZNetScene.instance.GetPrefab("wood_floor");
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			WearNTear component = val.GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			_woodHealth = ((component.m_health > 0f) ? component.m_health : 400f);
			_woodMaterial = component.m_materialType;
			_woodSupports = component.m_supports;
			_woodNoRoofWear = component.m_noRoofWear;
			_woodNoSupportWear = component.m_noSupportWear;
			_woodAshDamageImmune = component.m_ashDamageImmune;
			_woodBurnable = component.m_burnable;
			try
			{
				_woodDamages = component.m_damages;
				_copiedDamages = true;
			}
			catch
			{
				_copiedDamages = false;
			}
			try
			{
				_woodDestroyedEffect = component.m_destroyedEffect;
				_woodHitEffect = component.m_hitEffect;
				_woodSwitchEffect = component.m_switchEffect;
				_copiedEffects = true;
			}
			catch
			{
				_copiedEffects = false;
			}
		}
	}
}
namespace WoodFloors2645.Patches
{
	[HarmonyPatch(typeof(Player), "FindClosestSnapPoints")]
	internal static class SnapPreferPatch
	{
		private static readonly List<Transform> GhostSnaps = new List<Transform>(8);

		private static readonly List<Transform> WorldSnaps = new List<Transform>(64);

		private static bool Prefix(Transform ghost, float maxSnapDistance, ref Transform a, ref Transform b, List<Piece> pieces, ref bool __result)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ghost == (Object)null)
				{
					return true;
				}
				Piece component = ((Component)ghost).GetComponent<Piece>();
				if (!PieceRegistration.IsAngledWoodFloor(component))
				{
					return true;
				}
				GhostSnaps.Clear();
				component.GetSnapPoints(GhostSnaps);
				if (GhostSnaps.Count == 0)
				{
					return true;
				}
				if (pieces == null)
				{
					return true;
				}
				WorldSnaps.Clear();
				pieces.Clear();
				Piece.GetSnapPoints(ghost.position, 10f, WorldSnaps, pieces);
				if (WorldSnaps.Count == 0)
				{
					return true;
				}
				Transform val = null;
				Transform val2 = null;
				float num = float.MaxValue;
				for (int i = 0; i < GhostSnaps.Count; i++)
				{
					Transform val3 = GhostSnaps[i];
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					SnapRole snapRole = SnapRole.Get(val3);
					for (int j = 0; j < WorldSnaps.Count; j++)
					{
						Transform val4 = WorldSnaps[j];
						if ((Object)(object)val4 == (Object)null)
						{
							continue;
						}
						Piece componentInParent = ((Component)val4).GetComponentInParent<Piece>();
						if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent == (Object)(object)component)
						{
							continue;
						}
						float num2 = Vector3.Distance(val3.position, val4.position);
						if (num2 > maxSnapDistance)
						{
							continue;
						}
						SnapRole snapRole2 = SnapRole.Get(val4);
						float num3 = num2;
						if (PieceRegistration.IsAngledWoodFloor(componentInParent) && (Object)(object)snapRole != (Object)null && (Object)(object)snapRole2 != (Object)null)
						{
							if (snapRole.Side == snapRole2.Side && snapRole.Side != SnapRole.Lateral.None)
							{
								num3 -= 8f;
							}
							else if (snapRole.Side != SnapRole.Lateral.None && snapRole2.Side != SnapRole.Lateral.None)
							{
								num3 += 6f;
							}
							if ((snapRole.SlopeEnd == SnapRole.End.Low && snapRole2.SlopeEnd == SnapRole.End.High) || (snapRole.SlopeEnd == SnapRole.End.High && snapRole2.SlopeEnd == SnapRole.End.Low))
							{
								num3 -= 6f;
							}
							else if (snapRole.SlopeEnd == snapRole2.SlopeEnd && snapRole.SlopeEnd != SnapRole.End.Mid)
							{
								num3 += 4f;
							}
						}
						if (num3 < num)
						{
							num = num3;
							val = val3;
							val2 = val4;
						}
					}
				}
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					return true;
				}
				a = val;
				b = val2;
				__result = true;
				return false;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("SnapPreferPatch fell back to vanilla: " + ex.Message));
				}
				return true;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}