Decompiled source of NarrowChimneysMaxVlasov v0.5.5

BepInEx/plugins/NarrowChimneys.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("NarrowChimneys")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.3.1")]
[assembly: AssemblyInformationalVersion("0.5.3.1")]
[assembly: AssemblyProduct("NarrowChimneys")]
[assembly: AssemblyTitle("NarrowChimneys")]
[assembly: AssemblyVersion("0.5.3.1")]
[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 NarrowChimneys
{
	internal static class ChimneyDiagnostics
	{
		private sealed class Record
		{
			internal float LastLog;

			internal string Evidence = "none";
		}

		private static readonly ConditionalWeakTable<WearNTear, Record> records = new ConditionalWeakTable<WearNTear, Record>();

		private static readonly MethodInfo minimum = AccessTools.Method(typeof(WearNTear), "GetMinSupport", (Type[])null, (Type[])null);

		private static readonly MethodInfo currentSupport = AccessTools.Method(typeof(WearNTear), "GetSupport", (Type[])null, (Type[])null);

		internal static bool IsChimney(WearNTear wear)
		{
			if (Object.op_Implicit((Object)(object)wear))
			{
				return ModuleCatalog.Find(((Object)wear).name) != null;
			}
			return false;
		}

		internal static void Evidence(WearNTear wear, string evidence)
		{
			if (IsChimney(wear))
			{
				records.GetValue(wear, (WearNTear _) => new Record()).Evidence = evidence;
			}
		}

		internal static void Report(WearNTear wear, string phase, float support, bool force = false)
		{
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			if (!IsChimney(wear))
			{
				return;
			}
			try
			{
				ZNetView component = ((Component)wear).GetComponent<ZNetView>();
				if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
				{
					return;
				}
				Record value = records.GetValue(wear, (WearNTear _) => new Record());
				float num = (float)minimum.Invoke(wear, null);
				if (!force && (!NarrowChimneysPlugin.Diagnostics.Value || Time.time < value.LastLog + 2f))
				{
					return;
				}
				value.LastLog = Time.time;
				Collider[] componentsInChildren = ((Component)wear).GetComponentsInChildren<Collider>(true);
				string text = "";
				Collider[] array = componentsInChildren;
				foreach (Collider val in array)
				{
					string text2 = text;
					object[] obj = new object[7]
					{
						((Object)val).name,
						((Component)val).gameObject.layer,
						val.enabled,
						val.isTrigger,
						Object.op_Implicit((Object)(object)val.attachedRigidbody) ? ((Object)val.attachedRigidbody).name : "none",
						null,
						null
					};
					Bounds bounds = val.bounds;
					obj[5] = ((Bounds)(ref bounds)).center;
					bounds = val.bounds;
					obj[6] = ((Bounds)(ref bounds)).size;
					text = text2 + string.Format(" [{0}:layer={1},enabled={2},trigger={3},body={4},centre={5},size={6}]", obj);
				}
				string text3 = $"Chimney {phase}: id={((Object)wear).GetInstanceID()}, pos={((Component)wear).transform.position}, owner={component.IsOwner()}, active={((Component)wear).gameObject.activeInHierarchy}, healthFraction={wear.GetHealthPercentage()}, maxHealth={wear.m_health}, support={support}, minimum={num}, material={wear.m_materialType}, COM={wear.m_comOffset}, supportWear={wear.m_noSupportWear}, lastEvidence={value.Evidence}; colliders={componentsInChildren.Length}{text}";
				if (force)
				{
					NarrowChimneysPlugin.Log.LogWarning((object)text3);
				}
				else
				{
					NarrowChimneysPlugin.Log.LogInfo((object)text3);
				}
				if (!NarrowChimneysPlugin.Diagnostics.Value || !(support < num))
				{
					return;
				}
				array = Physics.OverlapBox(((Component)wear).transform.position + Vector3.up * 0.5f, new Vector3(0.65f, 0.65f, 0.65f), ((Component)wear).transform.rotation, LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" }), (QueryTriggerInteraction)1);
				foreach (Collider val2 in array)
				{
					if (!((Object)(object)((Component)val2).GetComponentInParent<WearNTear>() == (Object)(object)wear))
					{
						WearNTear componentInParent = ((Component)val2).GetComponentInParent<WearNTear>();
						ZNetView val3 = (Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).GetComponent<ZNetView>() : null);
						string text4 = ((Object.op_Implicit((Object)(object)componentInParent) && Object.op_Implicit((Object)(object)val3) && val3.IsValid()) ? currentSupport.Invoke(componentInParent, null).ToString() : "not available");
						NarrowChimneysPlugin.Log.LogInfo((object)string.Format("Support neighbour: {0}, layer={1}, body={2}, supports={3}, material={4}, actualSupport={5}", ((Object)val2).name, ((Component)val2).gameObject.layer, Object.op_Implicit((Object)(object)val2.attachedRigidbody) ? ((Object)val2.attachedRigidbody).name : "none", Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.m_supports.ToString() : "world solid", Object.op_Implicit((Object)(object)componentInParent) ? ((object)Unsafe.As<MaterialType, MaterialType>(ref componentInParent.m_materialType)/*cast due to .constrained prefix*/).ToString() : "terrain/static", text4));
					}
				}
			}
			catch (Exception ex)
			{
				NarrowChimneysPlugin.ErrorOnce("Chimney diagnostics failed", ex);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Awake")]
	internal static class ChimneyWearAwakePatch
	{
		private static void Postfix(WearNTear __instance, float ___m_support)
		{
			ChimneyDiagnostics.Report(__instance, "Awake", ___m_support);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	internal static class ChimneySupportPatch
	{
		private static void Postfix(WearNTear __instance, float ___m_support)
		{
			ChimneyDiagnostics.Report(__instance, "support update", ___m_support);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "OnPlaced")]
	internal static class ChimneyPlacedPatch
	{
		private static void Postfix(WearNTear __instance, float ___m_support)
		{
			ChimneyDiagnostics.Report(__instance, "OnPlaced", ___m_support, force: true);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
	internal static class ChimneyDamagePatch
	{
		private static void Prefix(WearNTear __instance, float damage, HitData hitData, float ___m_support)
		{
			ChimneyDiagnostics.Evidence(__instance, string.Format("ApplyDamage={0}, hit={1}", damage, (hitData == null) ? "null (wear/environment possible)" : ((object)Unsafe.As<HitType, HitType>(ref hitData.m_hitType)/*cast due to .constrained prefix*/).ToString()));
			if (ChimneyDiagnostics.IsChimney(__instance))
			{
				ChimneyDiagnostics.Report(__instance, "before damage", ___m_support, force: true);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "RPC_Remove")]
	internal static class ChimneyRemovalPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			ChimneyDiagnostics.Evidence(__instance, "RPC_Remove requested");
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class ChimneyDestroyPatch
	{
		private static void Prefix(WearNTear __instance, float ___m_support)
		{
			ChimneyDiagnostics.Report(__instance, "BEFORE DESTROY", ___m_support, force: true);
		}
	}
	internal static class ChimneyFactory
	{
		internal const string PrefabName = "NC_Chimney";

		private static readonly Dictionary<ModuleKind, Mesh> models = new Dictionary<ModuleKind, Mesh>();

		internal static void ReleaseVisualCache()
		{
			foreach (Mesh value in models.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					Object.Destroy((Object)(object)value);
				}
			}
			models.Clear();
		}

		internal static GameObject Create(GameObject template, Transform storage, ModuleDefinition definition)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Expected O, but got Unknown
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, storage, false);
			try
			{
				((Object)val).name = definition.Id;
				val.transform.localPosition = Vector3.zero;
				val.transform.localRotation = Quaternion.identity;
				val.transform.localScale = Vector3.one;
				val.SetActive(true);
				Piece component = val.GetComponent<Piece>();
				ZNetView component2 = val.GetComponent<ZNetView>();
				WearNTear component3 = val.GetComponent<WearNTear>();
				if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)component3) || component2.GetZDO() != null)
				{
					throw new InvalidOperationException("Unsafe stone template or live ZDO during factory setup");
				}
				int num = LayerMask.NameToLayer("piece");
				if (num < 0)
				{
					throw new InvalidOperationException("Physics layer 'piece' missing");
				}
				val.layer = num;
				Material[] array = StoneMaterials.Create(template);
				ModuleGeometry moduleGeometry = ModuleGeometry.Build(definition.Kind);
				Component[] components = val.GetComponents<Component>();
				foreach (Component val2 in components)
				{
					if (!(val2 is Transform) && !(val2 is Piece) && !(val2 is ZNetView) && !(val2 is WearNTear))
					{
						NarrowChimneysPlugin.Detail("Removing inherited chimney helper: " + ((object)val2).GetType().Name);
						Object.DestroyImmediate((Object)(object)val2);
					}
				}
				while (val.transform.childCount > 0)
				{
					Object.DestroyImmediate((Object)(object)((Component)val.transform.GetChild(0)).gameObject);
				}
				GameObject val3 = new GameObject("NC_Masonry");
				val3.layer = num;
				val3.transform.SetParent(val.transform, false);
				if (!models.TryGetValue(definition.Kind, out var value) || !Object.op_Implicit((Object)(object)value))
				{
					value = CreateMesh(moduleGeometry.Model);
					models[definition.Kind] = value;
				}
				val3.AddComponent<MeshFilter>().sharedMesh = value;
				MeshRenderer val4 = val3.AddComponent<MeshRenderer>();
				((Renderer)val4).sharedMaterials = array;
				((Renderer)val4).shadowCastingMode = (ShadowCastingMode)1;
				((Renderer)val4).receiveShadows = true;
				val3.AddComponent<SurfaceDisplay>().Initialize(val4, definition.Kind);
				for (int j = 0; j < moduleGeometry.Collisions.Count; j++)
				{
					ModuleGeometry.Collision collision = moduleGeometry.Collisions[j];
					GameObject val5 = new GameObject("NC_Collision" + j);
					val5.layer = num;
					val5.transform.SetParent(val.transform, false);
					if (collision.Mesh != null)
					{
						MeshCollider obj = val5.AddComponent<MeshCollider>();
						obj.sharedMesh = CreateMesh(collision.Mesh, collider: true);
						obj.convex = false;
					}
					else
					{
						val5.transform.localPosition = new Vector3(collision.Centre.X, collision.Centre.Y, collision.Centre.Z);
						val5.transform.localRotation = new Quaternion(collision.Rotation.X, collision.Rotation.Y, collision.Rotation.Z, collision.Rotation.W);
						val5.AddComponent<BoxCollider>().size = new Vector3(collision.Size.X, collision.Size.Y, collision.Size.Z);
					}
				}
				component3.m_new = (component3.m_worn = (component3.m_broken = val3));
				component3.m_wet = null;
				component3.m_snow = (component3.m_snowWorn = (component3.m_snowBroken = null));
				component3.m_nonSolidRenderers = new List<Renderer>();
				component3.m_fragmentRoots = (GameObject[])(object)new GameObject[1] { val3 };
				component3.m_materialType = (MaterialType)((definition.Kind != ModuleKind.Bracket) ? 1 : 2);
				component3.m_supports = true;
				component3.m_noSupportWear = true;
				component3.m_noRoofWear = false;
				component3.m_burnable = false;
				Vector3 vector = SurfacePalette.SupportCentre(definition.Kind);
				component3.m_comOffset = new Vector3(vector.X, vector.Y, vector.Z);
				component3.m_forceCorrectCOMCalculation = true;
				component3.m_staticPosition = true;
				component3.m_health = 1500f;
				component3.m_requiredPersistentEvent = "";
				component3.m_requiredBiome = (Biome)0;
				foreach (ModuleGeometry.Snap snap in moduleGeometry.Snaps)
				{
					Snap(val.transform, snap.Name, new Vector3(snap.Position.X, snap.Position.Y, snap.Position.Z));
				}
				component.m_name = definition.Name;
				component.m_description = definition.Description;
				component.m_enabled = true;
				component.m_groundOnly = false;
				component.m_groundPiece = false;
				component.m_category = (PieceCategory)3;
				component.m_usage = (UsageTagFlags)20;
				component.m_icon = CreateIcon(moduleGeometry.Model, definition.Id);
				Requirement[] resources = component.m_resources;
				for (int i = 0; i < resources.Length; i++)
				{
					resources[i].m_amount = definition.Stone;
				}
				if (definition.Kind == ModuleKind.Roof || definition.Kind == ModuleKind.Arch || definition.Kind == ModuleKind.Grate)
				{
					val.AddComponent<ChimneyVent>().Kind = definition.Kind;
				}
				component2.m_persistent = true;
				component2.m_syncInitialScale = false;
				ClearDestroyedReferences((Component)(object)component);
				ClearDestroyedReferences((Component)(object)component3);
				ClearDestroyedReferences((Component)(object)component2);
				Validate(val, moduleGeometry.Collisions.Count);
				NarrowChimneysPlugin.Log.LogInfo((object)$"Module {definition.Id} ready: shader={((Object)array[0].shader).name}, mesh={val3.GetComponent<MeshFilter>().sharedMesh.vertexCount} vertices, colliders={moduleGeometry.Collisions.Count}, snaps={moduleGeometry.Snaps.Count}, layer={num}, health={component3.m_health}, COM={component3.m_comOffset}, support={component3.m_materialType}");
				return val;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}

		private static Mesh CreateMesh(ChimneyGeometry data, bool collider = false)
		{
			//IL_0062: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			List<Vector3> list2 = new List<Vector3>();
			List<Vector2> list3 = new List<Vector2>();
			List<Color> list4 = new List<Color>();
			for (int i = 0; i < data.Vertices.Count; i++)
			{
				Vector3 vector = data.Vertices[i];
				Vector3 vector2 = data.Normals[i];
				_ = data.UV[i];
				list.Add(new Vector3(vector.X, vector.Y, vector.Z));
				list2.Add(new Vector3(vector2.X, vector2.Y, vector2.Z));
				Vector2 vector3 = (collider ? data.UV[i] : data.AtlasUV[i]);
				list3.Add(new Vector2(vector3.X, vector3.Y));
				list4.Add(Color.white);
			}
			Mesh val = new Mesh
			{
				name = "NC_BevelledStoneShaft"
			};
			val.SetVertices(list);
			val.SetNormals(list2);
			val.SetUVs(0, list3);
			val.SetUVs(1, list3);
			val.SetColors(list4);
			val.subMeshCount = (collider ? 4 : 6);
			List<int>[] array = new List<int>[val.subMeshCount];
			for (int j = 0; j < array.Length; j++)
			{
				array[j] = new List<int>();
			}
			for (int k = 0; k < data.Triangles.Count; k += 3)
			{
				int num = SurfacePalette.Slot(data, k);
				if (collider)
				{
					float num2 = data.Shades[data.Triangles[k]];
					if (num2 < 0f)
					{
						num2 = 0.88f;
					}
					num = ((num2 < 0.5f) ? 3 : ((!(num2 < 0.9f)) ? ((num2 < 0.97f) ? 1 : 2) : 0));
				}
				array[num].Add(data.Triangles[k]);
				array[num].Add(data.Triangles[k + 1]);
				array[num].Add(data.Triangles[k + 2]);
			}
			for (int l = 0; l < array.Length; l++)
			{
				val.SetTriangles(array[l], l);
			}
			val.RecalculateBounds();
			val.RecalculateTangents();
			return val;
		}

		private static void Snap(Transform parent, string name, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("NC_Snap" + name);
			val.transform.SetParent(parent, false);
			val.transform.localPosition = position;
			val.tag = "snappoint";
		}

		private static Sprite CreateIcon(ChimneyGeometry mesh, string id)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			SurfacePreview style = new SurfacePreview
			{
				Atlas = StoneMaterials.PreviewAtlas
			};
			byte[] array = ChimneyIcon.Render(mesh, 128, null, night: false, fitSmall: false, style);
			Color32[] array2 = (Color32[])(object)new Color32[16384];
			for (int i = 0; i < array2.Length; i++)
			{
				array2[i] = new Color32(array[i * 4], array[i * 4 + 1], array[i * 4 + 2], array[i * 4 + 3]);
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				name = id + "_icon"
			};
			val.SetPixels32(array2);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 128f);
		}

		private static void Validate(GameObject prefab, int expectedColliders)
		{
			if (!prefab.activeSelf || prefab.activeInHierarchy || prefab.GetComponent<ZNetView>().GetZDO() != null)
			{
				throw new InvalidOperationException("Unsafe prefab activation");
			}
			Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
			if (componentsInChildren.Length != expectedColliders)
			{
				throw new InvalidOperationException("Module collider count mismatch");
			}
			Collider[] array = componentsInChildren;
			foreach (Collider val in array)
			{
				if ((!(val is BoxCollider) && !(val is MeshCollider)) || val.isTrigger || Object.op_Implicit((Object)(object)val.attachedRigidbody) || ((Component)val).gameObject.layer != prefab.layer)
				{
					throw new InvalidOperationException("Invalid chimney wall collider/support body");
				}
			}
			Piece component = prefab.GetComponent<Piece>();
			WearNTear component2 = prefab.GetComponent<WearNTear>();
			if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>()) || !Object.op_Implicit((Object)(object)component.m_icon) || !Object.op_Implicit((Object)(object)component2.m_new) || (Object)(object)component2.m_new != (Object)(object)component2.m_worn || (Object)(object)component2.m_new != (Object)(object)component2.m_broken || component2.m_health <= 0f || !component2.m_supports || !component2.m_noSupportWear)
			{
				throw new InvalidOperationException("Chimney lost required piece/wear/network/model references");
			}
			if (component.m_resources == null || component.m_resources.Length == 0)
			{
				throw new InvalidOperationException("Stone template resource requirements missing");
			}
		}

		internal static bool ConfigureResources(GameObject prefab, ModuleDefinition definition, ObjectDB db)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0088: Expected O, but got Unknown
			//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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			GameObject itemPrefab = db.GetItemPrefab("Stone");
			GameObject val = ((definition.Iron > 0) ? db.GetItemPrefab("Iron") : null);
			if (!Object.op_Implicit((Object)(object)itemPrefab) || !Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>()) || (definition.Iron > 0 && (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.GetComponent<ItemDrop>()))))
			{
				return false;
			}
			List<Requirement> list = new List<Requirement>
			{
				new Requirement
				{
					m_resItem = itemPrefab.GetComponent<ItemDrop>(),
					m_amount = definition.Stone,
					m_recover = true
				}
			};
			if (Object.op_Implicit((Object)(object)val))
			{
				list.Add(new Requirement
				{
					m_resItem = val.GetComponent<ItemDrop>(),
					m_amount = definition.Iron,
					m_recover = true
				});
			}
			prefab.GetComponent<Piece>().m_resources = list.ToArray();
			return true;
		}

		private static void ClearDestroyedReferences(Component component)
		{
			FieldInfo[] fields = ((object)component).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (typeof(Object).IsAssignableFrom(fieldInfo.FieldType))
				{
					object? value = fieldInfo.GetValue(component);
					Object val = (Object)((value is Object) ? value : null);
					if (val != null && !Object.op_Implicit(val))
					{
						fieldInfo.SetValue(component, null);
						NarrowChimneysPlugin.Log.LogWarning((object)("Cleared destroyed template reference: " + ((object)component).GetType().Name + "." + fieldInfo.Name));
					}
				}
			}
		}
	}
	internal sealed class ChimneyGeometry
	{
		internal readonly struct WallSpec
		{
			internal readonly Vector3 Centre;

			internal readonly Vector3 Size;

			internal WallSpec(Vector3 centre, Vector3 size)
			{
				Centre = centre;
				Size = size;
			}
		}

		internal static readonly WallSpec[] Walls = new WallSpec[4]
		{
			new WallSpec(new Vector3(0f, 0.5f, 0.375f), new Vector3(1f, 1f, 0.25f)),
			new WallSpec(new Vector3(0f, 0.5f, -0.375f), new Vector3(1f, 1f, 0.25f)),
			new WallSpec(new Vector3(0.375f, 0.5f, 0f), new Vector3(0.25f, 1f, 0.5f)),
			new WallSpec(new Vector3(-0.375f, 0.5f, 0f), new Vector3(0.25f, 1f, 0.5f))
		};

		internal readonly List<Vector3> Vertices = new List<Vector3>();

		internal readonly List<Vector3> Normals = new List<Vector3>();

		internal readonly List<Vector2> UV = new List<Vector2>();

		internal readonly List<Vector2> AtlasUV = new List<Vector2>();

		internal readonly List<float> Shades = new List<float>();

		internal readonly List<int> Triangles = new List<int>();

		internal static ChimneyGeometry Build()
		{
			ChimneyGeometry chimneyGeometry = new ChimneyGeometry();
			for (int i = 0; i < 6; i++)
			{
				float y = ((float)i + 0.5f) / 6f;
				float[] array = ((i % 2 != 0) ? new float[4] { -0.5f, -0.25f, 0.25f, 0.5f } : new float[3] { -0.5f, 0f, 0.5f });
				int[] array2;
				for (int j = 0; j < array.Length - 1; j++)
				{
					array2 = new int[2] { -1, 1 };
					foreach (int num in array2)
					{
						chimneyGeometry.Brick(new Vector3((array[j] + array[j + 1]) / 2f, y, (float)num * 0.375f), new Vector3(array[j + 1] - array[j] - 0.014f, 0.15466668f, 0.24f), 0.82f + (float)((i + j) % 5) * 0.045f);
					}
				}
				array2 = new int[2] { -1, 1 };
				foreach (int num2 in array2)
				{
					chimneyGeometry.Brick(new Vector3((float)num2 * 0.375f, y, 0f), new Vector3(0.24f, 0.15466668f, 0.49f), 0.84f + (float)(i % 4) * 0.05f);
				}
			}
			chimneyGeometry.MortarRing(0f, 1f, 0.45f, 0.29f);
			return chimneyGeometry;
		}

		internal void MortarRing(float bottom, float height, float outer, float inner)
		{
			Vector3[] array = new Vector3[4]
			{
				new Vector3(0f - outer, 0f, 0f - outer),
				new Vector3(outer, 0f, 0f - outer),
				new Vector3(outer, 0f, outer),
				new Vector3(0f - outer, 0f, outer)
			};
			Vector3[] array2 = new Vector3[4]
			{
				new Vector3(0f - inner, 0f, 0f - inner),
				new Vector3(inner, 0f, 0f - inner),
				new Vector3(inner, 0f, inner),
				new Vector3(0f - inner, 0f, inner)
			};
			Vector3 vector = new Vector3(0f, bottom, 0f);
			Vector3 vector2 = new Vector3(0f, bottom + height, 0f);
			for (int i = 0; i < 4; i++)
			{
				int num = (i + 1) % 4;
				Vector3 vector3 = Vector3.Normalize(array[i] + array[num]);
				Face(new Vector3[4]
				{
					array[i] + vector,
					array[num] + vector,
					array[num] + vector2,
					array[i] + vector2
				}, Vector3.Zero, 0.35f, vector3);
				Face(new Vector3[4]
				{
					array2[i] + vector,
					array2[num] + vector,
					array2[num] + vector2,
					array2[i] + vector2
				}, Vector3.Zero, 0.35f, -vector3);
				Face(new Vector3[4]
				{
					array[i] + vector2,
					array[num] + vector2,
					array2[num] + vector2,
					array2[i] + vector2
				}, Vector3.Zero, 0.35f, Vector3.UnitY);
				Face(new Vector3[4]
				{
					array[i] + vector,
					array[num] + vector,
					array2[num] + vector,
					array2[i] + vector
				}, Vector3.Zero, 0.35f, -Vector3.UnitY);
			}
		}

		internal void MortarBox(Vector3 centre, Vector3 size)
		{
			Vector3 vector = size / 2f;
			Vector3[] surface = new Vector3[4]
			{
				centre + new Vector3(0f - vector.X, 0f - vector.Y, 0f - vector.Z),
				centre + new Vector3(vector.X, 0f - vector.Y, 0f - vector.Z),
				centre + new Vector3(vector.X, vector.Y, 0f - vector.Z),
				centre + new Vector3(0f - vector.X, vector.Y, 0f - vector.Z)
			};
			Add(Prism(surface, new Vector3(0f, 0f, size.Z), 0.35f), Matrix4x4.Identity);
		}

		internal void Brick(Vector3 centre, Vector3 size, float shade)
		{
			Vector3 v = size / 2f;
			float num = Math.Min(Math.Min(size.X, Math.Min(size.Y, size.Z)) * 0.15f, 0.018f + (float)((int)Math.Abs(centre.X * 711f + centre.Y * 113f + centre.Z * 257f) % 5) * 0.001f);
			int seed = (int)Math.Abs(centre.X * 13717f + centre.Y * 27191f + centre.Z * 7739f + size.X * 313f + size.Z * 521f);
			int[] array;
			for (int i = 0; i < 3; i++)
			{
				array = new int[2] { -1, 1 };
				foreach (int num2 in array)
				{
					int axis = (i + 1) % 3;
					int axis2 = (i + 2) % 3;
					Vector3[] array2 = new Vector3[4];
					int[,] array3 = new int[4, 2]
					{
						{ -1, -1 },
						{ 1, -1 },
						{ 1, 1 },
						{ -1, 1 }
					};
					for (int k = 0; k < 4; k++)
					{
						array2[k] = Axis(i, (float)num2 * At(v, i)) + Axis(axis, (float)array3[k, 0] * (At(v, axis) - num)) + Axis(axis2, (float)array3[k, 1] * (At(v, axis2) - num));
					}
					StoneFace(array2);
				}
			}
			for (int l = 0; l < 3; l++)
			{
				array = new int[2] { -1, 1 };
				foreach (int num3 in array)
				{
					int[] array4 = new int[2] { -1, 1 };
					foreach (int num4 in array4)
					{
						int axis3 = (l + 1) % 3;
						int axis4 = (l + 2) % 3;
						Vector3 vector = Axis(axis3, (float)num3 * At(v, axis3)) + Axis(axis4, (float)num4 * (At(v, axis4) - num));
						Vector3 vector2 = Axis(axis3, (float)num3 * (At(v, axis3) - num)) + Axis(axis4, (float)num4 * At(v, axis4));
						Vector3 vector3 = Axis(l, At(v, l) - num);
						StoneFace(new Vector3[4]
						{
							vector - vector3,
							vector + vector3,
							vector2 + vector3,
							vector2 - vector3
						});
					}
				}
			}
			array = new int[2] { -1, 1 };
			foreach (int num5 in array)
			{
				int[] array4 = new int[2] { -1, 1 };
				foreach (int num6 in array4)
				{
					int[] array5 = new int[2] { -1, 1 };
					foreach (int num7 in array5)
					{
						StoneFace(new Vector3[3]
						{
							new Vector3((float)num5 * v.X, (float)num6 * (v.Y - num), (float)num7 * (v.Z - num)),
							new Vector3((float)num5 * (v.X - num), (float)num6 * v.Y, (float)num7 * (v.Z - num)),
							new Vector3((float)num5 * (v.X - num), (float)num6 * (v.Y - num), (float)num7 * v.Z)
						});
					}
				}
			}
			void StoneFace(Vector3[] points)
			{
				for (int num8 = 0; num8 < points.Length; num8++)
				{
					Vector3 vector4 = points[num8];
					float num9 = (float)((((int)Math.Round(vector4.X * 100000f) * 73856093) ^ ((int)Math.Round(vector4.Y * 100000f) * 19349663) ^ ((int)Math.Round(vector4.Z * 100000f) * 83492791) ^ seed) & 0x3FF) / 1023f * 0.012f;
					points[num8] = vector4 * (1f - num9);
				}
				Face(points, centre, shade);
			}
		}

		private void Face(Vector3[] points, Vector3 centre, float shade, Vector3? outward = null)
		{
			Vector3 vector = Vector3.Normalize(Vector3.Cross(points[1] - points[0], points[2] - points[0]));
			Vector3 zero = Vector3.Zero;
			Vector3[] array = points;
			foreach (Vector3 vector2 in array)
			{
				zero += vector2;
			}
			if (Vector3.Dot(vector, outward ?? zero) < 0f)
			{
				Array.Reverse(points);
				vector = -vector;
			}
			Vector3 vector3 = Vector3.Normalize(Vector3.Cross((Math.Abs(vector.Y) > 0.95f) ? Vector3.UnitZ : Vector3.UnitY, vector));
			Vector3 vector4 = Vector3.Cross(vector, vector3);
			Vector2 vector5 = new Vector2(float.MaxValue);
			Vector2 value = new Vector2(float.MinValue);
			array = points;
			foreach (Vector3 vector6 in array)
			{
				Vector3 vector7 = centre + vector6;
				Vector2 value2 = new Vector2(Vector3.Dot(vector7, vector3), Vector3.Dot(vector7, vector4)) * 1.6f;
				vector5 = Vector2.Min(vector5, value2);
				value = Vector2.Max(value, value2);
			}
			float num = Math.Min(1f, 0.94f / Math.Max(0.0001f, Math.Max(value.X - vector5.X, value.Y - vector5.Y)));
			int num2 = (int)((uint)((int)(centre.X * 1703f) ^ (int)(centre.Y * 2719f) ^ (int)(centre.Z * 1381f) ^ (int)(vector.X * 127f + vector.Z * 311f + shade * 101f)) % 16u);
			Vector2 vector8 = new Vector2(num2 % 4 * 64 + 9, num2 / 4 * 64 + 9) / 256f;
			for (int j = 1; j < points.Length - 1; j++)
			{
				Vector3 vector9 = points[0];
				Vector3 vector10 = points[j];
				Vector3 vector11 = points[j + 1];
				Vector3 item = Vector3.Normalize(Vector3.Cross(vector10 - vector9, vector11 - vector9));
				int count = Vertices.Count;
				array = new Vector3[3] { vector9, vector10, vector11 };
				foreach (Vector3 vector12 in array)
				{
					Vector3 vector13 = centre + vector12;
					Vertices.Add(vector13);
					Normals.Add(item);
					UV.Add(new Vector2(Vector3.Dot(vector13, vector3), Vector3.Dot(vector13, vector4)) * 1.6f);
					AtlasUV.Add(vector8 + (UV[UV.Count - 1] - vector5) * num * (23f / 128f));
					Shades.Add(shade);
				}
				Triangles.Add(count);
				Triangles.Add(count + 1);
				Triangles.Add(count + 2);
			}
		}

		private static Vector3 Axis(int axis, float value)
		{
			return axis switch
			{
				1 => new Vector3(0f, value, 0f), 
				0 => new Vector3(value, 0f, 0f), 
				_ => new Vector3(0f, 0f, value), 
			};
		}

		private static float At(Vector3 v, int axis)
		{
			return axis switch
			{
				1 => v.Y, 
				0 => v.X, 
				_ => v.Z, 
			};
		}

		internal void Add(ChimneyGeometry source, Matrix4x4 transform)
		{
			int count = Vertices.Count;
			if (!Matrix4x4.Invert(transform, out var result))
			{
				throw new ArgumentException("Singular mesh transform");
			}
			Matrix4x4 matrix = Matrix4x4.Transpose(result);
			for (int i = 0; i < source.Vertices.Count; i++)
			{
				Vertices.Add(Vector3.Transform(source.Vertices[i], transform));
				Normals.Add(Vector3.Normalize(Vector3.TransformNormal(source.Normals[i], matrix)));
				UV.Add(source.UV[i] + new Vector2(transform.M41 * 0.73f + transform.M43 * 0.31f, transform.M42 * 0.83f + transform.M43 * 0.23f));
				Shades.Add(source.Shades[i]);
				AtlasUV.Add(source.AtlasUV[i]);
			}
			foreach (int triangle in source.Triangles)
			{
				Triangles.Add(count + triangle);
			}
		}

		internal static ChimneyGeometry Frustum(float bottomHalf, float topHalf, float bottom, float top)
		{
			ChimneyGeometry mesh = new ChimneyGeometry();
			Vector3[] array = new Vector3[4];
			Vector3[] array2 = new Vector3[4];
			for (int i = 0; i < 4; i++)
			{
				float num = ((i != 0 && i != 3) ? 1 : (-1));
				float num2 = ((i >= 2) ? 1 : (-1));
				array[i] = new Vector3(num * bottomHalf, bottom, num2 * bottomHalf);
				array2[i] = new Vector3(num * topHalf, top, num2 * topHalf);
			}
			Vector3 centre = new Vector3(0f, (top + bottom) / 2f, 0f);
			FaceWorld((Vector3[])array.Clone());
			FaceWorld((Vector3[])array2.Clone());
			for (int j = 0; j < 4; j++)
			{
				FaceWorld(new Vector3[4]
				{
					array[j],
					array[(j + 1) % 4],
					array2[(j + 1) % 4],
					array2[j]
				});
			}
			return mesh;
			void FaceWorld(Vector3[] points)
			{
				for (int k = 0; k < points.Length; k++)
				{
					points[k] -= centre;
				}
				mesh.Face(points, centre, 0.95f);
			}
		}

		internal static ChimneyGeometry Prism(Vector3[] surface, Vector3 extrusion, float shade)
		{
			ChimneyGeometry mesh = new ChimneyGeometry();
			Vector3 centre = Vector3.Zero;
			foreach (Vector3 vector in surface)
			{
				centre += vector;
			}
			centre = centre / surface.Length + extrusion / 2f;
			Vector3[] array = new Vector3[surface.Length];
			for (int j = 0; j < array.Length; j++)
			{
				array[j] = surface[j] + extrusion;
			}
			AddFace((Vector3[])surface.Clone());
			AddFace(array);
			for (int k = 0; k < surface.Length; k++)
			{
				AddFace(new Vector3[4]
				{
					surface[k],
					surface[(k + 1) % surface.Length],
					surface[(k + 1) % surface.Length] + extrusion,
					surface[k] + extrusion
				});
			}
			return mesh;
			void AddFace(Vector3[] points)
			{
				for (int l = 0; l < points.Length; l++)
				{
					points[l] -= centre;
				}
				mesh.Face(points, centre, shade);
			}
		}

		internal static ChimneyGeometry BevelledPrism(Vector3[] surface, Vector3 extrusion, float shade, float bevel)
		{
			ChimneyGeometry mesh = new ChimneyGeometry();
			Vector3 zero = Vector3.Zero;
			foreach (Vector3 vector in surface)
			{
				zero += vector;
			}
			zero /= (float)surface.Length;
			Vector3 centre = zero + extrusion / 2f;
			Vector3 vector2 = Vector3.Normalize(extrusion) * bevel;
			Vector3[][] array = new Vector3[4][];
			for (int j = 0; j < 4; j++)
			{
				array[j] = new Vector3[surface.Length];
				for (int k = 0; k < surface.Length; k++)
				{
					array[j][k] = j switch
					{
						2 => surface[k] + extrusion - vector2, 
						1 => surface[k] + vector2, 
						0 => zero + (surface[k] - zero) * 0.94f, 
						_ => zero + (surface[k] - zero) * 0.94f + extrusion, 
					};
				}
			}
			AddFace((Vector3[])array[0].Clone());
			AddFace((Vector3[])array[3].Clone());
			for (int l = 0; l < 3; l++)
			{
				for (int m = 0; m < surface.Length; m++)
				{
					AddFace(new Vector3[4]
					{
						array[l][m],
						array[l][(m + 1) % surface.Length],
						array[l + 1][(m + 1) % surface.Length],
						array[l + 1][m]
					});
				}
			}
			return mesh;
			void AddFace(Vector3[] points)
			{
				for (int n = 0; n < points.Length; n++)
				{
					points[n] -= centre;
				}
				mesh.Face(points, centre, shade);
			}
		}
	}
	internal sealed class SurfacePreview
	{
		internal MasonryAtlas Atlas { get; set; }

		internal byte[] NativeColor { get; set; }

		internal byte[] NativeNormal { get; set; }

		internal int NativeSize { get; set; } = 128;

		internal bool Diagnostic { get; set; }

		internal bool Shadow { get; set; }
	}
	internal static class ChimneyIcon
	{
		internal static byte[] Render(ChimneyGeometry mesh, int size, Vector3? view = null, bool night = false, bool fitSmall = false, SurfacePreview style = null)
		{
			byte[] array = new byte[size * size * 4];
			float[] array2 = new float[size * size];
			for (int i = 0; i < array2.Length; i++)
			{
				array2[i] = float.NegativeInfinity;
			}
			Vector3 vector = new Vector3(float.MaxValue);
			Vector3 vector2 = new Vector3(float.MinValue);
			foreach (Vector3 vertex in mesh.Vertices)
			{
				vector = Vector3.Min(vector, vertex);
				vector2 = Vector3.Max(vector2, vertex);
			}
			Vector3 vector3 = (vector + vector2) / 2f;
			float num = Math.Max(fitSmall ? 0.1f : 1f, Math.Max(vector2.Y - vector.Y, Math.Max(vector2.X - vector.X, vector2.Z - vector.Z)));
			Vector3 vector4 = Vector3.Normalize(view ?? new Vector3(1f, 0.8f, 1f));
			Vector3 vector5 = Vector3.Normalize(Vector3.Cross((Math.Abs(vector4.Y) > 0.99f) ? Vector3.UnitZ : Vector3.UnitY, vector4));
			Vector3 up = Vector3.Normalize(Vector3.Cross(vector4, vector5));
			Vector3 vector6 = Vector3.Normalize(night ? new Vector3(-0.4f, 0.15f, 0.8f) : new Vector3(-0.3f, 1f, 0.6f));
			StoneMaps maps = StoneSurface.Maps;
			for (int j = 0; j < mesh.Triangles.Count; j += 3)
			{
				int index = mesh.Triangles[j];
				int index2 = mesh.Triangles[j + 1];
				int index3 = mesh.Triangles[j + 2];
				int num2 = SurfacePalette.Slot(mesh, j);
				bool flag = (num2 < 3 || num2 == 4) && style?.Atlas != null;
				bool flag2 = (num2 < 3 || num2 == 4) && style?.NativeColor != null;
				List<Vector2> list = (flag ? mesh.AtlasUV : mesh.UV);
				if (Vector3.Dot(mesh.Normals[index], vector4) <= 0f)
				{
					continue;
				}
				Vector3 vector7 = Project((mesh.Vertices[index] - vector3) / num, vector5, up, vector4, size);
				Vector3 vector8 = Project((mesh.Vertices[index2] - vector3) / num, vector5, up, vector4, size);
				Vector3 vector9 = Project((mesh.Vertices[index3] - vector3) / num, vector5, up, vector4, size);
				float num3 = Edge(vector7, vector8, vector9.X, vector9.Y);
				if (Math.Abs(num3) < 0.0001f)
				{
					continue;
				}
				int num4 = Math.Max(0, (int)Math.Floor(Math.Min(vector7.X, Math.Min(vector8.X, vector9.X))));
				int num5 = Math.Min(size - 1, (int)Math.Ceiling(Math.Max(vector7.X, Math.Max(vector8.X, vector9.X))));
				int num6 = Math.Max(0, (int)Math.Floor(Math.Min(vector7.Y, Math.Min(vector8.Y, vector9.Y))));
				int num7 = Math.Min(size - 1, (int)Math.Ceiling(Math.Max(vector7.Y, Math.Max(vector8.Y, vector9.Y))));
				Vector3 vector10 = mesh.Vertices[index2] - mesh.Vertices[index];
				Vector3 vector11 = mesh.Vertices[index3] - mesh.Vertices[index];
				Vector2 vector12 = list[index2] - list[index];
				Vector2 vector13 = list[index3] - list[index];
				float num8 = vector12.X * vector13.Y - vector12.Y * vector13.X;
				Vector3 vector14 = ((Math.Abs(num8) > 1E-06f) ? Vector3.Normalize((vector10 * vector13.Y - vector11 * vector12.Y) / num8) : Vector3.UnitX);
				Vector3 vector15 = ((Math.Abs(num8) > 1E-06f) ? Vector3.Normalize((vector11 * vector12.X - vector10 * vector13.X) / num8) : Vector3.UnitY);
				for (int k = num6; k <= num7; k++)
				{
					for (int l = num4; l <= num5; l++)
					{
						float num9 = Edge(vector8, vector9, (float)l + 0.5f, (float)k + 0.5f) / num3;
						float num10 = Edge(vector9, vector7, (float)l + 0.5f, (float)k + 0.5f) / num3;
						float num11 = 1f - num9 - num10;
						if (num9 < 0f || num10 < 0f || num11 < 0f)
						{
							continue;
						}
						int num12 = k * size + l;
						float num13 = num9 * vector7.Z + num10 * vector8.Z + num11 * vector9.Z;
						if (num13 <= array2[num12])
						{
							continue;
						}
						array2[num12] = num13;
						Vector2 vector16 = list[index] * num9 + list[index2] * num10 + list[index3] * num11;
						int num14 = (flag ? 256 : (flag2 ? style.NativeSize : 512));
						int num15 = (int)((vector16.X - (float)Math.Floor(vector16.X)) * (float)(num14 - 1));
						int num16 = (int)((vector16.Y - (float)Math.Floor(vector16.Y)) * (float)(num14 - 1));
						int num17 = (num16 * num14 + num15) * 4;
						Vector3 vector17;
						if (flag || flag2)
						{
							byte[] array3 = (flag ? style.Atlas.Normal : style.NativeNormal);
							vector17 = SurfacePalette.DecodeNormal(array3[num17], array3[num17 + 1], array3[num17 + 3]);
						}
						else
						{
							vector17 = new Vector3((float)(int)maps.NormalRGB[num17] / 127.5f - 1f, (float)(int)maps.NormalRGB[num17 + 1] / 127.5f - 1f, (float)(int)maps.NormalRGB[num17 + 2] / 127.5f - 1f);
						}
						if (num2 == 3 || num2 == 5 || (style != null && style.Diagnostic))
						{
							vector17 = Vector3.UnitZ;
						}
						Vector3 vector18 = Vector3.Normalize(vector14 * vector17.X + vector15 * vector17.Y + mesh.Normals[index] * vector17.Z);
						float num18 = Math.Max(0f, Vector3.Dot(vector18, vector6));
						float num19 = ((flag || flag2) ? 1f : ((float)(int)maps.Occlusion[num17 + 1] / 255f));
						float num20 = num2 switch
						{
							1 => 1f, 
							0 => 0.96f, 
							4 => 0.94f, 
							5 => 0.65f, 
							3 => 0.7f, 
							_ => 1.035f, 
						};
						for (int m = 0; m < 3; m++)
						{
							float num21 = ((!night) ? (0.28f + 0.85f * num18) : (m switch
							{
								1 => 0.1f + 0.48f * num18, 
								0 => 0.13f + 0.95f * num18, 
								_ => 0.16f + 0.2f * num18, 
							}));
							float num22 = (flag ? ((float)(int)style.Atlas.Albedo[num17 + m] / 255f) : (flag2 ? ((float)(int)style.NativeColor[num17 + m] / 255f) : ((float)(int)maps.Albedo[num17 + m] / 255f)));
							if (num2 == 3 || num2 == 5)
							{
								num22 = 0.24f + 0.08f * (float)((num15 * 13 + num16 * 7) % 17) / 16f;
							}
							if (style != null && style.Diagnostic)
							{
								Vector3 vector19 = SurfacePalette.DebugColor(num2);
								num22 = m switch
								{
									1 => vector19.Y, 
									0 => vector19.X, 
									_ => vector19.Z, 
								};
								num20 = 1f;
							}
							if (style != null && style.Shadow)
							{
								num21 = 0.21f;
							}
							float num23 = (float)Math.Pow(Math.Pow(num22, 2.2) * (double)num21 * (double)num19 * (double)num20, 0.45454545454545453);
							array[num12 * 4 + m] = (byte)Math.Max(0f, Math.Min(255f, num23 * 255f));
						}
						array[num12 * 4 + 3] = byte.MaxValue;
					}
				}
			}
			return array;
		}

		private static Vector3 Project(Vector3 v, Vector3 right, Vector3 up, Vector3 toward, int size)
		{
			return new Vector3((float)size * (0.5f + Vector3.Dot(v, right) * 0.62f), (float)size * (0.5f + Vector3.Dot(v, up) * 0.62f), Vector3.Dot(v, toward));
		}

		private static float Edge(Vector3 a, Vector3 b, float x, float y)
		{
			return (b.X - a.X) * (y - a.Y) - (b.Y - a.Y) * (x - a.X);
		}
	}
	internal sealed class ChimneyRegistry
	{
		private GameObject storage;

		private readonly List<GameObject> prefabs = new List<GameObject>();

		private ZNetScene readyScene;

		private readonly HashSet<PieceTable> loggedTables = new HashSet<PieceTable>();

		private readonly HashSet<List<Piece>> caches = new HashSet<List<Piece>>();

		internal void BeforeSceneAwake(ZNetScene scene)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			readyScene = null;
			if (!NarrowChimneysPlugin.BuildableChimney.Value)
			{
				return;
			}
			try
			{
				if (prefabs.Count == 0)
				{
					GameObject val = scene.m_prefabs.Find((GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).name == "stone_wall_1x1");
					if (!Object.op_Implicit((Object)(object)val))
					{
						throw new InvalidOperationException("stone_wall_1x1 not found");
					}
					if (!Object.op_Implicit((Object)(object)storage))
					{
						storage = new GameObject("NarrowChimneys_PrefabStorage");
						storage.SetActive(false);
						Object.DontDestroyOnLoad((Object)(object)storage);
					}
					try
					{
						ModuleDefinition[] all = ModuleCatalog.All;
						foreach (ModuleDefinition definition in all)
						{
							prefabs.Add(ChimneyFactory.Create(val, storage.transform, definition));
						}
					}
					catch
					{
						foreach (GameObject prefab2 in prefabs)
						{
							Object.Destroy((Object)(object)prefab2);
						}
						prefabs.Clear();
						throw;
					}
				}
				HashSet<int> hashSet = new HashSet<int>();
				foreach (GameObject prefab in prefabs)
				{
					int prefabHash = scene.GetPrefabHash(prefab);
					if (!hashSet.Add(prefabHash))
					{
						throw new InvalidOperationException("Internal module hash collision: " + ((Object)prefab).name);
					}
					foreach (GameObject prefab3 in scene.m_prefabs)
					{
						CheckCollision(scene, prefab3, prefab, prefabHash);
					}
					foreach (GameObject nonNetViewPrefab in scene.m_nonNetViewPrefabs)
					{
						CheckCollision(scene, nonNetViewPrefab, prefab, prefabHash);
					}
					if (scene.m_prefabs.FindAll((GameObject p) => (Object)(object)p == (Object)(object)prefab).Count > 1)
					{
						throw new InvalidOperationException("Duplicate module in prefab list: " + ((Object)prefab).name);
					}
				}
				foreach (GameObject prefab4 in prefabs)
				{
					if (!scene.m_prefabs.Contains(prefab4))
					{
						scene.m_prefabs.Add(prefab4);
					}
					NarrowChimneysPlugin.Detail($"Module queued {((Object)prefab4).name}, hash={scene.GetPrefabHash(prefab4)}, activeSelf={prefab4.activeSelf}, live={prefab4.activeInHierarchy}");
				}
				NarrowChimneysPlugin.Log.LogInfo((object)"Queued 5 chimney modules, optional bracket and legacy NC_Chimney before ZNetScene.Awake");
			}
			catch (Exception ex)
			{
				scene.m_prefabs.RemoveAll((GameObject p) => prefabs.Contains(p));
				foreach (PieceTable loggedTable in loggedTables)
				{
					if (Object.op_Implicit((Object)(object)loggedTable))
					{
						loggedTable.m_pieces.RemoveAll((GameObject p) => prefabs.Contains(p));
					}
				}
				foreach (List<Piece> cache in caches)
				{
					cache.RemoveAll((Piece p) => prefabs.Exists((GameObject go) => Object.op_Implicit((Object)(object)go) && (Object)(object)go.GetComponent<Piece>() == (Object)(object)p));
				}
				NarrowChimneysPlugin.Log.LogError((object)("Module registration aborted atomically: " + ex));
			}
		}

		private static void CheckCollision(ZNetScene scene, GameObject candidate, GameObject prefab, int hash)
		{
			if (Object.op_Implicit((Object)(object)candidate) && (Object)(object)candidate != (Object)(object)prefab && scene.GetPrefabHash(candidate) == hash)
			{
				throw new InvalidOperationException(((Object)prefab).name + " hash/name conflict with " + ((Object)candidate).name);
			}
		}

		internal void AfterSceneAwake(ZNetScene scene)
		{
			if (!NarrowChimneysPlugin.BuildableChimney.Value || prefabs.Count == 0)
			{
				return;
			}
			foreach (GameObject prefab in prefabs)
			{
				if ((Object)(object)scene.GetPrefab(((Object)prefab).name) != (Object)(object)prefab || (Object)(object)scene.GetPrefab(scene.GetPrefabHash(prefab)) != (Object)(object)prefab)
				{
					NarrowChimneysPlugin.Log.LogError((object)("Module lookup validation failed: " + ((Object)prefab).name));
					return;
				}
			}
			readyScene = scene;
			NarrowChimneysPlugin.Log.LogInfo((object)"All 7 prefab lookups validated, including legacy NC_Chimney");
			RegisterHammer(ObjectDB.instance);
		}

		internal void RegisterHammer(ObjectDB db)
		{
			if (!Object.op_Implicit((Object)(object)readyScene) || !Object.op_Implicit((Object)(object)db))
			{
				return;
			}
			try
			{
				GameObject itemPrefab = db.GetItemPrefab("Hammer");
				PieceTable val = ((!Object.op_Implicit((Object)(object)itemPrefab)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces);
				if (!Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				foreach (GameObject piece in val.m_pieces)
				{
					if (Object.op_Implicit((Object)(object)piece) && ModuleCatalog.Find(((Object)piece).name) != null && !prefabs.Contains(piece))
					{
						throw new InvalidOperationException("Foreign module in hammer table: " + ((Object)piece).name);
					}
				}
				List<Piece> allBuildPieces = db.GetAllBuildPieces(false);
				caches.Add(allBuildPieces);
				foreach (GameObject prefab in prefabs)
				{
					ModuleDefinition moduleDefinition = ModuleCatalog.Find(((Object)prefab).name);
					if (!moduleDefinition.Visible)
					{
						continue;
					}
					if (!ChimneyFactory.ConfigureResources(prefab, moduleDefinition, db))
					{
						NarrowChimneysPlugin.Detail("Waiting for resources: " + moduleDefinition.Id);
						continue;
					}
					if (!val.m_pieces.Contains(prefab))
					{
						val.m_pieces.Add(prefab);
					}
					Piece component = prefab.GetComponent<Piece>();
					if (!allBuildPieces.Contains(component))
					{
						allBuildPieces.Add(component);
					}
				}
				loggedTables.RemoveWhere((PieceTable t) => !Object.op_Implicit((Object)(object)t));
				if (loggedTables.Add(val))
				{
					NarrowChimneysPlugin.Log.LogInfo((object)"Hammer: 5 chimney modules + optional bracket registered; legacy network prefab preserved");
				}
			}
			catch (Exception ex)
			{
				NarrowChimneysPlugin.ErrorOnce("Hammer module registration failed", ex);
			}
		}
	}
	public sealed class ChimneyVent : MonoBehaviour
	{
		[SerializeField]
		internal ModuleKind Kind;

		private static readonly List<ChimneyVent> instances = new List<ChimneyVent>();

		private void OnEnable()
		{
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && !instances.Contains(this))
			{
				instances.Add(this);
			}
		}

		private void OnDisable()
		{
			instances.Remove(this);
		}

		private void Start()
		{
			OnEnable();
		}

		internal static bool TryTarget(Vector3 position, float radius, Func<Vector3, Vector3, float, bool> clear, out Vector3 target)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			target = position;
			foreach (ChimneyVent instance in instances)
			{
				ChimneyVent cap = instance;
				if (!Object.op_Implicit((Object)(object)cap))
				{
					continue;
				}
				Vector3 val = ((Component)cap).transform.position - position;
				if (!(((Vector3)(ref val)).sqrMagnitude > 4f))
				{
					Vector3 val2 = ((Component)cap).transform.InverseTransformPoint(position);
					if (ModuleVentRoutes.Choose(cap.Kind, new Vector3(val2.x, val2.y, val2.z), radius, Check, out var target2))
					{
						target = ((Component)cap).transform.TransformPoint(new Vector3(target2.X, target2.Y, target2.Z));
						return true;
					}
				}
				bool Check(Vector3 a, Vector3 b, float r)
				{
					//IL_001d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					//IL_0045: Unknown result type (might be due to invalid IL or missing references)
					//IL_004a: Unknown result type (might be due to invalid IL or missing references)
					//IL_004f: Unknown result type (might be due to invalid IL or missing references)
					//IL_005b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0061: Unknown result type (might be due to invalid IL or missing references)
					//IL_0067: Unknown result type (might be due to invalid IL or missing references)
					//IL_0072: Unknown result type (might be due to invalid IL or missing references)
					//IL_0078: Unknown result type (might be due to invalid IL or missing references)
					//IL_007e: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val3 = ((Component)cap).transform.TransformPoint(new Vector3(a.X, a.Y, a.Z));
					Vector3 val4 = ((Component)cap).transform.TransformPoint(new Vector3(b.X, b.Y, b.Z));
					return clear(new Vector3(val3.x, val3.y, val3.z), new Vector3(val4.x, val4.y, val4.z), r);
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(SmokeSpawner), "Spawn")]
	internal static class OutletEmissionPatch
	{
		internal static void Prefix(SmokeSpawner __instance, out float __state)
		{
			__state = __instance.m_spawnRadius;
			if (NarrowChimneysPlugin.CenterEmission.Value && Object.op_Implicit((Object)(object)__instance.m_smokePrefab) && Object.op_Implicit((Object)(object)__instance.m_smokePrefab.GetComponent<Smoke>()))
			{
				__instance.m_spawnRadius = 0f;
			}
			NarrowChimneysPlugin.Detail($"Emission {((Object)__instance).GetInstanceID()}: originalScatter={__state}; effectiveScatter={__instance.m_spawnRadius}; ventilationRadius={__instance.m_testRadius}; mask={((LayerMask)(ref __instance.m_testMask)).value}; interval={__instance.m_interval}; activeSmoke={Smoke.Instances.Count}");
		}

		internal static void Finalizer(SmokeSpawner __instance, float __state)
		{
			__instance.m_spawnRadius = __state;
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class SceneAwakePatch
	{
		private static void Prefix(ZNetScene __instance)
		{
			StoneMaterials.RegisterSources(__instance.m_prefabs);
			NarrowChimneysPlugin.Registry.BeforeSceneAwake(__instance);
		}

		private static void Postfix(ZNetScene __instance)
		{
			NarrowChimneysPlugin.Registry.AfterSceneAwake(__instance);
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDbAwakePatch
	{
		private static void Postfix(ObjectDB __instance)
		{
			NarrowChimneysPlugin.Registry.RegisterHammer(__instance);
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDbCopyPatch
	{
		private static void Postfix(ObjectDB __instance)
		{
			NarrowChimneysPlugin.Registry.RegisterHammer(__instance);
		}
	}
	[HarmonyPatch(typeof(ZNetView), "Awake")]
	internal static class ChimneyInstancePatch
	{
		private static void Postfix(ZNetView __instance)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (ZNetView.m_forceDisableInit || ZDOMan.instance == null || ModuleCatalog.Find(((Object)__instance).name) == null)
			{
				return;
			}
			try
			{
				ZDO zDO = __instance.GetZDO();
				if (zDO == null)
				{
					throw new InvalidOperationException("Live chimney has no ZDO");
				}
				NarrowChimneysPlugin.Log.LogInfo((object)$"Chimney instance: id={((Object)__instance).GetInstanceID()}; active={((Component)__instance).gameObject.activeInHierarchy}; persistent={zDO.Persistent}; hash={zDO.GetPrefab()}; position={((Component)__instance).transform.position}");
			}
			catch (Exception ex)
			{
				NarrowChimneysPlugin.ErrorOnce("Live chimney validation failed", ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
	internal static class SceneRemoveDiagnostics
	{
		private static void Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				NarrowChimneysPlugin.ErrorOnce("ZNetScene.RemoveObjects failed (exception preserved); inspect full stack and other mods", __exception);
			}
		}
	}
	internal sealed class MasonryAtlas
	{
		internal readonly struct Fragment
		{
			internal readonly int X;

			internal readonly int Y;

			internal readonly int Width;

			internal readonly int Height;

			internal Fragment(int x, int y, int width, int height)
			{
				X = x;
				Y = y;
				Width = width;
				Height = height;
			}
		}

		internal const int Size = 256;

		internal const int Columns = 4;

		internal const int Pitch = 64;

		internal const int Padding = 8;

		internal readonly byte[] Albedo = new byte[262144];

		internal readonly byte[] Normal = new byte[262144];

		internal readonly List<Fragment> Fragments = new List<Fragment>();

		internal static bool[] Coverage(Vector2[] uv, int[] triangles, int width, int height)
		{
			bool[] array = new bool[width * height];
			for (int i = 0; i < triangles.Length; i += 3)
			{
				Vector2 vector = uv[triangles[i]];
				Vector2 vector2 = uv[triangles[i + 1]];
				Vector2 vector3 = uv[triangles[i + 2]];
				float num = Cross(vector2 - vector, vector3 - vector);
				if (Math.Abs(num) < 1E-07f)
				{
					continue;
				}
				int num2 = Math.Max(0, (int)Math.Floor(Math.Min(vector.X, Math.Min(vector2.X, vector3.X)) * (float)width));
				int num3 = Math.Min(width - 1, (int)Math.Ceiling(Math.Max(vector.X, Math.Max(vector2.X, vector3.X)) * (float)width));
				int num4 = Math.Max(0, (int)Math.Floor(Math.Min(vector.Y, Math.Min(vector2.Y, vector3.Y)) * (float)height));
				int num5 = Math.Min(height - 1, (int)Math.Ceiling(Math.Max(vector.Y, Math.Max(vector2.Y, vector3.Y)) * (float)height));
				for (int j = num4; j <= num5; j++)
				{
					for (int k = num2; k <= num3; k++)
					{
						Vector2 vector4 = new Vector2(((float)k + 0.5f) / (float)width, ((float)j + 0.5f) / (float)height);
						float num6 = Cross(vector2 - vector4, vector3 - vector4) / num;
						float num7 = Cross(vector3 - vector4, vector - vector4) / num;
						if (num6 >= -1E-06f && num7 >= -1E-06f && num6 + num7 <= 1.000001f)
						{
							array[j * width + k] = true;
						}
					}
				}
			}
			return array;
			static float Cross(Vector2 p, Vector2 q)
			{
				return p.X * q.Y - p.Y * q.X;
			}
		}

		internal static MasonryAtlas Build(byte[] albedo, byte[] normal, int width, int height, Vector2[] uv, int[] triangles)
		{
			if (albedo.Length != width * height * 4 || normal.Length != albedo.Length)
			{
				throw new ArgumentException("Atlas source maps must have matching dimensions/channels");
			}
			bool[] array = Coverage(uv, triangles, width, height);
			List<Fragment> list = new List<Fragment>();
			int[] array2 = new int[4] { 16, 12, 8, 4 };
			foreach (int num in array2)
			{
				for (int j = 1; j + num < height; j += 2)
				{
					for (int k = 1; k + num < width; k += 2)
					{
						bool flag = true;
						for (int l = j - 1; l <= j + num && flag; l++)
						{
							for (int m = k - 1; m <= k + num; m++)
							{
								int num2 = l * width + m;
								int num3 = num2 * 4;
								if (!array[num2] || albedo[num3 + 3] < 250 || Math.Min(albedo[num3], Math.Min(albedo[num3 + 1], albedo[num3 + 2])) > 215)
								{
									flag = false;
									break;
								}
							}
						}
						if (flag)
						{
							list.Add(new Fragment(k, j, num, num));
						}
					}
				}
				if (list.Count >= 16)
				{
					break;
				}
				list.Clear();
			}
			if (list.Count == 0)
			{
				throw new InvalidOperationException("No safe fragment covered by original mesh UVs; raw native atlas must not be assigned to procedural UVs");
			}
			MasonryAtlas masonryAtlas = new MasonryAtlas();
			HashSet<int> hashSet = new HashSet<int>();
			for (int n = 0; n < 16; n++)
			{
				int num4 = 17 % list.Count;
				if (n > 0)
				{
					double num5 = -1.0;
					for (int num6 = 0; num6 < list.Count; num6++)
					{
						if (hashSet.Contains(num6))
						{
							continue;
						}
						Fragment fragment = list[num6];
						double num7 = double.MaxValue;
						foreach (Fragment fragment2 in masonryAtlas.Fragments)
						{
							double num8 = fragment.X - fragment2.X;
							double num9 = fragment.Y - fragment2.Y;
							num7 = Math.Min(num7, num8 * num8 + num9 * num9);
						}
						if (num7 > num5)
						{
							num5 = num7;
							num4 = num6;
						}
					}
				}
				hashSet.Add(num4);
				Fragment item = list[num4];
				masonryAtlas.Fragments.Add(item);
				for (int num10 = 0; num10 < 64; num10++)
				{
					for (int num11 = 0; num11 < 64; num11++)
					{
						float num12 = Math.Max(0f, Math.Min(1f, (float)(num11 - 8) / 47f));
						float num13 = Math.Max(0f, Math.Min(1f, (float)(num10 - 8) / 47f));
						int num14 = item.X + (int)Math.Round(num12 * (float)(item.Width - 1));
						int num15 = ((item.Y + (int)Math.Round(num13 * (float)(item.Height - 1))) * width + num14) * 4;
						int num16 = ((n / 4 * 64 + num10) * 256 + n % 4 * 64 + num11) * 4;
						for (int num17 = 0; num17 < 4; num17++)
						{
							masonryAtlas.Albedo[num16 + num17] = albedo[num15 + num17];
							masonryAtlas.Normal[num16 + num17] = normal[num15 + num17];
						}
					}
				}
			}
			return masonryAtlas;
		}
	}
	internal enum ModuleKind
	{
		Section,
		Open,
		Grate,
		Roof,
		Arch,
		Bracket
	}
	internal sealed class ModuleDefinition
	{
		internal readonly string Id;

		internal readonly string Name;

		internal readonly string Description;

		internal readonly ModuleKind Kind;

		internal readonly int Stone;

		internal readonly int Iron;

		internal readonly bool Visible;

		internal ModuleDefinition(string id, string name, string description, ModuleKind kind, int stone, int iron = 0, bool visible = true)
		{
			Id = id;
			Name = name;
			Description = description;
			Kind = kind;
			Stone = stone;
			Iron = iron;
			Visible = visible;
		}
	}
	internal static class ModuleCatalog
	{
		internal static readonly ModuleDefinition[] All = new ModuleDefinition[7]
		{
			new ModuleDefinition("NC_Chimney", "Дымоход (старый)", "Совместимая секция 0.4.0. Каменная опора обязательна, кронштейн необязателен.", ModuleKind.Section, 6, 0, visible: false),
			new ModuleDefinition("nc_chimney_section", "Дымоход: секция 1 м", "Полая каменная секция 1×1×1 м. Канал 0,5 м. Соединяйте по высоте; нужна достаточная опора.", ModuleKind.Section, 6),
			new ModuleDefinition("nc_chimney_cap_open", "Дымоход: открытый оголовок", "Каменный обод на четырёх стойках. Открытый верх и боковые проёмы.", ModuleKind.Open, 4),
			new ModuleDefinition("nc_chimney_cap_grate", "Дымоход: оголовок с решёткой", "Каменная решётка с четырьмя сквозными отверстиями шириной 0,325 м; не закрывает проход дыму.", ModuleKind.Grate, 6),
			new ModuleDefinition("nc_chimney_cap_roof", "Дымоход: оголовок с крышей", "Четырёхскатная каменная крыша. Дым выходит через открытые боковые проёмы.", ModuleKind.Roof, 8),
			new ModuleDefinition("nc_chimney_cap_arch", "Дымоход: арочный оголовок", "Каменные арки, крыша и навершие. Настоящие боковые вентиляционные проёмы.", ModuleKind.Arch, 10),
			new ModuleDefinition("nc_chimney_bracket", "Дымоход: каменный кронштейн", "Необязательная опора с железным усилением. Крепится к прочной стене или перекрытию; не заменяет их поддержку.", ModuleKind.Bracket, 6, 2)
		};

		internal static ModuleDefinition Find(string name)
		{
			if (name != null && name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - 7);
			}
			ModuleDefinition[] all = All;
			foreach (ModuleDefinition moduleDefinition in all)
			{
				if (moduleDefinition.Id == name)
				{
					return moduleDefinition;
				}
			}
			return null;
		}
	}
	internal sealed class ModuleGeometry
	{
		internal sealed class Collision
		{
			internal Vector3 Centre;

			internal Vector3 Size;

			internal Quaternion Rotation = Quaternion.Identity;

			internal ChimneyGeometry Mesh;
		}

		internal sealed class Snap
		{
			internal string Name;

			internal Vector3 Position;

			internal Snap(string name, Vector3 position)
			{
				Name = name;
				Position = position;
			}
		}

		internal ChimneyGeometry Model = new ChimneyGeometry();

		internal readonly List<Collision> Collisions = new List<Collision>();

		internal readonly List<Snap> Snaps = new List<Snap>();

		internal float Height;

		internal Vector3 CentreOfMass
		{
			get
			{
				float num = 0f;
				Vector3 zero = Vector3.Zero;
				for (int i = 0; i < Model.Triangles.Count; i += 3)
				{
					Vector3 vector = Model.Vertices[Model.Triangles[i]];
					Vector3 vector2 = Model.Vertices[Model.Triangles[i + 1]];
					Vector3 vector3 = Model.Vertices[Model.Triangles[i + 2]];
					float num2 = Vector3.Dot(vector, Vector3.Cross(vector2, vector3)) / 6f;
					num += num2;
					zero += (vector + vector2 + vector3) * (num2 / 4f);
				}
				if (!(num > 1E-05f))
				{
					return new Vector3(0f, Height / 2f, 0f);
				}
				return zero / num;
			}
		}

		internal static ModuleGeometry Build(ModuleKind kind)
		{
			ModuleGeometry moduleGeometry = new ModuleGeometry();
			int[] array;
			switch (kind)
			{
			case ModuleKind.Section:
			{
				moduleGeometry.Model = ChimneyGeometry.Build();
				moduleGeometry.Height = 1f;
				ChimneyGeometry.WallSpec[] walls = ChimneyGeometry.Walls;
				for (int i = 0; i < walls.Length; i++)
				{
					ChimneyGeometry.WallSpec wallSpec = walls[i];
					moduleGeometry.Collisions.Add(new Collision
					{
						Centre = wallSpec.Centre,
						Size = wallSpec.Size
					});
				}
				break;
			}
			case ModuleKind.Bracket:
			{
				moduleGeometry.Height = 0.8f;
				moduleGeometry.Ring(0.64f, 0.16f, 0.5f, 0.25f);
				moduleGeometry.Box(new Vector3(0f, 0.36f, -0.57f), new Vector3(0.94f, 0.72f, 0.18f));
				array = new int[2] { -1, 1 };
				for (int i = 0; i < array.Length; i++)
				{
					float x = (float)array[i] * 0.375f - 0.075f;
					ChimneyGeometry chimneyGeometry3 = ChimneyGeometry.Prism(new Vector3[3]
					{
						new Vector3(x, 0.12f, -0.48f),
						new Vector3(x, 0.64f, -0.48f),
						new Vector3(x, 0.64f, 0.43f)
					}, new Vector3(0.15f, 0f, 0f), -0.75f);
					moduleGeometry.Model.Add(chimneyGeometry3, Matrix4x4.Identity);
					moduleGeometry.Collisions.Add(new Collision
					{
						Mesh = chimneyGeometry3
					});
				}
				moduleGeometry.Snaps.Add(new Snap("WallAnchor", new Vector3(0f, 0.32f, -0.66f)));
				moduleGeometry.Snaps.Add(new Snap("CeilingAnchor", new Vector3(0f, 0.8f, -0.57f)));
				break;
			}
			default:
			{
				moduleGeometry.Ring(0f, 0.18f, 0.5f, 0.25f);
				float num = ((kind == ModuleKind.Roof) ? 0.18f : 0.16f);
				array = new int[2] { -1, 1 };
				foreach (int num2 in array)
				{
					int[] array2 = new int[2] { -1, 1 };
					foreach (int num3 in array2)
					{
						int num4 = ((kind == ModuleKind.Arch) ? 2 : 3);
						float num5 = ((kind == ModuleKind.Grate) ? 0.16f : 0.18f);
						moduleGeometry.Model.MortarBox(new Vector3((float)num2 * 0.4f, 0.18f + (float)num4 * num / 2f, (float)num3 * 0.4f), new Vector3(num5 - 0.07f, (float)num4 * num, num5 - 0.07f));
						for (int k = 0; k < ((kind == ModuleKind.Arch) ? 2 : 3); k++)
						{
							moduleGeometry.Box(new Vector3((float)num2 * 0.4f, 0.18f + ((float)k + 0.5f) * num, (float)num3 * 0.4f), new Vector3((kind == ModuleKind.Grate) ? 0.16f : 0.18f, num - 0.004f, (kind == ModuleKind.Grate) ? 0.16f : 0.18f));
						}
					}
				}
				if (kind == ModuleKind.Open)
				{
					moduleGeometry.Ring(0.66f, 0.18f, 0.56f, 0.28f);
					moduleGeometry.Height = 0.84f;
					break;
				}
				if (kind == ModuleKind.Grate)
				{
					moduleGeometry.Ring(0.66f, 0.18f, 0.56f, 0.36f);
					moduleGeometry.Box(new Vector3(0f, 0.75f, 0f), new Vector3(0.07f, 0.18f, 0.72f));
					moduleGeometry.Box(new Vector3(0f, 0.75f, 0f), new Vector3(0.72f, 0.18f, 0.07f));
					moduleGeometry.Height = 0.84f;
					break;
				}
				float num6 = ((kind == ModuleKind.Arch) ? 0.94f : 0.72f);
				if (kind == ModuleKind.Arch)
				{
					moduleGeometry.Arches();
				}
				ChimneyGeometry chimneyGeometry = RoofMasonry(num6);
				moduleGeometry.Model.Add(chimneyGeometry, Matrix4x4.Identity);
				moduleGeometry.Collisions.Add(new Collision
				{
					Mesh = chimneyGeometry
				});
				moduleGeometry.Height = num6 + 0.32f;
				if (kind == ModuleKind.Arch)
				{
					moduleGeometry.Box(new Vector3(0f, moduleGeometry.Height + 0.035f, 0f), new Vector3(0.21f, 0.07f, 0.21f));
					ChimneyGeometry chimneyGeometry2 = ChimneyGeometry.Frustum(0.09f, 0.015f, moduleGeometry.Height + 0.07f, moduleGeometry.Height + 0.21f);
					moduleGeometry.Model.Add(chimneyGeometry2, Matrix4x4.Identity);
					moduleGeometry.Collisions.Add(new Collision
					{
						Mesh = chimneyGeometry2
					});
					moduleGeometry.Height += 0.21f;
				}
				break;
			}
			}
			moduleGeometry.Snaps.Add(new Snap("Bottom", Vector3.Zero));
			moduleGeometry.Snaps.Add(new Snap("Top", new Vector3(0f, moduleGeometry.Height, 0f)));
			array = new int[2] { -1, 1 };
			foreach (int num7 in array)
			{
				int[] array2 = new int[2] { -1, 1 };
				foreach (int num8 in array2)
				{
					moduleGeometry.Snaps.Add(new Snap("BottomRim", new Vector3((float)num7 * 0.5f, 0f, (float)num8 * 0.5f)));
					moduleGeometry.Snaps.Add(new Snap("TopRim", new Vector3((float)num7 * 0.5f, moduleGeometry.Height, (float)num8 * 0.5f)));
				}
			}
			return moduleGeometry;
		}

		private void Box(Vector3 centre, Vector3 size, Quaternion? rotation = null)
		{
			Quaternion quaternion = rotation ?? Quaternion.Identity;
			ChimneyGeometry chimneyGeometry = new ChimneyGeometry();
			chimneyGeometry.Brick(Vector3.Zero, size, 0.82f + (float)(Collisions.Count % 5) * 0.045f);
			Model.Add(chimneyGeometry, Matrix4x4.CreateFromQuaternion(quaternion) * Matrix4x4.CreateTranslation(centre));
			Collisions.Add(new Collision
			{
				Centre = centre,
				Size = size,
				Rotation = quaternion
			});
		}

		private void Ring(float bottom, float height, float outer, float inner)
		{
			float num = outer - inner;
			float num2 = (outer + inner) / 2f;
			Model.MortarRing(bottom + 0.03f, height - 0.06f, outer - 0.04f, inner + 0.04f);
			int[] array = new int[2] { -1, 1 };
			foreach (int num3 in array)
			{
				for (int j = 0; j < 3; j++)
				{
					Box(new Vector3(0f - outer + ((float)j + 0.5f) * 2f * outer / 3f, bottom + height / 2f, (float)num3 * num2), new Vector3(2f * outer / 3f, height, num));
				}
				for (int k = 0; k < 2; k++)
				{
					Box(new Vector3((float)num3 * num2, bottom + height / 2f, 0f - inner + ((float)k + 0.5f) * inner), new Vector3(num, height, inner));
				}
			}
		}

		private void Arches()
		{
			for (int i = 0; i < 4; i++)
			{
				Quaternion quaternion = Quaternion.CreateFromAxisAngle(Vector3.UnitY, (float)i * MathF.PI / 2f);
				for (int j = 0; j < 9; j++)
				{
					float angle = (float)j * MathF.PI / 9f + 0.012f;
					float angle2 = (float)(j + 1) * MathF.PI / 9f - 0.012f;
					ChimneyGeometry source = ChimneyGeometry.BevelledPrism(new Vector3[4]
					{
						Point(0.44f, angle),
						Point(0.44f, angle2),
						Point(0.285f, angle2),
						Point(0.285f, angle)
					}, new Vector3(0f, 0f, 0.22f), 0.83f + (float)(j % 4) * 0.05f, 0.01f);
					ChimneyGeometry chimneyGeometry = new ChimneyGeometry();
					chimneyGeometry.Add(source, Matrix4x4.CreateFromQuaternion(quaternion));
					Model.Add(chimneyGeometry, Matrix4x4.Identity);
					Collisions.Add(new Collision
					{
						Mesh = chimneyGeometry
					});
					angle = (float)j * MathF.PI / 9f;
					angle2 = (float)(j + 1) * MathF.PI / 9f;
					ChimneyGeometry source2 = ChimneyGeometry.Prism(new Vector3[4]
					{
						MortarPoint(0.43f, angle),
						MortarPoint(0.43f, angle2),
						MortarPoint(0.295f, angle2),
						MortarPoint(0.295f, angle)
					}, new Vector3(0f, 0f, 0.18f), 0.35f);
					Model.Add(source2, Matrix4x4.CreateFromQuaternion(quaternion));
				}
			}
			static Vector3 MortarPoint(float radius, float num)
			{
				return new Vector3(radius * (float)Math.Cos(num), 0.5f + radius * (float)Math.Sin(num), -0.49f);
			}
			static Vector3 Point(float radius, float num)
			{
				return new Vector3(radius * (float)Math.Cos(num), 0.5f + radius * (float)Math.Sin(num), -0.51f);
			}
		}

		private static ChimneyGeometry RoofMasonry(float bottom)
		{
			ChimneyGeometry chimneyGeometry = ChimneyGeometry.Frustum(0.59f, 0.055f, bottom, bottom + 0.32f);
			Vector3 vector = Vector3.Normalize(new Vector3(0f, 0.535f, -0.32f));
			for (int i = 0; i < 4; i++)
			{
				Matrix4x4 transform = Matrix4x4.CreateRotationY((float)i * MathF.PI / 2f);
				for (int j = 0; j < 2; j++)
				{
					for (int k = 0; k < 2; k++)
					{
						float num = (float)j / 2f + 0.014f;
						float num2 = (float)(j + 1) / 2f - 0.014f;
						float num3 = 0.59f - 0.535f * num;
						float num4 = 0.59f - 0.535f * num2;
						float num5 = (float)(-1 + k) + 0.014f;
						float num6 = (float)k - 0.014f;
						Vector3[] surface = new Vector3[4]
						{
							new Vector3(num5 * num3, bottom + 0.32f * num, 0f - num3) + vector * 0.006f,
							new Vector3(num6 * num3, bottom + 0.32f * num, 0f - num3) + vector * 0.006f,
							new Vector3(num6 * num4, bottom + 0.32f * num2, 0f - num4) + vector * 0.006f,
							new Vector3(num5 * num4, bottom + 0.32f * num2, 0f - num4) + vector * 0.006f
						};
						chimneyGeometry.Add(ChimneyGeometry.BevelledPrism(surface, -vector * 0.04f, 0.84f + (float)((j + k) % 3) * 0.06f, 0.006f), transform);
					}
				}
			}
			return chimneyGeometry;
		}
	}
	internal static class ModuleVentRoutes
	{
		internal static Vector3[] Exits(ModuleKind kind)
		{
			if (kind != ModuleKind.Grate)
			{
				return new Vector3[4]
				{
					new Vector3(-0.92f, 0.42f, 0f),
					new Vector3(0.92f, 0.42f, 0f),
					new Vector3(0f, 0.42f, -0.92f),
					new Vector3(0f, 0.42f, 0.92f)
				};
			}
			return new Vector3[4]
			{
				new Vector3(-0.1975f, 1.12f, -0.1975f),
				new Vector3(0.1975f, 1.12f, -0.1975f),
				new Vector3(-0.1975f, 1.12f, 0.1975f),
				new Vector3(0.1975f, 1.12f, 0.1975f)
			};
		}

		internal static bool Choose(ModuleKind kind, Vector3 position, float radius, Func<Vector3, Vector3, float, bool> clear, out Vector3 target)
		{
			target = position;
			if (kind != ModuleKind.Grate && kind != ModuleKind.Roof && kind != ModuleKind.Arch)
			{
				return false;
			}
			if (Math.Abs(position.X) > 1f || Math.Abs(position.Z) > 1f || position.Y < -0.2f || position.Y > 1.2f)
			{
				return false;
			}
			if (kind != ModuleKind.Grate && Math.Max(Math.Abs(position.X), Math.Abs(position.Z)) >= 0.72f + radius && position.Y >= 0.3f)
			{
				return false;
			}
			if (kind == ModuleKind.Grate && position.Y >= 0.86f + radius)
			{
				return false;
			}
			if (position.Y < 0.3f)
			{
				target = new Vector3(0f, 0.38f, 0f);
				return clear(position, target, radius);
			}
			float num = float.MaxValue;
			Vector3[] array;
			if (kind == ModuleKind.Grate)
			{
				array = Exits(kind);
				for (int i = 0; i < array.Length; i++)
				{
					Vector3 vector = array[i];
					if (Math.Abs(position.X - vector.X) + Math.Abs(position.Z - vector.Z) < 0.035f && clear(position, vector, radius))
					{
						target = vector;
						return true;
					}
				}
			}
			array = Exits(kind);
			for (int i = 0; i < array.Length; i++)
			{
				Vector3 vector2 = array[i];
				Vector3 vector3 = ((kind == ModuleKind.Grate) ? new Vector3(vector2.X, 0.4f, vector2.Z) : vector2);
				Vector3 vector4 = vector3;
				if (clear(position, vector4, radius) && clear(vector3, vector2, radius))
				{
					float num2 = Vector3.Distance(position, vector4);
					if (num2 < num)
					{
						num = num2;
						target = vector4;
					}
				}
			}
			return num < float.MaxValue;
		}
	}
	internal static class PassagePlanner
	{
		internal static bool Choose(Vector3 start, float radius, Func<Vector3, Vector3, float, bool> clearSegment, Func<Vector3, float, float> riseClearance, out Vector3 target)
		{
			target = start;
			float num = float.NegativeInfinity;
			for (int i = 0; i < 9; i++)
			{
				int num2 = ((i != 0) ? ((i - 1) % 3 - 1) : 0);
				int num3 = ((i != 0) ? ((i - 1) / 3 - 1) : 0);
				if (i > 4)
				{
					num2 = i % 3 - 1;
					num3 = i / 3 - 1;
				}
				Vector3 vector = start + new Vector3((float)num2 * 0.18f, 0.3f, (float)num3 * 0.18f);
				if (clearSegment(start, vector, radius))
				{
					float num4 = riseClearance(vector, radius) - 0.6f * (float)(num2 * num2 + num3 * num3) * 0.18f;
					if (num4 > num)
					{
						num = num4;
						target = vector;
					}
					if (i == 0 && num4 >= 1.5f)
					{
						return true;
					}
				}
			}
			return !float.IsNegativeInfinity(num);
		}
	}
	[BepInPlugin("com.community.narrowchimneys", "Narrow Chimneys", "0.5.3.1")]
	public sealed class NarrowChimneysPlugin : BaseUnityPlugin
	{
		internal const string Id = "com.community.narrowchimneys";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> NarrowPassages;

		internal static ConfigEntry<bool> BuildableChimney;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<float> SmokeScale;

		internal static ConfigEntry<float> ExitRadius;

		internal static ConfigEntry<bool> Guidance;

		internal static ConfigEntry<bool> Diagnostics;

		internal static ConfigEntry<bool> ResizeOutlet;

		internal static ConfigEntry<bool> CenterEmission;

		internal static ConfigEntry<bool> ResizeCloud;

		internal static ConfigEntry<float> VisualSize;

		internal static ConfigEntry<float> UpwardStrength;

		internal static ConfigEntry<float> ProbeInterval;

		internal static ConfigEntry<float> PhysicalRadius;

		internal static ConfigEntry<int> QueryBudget;

		internal static ConfigEntry<float> VisualVolume;

		internal static ConfigEntry<float> VisualDensity;

		internal static ConfigEntry<float> VisualOpacity;

		internal static ConfigEntry<float> EmissionFrequency;

		internal static ConfigEntry<float> MaximumSpawns;

		internal static ConfigEntry<int> MaximumSmoke;

		internal static ConfigEntry<bool> ClipVisuals;

		internal static ConfigEntry<bool> SupportWarnings;

		internal static ConfigEntry<bool> ExpandVolume;

		internal static ConfigEntry<bool> EnhanceDensity;

		internal static ConfigEntry<bool> AdjustFrequency;

		internal static ConfigEntry<bool> LimitEmission;

		internal static ConfigEntry<bool> RouteCheck;

		internal static ConfigEntry<bool> UseProfiles;

		internal static ConfigEntry<bool> ProfileFrequency;

		internal static ConfigEntry<bool> OverrideSpawnRate;

		internal static ConfigEntry<float> SpawnRateMultiplier;

		internal static ConfigEntry<float> RouteInterval;

		internal static ConfigEntry<float> RouteHeight;

		internal static ConfigEntry<float> RouteGrace;

		internal static ConfigEntry<float> ProfileCloudScale;

		internal static ConfigEntry<float> ProfileDensityScale;

		internal static ConfigEntry<int> RouteBudget;

		internal static ConfigEntry<SmokeProfile> Profile;

		internal static ConfigEntry<StoneMaterialMode> MaterialMode;

		internal static ConfigEntry<bool> SurfaceColors;

		internal static ChimneyRegistry Registry;

		private Harmony harmony;

		private static readonly HashSet<string> reported = new HashSet<string>();

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Expected O, but got Unknown
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Expected O, but got Unknown
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Expected O, but got Unknown
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Expected O, but got Unknown
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Expected O, but got Unknown
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Expected O, but got Unknown
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Expected O, but got Unknown
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Expected O, but got Unknown
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Expected O, but got Unknown
			//IL_0546: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Expected O, but got Unknown
			//IL_0583: Unknown result type (might be due to invalid IL or missing references)
			//IL_058d: Expected O, but got Unknown
			//IL_05e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ea: Expected O, but got Unknown
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_065d: Expected O, but got Unknown
			//IL_0690: Unknown result type (might be due to invalid IL or missing references)
			//IL_069a: Expected O, but got Unknown
			//IL_077a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0784: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			NarrowPassages = ((BaseUnityPlugin)this).Config.Bind<bool>("Smoke", "EnableNarrowPassages", true, "Уменьшать физические коллайдеры новых частиц дыма, включая проходы между обычными блоками. Независимо от визуальных настроек.");
			SmokeScale = ((BaseUnityPlugin)this).Config.Bind<float>("Smoke", "ColliderRadiusMultiplier", 0.25f, new ConfigDescription("Множитель исходных размеров коллайдера. После изменения перезапустите игру.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			PhysicalRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Smoke", "PhysicalRadius", 0.1f, new ConfigDescription("Максимальный локальный радиус сферы/капсулы в метрах. Рассчитывается от исходного размера; направление не уменьшает его ради прохождения препятствий.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.03f, 0.5f), Array.Empty<object>()));
			ExitRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Smoke", "OutletTestRadius", 0.1f, new ConfigDescription("Максимальный радиус проверки выхода в метрах. Не увеличивает меньший исходный радиус. Применяется при ResizeOutlet=true.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.5f), Array.Empty<object>()));
			ResizeOutlet = ((BaseUnityPlugin)this).Config.Bind<bool>("Ventilation", "ResizeOutlet", true, "Изменять радиус проверки выхода независимо от движения частиц. Проверяется место выпуска, а не весь путь до неба.");
			RouteCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Ventilation", "CheckEnclosedRoute", true, "Дополнительно искать вентиляционный маршрут. Только подтверждённый закрытый объём добавляет блокировку; неизвестный результат оставляет штатные проверки. False — поведение 0.4.0/0.5.0.");
			RouteInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Ventilation", "RouteInterval", 2f, new ConfigDescription("Пауза между проверками источника в секундах; открытие выхода обнаруживается также у неактивного источника.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
			RouteHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Ventilation", "RouteHeight", 8f, new ConfigDescription("Дальность поиска свободного подъёма в метрах. Выход за пределы поиска не считается стеной.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 16f), Array.Empty<object>()));
			RouteGrace = ((BaseUnityPlugin)this).Config.Bind<float>("Ventilation", "ClosedGrace", 3f, new ConfigDescription("Задержка подтверждённой блокировки в секундах. Штатные проверки Fireplace/SmokeSpawner сохраняются.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			RouteBudget = ((BaseUnityPlugin)this).Config.Bind<int>("Ventilation", "QueriesPerFrame", 128, new ConfigDescription("Общий бюджет физических запросов проверки вентиляции за кадр, отдельно от движения частиц.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 512), Array.Empty<object>()));
			CenterEmission = ((BaseUnityPlugin)this).Config.Bind<bool>("Emission", "CenterEmission", true, "Убрать случайное смещение только при создании Smoke. Исходный разброс затем восстанавливается; проверка вентиляции не меняется.");
			Guidance = ((BaseUnityPlugin)this).Config.Bind<bool>("Motion", "EnableGuidance", true, "Направлять Rigidbody к доступному пути вверх; в новых крытых оголовках — к боковым проёмам. Коллизии сохраняются.");
			UpwardStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Motion", "UpwardStrength", 3f, new ConfigDescription("Сила реакции направления в секунду. Ноль отключает дополнительную силу; телепортации нет.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 8f), Array.Empty<object>()));
			ProbeInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Motion", "ProbeInterval", 0.35f, new ConfigDescription("Интервал локального поиска для одной частицы в секундах. Большие значения снижают нагрузку.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 2f), Array.Empty<object>()));
			QueryBudget = ((BaseUnityPlugin)this).Config.Bind<int>("Motion", "QueryBudgetPerFrame", 192, new ConfigDescription("Общий предел физических запросов направления за кадр для всех частиц.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 1024), Array.Empty<object>()));
			VisualSize = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "CloudSize", 0.45f, new ConfigDescription("Базовый визуальный диаметр облака в метрах. Не изменяет коллайдер. Для сравнения с 0.4.0 используйте 0.45.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 4f), Array.Empty<object>()));
			ResizeCloud = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ResizeCloud", true, "Применять CloudSize независимо от физики и выпуска. False возвращает исходный размер рендера игры (обычно 4 м), а не размер 0.4.0.");
			UseProfiles = ((BaseUnityPlugin)this).Config.Bind<bool>("Presentation", "UseProfiles", true, "Применять профиль только к визуальному дыму и частоте. False использует отдельные параметры 0.5.0; физика и вентиляция профилем не меняются.");
			Profile = ((BaseUnityPlugin)this).Config.Bind<SmokeProfile>("Presentation", "Profile", SmokeProfile.Dense, "Original — штатный размер/частота; Dense — диаметр до 3 м вне стен, плотность 1.2, частота 4×; Performance — меньшая визуальная нагрузка. Физика не меняется; реальный поток ограничен бюджетом и локальными препятствиями.");
			ProfileCloudScale = ((BaseUnityPlugin)this).Config.Bind<float>("Presentation", "CloudScale", 1f, new ConfigDescription("Независимый множитель размера выбранного профиля.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 2f), Array.Empty<object>()));
			ProfileDensityScale = ((BaseUnityPlugin)this).Config.Bind<float>("Presentation", "DensityScale", 1f, new ConfigDescription("Независимый множитель плотности выбранного профиля; Opacity применяется отдельно.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			ProfileFrequency = ((BaseUnityPlugin)this).Config.Bind<bool>("Emission", "UseProfileFrequency", true, "Применять частоту профиля при UseProfiles=true. False оставляет штатную частоту, если отдельно не включён AdjustFrequency.");
			OverrideSpawnRate = ((BaseUnityPlugin)this).Config.Bind<bool>("Emission", "OverrideSpawnRate", false, "Использовать точный SmokeSpawnRateMultiplier вместо частоты профиля и старого FrequencyMultiplier. Позволяет независимо сравнить 1×–5×. False возвращает профиль/старые настройки; собственный генератор дыма не запускается.");
			SpawnRateMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Emission", "SmokeSpawnRateMultiplier", 4f, new ConfigDescription("Точный множитель исходной частоты при OverrideSpawnRate=true: 1 — исходная, 2 — двойная, 3 — тройная, 4 — четырёхкратная, 5 — экспериментальная. Для исходного интервала 0.5 с: 0.5/0.25/0.167/0.125/0.1 с. Это попытки, а не гарантия выпуска: сохраняются блокировка и лимит физических объектов. Не умножается повторно на Dense.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
			VisualVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "VolumeMultiplier", 2f, new ConfigDescription("Множитель визуального диаметра. Действует при ExpandVolume=true; коллайдеры не меняются.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
			ExpandVolume = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ExpandVolume", true, "Включить дополнительный объём версии 0.5.0. False оставляет только CloudSize, как в 0.4.0.");
			EnhanceDensity = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "AdjustDensityAndOpacity", true, "Применять Density и Opacity. False возвращает исходную прозрачность рендера, как в 0.4.0. Нейтральные значения отдельных множителей: 1.");
			VisualDensity = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "Density", 1.6f, new ConfigDescription("Оптическая плотность исходной текстуры дыма. Не создаёт объекты и не меняет коллайдеры; 1 — нейтральное значение.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2.5f), Array.Empty<object>()));
			VisualOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "Opacity", 1f, new ConfigDescription("Множитель непрозрачности. 1 не ослабляет дым; меньшее значение делает его прозрачнее. Независимо от физического размера и частоты.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			ClipVisuals = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "LimitCloudNearSolids", true, "Ограничивать размер облака свободным объёмом у стен. До 64 дополнительных запросов в кадре. False отключает только это визуальное ограничение.");
			EmissionFrequency = ((BaseUnityPlugin)this).Config.Bind<float>("Emission", "FrequencyMultiplier", 1f, new ConfigDescription("Старый множитель частоты при AdjustFrequency=true и OverrideSpawnRate=false. Совмещается с профилем, суммарно не более 5×, ускоренный интервал не ниже 0.1 с. Для точного сравнения 1×–5× используйте SmokeSpawnRateMultiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			AdjustFrequency = ((BaseUnityPlugin)this).Config.Bind<bool>("Emission", "AdjustFrequency", false, "Менять частоту выпуска. False оставляет точный исходный интервал источника, как в 0.4.0.");
			LimitEmission = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "LimitEmission", true, "Включить новые ограничения количества объектов и скорости создания. False возвращает исходный выпуск 0.4.0. Ограничение нагрузки не объявляет свободную вентиляцию заблокированной.");
			MaximumSmoke = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "MaximumActiveSmoke", 96, new ConfigDescription("Предел активных Smoke, включая исчезающие объекты. Буфер игры рассчитан на 100 частиц; предел применяется только при LimitEmission=true.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 96), Array.Empty<object>()));
			MaximumSpawns = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "MaximumSpawnsPerSecond", 24f, new ConfigDescription("Общий бюджет создания физических объектов в секунду; запас до 10 для одновременных выпусков. При перегрузке источники получают очередь разрешений; отложенные частицы не создаются. Одиночный штатный источник с интервалом 0.5 с не ограничивается значением 12.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			SupportWarnings = ((BaseUnityPlugin)this).Config.Bind<bool>("Building", "SupportWarnings", true, "Показывать предварительное предупреждение о слабой опоре. Не запрещает размещение и не заменяет WearNTear; кронштейн необязателен.");
			MaterialMode = ((BaseUnityPlugin)this).Config.Bind<StoneMaterialMode>("Building", "StoneMaterial", StoneMaterialMode.DetailedPBR, "Сохранённый ключ совместимости 0.5.2. В 0.5.3 оба значения используют проверенный Custom/Piece и безопасный согласованный атлас; исходный атлас stone не назначается процедурным UV напрямую. Требуется перезапуск.");
			SurfaceColors = ((BaseUnityPlugin)this).Config.Bind<bool>("Building", "SurfaceDiagnosticColors", false, "Визуальная диагностика: камень оранжевый, раствор бирюзовый, внутренняя облицовка синяя, металл фиолетовый; зелёные контуры показывают открытые проёмы. Выключено по умолчанию, не меняет коллайдеры, опоры или дым. Переключение отображается примерно за 0.5 с.");
			Diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "Diagnostics", false, "Маркеры физического центра дыма (до 12 частиц), препятствий и диагностика поддержки, здоровья и разрушения.");
			BuildableChimney = ((BaseUnityPlugin)this).Config.Bind<bool>("Building", "EnableBuildableChimney", true, "Регистрировать модули, необязательный кронштейн и старый NC_Chimney. Не отключайте при загрузке мира с такими деталями.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "Verbose", false, "Подробный журнал коллайдеров и источников. Может создавать много записей.");
			((BaseUnityPlugin)this).Config.Save();
			Registry = new ChimneyRegistry();
			harmony = new Harmony("com.community.narrowchimneys");
			try
			{
				harmony.PatchAll(typeof(NarrowChimneysPlugin).Assembly);
				Log.LogInfo((object)$"Loaded 0.5.3.1 experimental; profile={Profile.Value}, profiles={UseProfiles.Value},