Decompiled source of TerrainMistile v1.1.0

TerrainMistile.dll

Decompiled 2 days 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 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.1.0")]
[assembly: InternalsVisibleTo("TerrainMistile.Tests")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.1.0.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.1.0")]
	public class TerrainMistilePlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private static class ConfigPersistence
		{
			public static void Save(ConfigFile config, bool reload = false)
			{
				bool saveOnConfigSet = config.SaveOnConfigSet;
				config.SaveOnConfigSet = false;
				try
				{
					if (reload)
					{
						config.Reload();
					}
					config.Save();
				}
				finally
				{
					config.SaveOnConfigSet = saveOnConfigSet;
				}
			}
		}

		internal const string ModName = "TerrainMistile";

		internal const string ModVersion = "1.1.0";

		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");

		private bool _initialized;

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

		private static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.TerrainMistile")
		{
			DisplayName = "TerrainMistile",
			CurrentVersion = "1.1.0",
			MinimumRequiredVersion = "1.1.0",
			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;
			try
			{
				_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");
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				_harmony.PatchAll(executingAssembly);
				TerrainMistileLocalization.ApplyToCurrentLanguage();
				TerrainMistileExternalTerrainCompat.Initialize(TerrainMistileLogger, _harmony);
				SetupWatcher();
				if (ConfigSync.IsSourceOfTruth)
				{
					PushLocalSpawnRulesYamlToSync();
				}
				((BaseUnityPlugin)this).Config.Save();
				_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
				_initialized = true;
			}
			catch
			{
				Cleanup();
				throw;
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			try
			{
				if (_initialized)
				{
					ConfigPersistence.Save(((BaseUnityPlugin)this).Config);
				}
			}
			finally
			{
				Cleanup();
			}
		}

		private void Cleanup()
		{
			_initialized = false;
			try
			{
				if (SpawnRulesYaml != null)
				{
					SpawnRulesYaml.ValueChanged -= OnSyncedSpawnRulesYamlChanged;
				}
				ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged;
				try
				{
					_harmony.UnpatchSelf();
				}
				finally
				{
					TerrainMistilePrefab.ReleasePrefabs();
				}
			}
			finally
			{
				try
				{
					_watcher?.Dispose();
				}
				finally
				{
					_spawnRulesWatcher?.Dispose();
				}
			}
		}

		private void Update()
		{
			if (_initialized)
			{
				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;
					}
					ConfigPersistence.Save(((BaseUnityPlugin)this).Config, 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 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, bool paintOnly);

		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, bool resetPaint)
		{
			//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, paintOnly: false);
			Heightmap val = _heightmap.Invoke(terrainComp);
			val.Poke(0, false);
			if (resetPaint)
			{
				_paintMask.Invoke(terrainComp) = val.GetPaintMask().GetPixels();
			}
		}

		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, new Type[1] { typeof(bool) }, null) ?? throw new MissingMethodException(typeof(TerrainComp).FullName, name);
			if (methodInfo.IsStatic || methodInfo.ReturnType != typeof(void))
			{
				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 const float GuidanceCooldown = 60f;

		private static float _nextGuidanceTime;

		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);
		}

		internal static void ResetGuidanceState()
		{
			_nextGuidanceTime = 0f;
		}

		internal static void TryShowGuidance(Vector3 center)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || Time.time < _nextGuidanceTime)
			{
				return;
			}
			TerrainMistileBiomeSpawnRule rule = TerrainMistileSpawnRules.GetRule(TerrainMistileSpawnRules.GetBiomeKey(center));
			if (rule.PlayerBaseValue > 0 && !(rule.BaseCheckRadius <= 0f))
			{
				Vector3 position = ((Component)localPlayer).transform.position;
				float num = position.x - center.x;
				float num2 = position.z - center.z;
				if (!(num * num + num2 * num2 > 4096f))
				{
					string text = ((Localization.instance != null) ? Localization.instance.Localize("$terrainmistile_compendium_topic") : "TerrainMistile");
					string text2 = ((Localization.instance != null) ? Localization.instance.Localize("$terrainmistile_message_check_compendium", new string[1] { text }) : ("Check the Compendium: " + text));
					((Character)localPlayer).Message((MessageType)2, text2, 0, (Sprite)null, false);
					_nextGuidanceTime = Time.time + 60f;
				}
			}
		}

		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 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			}
			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 ExpandWorldDataBlueprintTypeName = "ExpandWorldData.Blueprint";

		private const string ExpandWorldDataTerrainTypeName = "ExpandWorldData.Terrain";

		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 readonly Dictionary<Type, PropertyInfo> SnapshotHasValuesProperties = new Dictionary<Type, PropertyInfo>();

		private static ManualLogSource? _logger;

		private static Harmony? _harmony;

		private static bool _terrainPatched;

		private static bool _blueprintTerrainPatched;

		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;

		private static MethodInfo? _tryGetBlueprintMethod;

		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 || !_blueprintTerrainPatched || !_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();
			TryPatchBlueprintTerrainHandler();
			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 TryPatchBlueprintTerrainHandler()
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			if (_blueprintTerrainPatched || _harmony == null)
			{
				return;
			}
			Type type = FindLoadedType("ExpandWorldData.Terrain");
			Type type2 = FindLoadedType("ExpandWorldData.Blueprint");
			if (type == null || type2 == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "ApplyBlueprint", new Type[5]
			{
				type2,
				typeof(Vector3),
				typeof(Quaternion),
				typeof(SpawnMode),
				typeof(List<GameObject>)
			}, (Type[])null);
			if (!(methodInfo == null))
			{
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(TerrainMistileExternalTerrainCompat), "ApplyBlueprintPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_blueprintTerrainPatched = true;
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)"Expand World Data blueprint terrain snapshot 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_0045: 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);
			}
			try
			{
				if (TryGetLoadedBlueprint(text, out object blueprint) && blueprint != null)
				{
					float blueprintProtectionRadius = GetBlueprintProtectionRadius(radius, data, blueprint);
					TerrainMistileSystem.RegisterProtectedTerrainArea(pos, blueprintProtectionRadius, "Expand World Data blueprint terrain " + text);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Expand World Data blueprint terrain protection failed: " + ex.Message));
				}
			}
		}

		private static void ApplyBlueprintPrefix(object blueprint, Vector3 position)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				float blueprintSnapshotRadius = GetBlueprintSnapshotRadius(blueprint);
				if (blueprintSnapshotRadius > 0f)
				{
					TerrainMistileSystem.RegisterProtectedTerrainArea(position, blueprintSnapshotRadius + 5f, "Expand World Data blueprint terrain " + GetString(blueprint, "Name"));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Expand World Data blueprint terrain snapshot protection failed: " + ex.Message));
				}
			}
		}

		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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00b7: 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 (!TryGetLoadedBlueprint(prefabName, out object blueprint))
					{
						return;
					}
					if (blueprint != null)
					{
						object locationYaml;
						bool flag = TryGetLocationYaml(value.m_location, out locationYaml);
						if (!flag && _tryGetLocationYamlMethod == null)
						{
							return;
						}
						object data = (flag ? locationYaml : null);
						float blueprintProtectionRadius = GetBlueprintProtectionRadius(value.m_location.m_exteriorRadius, data, blueprint);
						list.Add(new TerrainMistileSystem.ProtectedTerrainAreaData(value.m_position, blueprintProtectionRadius, "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 TryGetLoadedBlueprint(string prefab, out object? blueprint)
		{
			blueprint = null;
			if (!EnsureBlueprintReflectionMethods())
			{
				return false;
			}
			try
			{
				if (_isBlueprintPrefabMethod.Invoke(null, new object[1] { prefab }) as bool? != true)
				{
					return true;
				}
				object[] array = new object[2] { prefab, null };
				if (_tryGetBlueprintMethod.Invoke(null, array) as bool? != true || array[1] == null)
				{
					return false;
				}
				blueprint = array[1];
				return true;
			}
			catch (Exception ex)
			{
				_isBlueprintPrefabMethod = null;
				_tryGetBlueprintMethod = 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 && _isBlueprintPrefabMethod != null)
				{
					return _tryGetBlueprintMethod != null;
				}
				return false;
			}
			Type type = FindLoadedType("ExpandWorldData.LocationExtra");
			Type type2 = FindLoadedType("ExpandWorldData.LocationYaml");
			Type type3 = FindLoadedType("ExpandWorldData.BlueprintManager");
			Type type4 = FindLoadedType("ExpandWorldData.Blueprint");
			if (type == null || type2 == null || type3 == null || type4 == 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);
			_tryGetBlueprintMethod = AccessTools.Method(type3, "TryGet", new Type[2]
			{
				typeof(string),
				type4.MakeByRefType()
			}, (Type[])null);
			_blueprintReflectionResolved = true;
			if (_tryGetLocationYamlMethod != null && _isBlueprintPrefabMethod != null)
			{
				return _tryGetBlueprintMethod != null;
			}
			return false;
		}

		internal static float GetBlueprintProtectionRadius(float exteriorRadius, object? data, object blueprint)
		{
			float num = Mathf.Max(exteriorRadius, GetBlueprintSnapshotRadius(blueprint));
			if (data != null)
			{
				float num2 = Mathf.Max(exteriorRadius, GetFloat(data, "exteriorRadius"));
				bool isBlueprint = GetSnapshotNodeSpacing(GetField(blueprint, "TerrainHeight")?.GetValue(blueprint)) == 0f;
				num = Mathf.Max(new float[4]
				{
					num,
					num2,
					GetTerrainRadius(exteriorRadius, isBlueprint, data),
					GetNoBuildRadius(data, num2)
				});
			}
			return num + 5f;
		}

		internal static float GetBlueprintSnapshotRadius(object blueprint)
		{
			float num = Mathf.Max(GetSnapshotNodeSpacing(GetField(blueprint, "TerrainHeight")?.GetValue(blueprint)), GetSnapshotNodeSpacing(GetField(blueprint, "TerrainPaint")?.GetValue(blueprint)));
			if (num == 0f)
			{
				return 0f;
			}
			if (!(GetField(blueprint, "Radius")?.GetValue(blueprint) is float num2) || float.IsNaN(num2) || float.IsInfinity(num2) || num2 < 0f || float.IsInfinity(num2 + num))
			{
				throw new InvalidOperationException("Invalid Expand World Data blueprint terrain radius.");
			}
			return num2 + num;
		}

		private static float GetSnapshotNodeSpacing(object? snapshot)
		{
			if (snapshot == null)
			{
				return 0f;
			}
			Type type = snapshot.GetType();
			if (!SnapshotHasValuesProperties.TryGetValue(type, out PropertyInfo value))
			{
				value = type.GetProperty("HasValues", BindingFlags.Instance | BindingFlags.Public) ?? throw new MissingMemberException(type.FullName, "HasValues");
				SnapshotHasValuesProperties[type] = value;
			}
			object value2 = value.GetValue(snapshot);
			if (!(value2 is bool) || !(bool)value2)
			{
				return 0f;
			}
			float num = GetFloat(snapshot, "DistanceBetweenNodes");
			if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f)
			{
				throw new InvalidOperationException("Invalid Expand World Data terrain node spacing.");
			}
			return num;
		}

		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 TerrainMistileExpandWorldDataBiomeCompat
	{
		private sealed class ExpandWorldDataBiomeYaml
		{
			public string Biome { get; set; } = "";
		}

		private const int FirstCustomBiomeBase = 512;

		private static readonly Dictionary<string, int> OriginalBiomes = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
		{
			["None"] = 0,
			["Meadows"] = 1,
			["Swamp"] = 2,
			["Mountain"] = 4,
			["BlackForest"] = 8,
			["Plains"] = 16,
			["AshLands"] = 32,
			["DeepNorth"] = 64,
			["Ocean"] = 256,
			["Mistlands"] = 512
		};

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

		private static readonly Dictionary<string, int> FileNameToBiome = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<int, string> FileBiomeToName = new Dictionary<int, string>();

		private static bool _fileMappingLoaded;

		private static MethodInfo? _tryGetBiomeMethod;

		private static MethodInfo? _tryGetDisplayNameMethod;

		internal static bool TryGetVanillaBiome(string name, out int biome)
		{
			return OriginalBiomes.TryGetValue(name, out biome);
		}

		internal static bool TryGetBiome(string name, out int biome)
		{
			biome = 0;
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			if (TryGetBiomeFromExpandWorldData(name.Trim(), out biome))
			{
				return true;
			}
			EnsureFileMappingLoaded();
			if (!FileNameToBiome.TryGetValue(name.Trim(), out biome))
			{
				return FileNameToBiome.TryGetValue(NormalizeName(name), out biome);
			}
			return true;
		}

		internal static bool TryGetDisplayName(int biome, out string name)
		{
			if (TryGetDisplayNameFromExpandWorldData(biome, out name))
			{
				return true;
			}
			EnsureFileMappingLoaded();
			return FileBiomeToName.TryGetValue(biome, out name);
		}

		private static bool TryGetBiomeFromExpandWorldData(string name, out int biome)
		{
			biome = 0;
			try
			{
				MethodInfo methodInfo = _tryGetBiomeMethod;
				if (methodInfo == null)
				{
					Type type = TerrainMistileExternalTerrainCompat.FindLoadedType("ExpandWorldData.BiomeManager");
					if (type == null)
					{
						return false;
					}
					methodInfo = (_tryGetBiomeMethod = type.GetMethod("TryGetBiome", BindingFlags.Static | BindingFlags.Public));
				}
				if (methodInfo == null)
				{
					return false;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				Type type2 = ((parameters.Length == 2) ? parameters[1].ParameterType.GetElementType() : null);
				if (methodInfo.ReturnType != typeof(bool) || parameters.Length != 2 || parameters[0].ParameterType != typeof(string) || !parameters[1].ParameterType.IsByRef || !parameters[1].IsOut || type2 == null || !type2.IsEnum)
				{
					_tryGetBiomeMethod = null;
					return false;
				}
				object[] array = new object[2]
				{
					name,
					Enum.ToObject(type2, 0)
				};
				object obj = methodInfo.Invoke(null, array);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					biome = Convert.ToInt32(array[1], CultureInfo.InvariantCulture);
					return true;
				}
			}
			catch
			{
				_tryGetBiomeMethod = null;
			}
			return false;
		}

		private static bool TryGetDisplayNameFromExpandWorldData(int biome, out string name)
		{
			name = "";
			try
			{
				MethodInfo methodInfo = _tryGetDisplayNameMethod;
				if (methodInfo == null)
				{
					Type type = TerrainMistileExternalTerrainCompat.FindLoadedType("ExpandWorldData.BiomeManager");
					if (type == null)
					{
						return false;
					}
					methodInfo = (_tryGetDisplayNameMethod = type.GetMethod("TryGetDisplayName", BindingFlags.Static | BindingFlags.Public));
				}
				if (methodInfo == null)
				{
					return false;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (methodInfo.ReturnType != typeof(bool) || parameters.Length != 2 || !parameters[0].ParameterType.IsEnum || !parameters[1].ParameterType.IsByRef || !parameters[1].IsOut || parameters[1].ParameterType.GetElementType() != typeof(string))
				{
					_tryGetDisplayNameMethod = null;
					return false;
				}
				object[] array = new object[2]
				{
					Enum.ToObject(parameters[0].ParameterType, biome),
					""
				};
				object obj = methodInfo.Invoke(null, array);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is string text && !string.IsNullOrWhiteSpace(text))
				{
					name = text;
					return true;
				}
			}
			catch
			{
				_tryGetDisplayNameMethod = null;
			}
			return false;
		}

		private static void EnsureFileMappingLoaded()
		{
			if (_fileMappingLoaded)
			{
				return;
			}
			string configPath = Paths.ConfigPath;
			if (!TryBuildFileMapping(string.IsNullOrWhiteSpace(configPath) ? "" : Path.Combine(configPath, "expand_world"), out Dictionary<string, int> nameToBiome, out Dictionary<int, string> biomeToName, out string error))
			{
				_fileMappingLoaded = true;
				if (error.Length > 0)
				{
					TerrainMistilePlugin.TerrainMistileLogger.LogWarning((object)error);
				}
				return;
			}
			FileNameToBiome.Clear();
			foreach (KeyValuePair<string, int> item in nameToBiome)
			{
				FileNameToBiome[item.Key] = item.Value;
			}
			FileBiomeToName.Clear();
			foreach (KeyValuePair<int, string> item2 in biomeToName)
			{
				FileBiomeToName[item2.Key] = item2.Value;
			}
			_fileMappingLoaded = true;
			if (error.Length > 0)
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogWarning((object)error);
			}
		}

		internal static void InvalidateFileMapping()
		{
			_fileMappingLoaded = false;
		}

		internal static bool TryBuildFileMappingForTests(string directory, out Dictionary<string, int> mapping, out string error)
		{
			Dictionary<int, string> biomeToName;
			return TryBuildFileMapping(directory, out mapping, out biomeToName, out error);
		}

		private static bool TryBuildFileMapping(string directory, out Dictionary<string, int> nameToBiome, out Dictionary<int, string> biomeToName, out string error)
		{
			nameToBiome = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			biomeToName = new Dictionary<int, string>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			error = "";
			foreach (KeyValuePair<string, int> originalBiome in OriginalBiomes)
			{
				AddFileBiomeName(originalBiome.Key, originalBiome.Value, hashSet, nameToBiome, biomeToName);
			}
			if (!Directory.Exists(directory))
			{
				return true;
			}
			string[] files;
			try
			{
				files = Directory.GetFiles(directory, "expand_biomes*.yaml", SearchOption.AllDirectories);
			}
			catch (Exception ex)
			{
				error = "Failed to enumerate Expand World Data biome files: " + ex.Message;
				return false;
			}
			Array.Sort(files, (string left, string right) => StringComparer.OrdinalIgnoreCase.Compare(right, left));
			int nextBiome = 512;
			string[] array = files;
			foreach (string text in array)
			{
				List<ExpandWorldDataBiomeYaml> list;
				try
				{
					list = ExpandWorldDataBiomeDeserializer.Deserialize<List<ExpandWorldDataBiomeYaml>>(File.ReadAllText(text));
				}
				catch (Exception ex2)
				{
					string text2 = "Skipping invalid Expand World Data biome file '" + text + "': " + ex2.Message;
					error = ((error.Length == 0) ? text2 : (error + Environment.NewLine + text2));
					continue;
				}
				if (list == null)
				{
					continue;
				}
				foreach (ExpandWorldDataBiomeYaml item in list)
				{
					string text3 = item?.Biome?.Trim() ?? "";
					if (text3.Length != 0 && !hashSet.Contains(text3))
					{
						if (!TryGetNextBiome(nextBiome, out nextBiome))
						{
							error = "Expand World Data biome limit was exceeded while assigning '" + text3 + "'.";
							return false;
						}
						AddFileBiomeName(text3, nextBiome, hashSet, nameToBiome, biomeToName);
					}
				}
			}
			return true;
		}

		private static bool TryGetNextBiome(int biome, out int nextBiome)
		{
			int num;
			switch (biome)
			{
			case 128:
				nextBiome = 0;
				return false;
			default:
				num = 2 * biome;
				break;
			case int.MinValue:
				num = 128;
				break;
			}
			uint num2 = (uint)num;
			nextBiome = (int)num2;
			return true;
		}

		private static void AddFileBiomeName(string name, int biome, HashSet<string> exactBiomeNames, Dictionary<string, int> nameToBiome, Dictionary<int, string> biomeToName)
		{
			exactBiomeNames.Add(name);
			nameToBiome[name] = biome;
			string text = NormalizeName(name);
			if (!string.Equals(name, text, StringComparison.OrdinalIgnoreCase) && !exactBiomeNames.Contains(text))
			{
				nameToBiome[text] = biome;
			}
			biomeToName[biome] = name;
		}

		internal static string NormalizeName(string value)
		{
			return value.Trim().Replace(" ", "").Replace("_", "")
				.Replace("-", "");
		}
	}
	internal static class TerrainMistileLocalization
	{
		private static class GameAccess
		{
			internal static readonly FieldRef<Localization> Instance = AccessTools.StaticFieldRefAccess<Localization>(AccessTools.Field(typeof(Localization), "m_instance"));

			internal static readonly FieldRef<Localization, Dictionary<string, string>> Translations = AccessTools.FieldRefAccess<Localization, Dictionary<string, string>>("m_translations");

			internal static readonly FieldRef<Localization, LRUCache<string>> Cache = AccessTools.FieldRefAccess<Localization, LRUCache<string>>("m_cache");
		}

		[HarmonyPatch(typeof(Localization), "SetupLanguage", new Type[] { typeof(string) })]
		private static class SetupLanguagePatch
		{
			private static void Postfix(string language, Dictionary<string, string> ___m_translations, LRUCache<string> ___m_cache)
			{
				ApplyLanguage(___m_translations, language);
				___m_cache.EvictAll();
			}
		}

		[HarmonyPatch(typeof(Localization), "LoadLanguages")]
		private static class LoadLanguagesPatch
		{
			private static void Postfix(List<string> __result)
			{
				AddLanguages(__result);
			}
		}

		private const string FileExtension = ".yml";

		private static readonly string FilePrefix = "TerrainMistile.";

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

		private static readonly char[] InvalidTokenCharacters = " (){}[]+-!?/\\&%,.:-=<>\n".ToCharArray();

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

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

		internal static void ApplyToCurrentLanguage()
		{
			Localization val = GameAccess.Instance.Invoke();
			if (val != null)
			{
				AddLanguages(val.GetLanguages());
				ApplyLanguage(GameAccess.Translations.Invoke(val), val.GetSelectedLanguage());
				GameAccess.Cache.Invoke(val).EvictAll();
			}
		}

		private static void AddLanguages(List<string> languages)
		{
			foreach (string key in Languages.Keys)
			{
				if (!languages.Contains(key))
				{
					languages.Add(key);
				}
			}
		}

		private static void ApplyLanguage(Dictionary<string, string> target, string language)
		{
			Languages.TryGetValue("English", out Dictionary<string, string> value);
			Languages.TryGetValue(language, out Dictionary<string, string> value2);
			ApplyTranslations(target, value, value2);
		}

		internal static void ApplyTranslations(Dictionary<string, string> target, Dictionary<string, string>? english, Dictionary<string, string>? selected)
		{
			if (english != null)
			{
				foreach (KeyValuePair<string, string> item in english)
				{
					target[item.Key] = item.Value;
				}
			}
			if (selected == null || selected == english)
			{
				return;
			}
			foreach (KeyValuePair<string, string> item2 in selected)
			{
				target[item2.Key] = item2.Value;
			}
		}

		private static bool TryLoadEmbeddedYaml(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(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(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(language, yaml, text, logger))
				{
					dictionary[language] = text;
				}
			}
		}

		private static bool TryAddYamlTranslations(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
			{
				if (!Languages.TryGetValue(language, out Dictionary<string, string> value))
				{
					Languages.Add(language, value = new Dictionary<string, string>(StringComparer.Ordinal));
				}
				foreach (KeyValuePair<string, string> item in translations)
				{
					string text = item.Key.TrimStart(new char[1] { '$' });
					if (text.Length == 0 || text.IndexOfAny(InvalidTokenCharacters) >= 0)
					{
						logger.LogWarning((object)("Ignoring invalid localization token '" + item.Key + "' from " + source + "."));
					}
					else
					{
						value[text] = item.Value;
					}
				}
				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>),
		typeof(bool)
	})]
	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
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class RegisterPrefabsPatch
		{
			private static void Prefix(ZNetScene __instance, Dictionary<int, GameObject> ___m_namedPrefabs)
			{
				RegisterPrefabs(__instance, ___m_namedPrefabs);
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "OnDestroy")]
		private static class ReleasePrefabsPatch
		{
			private static void Prefix(ZNetScene __instance)
			{
				if (_registeredScene == __instance)
				{
					ReleasePrefabs();
				}
			}
		}

		internal const string PrefabName = "TerrainMistile";

		internal const string EnforcerPrefabName = "TerrainMistileEnforcer";

		private const string SourcePrefabName = "Mistile";

		private static ZNetScene? _registeredScene;

		private static GameObject? _container;

		private static Dictionary<int, GameObject>? _namedPrefabs;

		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>();

		private static void RegisterPrefabs(ZNetScene scene, Dictionary<int, GameObject> namedPrefabs)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			if (_registeredScene == scene)
			{
				return;
			}
			ReleasePrefabs();
			try
			{
				GameObject val = null;
				foreach (GameObject prefab in scene.m_prefabs)
				{
					if (Object.op_Implicit((Object)(object)prefab))
					{
						CheckNameConflict(prefab);
						if (((Object)prefab).name == "Mistile")
						{
							val = prefab;
						}
					}
				}
				foreach (GameObject nonNetViewPrefab in scene.m_nonNetViewPrefabs)
				{
					if (Object.op_Implicit((Object)(object)nonNetViewPrefab))
					{
						CheckNameConflict(nonNetViewPrefab);
					}
				}
				if (!Object.op_Implicit((Object)(object)val) || namedPrefabs.ContainsKey(StringExtensionMethods.GetStableHashCode("TerrainMistile")) || namedPrefabs.ContainsKey(StringExtensionMethods.GetStableHashCode("TerrainMistileEnforcer")))
				{
					throw new InvalidOperationException("Mistile source is missing or a TerrainMistile prefab hash is already registered.");
				}
				_registeredScene = scene;
				_namedPrefabs = namedPrefabs;
				_container = new GameObject("TerrainMistile prefabs");
				_container.SetActive(false);
				_container.transform.SetParent(((Component)scene).transform, false);
				_registeredPrefab = CreateTerrainMistilePrefab(val, "TerrainMistile", enforcer: false, RegisteredPrefabVisualMaterials);
				_registeredEnforcerPrefab = CreateTerrainMistilePrefab(val, "TerrainMistileEnforcer", enforcer: true, RegisteredEnforcerPrefabVisualMaterials);
				scene.m_prefabs.Add(_registeredPrefab);
				scene.m_prefabs.Add(_registeredEnforcerPrefab);
				TerrainMistilePlugin.TerrainMistileLogger.LogInfo((object)"Registered TerrainMistile and TerrainMistileEnforcer prefabs.");
			}
			catch (Exception arg)
			{
				ReleasePrefabs();
				TerrainMistilePlugin.TerrainMistileLogger.LogError((object)$"TerrainMistile prefab registration failed: {arg}");
			}
		}

		private static void CheckNameConflict(GameObject prefab)
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
			if (stableHashCode == StringExtensionMethods.GetStableHashCode("TerrainMistile") || stableHashCode == StringExtensionMethods.GetStableHashCode("TerrainMistileEnforcer"))
			{
				throw new InvalidOperationException("Prefab '" + ((Object)prefab).name + "' conflicts with a TerrainMistile prefab hash.");
			}
		}

		internal static void ReleasePrefabs()
		{
			RemovePrefab(_registeredPrefab);
			RemovePrefab(_registeredEnforcerPrefab);
			_registeredPrefab = null;
			_registeredEnforcerPrefab = null;
			_registeredScene = null;
			_namedPrefabs = null;
			if (Object.op_Implicit((Object)(object)_container))
			{
				Object.Destroy((Object)(object)_container);
			}
			_container = null;
			ReleaseVisualMaterials(RegisteredPrefabVisualMaterials);
			ReleaseVisualMaterials(RegisteredEnforcerPrefabVisualMaterials);
		}

		private static void RemovePrefab(GameObject? prefab)
		{
			if (prefab != null)
			{
				if (Object.op_Implicit((Object)(object)_registeredScene))
				{
					_registeredScene.m_prefabs.Remove(prefab);
				}
				if (Object.op_Implicit((Object)(object)prefab) && _namedPrefabs != null && _namedPrefabs.TryGetValue(StringExtensionMethods.GetStableHashCode(((Object)prefab).name), out GameObject value) && value == prefab)
				{
					_namedPrefabs.Remove(StringExtensionMethods.GetStableHashCode(((Object)prefab).name));
				}
			}
		}

		private static GameObject CreateTerrainMistilePrefab(GameObject source, string prefabName, bool enforcer, List<Material> registeredVisualMaterials)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(source, _container.transform);
			((Object)val).name = prefabName;
			val.SetActive(true);
			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))
			{
				terrainM