Decompiled source of TerrainMistile v1.0.1

TerrainMistile.dll

Decompiled 5 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.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TerrainMistile")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("TerrainMistile")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

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

		internal const string ModName = "TerrainMistile";

		internal const string ModVersion = "1.0.1";

		internal const string Author = "sighsorry";

		internal const string DefaultDisplayName = "Earth Warden";

		private const string ModGUID = "sighsorry.TerrainMistile";

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

		private static string ConfigFileFullPath;

		private static string SpawnRulesFileName;

		internal static string SpawnRulesFileFullPath;

		internal static string ConnectionError;

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

		public static readonly ManualLogSource TerrainMistileLogger;

		private static readonly ConfigSync ConfigSync;

		private FileSystemWatcher _watcher;

		private FileSystemWatcher _spawnRulesWatcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private DateTime _lastSpawnRulesReloadTime;

		private const long RELOAD_DELAY = 10000000L;

		private static ConfigEntry<Toggle> _serverConfigLocked;

		private static ConfigEntry<string> _displayName;

		internal static CustomSyncedValue<string> SpawnRulesYaml;

		internal static string DisplayName
		{
			get
			{
				string text = (_displayName?.Value ?? "").Trim();
				if (text.Length != 0)
				{
					return text;
				}
				return "Earth Warden";
			}
		}

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_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);
			_displayName = config("2 - Display", "Display Name", "Earth Warden", "In-game name shown to players for TerrainMistile.");
			TerrainMistileSpawnRules.Initialize(TerrainMistileLogger);
			TerrainMistileSpawnRules.EnsureFileExists(SpawnRulesFileFullPath);
			SpawnRulesYaml = new CustomSyncedValue<string>(ConfigSync, "SpawnRulesYaml", string.Empty);
			SpawnRulesYaml.ValueChanged += OnSyncedSpawnRulesYamlChanged;
			ConfigSync.SourceOfTruthChanged += OnSourceOfTruthChanged;
			TerrainMistileSpawnRules.LoadYamlText(File.ReadAllText(SpawnRulesFileFullPath), "local fallback");
			TerrainMistilePrefab.RegisterPrefabHook();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			TerrainMistileExternalTerrainCompat.Initialize(TerrainMistileLogger, _harmony);
			SetupWatcher();
			if (ConfigSync.IsSourceOfTruth)
			{
				PushLocalSpawnRulesYamlToSync();
			}
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

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

		private void Update()
		{
			TerrainMistileSystem.UpdateResetEffectRpcRegistration();
			TerrainMistileExternalTerrainCompat.Update();
			TerrainMistileSystem.UpdatePersistentTerrainSpawns();
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
			_spawnRulesWatcher = new FileSystemWatcher(Paths.ConfigPath, SpawnRulesFileName);
			_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
				{
					TerrainMistileLogger.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true);
					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)
				{
					TerrainMistileLogger.LogDebug((object)"Ignoring local TerrainMistile spawn rules YAML change while server data is authoritative.");
					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 (TerrainMistileSpawnRules.LoadYamlText(text, "local file"))
			{
				SpawnRulesYaml.Value = text;
			}
		}

		private void OnSyncedSpawnRulesYamlChanged()
		{
			TerrainMistileSpawnRules.LoadYamlText(SpawnRulesYaml.Value ?? string.Empty, ConfigSync.IsSourceOfTruth ? "local sync" : "server sync");
		}

		private void OnSourceOfTruthChanged(bool isSourceOfTruth)
		{
			if (isSourceOfTruth)
			{
				PushLocalSpawnRulesYamlToSync();
			}
			else
			{
				TerrainMistileSpawnRules.LoadYamlText(SpawnRulesYaml.Value ?? string.Empty, "server sync");
			}
		}

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

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

		static TerrainMistilePlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			SpawnRulesFileName = "TerrainMistile.yml";
			string configPath2 = Paths.ConfigPath;
			directorySeparatorChar = Path.DirectorySeparatorChar;
			SpawnRulesFileFullPath = configPath2 + directorySeparatorChar + SpawnRulesFileName;
			ConnectionError = "";
			TerrainMistileLogger = Logger.CreateLogSource("TerrainMistile");
			ConfigSync = new ConfigSync("sighsorry.TerrainMistile")
			{
				DisplayName = "TerrainMistile",
				CurrentVersion = "1.0.1",
				MinimumRequiredVersion = "1.0.1"
			};
			_serverConfigLocked = null;
			_displayName = null;
			SpawnRulesYaml = null;
		}
	}
	public class TerrainMistileBehaviour : MonoBehaviour
	{
		internal const string SelfDestructZdoKey = "TerrainMistileSelfDestruct";

		internal const string TerrainMistileZdoKey = "TerrainMistile";

		internal const string TerrainSourceZdoKey = "TerrainMistileSource";

		internal const string TerrainSourceSetZdoKey = "TerrainMistileSourceSet";

		internal const string ResetRadiusZdoKey = "TerrainMistileResetRadius";

		internal const string HealthZdoKey = "TerrainMistileHealth";

		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 Character _character;

		private MonsterAI _monsterAI;

		private ZNetView _nview;

		private Vector3 _terrainTarget;

		private float _resetRadius = 8f;

		private bool _hasTerrainTarget;

		private bool _selfDestructTriggered;

		private bool _terrainResetDone;

		private bool _visualColorApplied;

		private float _nextVisualSyncTime;

		private void Awake()
		{
			_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()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_character))
			{
				Character character = _character;
				character.m_onDeath = (Action)Delegate.Remove(character.m_onDeath, new Action(OnDeath));
			}
			if (_hasTerrainTarget)
			{
				TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
			}
			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)
			{
				ApplyIdentity();
				ConfigureTerrainSeeker();
				if (TryLoadTerrainTarget())
				{
					MoveTowardTerrainTarget();
				}
			}
		}

		internal void Initialize(Player target, Vector3 terrainOperationPoint, float resetRadius, float health)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			ApplyIdentity();
			ConfigureTerrainSeeker();
			ApplyHealth(health);
			SetTerrainTarget(terrainOperationPoint, resetRadius);
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				_nview.GetZDO().Set("TerrainMistile", true);
			}
		}

		internal void MarkSelfDestruct(string reason = "self-destruct")
		{
			_selfDestructTriggered = true;
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				_nview.GetZDO().Set("TerrainMistileSelfDestruct", true);
			}
			TerrainMistilePlugin.TerrainMistileLogger.LogDebug((object)("Marked TerrainMistile self-destruct from " + reason + "."));
		}

		private void ApplyIdentity()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_character))
			{
				_character.m_name = TerrainMistilePlugin.DisplayName;
				_character.m_faction = (Faction)10;
				_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);
				ZDO zDO = _nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set("TerrainMistileHealth", health);
				}
			}
		}

		private void ApplyVisualColor(Color color, bool syncToZdo)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//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)
			TerrainMistilePrefab.ApplyVisuals(((Component)this).gameObject, color);
			_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;
				_monsterAI.m_targetCreature = null;
				_monsterAI.m_targetStatic = null;
				if (Object.op_Implicit((Object)(object)((BaseAI)_monsterAI).m_nview) && ((BaseAI)_monsterAI).m_nview.IsValid())
				{
					((BaseAI)_monsterAI).SetHuntPlayer(false);
					((BaseAI)_monsterAI).SetAlerted(false);
				}
				else
				{
					((BaseAI)_monsterAI).m_huntPlayer = false;
					((BaseAI)_monsterAI).m_alerted = false;
				}
				((Behaviour)_monsterAI).enabled = false;
			}
		}

		private void SetTerrainTarget(Vector3 terrainOperationPoint, float resetRadius)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (_hasTerrainTarget)
			{
				TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
			}
			_terrainTarget = terrainOperationPoint;
			_resetRadius = Mathf.Max(0.1f, resetRadius);
			_hasTerrainTarget = true;
			TerrainMistileSystem.ReserveTerrainTarget(_terrainTarget, _resetRadius);
			ApplyVisualColorForTarget(_terrainTarget);
			if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner())
			{
				_nview.GetZDO().Set("TerrainMistileSource", terrainOperationPoint);
				_nview.GetZDO().Set("TerrainMistileSourceSet", true);
				_nview.GetZDO().Set("TerrainMistileResetRadius", _resetRadius);
			}
		}

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

		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)
			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);
			resetRadius = zDO.GetFloat("TerrainMistileResetRadius", 0f);
			return true;
		}

		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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			if (!_terrainResetDone && Object.op_Implicit((Object)(object)_character) && !_character.IsDead() && !TryRetargetOrDespawnFromEmptyImpact())
			{
				MarkSelfDestruct("terrain impact");
				TryResetTerrain("terrain impact");
				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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0050: 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_006b: 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)
			TryLoadTerrainTarget();
			if (!_hasTerrainTarget)
			{
				return false;
			}
			if (TerrainMistileSystem.HasModifiedTerrainAround(_terrainTarget, _resetRadius))
			{
				return false;
			}
			TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
			if (TerrainMistileSystem.TryFindReplacementTerrainTarget(((Component)this).transform.position, out var modifiedPoint))
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogDebug((object)$"TerrainMistile target at {_terrainTarget} was already reset; retargeting to {modifiedPoint}.");
				SetTerrainTarget(modifiedPoint, TerrainMistileSystem.GetResetRadiusForPoint(modifiedPoint));
				return true;
			}
			TerrainMistilePlugin.TerrainMistileLogger.LogDebug((object)$"TerrainMistile target at {_terrainTarget} was already reset and no replacement target was found; destroying without terrain reset.");
			DestroyWithoutTerrainReset();
			return true;
		}

		private void DestroyWithoutTerrainReset()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			_terrainResetDone = true;
			if (_hasTerrainTarget)
			{
				TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
			}
			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("death");
		}

		internal void TryResetTerrain(string reason)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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;
				}
				_selfDestructTriggered |= _nview.GetZDO().GetBool("TerrainMistileSelfDestruct", false);
			}
			if (_terrainResetDone)
			{
				return;
			}
			if (!_selfDestructTriggered)
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogDebug((object)("Skipping TerrainMistile terrain reset on " + reason + "; self-destruct was not detected."));
				return;
			}
			_terrainResetDone = true;
			TryLoadTerrainTarget();
			Vector3 val = (_hasTerrainTarget ? _terrainTarget : ((Component)this).transform.position);
			if (_hasTerrainTarget)
			{
				TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget);
			}
			TerrainMistilePlugin.TerrainMistileLogger.LogInfo((object)$"TerrainMistile terrain reset triggered by {reason} at {val}.");
			TerrainMistileSystem.ResetTerrainAround(val, _resetRadius, resetPaint: true);
		}
	}
	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 ExpandWorldDataLocationDataTypeName = "ExpandWorldData.LocationData";

		private const string ExpandWorldDataLocationLoadingTypeName = "ExpandWorldData.LocationLoading";

		private const string ExpandWorldDataNoBuildManagerTypeName = "ExpandWorldData.NoBuildManager";

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

		private const float PatchRetryInterval = 2f;

		private static ManualLogSource? _logger;

		private static Harmony? _harmony;

		private static bool _terrainPatched;

		private static bool _protectionSyncPatched;

		private static float _nextPatchAttemptTime;

		public static void Initialize(ManualLogSource logger, Harmony harmony)
		{
			_logger = logger;
			_harmony = harmony;
			TryPatch();
		}

		public static void Update()
		{
			if ((!_terrainPatched || !_protectionSyncPatched) && !(Time.time < _nextPatchAttemptTime))
			{
				_nextPatchAttemptTime = Time.time + 2f;
				TryPatch();
			}
		}

		private static void TryPatch()
		{
			if (_harmony != null)
			{
				TryPatchTerrainHandler();
				TryPatchBlueprintProtectionSync();
			}
		}

		private static void TryPatchTerrainHandler()
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			if (_terrainPatched || _harmony == null)
			{
				return;
			}
			Type type = FindLoadedType("ExpandWorldData.LocationObjectDataAndSwap");
			Type type2 = FindLoadedType("ExpandWorldData.LocationData");
			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)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogDebug((object)"Expand World Data terrain compat skipped: HandleTerrain was not found.");
				}
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_terrainPatched = true;
			ManualLogSource? logger2 = _logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)"Expand World Data terrain compat initialized.");
			}
		}

		private static void TryPatchBlueprintProtectionSync()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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)
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogDebug((object)"Expand World Data noBuild compat skipped: NoBuildManager.UpdateData was not found.");
				}
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_protectionSyncPatched = true;
			ManualLogSource? logger2 = _logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)"Expand World Data blueprint terrain protection initialized.");
			}
		}

		private static Type? FindLoadedType(string fullName)
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				Type type = ((object)value.Instance)?.GetType().Assembly.GetType(fullName, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		private static void HandleTerrainPrefix(Vector3 pos, float radius, bool isBlueprint, object data)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			string text = GetString(data, "prefab");
			string text2 = GetString(data, "noBuild").Trim();
			float terrainRadius = GetTerrainRadius(radius, isBlueprint, data);
			if (terrainRadius > 0f)
			{
				TerrainMistileSystem.RegisterExternalTerrainIgnoreArea(pos, terrainRadius, "Expand World Data location terrain", 10f);
			}
			float noBuildRadius = GetNoBuildRadius(data, Mathf.Max(GetFloat(data, "exteriorRadius"), radius));
			float num = Mathf.Max(new float[4]
			{
				terrainRadius,
				radius,
				GetFloat(data, "exteriorRadius"),
				noBuildRadius
			}) + 5f;
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogDebug((object)($"EWD terrain compat HandleTerrain prefab='{text}', isBlueprint={isBlueprint}, pos={TerrainMistileSystem.FormatPoint(pos)}, inputRadius={radius:0.##}, " + $"terrainRadius={terrainRadius:0.##}, noBuild='{text2}', noBuildRadius={noBuildRadius:0.##}, protectedRadius={num:0.##}."));
			}
			if (isBlueprint)
			{
				TerrainMistileSystem.RegisterProtectedTerrainArea(pos, num, "Expand World Data blueprint terrain " + text);
			}
		}

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

		private static void SyncBlueprintProtectedAreas()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			IDictionary locationDataLookup = GetLocationDataLookup();
			IDictionary blueprintLookup = GetBlueprintLookup();
			ZoneSystem instance = ZoneSystem.instance;
			if (blueprintLookup == null || !Object.op_Implicit((Object)(object)instance))
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogDebug((object)$"EWD blueprint protected terrain sync skipped. HasBlueprints={blueprintLookup != null}, HasZoneSystem={(Object)(object)instance != (Object)null}.");
				}
				return;
			}
			TerrainMistileSystem.ClearProtectedTerrainAreas("Expand World Data blueprint terrain");
			int num = 0;
			int num2 = 0;
			foreach (LocationInstance value in instance.m_locationInstances.Values)
			{
				if (value.m_location != null)
				{
					string prefabName = value.m_location.m_prefabName;
					if (!string.IsNullOrWhiteSpace(prefabName) && blueprintLookup.Contains(prefabName))
					{
						num++;
						object obj = ((locationDataLookup != null && locationDataLookup.Contains(prefabName)) ? locationDataLookup[prefabName] : null);
						float num3 = ((obj == null) ? 0f : GetTerrainRadius(value.m_location.m_exteriorRadius, isBlueprint: true, obj));
						float num4 = ((obj == null) ? 0f : GetNoBuildRadius(obj, value.m_location.m_exteriorRadius));
						float radius = Mathf.Max(new float[3]
						{
							num3,
							value.m_location.m_exteriorRadius,
							num4
						}) + 5f;
						TerrainMistileSystem.RegisterProtectedTerrainArea(value.m_position, radius, "Expand World Data blueprint terrain " + prefabName);
						num2++;
					}
				}
			}
			ManualLogSource? logger2 = _logger;
			if (logger2 != null)
			{
				logger2.LogDebug((object)$"EWD blueprint protected terrain sync complete. BlueprintData={blueprintLookup.Count}, LocationData={locationDataLookup?.Count ?? 0}, matchedLocations={num}, protectedRegistered={num2}.");
			}
		}

		private static IDictionary? GetLocationDataLookup()
		{
			Type type = FindLoadedType("ExpandWorldData.LocationLoading");
			if (type == null)
			{
				return null;
			}
			return AccessTools.Field(type, "LocationData")?.GetValue(null) as IDictionary;
		}

		private static IDictionary? GetBlueprintLookup()
		{
			Type type = FindLoadedType("ExpandWorldData.LocationLoading");
			if (type == null)
			{
				return null;
			}
			return AccessTools.Field(type, "Objects")?.GetValue(null) as IDictionary;
		}

		private 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.OrdinalIgnoreCase)));
			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)
		{
			return data.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "SpawnLocation", new Type[]
	{
		typeof(ZoneLocation),
		typeof(int),
		typeof(Vector3),
		typeof(Quaternion),
		typeof(SpawnMode),
		typeof(List<GameObject>)
	})]
	internal static class ZoneSystemSpawnLocationPatch
	{
		private static void Prefix(ZoneLocation location, Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			TerrainMistileSystem.RegisterLocationTerrainLoadGrace(location, pos);
		}
	}
	[HarmonyPatch(typeof(Attack), "Stop")]
	internal static class AttackStopPatch
	{
		private static void Prefix(Attack __instance)
		{
			if (__instance != null && __instance.m_attackKillsSelf && !__instance.m_attackDone && Object.op_Implicit((Object)(object)__instance.m_character))
			{
				TerrainMistileBehaviour component = ((Component)__instance.m_character).GetComponent<TerrainMistileBehaviour>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.MarkSelfDestruct();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class CharacterApplyDamagePatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			if (Object.op_Implicit((Object)(object)__instance) && hit != null && (int)hit.m_hitType == 14)
			{
				TerrainMistileBehaviour component = ((Component)__instance).GetComponent<TerrainMistileBehaviour>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.MarkSelfDestruct();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "OnAttackTrigger")]
	internal static class HumanoidOnAttackTriggerPatch
	{
		private const string MistileKamikazePrefabName = "Mistile_kamikaze";

		private static void Prefix(Humanoid __instance)
		{
			if (!Object.op_Implicit((Object)(object)__instance) || !((Character)__instance).m_nview.IsValid() || !((Character)__instance).m_nview.IsOwner())
			{
				return;
			}
			TerrainMistileBehaviour component = ((Component)__instance).GetComponent<TerrainMistileBehaviour>();
			if (Object.op_Implicit((Object)(object)component))
			{
				ItemData currentWeapon = __instance.GetCurrentWeapon();
				if (currentWeapon != null && IsMistileKamikaze(currentWeapon))
				{
					component.MarkSelfDestruct("Mistile_kamikaze");
				}
			}
		}

		private static bool IsMistileKamikaze(ItemData weapon)
		{
			if (Object.op_Implicit((Object)(object)weapon.m_dropPrefab) && string.Equals(((Object)weapon.m_dropPrefab).name, "Mistile_kamikaze", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return string.Equals(weapon.m_shared.m_name, "Mistile_kamikaze", StringComparison.OrdinalIgnoreCase);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Destroy", new Type[] { typeof(GameObject) })]
	internal static class ZNetSceneDestroyPatch
	{
		private static void Prefix(object[] __args)
		{
			if (__args.Length == 0)
			{
				return;
			}
			object obj = __args[0];
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if (val != null && Object.op_Implicit((Object)(object)val))
			{
				TerrainMistileBehaviour component = val.GetComponent<TerrainMistileBehaviour>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.TryResetTerrain("destroy");
				}
			}
		}
	}
	internal static class TerrainMistilePrefab
	{
		internal const string PrefabName = "TerrainMistile";

		private const string SourcePrefabName = "Mistile";

		private static bool _hooked;

		private static bool _registered;

		private static GameObject? _registeredPrefab;

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

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

		private static void CreateTerrainMistilePrefab()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (_registered)
			{
				return;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("TerrainMistile", "Mistile");
			if (!Object.op_Implicit((Object)(object)val))
			{
				TerrainMistilePlugin.TerrainMistileLogger.LogError((object)"Could not clone vanilla prefab 'Mistile'.");
				return;
			}
			Character component = val.GetComponent<Character>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_name = TerrainMistilePlugin.DisplayName;
				component.m_faction = (Faction)10;
				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 = (GameObject[])(object)new GameObject[0];
			}
			if (!Object.op_Implicit((Object)(object)val.GetComponent<TerrainMistileBehaviour>()))
			{
				val.AddComponent<TerrainMistileBehaviour>();
			}
			MakeCollidersNonBlocking(val);
			ApplyVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor);
			PrefabManager.Instance.AddPrefab(val);
			_registeredPrefab = val;
			_registered = true;
			TerrainMistilePlugin.TerrainMistileLogger.LogInfo((object)"Registered 'TerrainMistile' prefab from 'Mistile'.");
		}

		internal static void RefreshRegisteredPrefabVisuals()
		{
			//IL_0011: 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)
			if (Object.op_Implicit((Object)(object)_registeredPrefab))
			{
				ApplyVisuals(_registeredPrefab, TerrainMistileSpawnRules.DefaultVisualColor);
				return;
			}
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab("TerrainMistile") : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				ApplyVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor);
			}
		}

		internal static void ApplyVisuals(GameObject root, Color color)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)root))
			{
				ApplyLightColor(root, color);
				ApplyParticleColor(root, color);
				ApplyMaterialColor(root, color);
			}
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

			public float? PlayerSearchRadius { get; set; }

			public float? SpawnChance { get; set; }

			public float? MaxDeformationSpawnChanceBonus { get; set; }

			public int? MaxSpawn { get; set; }

			public bool? PerPlayerSpawn { get; set; }

			public int? PlayerBaseValue { get; set; }

			public float? BaseCheckRadius { get; set; }

			public string? SpawnRadius { get; set; }

			public float? SpawnAltitude { get; set; }

			public float? ResetRadius { get; set; }

			public float? Health { get; set; }

			public string? VisualColor { get; set; }
		}

		private const float DefaultInterval = 60f;

		private const float DefaultPlayerSearchRadius = 32f;

		private const float DefaultSpawnChance = 0.25f;

		private const float DefaultMaxDeformationSpawnChanceBonus = 0.25f;

		private const bool DefaultScaleSpawnsWithNearbyPlayers = true;

		private const int DefaultIgnorePlayerBaseBaseValue = 1;

		private const float DefaultBaseCheckRadius = 24f;

		private const int DefaultMaxActiveTerrainMistilesPerArea = 3;

		private const float DefaultSpawnRadiusMin = 16f;

		private const float DefaultSpawnRadiusMax = 32f;

		private const float DefaultSpawnAltitude = 8f;

		private const float DefaultResetRadius = 8f;

		private const float DefaultHealth = 1f;

		internal const string DefaultVisualColorHex = "#45FF5A";

		private static readonly Color FallbackVisualColor = new Color(0.27f, 1f, 0.353f, 1f);

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

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

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

		private static TerrainMistileBiomeSpawnRule _defaultRule = CreateDefaultRule();

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

		private static float _maxPlayerSearchRadius = 32f;

		private static float _maxResetRadius = 8f;

		private static bool _hasEnabledRules = true;

		private static ManualLogSource? _logger;

		internal static float MaxPlayerSearchRadius => _maxPlayerSearchRadius;

		internal static float MaxResetRadius => _maxResetRadius;

		internal static bool HasEnabledRules => _hasEnabledRules;

		internal static Color DefaultVisualColor => _defaultRule.VisualColor;

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

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

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

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

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

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

		internal static int GetBiomeKey(Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected I4, but got Unknown
			return (int)Heightmap.FindBiome(point);
		}

		internal static string GetBiomeName(int biome)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (TerrainMistileExpandWorldDataBiomeCompat.TryGetDisplayName(biome, out string name))
			{
				return name;
			}
			string text = ((object)(Biome)biome/*cast due to .constrained prefix*/).ToString();
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return biome.ToString(CultureInfo.InvariantCulture);
		}

		private static bool TryParseYaml(string yaml, out TerrainMistileBiomeSpawnRule defaultRule, out Dictionary<int, TerrainMistileBiomeSpawnRule> parsedRules, out HashSet<string> playerBasePrefabNames, out string error)
		{
			defaultRule = CreateDefaultRule();
			parsedRules = new Dictionary<int, TerrainMistileBiomeSpawnRule>();
			playerBasePrefabNames = CreateDefaultPlayerBasePrefabNames();
			error = "";
			if (string.IsNullOrWhiteSpace(yaml))
			{
				return true;
			}
			Dictionary<object, object> dictionary;
			try
			{
				dictionary = Deserializer.Deserialize<Dictionary<object, object>>(yaml);
			}
			catch (Exception ex)
			{
				error = ex.Message;
				return false;
			}
			if (dictionary == null)
			{
				return true;
			}
			TerrainMistileSpawnRuleValues terrainMistileSpawnRuleValues = null;
			foreach (KeyValuePair<object, object> item in dictionary)
			{
				string yamlKey = GetYamlKey(item.Key);
				if (yamlKey.Equals("defaults", StringComparison.OrdinalIgnoreCase))
				{
					terrainMistileSpawnRuleValues = CreateValues(item.Value);
				}
				else if (yamlKey.Equals("playerBasePrefabs", StringComparison.OrdinalIgnoreCase))
				{
					playerBasePrefabNames = CreatePlayerBasePrefabNames(item.Value);
				}
			}
			if (terrainMistileSpawnRuleValues != null)
			{
				defaultRule = CreateRule(terrainMistileSpawnRuleValues, defaultRule);
			}
			foreach (KeyValuePair<object, object> item2 in dictionary)
			{
				string yamlKey2 = GetYamlKey(item2.Key);
				if (yamlKey2.Equals("defaults", StringComparison.OrdinalIgnoreCase) || yamlKey2.Equals("playerBasePrefabs", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				string text = NormalizeBiomeName(yamlKey2);
				if (text.Length == 0 || text.Equals("None", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				if (!TryResolveBiomeKey(yamlKey2, out var biome))
				{
					ManualLogSource? logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Ignoring unknown biome '" + yamlKey2 + "' in TerrainMistile spawn rules."));
					}
				}
				else
				{
					parsedRules[biome] = CreateRule(CreateValues(item2.Value), defaultRule);
				}
			}
			return true;
		}

		private static string GetYamlKey(object? key)
		{
			return key?.ToString()?.Trim() ?? "";
		}

		private static TerrainMistileSpawnRuleValues? CreateValues(object? value)
		{
			if (value == null)
			{
				return null;
			}
			if (!(value is IDictionary<object, object> dictionary))
			{
				return null;
			}
			TerrainMistileSpawnRuleValues terrainMistileSpawnRuleValues = new TerrainMistileSpawnRuleValues();
			foreach (KeyValuePair<object, object> item in dictionary)
			{
				switch (NormalizeFieldName(GetYamlKey(item.Key)))
				{
				case "interval":
				{
					if (TryGetFloat(item.Value, out var result11))
					{
						terrainMistileSpawnRuleValues.Interval = result11;
					}
					break;
				}
				case "playersearchradius":
				{
					if (TryGetFloat(item.Value, out var result7))
					{
						terrainMistileSpawnRuleValues.PlayerSearchRadius = result7;
					}
					break;
				}
				case "spawnchance":
				{
					if (TryGetFloat(item.Value, out var result3))
					{
						terrainMistileSpawnRuleValues.SpawnChance = result3;
					}
					break;
				}
				case "maxdeformationspawnchancebonus":
				{
					if (TryGetFloat(item.Value, out var result2))
					{
						terrainMistileSpawnRuleValues.MaxDeformationSpawnChanceBonus = result2;
					}
					break;
				}
				case "maxspawn":
				{
					if (TryGetInt(item.Value, out var result10))
					{
						terrainMistileSpawnRuleValues.MaxSpawn = result10;
					}
					break;
				}
				case "perplayerspawn":
				{
					if (TryGetBool(item.Value, out var result9))
					{
						terrainMistileSpawnRuleValues.PerPlayerSpawn = result9;
					}
					break;
				}
				case "playerbasevalue":
				{
					if (TryGetInt(item.Value, out var result5))
					{
						terrainMistileSpawnRuleValues.PlayerBaseValue = result5;
					}
					break;
				}
				case "basecheckradius":
				{
					if (TryGetFloat(item.Value, out var result))
					{
						terrainMistileSpawnRuleValues.BaseCheckRadius = result;
					}
					break;
				}
				case "spawnradius":
					terrainMistileSpawnRuleValues.SpawnRadius = item.Value?.ToString();
					break;
				case "spawnaltitude":
				{
					if (TryGetFloat(item.Value, out var result8))
					{
						terrainMistileSpawnRuleValues.SpawnAltitude = result8;
					}
					break;
				}
				case "resetradius":
				{
					if (TryGetFloat(item.Value, out var result6))
					{
						terrainMistileSpawnRuleValues.ResetRadius = result6;
					}
					break;
				}
				case "health":
				{
					if (TryGetFloat(item.Value, out var result4))
					{
						terrainMistileSpawnRuleValues.Health = result4;
					}
					break;
				}
				case "visualcolor":
					terrainMistileSpawnRuleValues.VisualColor = item.Value?.ToString();
					break;
				}
			}
			return terrainMistileSpawnRuleValues;
		}

		private static HashSet<string> CreatePlayerBasePrefabNames(object? value)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (value is string value2)
			{
				AddPlayerBasePrefabNames(value2, hashSet);
				return hashSet;
			}
			if (value is IEnumerable<object> enumerable)
			{
				{
					foreach (object item in enumerable)
					{
						AddPlayerBasePrefabNames(item?.ToString(), hashSet);
					}
					return hashSet;
				}
			}
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogWarning((object)"Ignoring invalid playerBasePrefabs value in TerrainMistile spawn rules. Use a YAML list or comma-separated string.");
			}
			return hashSet;
		}

		private static HashSet<string> CreateDefaultPlayerBasePrefabNames()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] defaultPlayerBasePrefabNames = DefaultPlayerBasePrefabNames;
			for (int i = 0; i < defaultPlayerBasePrefabNames.Length; i++)
			{
				AddPlayerBasePrefabNames(defaultPlayerBasePrefabNames[i], hashSet);
			}
			return hashSet;
		}

		private static void AddPlayerBasePrefabNames(string? value, HashSet<string> names)
		{
			string text = value?.Trim();
			if (text == null || text.Length == 0)
			{
				return;
			}
			string[] array = text.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length > 0)
				{
					names.Add(text2);
				}
			}
		}

		private static string NormalizeFieldName(string value)
		{
			return value.Trim().Replace(" ", "").Replace("_", "")
				.Replace("-", "")
				.ToLowerInvariant();
		}

		private static bool TryGetFloat(object? value, out float result)
		{
			if (!(value is float num))
			{
				if (!(value is double num2))
				{
					if (!(value is decimal num3))
					{
						if (!(value is int num4))
						{
							if (!(value is long num5))
							{
								if (value is string value2)
								{
									return TryParseFloat(value2, out result);
								}
								result = 0f;
								return false;
							}
							result = num5;
							return true;
						}
						result = num4;
						return true;
					}
					result = (float)num3;
					return true;
				}
				result = (float)num2;
				return true;
			}
			result = num;
			return true;
		}

		private static bool TryGetInt(object? value, out int result)
		{
			if (!(value is int num))
			{
				if (!(value is long num2))
				{
					if (!(value is float num3))
					{
						if (!(value is double a))
						{
							if (value is string text)
							{
								return int.TryParse(text.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
							}
							result = 0;
							return false;
						}
						result = (int)Math.Round(a);
						return true;
					}
					result = Mathf.RoundToInt(num3);
					return true;
				}
				result = (int)num2;
				return true;
			}
			result = num;
			return true;
		}

		private static bool TryGetBool(object? value, out bool result)
		{
			if (!(value is bool flag))
			{
				if (value is string text)
				{
					return bool.TryParse(text.Trim(), out result);
				}
				result = false;
				return false;
			}
			result = flag;
			return true;
		}

		private static bool TryResolveBiomeKey(string value, out int biome)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected I4, but got Unknown
			string text = value.Trim();
			string text2 = NormalizeBiomeName(text);
			if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out biome))
			{
				return true;
			}
			if (uint.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				biome = (int)result;
				return true;
			}
			if (Enum.TryParse<Biome>(text2, ignoreCase: true, out Biome result2))
			{
				biome = (int)result2;
				return true;
			}
			if (TerrainMistileExpandWorldDataBiomeCompat.TryGetBiome(text, out biome))
			{
				return true;
			}
			if (!string.Equals(text, text2, StringComparison.Ordinal))
			{
				return TerrainMistileExpandWorldDataBiomeCompat.TryGetBiome(text2, out biome);
			}
			return false;
		}

		private static TerrainMistileBiomeSpawnRule CreateRule(TerrainMistileSpawnRuleValues? values, TerrainMistileBiomeSpawnRule fallback)
		{
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			if (values == null)
			{
				return fallback;
			}
			float num = values.Interval ?? fallback.Interval;
			float num2 = values.PlayerSearchRadius ?? fallback.PlayerSearchRadius;
			float num3 = values.SpawnChance ?? fallback.SpawnChance;
			float num4 = values.MaxDeformationSpawnChanceBonus ?? fallback.MaxDeformationSpawnChanceBonus;
			bool scaleSpawnsWithNearbyPlayers = values.PerPlayerSpawn ?? fallback.ScaleSpawnsWithNearbyPlayers;
			int num5 = values.PlayerBaseValue ?? fallback.IgnorePlayerBaseBaseValue;
			float num6 = values.BaseCheckRadius ?? fallback.BaseCheckRadius;
			int num7 = values.MaxSpawn ?? fallback.MaxActiveTerrainMistilesPerArea;
			float minRadius = fallback.SpawnRadiusMin;
			float maxRadius = fallback.SpawnRadiusMax;
			string spawnRadius = values.SpawnRadius;
			if (spawnRadius != null && !string.IsNullOrWhiteSpace(spawnRadius) && !TryParseSpawnRadius(spawnRadius, out minRadius, out maxRadius))
			{
				ManualLogSource? logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Ignoring invalid spawnRadius '" + spawnRadius + "' in TerrainMistile spawn rules. Use a number like '24' or a range like '16~32'."));
				}
				minRadius = fallback.SpawnRadiusMin;
				maxRadius = fallback.SpawnRadiusMax;
			}
			float num8 = values.SpawnAltitude ?? fallback.SpawnAltitude;
			float num9 = values.ResetRadius ?? fallback.ResetRadius;
			float num10 = values.Health ?? fallback.Health;
			Color visualColor = fallback.VisualColor;
			string visualColor2 = values.VisualColor;
			if (visualColor2 != null && !string.IsNullOrWhiteSpace(visualColor2))
			{
				if (TryParseVisualColor(visualColor2, out var color))
				{
					visualColor = color;
				}
				else
				{
					ManualLogSource? logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)("Ignoring invalid visualColor '" + visualColor2 + "' in TerrainMistile spawn rules. Use an HTML hex color like '#45FF5A' or '45FF5A'."));
					}
				}
			}
			float interval = Mathf.Clamp(num, 0f, 3600f);
			num2 = Mathf.Clamp(num2, 0f, 512f);
			num3 = Mathf.Clamp01(num3);
			num4 = Mathf.Clamp01(num4);
			num5 = Mathf.Clamp(num5, 0, 10);
			num6 = Mathf.Clamp(num6, 0f, 128f);
			num7 = Mathf.Clamp(num7, 0, 50);
			minRadius = Mathf.Clamp(minRadius, 1f, 256f);
			maxRadius = Mathf.Clamp(maxRadius, 1f, 256f);
			num8 = Mathf.Clamp(num8, 1f, 64f);
			num9 = Mathf.Clamp(num9, 1f, 64f);
			num10 = Mathf.Clamp(num10, 1f, 10000f);
			if (minRadius > maxRadius)
			{
				float num11 = maxRadius;
				maxRadius = minRadius;
				minRadius = num11;
			}
			return new TerrainMistileBiomeSpawnRule(interval, num2, num3, num4, scaleSpawnsWithNearbyPlayers, num5, num6, num7, minRadius, maxRadius, num8, num9, num10, visualColor);
		}

		private static void RecalculateRuntimeState()
		{
			_maxPlayerSearchRadius = (_defaultRule.Enabled ? _defaultRule.PlayerSearchRadius : 0f);
			_maxResetRadius = _defaultRule.ResetRadius;
			_hasEnabledRules = _defaultRule.Enabled;
			foreach (TerrainMistileBiomeSpawnRule value in BiomeRules.Values)
			{
				_maxResetRadius = Mathf.Max(_maxResetRadius, value.ResetRadius);
				if (value.Enabled)
				{
					_hasEnabledRules = true;
					_maxPlayerSearchRadius = Mathf.Max(_maxPlayerSearchRadius, value.PlayerSearchRadius);
				}
			}
		}

		private static TerrainMistileBiomeSpawnRule CreateDefaultRule()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			return new TerrainMistileBiomeSpawnRule(60f, 32f, 0.25f, 0.25f, scaleSpawnsWithNearbyPlayers: true, 1, 24f, 3, 16f, 32f, 8f, 8f, 1f, FallbackVisualColor);
		}

		private static bool TryParseSpawnRadius(string value, out float minRadius, out float maxRadius)
		{
			minRadius = 0f;
			maxRadius = 0f;
			string[] array = value.Split(new char[1] { '~' });
			if (array.Length == 1)
			{
				if (!TryParseFloat(array[0], out minRadius))
				{
					return false;
				}
				maxRadius = minRadius;
				return true;
			}
			if (array.Length != 2 || !TryParseFloat(array[0], out minRadius) || !TryParseFloat(array[1], out maxRadius))
			{
				return false;
			}
			return true;
		}

		private static bool TryParseFloat(string value, out float result)
		{
			return float.TryParse(value.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result);
		}

		private static bool TryParseVisualColor(string value, out Color color)
		{
			string text = value.Trim();
			if (text.Length > 0 && text[0] != '#')
			{
				text = "#" + text;
			}
			return ColorUtility.TryParseHtmlString(text, ref color);
		}

		private static string NormalizeBiomeName(string value)
		{
			return value.Trim().Replace(" ", "").Replace("_", "")
				.Replace("-", "");
		}

		private static string BuildDefaultPlayerBasePrefabsYaml()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# TerrainMistile base prefabs used by playerBaseValue. Only instances with ZDO longs.creator != 0 are counted.");
			stringBuilder.AppendLine("playerBasePrefabs:");
			string[] defaultPlayerBasePrefabNames = DefaultPlayerBasePrefabNames;
			foreach (string value in defaultPlayerBasePrefabNames)
			{
				stringBuilder.Append("  - ").AppendLine(value);
			}
			return stringBuilder.ToString();
		}

		private static string BuildDefaultYaml()
		{
			return "# Expand World Data custom biomes can use their custom biome name or numeric biome value.\n# defaults and playerBasePrefabs are reserved. Every other top-level key is treated as a biome rule.\n\ndefaults:\n  interval: 60 # Seconds between spawn rolls for one 32m terrain unit. 0 disables that biome.\n  playerSearchRadius: 32 # Players within this horizontal radius of a changed terrain unit activate its rolls.\n  spawnChance: 0.25 # Base chance used when the unit interval is ready and at least one player is nearby.\n  maxDeformationSpawnChanceBonus: 0.25 # Added to spawnChance when the largest height deformation in the 32m terrain unit reaches the 8m cap. Scales linearly from 0m to 8m.\n  maxSpawn: 3 # Maximum active TerrainMistiles with targets within 32m of the target. 0 disables that biome.\n  perPlayerSpawn: true # If true, one successful roll can spawn up to one TerrainMistile per nearby player, capped by maxSpawn and available targets.\n  playerBaseValue: 1 # playerBaseValue N skips spawn checks when at least N unique listed player-placed base prefab types are within baseCheckRadius meters horizontally of the changed terrain. 0 disables the PlayerBase check.\n  baseCheckRadius: 24 # Horizontal radius used by playerBaseValue to count listed player-placed base prefab types.\n  spawnRadius: 16~32 # Horizontal spawn distance from the selected nearby player. Use 24 for fixed distance or 16~32 for a random range.\n  spawnAltitude: 8 # Height above solid ground where TerrainMistile spawns.\n  resetRadius: 8 # Radius of terrain height and paint reset when TerrainMistile detonates.\n  health: 1 # Maximum and current health applied when TerrainMistile spawns. Biome rules can override it.\n  visualColor: \"#45FF5A\" # HTML hex color used for TerrainMistile flames, sparks, and light. Biome rules can override it.\nMeadows:\n  interval: 120\n  spawnChance: 0.1\n  resetRadius: 4\n  visualColor: \"#7CFF6B\"\nBlackForest:\n  interval: 120\n  resetRadius: 6\n  visualColor: \"#2ED36F\"\nSwamp:\n  playerBaseValue: 2\n  visualColor: \"#8FBF3F\"\nMountain:\n  interval: 120\n  playerBaseValue: 2\n  resetRadius: 6\n  visualColor: \"#8FE8FF\"\nPlains:\n  interval: 120\n  playerBaseValue: 3\n  resetRadius: 6\n  visualColor: \"#FFD15C\"\nMistlands:\n  interval: 120\n  playerBaseValue: 3\n  resetRadius: 6\n  visualColor: \"#B58CFF\"\nAshLands:\n  playerBaseValue: 4\n  visualColor: \"#FF5A2E\"\nDeepNorth:\n  playerBaseValue: 4\n  visualColor: \"#BFEFFF\"\nOcean:\n  visualColor: \"#3EA7FF\"\n\n" + BuildDefaultPlayerBasePrefabsYaml();
		}
	}
	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 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;
			EnsureReflectionMethods();
			if (_tryGetBiomeMethod == null)
			{
				return false;
			}
			ParameterInfo[] parameters = _tryGetBiomeMethod.GetParameters();
			object[] array = new object[2]
			{
				name,
				Enum.ToObject(parameters[1].ParameterType.GetElementType() ?? parameters[1].ParameterType, 0)
			};
			try
			{
				object obj = _tryGetBiomeMethod.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 = "";
			EnsureReflectionMethods();
			if (_tryGetDisplayNameMethod == null)
			{
				return false;
			}
			Type parameterType = _tryGetDisplayNameMethod.GetParameters()[0].ParameterType;
			object[] array = new object[2]
			{
				Enum.ToObject(parameterType, biome),
				""
			};
			try
			{
				object obj = _tryGetDisplayNameMethod.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 EnsureReflectionMethods()
		{
			if (_tryGetBiomeMethod != null && _tryGetDisplayNameMethod != null)
			{
				return;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				AssemblyName name;
				try
				{
					name = assembly.GetName();
				}
				catch
				{
					continue;
				}
				if (!string.Equals(name.Name, "ExpandWorldData", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Type type = assembly.GetType("ExpandWorldData.BiomeManager", throwOnError: false);
				if (!(type == null))
				{
					if ((object)_tryGetBiomeMethod == null)
					{
						_tryGetBiomeMethod = type.GetMethod("TryGetBiome", BindingFlags.Static | BindingFlags.Public);
					}
					if ((object)_tryGetDisplayNameMethod == null)
					{
						_tryGetDisplayNameMethod = type.GetMethod("TryGetDisplayName", BindingFlags.Static | BindingFlags.Public);
					}
					break;
				}
			}
		}

		private static void EnsureFileMappingLoaded()
		{
			if (_fileMappingLoaded)
			{
				return;
			}
			_fileMappingLoaded = true;
			foreach (KeyValuePair<string, int> originalBiome in OriginalBiomes)
			{
				AddFileBiomeName(originalBiome.Key, originalBiome.Value);
			}
			string path = Path.Combine(Paths.ConfigPath, "expand_world");
			if (!Directory.Exists(path))
			{
				return;
			}
			string[] files;
			try
			{
				files = Directory.GetFiles(path, "expand_biomes*.yaml");
			}
			catch
			{
				return;
			}
			Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			int biome = 512;
			string[] array = files;
			foreach (string path2 in array)
			{
				List<ExpandWorldDataBiomeYaml> list;
				try
				{
					list = ExpandWorldDataBiomeDeserializer.Deserialize<List<ExpandWorldDataBiomeYaml>>(File.ReadAllText(path2));
				}
				catch
				{
					continue;
				}
				if (list == null)
				{
					continue;
				}
				foreach (ExpandWorldDataBiomeYaml item in list)
				{
					string text = item.Biome.Trim();
					if (text.Length != 0 && !FileNameToBiome.ContainsKey(text))
					{
						biome = NextBiome(biome);
						AddFileBiomeName(text, biome);
					}
				}
			}
		}

		private static int NextBiome(int biome)
		{
			return biome switch
			{
				128 => 128, 
				int.MinValue => 128, 
				_ => 2 * biome, 
			};
		}

		private static void AddFileBiomeName(string name, int biome)
		{
			FileNameToBiome[name] = biome;
			string text = NormalizeName(name);
			if (!string.Equals(name, text, StringComparison.OrdinalIgnoreCase))
			{
				FileNameToBiome[text] = biome;
			}
			FileBiomeToName[biome] = name;
		}

		private static string NormalizeName(string value)
		{
			return value.Trim().Replace(" ", "").Replace("_", "")
				.Replace("-", "");
		}
	}
	internal readonly struct TerrainMistileBiomeSpawnRule
	{
		public float Interval { get; }

		public float PlayerSearchRadius { get; }

		public float SpawnChance { get; }

		public float MaxDeformationSpawnChanceBonus { get; }

		public bool ScaleSpawnsWithNearbyPlayers { get; }

		public int IgnorePlayerBaseBaseValue { get; }

		public float BaseCheckRadius { get; }

		public int MaxActiveTerrainMistilesPerArea { get; }

		public float SpawnRadiusMin { get; }

		public float SpawnRadiusMax { get; }

		public float SpawnAltitude { get; }

		public float ResetRadius { get; }

		public float Health { get; }

		public Color VisualColor { get; }

		public bool Enabled
		{
			get
			{
				if (Interval > 0f && PlayerSearchRadius > 0f && (SpawnChance > 0f || MaxDeformationSpawnChanceBonus > 0f))
				{
					return MaxActiveTerrainMistilesPerArea > 0;
				}
				return false;
			}
		}

		public TerrainMistileBiomeSpawnRule(float interval, float playerSearchRadius, float spawnChance, float maxDeformationSpawnChanceBonus, bool scaleSpawnsWithNearbyPlayers, int ignorePlayerBaseBaseValue, float baseCheckRadius, int maxActiveTerrainMistilesPerArea, float spawnRadiusMin, float spawnRadiusMax, float spawnAltitude, float resetRadius, float health, Color visualColor)
		{
			//IL_0066: 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)
			Interval = interval;
			PlayerSearchRadius = playerSearchRadius;
			SpawnChance = spawnChance;
			MaxDeformationSpawnChanceBonus = maxDeformationSpawnChanceBonus;
			ScaleSpawnsWithNearbyPlayers = scaleSpawnsWithNearbyPlayers;
			IgnorePlayerBaseBaseValue = ignorePlayerBaseBaseValue;
			BaseCheckRadius = baseCheckRadius;
			MaxActiveTerrainMistilesPerArea = maxActiveTerrainMistilesPerArea;
			SpawnRadiusMin = spawnRadiusMin;
			SpawnRadiusMax = spawnRadiusMax;
			SpawnAltitude = spawnAltitude;
			ResetRadius = resetRadius;
			Health = health;
			VisualColor = visualColor;
		}

		public float GetEffectiveSpawnChance(float deformationPressure)
		{
			return Mathf.Clamp01(SpawnChance + MaxDeformationSpawnChanceBonus * Mathf.Clamp01(deformationPressure));
		}

		public override string ToString()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			return $"interval={Interval:0.##}, playerSearchRadius={PlayerSearchRadius:0.##}, spawnChance={SpawnChance:0.###}, maxDeformationSpawnChanceBonus={MaxDeformationSpawnChanceBonus:0.###}, maxActive={MaxActiveTerrainMistilesPerArea}, playerBaseValue={IgnorePlayerBaseBaseValue}, baseCheckRadius={BaseCheckRadius:0.##}, spawnRadius={SpawnRadiusMin:0.##}~{SpawnRadiusMax:0.##}, resetRadius={ResetRadius:0.##}, health={Health:0.##}, visualColor=#{ColorUtility.ToHtmlStringRGB(VisualColor)}";
		}
	}
	internal static class TerrainMistileSystem
	{
		private sealed class TargetReservation
		{
			public Vector3 Point;

			public float Radius;

			public float ExpireTime;
		}

		private sealed class ExternalTerrainIgnoreArea
		{
			public Vector3 Center;

			public float Radius;

			public string Source = "";

			public float ExpireTime;
		}

		private readonly struct PlayerBasePiece
		{
			public string PrefabName { get; }

			public Vector3 Position { get; }

			public PlayerBasePiece(string prefabName, Vector3 position)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				PrefabName = prefabName;
				Position = position;
			}
		}

		private readonly struct PlayerBasePieceBucketKey : IEquatable<PlayerBasePieceBucketKey>
		{
			public int X { get; }

			public int Z { get; }

			public PlayerBasePieceBucketKey(int x, int z)
			{
				X = x;
				Z = z;
			}

			public bool Equals(PlayerBasePieceBucketKey other)
			{
				if (X == other.X)
				{
					return Z == other.Z;
				}
				return false;
			}

			public override bool Equals(object? obj)
			{
				if (obj is PlayerBasePieceBucketKey other)
				{
					return Equals(other);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (X * 397) ^ Z;
			}
		}

		private readonly struct NoModifiedTerrainCompCache
		{
			public int Operations { get; }

			public float ExpireTime { get; }

			public NoModifiedTerrainCompCache(int operations, float expireTime)
			{
				Operations = operations;
				ExpireTime = expireTime;
			}
		}

		private readonly struct SpawnUnitKey : IEquatable<SpawnUnitKey>
		{
			public int X { get; }

			public int Z { get; }

			public int Biome { get; }

			public SpawnUnitKey(int x, int z, int biome)
			{
				X = x;
				Z = z;
				Biome = biome;
			}

			public bool Equals(SpawnUnitKey other)
			{
				if (X == other.X && Z == other.Z)
				{
					return Biome == other.Biome;
				}
				return false;
			}

			public override bool Equals(object? obj)
			{
				if (obj is SpawnUnitKey other)
				{
					return Equals(other);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (((X * 397) ^ Z) * 397) ^ Biome;
			}

			public override string ToString()
			{
				return $"{TerrainMistileSpawnRules.GetBiomeName(Biome)}@{X},{Z}";
			}
		}

		private sealed class ModifiedTerrainUnitCandidate
		{
			public SpawnUnitKey Key;

			public int Biome;

			public Vector3 TargetPoint;

			public float BestScore;

			public float MaxDeformationPressure;

			public int ModifiedCellCount;
		}

		private const string ResetEffectsRpcName = "TerrainMistile_ResetEffects";

		private const float ResetEffectGroundOffset = 0.1f;

		private const float FallbackEffectDestroyDelay = 20f;

		private const float ActiveTerrainMistileAreaRadius = 32f;

		private const float SpawnUnitSize = 32f;

		private const float TerrainHeightDeformationCap = 8f;

		private const float SpawnUnitScanInterval = 1f;

		private const float SpawnUnitRollStateRetention = 600f;

		private const float NoModifiedTerrainCompRetention = 3f;

		private const float PlayerBasePieceBucketRefreshInterval = 2f;

		private const float TargetReservationDuration = 60f;

		private const float ExternalTerrainIgnoreMergeDistance = 0.25f;

		internal const float LocationTerrainProtectionPadding = 5f;

		internal const float LocationTerrainIgnoreDuration = 10f;

		private const string ResetVfxPrefabName = "fx_greenroots_projectile_hit";

		private const string ResetSfxPrefabName = "sfx_staff_elder_grow";

		private static readonly Dictionary<SpawnUnitKey, float> LastSpawnRollTimeByUnit = new Dictionary<SpawnUnitKey, float>();

		private static readonly Dictionary<TerrainComp, NoModifiedTerrainCompCache> NoModifiedTerrainComps = new Dictionary<TerrainComp, NoModifiedTerrainCompCache>();

		private static readonly Dictionary<SpawnUnitKey, ModifiedTerrainUnitCandidate> ModifiedTerrainUnits = new Dictionary<SpawnUnitKey, ModifiedTerrainUnitCandidate>();

		private static readonly List<SpawnUnitKey> TempSpawnUnitKeys = new List<SpawnUnitKey>();

		private static readonly HashSet<SpawnUnitKey> TempCooldownSpawnUnitKeys = new HashSet<SpawnUnitKey>();

		private static readonly List<TerrainComp> TempNoModifiedTerrainComps = new List<TerrainComp>();

		private static readonly List<TerrainMistileBehaviour> ActiveTerrainMistiles = new List<TerrainMistileBehaviour>();

		private static readonly List<Player> TempPlayers = new List<Player>();

		private static readonly List<Player> TempNearbyPlayers = new List<Player>();

		private static readonly List<Heightmap> TempHeightmaps = new List<Heightmap>();

		private static readonly List<TargetReservation> TargetReservations = new List<TargetReservation>();

		private static readonly List<ExternalTerrainIgnoreArea> ExternalTerrainIgnoreAreas = new List<ExternalTerrainIgnoreArea>();

		private static readonly List<ExternalTerrainIgnoreArea> ProtectedTerrainAreas = new List<ExternalTerrainIgnoreArea>();

		private static readonly Dictionary<PlayerBasePieceBucketKey, List<PlayerBasePiece>> PlayerBasePiecesByBucket = new Dictionary<PlayerBasePieceBucketKey, List<PlayerBasePiece>>();

		private static readonly HashSet<string> TempPlayerBasePrefabNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static bool _playerBasePieceBucketsBuilt;

		private static float _nextPlayerBasePieceBucketRefreshTime;

		private static bool _resettingTerrain;

		private static float _nextSpawnUnitScanTime;

		private static bool _resetEffectsRpcRegistered;

		internal static void UpdatePersistentTerrainSpawns()
		{
			if (_resettingTerrain || !TerrainMistileSpawnRules.HasEnabledRules || !Object.op_Implicit((Object)(object)ZNetScene.instance) || Time.time < _nextSpawnUnitScanTime)
			{
				return;
			}
			_nextSpawnUnitScanTime = Time.time + 1f;
			CollectEligiblePlayers();
			if (TempPlayers.Count == 0)
			{
				return;
			}
			CollectModifiedTerrainUnits();
			CleanupSpawnUnitRollState();
			foreach (ModifiedTerrainUnitCandidate value in ModifiedTerrainUnits.Values)
			{
				TryRollSpawnForTerrainUnit(value);
			}
		}

		internal static void UpdateResetEffectRpcRegistration()
		{
			if (!_resetEffectsRpcRegistered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<ZPackage>("TerrainMistile_ResetEffects", (Action<long, ZPackage>)RPC_ResetEffects);
				_resetEffectsRpcRegistered = true;
			}
		}

		i