Decompiled source of TerrainMistile v1.0.8

TerrainMistile.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TerrainMistile")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("TerrainMistile")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.8")]
[assembly: InternalsVisibleTo("TerrainMistile.Tests")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.8.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace TerrainMistile
{
	[BepInPlugin("sighsorry.TerrainMistile", "TerrainMistile", "1.0.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class TerrainMistilePlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string ModName = "TerrainMistile";

		internal const string ModVersion = "1.0.8";

		internal const string Author = "sighsorry";

		internal const string DisplayNameToken = "$terrainmistile_creature_name";

		internal const string EnforcerDisplayNameToken = "$terrainmistile_enforcer_name";

		internal const string CompendiumTopicToken = "$terrainmistile_compendium_topic";

		internal const string CheckCompendiumMessageToken = "$terrainmistile_message_check_compendium";

		private const string ModGUID = "sighsorry.TerrainMistile";

		private const string ConfigFileName = "sighsorry.TerrainMistile.cfg";

		private const string SpawnRulesFileName = "TerrainMistile.yml";

		private static readonly string ConfigFileFullPath = Path.Combine(Paths.ConfigPath, "sighsorry.TerrainMistile.cfg");

		private static readonly string SpawnRulesFileFullPath = Path.Combine(Paths.ConfigPath, "TerrainMistile.yml");

		private readonly Harmony _harmony = new Harmony("sighsorry.TerrainMistile");

		public static readonly ManualLogSource TerrainMistileLogger = Logger.CreateLogSource("TerrainMistile");

		private static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.TerrainMistile")
		{
			DisplayName = "TerrainMistile",
			CurrentVersion = "1.0.8",
			MinimumRequiredVersion = "1.0.8",
			ModRequired = true
		};

		private FileSystemWatcher _watcher;

		private FileSystemWatcher _spawnRulesWatcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private DateTime _lastSpawnRulesReloadTime;

		private string? _lastConfigFileText;

		private string? _appliedSpawnRulesYamlText;

		private const long RELOAD_DELAY = 10000000L;

		private static ConfigEntry<Toggle> _terrainMistileEnabled = null;

		private static ConfigEntry<Toggle> _serverConfigLocked = null;

		private static CustomSyncedValue<string> SpawnRulesYaml = null;

		internal static bool SpawningEnabled
		{
			get
			{
				ConfigEntry<Toggle> terrainMistileEnabled = _terrainMistileEnabled;
				if (terrainMistileEnabled == null)
				{
					return true;
				}
				return terrainMistileEnabled.Value != Toggle.Off;
			}
		}

		public void Awake()
		{
			TerrainMistileLocalization.Load(TerrainMistileLogger);
			TerrainCompAccess.Initialize();
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_terrainMistileEnabled = config("1 - General", "Enable TerrainMistile", Toggle.On, "Globally enables new TerrainMistile spawns. Existing TerrainMistiles are not removed when disabled.");
			_serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
			ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
			RemoveObsoleteDisplayNameConfig();
			TerrainMistileSpawnRules.Initialize(TerrainMistileLogger);
			TerrainMistileSpawnRules.EnsureFileExists(SpawnRulesFileFullPath);
			SpawnRulesYaml = new CustomSyncedValue<string>(ConfigSync, "SpawnRulesYaml", string.Empty);
			SpawnRulesYaml.ValueChanged += OnSyncedSpawnRulesYamlChanged;
			ConfigSync.SourceOfTruthChanged += OnSourceOfTruthChanged;
			ApplySpawnRulesYaml(File.ReadAllText(SpawnRulesFileFullPath), "local fallback");
			TerrainMistilePrefab.RegisterPrefabHook();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			TerrainMistileExternalTerrainCompat.Initialize(TerrainMistileLogger, _harmony);
			SetupWatcher();
			if (ConfigSync.IsSourceOfTruth)
			{
				PushLocalSpawnRulesYamlToSync();
			}
			((BaseUnityPlugin)this).Config.Save();
			_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			TerrainMistilePrefab.UnregisterPrefabHook();
			SpawnRulesYaml.ValueChanged -= OnSyncedSpawnRulesYamlChanged;
			ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged;
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
			_spawnRulesWatcher?.Dispose();
		}

		private void Update()
		{
			TerrainMistileSystem.UpdateResetEffectRpcRegistration();
			TerrainMistileSystem.UpdateProtectedTerrainAreaSync();
			TerrainMistileExternalTerrainCompat.Update();
			if (TerrainMistileExternalTerrainCompat.ConsumeBiomeMappingRefreshRequest() && _appliedSpawnRulesYamlText != null)
			{
				ApplySpawnRulesYaml(_appliedSpawnRulesYamlText, "Expand World Data biome refresh");
			}
			TerrainMistileSystem.UpdatePersistentTerrainSpawns();
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, "sighsorry.TerrainMistile.cfg");
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
			_spawnRulesWatcher = new FileSystemWatcher(Paths.ConfigPath, "TerrainMistile.yml");
			_spawnRulesWatcher.Changed += ReadSpawnRulesValues;
			_spawnRulesWatcher.Created += ReadSpawnRulesValues;
			_spawnRulesWatcher.Renamed += ReadSpawnRulesValues;
			_spawnRulesWatcher.IncludeSubdirectories = false;
			_spawnRulesWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_spawnRulesWatcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					TerrainMistileLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					string b = File.ReadAllText(ConfigFileFullPath);
					if (string.Equals(_lastConfigFileText, b, StringComparison.Ordinal))
					{
						return;
					}
					SaveWithRespectToConfigSet(reload: true);
					_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
					TerrainMistileLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					TerrainMistileLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
			_lastConfigReloadTime = now;
		}

		private void ReadSpawnRulesValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastSpawnRulesReloadTime.Ticks < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(SpawnRulesFileFullPath))
				{
					TerrainMistileLogger.LogWarning((object)"TerrainMistile spawn rules YAML does not exist. Skipping reload.");
					return;
				}
				if (!ConfigSync.IsSourceOfTruth)
				{
					return;
				}
				try
				{
					PushLocalSpawnRulesYamlToSync();
					TerrainMistileLogger.LogInfo((object)"TerrainMistile spawn rules YAML reload complete.");
				}
				catch (Exception ex)
				{
					TerrainMistileLogger.LogError((object)("Error reloading TerrainMistile spawn rules YAML: " + ex.Message));
				}
			}
			_lastSpawnRulesReloadTime = now;
		}

		private void PushLocalSpawnRulesYamlToSync()
		{
			TerrainMistileSpawnRules.EnsureFileExists(SpawnRulesFileFullPath);
			string text = File.ReadAllText(SpawnRulesFileFullPath);
			if ((!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal) || !string.Equals(SpawnRulesYaml.Value ?? string.Empty, text, StringComparison.Ordinal)) && ApplySpawnRulesYaml(text, "local file") && !string.Equals(SpawnRulesYaml.Value ?? string.Empty, text, StringComparison.Ordinal))
			{
				SpawnRulesYaml.Value = text;
			}
		}

		private void OnSyncedSpawnRulesYamlChanged()
		{
			string text = SpawnRulesYaml.Value ?? string.Empty;
			if (!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal))
			{
				ApplySpawnRulesYaml(text, ConfigSync.IsSourceOfTruth ? "local sync" : "server sync");
			}
		}

		private void OnSourceOfTruthChanged(bool isSourceOfTruth)
		{
			if (isSourceOfTruth)
			{
				PushLocalSpawnRulesYamlToSync();
				return;
			}
			string text = SpawnRulesYaml.Value ?? string.Empty;
			if (!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal))
			{
				ApplySpawnRulesYaml(text, "server sync");
			}
		}

		private bool ApplySpawnRulesYaml(string yaml, string source)
		{
			TerrainMistileExpandWorldDataBiomeCompat.InvalidateFileMapping();
			if (!TerrainMistileSpawnRules.LoadYamlText(yaml, source))
			{
				return false;
			}
			_appliedSpawnRulesYamlText = yaml;
			TerrainMistileSystem.InvalidateSpawnRuleState();
			TerrainMistilePrefab.RefreshRegisteredPrefabVisuals();
			return true;
		}

		private static string? ReadFileTextIfExists(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			return File.ReadAllText(path);
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void RemoveObsoleteDisplayNameConfig()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition("2 - Display", "Display Name");
			((BaseUnityPlugin)this).Config.Bind<string>(val, string.Empty, (ConfigDescription)null);
			((BaseUnityPlugin)this).Config.Remove(val);
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}
	}
	internal static class TerrainCompAccess
	{
		private delegate void SaveTerrainComp(TerrainComp terrainComp);

		private sealed class Bindings
		{
			internal readonly FieldInfo Instances = RequireField("s_instances", typeof(List<TerrainComp>), isStatic: true);

			internal readonly FieldInfo Heightmap = RequireField("m_hmap", typeof(Heightmap));

			internal readonly FieldInfo Operations = RequireField("m_operations", typeof(int));

			internal readonly FieldInfo ModifiedHeight = RequireField("m_modifiedHeight", typeof(bool[]));

			internal readonly FieldInfo LevelDelta = RequireField("m_levelDelta", typeof(float[]));

			internal readonly FieldInfo SmoothDelta = RequireField("m_smoothDelta", typeof(float[]));

			internal readonly FieldInfo ModifiedPaint = RequireField("m_modifiedPaint", typeof(bool[]));

			internal readonly FieldInfo PaintMask = RequireField("m_paintMask", typeof(Color[]));

			internal readonly FieldInfo LastOperationPoint = RequireField("m_lastOpPoint", typeof(Vector3));

			internal readonly FieldInfo LastOperationRadius = RequireField("m_lastOpRadius", typeof(float));

			internal readonly MethodInfo Save = RequireMethod("Save");
		}

		internal readonly struct ScanData
		{
			internal readonly Heightmap Heightmap;

			internal readonly int VertexWidth;

			internal readonly int Operations;

			internal readonly bool[] ModifiedHeight;

			internal readonly float[] LevelDelta;

			internal readonly float[] SmoothDelta;

			internal ScanData(Heightmap heightmap, int vertexWidth, int operations, bool[] modifiedHeight, float[] levelDelta, float[] smoothDelta)
			{
				Heightmap = heightmap;
				VertexWidth = vertexWidth;
				Operations = operations;
				ModifiedHeight = modifiedHeight;
				LevelDelta = levelDelta;
				SmoothDelta = smoothDelta;
			}
		}

		internal readonly struct ResetData
		{
			internal readonly ScanData Scan;

			internal readonly bool[] ModifiedPaint;

			internal readonly Color[] PaintMask;

			internal ResetData(ScanData scan, bool[] modifiedPaint, Color[] paintMask)
			{
				Scan = scan;
				ModifiedPaint = modifiedPaint;
				PaintMask = paintMask;
			}
		}

		private static FieldRef<List<TerrainComp>> _instances;

		private static FieldRef<TerrainComp, Heightmap> _heightmap;

		private static FieldRef<TerrainComp, int> _operations;

		private static FieldRef<TerrainComp, bool[]> _modifiedHeight;

		private static FieldRef<TerrainComp, float[]> _levelDelta;

		private static FieldRef<TerrainComp, float[]> _smoothDelta;

		private static FieldRef<TerrainComp, bool[]> _modifiedPaint;

		private static FieldRef<TerrainComp, Color[]> _paintMask;

		private static FieldRef<TerrainComp, Vector3> _lastOperationPoint;

		private static FieldRef<TerrainComp, float> _lastOperationRadius;

		private static SaveTerrainComp _save;

		private static bool _initialized;

		internal static List<TerrainComp> Instances => _instances.Invoke();

		internal static void Initialize()
		{
			if (!_initialized)
			{
				Bindings bindings = ResolveBindings();
				_instances = AccessTools.StaticFieldRefAccess<List<TerrainComp>>(bindings.Instances);
				_heightmap = AccessTools.FieldRefAccess<TerrainComp, Heightmap>(bindings.Heightmap);
				_operations = AccessTools.FieldRefAccess<TerrainComp, int>(bindings.Operations);
				_modifiedHeight = AccessTools.FieldRefAccess<TerrainComp, bool[]>(bindings.ModifiedHeight);
				_levelDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>(bindings.LevelDelta);
				_smoothDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>(bindings.SmoothDelta);
				_modifiedPaint = AccessTools.FieldRefAccess<TerrainComp, bool[]>(bindings.ModifiedPaint);
				_paintMask = AccessTools.FieldRefAccess<TerrainComp, Color[]>(bindings.PaintMask);
				_lastOperationPoint = AccessTools.FieldRefAccess<TerrainComp, Vector3>(bindings.LastOperationPoint);
				_lastOperationRadius = AccessTools.FieldRefAccess<TerrainComp, float>(bindings.LastOperationRadius);
				_save = AccessTools.MethodDelegate<SaveTerrainComp>(bindings.Save, (object)null, false);
				_initialized = true;
			}
		}

		internal static bool TryCaptureScanData(TerrainComp terrainComp, out ScanData data)
		{
			Heightmap val = _heightmap.Invoke(terrainComp);
			bool[] array = _modifiedHeight.Invoke(terrainComp);
			float[] array2 = _levelDelta.Invoke(terrainComp);
			float[] array3 = _smoothDelta.Invoke(terrainComp);
			int num = (Object.op_Implicit((Object)(object)val) ? (val.m_width + 1) : 0);
			long num2 = (long)num * (long)num;
			if (!Object.op_Implicit((Object)(object)val) || num <= 0 || num2 > int.MaxValue || array == null || array2 == null || array3 == null || array.Length < num2 || array2.Length < num2 || array3.Length < num2)
			{
				data = default(ScanData);
				return false;
			}
			data = new ScanData(val, num, _operations.Invoke(terrainComp), array, array2, array3);
			return true;
		}

		internal static bool TryCaptureResetData(TerrainComp terrainComp, bool includePaint, out ResetData data)
		{
			if (!TryCaptureScanData(terrainComp, out var data2))
			{
				data = default(ResetData);
				return false;
			}
			if (!includePaint)
			{
				data = new ResetData(data2, Array.Empty<bool>(), Array.Empty<Color>());
				return true;
			}
			bool[] array = _modifiedPaint.Invoke(terrainComp);
			Color[] array2 = _paintMask.Invoke(terrainComp);
			int num = data2.VertexWidth * data2.VertexWidth;
			if (array == null || array2 == null || array.Length < num || array2.Length < num)
			{
				data = default(ResetData);
				return false;
			}
			data = new ResetData(data2, array, array2);
			return true;
		}

		internal static void CommitReset(TerrainComp terrainComp, Vector3 center, float radius)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			_operations.Invoke(terrainComp)++;
			_lastOperationPoint.Invoke(terrainComp) = center;
			_lastOperationRadius.Invoke(terrainComp) = radius;
			_save(terrainComp);
		}

		private static Bindings ResolveBindings()
		{
			return new Bindings();
		}

		private static FieldInfo RequireField(string name, Type fieldType, bool isStatic = false)
		{
			FieldInfo fieldInfo = typeof(TerrainComp).GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(TerrainComp).FullName, name);
			if (fieldInfo.FieldType != fieldType || fieldInfo.IsStatic != isStatic)
			{
				throw new InvalidOperationException(typeof(TerrainComp).FullName + "." + name + " no longer matches the expected field contract.");
			}
			return fieldInfo;
		}

		private static MethodInfo RequireMethod(string name)
		{
			MethodInfo methodInfo = typeof(TerrainComp).GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) ?? throw new MissingMethodException(typeof(TerrainComp).FullName, name);
			if (methodInfo.IsStatic || methodInfo.ReturnType != typeof(void) || methodInfo.GetParameters().Length != 0)
			{
				throw new InvalidOperationException(typeof(TerrainComp).FullName + "." + name + " no longer matches the expected method contract.");
			}
			return methodInfo;
		}
	}
	public class TerrainMistileBehaviour : MonoBehaviour
	{
		internal const string SelfDestructZdoKey = "TerrainMistileSelfDestruct";

		internal const string TerrainSourceZdoKey = "TerrainMistileSource";

		internal const string TerrainSourceSetZdoKey = "TerrainMistileSourceSet";

		internal const string ResetRadiusZdoKey = "TerrainMistileResetRadius";

		internal const string VisualColorZdoKey = "TerrainMistileVisualColor";

		internal const string VisualColorSetZdoKey = "TerrainMistileVisualColorSet";

		private const float TerrainImpactDistance = 1.25f;

		private const float TerrainImpactHeight = 0.85f;

		private const float TerrainTargetGroundOffset = 0.15f;

		private const float VisualSyncRetryInterval = 0.25f;

		private const float EnforcerTargetSearchInterval = 1f;

		private const float EnforcerTargetSearchTimeout = 10f;

		private Character _character;

		private MonsterAI _monsterAI;

		private ZNetView _nview;

		[SerializeField]
		private bool _enforcer;

		private Vector3 _terrainTarget;

		private float _resetRadius = 8f;

		private bool _hasTerrainTarget;

		private bool _selfDestructTriggered;

		private bool _terrainResetDone;

		private bool _terrainResetInProgress;

		private bool _visualColorApplied;

		private float _nextVisualSyncTime;

		private bool _enforcerTargetSearchInitialized;

		private float _nextEnforcerTargetSearchTime;

		private float _enforcerTargetSearchDeadline;

		private Vector3 _enforcerSpawnPoint;

		private readonly List<Material> _ownedVisualMaterials = new List<Material>();

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			_enforcerSpawnPoint = ((Component)this).transform.position;
			_character = ((Component)this).GetComponent<Character>();
			_monsterAI = ((Component)this).GetComponent<MonsterAI>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			TryApplySyncedVisualColor();
			ApplyIdentity();
			ConfigureTerrainSeeker();
			TerrainMistilePrefab.MakeCollidersNonBlocking(((Component)this).gameObject);
			TerrainMistileSystem.RegisterActiveTerrainMistile(this);
			if (Object.op_Implicit((Object)(object)_character))
			{
				Character character = _character;
				character.m_onDeath = (Action)Delegate.Combine(character.m_onDeath, new Action(OnDeath));
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)_character))
			{
				Character character = _character;
				character.m_onDeath = (Action)Delegate.Remove(character.m_onDeath, new Action(OnDeath));
			}
			TerrainMistilePrefab.ReleaseVisualMaterials(_ownedVisualMaterials);
			ReleaseCurrentTerrainTarget();
			TerrainMistileSystem.UnregisterActiveTerrainMistile(this);
		}

		private void FixedUpdate()
		{
			TryApplySyncedVisualColor();
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)_character) && !_character.IsDead() && !_terrainResetDone && (TryLoadTerrainTarget() || TryAcquireEnforcerTerrainTarget()))
			{
				MoveTowardTerrainTarget();
			}
		}

		internal void Initialize(Vector3 terrainOperationPoint, float resetRadius, float health)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			ApplyIdentity();
			ConfigureTerrainSeeker();
			ApplyHealth(health);
			SetTerrainTarget(terrainOperationPoint, resetRadius);
		}

		internal void ConfigureEnforcer(bool enforcer)
		{
			_enforcer = enforcer;
		}

		internal void MarkSelfDestruct()
		{
			_selfDestructTriggered = true;
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				_nview.GetZDO().Set("TerrainMistileSelfDestruct", true);
			}
		}

		private void ApplyIdentity()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_character))
			{
				_character.m_name = (_enforcer ? "$terrainmistile_enforcer_name" : "$terrainmistile_creature_name");
				_character.m_faction = (Faction)8;
				_character.m_aiSkipTarget = true;
				_character.m_flying = true;
			}
		}

		private void ApplyHealth(float health)
		{
			if (Object.op_Implicit((Object)(object)_character) && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				health = Mathf.Max(1f, health);
				_character.SetMaxHealth(health);
				_character.SetHealth(health);
			}
		}

		private void ApplyVisualColor(Color color, bool syncToZdo)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			TerrainMistilePrefab.ApplyVisuals(((Component)this).gameObject, color, _ownedVisualMaterials);
			_visualColorApplied = true;
			if (syncToZdo && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				ZDO zDO = _nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set("TerrainMistileVisualColor", new Vector3(color.r, color.g, color.b));
					zDO.Set("TerrainMistileVisualColorSet", true);
				}
			}
		}

		private void ApplyVisualColorForTarget(Vector3 target)
		{
			//IL_0000: 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)
			ApplyVisualColor(TerrainMistileSpawnRules.GetRule(TerrainMistileSpawnRules.GetBiomeKey(target)).VisualColor, syncToZdo: true);
		}

		private void TryApplySyncedVisualColor()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (_visualColorApplied || Time.time < _nextVisualSyncTime)
			{
				return;
			}
			_nextVisualSyncTime = Time.time + 0.25f;
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid())
			{
				ZDO zDO = _nview.GetZDO();
				if (zDO != null && zDO.GetBool("TerrainMistileVisualColorSet", false))
				{
					Vector3 vec = zDO.GetVec3("TerrainMistileVisualColor", Vector3.zero);
					ApplyVisualColor(new Color(vec.x, vec.y, vec.z, 1f), syncToZdo: false);
				}
			}
		}

		private void ConfigureTerrainSeeker()
		{
			if (Object.op_Implicit((Object)(object)_monsterAI))
			{
				_monsterAI.m_enableHuntPlayer = false;
				_monsterAI.m_attackPlayerObjects = false;
				((BaseAI)_monsterAI).m_aggravatable = false;
				ZNetView component = ((Component)_monsterAI).GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid())
				{
					((BaseAI)_monsterAI).SetHuntPlayer(false);
				}
				((Behaviour)_monsterAI).enabled = false;
			}
		}

		private void SetTerrainTarget(Vector3 terrainOperationPoint, float resetRadius)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			ReleaseCurrentTerrainTarget();
			_terrainTarget = terrainOperationPoint;
			_resetRadius = NormalizeResetRadius(resetRadius);
			_hasTerrainTarget = true;
			TerrainMistileSystem.ReserveTerrainTarget(_terrainTarget, _resetRadius);
			ApplyVisualColorForTarget(_terrainTarget);
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				ZDO zDO = _nview.GetZDO();
				zDO.Set("TerrainMistileSource", terrainOperationPoint);
				zDO.Set("TerrainMistileResetRadius", _resetRadius);
				zDO.Set("TerrainMistileSourceSet", true);
			}
		}

		private void ReleaseCurrentTerrainTarget()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (_hasTerrainTarget)
			{
				TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
				_hasTerrainTarget = false;
			}
		}

		private bool TryLoadTerrainTarget()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetTerrainTarget(out var terrainTarget, out var resetRadius))
			{
				return false;
			}
			if (!_hasTerrainTarget)
			{
				_terrainTarget = terrainTarget;
				_resetRadius = resetRadius;
				_hasTerrainTarget = true;
				TerrainMistileSystem.ReserveTerrainTarget(_terrainTarget, _resetRadius);
			}
			return true;
		}

		private bool TryAcquireEnforcerTerrainTarget()
		{
			//IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			if (!_enforcer)
			{
				return false;
			}
			if (!_enforcerTargetSearchInitialized)
			{
				_enforcerTargetSearchInitialized = true;
				_nextEnforcerTargetSearchTime = Time.time;
				_enforcerTargetSearchDeadline = Time.time + 10f;
			}
			if (Time.time < _nextEnforcerTargetSearchTime)
			{
				return false;
			}
			_nextEnforcerTargetSearchTime = Time.time + 1f;
			if (TerrainMistileSystem.TryFindEnforcerTerrainTarget(_enforcerSpawnPoint, out var modifiedPoint))
			{
				TerrainMistileBiomeSpawnRule rule = TerrainMistileSpawnRules.GetRule(TerrainMistileSpawnRules.GetBiomeKey(modifiedPoint));
				Initialize(modifiedPoint, rule.ResetRadius, rule.Health);
				return true;
			}
			if (Time.time >= _enforcerTargetSearchDeadline)
			{
				DestroyWithoutTerrainReset();
			}
			return false;
		}

		internal bool TryGetTerrainTarget(out Vector3 terrainTarget)
		{
			float resetRadius;
			return TryGetTerrainTarget(out terrainTarget, out resetRadius);
		}

		internal bool TryGetTerrainTarget(out Vector3 terrainTarget, out float resetRadius)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if (_hasTerrainTarget)
			{
				terrainTarget = _terrainTarget;
				resetRadius = _resetRadius;
				return true;
			}
			if (!Object.op_Implicit((Object)(object)_nview) || !_nview.IsValid())
			{
				terrainTarget = default(Vector3);
				resetRadius = _resetRadius;
				return false;
			}
			ZDO zDO = _nview.GetZDO();
			if (zDO == null || !zDO.GetBool("TerrainMistileSourceSet", false))
			{
				terrainTarget = default(Vector3);
				resetRadius = _resetRadius;
				return false;
			}
			terrainTarget = zDO.GetVec3("TerrainMistileSource", ((Component)this).transform.position);
			if (!TerrainMistileSystem.IsFinite(terrainTarget))
			{
				terrainTarget = default(Vector3);
				resetRadius = _resetRadius;
				return false;
			}
			resetRadius = NormalizeResetRadius(zDO.GetFloat("TerrainMistileResetRadius", 0f));
			return true;
		}

		private float NormalizeResetRadius(float resetRadius)
		{
			if (float.IsNaN(resetRadius) || float.IsInfinity(resetRadius))
			{
				return _resetRadius;
			}
			return Mathf.Clamp(resetRadius, 0.1f, 64f);
		}

		private void MoveTowardTerrainTarget()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)this).transform.position;
			Vector3 terrainTarget = _terrainTarget;
			if (TerrainMistileSystem.TryGetGroundHeight(terrainTarget, out var height))
			{
				terrainTarget.y = height + 0.15f;
			}
			Vector3 val = terrainTarget - position;
			Vector2 val2 = new Vector2(val.x, val.z);
			bool num = ((Vector2)(ref val2)).magnitude <= 1.25f || ((Vector3)(ref val)).sqrMagnitude <= 1.5625f;
			float height2;
			bool flag = TerrainMistileSystem.TryGetGroundHeight(position, out height2) && position.y <= height2 + 0.85f;
			if (num && flag)
			{
				DetonateOnTerrain();
				return;
			}
			if (((Vector3)(ref val)).sqrMagnitude <= 0.01f)
			{
				DetonateOnTerrain();
				return;
			}
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			_character.SetRun(true);
			_character.SetMoveDir(normalized);
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(normalized.x, 0f, normalized.z);
			if (((Vector3)(ref val3)).sqrMagnitude > 0.001f)
			{
				_character.SetLookDir(((Vector3)(ref val3)).normalized, 0f);
			}
		}

		private void DetonateOnTerrain()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (_terrainResetDone || !Object.op_Implicit((Object)(object)_character) || _character.IsDead() || TryRetargetOrDespawnFromEmptyImpact())
			{
				return;
			}
			MarkSelfDestruct();
			if (TryResetTerrain())
			{
				HitData val = new HitData();
				val.m_point = ((Component)this).transform.position;
				val.m_damage.m_damage = 9999999f;
				val.m_hitType = (HitType)14;
				_character.ApplyDamage(val, false, true, (DamageModifier)0);
				if (!_character.IsDead() && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					ZNetScene.instance.Destroy(((Component)this).gameObject);
				}
			}
		}

		private bool TryRetargetOrDespawnFromEmptyImpact()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			TryLoadTerrainTarget();
			if (!_hasTerrainTarget)
			{
				return false;
			}
			if (TerrainMistileSystem.HasModifiedTerrainAround(_terrainTarget, _resetRadius))
			{
				return false;
			}
			ReleaseCurrentTerrainTarget();
			if (_enforcer ? TerrainMistileSystem.TryFindEnforcerTerrainTarget(_enforcerSpawnPoint, out var modifiedPoint) : TerrainMistileSystem.TryFindReplacementTerrainTarget(((Component)this).transform.position, out modifiedPoint))
			{
				SetTerrainTarget(modifiedPoint, TerrainMistileSystem.GetResetRadiusForPoint(modifiedPoint));
				return true;
			}
			DestroyWithoutTerrainReset();
			return true;
		}

		private void DestroyWithoutTerrainReset()
		{
			_terrainResetDone = true;
			ReleaseCurrentTerrainTarget();
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				ZNetScene.instance.Destroy(((Component)this).gameObject);
			}
			else
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private void OnDeath()
		{
			TryResetTerrain();
		}

		internal bool TryResetTerrain()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid())
			{
				if (!_nview.IsOwner())
				{
					return false;
				}
				_selfDestructTriggered |= _nview.GetZDO().GetBool("TerrainMistileSelfDestruct", false);
			}
			if (_terrainResetDone)
			{
				return true;
			}
			if (_terrainResetInProgress || !_selfDestructTriggered)
			{
				return false;
			}
			_terrainResetInProgress = true;
			try
			{
				TryLoadTerrainTarget();
				if (!TerrainMistileSystem.ResetTerrainAround(_hasTerrainTarget ? _terrainTarget : ((Component)this).transform.position, _resetRadius, resetPaint: true))
				{
					return false;
				}
				_terrainResetDone = true;
				ReleaseCurrentTerrainTarget();
				return true;
			}
			catch (Exception ex)
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogError((object)("TerrainMistile terrain reset failed: " + ex.Message));
				return false;
			}
			finally
			{
				_terrainResetInProgress = false;
			}
		}
	}
	[HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")]
	internal static class TerrainMistileCompendium
	{
		private readonly struct VanillaBiomeEntry
		{
			internal readonly Biome Biome;

			internal readonly string EnglishName;

			internal VanillaBiomeEntry(Biome biome, string englishName)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Biome = biome;
				EnglishName = englishName;
			}
		}

		private readonly struct BiomeDisplayEntry
		{
			internal readonly int BiomeKey;

			internal readonly string DisplayName;

			internal BiomeDisplayEntry(int biomeKey, string displayName)
			{
				BiomeKey = biomeKey;
				DisplayName = displayName;
			}
		}

		private readonly struct PrefabDisplayEntry
		{
			internal readonly string PrefabName;

			internal readonly string DisplayName;

			internal PrefabDisplayEntry(string prefabName, string displayName)
			{
				PrefabName = prefabName;
				DisplayName = displayName;
			}
		}

		private const string PageTopic = "$terrainmistile_compendium_topic";

		private const string ExplanationToken = "$terrainmistile_compendium_explanation";

		private const string ProtectionHeadingToken = "$terrainmistile_compendium_protection_heading";

		private const string RequirementToken = "$terrainmistile_compendium_requirement";

		private const string MistileDisabledToken = "$terrainmistile_compendium_mistile_disabled";

		private const string ProtectionDisabledToken = "$terrainmistile_compendium_protection_disabled";

		private const string OtherBiomesToken = "$terrainmistile_compendium_other_biomes";

		private const string PiecesHeadingToken = "$terrainmistile_compendium_pieces_heading";

		private const string NoPiecesToken = "$terrainmistile_compendium_no_pieces";

		private static readonly VanillaBiomeEntry[] VanillaBiomes = new VanillaBiomeEntry[9]
		{
			new VanillaBiomeEntry((Biome)1, "Meadows"),
			new VanillaBiomeEntry((Biome)8, "Black Forest"),
			new VanillaBiomeEntry((Biome)2, "Swamp"),
			new VanillaBiomeEntry((Biome)4, "Mountain"),
			new VanillaBiomeEntry((Biome)16, "Plains"),
			new VanillaBiomeEntry((Biome)512, "Mistlands"),
			new VanillaBiomeEntry((Biome)32, "Ashlands"),
			new VanillaBiomeEntry((Biome)64, "Deep North"),
			new VanillaBiomeEntry((Biome)256, "Ocean")
		};

		private static void Postfix(List<TextInfo> ___m_texts)
		{
			AddPage(___m_texts);
		}

		private static void AddPage(List<TextInfo> texts)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			if (texts != null)
			{
				string localizedTopic = LocalizeOrFallback("$terrainmistile_compendium_topic", "TerrainMistile");
				texts.RemoveAll((TextInfo text) => string.Equals(text?.m_topic, "$terrainmistile_compendium_topic", StringComparison.Ordinal) || string.Equals(text?.m_topic, localizedTopic, StringComparison.Ordinal));
				texts.Add(new TextInfo(localizedTopic, BuildPageText(localizedTopic)));
				texts.Sort((TextInfo left, TextInfo right) => string.Compare(left?.m_topic, right?.m_topic, StringComparison.OrdinalIgnoreCase));
			}
		}

		private static string BuildPageText(string localizedTopic)
		{
			StringBuilder stringBuilder = new StringBuilder(4096);
			stringBuilder.Append(LocalizeOrFallback("$terrainmistile_compendium_explanation", "Protection counts different recognized settlement facility types around each altered terrain point. Multiple copies of the same facility count as one type. " + localizedTopic + " spawning is blocked on protected terrain.", localizedTopic));
			stringBuilder.Append("\n\n");
			AppendBiomeRules(stringBuilder, localizedTopic);
			stringBuilder.Append("\n\n");
			AppendPlayerBasePrefabs(stringBuilder);
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
		}

		private static void AppendBiomeRules(StringBuilder builder, string localizedTopic)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected I4, but got Unknown
			builder.Append("<color=#FFD27A><b>").Append(LocalizeOrFallback("$terrainmistile_compendium_protection_heading", "Settlement Protection by Biome")).Append("</b></color>\n\n");
			HashSet<int> hashSet = new HashSet<int>();
			VanillaBiomeEntry[] vanillaBiomes = VanillaBiomes;
			for (int i = 0; i < vanillaBiomes.Length; i++)
			{
				VanillaBiomeEntry biome = vanillaBiomes[i];
				int num = (int)biome.Biome;
				hashSet.Add(num);
				AppendBiomeRule(builder, GetLocalizedVanillaBiomeName(biome), TerrainMistileSpawnRules.GetRule(num), localizedTopic);
			}
			List<BiomeDisplayEntry> list = new List<BiomeDisplayEntry>();
			foreach (int item in TerrainMistileSpawnRules.GetConfiguredBiomeKeysSnapshot())
			{
				if (item != 0 && hashSet.Add(item))
				{
					list.Add(new BiomeDisplayEntry(item, GetLocalizedCustomBiomeName(item)));
				}
			}
			list.Sort(delegate(BiomeDisplayEntry left, BiomeDisplayEntry right)
			{
				int num2 = string.Compare(left.DisplayName, right.DisplayName, StringComparison.OrdinalIgnoreCase);
				if (num2 == 0)
				{
					int biomeKey = left.BiomeKey;
					return biomeKey.CompareTo(right.BiomeKey);
				}
				return num2;
			});
			foreach (BiomeDisplayEntry item2 in list)
			{
				AppendBiomeRule(builder, item2.DisplayName, TerrainMistileSpawnRules.GetRule(item2.BiomeKey), localizedTopic);
			}
			AppendBiomeRule(builder, LocalizeOrFallback("$terrainmistile_compendium_other_biomes", "Other biomes (defaults)"), TerrainMistileSpawnRules.DefaultRule, localizedTopic);
		}

		private static void AppendBiomeRule(StringBuilder builder, string biomeName, TerrainMistileBiomeSpawnRule rule, string localizedTopic)
		{
			builder.Append("<color=orange><b>").Append(biomeName).Append("</b></color>: ");
			if (!rule.Enabled)
			{
				builder.Append(LocalizeOrFallback("$terrainmistile_compendium_mistile_disabled", localizedTopic + " spawning disabled", localizedTopic));
			}
			else if (rule.PlayerBaseValue <= 0 || rule.BaseCheckRadius <= 0f)
			{
				builder.Append(LocalizeOrFallback("$terrainmistile_compendium_protection_disabled", "Settlement protection disabled"));
			}
			else
			{
				string text = rule.PlayerBaseValue.ToString(CultureInfo.InvariantCulture);
				string text2 = rule.BaseCheckRadius.ToString("0.##", CultureInfo.InvariantCulture);
				builder.Append(LocalizeOrFallback("$terrainmistile_compendium_requirement", "Required facility types: " + text + " · Check radius: " + text2 + " m", text, text2));
			}
			builder.Append('\n');
		}

		private static void AppendPlayerBasePrefabs(StringBuilder builder)
		{
			List<PrefabDisplayEntry> list = new List<PrefabDisplayEntry>();
			foreach (string item in TerrainMistileSpawnRules.GetPlayerBasePrefabNamesSnapshot())
			{
				list.Add(new PrefabDisplayEntry(item, GetPrefabDisplayName(item)));
			}
			list.Sort(delegate(PrefabDisplayEntry left, PrefabDisplayEntry right)
			{
				int num = string.Compare(left.DisplayName, right.DisplayName, StringComparison.OrdinalIgnoreCase);
				return (num == 0) ? string.Compare(left.PrefabName, right.PrefabName, StringComparison.OrdinalIgnoreCase) : num;
			});
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			foreach (PrefabDisplayEntry item2 in list)
			{
				dictionary.TryGetValue(item2.DisplayName, out var value);
				dictionary[item2.DisplayName] = value + 1;
			}
			string text = list.Count.ToString(CultureInfo.InvariantCulture);
			builder.Append("<color=#FFD27A><b>").Append(LocalizeOrFallback("$terrainmistile_compendium_pieces_heading", "Recognized Settlement Facilities (" + text + ")", text)).Append("</b></color>\n\n");
			if (list.Count == 0)
			{
				builder.Append(LocalizeOrFallback("$terrainmistile_compendium_no_pieces", "No settlement facilities configured.")).Append('\n');
				return;
			}
			foreach (PrefabDisplayEntry item3 in list)
			{
				builder.Append("- ").Append(item3.DisplayName);
				if (dictionary[item3.DisplayName] > 1 && !string.Equals(item3.DisplayName, item3.PrefabName, StringComparison.OrdinalIgnoreCase))
				{
					builder.Append(" <color=#999999>[").Append(item3.PrefabName).Append("]</color>");
				}
				builder.Append('\n');
			}
		}

		private static string GetPrefabDisplayName(string prefabName)
		{
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = PrefabManager.Instance.GetPrefab(prefabName);
			}
			Piece val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Piece>() : null);
			if (!Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val))
			{
				val2 = val.GetComponentInChildren<Piece>(true);
			}
			if (!Object.op_Implicit((Object)(object)val2) || string.IsNullOrWhiteSpace(val2.m_name))
			{
				return prefabName;
			}
			return LocalizeOrFallback(val2.m_name, prefabName);
		}

		private static string GetLocalizedVanillaBiomeName(VanillaBiomeEntry biome)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			return LocalizeOrFallback("$biome_" + ((object)biome.Biome/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(), biome.EnglishName);
		}

		private static string GetLocalizedCustomBiomeName(int biomeKey)
		{
			string biomeName = TerrainMistileSpawnRules.GetBiomeName(biomeKey);
			if (string.IsNullOrWhiteSpace(biomeName))
			{
				return biomeKey.ToString(CultureInfo.InvariantCulture);
			}
			string fallback = ((biomeName[0] == '$') ? biomeName.Substring(1) : biomeName);
			return LocalizeOrFallback(biomeName, fallback);
		}

		private static string LocalizeOrFallback(string value, string fallback, params string[] words)
		{
			if (string.IsNullOrWhiteSpace(value) || Localization.instance == null)
			{
				return fallback;
			}
			string text = Localization.instance.Localize(value, words).Trim();
			if (text.Length == 0)
			{
				return fallback;
			}
			if (value[0] == '$')
			{
				string b = "[" + value.Substring(1) + "]";
				if (string.Equals(text, b, StringComparison.Ordinal) || text.IndexOf("MISSING KEY", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return fallback;
				}
			}
			return text;
		}
	}
	public static class TerrainMistileCompat
	{
		public static void RegisterIgnoredTerrainArea(Vector3 center, float radius, string source)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			TerrainMistileSystem.RegisterExternalTerrainIgnoreArea(center, radius, source);
		}
	}
	internal static class TerrainMistileExternalTerrainCompat
	{
		private const string ExpandWorldDataLocationTypeName = "ExpandWorldData.LocationObjectDataAndSwap";

		private const string ExpandWorldDataLocationYamlTypeName = "ExpandWorldData.LocationYaml";

		private const string ExpandWorldDataLocationExtraTypeName = "ExpandWorldData.LocationExtra";

		private const string ExpandWorldDataBlueprintManagerTypeName = "ExpandWorldData.BlueprintManager";

		private const string ExpandWorldDataNoBuildManagerTypeName = "ExpandWorldData.NoBuildManager";

		private const string ExpandWorldDataBiomeManagerTypeName = "ExpandWorldData.BiomeManager";

		private const string BlueprintProtectedSourcePrefix = "Expand World Data blueprint terrain";

		private const float PatchRetryInterval = 2f;

		private const float MissingDependencyRetryInterval = 10f;

		private const int MaxPatchAttempts = 15;

		private static readonly Dictionary<string, Type> LoadedTypes = new Dictionary<string, Type>(StringComparer.Ordinal);

		private static readonly Dictionary<Type, Dictionary<string, FieldInfo?>> FieldsByType = new Dictionary<Type, Dictionary<string, FieldInfo>>();

		private static ManualLogSource? _logger;

		private static Harmony? _harmony;

		private static bool _terrainPatched;

		private static bool _protectionSyncPatched;

		private static bool _biomeMappingRefreshPatched;

		private static bool _biomeNamesFromFilePatched;

		private static bool _biomeSetNamesPatched;

		private static bool _biomeLoadPatched;

		private static bool _biomeMappingRefreshRequested;

		private static bool _expandWorldDataDetected;

		private static bool _blueprintReflectionResolved;

		private static int _patchAttempts;

		private static float _nextPatchAttemptTime;

		private static MethodInfo? _tryGetLocationYamlMethod;

		private static MethodInfo? _isBlueprintPrefabMethod;

		public static void Initialize(ManualLogSource logger, Harmony harmony)
		{
			_logger = logger;
			_harmony = harmony;
			bool flag = TryPatch();
			_nextPatchAttemptTime = Time.time + (flag ? 2f : 10f);
		}

		public static void Update()
		{
			if ((!_terrainPatched || !_protectionSyncPatched || !_biomeMappingRefreshPatched) && _patchAttempts < 15 && !(Time.time < _nextPatchAttemptTime))
			{
				bool flag = TryPatch();
				_nextPatchAttemptTime = Time.time + (flag ? 2f : 10f);
			}
		}

		internal static bool ConsumeBiomeMappingRefreshRequest()
		{
			if (!_biomeMappingRefreshRequested)
			{
				return false;
			}
			_biomeMappingRefreshRequested = false;
			return true;
		}

		private static bool TryPatch()
		{
			if (_harmony == null || _patchAttempts >= 15)
			{
				return false;
			}
			if (FindLoadedType("ExpandWorldData.BiomeManager") == null)
			{
				return false;
			}
			if (!_expandWorldDataDetected)
			{
				_expandWorldDataDetected = true;
				_biomeMappingRefreshRequested = true;
			}
			_patchAttempts++;
			TryPatchTerrainHandler();
			TryPatchBlueprintProtectionSync();
			TryPatchBiomeMappingRefresh();
			return true;
		}

		private static void TryPatchTerrainHandler()
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			if (_terrainPatched || _harmony == null)
			{
				return;
			}
			Type type = FindLoadedType("ExpandWorldData.LocationObjectDataAndSwap");
			Type type2 = FindLoadedType("ExpandWorldData.LocationYaml");
			if (type == null || type2 == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "HandleTerrain", new Type[4]
			{
				typeof(Vector3),
				typeof(float),
				typeof(bool),
				type2
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "HandleTerrainPrefix", (Type[])null, (Type[])null);
			if (!(methodInfo == null) && !(methodInfo2 == null))
			{
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_terrainPatched = true;
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)"Expand World Data terrain compat initialized.");
				}
			}
		}

		private static void TryPatchBlueprintProtectionSync()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			if (_protectionSyncPatched || _harmony == null)
			{
				return;
			}
			Type type = FindLoadedType("ExpandWorldData.NoBuildManager");
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "UpdateData", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "BlueprintProtectionSyncPostfix", (Type[])null, (Type[])null);
			if (!(methodInfo == null) && !(methodInfo2 == null))
			{
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_protectionSyncPatched = true;
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.m_locationInstances.Count > 0)
				{
					SyncBlueprintProtectedAreas();
				}
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)"Expand World Data blueprint terrain protection initialized.");
				}
			}
		}

		private static void TryPatchBiomeMappingRefresh()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			if (_biomeMappingRefreshPatched || _harmony == null)
			{
				return;
			}
			Type type = FindLoadedType("ExpandWorldData.BiomeManager");
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "BiomeMappingChangedPostfix", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return;
			}
			HarmonyMethod postfix = new HarmonyMethod(methodInfo);
			List<string> list = new List<string>();
			TryPatchBiomeMappingRefreshMethod(type, "NamesFromFile", Type.EmptyTypes, postfix, ref _biomeNamesFromFilePatched, list);
			TryPatchBiomeMappingRefreshMethod(type, "SetNames", null, postfix, ref _biomeSetNamesPatched, list);
			TryPatchBiomeMappingRefreshMethod(type, "Load", new Type[1] { typeof(string) }, postfix, ref _biomeLoadPatched, list);
			_biomeMappingRefreshPatched = _biomeNamesFromFilePatched && _biomeSetNamesPatched && _biomeLoadPatched;
			if (_biomeMappingRefreshPatched)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)"Expand World Data biome mapping refresh initialized.");
				}
			}
			else if (_patchAttempts >= 15)
			{
				ManualLogSource? logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)("Expand World Data biome mapping refresh is only partially available. Unavailable methods: " + string.Join(", ", list) + "."));
				}
			}
		}

		private static void TryPatchBiomeMappingRefreshMethod(Type biomeManagerType, string methodName, Type[]? parameterTypes, HarmonyMethod postfix, ref bool patched, List<string> unavailableMethods)
		{
			if (patched || _harmony == null)
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(biomeManagerType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(biomeManagerType, methodName, parameterTypes, (Type[])null));
				if (methodInfo == null)
				{
					unavailableMethods.Add(methodName);
					return;
				}
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				patched = true;
			}
			catch (Exception ex)
			{
				unavailableMethods.Add(methodName + " (" + ex.Message + ")");
			}
		}

		private static void BiomeMappingChangedPostfix()
		{
			_biomeMappingRefreshRequested = true;
		}

		internal static Type? FindLoadedType(string fullName)
		{
			if (LoadedTypes.TryGetValue(fullName, out Type value))
			{
				return value;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type;
				try
				{
					type = assembly.GetType(fullName, throwOnError: false);
				}
				catch
				{
					continue;
				}
				if (type != null)
				{
					LoadedTypes[fullName] = type;
					return type;
				}
			}
			return null;
		}

		private static void HandleTerrainPrefix(Vector3 pos, float radius, bool isBlueprint, object data)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			string text = GetString(data, "prefab");
			float terrainRadius = GetTerrainRadius(radius, isBlueprint, data);
			if (terrainRadius > 0f)
			{
				TerrainMistileSystem.RegisterExternalTerrainIgnoreArea(pos, terrainRadius, "Expand World Data location terrain", 10f);
			}
			float noBuildRadius = GetNoBuildRadius(data, Mathf.Max(GetFloat(data, "exteriorRadius"), radius));
			float radius2 = Mathf.Max(new float[4]
			{
				terrainRadius,
				radius,
				GetFloat(data, "exteriorRadius"),
				noBuildRadius
			}) + 5f;
			if (isBlueprint)
			{
				TerrainMistileSystem.RegisterProtectedTerrainArea(pos, radius2, "Expand World Data blueprint terrain " + text);
			}
		}

		private static void BlueprintProtectionSyncPostfix()
		{
			SyncBlueprintProtectedAreas();
		}

		private static void SyncBlueprintProtectedAreas()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if (!Object.op_Implicit((Object)(object)instance) || !EnsureBlueprintReflectionMethods())
			{
				return;
			}
			List<TerrainMistileSystem.ProtectedTerrainAreaData> list = new List<TerrainMistileSystem.ProtectedTerrainAreaData>();
			try
			{
				foreach (LocationInstance value in instance.m_locationInstances.Values)
				{
					if (value.m_location == null)
					{
						continue;
					}
					string prefabName = value.m_location.m_prefabName;
					if (string.IsNullOrWhiteSpace(prefabName))
					{
						continue;
					}
					if (!TryIsBlueprintPrefab(prefabName, out var isBlueprint))
					{
						return;
					}
					if (isBlueprint)
					{
						object locationYaml;
						bool flag = TryGetLocationYaml(value.m_location, out locationYaml);
						if (!flag && _tryGetLocationYamlMethod == null)
						{
							return;
						}
						object obj = (flag ? locationYaml : null);
						float num = ((obj == null) ? 0f : GetTerrainRadius(value.m_location.m_exteriorRadius, isBlueprint: true, obj));
						float num2 = ((obj == null) ? 0f : GetNoBuildRadius(obj, value.m_location.m_exteriorRadius));
						float radius = Mathf.Max(new float[3]
						{
							num,
							value.m_location.m_exteriorRadius,
							num2
						}) + 5f;
						list.Add(new TerrainMistileSystem.ProtectedTerrainAreaData(value.m_position, radius, "Expand World Data blueprint terrain " + prefabName));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Expand World Data blueprint terrain protection refresh failed: " + ex.Message));
				}
				return;
			}
			TerrainMistileSystem.ReplaceProtectedTerrainAreas("Expand World Data blueprint terrain", list);
		}

		private static bool TryGetLocationYaml(ZoneLocation location, out object? locationYaml)
		{
			locationYaml = null;
			MethodInfo tryGetLocationYamlMethod = _tryGetLocationYamlMethod;
			if (tryGetLocationYamlMethod == null)
			{
				return false;
			}
			object[] array = new object[2] { location, null };
			try
			{
				bool valueOrDefault = tryGetLocationYamlMethod.Invoke(null, array) as bool? == true;
				locationYaml = array[1];
				return valueOrDefault && locationYaml != null;
			}
			catch (Exception ex)
			{
				_tryGetLocationYamlMethod = null;
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Expand World Data location lookup failed: " + ex.Message));
				}
				return false;
			}
		}

		private static bool TryIsBlueprintPrefab(string prefab, out bool isBlueprint)
		{
			isBlueprint = false;
			MethodInfo isBlueprintPrefabMethod = _isBlueprintPrefabMethod;
			if (isBlueprintPrefabMethod == null)
			{
				return false;
			}
			try
			{
				isBlueprint = isBlueprintPrefabMethod.Invoke(null, new object[1] { prefab }) as bool? == true;
				return true;
			}
			catch (Exception ex)
			{
				_isBlueprintPrefabMethod = null;
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Expand World Data blueprint lookup failed: " + ex.Message));
				}
				return false;
			}
		}

		private static bool EnsureBlueprintReflectionMethods()
		{
			if (_blueprintReflectionResolved)
			{
				if (_tryGetLocationYamlMethod != null)
				{
					return _isBlueprintPrefabMethod != null;
				}
				return false;
			}
			Type type = FindLoadedType("ExpandWorldData.LocationExtra");
			Type type2 = FindLoadedType("ExpandWorldData.LocationYaml");
			Type type3 = FindLoadedType("ExpandWorldData.BlueprintManager");
			if (type == null || type2 == null || type3 == null)
			{
				return false;
			}
			_tryGetLocationYamlMethod = AccessTools.Method(type, "TryGetData", new Type[2]
			{
				typeof(ZoneLocation),
				type2.MakeByRefType()
			}, (Type[])null);
			_isBlueprintPrefabMethod = AccessTools.Method(type3, "Has", new Type[1] { typeof(string) }, (Type[])null);
			_blueprintReflectionResolved = true;
			if (_tryGetLocationYamlMethod != null)
			{
				return _isBlueprintPrefabMethod != null;
			}
			return false;
		}

		internal static float GetTerrainRadius(float exteriorRadius, bool isBlueprint, object data)
		{
			string text = GetString(data, "levelArea");
			string text2 = GetString(data, "paint");
			bool num = ((text.Length == 0) ? isBlueprint : (!text.Equals("false", StringComparison.Ordinal)));
			float num2 = 0f;
			if (num)
			{
				float num3 = GetFloat(data, "levelRadius");
				float num4 = GetFloat(data, "levelBorder");
				num2 = Mathf.Max(num2, (num3 == 0f && num4 == 0f) ? exteriorRadius : (num3 + num4));
			}
			if (text2.Length > 0)
			{
				float num5 = GetNullableFloat(data, "paintRadius") ?? exteriorRadius;
				float num6 = GetNullableFloat(data, "paintBorder") ?? 5f;
				num2 = Mathf.Max(num2, num5 + num6);
			}
			return num2;
		}

		private static float GetNoBuildRadius(object data, float exteriorRadius)
		{
			string text = GetString(data, "noBuild").Trim();
			if (text.Length == 0 || text.Equals("false", StringComparison.OrdinalIgnoreCase))
			{
				return 0f;
			}
			if (text.Equals("true", StringComparison.OrdinalIgnoreCase))
			{
				return exteriorRadius;
			}
			if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return 0f;
			}
			return Mathf.Max(0f, result);
		}

		private static string GetString(object data, string fieldName)
		{
			return (GetField(data, fieldName)?.GetValue(data) as string) ?? "";
		}

		private static float GetFloat(object data, string fieldName)
		{
			object obj = GetField(data, fieldName)?.GetValue(data);
			if (obj is float)
			{
				return (float)obj;
			}
			return 0f;
		}

		private static float? GetNullableFloat(object data, string fieldName)
		{
			object obj = GetField(data, fieldName)?.GetValue(data);
			if (!(obj is float))
			{
				return null;
			}
			return (float)obj;
		}

		private static FieldInfo? GetField(object data, string fieldName)
		{
			Type type = data.GetType();
			if (!FieldsByType.TryGetValue(type, out Dictionary<string, FieldInfo> value))
			{
				value = new Dictionary<string, FieldInfo>(StringComparer.Ordinal);
				FieldsByType[type] = value;
			}
			if (!value.TryGetValue(fieldName, out var value2))
			{
				value2 = (value[fieldName] = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			}
			return value2;
		}
	}
	internal static class TerrainMistileLocalization
	{
		private const string FileExtension = ".yml";

		private static readonly string FilePrefix = "TerrainMistile.";

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build();

		internal static void Load(ManualLogSource logger)
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			bool flag = TryLoadEmbeddedYaml(localization, "English", logger);
			TryLoadEmbeddedYaml(localization, "Korean", logger);
			LoadExternalYamlFiles(localization, logger);
			if (!flag)
			{
				logger.LogWarning((object)"Embedded English localization was not found or was invalid. External localization files may still provide a fallback.");
			}
		}

		private static bool TryLoadEmbeddedYaml(CustomLocalization localization, string language, ManualLogSource logger)
		{
			string resourceSuffix = ".Translations.TerrainMistile." + language + ".yml";
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceSuffix, StringComparison.Ordinal));
			if (text == null)
			{
				logger.LogWarning((object)("Embedded localization resource not found: Translations/TerrainMistile." + language + ".yml"));
				return false;
			}
			try
			{
				using Stream stream = executingAssembly.GetManifestResourceStream(text);
				if (stream == null)
				{
					logger.LogWarning((object)("Embedded localization resource could not be opened: " + text));
					return false;
				}
				using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
				return TryAddYamlTranslations(localization, language, streamReader.ReadToEnd(), "embedded TerrainMistile." + language + ".yml", logger);
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not load embedded " + language + " localization: " + ex.Message));
				return false;
			}
		}

		private static void LoadExternalYamlFiles(CustomLocalization localization, ManualLogSource logger)
		{
			string bepInExRootPath = Paths.BepInExRootPath;
			string[] files;
			try
			{
				files = Directory.GetFiles(bepInExRootPath, FilePrefix + "*.yml", SearchOption.AllDirectories);
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not search for TerrainMistile localization files: " + ex.Message));
				return;
			}
			string[] array = files.OrderBy((string path) => IsInsideDirectory(path, Paths.ConfigPath) ? 1 : 0).ThenBy<string, string>((string path) => path, StringComparer.Ordinal).ToArray();
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (!TryGetLanguageFromFileName(text, out string language))
				{
					logger.LogWarning((object)("Ignoring localization file with an invalid name: " + text + ". Expected TerrainMistile.<Language>.yml."));
					continue;
				}
				string yaml;
				try
				{
					yaml = File.ReadAllText(text, Encoding.UTF8);
				}
				catch (Exception ex2)
				{
					logger.LogWarning((object)("Could not read " + language + " localization from " + text + ": " + ex2.Message));
					continue;
				}
				if (dictionary.TryGetValue(language, out var value))
				{
					logger.LogWarning((object)("Loading another " + language + " localization file from " + text + ". Its duplicate tokens override values loaded from " + value + "."));
				}
				if (TryAddYamlTranslations(localization, language, yaml, text, logger))
				{
					dictionary[language] = text;
				}
			}
		}

		private static bool TryAddYamlTranslations(CustomLocalization localization, string language, string yaml, string source, ManualLogSource logger)
		{
			if (!TryParseYaml(yaml, out Dictionary<string, string> translations, out string error))
			{
				logger.LogWarning((object)("Could not load " + language + " localization from " + source + ": " + error));
				return false;
			}
			try
			{
				localization.AddTranslation(ref language, translations);
				logger.LogInfo((object)$"Loaded {translations.Count} {language} localization entries from {source}.");
				return true;
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not register " + language + " localization from " + source + ": " + ex.Message));
				return false;
			}
		}

		internal static bool TryGetLanguageFromFileName(string path, out string language)
		{
			language = string.Empty;
			string fileName = Path.GetFileName(path);
			if (!fileName.StartsWith(FilePrefix, StringComparison.Ordinal) || !fileName.EndsWith(".yml", StringComparison.Ordinal))
			{
				return false;
			}
			int num = fileName.Length - FilePrefix.Length - ".yml".Length;
			if (num <= 0)
			{
				return false;
			}
			language = fileName.Substring(FilePrefix.Length, num);
			if (language.IndexOf('.') >= 0 || !char.IsUpper(language[0]))
			{
				language = string.Empty;
				return false;
			}
			return true;
		}

		internal static bool TryParseYaml(string yaml, out Dictionary<string, string> translations, out string error)
		{
			translations = new Dictionary<string, string>(StringComparer.Ordinal);
			error = string.Empty;
			try
			{
				Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(yaml);
				if (dictionary == null || dictionary.Count == 0)
				{
					error = "The localization file is empty.";
					return false;
				}
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					string text = item.Key?.Trim() ?? string.Empty;
					if (text.Length == 0)
					{
						error = "Localization keys must not be empty.";
						return false;
					}
					if (item.Value == null)
					{
						error = "Localization value for '" + text + "' must not be null.";
						return false;
					}
					if (translations.ContainsKey(text))
					{
						error = "Localization key '" + text + "' is duplicated.";
						return false;
					}
					translations.Add(text, item.Value);
				}
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				return false;
			}
		}

		private static bool IsInsideDirectory(string path, string directory)
		{
			string text = Path.GetFullPath(directory).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			string value = text + directorySeparatorChar;
			return Path.GetFullPath(path).StartsWith(value, StringComparison.OrdinalIgnoreCase);
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "SpawnLocation", new Type[]
	{
		typeof(ZoneLocation),
		typeof(int),
		typeof(Vector3),
		typeof(Quaternion),
		typeof(SpawnMode),
		typeof(List<GameObject>)
	})]
	internal static class ZoneSystemSpawnLocationPatch
	{
		private static void Prefix(ZoneLocation location, Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			TerrainMistileSystem.RegisterLocationTerrainLoadGrace(location, pos);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Destroy", new Type[] { typeof(GameObject) })]
	internal static class ZNetSceneDestroyPatch
	{
		private static void Prefix(GameObject __0)
		{
			if (Object.op_Implicit((Object)(object)__0))
			{
				TerrainMistileBehaviour component = __0.GetComponent<TerrainMistileBehaviour>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.TryResetTerrain();
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownPatch
	{
		private static void Postfix()
		{
			TerrainMistileSystem.ClearWorldState();
		}
	}
	internal static class TerrainMistilePrefab
	{
		internal const string PrefabName = "TerrainMistile";

		internal const string EnforcerPrefabName = "TerrainMistileEnforcer";

		private const string SourcePrefabName = "Mistile";

		private static bool _hooked;

		private static bool _terrainMistileRegistered;

		private static bool _enforcerRegistered;

		private static GameObject? _registeredPrefab;

		private static GameObject? _registeredEnforcerPrefab;

		private static readonly List<Material> RegisteredPrefabVisualMaterials = new List<Material>();

		private static readonly List<Material> RegisteredEnforcerPrefabVisualMaterials = new List<Material>();

		internal static void RegisterPrefabHook()
		{
			if (!_hooked)
			{
				PrefabManager.OnVanillaPrefabsAvailable += CreateTerrainMistilePrefab;
				_hooked = true;
			}
		}

		internal static void UnregisterPrefabHook()
		{
			if (_hooked)
			{
				PrefabManager.OnVanillaPrefabsAvailable -= CreateTerrainMistilePrefab;
				_hooked = false;
			}
		}

		private static void CreateTerrainMistilePrefab()
		{
			if (!_terrainMistileRegistered && TryCreateTerrainMistilePrefab("TerrainMistile", enforcer: false, RegisteredPrefabVisualMaterials, out GameObject registeredPrefab))
			{
				_registeredPrefab = registeredPrefab;
				_terrainMistileRegistered = true;
			}
			if (!_enforcerRegistered && TryCreateTerrainMistilePrefab("TerrainMistileEnforcer", enforcer: true, RegisteredEnforcerPrefabVisualMaterials, out GameObject registeredPrefab2))
			{
				_registeredEnforcerPrefab = registeredPrefab2;
				_enforcerRegistered = true;
			}
		}

		private static bool TryCreateTerrainMistilePrefab(string prefabName, bool enforcer, List<Material> registeredVisualMaterials, out GameObject? registeredPrefab)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			registeredPrefab = null;
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(prefabName, "Mistile");
			if (!Object.op_Implicit((Object)(object)val))
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogError((object)("Could not clone vanilla prefab 'Mistile' as '" + prefabName + "'."));
				return false;
			}
			Character component = val.GetComponent<Character>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_faction = (Faction)8;
				component.m_aiSkipTarget = true;
			}
			MonsterAI component2 = val.GetComponent<MonsterAI>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.m_enableHuntPlayer = false;
				component2.m_attackPlayerObjects = false;
				((BaseAI)component2).m_aggravatable = false;
				component2.m_alertRange = 0f;
				((BaseAI)component2).m_viewRange = 0f;
				((BaseAI)component2).m_hearRange = 0f;
			}
			Humanoid component3 = val.GetComponent<Humanoid>();
			if (Object.op_Implicit((Object)(object)component3))
			{
				component3.m_defaultItems = Array.Empty<GameObject>();
			}
			TerrainMistileBehaviour terrainMistileBehaviour = val.GetComponent<TerrainMistileBehaviour>();
			if (!Object.op_Implicit((Object)(object)terrainMistileBehaviour))
			{
				terrainMistileBehaviour = val.AddComponent<TerrainMistileBehaviour>();
			}
			terrainMistileBehaviour.ConfigureEnforcer(enforcer);
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_name = (enforcer ? "$terrainmistile_enforcer_name" : "$terrainmistile_creature_name");
			}
			MakeCollidersNonBlocking(val);
			ApplyVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor, registeredVisualMaterials);
			PrefabManager.Instance.AddPrefab(val);
			registeredPrefab = val;
			TerrainMistilePlugin.TerrainMistileLogger.LogInfo((object)("Registered '" + prefabName + "' prefab from 'Mistile'."));
			return true;
		}

		internal static void RefreshRegisteredPrefabVisuals()
		{
			RefreshRegisteredPrefabVisuals("TerrainMistile", _registeredPrefab, RegisteredPrefabVisualMaterials);
			RefreshRegisteredPrefabVisuals("TerrainMistileEnforcer", _registeredEnforcerPrefab, RegisteredEnforcerPrefabVisualMaterials);
		}

		private static void RefreshRegisteredPrefabVisuals(string prefabName, GameObject? registeredPrefab, List<Material> registeredVisualMaterials)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (GameObject)(Object.op_Implicit((Object)(object)registeredPrefab) ? ((object)registeredPrefab) : ((object)(Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null)));
			if (Object.op_Implicit((Object)(object)val))
			{
				RefreshRegisteredPrefabVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor, registeredVisualMaterials);
			}
		}

		internal static void ApplyVisuals(GameObject root, Color color, List<Material>? ownedMaterials = null)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)root))
			{
				Array materials = ((ownedMaterials != null && ownedMaterials.Count > 0) ? ((Array)ownedMaterials.ToArray()) : ((Array)Array.Empty<Material>()));
				ownedMaterials?.Clear();
				ApplyLightColor(root, color);
				ApplyParticleColor(root, color);
				ApplyMaterialColor(root, color, ownedMaterials);
				ReleaseVisualMaterials((Material[])materials);
			}
		}

		internal static void ReleaseVisualMaterials(List<Material> materials)
		{
			foreach (Material material in materials)
			{
				if (Object.op_Implicit((Object)(object)material))
				{
					Object.Destroy((Object)(object)material);
				}
			}
			materials.Clear();
		}

		private static void ReleaseVisualMaterials(Material[] materials)
		{
			foreach (Material val in materials)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static void RefreshRegisteredPrefabVisuals(GameObject root, Color color, List<Material> registeredVisualMaterials)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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)
			ApplyLightColor(root, color);
			ApplyParticleColor(root, color);
			for (int num = registeredVisualMaterials.Count - 1; num >= 0; num--)
			{
				Material val = registeredVisualMaterials[num];
				if (!Object.op_Implicit((Object)(object)val))
				{
					registeredVisualMaterials.RemoveAt(num);
				}
				else
				{
					ApplyMaterialProperties(val, color);
				}
			}
			if (registeredVisualMaterials.Count == 0)
			{
				ApplyMaterialColor(root, color, registeredVisualMaterials);
			}
		}

		internal static void MakeCollidersNonBlocking(GameObject root)
		{
			if (!Object.op_Implicit((Object)(object)root))
			{
				return;
			}
			Collider[] componentsInChildren = root.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					val.isTrigger = true;
				}
			}
		}

		private static void ApplyLightColor(GameObject root, Color color)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Light[] componentsInChildren = root.GetComponentsInChildren<Light>(true);
			foreach (Light val in componentsInChildren)
			{
				val.color = WithAlpha(color, val.color.a);
			}
		}

		private static void ApplyParticleColor(GameObject root, Color color)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystem[] componentsInChildren = root.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem obj in componentsInChildren)
			{
				MainModule main = obj.main;
				((MainModule)(ref main)).startColor = Recolor(((MainModule)(ref main)).startColor, color);
				ColorOverLifetimeModule colorOverLifetime = obj.colorOverLifetime;
				if (((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled)
				{
					((ColorOverLifetimeModule)(ref colorOverLifetime)).color = Recolor(((ColorOverLifetimeModule)(ref colorOverLifetime)).color, color);
				}
			}
		}

		private static MinMaxGradient Recolor(MinMaxGradient source, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected I4, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemGradientMode mode = ((MinMaxGradient)(ref source)).mode;
			return (MinMaxGradient)((int)mode switch
			{
				0 => new MinMaxGradient(WithAlpha(color, ((MinMaxGradient)(ref source)).color.a)), 
				2 => new MinMaxGradient(WithAlpha(ScaleRgb(color, 0.65f), ((MinMaxGradient)(ref source)).colorMin.a), WithAlpha(ScaleRgb(color, 1.15f), ((MinMaxGradient)(ref source)).colorMax.a)), 
				1 => new MinMaxGradient(CreateGradient(((MinMaxGradient)(ref source)).gradient, color)), 
				3 => new MinMaxGradient(CreateGradient(((MinMaxGradient)(ref source)).gradientMin, color), CreateGradient(((MinMaxGradient)(ref source)).gradientMax, color)), 
				_ => new MinMaxGradient(color), 
			});
		}

		private static Gradient CreateGradient(Gradient source, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			Gradient val = new Gradient();
			GradientColorKey[] array = source.colorKeys;
			GradientAlphaKey[] array2 = source.alphaKeys;
			if (array == null || array.Length == 0)
			{
				array = (GradientColorKey[])(object)new GradientColorKey[2]
				{
					new GradientColorKey(color, 0f),
					new GradientColorKey(color, 1f)
				};
			}
			GradientColorKey[] array3 = (GradientColorKey[])(object)new GradientColorKey[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array3[i] = new GradientColorKey(GetGradientShade(color, i, array.Length), array[i].time);
			}
			if (array2 == null || array2.Length == 0)
			{
				array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[2]
				{
					new GradientAlphaKey(color.a, 0f),
					new GradientAlphaKey(0f, 1f)
				};
			}
			val.SetKeys(array3, array2);
			val.mode = source.mode;
			return val;
		}

		private static Color GetGradientShade(Color color, int index, int count)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			float num = ((count <= 1) ? 0f : ((float)index / (float)(count - 1)));
			return ScaleRgb(color, Mathf.Lerp(1.3f, 0.7f, num));
		}

		private static void ApplyMaterialColor(GameObject root, Color color, List<Material>? ownedMaterials)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val2 = sharedMaterials[j];
					if (Object.op_Implicit((Object)(object)val2) && ShouldColorMaterial(val, val2))
					{
						Material val3 = Object.Instantiate<Material>(val2);
						ApplyMaterialProperties(val3, color);
						sharedMaterials[j] = val3;
						ownedMaterials?.Add(val3);
						flag = true;
					}
				}
				if (flag)
				{
					val.sharedMaterials = sharedMaterials;
				}
			}
		}

		private static bool ShouldColorMaterial(Renderer renderer, Material material)
		{
			string value = (Object.op_Implicit((Object)(object)((Component)renderer).gameObject) ? ((Object)((Component)renderer).gameObject).name : "");
			string value2 = (Object.op_Implicit((Object)(object)material) ? ((Object)material).name : "");
			if (!ContainsAny(value, "flame", "flare", "spark", "ember"))
			{
				return ContainsAny(value2, "flame", "spark", "glow", "pixel_unlit");
			}
			return true;
		}

		private static void ApplyMaterialProperties(Material material, Color color)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			SetMaterialColor(material, "_TintColor", color, preserveIntensity: false);
			SetMaterialColor(material, "_Color", color, preserveIntensity: false);
			SetMaterialColor(material, "_EmissionColor", color, preserveIntensity: true);
		}

		private static void SetMaterialColor(Material material, string property, Color color, bool preserveIntensity)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (material.HasProperty(property))
			{
				Color color2 = material.GetColor(property);
				float scale = (preserveIntensity ? Mathf.Max(new float[4] { 1f, color2.r, color2.g, color2.b }) : 1f);
				material.SetColor(property, WithAlpha(ScaleRgb(color, scale), color2.a));
			}
		}

		private static bool ContainsAny(string value, params string[] needles)
		{
			foreach (string value2 in needles)
			{
				if (value.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static Color ScaleRgb(Color color, float scale)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			return new Color(color.r * scale, color.g * scale, color.b * scale, color.a);
		}

		private static Color WithAlpha(Color color, float alpha)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			color.a = alpha;
			return color;
		}
	}
	internal static class TerrainMistileSpawnRules
	{
		private sealed class TerrainMistileSpawnRuleValues
		{
			public float? Interval { get; set; }

			public float? PlayerSearchRadius { get; set; }

			public float? SpawnChance { get; set; }

			public float? MaxDeformationSpawnChanceBonus { get; set; }

			public int? MaxSpawn { get; set; }

			public bool? PerPlayerSpawn { get; set; }

			public int? PlayerBaseValue { get; set; }

			public float? BaseCheckRadius { get; set; }

			public string? SpawnRadius { get; set; }

			public float? SpawnAltitude { get; set; }

			public float? ResetRadius { get; set; }

			public float? Health { get; set; }

			public string? VisualColor { get; set; }
		}

		private const float DefaultInterval = 60f;

		private const float DefaultPlayerSearchRadius = 32f;

		private const float DefaultSpawnChance = 0.25f;

		private const float DefaultMaxDeformationSpawnChanceBonus = 0.25f;

		private const bool DefaultPerPlayerSpawn = true;

		private const int DefaultPlayerBaseValue = 1;

		private const float DefaultBaseCheckRadius = 24f;

		private const int DefaultMaxSpawn = 3;

		private const float DefaultSpawnRadiusMin = 16f;

		private const float DefaultSpawnRadiusMax = 32f;

		private const float DefaultSpawnAltitude = 8f;

		private const float DefaultResetRadius = 8f;

		private const float DefaultHealth = 1f;

		internal const float MaximumResetRadius = 64f;

		internal const string DefaultVisualColorHex = "#45FF5A";

		private static readonly Color FallbackVisualColor = new Color(23f / 85f, 1f, 0.3529412f, 1f);

		private static readonly string[] DefaultPlayerBasePrefabNames = new string[40]
		{
			"ashwood_bed", "bed", "blackforge", "blastfurnace", "BogWitch_Fire_Pit", "bonfire", "charcoal_kiln", "charred_shieldgenerator", "dverger_guardstone", "eitrrefinery",
			"fermenter", "fire_pit", "fire_pit_haldor", "fire_pit_hildir", "fire_pit_iron", "forge", "guard_stone", "hearth", "piece_artisanstation", "piece_bed02",
			"piece_brazierceiling01", "piece_brazierfloor01", "piece_brazierfloor02", "piece_groundtorch", "piece_groundtorch_blue", "piece_groundtorch_green", "piece_groundtorch_mist", "piece_groundtorch_wood", "piece_magetable", "piece_oven",
			"piece_shieldgenerator", "piece_spinningwheel", "piece_stonecutter", "piece_walltorch", "piece_workbench", "portal", "portal_stone", "portal_wood", "smelter", "windmill"
		};

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		private static readonly Dictionary<int, TerrainMistileBiomeSpawnRule> BiomeRules = new Dictionary<int, TerrainMistileBiomeSpawnRule>();

		private static TerrainMistileBiomeSpawnRule _defaultRule = CreateDefaultRule();

		private static HashSet<string> _playerBasePrefabNames = CreateDefaultPlayerBasePrefabNames();

		private static float _maxPlayerSearchRadius = 32f;

		private static float _maxResetRadius = 8f;

		private static bool _hasEnabledRules = true;

		private static ManualLogSource? _logger;

		internal static float MaxPlayerSearchRadius => _maxPlayerSearchRadius;

		internal static float MaxResetRadius => _maxResetRadius;

		internal static bool HasEnabledRules => _hasEnabledRules;

		internal static Color DefaultVisualColor => _defaultRule.VisualColor;

		internal static TerrainMistileBiomeSpawnRule DefaultRule => _defaultRule;

		internal static bool IsPlayerBasePrefabName(string prefabName)
		{
			return _playerBasePrefabNames.Contains(prefabName);
		}

		internal static List<string> GetPlayerBasePrefabNamesSnapshot()
		{
			return new List<string>(_playerBasePrefabNames);
		}

		internal static List<int> GetConfiguredBiomeKeysSnapshot()
		{
			return new List<int>(BiomeRules.Keys);
		}

		internal static void Initialize(ManualLogSource logger)
		{
			_logger = logger;
		}

		internal static void EnsureFileExists(string path)
		{
			if (!File.Exists(path))
			{
				string directoryName = Path.GetDirectoryName(path);
				if (!string.IsNullOrWhiteSpace(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				File.WriteAllText(path, BuildDefaultYaml());
			}
		}

		internal static bool LoadYamlText(string yaml, string source)
		{
			if (!TryParseYaml(yaml, out TerrainMistileBiomeSpawnRule defaultRule, out Dictionary<int, TerrainMistileBiomeSpawnRule> parsedRules, out HashSet<string> playerBasePrefabNames, out string error))
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("Failed to parse TerrainMistile spawn rules from " + source + ": " + error));
				}
				return false;
			}
			_defaultRule = defaultRule;
			_playerBasePrefabNames = playerBasePrefabNames;
			BiomeRules.Clear();
			foreach (KeyValuePair<int, TerrainMistileBiomeSpawnRule> item in parsedRules)
			{
				BiomeRules[item.Key] = item.Value;
			}
			RecalculateRuntimeState();
			ManualLogSource? logger2 = _logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)$"Loaded TerrainMistile spawn rules from {source}. Default={_defaultRule}; overrides={BiomeRules.Count}; playerBasePrefabs={_playerBasePrefabNames.Count}; enabled={_hasEnabledRules}.");
			}
			return true;
		}

		internal static bool TryGetEnabledRule(int biome, out TerrainMistileBiomeSpawnRule rule)
		{
			rule = GetRule(biome);
			if (biome != 0)
			{
				return rule.Enabled;
			}
			return false;
		}

		internal static TerrainMistileBiomeSpawnRule GetRule(int biome)
		{
			if (!BiomeRules.TryGetValue(biome, out var value))
			{
				return _defaultRule;
			}
			return value;
		}

		internal static int GetBiomeKey(Vector3 point)
		{
			//IL_0000: