Decompiled source of RavenMap v1.7.7

RavenMap.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: InternalsVisibleTo("RavenMap.Tests")]
[assembly: AssemblyCompany("RavenMap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.7.7.0")]
[assembly: AssemblyInformationalVersion("1.7.7")]
[assembly: AssemblyProduct("RavenMap")]
[assembly: AssemblyTitle("RavenMap")]
[assembly: AssemblyVersion("1.7.7.0")]
namespace DragonMotion.RavenMap;

internal static class RavenCommands
{
	private const string CommandName = "ravenmap";

	private static readonly FieldInfo TerminalCommandsField = AccessTools.Field(typeof(Terminal), "commands");

	private static ConsoleCommand _command;

	private static bool _active;

	internal static void Initialize()
	{
		//IL_001f: 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_003d: Expected O, but got Unknown
		//IL_003d: Expected O, but got Unknown
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		if (_command != null)
		{
			_active = true;
			return;
		}
		_command = new ConsoleCommand("ravenmap", string.Empty, new ConsoleEvent(Run), false, true, true, false, false, new ConsoleOptionsFetcher(GetOptions), false, true, true);
		_active = true;
		RefreshLocalization();
	}

	internal static void RefreshLocalization(string language = null)
	{
		if (_command != null)
		{
			_command.Description = (IsRussian(language) ? "Сброс общих автоматических отметок RavenMap" : "Reset RavenMap shared automatic pins");
		}
	}

	internal static void Shutdown()
	{
		_active = false;
		if (_command != null)
		{
			if (TerminalCommandsField?.GetValue(null) is Dictionary<string, ConsoleCommand> dictionary && dictionary.TryGetValue("ravenmap", out var value) && value == _command)
			{
				dictionary.Remove("ravenmap");
			}
			_command = null;
		}
	}

	private static void Run(ConsoleEventArgs args)
	{
		if (!_active)
		{
			return;
		}
		bool flag = IsRussian();
		if (args == null || args.Length != 3 || !string.Equals(args[1], "resetpins", StringComparison.OrdinalIgnoreCase) || !string.Equals(args[2], "confirm", StringComparison.OrdinalIgnoreCase))
		{
			if (args != null)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString(flag ? "Использование: ravenmap resetpins confirm" : "Usage: ravenmap resetpins confirm");
				}
			}
		}
		else if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			Terminal context2 = args.Context;
			if (context2 != null)
			{
				context2.AddString(flag ? "Сбрасывать общие автоматические отметки может только хост или администратор сервера." : "Only the host or a server administrator can reset shared automatic pins.");
			}
		}
		else
		{
			bool flag2 = RavenRuntime.RequestAutomaticPinReset();
			Terminal context3 = args.Context;
			if (context3 != null)
			{
				context3.AddString((!flag2) ? (flag ? "Не удалось сбросить автоматические отметки: мир или сетевой сеанс RavenMap ещё не готов." : "Automatic pins could not be reset because the RavenMap world/network session is not ready.") : (flag ? "Общие автоматические отметки сброшены. Загруженные подходящие объекты будут обнаружены повторно." : "Shared automatic pins were reset. Loaded eligible objects will be discovered again."));
			}
		}
	}

	private static List<string> GetOptions()
	{
		return new List<string> { "resetpins" };
	}

	private static bool IsRussian(string language = null)
	{
		try
		{
			object obj = language;
			if (obj == null)
			{
				Localization instance = Localization.instance;
				obj = ((instance != null) ? instance.GetSelectedLanguage() : null);
			}
			return ConfigText.IsRussian((string)obj);
		}
		catch
		{
			return false;
		}
	}
}
internal static class GameAccess
{
	private static FieldRef<Minimap, bool[]> _explored;

	private static FieldRef<Minimap, bool[]> _exploredOthers;

	private static FieldRef<Minimap, Texture2D> _fogTexture;

	private static FieldRef<Minimap, List<PinData>> _pins;

	private static FieldRef<Minimap, bool> _pinUpdateRequired;

	private static FieldRef<MineRock, Collider[]> _mineRockHitAreas;

	private static FieldRef<MineRock5, MeshRenderer> _mineRock5MeshRenderer;

	private static Func<MineRock5, object> _mineRock5HitAreas;

	private static Func<object, float> _mineRock5HitAreaHealth;

	private static bool _initialized;

	internal static bool TryInitialize(out string error)
	{
		if (_initialized)
		{
			error = string.Empty;
			return true;
		}
		try
		{
			_explored = AccessTools.FieldRefAccess<Minimap, bool[]>("m_explored");
			_exploredOthers = AccessTools.FieldRefAccess<Minimap, bool[]>("m_exploredOthers");
			_fogTexture = AccessTools.FieldRefAccess<Minimap, Texture2D>("m_fogTexture");
			_pins = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");
			_pinUpdateRequired = AccessTools.FieldRefAccess<Minimap, bool>("m_pinUpdateRequired");
			_mineRockHitAreas = AccessTools.FieldRefAccess<MineRock, Collider[]>("m_hitAreas");
			_mineRock5MeshRenderer = AccessTools.FieldRefAccess<MineRock5, MeshRenderer>("m_meshRenderer");
			FieldInfo fieldInfo = AccessTools.Field(typeof(MineRock5), "m_hitAreas");
			if (fieldInfo == null)
			{
				throw new MissingFieldException(typeof(MineRock5).FullName, "m_hitAreas");
			}
			_mineRock5HitAreas = BuildObjectFieldGetter<MineRock5>(fieldInfo);
			Type type = fieldInfo.FieldType.GetGenericArguments()[0];
			FieldInfo fieldInfo2 = AccessTools.Field(type, "m_health");
			if (fieldInfo2 == null || fieldInfo2.FieldType != typeof(float))
			{
				throw new MissingFieldException(type.FullName, "m_health");
			}
			_mineRock5HitAreaHealth = BuildFloatFieldGetter(fieldInfo2);
			if (_explored == null || _exploredOthers == null || _fogTexture == null || _pins == null || _pinUpdateRequired == null || _mineRockHitAreas == null || _mineRock5MeshRenderer == null || _mineRock5HitAreas == null || _mineRock5HitAreaHealth == null)
			{
				throw new MissingFieldException("One or more required Minimap accessors could not be created.");
			}
			_initialized = true;
			error = string.Empty;
			return true;
		}
		catch (Exception ex)
		{
			_explored = null;
			_exploredOthers = null;
			_fogTexture = null;
			_pins = null;
			_pinUpdateRequired = null;
			_mineRockHitAreas = null;
			_mineRock5MeshRenderer = null;
			_mineRock5HitAreas = null;
			_mineRock5HitAreaHealth = null;
			_initialized = false;
			error = ex.GetType().Name + ": " + ex.Message;
			return false;
		}
	}

	internal static bool[] GetExplored(Minimap map)
	{
		if (!((Object)(object)map != (Object)null) || _explored == null)
		{
			return null;
		}
		return _explored.Invoke(map);
	}

	internal static bool[] GetExploredOthers(Minimap map)
	{
		if (!((Object)(object)map != (Object)null) || _exploredOthers == null)
		{
			return null;
		}
		return _exploredOthers.Invoke(map);
	}

	internal static Texture2D GetFogTexture(Minimap map)
	{
		if (!((Object)(object)map != (Object)null) || _fogTexture == null)
		{
			return null;
		}
		return _fogTexture.Invoke(map);
	}

	internal static List<PinData> GetPins(Minimap map)
	{
		if (!((Object)(object)map != (Object)null) || _pins == null)
		{
			return null;
		}
		return _pins.Invoke(map);
	}

	internal static void MarkPinsDirty(Minimap map)
	{
		if (!((Object)(object)map == (Object)null) && _pinUpdateRequired != null)
		{
			_pinUpdateRequired.Invoke(map) = true;
		}
	}

	internal static int GetMineRockHitAreaCount(MineRock rock)
	{
		if (rock == null || _mineRockHitAreas == null)
		{
			return 0;
		}
		Collider[] obj = _mineRockHitAreas.Invoke(rock);
		if (obj == null)
		{
			return 0;
		}
		return obj.Length;
	}

	internal static int GetMineRock5HitAreaCount(MineRock5 rock)
	{
		if (rock == null || _mineRock5HitAreas == null)
		{
			return 0;
		}
		if (!(_mineRock5HitAreas(rock) is ICollection collection))
		{
			return 0;
		}
		return collection.Count;
	}

	internal static bool TryMeasureMineRock5HitAreas(MineRock5 rock, float maximumHealth, out float remaining, out int totalParts, out int remainingParts)
	{
		remaining = 0f;
		totalParts = 0;
		remainingParts = 0;
		if (rock == null || maximumHealth <= 0f || _mineRock5HitAreas == null || _mineRock5HitAreaHealth == null || !(_mineRock5HitAreas(rock) is IList { Count: not 0 } list))
		{
			return false;
		}
		totalParts = list.Count;
		for (int i = 0; i < list.Count; i++)
		{
			object obj = list[i];
			if (obj != null)
			{
				float num = Mathf.Clamp(_mineRock5HitAreaHealth(obj), 0f, maximumHealth);
				remaining += num;
				if (num > 0f)
				{
					remainingParts++;
				}
			}
		}
		return true;
	}

	internal static MeshRenderer GetMineRock5CombinedRenderer(MineRock5 rock)
	{
		if (rock != null && _mineRock5MeshRenderer != null)
		{
			return _mineRock5MeshRenderer.Invoke(rock);
		}
		return null;
	}

	internal static bool TryGetMapState(Minimap map, out bool[] explored, out Texture2D fogTexture, out int textureSize)
	{
		explored = GetExplored(map);
		fogTexture = GetFogTexture(map);
		textureSize = (((Object)(object)map != (Object)null) ? map.m_textureSize : 0);
		if (explored != null && (Object)(object)fogTexture != (Object)null && textureSize > 0)
		{
			return explored.Length == textureSize * textureSize;
		}
		return false;
	}

	internal static bool ExploreRed(bool[] explored, Texture2D fogTexture, int size, int x, int y)
	{
		//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_004b: Unknown result type (might be due to invalid IL or missing references)
		if (explored == null || (Object)(object)fogTexture == (Object)null || size <= 0 || explored.Length != size * size || (uint)x >= (uint)size || (uint)y >= (uint)size)
		{
			return false;
		}
		int num = y * size + x;
		if (explored[num])
		{
			return false;
		}
		Color pixel = fogTexture.GetPixel(x, y);
		pixel.r = 0f;
		fogTexture.SetPixel(x, y, pixel);
		explored[num] = true;
		return true;
	}

	private static Func<T, object> BuildObjectFieldGetter<T>(FieldInfo field)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("RavenMap_Get_" + typeof(T).Name + "_" + field.Name, typeof(object), new Type[1] { typeof(T) }, typeof(GameAccess), skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldfld, field);
		if (field.FieldType.IsValueType)
		{
			iLGenerator.Emit(OpCodes.Box, field.FieldType);
		}
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>));
	}

	private static Func<object, float> BuildFloatFieldGetter(FieldInfo field)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("RavenMap_Get_" + field.DeclaringType.Name + "_" + field.Name, typeof(float), new Type[1] { typeof(object) }, typeof(GameAccess), skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType);
		iLGenerator.Emit(OpCodes.Ldfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<object, float>)dynamicMethod.CreateDelegate(typeof(Func<object, float>));
	}
}
internal static class ConfigText
{
	private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>(StringComparer.Ordinal)
	{
		["01 - Interface"] = "01 — Интерфейс",
		["02 - Sharing"] = "02 — Общая карта",
		["03 - Discovery"] = "03 — Обнаружение",
		["04 - Resources"] = "04 — Ресурсы",
		["04.1 - Pickables"] = "04.1 — Собираемые ресурсы",
		["05 - Dungeons"] = "05 — Подземелья",
		["06 - Markers"] = "06 — Метки",
		["07 - Deduplication"] = "07 — Дедупликация",
		["08 - Shimmer"] = "08 — Подсветка",
		["08.1 - Pulse"] = "08.1 — Пульсация",
		["08.2 - Sweep"] = "08.2 — Переливание",
		["08.3 - X-ray"] = "08.3 — Рентген",
		["09 - Performance"] = "09 — Производительность",
		["10 - Diagnostics"] = "10 — Диагностика",
		["Enabled"] = "Включено",
		["Automatic pin names"] = "Названия автометок",
		["Portal names"] = "Названия порталов",
		["Pin icon size"] = "Размер значков",
		["Filter scroll speed"] = "Скорость прокрутки",
		["Shared fog"] = "Общий туман войны",
		["Shared automatic pins"] = "Общие автометки",
		["Resource radius"] = "Радиус ресурсов",
		["Dungeon radius"] = "Радиус подземелий",
		["Silver discovery"] = "Обнаружение серебра",
		["Underground resources"] = "Подземные ресурсы",
		["Discovery radius"] = "Радиус обнаружения",
		["Cross out after harvest"] = "Зачёркивать после сбора",
		["Merge distance"] = "Дистанция слияния",
		["Remove after depletion"] = "Удаление после выработки",
		["Progress update step"] = "Шаг обновления прогресса",
		["Progress update interval"] = "Интервал обновления прогресса",
		["Source prefab allow list"] = "Разрешённые префабы источников",
		["Source prefab block list"] = "Запрещённые префабы источников",
		["Dropped item block list"] = "Запрещённые выпадающие предметы",
		["Dungeon prefab allow list"] = "Разрешённые префабы подземелий",
		["Defeated enemies required"] = "Требуемый процент врагов",
		["Inspected chests required"] = "Требуемый процент сундуков",
		["Minimum observed chests"] = "Минимум найденных сундуков",
		["Dungeon inventory settle delay"] = "Задержка учёта подземелья",
		["Enemy respawn mode"] = "Режим респауна врагов",
		["Reopen on respawn"] = "Возвращать при респауне",
		["Manual dungeon toggle"] = "Ручная зачистка подземелья",
		["Automatic portal pins"] = "Автометки порталов",
		["Track ships"] = "Отслеживать корабли",
		["Track carts"] = "Отслеживать повозки",
		["Vehicle update interval"] = "Интервал обновления транспорта",
		["Vehicle move threshold"] = "Порог движения транспорта",
		["Resource merge distance"] = "Дистанция слияния ресурсов",
		["Dungeon merge distance"] = "Дистанция слияния подземелий",
		["Portal merge distance"] = "Дистанция слияния порталов",
		["Animation"] = "Режим анимации",
		["Pickable shimmer"] = "Подсветка собираемых ресурсов",
		["Pulse color"] = "Цвет пульсации",
		["Pulse interval"] = "Интервал пульсации",
		["Pulse duration"] = "Длительность пульсации",
		["Pulse intensity"] = "Яркость пульсации",
		["Sweep color"] = "Цвет переливания",
		["Sweep interval"] = "Интервал переливания",
		["Sweep speed"] = "Скорость переливания",
		["Sweep maximum duration"] = "Максимальная длительность переливания",
		["Sweep intensity"] = "Яркость переливания",
		["Radius"] = "Радиус",
		["Sweep width"] = "Ширина волны",
		["Sweep softness"] = "Мягкость волны",
		["X-ray through terrain"] = "X-ray сквозь землю",
		["X-ray toggle key"] = "Клавиша X-ray",
		["X-ray color"] = "Цвет X-ray",
		["X-ray intensity"] = "Яркость X-ray",
		["X-ray radius"] = "Радиус X-ray",
		["X-ray renderer budget"] = "Лимит рендереров X-ray",
		["X-ray resource draw budget"] = "Лимит отрисовки ресурсов X-ray",
		["Renderer budget"] = "Лимит рендереров",
		["Save debounce"] = "Задержка сохранения",
		["Fog pixels per packet"] = "Пикселей тумана в пакете",
		["Fog merge cells per frame"] = "Ячеек тумана за кадр",
		["Peer event limit"] = "Лимит событий игрока",
		["Pin adds per frame"] = "Добавлений меток за кадр",
		["Overlap counters"] = "Счётчики наложений",
		["Overlap distance"] = "Дистанция наложения",
		["State diagnostics on map"] = "Диагностика состояния на карте",
		["Detailed logging"] = "Подробный журнал",
		["Enable RavenMap. Requires a restart."] = "Включает RavenMap. Требуется перезапуск.",
		["Show names beside automatic pins. Local setting."] = "Показывает названия рядом с автометками. Локальная настройка.",
		["Show portal names independently. Local setting."] = "Показывает названия порталов независимо от других меток. Локальная настройка.",
		["Local automatic-pin size multiplier."] = "Локальный множитель размера автометок.",
		["Map-filter wheel speed in UI pixels per step."] = "Скорость колеса в окне фильтров, в пикселях за шаг.",
		["Merge explored fog into the shared server map."] = "Объединяет разведанный туман войны на общей карте сервера.",
		["Share automatic pins with every player."] = "Делится автометками со всеми игроками.",
		["Discovery radius for loaded resources; unloaded zones are never scanned."] = "Радиус обнаружения загруженных ресурсов; незагруженные зоны не сканируются.",
		["Discovery radius for loaded dungeons."] = "Радиус обнаружения загруженных подземелий.",
		["WishboneOnly is vanilla-like; Radius is automatic; Disabled never pins silver."] = "«Только с дужкой» работает как в ваниле; «По радиусу» — автоматически; «Выключено» — не отмечать серебро.",
		["Discover non-silver underground resources automatically instead of on detection or mining."] = "Автоматически обнаруживает подземные ресурсы, кроме серебра, не дожидаясь поиска или добычи.",
		["Server policy: track naturally spawned renewable and one-shot berries, mushrooms, plants and other Pickable objects."] = "Серверное правило: отслеживает естественно появившиеся возобновляемые и одноразовые ягоды, грибы, растения и другие собираемые объекты.",
		["Server discovery radius for loaded pickable plants; unloaded zones are never scanned."] = "Серверный радиус обнаружения загруженных собираемых растений; незагруженные зоны не сканируются.",
		["Server policy: cross out a renewable pickable after harvest and restore it when the game respawns it."] = "Серверное правило: зачёркивает возобновляемый объект после сбора и снимает зачёркивание, когда игра восстановит его.",
		["Server merge distance for equivalent pickable pins."] = "Серверная дистанция слияния одинаковых отметок собираемых объектов.",
		["Remove a vein pin after this share of its original amount is depleted."] = "Удаляет метку жилы после выработки указанной доли исходного объёма.",
		["Extra depletion required before another progress update."] = "Дополнительная выработка до следующего обновления прогресса.",
		["Minimum interval between ordinary updates for one resource."] = "Минимальный интервал между обычными обновлениями одного ресурса.",
		["Semicolon-separated source prefabs; SourcePrefab=ItemPrefab overrides inferred drops."] = "Префабы источников через точку с запятой; SourcePrefab=ItemPrefab переопределяет определённый предмет.",
		["Semicolon-separated prefabs never treated as resources."] = "Префабы через точку с запятой, которые никогда не считаются ресурсами.",
		["Ignore deposits whose inferred drops are only these item keys."] = "Игнорирует залежи, из которых определяются только перечисленные предметы.",
		["Semicolon-separated location prefabs always treated as clearable dungeons."] = "Префабы локаций через точку с запятой, всегда считающиеся зачищаемыми подземельями.",
		["Defeated share of observed enemies required before crossing out a dungeon."] = "Доля побеждённых обнаруженных врагов для зачёркивания подземелья.",
		["Observed-chest share required before crossing out a dungeon."] = "Доля осмотренных обнаруженных сундуков для зачёркивания подземелья.",
		["Minimum observed chests required for automatic clearing."] = "Минимум обнаруженных сундуков для автоматической отметки о зачистке.",
		["Quiet period with no newly observed enemies or chests before the loaded dungeon inventory is considered complete."] = "Пауза без новых врагов или сундуков, после которой учёт загруженного подземелья считается завершённым.",
		["Auto follows observed spawners; Enabled forces reopening; Disabled keeps cleared state."] = "«Авто» учитывает найденные спавнеры; «Включено» всегда возвращает метку; «Выключено» сохраняет зачистку.",
		["Remove crossed-out state when a respawn-capable dungeon gets a live enemy."] = "Снимает зачёркивание, когда в подземелье с респауном появляется живой враг.",
		["Hold this key and left-click a RavenMap dungeon icon to share a forced cleared/active state. Set None to disable."] = "Зажмите эту клавишу и щёлкните ЛКМ по значку подземелья RavenMap, чтобы принудительно и для всех переключить состояние зачистки. Значение None отключает функцию.",
		["Pin portals automatically and keep their tag current."] = "Автоматически отмечает порталы и обновляет их название.",
		["Track vanilla and modded Ship components."] = "Отслеживает ванильные и модифицированные компоненты Ship.",
		["Track vanilla and modded Vagon components."] = "Отслеживает ванильные и модифицированные компоненты Vagon.",
		["Position-report interval for loaded vehicles."] = "Интервал отправки позиции загруженного транспорта.",
		["Movement required before reporting a new vehicle position."] = "Минимальное движение до отправки новой позиции транспорта.",
		["Server merge distance for equivalent resource pins."] = "Серверная дистанция слияния одинаковых меток ресурсов.",
		["Server merge distance for equivalent dungeon pins."] = "Серверная дистанция слияния одинаковых меток подземелий.",
		["Server merge distance for equivalent portal pins."] = "Серверная дистанция слияния одинаковых меток порталов.",
		["Periodically highlight nearby visible resources and enabled Pickables without material instances."] = "Периодически подсвечивает ближайшие видимые ресурсы и включённые природные собираемые объекты без создания копий материалов.",
		["Sweep travels across highlighted geometry; Pulse brightens the whole object."] = "«Переливание» проходит волной по подсвечиваемой геометрии; «Пульсация» плавно подсвечивает весь объект.",
		["Apply the same Pulse or Sweep highlight to nearby loaded natural Pickables. Local setting."] = "Применяет ту же пульсацию или переливание к ближайшим загруженным природным собираемым ресурсам. Локальная настройка.",
		["Color used by the whole-object pulse."] = "Цвет пульсации, подсвечивающей весь объект.",
		["Delay between pulse effects."] = "Пауза между пульсациями.",
		["Duration of one smooth pulse."] = "Длительность одной плавной пульсации.",
		["Tint and HDR glow strength of the pulse."] = "Сила оттенка и HDR-свечения пульсации.",
		["Color of the soft wave travelling across highlighted geometry."] = "Цвет мягкой волны, проходящей по подсвечиваемой геометрии.",
		["Delay between sweep effects."] = "Пауза между переливаниями.",
		["World-space speed of the travelling wave."] = "Скорость движения волны в метрах игрового мира в секунду.",
		["Safety limit for one sweep; very large geometry still completes its exit."] = "Предельная длительность одного переливания; на очень большой геометрии волна всё равно полностью выходит за дальний край.",
		["Tint and HDR glow strength of the travelling wave."] = "Сила оттенка и HDR-свечения движущейся волны.",
		["Only loaded resources and enabled Pickables inside this radius may shimmer."] = "Переливаются только загруженные ресурсы и включённые собираемые объекты в этом радиусе.",
		["Relative width of the soft wave; the maximum covers almost the complete highlighted object."] = "Относительная ширина мягкой волны; максимальное значение покрывает почти весь подсвечиваемый объект.",
		["Softness of the moving wave; higher values remove the flat center and spread the fade across the whole effect."] = "Мягкость движущейся волны; большие значения убирают плоский центр и растягивают затухание на весь эффект.",
		["Enable local X-ray rendering. Its hotkey only changes temporary visibility while this setting is enabled."] = "Включает локальный X-ray. Горячая клавиша меняет только временную видимость, пока эта настройка включена.",
		["Toggle local X-ray visibility during gameplay. Works only when XrayEnabled is on; set None to disable."] = "Переключает локальную видимость X-ray во время игры. Работает только при включённом XrayEnabled; значение None отключает клавишу.",
		["Color used for vein geometry visible through terrain."] = "Цвет геометрии жилы, видимой сквозь землю.",
		["HDR brightness of X-ray vein geometry."] = "HDR-яркость геометрии жилы в режиме X-ray.",
		["Only already discovered loaded veins inside this local radius appear in X-ray."] = "В X-ray видны только уже обнаруженные и загруженные жилы в пределах этого локального радиуса.",
		["Total renderer budget for the current X-ray selection."] = "Общий лимит рендереров в текущей выборке X-ray.",
		["Hard resource draw-command limit for modded meshes with many submeshes."] = "Жёсткий лимит команд отрисовки ресурсов для модифицированных мешей с большим числом сабмешей.",
		["Hard total renderer limit for one complete shimmer batch."] = "Жёсткий общий лимит рендереров для одного полного цикла подсветки.",
		["Idle time after the last state change before saving; continuous changes are flushed periodically."] = "Время ожидания после последнего изменения; при непрерывных изменениях сохранение всё равно выполняется периодически.",
		["Hard fog-delta packet limit."] = "Жёсткий лимит изменений тумана в одном пакете.",
		["Shared-fog cells examined per frame; lower values reduce spikes."] = "Ячейки общего тумана, обрабатываемые за кадр; меньшее значение снижает скачки нагрузки.",
		["Server event-rate guard per peer."] = "Серверный лимит частоты событий для одного игрока.",
		["Frame budget for restoring or enabling automatic pins."] = "Кадровый лимит восстановления или включения автометок.",
		["Show local P/S counters for overlapping canonical pins and sources."] = "Показывает локальные счётчики P/S для наложившихся меток и источников.",
		["World-space grouping distance for overlap counters."] = "Дистанция группировки счётчиков наложения в игровом мире.",
		["Show local resource measurements and dungeon clearing blockers beside RavenMap pins. Local setting."] = "Показывает рядом с отметками RavenMap локальные измерения ресурсов и причины, мешающие зачистке подземелья. Локальная настройка.",
		["Log classification, deduplication and network details."] = "Записывает подробности классификации, дедупликации и сети."
	};

	internal static bool IsRussian(string language)
	{
		return string.Equals(language, "Russian", StringComparison.OrdinalIgnoreCase);
	}

	internal static string Localize(string english, bool russian)
	{
		if (!russian || string.IsNullOrEmpty(english))
		{
			return english ?? string.Empty;
		}
		if (!Russian.TryGetValue(english, out var value))
		{
			return english;
		}
		return value;
	}

	internal static bool HasRussian(string english)
	{
		if (!string.IsNullOrEmpty(english))
		{
			return Russian.ContainsKey(english);
		}
		return false;
	}

	internal unsafe static string EnumLabel(object value, bool russian)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: 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_0111: Expected I4, but got Unknown
		if (value is SilverDiscoveryMode silverDiscoveryMode)
		{
			if (!russian)
			{
				if (silverDiscoveryMode != SilverDiscoveryMode.WishboneOnly)
				{
					return silverDiscoveryMode.ToString();
				}
				return "Wishbone only";
			}
			switch (silverDiscoveryMode)
			{
			case SilverDiscoveryMode.WishboneOnly:
				return "Только с дужкой";
			case SilverDiscoveryMode.Radius:
				return "По радиусу";
			case SilverDiscoveryMode.Disabled:
				return "Выключено";
			}
		}
		if (value is DungeonRespawnMode dungeonRespawnMode)
		{
			if (!russian)
			{
				return dungeonRespawnMode.ToString();
			}
			switch (dungeonRespawnMode)
			{
			case DungeonRespawnMode.Auto:
				return "Авто";
			case DungeonRespawnMode.Enabled:
				return "Включено";
			case DungeonRespawnMode.Disabled:
				return "Выключено";
			}
		}
		if (value is ShimmerAnimationMode shimmerAnimationMode)
		{
			if (!russian)
			{
				return shimmerAnimationMode.ToString();
			}
			switch (shimmerAnimationMode)
			{
			case ShimmerAnimationMode.Sweep:
				return "Переливание";
			case ShimmerAnimationMode.Pulse:
				return "Пульсация";
			}
		}
		if (value is KeyCode val)
		{
			if (!russian)
			{
				return ((object)(*(KeyCode*)(&val))/*cast due to .constrained prefix*/).ToString();
			}
			if ((int)val != 0)
			{
				return (val - 303) switch
				{
					5 => "Левый Alt", 
					4 => "Правый Alt", 
					3 => "Левый Ctrl", 
					2 => "Правый Ctrl", 
					1 => "Левый Shift", 
					0 => "Правый Shift", 
					_ => ((object)(*(KeyCode*)(&val))/*cast due to .constrained prefix*/).ToString(), 
				};
			}
			return "Отключено";
		}
		return null;
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public string Category { get; set; }

	public string DispName { get; set; }

	public string Description { get; set; }

	public bool? IsAdvanced { get; set; }

	public int? Order { get; set; }

	public bool? Browsable { get; set; }

	internal string EnglishCategory { get; set; }

	internal string EnglishDispName { get; set; }

	internal string EnglishDescription { get; set; }

	internal bool HasCompleteRussianTranslation
	{
		get
		{
			if (ConfigText.HasRussian(EnglishCategory) && ConfigText.HasRussian(EnglishDispName))
			{
				return ConfigText.HasRussian(EnglishDescription);
			}
			return false;
		}
	}

	internal bool ApplyLanguage(string language)
	{
		bool russian = ConfigText.IsRussian(language);
		string text = ConfigText.Localize(EnglishCategory, russian);
		string text2 = ConfigText.Localize(EnglishDispName, russian);
		string text3 = ConfigText.Localize(EnglishDescription, russian);
		bool result = Category != text || DispName != text2 || Description != text3;
		Category = text;
		DispName = text2;
		Description = text3;
		return result;
	}
}
internal static class ConfigurationManagerIntegration
{
	private const string PluginGuid = "_shudnal.ConfigurationManager";

	private const string DrawerTypeName = "ConfigurationManager.SettingFieldDrawer";

	private static bool _russian;

	private static bool _refreshFailureReported;

	private static PropertyInfo _guiContentTextProperty;

	internal static void SetLanguage(string language)
	{
		_russian = ConfigText.IsRussian(language);
	}

	internal static string GetEnumLabel(object value)
	{
		return ConfigText.EnumLabel(value, _russian);
	}

	internal static bool TryInstallEnumLocalization(Harmony harmony)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Expected O, but got Unknown
		if (harmony == null)
		{
			return false;
		}
		try
		{
			Type type = AccessTools.TypeByName("ConfigurationManager.SettingFieldDrawer");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "ObjectToGuiContent", new Type[1] { typeof(object) }, (Type[])null));
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ConfigurationManagerIntegration), "EnumGuiContentPostfix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				return false;
			}
			_guiContentTextProperty = methodInfo.ReturnType.GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
			if (_guiContentTextProperty == null || !_guiContentTextProperty.CanWrite)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource log = RavenMapPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("Configuration Manager enum localization was unavailable: " + ex.Message));
			}
			return false;
		}
	}

	internal static void RefreshIfInstalled()
	{
		try
		{
			Type type = AccessTools.TypeByName("ConfigurationManager.SettingFieldDrawer");
			if (type != null)
			{
				AccessTools.Method(type, "ClearCache", (Type[])null, (Type[])null)?.Invoke(null, null);
			}
			if (Chainloader.PluginInfos.TryGetValue("_shudnal.ConfigurationManager", out var value) && !((Object)(object)value.Instance == (Object)null))
			{
				AccessTools.Method(((object)value.Instance).GetType(), "BuildSettingList", (Type[])null, (Type[])null)?.Invoke(value.Instance, null);
				_refreshFailureReported = false;
			}
		}
		catch (Exception ex)
		{
			if (!_refreshFailureReported)
			{
				_refreshFailureReported = true;
				ManualLogSource log = RavenMapPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Configuration Manager could not refresh localized RavenMap labels: " + ex.Message));
				}
			}
		}
	}

	private static void EnumGuiContentPostfix(object x, object __result)
	{
		string enumLabel = GetEnumLabel(x);
		if (!string.IsNullOrEmpty(enumLabel) && __result != null && _guiContentTextProperty != null)
		{
			_guiContentTextProperty.SetValue(__result, enumLabel, null);
		}
	}
}
internal sealed class ModConfig
{
	internal const string LegacyResourcePrefabAllowList = "MineRock_Copper;MineRock_Tin;mine_rock_iron;silvervein;MineRock_Obsidian;MineRock_Meteorite;Leviathan;giant_brain;softtissue_mineable;blackmarble_1;blackmarble_2;blackmarble_3;blackmarble_4;blackmarble_head_big01;blackmarble_head_big02;blackmarble_head01;blackmarble_head02;blackmarble_out_1;blackmarble_out_2;blackmarble_out_3;blackmarble_out_4;blackmarble_tip;MineRock_Lava;MineRock_Lava_big;coal_rock1_bal;coal_rock_nomoss_bal;MineRock_Coal_bal;MineRock_CoalSnow_bal;gold_Rock_bal;MineRock_Blackmetal_bal;MineRock_Borax_bal;MineRock_Cobalt_bal;MineRock_DarkIron_bal;MineRock_Lead_bal;MineRock_Zinc_bal;MineRock_Nickel_bal;rock_nickel_bal;rock_nickel_nomoss_bal;MineRock_Copper_bal;MineRock_IronNew_bal;MineRock_IronSnow_bal;MineRock_Guck_bal;rock_silver_small_bal;rock_silver_small_frac_bal;ClayDeposit_bal;ChitinDeposit_bal;bloodshard_deposit_bal;MineRock_MeteoriteNew_bal;Pickable_Clay_bal;Pickable_GuckSack_bal;Pickable_MeteoriteNew_bal;Pickable_MagmaStone_bal;Pickable_TinNew_bal;Pickable_MountainCaveCrystal_bal;deposit_jotunfinger_bal";

	internal const string DefaultResourcePrefabAllowList = "MineRock_Copper;rock4_copper;MineRock_Tin;mine_rock_iron;silvervein;MineRock_Obsidian;MineRock_Meteorite;Leviathan;giant_brain;softtissue_mineable;blackmarble_1;blackmarble_2;blackmarble_3;blackmarble_4;blackmarble_head_big01;blackmarble_head_big02;blackmarble_head01;blackmarble_head02;blackmarble_out_1;blackmarble_out_2;blackmarble_out_3;blackmarble_out_4;blackmarble_tip;MineRock_Lava;MineRock_Lava_big;coal_rock1_bal;coal_rock_nomoss_bal;MineRock_Coal_bal;MineRock_CoalSnow_bal;gold_Rock_bal;MineRock_Blackmetal_bal;MineRock_Borax_bal;MineRock_Cobalt_bal;MineRock_DarkIron_bal;MineRock_Lead_bal;MineRock_Zinc_bal;MineRock_Nickel_bal;rock_nickel_bal;rock_nickel_nomoss_bal;MineRock_Copper_bal;MineRock_IronNew_bal;MineRock_IronSnow_bal;MineRock_Guck_bal;rock_silver_small_bal;ClayDeposit_bal;ChitinDeposit_bal;MineRock_MeteoriteNew_bal;deposit_jotunfinger_bal";

	internal ConfigEntry<bool> Enabled;

	internal ConfigEntry<bool> ShowPinNames;

	internal ConfigEntry<bool> ShowPortalNames;

	internal ConfigEntry<float> PinIconScale;

	internal ConfigEntry<float> FilterScrollSensitivity;

	internal ConfigEntry<bool> ShareFog;

	internal ConfigEntry<bool> SharePins;

	internal ConfigEntry<float> ResourceDiscoveryRadius;

	internal ConfigEntry<float> DungeonDiscoveryRadius;

	internal ConfigEntry<SilverDiscoveryMode> SilverMode;

	internal ConfigEntry<bool> AutoPinUndergroundResources;

	internal ConfigEntry<bool> TrackPickables;

	internal ConfigEntry<float> PickableDiscoveryRadius;

	internal ConfigEntry<bool> CrossOutRespawningPickables;

	internal ConfigEntry<float> ResourceRemovalPercent;

	internal ConfigEntry<float> ResourceProgressStepPercent;

	internal ConfigEntry<float> ResourceProgressMinInterval;

	internal ConfigEntry<string> ResourcePrefabAllowList;

	internal ConfigEntry<string> ResourcePrefabBlockList;

	internal ConfigEntry<string> ResourceItemBlockList;

	internal ConfigEntry<string> DungeonPrefabAllowList;

	internal ConfigEntry<float> RequiredDefeatedEnemiesPercent;

	internal ConfigEntry<float> DungeonChestInspectionPercent;

	internal ConfigEntry<int> DungeonMinimumObservedChests;

	internal ConfigEntry<float> DungeonCensusSettleSeconds;

	internal ConfigEntry<DungeonRespawnMode> DungeonRespawn;

	internal ConfigEntry<bool> ReopenDungeonOnRespawn;

	internal ConfigEntry<KeyCode> ManualDungeonToggleModifier;

	internal ConfigEntry<bool> AutoPinPortals;

	internal ConfigEntry<bool> TrackShips;

	internal ConfigEntry<bool> TrackCarts;

	internal ConfigEntry<float> VehicleUpdateInterval;

	internal ConfigEntry<float> VehicleMinimumMoveDistance;

	internal ConfigEntry<float> ResourceDedupeDistance;

	internal ConfigEntry<float> PickableDedupeDistance;

	internal ConfigEntry<float> DungeonDedupeDistance;

	internal ConfigEntry<float> PortalDedupeDistance;

	internal ConfigEntry<bool> ShimmerEnabled;

	internal ConfigEntry<ShimmerAnimationMode> ShimmerMode;

	internal ConfigEntry<bool> HighlightPickables;

	internal ConfigEntry<Color> PulseColor;

	internal ConfigEntry<float> PulseInterval;

	internal ConfigEntry<float> PulseDuration;

	internal ConfigEntry<float> PulseIntensity;

	internal ConfigEntry<Color> SweepColor;

	internal ConfigEntry<float> SweepInterval;

	internal ConfigEntry<float> SweepSpeed;

	internal ConfigEntry<float> SweepMaximumDuration;

	internal ConfigEntry<float> SweepIntensity;

	internal ConfigEntry<float> ShimmerRadius;

	internal ConfigEntry<float> SweepWaveWidth;

	internal ConfigEntry<float> SweepSoftness;

	internal ConfigEntry<bool> XrayEnabled;

	internal ConfigEntry<KeyCode> XrayToggleKey;

	internal ConfigEntry<Color> XrayColor;

	internal ConfigEntry<float> XrayIntensity;

	internal ConfigEntry<float> XrayRadius;

	internal ConfigEntry<int> XrayRendererBudget;

	internal ConfigEntry<int> XrayDrawCommandBudget;

	internal ConfigEntry<int> ShimmerRendererBudget;

	internal ConfigEntry<float> SaveDebounceSeconds;

	internal ConfigEntry<int> MaxFogDeltaPixels;

	internal ConfigEntry<int> FogMergeCellsPerFrame;

	internal ConfigEntry<int> MaxNetworkEventsPerTenSeconds;

	internal ConfigEntry<int> MaxPinAddsPerFrame;

	internal ConfigEntry<bool> ShowPinOverlapDiagnostics;

	internal ConfigEntry<float> PinOverlapDiagnosticDistance;

	internal ConfigEntry<bool> ShowStateDiagnostics;

	internal ConfigEntry<bool> DetailedLogging;

	private const string InterfaceCategory = "01 - Interface";

	private const string SharingCategory = "02 - Sharing";

	private const string DiscoveryCategory = "03 - Discovery";

	private const string ResourcesCategory = "04 - Resources";

	private const string PickablesCategory = "04.1 - Pickables";

	private const string DungeonsCategory = "05 - Dungeons";

	private const string MarkersCategory = "06 - Markers";

	private const string DeduplicationCategory = "07 - Deduplication";

	private const string ShimmerCategory = "08 - Shimmer";

	private const string PulseCategory = "08.1 - Pulse";

	private const string SweepCategory = "08.2 - Sweep";

	private const string XrayCategory = "08.3 - X-ray";

	private const string PerformanceCategory = "09 - Performance";

	private const string DiagnosticsCategory = "10 - Diagnostics";

	private const int VisualSettingsVersion = 1;

	private static readonly FieldInfo[] ConfigEntryFields = (from field in typeof(ModConfig).GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
		where typeof(ConfigEntryBase).IsAssignableFrom(field.FieldType)
		select field).ToArray();

	private static readonly FieldInfo ConfigDescriptionTextField = typeof(ConfigDescription).GetField("<Description>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

	private static bool _canRewriteConfigDescription = ConfigDescriptionTextField != null;

	internal bool AnyResourceVisualEnabled
	{
		get
		{
			if (!ShimmerEnabled.Value)
			{
				return XrayEnabled.Value;
			}
			return true;
		}
	}

	private static ConfigDescription Describe(string description, string category, string displayName, int order, AcceptableValueBase acceptableValues = null, bool advanced = false)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
		{
			EnglishCategory = category,
			EnglishDispName = displayName,
			EnglishDescription = description,
			Order = order,
			IsAdvanced = advanced
		};
		configurationManagerAttributes.ApplyLanguage("English");
		return new ConfigDescription(configurationManagerAttributes.Description, acceptableValues, new object[1] { configurationManagerAttributes });
	}

	internal static ModConfig Bind(ConfigFile file, string language = "English")
	{
		if (file == null)
		{
			throw new ArgumentNullException("file");
		}
		bool saveOnConfigSet = file.SaveOnConfigSet;
		file.SaveOnConfigSet = false;
		try
		{
			ModConfig result = BindCore(file, language);
			if (saveOnConfigSet)
			{
				file.Save();
			}
			return result;
		}
		finally
		{
			file.SaveOnConfigSet = saveOnConfigSet;
		}
	}

	private static ModConfig BindCore(ConfigFile file, string language)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Expected O, but got Unknown
		//IL_099f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ab5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d43: Unknown result type (might be due to invalid IL or missing references)
		//IL_10f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_1148: Unknown result type (might be due to invalid IL or missing references)
		int num = ((!HasPersistedLegacyVisualSettings(file)) ? 1 : 0);
		ConfigEntry<int> val = file.Bind<int>("00 - Internal", "VisualSettingsVersion", num, new ConfigDescription("Internal RavenMap visual-settings migration version.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Browsable = false,
				IsAdvanced = true,
				Order = int.MinValue
			}
		}));
		ModConfig modConfig = new ModConfig
		{
			Enabled = file.Bind<bool>("01 - General", "Enabled", true, Describe("Enable RavenMap. Requires a restart.", "01 - Interface", "Enabled", 1000)),
			ShowPinNames = file.Bind<bool>("01 - General", "ShowAutomaticPinNames", false, Describe("Show names beside automatic pins. Local setting.", "01 - Interface", "Automatic pin names", 990)),
			ShowPortalNames = file.Bind<bool>("01 - General", "ShowPortalNames", true, Describe("Show portal names independently. Local setting.", "01 - Interface", "Portal names", 980)),
			PinIconScale = file.Bind<float>("01 - General", "AutomaticPinIconScale", 0.7f, Describe("Local automatic-pin size multiplier.", "01 - Interface", "Pin icon size", 970, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f))),
			FilterScrollSensitivity = file.Bind<float>("01 - General", "FilterScrollSpeed", 360f, Describe("Map-filter wheel speed in UI pixels per step.", "01 - Interface", "Filter scroll speed", 960, (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 1200f))),
			ShareFog = file.Bind<bool>("02 - Multiplayer", "ShareFog", true, Describe("Merge explored fog into the shared server map.", "02 - Sharing", "Shared fog", 900)),
			SharePins = file.Bind<bool>("02 - Multiplayer", "ShareAutomaticPins", true, Describe("Share automatic pins with every player.", "02 - Sharing", "Shared automatic pins", 890)),
			ResourceDiscoveryRadius = file.Bind<float>("03 - Discovery", "ResourceRadius", 30f, Describe("Discovery radius for loaded resources; unloaded zones are never scanned.", "03 - Discovery", "Resource radius", 800, (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 128f))),
			DungeonDiscoveryRadius = file.Bind<float>("03 - Discovery", "DungeonRadius", 30f, Describe("Discovery radius for loaded dungeons.", "03 - Discovery", "Dungeon radius", 790, (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 256f))),
			SilverMode = file.Bind<SilverDiscoveryMode>("03 - Discovery", "SilverDiscovery", SilverDiscoveryMode.WishboneOnly, Describe("WishboneOnly is vanilla-like; Radius is automatic; Disabled never pins silver.", "03 - Discovery", "Silver discovery", 780)),
			AutoPinUndergroundResources = file.Bind<bool>("03 - Discovery", "AutoPinUndergroundResources", false, Describe("Discover non-silver underground resources automatically instead of on detection or mining.", "03 - Discovery", "Underground resources", 770)),
			ResourceRemovalPercent = file.Bind<float>("04 - Resources", "RemoveAfterDepletedPercent", 50f, Describe("Remove a vein pin after this share of its original amount is depleted.", "04 - Resources", "Remove after depletion", 700, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f))),
			ResourceProgressStepPercent = file.Bind<float>("04 - Resources", "ProgressUpdateStepPercent", 2f, Describe("Extra depletion required before another progress update.", "04 - Resources", "Progress update step", 690, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), advanced: true)),
			ResourceProgressMinInterval = file.Bind<float>("04 - Resources", "ProgressUpdateMinIntervalSeconds", 0.25f, Describe("Minimum interval between ordinary updates for one resource.", "04 - Resources", "Progress update interval", 680, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), advanced: true)),
			ResourcePrefabAllowList = file.Bind<string>("04 - Resources", "PrefabAllowList", "MineRock_Copper;rock4_copper;MineRock_Tin;mine_rock_iron;silvervein;MineRock_Obsidian;MineRock_Meteorite;Leviathan;giant_brain;softtissue_mineable;blackmarble_1;blackmarble_2;blackmarble_3;blackmarble_4;blackmarble_head_big01;blackmarble_head_big02;blackmarble_head01;blackmarble_head02;blackmarble_out_1;blackmarble_out_2;blackmarble_out_3;blackmarble_out_4;blackmarble_tip;MineRock_Lava;MineRock_Lava_big;coal_rock1_bal;coal_rock_nomoss_bal;MineRock_Coal_bal;MineRock_CoalSnow_bal;gold_Rock_bal;MineRock_Blackmetal_bal;MineRock_Borax_bal;MineRock_Cobalt_bal;MineRock_DarkIron_bal;MineRock_Lead_bal;MineRock_Zinc_bal;MineRock_Nickel_bal;rock_nickel_bal;rock_nickel_nomoss_bal;MineRock_Copper_bal;MineRock_IronNew_bal;MineRock_IronSnow_bal;MineRock_Guck_bal;rock_silver_small_bal;ClayDeposit_bal;ChitinDeposit_bal;MineRock_MeteoriteNew_bal;deposit_jotunfinger_bal", Describe("Semicolon-separated source prefabs; SourcePrefab=ItemPrefab overrides inferred drops.", "04 - Resources", "Source prefab allow list", 670, null, advanced: true)),
			ResourcePrefabBlockList = file.Bind<string>("04 - Resources", "PrefabBlockList", "rock1_mountain;rock2_mountain;rock3_mountain", Describe("Semicolon-separated prefabs never treated as resources.", "04 - Resources", "Source prefab block list", 660, null, advanced: true)),
			ResourceItemBlockList = file.Bind<string>("04 - Resources", "DroppedItemBlockList", "$item_stone;$item_wood;$item_roundlog", Describe("Ignore deposits whose inferred drops are only these item keys.", "04 - Resources", "Dropped item block list", 650, null, advanced: true)),
			TrackPickables = file.Bind<bool>("03 - Discovery", "TrackRespawningPickables", true, Describe("Server policy: track naturally spawned renewable and one-shot berries, mushrooms, plants and other Pickable objects.", "04.1 - Pickables", "Enabled", 700)),
			PickableDiscoveryRadius = file.Bind<float>("03 - Discovery", "PickableRadius", 30f, Describe("Server discovery radius for loaded pickable plants; unloaded zones are never scanned.", "04.1 - Pickables", "Discovery radius", 690, (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 128f))),
			CrossOutRespawningPickables = file.Bind<bool>("04.1 - Pickables", "CrossOutRespawningPickables", true, Describe("Server policy: cross out a renewable pickable after harvest and restore it when the game respawns it.", "04.1 - Pickables", "Cross out after harvest", 680)),
			DungeonPrefabAllowList = file.Bind<string>("05 - Dungeons", "PrefabAllowList", "Crypt2;Crypt3;Crypt4;SunkenCrypt4;MountainCave02;Mistlands_DvergrTownEntrance1;Mistlands_DvergrTownEntrance2;Mistlands_DvergrTownEntrance3;BFD_Exterior;CD_Exterior1", Describe("Semicolon-separated location prefabs always treated as clearable dungeons.", "05 - Dungeons", "Dungeon prefab allow list", 620, null, advanced: true)),
			RequiredDefeatedEnemiesPercent = file.Bind<float>("05 - Dungeons", "RequiredDefeatedEnemiesPercent", 60f, Describe("Defeated share of observed enemies required before crossing out a dungeon.", "05 - Dungeons", "Defeated enemies required", 610, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f))),
			DungeonChestInspectionPercent = file.Bind<float>("05 - Dungeons", "RequiredInspectedChestsPercent", 60f, Describe("Observed-chest share required before crossing out a dungeon.", "05 - Dungeons", "Inspected chests required", 590, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f))),
			DungeonMinimumObservedChests = file.Bind<int>("05 - Dungeons", "MinimumObservedChests", 0, Describe("Minimum observed chests required for automatic clearing.", "05 - Dungeons", "Minimum observed chests", 580, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100))),
			DungeonCensusSettleSeconds = file.Bind<float>("05 - Dungeons", "CensusSettleSeconds", 1.25f, Describe("Quiet period with no newly observed enemies or chests before the loaded dungeon inventory is considered complete.", "05 - Dungeons", "Dungeon inventory settle delay", 570, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), advanced: true)),
			DungeonRespawn = file.Bind<DungeonRespawnMode>("05 - Dungeons", "EnemyRespawnMode", DungeonRespawnMode.Auto, Describe("Auto follows observed spawners; Enabled forces reopening; Disabled keeps cleared state.", "05 - Dungeons", "Enemy respawn mode", 560)),
			ReopenDungeonOnRespawn = file.Bind<bool>("05 - Dungeons", "ReopenWhenEnemiesRespawn", true, Describe("Remove crossed-out state when a respawn-capable dungeon gets a live enemy.", "05 - Dungeons", "Reopen on respawn", 550)),
			ManualDungeonToggleModifier = file.Bind<KeyCode>("05 - Dungeons", "ManualToggleModifier", (KeyCode)308, Describe("Hold this key and left-click a RavenMap dungeon icon to share a forced cleared/active state. Set None to disable.", "05 - Dungeons", "Manual dungeon toggle", 540)),
			AutoPinPortals = file.Bind<bool>("06 - Portals and vehicles", "AutoPinPortals", true, Describe("Pin portals automatically and keep their tag current.", "06 - Markers", "Automatic portal pins", 500)),
			TrackShips = file.Bind<bool>("06 - Portals and vehicles", "TrackShips", true, Describe("Track vanilla and modded Ship components.", "06 - Markers", "Track ships", 490)),
			TrackCarts = file.Bind<bool>("06 - Portals and vehicles", "TrackCarts", true, Describe("Track vanilla and modded Vagon components.", "06 - Markers", "Track carts", 480)),
			VehicleUpdateInterval = file.Bind<float>("06 - Portals and vehicles", "VehicleUpdateIntervalSeconds", 2f, Describe("Position-report interval for loaded vehicles.", "06 - Markers", "Vehicle update interval", 470, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), advanced: true)),
			VehicleMinimumMoveDistance = file.Bind<float>("06 - Portals and vehicles", "VehicleMinimumMoveDistance", 2f, Describe("Movement required before reporting a new vehicle position.", "06 - Markers", "Vehicle move threshold", 460, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 25f), advanced: true)),
			ResourceDedupeDistance = file.Bind<float>("07 - Deduplication", "ResourceDistance", 4f, Describe("Server merge distance for equivalent resource pins.", "07 - Deduplication", "Resource merge distance", 400, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f))),
			PickableDedupeDistance = file.Bind<float>("07 - Deduplication", "PickableDistance", 4f, Describe("Server merge distance for equivalent pickable pins.", "04.1 - Pickables", "Merge distance", 670, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f))),
			DungeonDedupeDistance = file.Bind<float>("07 - Deduplication", "DungeonDistance", 12f, Describe("Server merge distance for equivalent dungeon pins.", "07 - Deduplication", "Dungeon merge distance", 390, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f))),
			PortalDedupeDistance = file.Bind<float>("07 - Deduplication", "PortalDistance", 2f, Describe("Server merge distance for equivalent portal pins.", "07 - Deduplication", "Portal merge distance", 380, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f))),
			ShimmerEnabled = file.Bind<bool>("08 - Resource shimmer", "Enabled", true, Describe("Periodically highlight nearby visible resources and enabled Pickables without material instances.", "08 - Shimmer", "Enabled", 320)),
			ShimmerMode = file.Bind<ShimmerAnimationMode>("08 - Resource shimmer", "AnimationMode", ShimmerAnimationMode.Sweep, Describe("Sweep travels across highlighted geometry; Pulse brightens the whole object.", "08 - Shimmer", "Animation", 310)),
			HighlightPickables = file.Bind<bool>("08 - Resource shimmer", "HighlightPickables", false, Describe("Apply the same Pulse or Sweep highlight to nearby loaded natural Pickables. Local setting.", "08 - Shimmer", "Pickable shimmer", 300)),
			PulseColor = file.Bind<Color>("08 - Resource shimmer", "Color", new Color(1f, 1f, 1f, 0.2f), Describe("Color used by the whole-object pulse.", "08.1 - Pulse", "Pulse color", 300)),
			PulseInterval = file.Bind<float>("08 - Resource shimmer", "IntervalSeconds", 1f, Describe("Delay between pulse effects.", "08.1 - Pulse", "Pulse interval", 290, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f))),
			PulseDuration = file.Bind<float>("08 - Resource shimmer", "DurationSeconds", 1f, Describe("Duration of one smooth pulse.", "08.1 - Pulse", "Pulse duration", 280, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f))),
			PulseIntensity = file.Bind<float>("08 - Resource shimmer", "Intensity", 2f, Describe("Tint and HDR glow strength of the pulse.", "08.1 - Pulse", "Pulse intensity", 270, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))),
			SweepColor = file.Bind<Color>("08 - Resource shimmer", "SweepColor", new Color(1f, 1f, 1f, 0.2f), Describe("Color of the soft wave travelling across highlighted geometry.", "08.2 - Sweep", "Sweep color", 300)),
			SweepInterval = file.Bind<float>("08 - Resource shimmer", "SweepIntervalSeconds", 1f, Describe("Delay between sweep effects.", "08.2 - Sweep", "Sweep interval", 290, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f))),
			SweepSpeed = file.Bind<float>("08 - Resource shimmer", "SweepSpeedMetersPerSecond", 5f, Describe("World-space speed of the travelling wave.", "08.2 - Sweep", "Sweep speed", 280, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f))),
			SweepMaximumDuration = file.Bind<float>("08 - Resource shimmer", "SweepMaximumDurationSeconds", 3f, Describe("Safety limit for one sweep; very large geometry still completes its exit.", "08.2 - Sweep", "Sweep maximum duration", 270, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f))),
			SweepIntensity = file.Bind<float>("08 - Resource shimmer", "SweepIntensity", 3f, Describe("Tint and HDR glow strength of the travelling wave.", "08.2 - Sweep", "Sweep intensity", 260, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))),
			ShimmerRadius = file.Bind<float>("08 - Resource shimmer", "Radius", 20f, Describe("Only loaded resources and enabled Pickables inside this radius may shimmer.", "08 - Shimmer", "Radius", 260, (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 96f))),
			SweepWaveWidth = file.Bind<float>("08 - Resource shimmer", "WaveWidth", 1.5f, Describe("Relative width of the soft wave; the maximum covers almost the complete highlighted object.", "08.2 - Sweep", "Sweep width", 250, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f))),
			SweepSoftness = file.Bind<float>("08 - Resource shimmer", "SweepSoftness", 1f, Describe("Softness of the moving wave; higher values remove the flat center and spread the fade across the whole effect.", "08.2 - Sweep", "Sweep softness", 240, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))),
			XrayEnabled = file.Bind<bool>("08 - Resource shimmer", "XrayEnabled", false, Describe("Enable local X-ray rendering. Its hotkey only changes temporary visibility while this setting is enabled.", "08.3 - X-ray", "X-ray through terrain", 240)),
			XrayToggleKey = file.Bind<KeyCode>("08 - Resource shimmer", "XrayToggleKey", (KeyCode)285, Describe("Toggle local X-ray visibility during gameplay. Works only when XrayEnabled is on; set None to disable.", "08.3 - X-ray", "X-ray toggle key", 235)),
			XrayColor = file.Bind<Color>("08 - Resource shimmer", "XrayColor", new Color(1f, 1f, 0f, 0.050980393f), Describe("Color used for vein geometry visible through terrain.", "08.3 - X-ray", "X-ray color", 230)),
			XrayIntensity = file.Bind<float>("08 - Resource shimmer", "XrayIntensity", 1f, Describe("HDR brightness of X-ray vein geometry.", "08.3 - X-ray", "X-ray intensity", 220, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f))),
			XrayRadius = file.Bind<float>("08 - Resource shimmer", "XrayRadius", 10f, Describe("Only already discovered loaded veins inside this local radius appear in X-ray.", "08.3 - X-ray", "X-ray radius", 210, (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 96f))),
			XrayRendererBudget = file.Bind<int>("08 - Resource shimmer", "XrayMaxRenderers", 256, Describe("Total renderer budget for the current X-ray selection.", "08.3 - X-ray", "X-ray renderer budget", 200, (AcceptableValueBase)(object)new AcceptableValueRange<int>(16, 512), advanced: true)),
			XrayDrawCommandBudget = file.Bind<int>("08 - Resource shimmer", "XrayMaxDrawCommands", 1024, Describe("Hard resource draw-command limit for modded meshes with many submeshes.", "08.3 - X-ray", "X-ray resource draw budget", 190, (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 2048), advanced: true)),
			ShimmerRendererBudget = file.Bind<int>("08 - Resource shimmer", "MaxRenderersPerPulse", 256, Describe("Hard total renderer limit for one complete shimmer batch.", "08 - Shimmer", "Renderer budget", 200, (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 256), advanced: true)),
			SaveDebounceSeconds = file.Bind<float>("09 - Performance", "PersistenceDebounceSeconds", 10f, Describe("Idle time after the last state change before saving; continuous changes are flushed periodically.", "09 - Performance", "Save debounce", 200, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), advanced: true)),
			MaxFogDeltaPixels = file.Bind<int>("09 - Performance", "MaxFogPixelsPerPacket", 2048, Describe("Hard fog-delta packet limit.", "09 - Performance", "Fog pixels per packet", 190, (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 8192), advanced: true)),
			FogMergeCellsPerFrame = file.Bind<int>("09 - Performance", "FogMergeCellsPerFrame", 8192, Describe("Shared-fog cells examined per frame; lower values reduce spikes.", "09 - Performance", "Fog merge cells per frame", 180, (AcceptableValueBase)(object)new AcceptableValueRange<int>(4096, 262144), advanced: true)),
			MaxNetworkEventsPerTenSeconds = file.Bind<int>("09 - Performance", "MaxPeerEventsPerTenSeconds", 500, Describe("Server event-rate guard per peer.", "09 - Performance", "Peer event limit", 170, (AcceptableValueBase)(object)new AcceptableValueRange<int>(200, 5000), advanced: true)),
			MaxPinAddsPerFrame = file.Bind<int>("09 - Performance", "MaxPinAddsPerFrame", 96, Describe("Frame budget for restoring or enabling automatic pins.", "09 - Performance", "Pin adds per frame", 160, (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 512), advanced: true)),
			ShowPinOverlapDiagnostics = file.Bind<bool>("10 - Diagnostics", "ShowPinOverlapCounters", false, Describe("Show local P/S counters for overlapping canonical pins and sources.", "10 - Diagnostics", "Overlap counters", 100)),
			PinOverlapDiagnosticDistance = file.Bind<float>("10 - Diagnostics", "PinOverlapDistance", 1.5f, Describe("World-space grouping distance for overlap counters.", "10 - Diagnostics", "Overlap distance", 90, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), advanced: true)),
			ShowStateDiagnostics = file.Bind<bool>("10 - Diagnostics", "ShowStateOnMap", false, Describe("Show local resource measurements and dungeon clearing blockers beside RavenMap pins. Local setting.", "10 - Diagnostics", "State diagnostics on map", 80)),
			DetailedLogging = file.Bind<bool>("10 - Diagnostics", "DetailedLogging", false, Describe("Log classification, deduplication and network details.", "10 - Diagnostics", "Detailed logging", 70, null, advanced: true))
		};
		if (val.Value < 1)
		{
			if (modConfig.ShimmerMode.Value == ShimmerAnimationMode.Sweep)
			{
				modConfig.SweepColor.Value = modConfig.PulseColor.Value;
				modConfig.SweepInterval.Value = modConfig.PulseInterval.Value;
				modConfig.SweepIntensity.Value = modConfig.PulseIntensity.Value;
				modConfig.PulseColor.Value = new Color(1f, 1f, 1f, 0.2f);
				modConfig.PulseInterval.Value = 1f;
				modConfig.PulseDuration.Value = 1f;
				modConfig.PulseIntensity.Value = 2f;
			}
			val.Value = 1;
		}
		if (string.Equals(modConfig.ResourcePrefabAllowList.Value, "MineRock_Copper;MineRock_Tin;mine_rock_iron;silvervein;MineRock_Obsidian;MineRock_Meteorite;Leviathan;giant_brain;softtissue_mineable;blackmarble_1;blackmarble_2;blackmarble_3;blackmarble_4;blackmarble_head_big01;blackmarble_head_big02;blackmarble_head01;blackmarble_head02;blackmarble_out_1;blackmarble_out_2;blackmarble_out_3;blackmarble_out_4;blackmarble_tip;MineRock_Lava;MineRock_Lava_big;coal_rock1_bal;coal_rock_nomoss_bal;MineRock_Coal_bal;MineRock_CoalSnow_bal;gold_Rock_bal;MineRock_Blackmetal_bal;MineRock_Borax_bal;MineRock_Cobalt_bal;MineRock_DarkIron_bal;MineRock_Lead_bal;MineRock_Zinc_bal;MineRock_Nickel_bal;rock_nickel_bal;rock_nickel_nomoss_bal;MineRock_Copper_bal;MineRock_IronNew_bal;MineRock_IronSnow_bal;MineRock_Guck_bal;rock_silver_small_bal;rock_silver_small_frac_bal;ClayDeposit_bal;ChitinDeposit_bal;bloodshard_deposit_bal;MineRock_MeteoriteNew_bal;Pickable_Clay_bal;Pickable_GuckSack_bal;Pickable_MeteoriteNew_bal;Pickable_MagmaStone_bal;Pickable_TinNew_bal;Pickable_MountainCaveCrystal_bal;deposit_jotunfinger_bal", StringComparison.Ordinal))
		{
			modConfig.ResourcePrefabAllowList.Value = "MineRock_Copper;rock4_copper;MineRock_Tin;mine_rock_iron;silvervein;MineRock_Obsidian;MineRock_Meteorite;Leviathan;giant_brain;softtissue_mineable;blackmarble_1;blackmarble_2;blackmarble_3;blackmarble_4;blackmarble_head_big01;blackmarble_head_big02;blackmarble_head01;blackmarble_head02;blackmarble_out_1;blackmarble_out_2;blackmarble_out_3;blackmarble_out_4;blackmarble_tip;MineRock_Lava;MineRock_Lava_big;coal_rock1_bal;coal_rock_nomoss_bal;MineRock_Coal_bal;MineRock_CoalSnow_bal;gold_Rock_bal;MineRock_Blackmetal_bal;MineRock_Borax_bal;MineRock_Cobalt_bal;MineRock_DarkIron_bal;MineRock_Lead_bal;MineRock_Zinc_bal;MineRock_Nickel_bal;rock_nickel_bal;rock_nickel_nomoss_bal;MineRock_Copper_bal;MineRock_IronNew_bal;MineRock_IronSnow_bal;MineRock_Guck_bal;rock_silver_small_bal;ClayDeposit_bal;ChitinDeposit_bal;MineRock_MeteoriteNew_bal;deposit_jotunfinger_bal";
		}
		if (modConfig.PulseIntensity.Value < 1f)
		{
			modConfig.PulseIntensity.Value = 1f;
		}
		if (modConfig.SweepIntensity.Value < 1f)
		{
			modConfig.SweepIntensity.Value = 1f;
		}
		if (modConfig.XrayIntensity.Value < 1f)
		{
			modConfig.XrayIntensity.Value = 1f;
		}
		if (modConfig.ShimmerRendererBudget.Value == 48)
		{
			modConfig.ShimmerRendererBudget.Value = 256;
		}
		if (Mathf.Approximately(modConfig.FilterScrollSensitivity.Value, 28f) || Mathf.Approximately(modConfig.FilterScrollSensitivity.Value, 120f))
		{
			modConfig.FilterScrollSensitivity.Value = 360f;
		}
		if (modConfig.MaxNetworkEventsPerTenSeconds.Value < 200)
		{
			modConfig.MaxNetworkEventsPerTenSeconds.Value = 200;
		}
		if (modConfig.FogMergeCellsPerFrame.Value == 32768)
		{
			modConfig.FogMergeCellsPerFrame.Value = 8192;
		}
		modConfig.ApplyLocalization(language);
		return modConfig;
	}

	private static bool HasPersistedLegacyVisualSettings(ConfigFile file)
	{
		string text = ((file != null) ? file.ConfigFilePath : null);
		if (string.IsNullOrEmpty(text) || !File.Exists(text))
		{
			return false;
		}
		try
		{
			bool flag = false;
			foreach (string item in File.ReadLines(text))
			{
				string text2 = item.Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				if (text2[0] == '[' && text2[text2.Length - 1] == ']')
				{
					flag = string.Equals(text2.Substring(1, text2.Length - 2).Trim(), "08 - Resource shimmer", StringComparison.OrdinalIgnoreCase);
				}
				else
				{
					if (!flag)
					{
						continue;
					}
					int num = text2.IndexOf('=');
					if (num > 0)
					{
						string a = text2.Substring(0, num).Trim();
						if (string.Equals(a, "AnimationMode", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Color", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "IntervalSeconds", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "DurationSeconds", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Intensity", StringComparison.OrdinalIgnoreCase))
						{
							return true;
						}
					}
				}
			}
		}
		catch (IOException)
		{
			return true;
		}
		catch (UnauthorizedAccessException)
		{
			return true;
		}
		return false;
	}

	internal bool ApplyLocalization(string language)
	{
		bool flag = false;
		FieldInfo[] configEntryFields = ConfigEntryFields;
		for (int i = 0; i < configEntryFields.Length; i++)
		{
			object? value = configEntryFields[i].GetValue(this);
			ConfigEntryBase val = (ConfigEntryBase)((value is ConfigEntryBase) ? value : null);
			if (val == null || val.Description == null)
			{
				continue;
			}
			ConfigurationManagerAttributes configurationManagerAttributes = val.Description.Tags?.OfType<ConfigurationManagerAttributes>().FirstOrDefault();
			if (configurationManagerAttributes == null)
			{
				continue;
			}
			flag |= configurationManagerAttributes.ApplyLanguage(language);
			if (_canRewriteConfigDescription)
			{
				try
				{
					ConfigDescriptionTextField.SetValue(val.Description, configurationManagerAttributes.Description);
				}
				catch
				{
					_canRewriteConfigDescription = false;
				}
			}
		}
		return flag;
	}

	internal Color GetShimmerColor(ShimmerAnimationMode mode)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		if (mode != ShimmerAnimationMode.Pulse)
		{
			return SweepColor.Value;
		}
		return PulseColor.Value;
	}

	internal Color GetXrayColor()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return XrayColor.Value;
	}
}
internal static class StableZdoIdentity
{
	private const string TokenKey = "dragonmotion.ravenmap.source.v1";

	private const string SourcePrefix = "stable:";

	private const int TokenLength = 32;

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

	private static readonly Dictionary<ZDOID, string> SourcesByZdo = new Dictionary<ZDOID, string>();

	internal static void Reset()
	{
		TokenOwners.Clear();
		SourcesByZdo.Clear();
	}

	internal static bool IsStableSource(string sourceId)
	{
		string token;
		return TryGetToken(sourceId, out token);
	}

	internal static bool TryGetToken(string sourceId, out string token)
	{
		token = string.Empty;
		if (string.IsNullOrEmpty(sourceId) || !sourceId.StartsWith("stable:", StringComparison.Ordinal) || sourceId.Length != "stable:".Length + 32)
		{
			return false;
		}
		string text = sourceId.Substring("stable:".Length);
		if (!IsValidToken(text))
		{
			return false;
		}
		token = text;
		return true;
	}

	internal static string PinId(PinCategory category, string sourceId)
	{
		byte b = (byte)category;
		return b + ":" + sourceId;
	}

	internal static bool TryGetExistingSource(ZDO zdo, out string sourceId)
	{
		//IL_0012: 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)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		sourceId = string.Empty;
		if (zdo == null)
		{
			return false;
		}
		if (SourcesByZdo.TryGetValue(zdo.m_uid, out var value) && IsStableSource(value))
		{
			TryGetToken(value, out var token);
			if (string.Equals(zdo.GetString("dragonmotion.ravenmap.source.v1", string.Empty), token, StringComparison.Ordinal) && TryBind(token, zdo.m_uid, rejectCollision: false, out var _))
			{
				sourceId = value;
				return true;
			}
			Forget(zdo.m_uid);
		}
		string token3 = zdo.GetString("dragonmotion.ravenmap.source.v1", string.Empty);
		if (!IsValidToken(token3) || !TryBind(token3, zdo.m_uid, rejectCollision: false, out token3))
		{
			return false;
		}
		sourceId = "stable:" + token3;
		SourcesByZdo[zdo.m_uid] = sourceId;
		return true;
	}

	internal static bool TryAssignSource(ZDO zdo, string sourceId)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !TryGetToken(sourceId, out var token))
		{
			return false;
		}
		if (TryGetExistingSource(zdo, out var sourceId2))
		{
			return string.Equals(sourceId2, sourceId, StringComparison.Ordinal);
		}
		if (!string.IsNullOrEmpty(zdo.GetString("dragonmotion.ravenmap.source.v1", string.Empty)) || !TryBind(token, zdo.m_uid, rejectCollision: true, out var _))
		{
			return false;
		}
		zdo.Set("dragonmotion.ravenmap.source.v1", token);
		SourcesByZdo[zdo.m_uid] = sourceId;
		return true;
	}

	internal static bool TryGetOrCreateSource(ZDO zdo, out string sourceId)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		if (TryGetExistingSource(zdo, out sourceId))
		{
			return true;
		}
		sourceId = string.Empty;
		if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return false;
		}
		string token;
		do
		{
			token = Guid.NewGuid().ToString("N");
		}
		while (!TryBind(token, zdo.m_uid, rejectCollision: true, out token));
		zdo.Set("dragonmotion.ravenmap.source.v1", token);
		sourceId = "stable:" + token;
		SourcesByZdo[zdo.m_uid] = sourceId;
		return true;
	}

	internal static bool TryResolveSource(string sourceId, out ZDO zdo)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		zdo = null;
		if (!TryGetToken(sourceId, out var token) || !TokenOwners.TryGetValue(token, out var value))
		{
			return false;
		}
		ZDOMan instance = ZDOMan.instance;
		ZDO val = ((instance != null) ? instance.GetZDO(value) : null);
		if (val == null || !string.Equals(val.GetString("dragonmotion.ravenmap.source.v1", string.Empty), token, StringComparison.Ordinal))
		{
			SourcesByZdo.Remove(value);
			TokenOwners.Remove(token);
			return false;
		}
		zdo = val;
		return true;
	}

	internal static void Forget(ZDOID id)
	{
		//IL_0005: 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_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		if (SourcesByZdo.TryGetValue(id, out var value))
		{
			SourcesByZdo.Remove(id);
			if (TryGetToken(value, out var token) && TokenOwners.TryGetValue(token, out var value2) && value2 == id)
			{
				TokenOwners.Remove(token);
			}
		}
	}

	private static bool TryBind(string requested, ZDOID id, bool rejectCollision, out string token)
	{
		//IL_0049: 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)
		//IL_001f: 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)
		token = requested;
		if (!IsValidToken(token))
		{
			return false;
		}
		if (TokenOwners.TryGetValue(token, out var value) && value != id)
		{
			ZDOMan instance = ZDOMan.instance;
			if (((instance != null) ? instance.GetZDO(value) : null) != null || rejectCollision)
			{
				return false;
			}
		}
		TokenOwners[token] = id;
		return true;
	}

	private static bool IsValidToken(string token)
	{
		if (string.IsNullOrEmpty(token) || token.Length != 32)
		{
			return false;
		}
		foreach (char c in token)
		{
			if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
			{
				return false;
			}
		}
		return true;
	}
}
internal enum PinCategory : byte
{
	Resource = 1,
	Dungeon,
	Portal,
	Ship,
	Cart,
	Pickable
}
internal enum PinStatus : byte
{
	Active,
	Depleted,
	Cleared,
	Destroyed
}
internal enum SilverDiscoveryMode
{
	WishboneOnly,
	Radius,
	Disabled
}
internal enum DungeonRespawnMode
{
	Auto,
	Enabled,
	Disabled
}
internal enum ShimmerAnimationMode
{
	Sweep,
	Pulse
}
internal enum DungeonEventType : byte
{
	RegisterEnemy = 1,
	EnemyKilled,
	RegisterChest,
	ChestInspected,
	RespawnCapable,
	RoomCount,
	GenerationReset,
	EnemyResolved,
	ChestResolved,
	DungeonEntered,
	CensusSettled,
	ManualSetCleared
}
internal sealed class PinRecord
{
	internal string Id = string.Empty;

	internal string SourceId = string.Empty;

	internal PinCategory Category;

	internal PinStatus Status;

	internal string Prefab = string.Empty;

	internal string DisplayName = string.Empty;

	internal string ResourceType = string.Empty;

	internal bool Respawns;

	internal int SourceCount = 1;

	internal Biome Biome;

	internal Vector3 Position;

	internal float InitialAmount;

	internal float RemainingAmount;

	internal long Revision;

	internal long UpdatedUtcTicks;

	internal bool IsVisibleState
	{
		get
		{
			if (Status != PinStatus.Destroyed)
			{
				return Status != PinStatus.Depleted;
			}
			return false;
		}
	}

	internal PinRecord Clone()
	{
		return (PinRecord)MemberwiseClone();
	}
}
internal sealed class FilterCatalogEntry
{
	internal PinCategory Category { get; }

	internal Biome Biome { get; }

	internal string Prefab { get; }

	internal string DisplayName { get; }

	internal string ResourceType { get; }

	internal FilterCatalogEntry(PinCategory category, Biome biome, string prefab, string displayName, string resourceType)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		Category = category;
		Biome = biome;
		Prefab = prefab ?? string.Empty;
		DisplayName = displayName ?? string.Empty;
		ResourceType = resourceType ?? string.Empty;
	}
}
internal sealed class DungeonProgress
{
	internal readonly HashSet<string> Enemies = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> KilledEnemies = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> ResolvedEnemies = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> Chests = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> InspectedChests = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> ResolvedChests = new HashSet<string>(StringComparer.Ordinal);

	internal readonly HashSet<string> RespawnSources = new HashSet<string>(StringComparer.Ordinal);

	internal string GenerationToken = string.Empty;

	internal bool RespawnCapable;

	internal bool Entered;

	internal bool CensusSettled;

	internal bool Overflowed;

	internal int ManualOverride;

	internal int RoomCount;

	internal long Revision;

	internal int AliveEnemyCount
	{
		get
		{
			int num = 0;
			foreach (string enemy in Enemies)
			{
				if (!KilledEnemies.Contains(enemy) && !ResolvedEnemies.Contains(enemy))
				{
					num++;
				}
			}
			return num;
		}
	}

	internal int DefeatedEnemyCount
	{
		get
		{
			int num = 0;
			foreach (string enemy in Enemies)
			{
				if (KilledEnemies.Contains(enemy) || ResolvedEnemies.Contains(enemy))
				{
					num++;
				}
			}
			return num;
		}
	}

	internal int InspectedOrResolvedChestCount
	{
		get
		{
			int num = 0;
			foreach (string chest in Chests)
			{
				if (InspectedChests.Contains(chest) || ResolvedChests.Contains(chest))
				{
					num++;
				}
			}
			return num;
		}
	}

	internal float DefeatedFraction
	{
		get
		{
			if (Enemies.Count != 0)
			{
				return (float)DefeatedEnemyCount / (float)Enemies.Count;
			}
			return 1f;
		}
	}

	internal float InspectedOrResolvedFraction
	{
		get
		{
			if (Chests.Count != 0)
			{
				return (float)InspectedOrResolvedChestCount / (float)Chests.Count;
			}
			return 1f;
		}
	}
}
internal static class RavenIds
{
	internal unsafe static string ForZdo(PinCategory category, ZDOID id)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		byte b = (byte)category;
		string text = b.ToString();
		ZDOID val = id;
		return text + ":zdo:" + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString();
	}

	internal static string ForPosition(PinCategory category, string prefab, Vector3 position, float cellSize = 1f)
	{
		//IL_000c: 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)
		float num = Mathf.Max(0.25f, cellSize);
		int num2 = Mathf.RoundToInt(position.x / num);
		int num3 = Mathf.RoundToInt(position.z / num);
		string[] array = new string[7];
		byte b = (byte)category;
		array[0] = b.ToString();
		array[1] = ":pos:";
		array[2] = Normalize(prefab);
		array[3] = ":";
		array[4] = num2.ToString();
		array[5] = ":";
		array[6] = num3.ToString();
		return string.Concat(array);
	}

	internal static string Normalize(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		return value.Replace("(Clone)", string.Empty).Replace(" ", string.Empty).Replace("_", string.Empty)
			.Trim()
			.ToLowerInvariant();
	}
}
internal sealed class DiscoveryService
{
	private sealed class Candidate
	{
		internal string SourceId;

		internal GameObject Root;

		internal int RootInstanceId;

		internal ZNetView View;

		internal PrefabDescriptor Descriptor;

		internal PinCategory Category;

		internal Vector3 Position;

		internal long Cell;

		internal bool InSpatialIndex;

		internal bool Eligible = true;

		internal ResourceMath.ResourceProbe ResourceProbe;

		internal bool HasResourceMeasurement;

		internal ResourceMath.Measurement ResourceMeasurement;

		internal float LastSentInitial = -1f;

		internal float LastSentRemaining = -1f;

		internal float LastResourceReportTime = -999f;

		internal Vector3 LastSentPosition = new Vector3(float.PositiveInfinity, 0f, 0f);

		internal string LastSentName = string.Empty;

		internal long FirstObservationSequence;

		internal float FirstObservedAt;
	}

	internal sealed class ResourceDiagnosticSnapshot
	{
		internal string RequestedSourceId;

		internal string LocalSourceId;

		internal string StableSourceId;

		internal string Prefab;

		internal string ResourceType;

		internal ResourceMath.ProbeKind ProbeKind;

		internal bool Loaded;

		internal bool Eligible;

		internal bool Discovered;

		internal bool IsShell;

		internal bool ShellHandoffPending;

		internal bool RootActive;

		internal bool ViewActive;

		internal bool ZdoValid;

		internal bool Measured;

		internal float Initial;

		internal float Remaining;

		internal float DepletedPercent;

		internal int TotalParts;

		internal int RemainingParts;

		internal float LastSentInitial;

		internal float LastSentRemaining;
	}

	private sealed class PendingShellHandoff
	{
		internal string SourceId;

		internal string ResourceType;

		internal string ExpectedReplacementPrefab;

		internal Vector3 Position;

		internal long StartedAfterSequence;

		internal float StartedAt;

		internal float Deadline;

		internal bool ReplacementObserved;

		internal readonly HashSet<string> ExistingReplacementSources = new HashSet<string>(StringComparer.Ordinal);
	}

	private const float SpatialCellSize = 32f;

	private const float ShellHandoffRadius = 3f;

	private const float ShellPreDestroyGraceSeconds = 0.5f;

	private const float ShellPreDestroyPositionRadius = 0.75f;

	private readonly RavenMapPlugin _plugin;

	private readonly ModConfig _config;

	private readonly Action<PinRecord> _submitPin;

	private readonly Func<string, string, string, Vector3, bool> _isKnownResource;

	private readonly Action<IReadOnlyList<FilterCatalogEntry>> _publishFilterCatalog;

	private readonly PrefabClassifier _classifier;

	private readonly DungeonTracker _dungeons;

	private readonly ShimmerService _shimmer;

	private readonly Dictionary<string, Candidate> _loaded = new Dictionary<string, Candidate>(StringComparer.Ordinal);

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

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

	private readonly Dictionary<long, List<Candidate>> _resourceSpatial = new Dictionary<long, List<Candidate>>();

	private readonly Dictionary<long, List<Candidate>> _pickableSpatial = new Dictionary<long, List<Candidate>>();

	private readonly Dictionary<long, List<Candidate>> _dungeonSpatial = new Dictionary<long, List<Candidate>>();

	private readonly Dictionary<string, Candidate> _vehicles = new Dictionary<string, Candidate>(StringComparer.Ordinal);

	private readonly Dictionary<string, Candidate> _portals = new Dictionary<string, Candidate>(StringComparer.Ordinal);

	private readonly HashSet<string> _discovered = new HashSet<string>(StringComparer.Ordinal);

	private readonly HashSet<string> _repairedPersistentSources = new HashSet<string>(StringComparer.Ordinal);

	private readonly Dictionary<string, PendingShellHandoff> _pendingShellHandoffs = new Dictionary<string, PendingShellHandoff>(StringComparer.Ordinal);

	private readonly Dictionary<int, ResourceMath.ResourceProbe> _prefabResourceProbes = new Dictionary<int, ResourceMath.ResourceProbe>();

	private readonly HashSet<Candidate> _resourceVisualRefreshCandidates = new HashSet<Candidate>();

	private ZNetScene _scene;

	private Coroutine _reportRoutine;

	private Coroutine _shellHandoffRoutine;

	private bool _reportFailureLogged;

	private bool _knownResourceLookupFailureLogged;

	private bool _pinSubmissionFailureLogged;

	private bool _contentCatalogReady;

	private bool _destructiveContentCatalogReady;

	private bool _contentAvailabilityFailureLogged;

	private long _candidateSequence;

	internal bool CanPruneMissingContent => _destructiveContentCatalogReady;

	internal DiscoveryService(RavenMapPlugin plugin, ModConfig config, Action<PinRecord> submitPin, Action<string, DungeonEventType, string, int, string> submitDungeonEvent, Func<string, string, string, Vector3, bool> isKnownResource, Action<string> dungeonDiagnosticChanged, ShimmerService shimmer, Action<IReadOnlyList<FilterCatalogEntry>> publishFilterCatalog)
	{
		_plugin = plugin ?? throw new ArgumentNullException("plugin");
		_config = config ?? throw new ArgumentNullException("config");
		_submitPin = submitPin ?? throw new ArgumentNullException("submitPin");
		_isKnownResource = isKnownResource ?? throw new ArgumentNullException("isKnownResource");
		_publishFilterCatalog = publishFilterCatalog;
		_classifier = new PrefabClassifier(config);
		_dungeons = new DungeonTracker(_plugin, config, _classifier, submitDungeonEvent ?? throw new ArgumentNullException("submitDungeonEvent"), dungeonDiagnosticChanged ?? throw new ArgumentNullException("dungeonDiagnosticChanged"));
		_shimmer = shimmer ?? throw new ArgumentNullException("shimmer");
	}

	internal void OnSceneAwake(ZNetScene scene)
	{
		_contentCatalogReady = false;
		_destructiveContentCatalogReady = false;
		_scene = scene;
		_prefabResourceProbes.Clear();
		_classifier.Rebuild(scene);
		_publishFilterCatalog?.Invoke(_classifier.FilterCatalog);
		_shimmer.RefreshConfiguration();
		RestartReporter();
	}

	internal void RebuildCatalog()
	{
		_classifier.RefreshWorldCatalog(_scene ?? ZNetScene.instance);
		_contentCatalogReady = true;
		_destructiveContentCatalogReady = _classifier.HasCompleteContentPresenceCatalog();
		_publishFilterCatalog?.Invoke(_classifier.FilterCatalog);
	}

	internal ContentAvailability GetContentAvailability(PinRecord pin)
	{
		if (!_contentCatalogReady)
		{
			return ContentAvailability.Unknown;
		}
		try
		{
			return _classifier.GetContentAvailability(pin);
		}
		catch (Exception ex)
		{
			if (!_contentAvailabilityFailureLogged)
			{
				_contentAvailabilityFailureLogged = true;
				ManualLogSource log = RavenMapPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("RavenMap could not evaluate one local content registration; the record will remain dormant until the catalog is rebuilt. " + ex.GetType().Name + ": " + ex.Message));
				}
			}
			return ContentAvailability.Unknown;
		}
	}

	internal bool ValidatePersistedContentPresence(PinRecord pin)
	{
		if (!_destructiveContentCatalogReady || GetContentAvailability(pin) != ContentAvailability.Missing)
		{
			return true;
		}
		return !IsStrongMissingContentSource(pin);
	}

	internal static bool IsStrongMissingContentSource(PinRecord pin)
	{
		if (pin == null)
		{
			return false;
		}
		if ((pin.Category == PinCategory.Resource || pin.Category == PinCategory.Pickable) && StableZdoIdentity.IsStableSource(pin.SourceId))
		{
			return true;
		}
		if (pin.Category != PinCategory.Dungeon || !PinRecordLookup.TryParseDungeonSource(pin.SourceId, out var semantic, out var _))
		{
			return false;
		}
		return string.Equals(semantic, RavenIds.Normalize(pin.Prefab), StringComparison.Ordinal);
	}

	internal PinRecord TryRepairPersistedSource(PinRecord persisted)
	{
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		if (persisted == null || persisted.Category == PinCategory.Dungeon || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return null;
		}
		bool num = ServerWorldState.IsLegacyZdoSource(persisted.SourceId);
		bool flag = StableZdoIdentity.IsStableSource(persisted.SourceId);
		if (!num && !flag)
		{
			return null;
		}
		if (flag && _repairedPersistentSources.Contains(persisted.SourceId))
		{
			return null;
		}
		string b = RavenIds.Normalize(persisted.ResourceType);
		if (flag && StableZdoIdentity.TryResolveSource(persisted.SourceId, out var zdo))
		{
			PinRecord pinRecord = persisted.Clone();
			if (!NormalizeSubmittedPin(zdo, pinRecord) || pinRecord.Category != persisted.Category || ((persisted.Category == PinCategory.Resource || persisted.Category == PinCategory.Pickable) && !string.Equals(RavenIds.Normalize(pinRecord.ResourceType), b, StringComparison.Ordinal)))
			{
				return null;
			}
			return FinalizeRepairedPin(persisted, pinRecord, persisted.SourceId);
		}
		float num2 = ((persisted.Category == PinCategory.Resource || persisted.Category == PinCategory.Pickable) ? 0.35f : ((persisted.Category == PinCategory.Portal) ? 0.75f : (flag ? 0.75f : 8f)));
		int num3 = ((persisted.Category == PinCategory.Ship || persisted.Category == PinCategory.Cart) ? 1 : 0);
		List<ZDO> list = new List<ZDO>();
		ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(persisted.Position), num3, num3, list, (List<ZDO>)null);
		ZDO val = null;
		PinRecord pinRecord2 = null;
		float num4 = float.MaxValue;
		int num5 = 0;
		string b2 = RavenIds.Normalize(persisted.Prefab);
		float num6 = num2 * num2;
		for (int i = 0; i < list.Count; i++)
		{
			ZDO val2 = list[i];
			GameObject val3 = ((val2 != null) ? ZNetScene.instance.GetPrefab(val2.GetPrefab()) : null);
			if ((Object)(object)val3 == (Object)null || !string.Equals(RavenIds.Normalize(Utils.GetPrefabName(val3)), b2, StringComparison.Ordinal))
			{
				continue;
			}
			Vector3 val4 = val2.GetPosition() - persisted.Position;
			if (persisted.Category == PinCategory.Ship || persisted.Category == PinCategory.Cart)
			{
				val4.y = 0f;
			}
			float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
			if (sqrMagnitude > num6)
			{
				continue;
			}
			PinRecord pinRecord3 = persisted.Clone();
			if (NormalizeSubmittedPin(val2, pinRecord3) && pinRecord3.Category == persisted.Category && ((persisted.Category != PinCategory.Resource && persisted.Category != PinCategory.Pickable) || string.Equals(RavenIds.Normalize(pinRecord3.ResourceType), b, StringComparison.Ordinal)))
			{
				num5++;
				if (sqrMagnitude + 0.0001f < num4)
				{
					val = val2;
					pinRecord2 = pinRecord3;
					num4 = sqrMagnitude;
				}
			}
		}
		if (val == null || pinRecord2 == null || num5 != 1)
		{
			return null;
		}
		if (!StableZdoIdentity.TryGetExistingSource(val, out var sourceId))
		{
			if (flag)
			{
				if (!StableZdoIdentity.TryAssignSource(val, persisted.SourceId))
				{
					return null;
				}
				sourceId = persisted.SourceId;
			}
			else if (!StableZdoIdentity.TryGetOrCreateSource(val, out sourceId))
			{
				return null;
			}
		}
		return FinalizeRepairedPin(persisted, pinRecord2, sourceId);
	}

	internal bool ValidatePersistedPinMetadata(PinRecord pin)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		if (pin == null)
		{
			return false;
		}
		if (pin.Category != PinCategory.Pickable)
		{
			return true;
		}
		if (IsInteriorPickable(pin.Category, pin.Position))
		{
			return false;
		}
		ZNetScene instance = ZNetScene.instance;
		string text = CleanPrefabName(pin.Prefab);
		if (ResourceCatalog.IsRejectedPersistedPickable(pin.Category, text))
		{
			return false;
		}
		if ((Object)(object)instance == (Object)null || text.Length == 0)
		{
			return true;
		}
		GameObject prefab = instance.GetPrefab(text);
		if ((Object)(object)prefab == (Object)null || !string.Equals(RavenIds.Normalize(Utils.GetPrefabName(prefab)), RavenIds.Normalize(text), StringComparison.Ordinal))
		{
			return true;
		}
		PrefabDescriptor prefabDescriptor = _classifier.Describe(prefab, StringExtensionMethods.GetStableHashCode(text));
		if (prefabDescriptor.Kind != DiscoveryKind.Pickable || !string.Equals(RavenIds.Normalize(prefabDescriptor.ResourceType), RavenIds.Normalize(pin.ResourceType), StringComparison.Ordinal))
		{
			return false;
		}
		pin.Prefab = prefabDescriptor.PrefabName;
		pin.DisplayName = prefabDescriptor.DisplayName;
		pin.ResourceType = prefabDescriptor.ResourceType;
		pin.Respawns = prefabDescriptor.IsRespawningPickable;
		if (!pin.Respawns)
		{
			return pin.Status == PinStatus.Active;
		}
		return true;
	}

	private PinRecord FinalizeRepairedPin(PinRecord persisted, PinRecord normalized, string stableSource)
	{
		MergeRepairedResourceProgress(persisted, normalized);
		normalized.SourceId = stableSource;
		normalized.Id = StableZdoIdentity.PinId(normalized.Category, stableSource);
		normalized.SourceCount = 1;
		_repairedPersistentSources.Add(stableSource);
		return normalized;
	}

	internal static void MergeRepairedResourceProgress(PinRecord persisted, PinRecord normalized)
	{
		if (persisted != null && normalized != null && persisted.Category == PinCategory.Resource && !(persisted.InitialAmount <= 0f))
		{
			bool flag = normalized.InitialAmount > 0f;
			float num = Mathf.Max(0f, normalized.RemainingAmount);
			normalized.InitialAmount = persisted.InitialAmount;
			normalized.RemainingAmount = (flag ? Mathf.Min(persisted.RemainingAmount, num) : persisted.RemainingAmount);
			normalized.Status = persisted.Status;
			normalized.Respawns = persisted.Respawns;
		}
	}

	internal bool NormalizeSubmittedPin(ZDO zdo, PinRecord pin)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0223: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0307: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_0310: Unknown result type (might be due to invalid IL or missing references)
		//IL_0315: Unknown result type (might be due to invalid IL or missing references)
		//IL_031a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0374: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || pin == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return false;
		}
		int prefab = zdo.GetPrefab();
		GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
		if ((Object)(object)prefab2 == (Object)null)
		{
			return false;
		}
		PrefabDescriptor prefabDescriptor = _classifier.Describe(prefab2, prefab);
		PinCategory pinCategory = ToCategory(prefabDescriptor.Kind);
		if (pinCategory == (PinCategory)0 || pinCategory == PinCategory.Dungeon || pinCategory != pin.Category)
		{
			return false;
		}
		Vector3 position = zdo.GetPosition();
		ZNetView val = ((pinCategory == PinCategory.Pickable) ? ZNetScene.instance.FindInstance(zdo) : null);
		if ((pinCategory == PinCategory.Portal && !_config.AutoPinPortals.Value) || (pinCategory == PinCategory.Ship && !_config.TrackShips.Value) || (pinCategory == PinCategory.Cart && !_config.TrackCarts.Value) || (pinCategory == PinCategory.Pickable && !RavenNetwork.TrackPickables) || IsInteriorPickable(pinCategory, position, (val != null) ? ((Component)val).gameObject : null) || ((pinCategory == PinCategory.Resource ||