Decompiled source of RecipeGate v1.0.0

RecipeGate.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("RecipeGate")]
[assembly: AssemblyProduct("RecipeGate")]
[assembly: AssemblyFileVersion("2.3.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("2.3.0.0")]
namespace RecipeGate;

public static class CacheNucleo
{
	private const int Magica = 843269970;

	public const int Formato = 2;

	public const int MaxArquivo = 262144;

	private const int MaxTexto = 128;

	public static byte[] Montar(string versao, string assinatura, int quantas, byte[] bruto, int maxBruto, int maxComprimido)
	{
		if (string.IsNullOrEmpty(versao) || string.IsNullOrEmpty(assinatura) || quantas <= 0 || bruto == null || bruto.Length == 0 || bruto.Length > maxBruto)
		{
			throw new InvalidDataException("dados de cache invalidos");
		}
		byte[] array = RedeNucleo.Comprimir(bruto);
		if (array.Length == 0 || array.Length > maxComprimido)
		{
			throw new InvalidDataException("cache comprimido excede o limite");
		}
		using MemoryStream memoryStream = new MemoryStream();
		using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8);
		binaryWriter.Write(843269970);
		binaryWriter.Write(2);
		EscreverTexto(binaryWriter, versao);
		EscreverTexto(binaryWriter, assinatura);
		binaryWriter.Write(quantas);
		binaryWriter.Write(bruto.Length);
		byte[] array2 = RedeNucleo.Hash(bruto);
		binaryWriter.Write(array2.Length);
		binaryWriter.Write(array2);
		binaryWriter.Write(array.Length);
		binaryWriter.Write(array);
		binaryWriter.Flush();
		if (memoryStream.Length > 262144)
		{
			throw new InvalidDataException("arquivo de cache excede o limite");
		}
		return memoryStream.ToArray();
	}

	public static bool TentarLer(byte[] arquivo, string versaoEsperada, string assinaturaEsperada, int maxBruto, int maxComprimido, out int quantas, out byte[] bruto)
	{
		quantas = 0;
		bruto = null;
		if (arquivo == null || arquivo.Length == 0 || arquivo.Length > 262144 || string.IsNullOrEmpty(versaoEsperada) || string.IsNullOrEmpty(assinaturaEsperada))
		{
			return false;
		}
		try
		{
			using MemoryStream memoryStream = new MemoryStream(arquivo, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
			if (binaryReader.ReadInt32() != 843269970 || binaryReader.ReadInt32() != 2)
			{
				return false;
			}
			string text = LerTexto(binaryReader);
			string text2 = LerTexto(binaryReader);
			int num = binaryReader.ReadInt32();
			int num2 = binaryReader.ReadInt32();
			int num3 = binaryReader.ReadInt32();
			if (text != versaoEsperada || text2 != assinaturaEsperada || num <= 0 || num > 100000 || num2 <= 0 || num2 > maxBruto || num3 != 32)
			{
				return false;
			}
			byte[] a = LerExato(binaryReader, num3);
			int num4 = binaryReader.ReadInt32();
			if (num4 <= 0 || num4 > maxComprimido || num4 > memoryStream.Length - memoryStream.Position)
			{
				return false;
			}
			byte[] comprimido = LerExato(binaryReader, num4);
			if (memoryStream.Position != memoryStream.Length)
			{
				return false;
			}
			byte[] array = RedeNucleo.Descomprimir(comprimido, num2, maxBruto);
			if (!RedeNucleo.Iguais(a, RedeNucleo.Hash(array)))
			{
				return false;
			}
			quantas = num;
			bruto = array;
			return true;
		}
		catch
		{
			quantas = 0;
			bruto = null;
			return false;
		}
	}

	private static void EscreverTexto(BinaryWriter writer, string valor)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(valor);
		if (bytes.Length == 0 || bytes.Length > 128)
		{
			throw new InvalidDataException("cabecalho de cache invalido");
		}
		writer.Write(bytes.Length);
		writer.Write(bytes);
	}

	private static string LerTexto(BinaryReader reader)
	{
		int num = reader.ReadInt32();
		if (num <= 0 || num > 128)
		{
			throw new InvalidDataException("cabecalho de cache invalido");
		}
		return Encoding.UTF8.GetString(LerExato(reader, num));
	}

	private static byte[] LerExato(BinaryReader reader, int tamanho)
	{
		byte[] array = reader.ReadBytes(tamanho);
		if (array.Length != tamanho)
		{
			throw new EndOfStreamException();
		}
		return array;
	}
}
internal static class DumpSpawns
{
	private class Bicho
	{
		public string Prefab;

		public List<string> Biomas = new List<string>();

		public List<string> Chaves = new List<string>();

		public List<string> Drops = new List<string>();

		public int MenorTier = 99;

		public int MaiorTier = -1;
	}

	private static bool _feito;

	private static readonly Biome[] TierBiomes = (Biome[])(object)new Biome[8]
	{
		(Biome)1,
		(Biome)8,
		(Biome)2,
		(Biome)4,
		(Biome)16,
		(Biome)512,
		(Biome)32,
		(Biome)64
	};

	private static readonly string[] Nomes = new string[8] { "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "AshLands", "DeepNorth" };

	internal static void Escrever(SpawnSystem sistema)
	{
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		if (_feito || (Object)(object)sistema == (Object)null || sistema.m_spawnLists == null)
		{
			return;
		}
		_feito = true;
		Dictionary<string, Bicho> dictionary = new Dictionary<string, Bicho>(StringComparer.OrdinalIgnoreCase);
		int num = 0;
		for (int i = 0; i < sistema.m_spawnLists.Count; i++)
		{
			SpawnSystemList val = sistema.m_spawnLists[i];
			if ((Object)(object)val == (Object)null || val.m_spawners == null)
			{
				continue;
			}
			for (int j = 0; j < val.m_spawners.Count; j++)
			{
				SpawnData val2 = val.m_spawners[j];
				if (val2 == null || (Object)(object)val2.m_prefab == (Object)null)
				{
					continue;
				}
				num++;
				string prefabName = Utils.GetPrefabName(val2.m_prefab);
				if (!dictionary.TryGetValue(prefabName, out var value))
				{
					value = new Bicho();
					value.Prefab = prefabName;
					value.Drops = Drops(val2.m_prefab);
					dictionary[prefabName] = value;
				}
				string text = ((!val2.m_enabled) ? " [desativado]" : "") + (val2.m_devDisabled ? " [devDisabled]" : "");
				for (int k = 0; k < TierBiomes.Length; k++)
				{
					if ((val2.m_biome & TierBiomes[k]) == 0)
					{
						continue;
					}
					string item = Nomes[k] + text;
					if (!value.Biomas.Contains(item))
					{
						value.Biomas.Add(item);
					}
					if (val2.m_enabled && !val2.m_devDisabled)
					{
						if (k < value.MenorTier)
						{
							value.MenorTier = k;
						}
						if (k > value.MaiorTier)
						{
							value.MaiorTier = k;
						}
					}
				}
				if (!string.IsNullOrEmpty(val2.m_requiredGlobalKey))
				{
					string requiredGlobalKey = val2.m_requiredGlobalKey;
					if (!value.Chaves.Contains(requiredGlobalKey))
					{
						value.Chaves.Add(requiredGlobalKey);
					}
				}
			}
		}
		Salvar(dictionary, num);
	}

	private static string Ingredientes(ObjectDB db, GameObject produto)
	{
		if (db.m_recipes == null)
		{
			return "-";
		}
		for (int i = 0; i < db.m_recipes.Count; i++)
		{
			Recipe val = db.m_recipes[i];
			if ((Object)(object)val == (Object)null || (Object)(object)val.m_item == (Object)null || (Object)(object)((Component)val.m_item).gameObject != (Object)(object)produto)
			{
				continue;
			}
			if (val.m_resources == null)
			{
				return "-";
			}
			List<string> list = new List<string>();
			for (int j = 0; j < val.m_resources.Length; j++)
			{
				Requirement val2 = val.m_resources[j];
				if (val2 != null && (Object)(object)val2.m_resItem != (Object)null)
				{
					list.Add(Utils.GetPrefabName(((Component)val2.m_resItem).gameObject));
				}
			}
			if (list.Count <= 0)
			{
				return "-";
			}
			return string.Join(" + ", list.ToArray());
		}
		return "-";
	}

	private static void DaTabela(List<string> saida, DropTable t)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		if (t != null && t.m_drops != null)
		{
			for (int i = 0; i < t.m_drops.Count; i++)
			{
				Junta(saida, t.m_drops[i].m_item);
			}
		}
	}

	private static void Junta(List<string> saida, GameObject item)
	{
		if (!((Object)(object)item == (Object)null))
		{
			string prefabName = Utils.GetPrefabName(item);
			if (!string.IsNullOrEmpty(prefabName) && !saida.Contains(prefabName))
			{
				saida.Add(prefabName);
			}
		}
	}

	private static List<string> Drops(GameObject prefab)
	{
		List<string> list = new List<string>();
		CharacterDrop component = prefab.GetComponent<CharacterDrop>();
		if ((Object)(object)component == (Object)null || component.m_drops == null)
		{
			return list;
		}
		for (int i = 0; i < component.m_drops.Count; i++)
		{
			Drop val = component.m_drops[i];
			if (val != null && !((Object)(object)val.m_prefab == (Object)null))
			{
				string prefabName = Utils.GetPrefabName(val.m_prefab);
				if (!list.Contains(prefabName))
				{
					list.Add(prefabName);
				}
			}
		}
		return list;
	}

	private static void Salvar(Dictionary<string, Bicho> bichos, int entradas)
	{
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# RecipeGate - lista de spawn lida do jogo");
			stringBuilder.AppendLine("# gerado em " + DateTime.Now.ToString("yyyy-MM-dd HH:mm"));
			stringBuilder.AppendLine("# fonte: SpawnSystem.m_spawnLists (a mesma que o UpdateSpawning percorre)");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# " + bichos.Count + " criaturas em " + entradas + " entradas de spawn");
			stringBuilder.AppendLine();
			List<Bicho> list = new List<Bicho>(bichos.Values);
			list.Sort((Bicho a, Bicho b) => (a.MenorTier == b.MenorTier) ? string.Compare(a.Prefab, b.Prefab, StringComparison.OrdinalIgnoreCase) : (a.MenorTier - b.MenorTier));
			stringBuilder.AppendLine("## criaturas em mais de um tier (a regra do menor pode enganar aqui)");
			stringBuilder.AppendLine();
			int num = 0;
			for (int num2 = 0; num2 < list.Count; num2++)
			{
				Bicho bicho = list[num2];
				if (bicho.MaiorTier > bicho.MenorTier && bicho.MaiorTier >= 0)
				{
					num++;
					stringBuilder.AppendLine("    " + bicho.Prefab.PadRight(24) + Nomes[bicho.MenorTier] + " .. " + Nomes[bicho.MaiorTier]);
					stringBuilder.AppendLine("        biomas: " + string.Join(", ", bicho.Biomas.ToArray()));
					if (bicho.Chaves.Count > 0)
					{
						stringBuilder.AppendLine("        exige chave: " + string.Join(", ", bicho.Chaves.ToArray()));
					}
					if (bicho.Drops.Count > 0)
					{
						stringBuilder.AppendLine("        larga: " + string.Join(", ", bicho.Drops.ToArray()));
					}
				}
			}
			if (num == 0)
			{
				stringBuilder.AppendLine("    (nenhuma)");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## todas as criaturas");
			stringBuilder.AppendLine();
			for (int num3 = 0; num3 < list.Count; num3++)
			{
				Bicho bicho2 = list[num3];
				string text = ((bicho2.MenorTier < Nomes.Length) ? Nomes[bicho2.MenorTier] : "-");
				stringBuilder.AppendLine("    " + bicho2.Prefab.PadRight(24) + text);
				stringBuilder.AppendLine("        biomas: " + string.Join(", ", bicho2.Biomas.ToArray()));
				if (bicho2.Chaves.Count > 0)
				{
					stringBuilder.AppendLine("        exige chave: " + string.Join(", ", bicho2.Chaves.ToArray()));
				}
				if (bicho2.Drops.Count > 0)
				{
					stringBuilder.AppendLine("        larga: " + string.Join(", ", bicho2.Drops.ToArray()));
				}
			}
			string text2 = RecipeGatePlugin.CaminhoDeRelatorio("spawns.txt");
			File.WriteAllText(text2, stringBuilder.ToString());
			RecipeGatePlugin.Log.LogInfo((object)("dump de spawn escrito em " + text2 + " (" + bichos.Count + " criaturas, " + num + " atravessando tier)."));
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogError((object)("nao consegui escrever o dump: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(SpawnSystem), "Awake")]
internal static class SpawnSystemAwakeDumpPatch
{
	private static void Postfix(SpawnSystem __instance)
	{
		try
		{
			if (RecipeGatePlugin.DumpSpawn.Value)
			{
				DumpSpawns.Escrever(__instance);
			}
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogError((object)("falha no dump de spawn: " + ex));
		}
	}
}
public static class MapaWireNucleo
{
	public const int MaxCaracteres = 4194304;

	public const int MaxEntradas = 100000;

	public static bool TentarLer(string texto, int contagemEsperada, out Dictionary<string, Resultado> mapa)
	{
		mapa = null;
		if (string.IsNullOrEmpty(texto) || texto.Length > 4194304)
		{
			return false;
		}
		Dictionary<string, Resultado> dictionary = new Dictionary<string, Resultado>(StringComparer.OrdinalIgnoreCase);
		string[] array = texto.Split(new char[1] { '\n' });
		if (array.Length > 100001)
		{
			return false;
		}
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].TrimEnd(new char[1] { '\r' });
			if (text.Length != 0)
			{
				string[] array2 = text.Split(new char[1] { '\t' });
				if (array2.Length != 4 || array2[0].Length == 0 || array2[0].Length > 256 || array2[2].Length > 256 || array2[3].Length > 512)
				{
					return false;
				}
				if (!int.TryParse(array2[1], out var result) || result < 0 || result >= MapaNucleo.ChavePorTier.Length || dictionary.ContainsKey(array2[0]))
				{
					return false;
				}
				dictionary[array2[0]] = new Resultado(result, array2[2], array2[3]);
			}
		}
		if (dictionary.Count == 0 || dictionary.Count > 100000 || (contagemEsperada >= 0 && dictionary.Count != contagemEsperada))
		{
			return false;
		}
		mapa = dictionary;
		return true;
	}
}
internal static class Mapa
{
	private const int MaxMapaCache = 4194304;

	private const int MaxComprimidoCache = 245760;

	internal static Dictionary<string, Resultado> Derivado;

	internal static bool Pronto;

	private static readonly Biome[] TierBiomes = (Biome[])(object)new Biome[8]
	{
		(Biome)1,
		(Biome)8,
		(Biome)2,
		(Biome)4,
		(Biome)16,
		(Biome)512,
		(Biome)32,
		(Biome)64
	};

	private static SpawnSystem _sistema;

	private static string _assinaturaAtual;

	private static List<string> _marcosAnteriores;

	private static readonly List<string> Tempos = new List<string>();

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

	private static string[] _ignorar;

	private static readonly List<Fonte> Pisos = new List<Fonte>();

	internal static int Desativados;

	private static Dictionary<string, string> _como;

	private static string _mecanismo;

	private static int TierDoBioma(Biome mask)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < TierBiomes.Length; i++)
		{
			if ((mask & TierBiomes[i]) != 0)
			{
				return i;
			}
		}
		return 0;
	}

	private static string Nome(GameObject go)
	{
		if (!((Object)(object)go == (Object)null))
		{
			return Utils.GetPrefabName(go);
		}
		return null;
	}

	internal static void RefazerSePreciso()
	{
		if (!((Object)(object)_sistema == (Object)null) && (ListaMudou() || !string.Equals(_assinaturaAtual, Assinatura(_sistema), StringComparison.Ordinal)))
		{
			RecipeGatePlugin.Detalhe("os dados ou configs mudaram; invalidando o cache e refazendo o mapa.");
			Construir(_sistema);
		}
	}

	internal static void Esquecer()
	{
		Derivado = null;
		Pronto = false;
		_sistema = null;
		_assinaturaAtual = null;
		_ignorar = null;
	}

	internal static void Construir(SpawnSystem sistema)
	{
		_sistema = sistema;
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		string assinatura = (_assinaturaAtual = Assinatura(sistema));
		if (LerDoCache(assinatura))
		{
			Pronto = Derivado != null && Derivado.Count > 0;
			RecipeGatePlugin.Log.LogInfo((object)("mapa pronto: " + Derivado.Count + " materiais (cache)."));
			RecipeGatePlugin.Detalhe("mapa lido do cache em " + ((Time.realtimeSinceStartup - realtimeSinceStartup) * 1000f).ToString("0") + "ms; nada mudou nos dados do jogo.");
			Rede.PublicarMapaAtualizado();
			return;
		}
		List<Fonte> fontes = new List<Fonte>();
		List<Receita> receitas = new List<Receita>();
		List<Ferramenta> ferramentas = new List<Ferramenta>();
		List<Extensao> extensoes = new List<Extensao>();
		Pisos.Clear();
		Tempos.Clear();
		Desativados = 0;
		_ignorar = null;
		Tenta("vegetacao", delegate
		{
			DoChao(fontes);
		});
		Tenta("criaturas", delegate
		{
			DasCriaturas(fontes, sistema);
		});
		Tenta("localizacoes", delegate
		{
			DosLocais(fontes);
		});
		Tenta("bosses", delegate
		{
			DosBosses(fontes);
		});
		Tenta("salas", delegate
		{
			DasSalas(fontes);
		});
		Tenta("peixes", delegate
		{
			DosPeixes(fontes);
		});
		Tenta("receitas", delegate
		{
			DasReceitas(receitas, ferramentas, extensoes);
		});
		fontes.AddRange(Pisos);
		float realtimeSinceStartup2 = Time.realtimeSinceStartup;
		Derivado = MapaNucleo.Resolver(fontes, receitas, ferramentas, extensoes, 20);
		Tempos.Add("resolver " + ((Time.realtimeSinceStartup - realtimeSinceStartup2) * 1000f).ToString("0") + "ms");
		Pronto = Derivado != null && Derivado.Count > 0;
		RecipeGatePlugin.Log.LogInfo((object)("mapa pronto: " + Derivado.Count + " materiais em " + (Time.realtimeSinceStartup - realtimeSinceStartup).ToString("0.0") + "s."));
		RecipeGatePlugin.Detalhe("mapa derivado de " + fontes.Count + " fontes, " + receitas.Count + " receitas e " + extensoes.Count + " melhorias de estacao (" + Desativados + " colheitas ignoradas por filho desativado).");
		RecipeGatePlugin.Detalhe("tempo por etapa: " + string.Join(", ", Tempos.ToArray()));
		Explicar(fontes, receitas);
		string text = MapaNucleo.Conferir(Derivado, RecipeGatePlugin.TabelaDoWap(), RecipeGatePlugin.ChaveDoMaterial);
		if (!string.IsNullOrEmpty(text))
		{
			RecipeGatePlugin.Detalhe(text);
		}
		GravarCache(assinatura);
		Despejar(receitas, ferramentas);
		if (RecipeGatePlugin.GerarRelatorio.Value)
		{
			Escrever();
		}
		Rede.PublicarMapaAtualizado();
	}

	private static void Despejar(List<Receita> receitas, List<Ferramenta> ferramentas)
	{
		if (!RecipeGatePlugin.DumpSpawn.Value)
		{
			return;
		}
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# RecipeGate - receitas, como a formula as viu");
			stringBuilder.AppendLine("# formato: produto | estacao nivel N | ingrediente + ingrediente");
			stringBuilder.AppendLine();
			List<string> list = new List<string>();
			for (int i = 0; i < receitas.Count; i++)
			{
				Receita receita = receitas[i];
				if (receita != null && !string.IsNullOrEmpty(receita.Produto))
				{
					string text = (string.IsNullOrEmpty(receita.Estacao) ? "-" : receita.Estacao);
					if (receita.NivelEstacao > 1)
					{
						text = text + " nivel " + receita.NivelEstacao;
					}
					list.Add("    " + receita.Produto + "  |  " + text + "  |  " + string.Join(" + ", receita.Ingredientes.ToArray()));
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			for (int j = 0; j < list.Count; j++)
			{
				stringBuilder.AppendLine(list[j]);
			}
			File.WriteAllText(RecipeGatePlugin.CaminhoDeRelatorio("receitas.txt"), stringBuilder.ToString());
			stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# RecipeGate - ferramentas: nivel e que dano fazem");
			stringBuilder.AppendLine();
			list.Clear();
			for (int k = 0; k < ferramentas.Count; k++)
			{
				Ferramenta ferramenta = ferramentas[k];
				if (ferramenta != null)
				{
					list.Add("    " + ferramenta.Prefab.PadRight(26) + " nivel " + ferramenta.Nivel + (ferramenta.Corta ? "  corta" : "") + (ferramenta.Escava ? "  escava" : "") + ((!ferramenta.Corta && !ferramenta.Escava) ? "  (nao serve de ferramenta)" : ""));
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			for (int l = 0; l < list.Count; l++)
			{
				stringBuilder.AppendLine(list[l]);
			}
			File.WriteAllText(RecipeGatePlugin.CaminhoDeRelatorio("ferramentas.txt"), stringBuilder.ToString());
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogWarning((object)("despejo: " + ex.Message));
		}
	}

	private static string CaminhoDoCache()
	{
		return Path.Combine(RecipeGatePlugin.DiretorioDoCache(), "map-v2.cache");
	}

	private static string Assinatura(SpawnSystem sistema)
	{
		//IL_059a: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a4: Expected I4, but got Unknown
		//IL_0699: Unknown result type (might be due to invalid IL or missing references)
		//IL_06a3: Expected I4, but got Unknown
		ulong h = 14695981039346656037uL;
		List<string> list = new List<string>();
		h = Mistura(h, VersaoDoJogo());
		h = Mistura(h, "2.3.0");
		try
		{
			h = Mistura(h, typeof(Mapa).Module.ModuleVersionId.ToString());
		}
		catch
		{
		}
		List<string> list2 = new List<string>();
		foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
		{
			list2.Add(pluginInfo.Key + "@" + ((pluginInfo.Value != null && pluginInfo.Value.Metadata != null) ? pluginInfo.Value.Metadata.Version.ToString() : "?"));
		}
		list2.Sort(StringComparer.OrdinalIgnoreCase);
		for (int i = 0; i < list2.Count; i++)
		{
			h = Mistura(h, list2[i]);
		}
		list.Add("mods:" + h.ToString("x16"));
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance != (Object)null && instance.m_items != null)
		{
			h = Mistura(h, "itens" + instance.m_items.Count);
			for (int j = 0; j < instance.m_items.Count; j++)
			{
				if ((Object)(object)instance.m_items[j] != (Object)null)
				{
					h = Mistura(h, ((Object)instance.m_items[j]).name);
				}
			}
		}
		list.Add("itens:" + h.ToString("x16"));
		ZNetScene instance2 = ZNetScene.instance;
		if ((Object)(object)instance2 != (Object)null && instance2.m_prefabs != null)
		{
			h = Mistura(h, "prefabs" + instance2.m_prefabs.Count);
			for (int k = 0; k < instance2.m_prefabs.Count; k++)
			{
				if ((Object)(object)instance2.m_prefabs[k] != (Object)null)
				{
					h = Mistura(h, ((Object)instance2.m_prefabs[k]).name);
				}
			}
		}
		list.Add("prefabs:" + h.ToString("x16"));
		if ((Object)(object)instance != (Object)null && instance.m_recipes != null)
		{
			h = Mistura(h, "receitas" + instance.m_recipes.Count);
			for (int l = 0; l < instance.m_recipes.Count; l++)
			{
				Recipe val = instance.m_recipes[l];
				if ((Object)(object)val == (Object)null || (Object)(object)val.m_item == (Object)null)
				{
					continue;
				}
				h = Mistura(h, ((Object)val.m_item).name + "|" + val.m_enabled + "|" + val.m_minStationLevel + "|" + val.m_requireOnlyOneIngredient + "|" + (((Object)(object)val.m_craftingStation == (Object)null) ? "" : ((Object)val.m_craftingStation).name));
				if (val.m_resources == null)
				{
					continue;
				}
				for (int m = 0; m < val.m_resources.Length; m++)
				{
					Requirement val2 = val.m_resources[m];
					if (val2 != null && (Object)(object)val2.m_resItem != (Object)null)
					{
						h = Mistura(h, ((Object)val2.m_resItem).name);
					}
				}
			}
		}
		list.Add("receitas:" + h.ToString("x16"));
		if ((Object)(object)instance != (Object)null && instance.m_items != null)
		{
			for (int n = 0; n < instance.m_items.Count; n++)
			{
				GameObject val3 = instance.m_items[n];
				if (!((Object)(object)val3 == (Object)null))
				{
					ItemDrop component = val3.GetComponent<ItemDrop>();
					if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && component.m_itemData.m_shared.m_toolTier > 0)
					{
						h = Mistura(h, "tool:" + ((Object)val3).name + ":" + component.m_itemData.m_shared.m_toolTier);
					}
				}
			}
		}
		list.Add("ferramentas:" + h.ToString("x16"));
		ZoneSystem instance3 = ZoneSystem.instance;
		if ((Object)(object)instance3 != (Object)null && instance3.m_locations != null)
		{
			h = Mistura(h, "locais" + instance3.m_locations.Count);
			for (int num = 0; num < instance3.m_locations.Count; num++)
			{
				ZoneLocation val4 = instance3.m_locations[num];
				if (val4 != null)
				{
					h = Mistura(h, val4.m_prefabName + "|" + val4.m_quantity + "|" + (int)val4.m_biome + "|" + val4.m_enable);
				}
			}
		}
		list.Add("locais:" + h.ToString("x16"));
		if ((Object)(object)sistema != (Object)null && sistema.m_spawnLists != null)
		{
			for (int num2 = 0; num2 < sistema.m_spawnLists.Count; num2++)
			{
				SpawnSystemList val5 = sistema.m_spawnLists[num2];
				if ((Object)(object)val5 == (Object)null || val5.m_spawners == null)
				{
					continue;
				}
				for (int num3 = 0; num3 < val5.m_spawners.Count; num3++)
				{
					SpawnData val6 = val5.m_spawners[num3];
					if (val6 != null && !((Object)(object)val6.m_prefab == (Object)null))
					{
						h = Mistura(h, ((Object)val6.m_prefab).name + "|" + (int)val6.m_biome + "|" + val6.m_enabled + "|" + val6.m_requiredGlobalKey);
					}
				}
			}
		}
		list.Add("spawns:" + h.ToString("x16"));
		string[] array = ListaDeIgnorados();
		for (int num4 = 0; num4 < array.Length; num4++)
		{
			h = Mistura(h, "ign:" + array[num4]);
		}
		list.Add("ignorados:" + h.ToString("x16"));
		h = MisturaConfiguracoes(h);
		list.Add("configs:" + h.ToString("x16"));
		ConferirMarcos(list);
		return h.ToString("x16");
	}

	private static void ConferirMarcos(List<string> marcos)
	{
		List<string> marcosAnteriores = _marcosAnteriores;
		_marcosAnteriores = marcos;
		if (marcosAnteriores == null)
		{
			return;
		}
		List<string> list = new List<string>();
		for (int i = 0; i < marcos.Count && i < marcosAnteriores.Count; i++)
		{
			if (!(marcos[i] == marcosAnteriores[i]))
			{
				string item = marcos[i].Substring(0, marcos[i].IndexOf(':'));
				list.Add(item);
			}
		}
		if (list.Count != 0)
		{
			RecipeGatePlugin.Detalhe("assinatura mudou a partir de '" + list[0] + "' (arrastou: " + string.Join(", ", list.ToArray()) + ").");
		}
	}

	private static ulong MisturaConfiguracoes(ulong h)
	{
		try
		{
			string[] files = Directory.GetFiles(Paths.ConfigPath, "*", SearchOption.AllDirectories);
			Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < files.Length; i++)
			{
				string extension = Path.GetExtension(files[i]);
				if (extension.Equals(".cfg", StringComparison.OrdinalIgnoreCase) || extension.Equals(".yml", StringComparison.OrdinalIgnoreCase) || extension.Equals(".yaml", StringComparison.OrdinalIgnoreCase))
				{
					FileInfo fileInfo = new FileInfo(files[i]);
					string text = files[i].Substring(Paths.ConfigPath.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
					h = Mistura(h, "cfg:" + text.Replace(Path.DirectorySeparatorChar, '/'));
					h = Mistura(h, fileInfo.Length.ToString());
				}
			}
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogWarning((object)("nao consegui conferir a data das configs; o cache pode ficar conservador (" + ex.Message + ")."));
		}
		return h;
	}

	private static string VersaoDoJogo()
	{
		try
		{
			Type type = AccessTools.TypeByName("Version");
			if (type == null)
			{
				return "?";
			}
			MethodInfo methodInfo = AccessTools.Method(type, "GetVersionString", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return "?";
			}
			object obj = methodInfo.Invoke(null, null);
			return (obj == null) ? "?" : obj.ToString();
		}
		catch
		{
			return "?";
		}
	}

	private static ulong Mistura(ulong h, string texto)
	{
		if (texto == null)
		{
			texto = "";
		}
		for (int i = 0; i < texto.Length; i++)
		{
			h ^= texto[i];
			h *= 1099511628211L;
		}
		return h;
	}

	internal static string EmTexto()
	{
		if (Derivado == null)
		{
			return "";
		}
		StringBuilder stringBuilder = new StringBuilder(Derivado.Count * 48);
		foreach (KeyValuePair<string, Resultado> item in Derivado)
		{
			stringBuilder.Append(item.Key).Append('\t').Append(item.Value.Tier)
				.Append('\t')
				.Append(item.Value.Origem ?? "")
				.Append('\t')
				.Append(item.Value.Onde ?? "")
				.Append('\n');
		}
		return stringBuilder.ToString();
	}

	internal static bool DeTexto(string texto)
	{
		return DeTexto(texto, -1);
	}

	internal static bool DeTexto(string texto, int contagemEsperada)
	{
		if (!MapaWireNucleo.TentarLer(texto, contagemEsperada, out var mapa))
		{
			return false;
		}
		Derivado = mapa;
		Pronto = true;
		return true;
	}

	private static bool LerDoCache(string assinatura)
	{
		string text = CaminhoDoCache();
		if (LerArquivoDoCache(text, assinatura))
		{
			return true;
		}
		string caminho = text + ".bak";
		if (LerArquivoDoCache(caminho, assinatura))
		{
			RecipeGatePlugin.Detalhe("cache principal invalido; copia de recuperacao aceita.");
			return true;
		}
		return false;
	}

	private static bool LerArquivoDoCache(string caminho, string assinatura)
	{
		try
		{
			FileInfo fileInfo = new FileInfo(caminho);
			if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 262144)
			{
				return false;
			}
			if (!CacheNucleo.TentarLer(File.ReadAllBytes(caminho), "2.3.0", assinatura, 4194304, 245760, out var quantas, out var bruto))
			{
				return false;
			}
			if (!DeTexto(Encoding.UTF8.GetString(bruto), quantas) || Derivado == null || Derivado.Count != quantas)
			{
				Derivado = null;
				Pronto = false;
				return false;
			}
			_ignorar = ListaDeIgnorados();
			return true;
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Detalhe("cache ignorado: " + ex.Message);
			return false;
		}
	}

	private static void GravarCache(string assinatura)
	{
		string text = null;
		try
		{
			if (Derivado == null || Derivado.Count == 0)
			{
				return;
			}
			string text2 = CaminhoDoCache();
			Directory.CreateDirectory(Path.GetDirectoryName(text2));
			byte[] bytes = Encoding.UTF8.GetBytes(EmTexto());
			byte[] array = CacheNucleo.Montar("2.3.0", assinatura, Derivado.Count, bytes, 4194304, 245760);
			text = text2 + ".tmp-" + Process.GetCurrentProcess().Id + "-" + Guid.NewGuid().ToString("N");
			using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None))
			{
				fileStream.Write(array, 0, array.Length);
				fileStream.Flush(flushToDisk: true);
			}
			if (File.Exists(text2))
			{
				try
				{
					File.Replace(text, text2, text2 + ".bak", ignoreMetadataErrors: true);
					text = null;
					return;
				}
				catch (PlatformNotSupportedException)
				{
					File.Copy(text2, text2 + ".bak", overwrite: true);
					File.Copy(text, text2, overwrite: true);
					return;
				}
				catch (IOException)
				{
					File.Copy(text2, text2 + ".bak", overwrite: true);
					File.Copy(text, text2, overwrite: true);
					return;
				}
			}
			File.Move(text, text2);
			text = null;
		}
		catch (Exception ex3)
		{
			RecipeGatePlugin.Log.LogWarning((object)("cache nao gravado: " + ex3.Message));
		}
		finally
		{
			if (!string.IsNullOrEmpty(text) && File.Exists(text))
			{
				try
				{
					File.Delete(text);
				}
				catch
				{
				}
			}
		}
	}

	private static void Tenta(string oQue, Action acao)
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		try
		{
			acao();
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogWarning((object)(oQue + ": " + ex.Message));
		}
		Tempos.Add(oQue + " " + ((Time.realtimeSinceStartup - realtimeSinceStartup) * 1000f).ToString("0") + "ms");
	}

	private static void DoChao(List<Fonte> fontes)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance == (Object)null || instance.m_vegetation == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_vegetation.Count; i++)
		{
			ZoneVegetation val = instance.m_vegetation[i];
			if (val == null || !val.m_enable || (Object)(object)val.m_prefab == (Object)null)
			{
				continue;
			}
			int num = TierDoBioma(val.m_biome);
			if (num >= 0)
			{
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				Varrer(val.m_prefab, list, list2, null, 0);
				int tipo;
				int ferramenta = NivelDeFerramenta(val.m_prefab, 0, out tipo);
				string onde = Nome(val.m_prefab);
				for (int j = 0; j < list.Count; j++)
				{
					Fonte fonte = new Fonte(list[j], num, ferramenta, "chao", onde, fraca: false);
					fonte.TipoFerramenta = tipo;
					fontes.Add(fonte);
				}
				for (int k = 0; k < list2.Count; k++)
				{
					Fonte fonte2 = new Fonte(list2[k], num, ferramenta, "chao", onde, fraca: true);
					fonte2.TipoFerramenta = tipo;
					fontes.Add(fonte2);
				}
			}
		}
	}

	private static void DasCriaturas(List<Fonte> fontes, SpawnSystem sys)
	{
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)sys == (Object)null || sys.m_spawnLists == null)
		{
			return;
		}
		for (int i = 0; i < sys.m_spawnLists.Count; i++)
		{
			SpawnSystemList val = sys.m_spawnLists[i];
			if ((Object)(object)val == (Object)null || val.m_spawners == null)
			{
				continue;
			}
			for (int j = 0; j < val.m_spawners.Count; j++)
			{
				SpawnData val2 = val.m_spawners[j];
				if (val2 == null || (Object)(object)val2.m_prefab == (Object)null || !val2.m_enabled || val2.m_devDisabled)
				{
					continue;
				}
				int num = TierDoBioma(val2.m_biome);
				if (num >= 0)
				{
					int num2 = MapaNucleo.TierDaChave(val2.m_requiredGlobalKey);
					if (num2 > num)
					{
						num = num2;
					}
					List<string> list = new List<string>();
					List<string> list2 = new List<string>();
					Varrer(val2.m_prefab, list, list2, null, 0);
					string onde = Nome(val2.m_prefab);
					for (int k = 0; k < list.Count; k++)
					{
						fontes.Add(new Fonte(list[k], num, 0, "bicho", onde, fraca: false));
					}
					for (int l = 0; l < list2.Count; l++)
					{
						fontes.Add(new Fonte(list2[l], num, 0, "bicho", onde, fraca: true));
					}
				}
			}
		}
	}

	private static void DosBosses(List<Fonte> fontes)
	{
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || instance.m_prefabs == null)
		{
			return;
		}
		int num = 0;
		for (int i = 0; i < instance.m_prefabs.Count; i++)
		{
			GameObject val = instance.m_prefabs[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Character component = val.GetComponent<Character>();
			if ((Object)(object)component == (Object)null || !component.IsBoss())
			{
				continue;
			}
			string defeatSetGlobalKey = component.m_defeatSetGlobalKey;
			if (string.IsNullOrEmpty(defeatSetGlobalKey))
			{
				continue;
			}
			int num2 = MapaNucleo.TierDaChave(defeatSetGlobalKey);
			if (num2 <= 0)
			{
				continue;
			}
			CharacterDrop component2 = val.GetComponent<CharacterDrop>();
			if ((Object)(object)component2 == (Object)null || component2.m_drops == null)
			{
				continue;
			}
			num++;
			string onde = Nome(val) + " (" + defeatSetGlobalKey + ")";
			for (int j = 0; j < component2.m_drops.Count; j++)
			{
				Drop val2 = component2.m_drops[j];
				if (val2 != null && !((Object)(object)val2.m_prefab == (Object)null))
				{
					fontes.Add(new Fonte(Nome(val2.m_prefab), num2, 0, "boss", onde, fraca: false, piso: true));
				}
			}
		}
		if (num > 0)
		{
			RecipeGatePlugin.Detalhe(num + " bosses lidos pela chave de derrota.");
		}
	}

	private static void DosLocais(List<Fonte> fontes)
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Expected I4, but got Unknown
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance == (Object)null || instance.m_locations == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_locations.Count; i++)
		{
			ZoneLocation val = instance.m_locations[i];
			if (val == null || !val.m_enable || val.m_quantity <= 0 || Ignorada(val.m_prefabName))
			{
				continue;
			}
			int num = TierDoBioma(val.m_biome);
			if (num < 0)
			{
				continue;
			}
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			bool flag = false;
			try
			{
				if (!val.m_prefab.IsLoaded)
				{
					val.m_prefab.Load();
					flag = true;
				}
				GameObject asset = val.m_prefab.Asset;
				if ((Object)(object)asset != (Object)null)
				{
					Varrer(asset, list, list2, null, 0);
					Location component = asset.GetComponent<Location>();
					if ((Object)(object)component != (Object)null && (Object)(object)component.m_interiorPrefab != (Object)null)
					{
						Varrer(component.m_interiorPrefab, list, list2, null, 0);
					}
					DungeonGenerator[] componentsInChildren = asset.GetComponentsInChildren<DungeonGenerator>(true);
					for (int j = 0; j < componentsInChildren.Length; j++)
					{
						int num2 = (int)componentsInChildren[j].m_themes;
						for (int k = 0; k < 32; k++)
						{
							int num3 = 1 << k;
							if ((num2 & num3) != 0 && (!TierDoTema.TryGetValue(num3, out var value) || value > num))
							{
								TierDoTema[num3] = num;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				RecipeGatePlugin.Log.LogDebug((object)(val.m_prefabName + ": " + ex.Message));
			}
			finally
			{
				if (flag)
				{
					try
					{
						val.m_prefab.Release();
					}
					catch
					{
					}
				}
			}
			for (int l = 0; l < list.Count; l++)
			{
				fontes.Add(new Fonte(list[l], num, 0, "local", val.m_prefabName, fraca: false));
			}
			for (int m = 0; m < list2.Count; m++)
			{
				fontes.Add(new Fonte(list2[m], num, 0, "local", val.m_prefabName, fraca: true));
			}
		}
	}

	private static void DasSalas(List<Fonte> fontes)
	{
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Expected I4, but got Unknown
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		DungeonDB instance = DungeonDB.instance;
		if ((Object)(object)instance == (Object)null || instance.m_roomLists == null || TierDoTema.Count == 0)
		{
			return;
		}
		int num = 0;
		for (int i = 0; i < instance.m_roomLists.Count; i++)
		{
			GameObject val = instance.m_roomLists[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			RoomList component = val.GetComponent<RoomList>();
			if ((Object)(object)component == (Object)null || component.m_rooms == null)
			{
				continue;
			}
			for (int j = 0; j < component.m_rooms.Count; j++)
			{
				RoomData val2 = component.m_rooms[j];
				if (val2 == null || !val2.m_enabled || !TierDoTema.TryGetValue((int)val2.m_theme, out var value))
				{
					continue;
				}
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				bool flag = false;
				try
				{
					if (!val2.m_prefab.IsLoaded)
					{
						val2.m_prefab.Load();
						flag = true;
					}
					GameObject asset = val2.m_prefab.Asset;
					if ((Object)(object)asset == (Object)null)
					{
						continue;
					}
					Varrer(asset, list, list2, null, 0);
					num++;
					goto IL_013e;
				}
				catch (Exception ex)
				{
					RecipeGatePlugin.Log.LogDebug((object)("sala: " + ex.Message));
					goto IL_013e;
				}
				finally
				{
					if (flag)
					{
						try
						{
							val2.m_prefab.Release();
						}
						catch
						{
						}
					}
				}
				IL_013e:
				string onde = "sala " + val2.m_theme;
				for (int k = 0; k < list.Count; k++)
				{
					fontes.Add(new Fonte(list[k], value, 0, "sala", onde, fraca: false));
				}
				for (int l = 0; l < list2.Count; l++)
				{
					fontes.Add(new Fonte(list2[l], value, 0, "sala", onde, fraca: true));
				}
			}
		}
		RecipeGatePlugin.Detalhe(num + " salas de masmorra lidas, em " + TierDoTema.Count + " temas.");
	}

	internal static string[] ListaDeIgnorados()
	{
		List<string> list = new List<string>();
		string[] array = (RecipeGatePlugin.IgnorarLocais.Value ?? "").Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		return list.ToArray();
	}

	internal static bool ListaMudou()
	{
		if (!Pronto || _ignorar == null)
		{
			return false;
		}
		string[] array = ListaDeIgnorados();
		if (array.Length != _ignorar.Length)
		{
			return true;
		}
		for (int i = 0; i < array.Length; i++)
		{
			if (!string.Equals(array[i], _ignorar[i], StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static bool Ignorada(string nome)
	{
		if (string.IsNullOrEmpty(nome))
		{
			return false;
		}
		if (_ignorar == null)
		{
			_ignorar = ListaDeIgnorados();
		}
		for (int i = 0; i < _ignorar.Length; i++)
		{
			if (nome.StartsWith(_ignorar[i], StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static void PisoDeChave(string item, int tier, string onde)
	{
		Pisos.Add(new Fonte(item, tier, 0, "loja", onde, fraca: false, piso: true));
	}

	private static bool EstaAtivo(Component c, GameObject raiz)
	{
		if ((Object)(object)c == (Object)null)
		{
			return false;
		}
		Transform val = c.transform;
		while ((Object)(object)val != (Object)null)
		{
			if (!((Component)val).gameObject.activeSelf)
			{
				return false;
			}
			if ((Object)(object)raiz != (Object)null && (Object)(object)((Component)val).gameObject == (Object)(object)raiz)
			{
				break;
			}
			val = val.parent;
		}
		return true;
	}

	private static void Varrer(GameObject raiz, List<string> natural, List<string> bau, HashSet<int> vistos, int nivel)
	{
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)raiz == (Object)null || nivel > 3)
		{
			return;
		}
		if (vistos == null)
		{
			vistos = new HashSet<int>();
		}
		if (!vistos.Add(((Object)raiz).GetInstanceID()))
		{
			return;
		}
		Pickable[] componentsInChildren = raiz.GetComponentsInChildren<Pickable>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if (!EstaAtivo((Component)(object)componentsInChildren[i], raiz))
			{
				Desativados++;
				continue;
			}
			_mecanismo = "Pickable";
			Junta(natural, componentsInChildren[i].m_itemPrefab);
			DaTabela(natural, componentsInChildren[i].m_extraDrops);
		}
		PickableItem[] componentsInChildren2 = raiz.GetComponentsInChildren<PickableItem>(true);
		for (int j = 0; j < componentsInChildren2.Length; j++)
		{
			if (!EstaAtivo((Component)(object)componentsInChildren2[j], raiz))
			{
				Desativados++;
				continue;
			}
			_mecanismo = "PickableItem";
			if ((Object)(object)componentsInChildren2[j].m_itemPrefab != (Object)null)
			{
				Junta(natural, ((Component)componentsInChildren2[j].m_itemPrefab).gameObject);
			}
			if (componentsInChildren2[j].m_randomItemPrefabs == null)
			{
				continue;
			}
			for (int k = 0; k < componentsInChildren2[j].m_randomItemPrefabs.Length; k++)
			{
				RandomItem val = componentsInChildren2[j].m_randomItemPrefabs[k];
				if ((Object)(object)val.m_itemPrefab != (Object)null)
				{
					Junta(natural, ((Component)val.m_itemPrefab).gameObject);
				}
			}
		}
		_mecanismo = "MineRock5 (mineravel)";
		MineRock5[] componentsInChildren3 = raiz.GetComponentsInChildren<MineRock5>(true);
		for (int l = 0; l < componentsInChildren3.Length; l++)
		{
			DaTabela(natural, componentsInChildren3[l].m_dropItems);
		}
		_mecanismo = "MineRock (mineravel)";
		MineRock[] componentsInChildren4 = raiz.GetComponentsInChildren<MineRock>(true);
		for (int m = 0; m < componentsInChildren4.Length; m++)
		{
			DaTabela(natural, componentsInChildren4[m].m_dropItems);
		}
		_mecanismo = "TreeBase (arvore)";
		TreeBase[] componentsInChildren5 = raiz.GetComponentsInChildren<TreeBase>(true);
		for (int n = 0; n < componentsInChildren5.Length; n++)
		{
			DaTabela(natural, componentsInChildren5[n].m_dropWhenDestroyed);
			string mecanismo = _mecanismo;
			_mecanismo = "cadeia: arvore vira tronco " + Nome(componentsInChildren5[n].m_logPrefab);
			Varrer(componentsInChildren5[n].m_logPrefab, natural, bau, vistos, nivel + 1);
			Varrer(componentsInChildren5[n].m_stubPrefab, natural, bau, vistos, nivel + 1);
			_mecanismo = mecanismo;
		}
		_mecanismo = "TreeLog (tronco)";
		TreeLog[] componentsInChildren6 = raiz.GetComponentsInChildren<TreeLog>(true);
		for (int num = 0; num < componentsInChildren6.Length; num++)
		{
			DaTabela(natural, componentsInChildren6[num].m_dropWhenDestroyed);
			string mecanismo2 = _mecanismo;
			_mecanismo = "cadeia: tronco parte em " + Nome(componentsInChildren6[num].m_subLogPrefab);
			Varrer(componentsInChildren6[num].m_subLogPrefab, natural, bau, vistos, nivel + 1);
			_mecanismo = mecanismo2;
		}
		_mecanismo = "DropOnDestroyed";
		DropOnDestroyed[] componentsInChildren7 = raiz.GetComponentsInChildren<DropOnDestroyed>(true);
		for (int num2 = 0; num2 < componentsInChildren7.Length; num2++)
		{
			DaTabela(natural, componentsInChildren7[num2].m_dropWhenDestroyed);
		}
		_mecanismo = "CharacterDrop (larga ao morrer)";
		CharacterDrop[] componentsInChildren8 = raiz.GetComponentsInChildren<CharacterDrop>(true);
		for (int num3 = 0; num3 < componentsInChildren8.Length; num3++)
		{
			if (componentsInChildren8[num3].m_drops == null)
			{
				continue;
			}
			for (int num4 = 0; num4 < componentsInChildren8[num3].m_drops.Count; num4++)
			{
				Drop val2 = componentsInChildren8[num3].m_drops[num4];
				if (val2 != null)
				{
					Junta(natural, val2.m_prefab);
				}
			}
		}
		_mecanismo = "Container (bau)";
		Container[] componentsInChildren9 = raiz.GetComponentsInChildren<Container>(true);
		for (int num5 = 0; num5 < componentsInChildren9.Length; num5++)
		{
			DaTabela(bau, componentsInChildren9[num5].m_defaultItems);
		}
		_mecanismo = "CreatureSpawner";
		CreatureSpawner[] componentsInChildren10 = raiz.GetComponentsInChildren<CreatureSpawner>(true);
		for (int num6 = 0; num6 < componentsInChildren10.Length; num6++)
		{
			Varrer(componentsInChildren10[num6].m_creaturePrefab, natural, bau, vistos, nivel + 1);
		}
		_mecanismo = "SpawnArea (ninho)";
		SpawnArea[] componentsInChildren11 = raiz.GetComponentsInChildren<SpawnArea>(true);
		for (int num7 = 0; num7 < componentsInChildren11.Length; num7++)
		{
			if (componentsInChildren11[num7].m_prefabs == null)
			{
				continue;
			}
			for (int num8 = 0; num8 < componentsInChildren11[num7].m_prefabs.Count; num8++)
			{
				SpawnData val3 = componentsInChildren11[num7].m_prefabs[num8];
				if (val3 != null)
				{
					Varrer(val3.m_prefab, natural, bau, vistos, nivel + 1);
				}
			}
		}
		_mecanismo = "WispSpawner";
		WispSpawner[] componentsInChildren12 = raiz.GetComponentsInChildren<WispSpawner>(true);
		for (int num9 = 0; num9 < componentsInChildren12.Length; num9++)
		{
			Varrer(componentsInChildren12[num9].m_wispPrefab, natural, bau, vistos, nivel + 1);
		}
		_mecanismo = "TriggerSpawner";
		TriggerSpawner[] componentsInChildren13 = raiz.GetComponentsInChildren<TriggerSpawner>(true);
		for (int num10 = 0; num10 < componentsInChildren13.Length; num10++)
		{
			if (componentsInChildren13[num10].m_creaturePrefabs != null)
			{
				for (int num11 = 0; num11 < componentsInChildren13[num10].m_creaturePrefabs.Length; num11++)
				{
					Varrer(componentsInChildren13[num10].m_creaturePrefabs[num11], natural, bau, vistos, nivel + 1);
				}
			}
		}
		_mecanismo = "Trader (loja)";
		Trader[] componentsInChildren14 = raiz.GetComponentsInChildren<Trader>(true);
		for (int num12 = 0; num12 < componentsInChildren14.Length; num12++)
		{
			if (componentsInChildren14[num12].m_items == null)
			{
				continue;
			}
			for (int num13 = 0; num13 < componentsInChildren14[num12].m_items.Count; num13++)
			{
				TradeItem val4 = componentsInChildren14[num12].m_items[num13];
				if (val4 == null || (Object)(object)val4.m_prefab == (Object)null)
				{
					continue;
				}
				string text = Nome(((Component)val4.m_prefab).gameObject);
				if (!string.IsNullOrEmpty(text))
				{
					int num14 = MapaNucleo.TierDaChave(val4.m_requiredGlobalKey);
					if (num14 > 0)
					{
						PisoDeChave(text, num14, "loja: " + Nome(raiz));
					}
					else
					{
						Junta(natural, ((Component)val4.m_prefab).gameObject);
					}
				}
			}
		}
		_mecanismo = "Beehive";
		Beehive[] componentsInChildren15 = raiz.GetComponentsInChildren<Beehive>(true);
		for (int num15 = 0; num15 < componentsInChildren15.Length; num15++)
		{
			if ((Object)(object)componentsInChildren15[num15].m_honeyItem != (Object)null)
			{
				Junta(natural, ((Component)componentsInChildren15[num15].m_honeyItem).gameObject);
			}
		}
		_mecanismo = "SapCollector";
		SapCollector[] componentsInChildren16 = raiz.GetComponentsInChildren<SapCollector>(true);
		for (int num16 = 0; num16 < componentsInChildren16.Length; num16++)
		{
			if ((Object)(object)componentsInChildren16[num16].m_spawnItem != (Object)null)
			{
				Junta(natural, ((Component)componentsInChildren16[num16].m_spawnItem).gameObject);
			}
		}
		Destructible[] componentsInChildren17 = raiz.GetComponentsInChildren<Destructible>(true);
		for (int num17 = 0; num17 < componentsInChildren17.Length; num17++)
		{
			string mecanismo3 = _mecanismo;
			_mecanismo = "cadeia: destruido vira " + Nome(componentsInChildren17[num17].m_spawnWhenDestroyed);
			Varrer(componentsInChildren17[num17].m_spawnWhenDestroyed, natural, bau, vistos, nivel + 1);
			_mecanismo = mecanismo3;
		}
	}

	private static int NivelDeFerramenta(GameObject go, int nivel)
	{
		int tipo;
		return NivelDeFerramenta(go, nivel, out tipo);
	}

	private static int NivelDeFerramenta(GameObject go, int nivel, out int tipo)
	{
		if ((Object)(object)go == (Object)null || nivel > 3)
		{
			tipo = 0;
			return 0;
		}
		int maior = 0;
		int tipoMaior = 0;
		bool flag = false;
		bool flag2 = false;
		Action<int, int> action = delegate(int exigido, int deQueTipo)
		{
			if (exigido > maior)
			{
				maior = exigido;
				tipoMaior = deQueTipo;
			}
		};
		TreeBase[] componentsInChildren = go.GetComponentsInChildren<TreeBase>(true);
		if (componentsInChildren.Length > 0)
		{
			flag = true;
		}
		for (int num = 0; num < componentsInChildren.Length; num++)
		{
			action(componentsInChildren[num].m_minToolTier, 1);
			action(NivelDeFerramenta(componentsInChildren[num].m_logPrefab, nivel + 1, out var tipo2), tipo2);
			action(NivelDeFerramenta(componentsInChildren[num].m_stubPrefab, nivel + 1, out tipo2), tipo2);
		}
		TreeLog[] componentsInChildren2 = go.GetComponentsInChildren<TreeLog>(true);
		if (componentsInChildren2.Length > 0)
		{
			flag = true;
		}
		for (int num2 = 0; num2 < componentsInChildren2.Length; num2++)
		{
			action(componentsInChildren2[num2].m_minToolTier, 1);
			action(NivelDeFerramenta(componentsInChildren2[num2].m_subLogPrefab, nivel + 1, out var tipo3), tipo3);
		}
		MineRock5[] componentsInChildren3 = go.GetComponentsInChildren<MineRock5>(true);
		if (componentsInChildren3.Length > 0)
		{
			flag2 = true;
		}
		for (int num3 = 0; num3 < componentsInChildren3.Length; num3++)
		{
			action(componentsInChildren3[num3].m_minToolTier, 2);
		}
		MineRock[] componentsInChildren4 = go.GetComponentsInChildren<MineRock>(true);
		if (componentsInChildren4.Length > 0)
		{
			flag2 = true;
		}
		for (int num4 = 0; num4 < componentsInChildren4.Length; num4++)
		{
			action(componentsInChildren4[num4].m_minToolTier, 2);
		}
		Destructible[] componentsInChildren5 = go.GetComponentsInChildren<Destructible>(true);
		for (int num5 = 0; num5 < componentsInChildren5.Length; num5++)
		{
			action(componentsInChildren5[num5].m_minToolTier, 2);
			action(NivelDeFerramenta(componentsInChildren5[num5].m_spawnWhenDestroyed, nivel + 1, out var tipo4), tipo4);
		}
		if (maior == 0 && tipoMaior == 0)
		{
			if (flag2)
			{
				tipoMaior = 2;
			}
			else if (flag)
			{
				tipoMaior = 1;
			}
		}
		tipo = tipoMaior;
		return maior;
	}

	private static void DaTabela(List<string> saida, DropTable t)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		if (t != null && t.m_drops != null)
		{
			for (int i = 0; i < t.m_drops.Count; i++)
			{
				Junta(saida, t.m_drops[i].m_item);
			}
		}
	}

	private static void Junta(List<string> saida, GameObject item)
	{
		string text = Nome(item);
		if (!string.IsNullOrEmpty(text))
		{
			if (_como != null && !_como.ContainsKey(text))
			{
				_como[text] = _mecanismo;
			}
			if (!saida.Contains(text))
			{
				saida.Add(text);
			}
		}
	}

	private static void DosPeixes(List<Fonte> fontes)
	{
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || instance.m_prefabs == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_prefabs.Count; i++)
		{
			GameObject val = instance.m_prefabs[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Fish component = val.GetComponent<Fish>();
			if (!((Object)(object)component == (Object)null))
			{
				string onde = "peixe: " + Nome(val);
				if ((Object)(object)val.GetComponent<ItemDrop>() != (Object)null)
				{
					fontes.Add(new Fonte(Nome(val), 0, 0, "mar", onde, fraca: false));
				}
				if ((Object)(object)component.m_pickupItem != (Object)null)
				{
					fontes.Add(new Fonte(Nome(component.m_pickupItem), 0, 0, "mar", onde, fraca: false));
				}
			}
		}
	}

	private static void DasReceitas(List<Receita> receitas, List<Ferramenta> ferramentas, List<Extensao> extensoes)
	{
		DaBancada(receitas);
		DasFerramentas(ferramentas);
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || instance.m_prefabs == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_prefabs.Count; i++)
		{
			GameObject val = instance.m_prefabs[i];
			if (!((Object)(object)val == (Object)null))
			{
				DoQueProduzSozinho(receitas, val);
				DaCriacao(receitas, val);
				DasConversoes(receitas, val);
				DoPlantio(receitas, val);
				DaConstrucao(receitas, extensoes, val);
			}
		}
	}

	private static void DaBancada(List<Receita> receitas)
	{
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance == (Object)null || instance.m_recipes == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_recipes.Count; i++)
		{
			Recipe val = instance.m_recipes[i];
			if ((Object)(object)val == (Object)null || !val.m_enabled || (Object)(object)val.m_item == (Object)null || val.m_resources == null)
			{
				continue;
			}
			Receita receita = new Receita();
			receita.Produto = Nome(((Component)val.m_item).gameObject);
			receita.Estacao = (((Object)(object)val.m_craftingStation == (Object)null) ? null : Nome(((Component)val.m_craftingStation).gameObject));
			receita.NivelEstacao = ((val.m_minStationLevel < 1) ? 1 : val.m_minStationLevel);
			receita.QualquerUm = val.m_requireOnlyOneIngredient;
			for (int j = 0; j < val.m_resources.Length; j++)
			{
				Requirement val2 = val.m_resources[j];
				if (val2 != null && (Object)(object)val2.m_resItem != (Object)null)
				{
					receita.Ingredientes.Add(Nome(((Component)val2.m_resItem).gameObject));
				}
			}
			if (receita.Ingredientes.Count > 0)
			{
				receitas.Add(receita);
			}
		}
	}

	private static void DasFerramentas(List<Ferramenta> ferramentas)
	{
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance == (Object)null || instance.m_items == null)
		{
			return;
		}
		for (int i = 0; i < instance.m_items.Count; i++)
		{
			GameObject val = instance.m_items[i];
			if (!((Object)(object)val == (Object)null))
			{
				ItemDrop component = val.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && component.m_itemData.m_shared.m_toolTier > 0)
				{
					DamageTypes damages = component.m_itemData.m_shared.m_damages;
					ferramentas.Add(new Ferramenta(Nome(val), component.m_itemData.m_shared.m_toolTier, damages.m_chop > 0f, damages.m_pickaxe > 0f));
				}
			}
		}
	}

	private static void DoQueProduzSozinho(List<Receita> receitas, GameObject go)
	{
		string text = Nome(go);
		SapCollector component = go.GetComponent<SapCollector>();
		if ((Object)(object)component != (Object)null && (Object)(object)component.m_spawnItem != (Object)null)
		{
			receitas.Add(new Receita(Nome(((Component)component.m_spawnItem).gameObject), null, text));
		}
		Beehive component2 = go.GetComponent<Beehive>();
		if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_honeyItem != (Object)null)
		{
			receitas.Add(new Receita(Nome(((Component)component2.m_honeyItem).gameObject), null, text));
		}
		WispSpawner component3 = go.GetComponent<WispSpawner>();
		if ((Object)(object)component3 != (Object)null && (Object)(object)component3.m_wispPrefab != (Object)null)
		{
			RendeComoReceita(receitas, component3.m_wispPrefab, text);
		}
		CreatureSpawner component4 = go.GetComponent<CreatureSpawner>();
		if ((Object)(object)component4 != (Object)null && (Object)(object)component4.m_creaturePrefab != (Object)null && (Object)(object)go.GetComponent<Piece>() != (Object)null)
		{
			RendeComoReceita(receitas, component4.m_creaturePrefab, text);
		}
	}

	private static void DaCriacao(List<Receita> receitas, GameObject go)
	{
		EggGrow component = go.GetComponent<EggGrow>();
		if ((Object)(object)component != (Object)null && (Object)(object)component.m_grownPrefab != (Object)null)
		{
			receitas.Add(new Receita(Nome(component.m_grownPrefab), null, Nome(go)));
			DropsComoReceita(receitas, component.m_grownPrefab);
		}
		Growup component2 = go.GetComponent<Growup>();
		if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_grownPrefab != (Object)null)
		{
			receitas.Add(new Receita(Nome(component2.m_grownPrefab), null, Nome(go)));
			DropsComoReceita(receitas, component2.m_grownPrefab);
		}
	}

	private static void DasConversoes(List<Receita> receitas, GameObject go)
	{
		string estacao = Nome(go);
		Smelter component = go.GetComponent<Smelter>();
		if ((Object)(object)component != (Object)null && component.m_conversion != null)
		{
			for (int i = 0; i < component.m_conversion.Count; i++)
			{
				ItemConversion val = component.m_conversion[i];
				if (val != null && !((Object)(object)val.m_from == (Object)null) && !((Object)(object)val.m_to == (Object)null))
				{
					receitas.Add(new Receita(Nome(((Component)val.m_to).gameObject), estacao, Nome(((Component)val.m_from).gameObject)));
				}
			}
		}
		CookingStation component2 = go.GetComponent<CookingStation>();
		if ((Object)(object)component2 != (Object)null && component2.m_conversion != null)
		{
			for (int j = 0; j < component2.m_conversion.Count; j++)
			{
				ItemConversion val2 = component2.m_conversion[j];
				if (val2 != null && !((Object)(object)val2.m_from == (Object)null) && !((Object)(object)val2.m_to == (Object)null))
				{
					receitas.Add(new Receita(Nome(((Component)val2.m_to).gameObject), estacao, Nome(((Component)val2.m_from).gameObject)));
				}
			}
		}
		Fermenter component3 = go.GetComponent<Fermenter>();
		if (!((Object)(object)component3 != (Object)null) || component3.m_conversion == null)
		{
			return;
		}
		for (int k = 0; k < component3.m_conversion.Count; k++)
		{
			ItemConversion val3 = component3.m_conversion[k];
			if (val3 != null && !((Object)(object)val3.m_from == (Object)null) && !((Object)(object)val3.m_to == (Object)null))
			{
				receitas.Add(new Receita(Nome(((Component)val3.m_to).gameObject), estacao, Nome(((Component)val3.m_from).gameObject)));
			}
		}
	}

	private static void DoPlantio(List<Receita> receitas, GameObject go)
	{
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		Plant component = go.GetComponent<Plant>();
		if ((Object)(object)component == (Object)null || component.m_grownPrefabs == null)
		{
			return;
		}
		Piece component2 = go.GetComponent<Piece>();
		if ((Object)(object)component2 == (Object)null || component2.m_resources == null)
		{
			return;
		}
		List<string> list = new List<string>();
		for (int i = 0; i < component2.m_resources.Length; i++)
		{
			Requirement val = component2.m_resources[i];
			if (val != null && (Object)(object)val.m_resItem != (Object)null)
			{
				list.Add(Nome(((Component)val.m_resItem).gameObject));
			}
		}
		if (list.Count == 0)
		{
			return;
		}
		for (int j = 0; j < component.m_grownPrefabs.Length; j++)
		{
			GameObject val2 = component.m_grownPrefabs[j];
			if (!((Object)(object)val2 == (Object)null))
			{
				List<string> list2 = new List<string>();
				List<string> bau = new List<string>();
				Varrer(val2, list2, bau, null, 0);
				for (int k = 0; k < list2.Count; k++)
				{
					Receita receita = new Receita();
					receita.Produto = list2[k];
					receita.Ingredientes = new List<string>(list);
					receita.Piso = TierDoBioma(component.m_biome);
					receitas.Add(receita);
				}
			}
		}
	}

	private static void DaConstrucao(List<Receita> receitas, List<Extensao> extensoes, GameObject go)
	{
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		string text = Nome(go);
		StationExtension component = go.GetComponent<StationExtension>();
		if ((Object)(object)component != (Object)null && (Object)(object)component.m_craftingStation != (Object)null)
		{
			extensoes.Add(new Extensao(Nome(((Component)component.m_craftingStation).gameObject), text));
		}
		Piece component2 = go.GetComponent<Piece>();
		if ((Object)(object)component2 == (Object)null || component2.m_resources == null || component2.m_resources.Length == 0)
		{
			return;
		}
		Receita receita = new Receita();
		receita.Produto = text;
		receita.Estacao = (((Object)(object)component2.m_craftingStation == (Object)null) ? null : Nome(((Component)component2.m_craftingStation).gameObject));
		receita.Piso = TierDoBioma(component2.m_onlyInBiome);
		for (int i = 0; i < component2.m_resources.Length; i++)
		{
			Requirement val = component2.m_resources[i];
			if (val != null && (Object)(object)val.m_resItem != (Object)null)
			{
				receita.Ingredientes.Add(Nome(((Component)val.m_resItem).gameObject));
			}
		}
		if (receita.Ingredientes.Count > 0)
		{
			receitas.Add(receita);
		}
	}

	private static void RendeComoReceita(List<Receita> receitas, GameObject rende, string fonte)
	{
		if ((Object)(object)rende == (Object)null || string.IsNullOrEmpty(fonte))
		{
			return;
		}
		List<string> list = new List<string>();
		List<string> bau = new List<string>();
		Varrer(rende, list, bau, null, 0);
		for (int i = 0; i < list.Count; i++)
		{
			if (!string.Equals(list[i], fonte, StringComparison.OrdinalIgnoreCase))
			{
				receitas.Add(new Receita(list[i], null, fonte));
			}
		}
	}

	private static void DropsComoReceita(List<Receita> receitas, GameObject criatura)
	{
		if ((Object)(object)criatura == (Object)null)
		{
			return;
		}
		CharacterDrop component = criatura.GetComponent<CharacterDrop>();
		if ((Object)(object)component == (Object)null || component.m_drops == null)
		{
			return;
		}
		string text = Nome(criatura);
		if (string.IsNullOrEmpty(text))
		{
			return;
		}
		for (int i = 0; i < component.m_drops.Count; i++)
		{
			Drop val = component.m_drops[i];
			if (val != null && !((Object)(object)val.m_prefab == (Object)null))
			{
				string text2 = Nome(val.m_prefab);
				if (!string.IsNullOrEmpty(text2))
				{
					receitas.Add(new Receita(text2, null, text));
				}
			}
		}
	}

	private static void Explicar(List<Fonte> fontes, List<Receita> receitas)
	{
		string value = RecipeGatePlugin.Explicar.Value;
		if (string.IsNullOrEmpty(value))
		{
			return;
		}
		string[] array = value.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length == 0)
			{
				continue;
			}
			RecipeGatePlugin.Log.LogInfo((object)("--- " + text + " ---"));
			if (Derivado != null && Derivado.TryGetValue(text, out var value2))
			{
				RecipeGatePlugin.Log.LogInfo((object)("    VENCEU  tier " + value2.Tier + "  " + value2.Origem + ": " + value2.Onde + (value2.Fraca ? "   [FRACO]" : "   [forte]")));
			}
			for (int j = 0; j < fontes.Count; j++)
			{
				Fonte fonte = fontes[j];
				if (fonte != null && string.Equals(fonte.Prefab, text, StringComparison.OrdinalIgnoreCase))
				{
					RecipeGatePlugin.Log.LogInfo((object)("    fonte  tier " + fonte.Tier + "  " + fonte.Origem + ": " + fonte.Onde + (fonte.Fraca ? "  (bau)" : "") + (fonte.Piso ? "  (piso)" : "") + ((fonte.Ferramenta > 0) ? ("  ferramenta " + fonte.Ferramenta) : "")));
				}
			}
			QuemProduz(text);
			for (int k = 0; k < receitas.Count; k++)
			{
				Receita receita = receitas[k];
				if (receita != null)
				{
					bool flag = string.Equals(receita.Produto, text, StringComparison.OrdinalIgnoreCase);
					bool flag2 = receita.Ingredientes.Contains(text);
					if (flag || flag2)
					{
						RecipeGatePlugin.Log.LogInfo((object)("    " + (flag ? "sai de" : "entra em") + "  " + receita.Produto + " = " + string.Join(" + ", receita.Ingredientes.ToArray()) + (string.IsNullOrEmpty(receita.Estacao) ? "" : ("  @" + receita.Estacao)) + ((receita.NivelEstacao > 1) ? (" nv" + receita.NivelEstacao) : "") + (receita.QualquerUm ? "  (basta um)" : "") + ((receita.Piso > 0) ? ("  (so em " + MapaNucleo.NomeDoTier(receita.Piso) + ")") : "")));
					}
				}
			}
		}
	}

	private static void QuemProduz(string alvo)
	{
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || instance.m_prefabs == null)
		{
			return;
		}
		int num = 0;
		for (int i = 0; i < instance.m_prefabs.Count; i++)
		{
			GameObject val = instance.m_prefabs[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			_como = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			_mecanismo = "?";
			try
			{
				Varrer(val, list, list2, null, 0);
			}
			catch
			{
				_como = null;
				continue;
			}
			bool flag = list.Contains(alvo);
			bool flag2 = list2.Contains(alvo);
			if (!_como.TryGetValue(alvo, out var value))
			{
				value = "?";
			}
			_como = null;
			if (flag || flag2)
			{
				num++;
				if (num <= 20)
				{
					RecipeGatePlugin.Log.LogInfo((object)("    produz  " + Nome(val) + "  por  " + value + ((flag2 && !flag) ? "  (dentro de bau)" : "")));
				}
			}
		}
		if (num == 0)
		{
			RecipeGatePlugin.Log.LogInfo((object)"    NINGUEM produz isto em prefab nenhum do jogo.");
		}
		else if (num > 20)
		{
			RecipeGatePlugin.Log.LogInfo((object)("    ... e mais " + (num - 20) + " prefabs."));
		}
	}

	internal static void Escrever()
	{
		try
		{
			if (Derivado == null)
			{
				return;
			}
			List<string> list = new List<string>(Derivado.Keys);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# RecipeGate - mapa derivado dos dados do jogo");
			stringBuilder.AppendLine("# " + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "  |  " + Derivado.Count + " materiais");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# tier = MENOR entre as formas de obter, cada uma valendo");
			stringBuilder.AppendLine("#        MAX(bioma, ferramenta ou estacao exigida)");
			stringBuilder.AppendLine("# bau conta so quando nao ha nenhuma outra origem");
			stringBuilder.AppendLine();
			for (int i = 0; i < MapaNucleo.Biomas.Length; i++)
			{
				List<string> list2 = new List<string>();
				for (int j = 0; j < list.Count; j++)
				{
					if (Derivado[list[j]].Tier == i)
					{
						list2.Add(list[j]);
					}
				}
				if (list2.Count != 0)
				{
					string text = MapaNucleo.ChaveDoTier(i);
					stringBuilder.AppendLine("## " + MapaNucleo.NomeDoTier(i) + "  (" + ((text == null) ? "livre" : ("exige " + text)) + ")  " + list2.Count + " materiais");
					stringBuilder.AppendLine();
					for (int k = 0; k < list2.Count; k++)
					{
						Resultado resultado = Derivado[list2[k]];
						stringBuilder.AppendLine("    " + list2[k] + "  |  " + resultado.Origem + ": " + resultado.Onde);
					}
					stringBuilder.AppendLine();
				}
			}
			stringBuilder.AppendLine(MapaNucleo.Conferir(Derivado, RecipeGatePlugin.TabelaDoWap(), RecipeGatePlugin.ChaveDoMaterial));
			File.WriteAllText(RecipeGatePlugin.CaminhoDeRelatorio("mapa.txt"), stringBuilder.ToString());
			RecipeGatePlugin.Detalhe("relatorio do mapa escrito na pasta de relatorios.");
		}
		catch (Exception ex)
		{
			RecipeGatePlugin.Log.LogError((object)("falha ao escrever o relatorio: " + ex.Message));
		}
	}
}
public class Fonte
{
	public string Prefab;

	public int Tier;

	public int Ferramenta;

	public int TipoFerramenta;

	public string Origem;

	public string Onde;

	public bool Fraca;

	public bool Piso;

	public Fonte()
	{
	}

	public Fonte(string prefab, int tier, int ferramenta, string origem, string onde, bool fraca)
	{
		Prefab = prefab;
		Tier = tier;
		Ferramenta = ferramenta;
		Origem = origem;
		Onde = onde;
		Fraca = fraca;
	}

	public Fonte(string prefab, int tier, int ferramenta, string origem, string onde, bool fraca, bool piso)
		: this(prefab, tier, ferramenta, origem, onde, fraca)
	{
		Piso = piso;
	}
}
public class Receita
{
	public string Produto;

	public List<string> Ingredientes = new List<string>();

	public string Estacao;

	public bool QualquerUm;

	public int Piso;

	public int NivelEstacao;

	public Receita()
	{
	}

	public Receita(string produto, string estacao, params string[] ing)
	{
		Produto = produto;
		Estacao = estacao;
		NivelEstacao = 1;
		Ingredientes = new List<string>(ing);
	}
}
public class Extensao
{
	public string Estacao;

	public string Prefab;

	public Extensao()
	{
	}

	public Extensao(string estacao, string prefab)
	{
		Estacao = estacao;
		Prefab = prefab;
	}
}
public static class Tipo
{
	public const int Qualquer = 0;

	public const int Corte = 1;

	public const int Escavacao = 2;
}
public class Ferramenta
{
	public string Prefab;

	public int Nivel;

	public bool Corta;

	public bool Escava;

	public Ferramenta()
	{
	}

	public Ferramenta(string prefab, int nivel)
	{
		Prefab = prefab;
		Nivel = nivel;
		Corta = true;
		Escava = true;
	}

	public Ferramenta(string prefab, int nivel, bool corta, bool escava)
	{
		Prefab = prefab;
		Nivel = nivel;
		Corta = corta;
		Escava = escava;
	}
}
public class Resultado
{
	public int Tier;

	public string Origem;

	public string Onde;

	public bool Fraca;

	public Resultado(int t, string o, string onde)
	{
		Tier = t;
		Origem = o;
		Onde = onde;
	}

	public Resultado(int t, string o, string onde, bool fraca)
		: this(t, o, onde)
	{
		Fraca = fraca;
	}
}
public static class MapaNucleo
{
	public static readonly string[] Biomas = new string[8] { "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "AshLands", "DeepNorth" };

	public static readonly string[] ChavePorTier = new string[8] { null, "defeated_eikthyr", "defeated_gdking", "defeated_bonemass", "defeated_dragon", "defeated_goblinking", "defeated_queen", "defeated_fader" };

	private static Dictionary<string, Resultado> _piso;

	public static string NomeDoTier(int t)
	{
		if (t < 0 || t >= Biomas.Length)
		{
			return "?";
		}
		return Biomas[t];
	}

	public static string ChaveDoTier(int t)
	{
		if (t < 0 || t >= ChavePorTier.Length)
		{
			return null;
		}
		return ChavePorTier[t];
	}

	public static int TierDaChave(string chave)
	{
		if (string.IsNullOrEmpty(chave))
		{
			return 0;
		}
		for (int i = 1; i < ChavePorTier.Length; i++)
		{
			if (string.Equals(ChavePorTier[i], chave, StringComparison.OrdinalIgnoreCase))
			{
				return i;
			}
		}
		return 0;
	}

	public static Dictionary<string, Resultado> Resolver(List<Fonte> fontes, List<Receita> receitas, List<Ferramenta> ferramentas, int maxVoltas)
	{
		return Resolver(fontes, receitas, ferramentas, null, maxVoltas);
	}

	public static Dictionary<string, Resultado> Resolver(List<Fonte> fontes, List<Receita> receitas, List<Ferramenta> ferramentas, List<Extensao> extensoes, int maxVoltas)
	{
		if (extensoes == null)
		{
			extensoes = new List<Extensao>();
		}
		_piso = new Dictionary<string, Resultado>(StringComparer.OrdinalIgnoreCase);
		for (int i = 0; i < fontes.Count; i++)
		{
			Fonte fonte = fontes[i];
			if (fonte != null && fonte.Piso && !string.IsNullOrEmpty(fonte.Prefab) && (!_piso.TryGetValue(fonte.Prefab, out var value) || value.Tier < fonte.Tier))
			{
				_piso[fonte.Prefab] = new Resultado(fonte.Tier, fonte.Origem, fonte.Onde);
			}
		}
		Dictionary<string, Resultado> dictionary = new Dictionary<string, Resultado>(StringComparer.OrdinalIgnoreCase);
		if (fontes == null)
		{
			fontes = new List<Fonte>();
		}
		if (receitas == null)
		{
			receitas = new List<Receita>();
		}
		if (ferramentas == null)
		{
			ferramentas = new List<Ferramenta>();
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		for (int j = 0; j < receitas.Count; j++)
		{
			if (receitas[j] != null && !string.IsNullOrEmpty(receitas[j].Produto))
			{
				hashSet.Add(receitas[j].Produto);
			}
		}
		for (int k = 0; k < maxVoltas; k++)
		{
			Dictionary<string, Resultado> dictionary2 = new Dictionary<string, Resultado>(StringComparer.OrdinalIgnoreCase);
			for (int l = 0; l < fontes.Count; l++)
			{
				Fonte fonte2 = fontes[l];
				if (fonte2 != null && !string.IsNullOrEmpty(fonte2.Prefab) && fonte2.Tier >= 0 && (!fonte2.Fraca || !hashSet.Contains(fonte2.Prefab)))
				{
					int num = CustoFerramenta(fonte2.Ferramenta, fonte2.TipoFerramenta, ferramentas, dictionary);
					if (num >= 0)
					{
						int t = Math.Max(fonte2.Tier, num);
						Melhor(dictionary2, fonte2.Prefab, t, fonte2.Fraca ? "bau" : fonte2.Origem, fonte2.Onde, fonte2.Fraca);
					}
				}
			}
			for (int m = 0; m < 10; m++)
			{
				bool flag = false;
				for (int n = 0; n < receitas.Count; n++)
				{
					Receita receita = receitas[n];
					if (receita == null || string.IsNullOrEmpty(receita.Produto) || SeAlimentaDeSi(receita))
					{
						continue;
					}
					int num2 = 0;
					bool flag2 = true;
					if (receita.QualquerUm)
					{
						num2 = -1;
						for (int num3 = 0; num3 < receita.Ingredientes.Count; num3++)
						{
							int num4 = TierDe(receita.Ingredientes[num3], dictionary2, dictionary);
							if (num4 >= 0 && (num2 < 0 || num4 < num2))
							{
								num2 = num4;
							}
						}
						if (num2 < 0)
						{
							flag2 = false;
						}
					}
					else
					{
						for (int num5 = 0; num5 < receita.Ingredientes.Count; num5++)
						{
							int num6 = TierDe(receita.Ingredientes[num5], dictionary2, dictionary);
							if (num6 < 0)
							{
								flag2 = false;
								break;
							}
							if (num6 > num2)
							{
								num2 = num6;
							}
						}
					}
					if (!flag2)
					{
						continue;
					}
					if (receita.Piso > num2)
					{
						num2 = receita.Piso;
					}
					if (!string.IsNullOrEmpty(receita.Estacao))
					{
						int num7 = TierDe(receita.Estacao, dictionary2, dictionary);
						if (num7 < 0)
						{
							continue;
						}
						if (num7 > num2)
						{
							num2 = num7;
						}
						int num8 = CustoDoNivel(receita.Estacao, receita.NivelEstacao, extensoes, dictionary2, dictionary);
						if (num8 > num2)
						{
							num2 = num8;
						}
					}
					bool flag3 = EhFraca(receita.Estacao, dictionary2, dictionary);
					for (int num9 = 0; num9 < receita.Ingredientes.Count; num9++)
					{
						if (flag3)
						{
							break;
						}
						if (EhFraca(receita.Ingredientes[num9], dictionary2, dictionary))
						{
							flag3 = true;
						}
					}
					if (Melhor(dictionary2, receita.Produto, num2, "receita", Descrever(receita), flag3))
					{
						flag = true;
					}
				}
				if (!flag)
				{
					break;
				}
			}
			for (int num10 = 0; num10 < 10; num10++)
			{
				bool flag4 = false;
				for (int num11 = 0; num11 < receitas.Count; num11++)
				{
					Receita receita2 = receitas[num11];
					if (receita2 == null || string.IsNullOrEmpty(receita2.Produto) || SeAlimentaDeSi(receita2) || dictionary2.ContainsKey(receita2.Produto))
					{
						continue;
					}
					int num12 = 0;
					bool flag5 = false;
					for (int num13 = 0; num13 < receita2.Ingredientes.Count; num13++)
					{
						int num14 = TierDe(receita2.Ingredientes[num13], dictionary2, dictionary);
						if (num14 < 0)
						{
							continue;
						}
						if (receita2.QualquerUm)
						{
							if (!flag5 || num14 < num12)
							{
								num12 = num14;
							}
						}
						else if (num14 > num12)
						{
							num12 = num14;
						}
						flag5 = true;
					}
					if (!string.IsNullOrEmpty(receita2.Estacao))
					{
						int num15 = TierDe(receita2.Estacao, dictionary2, dictionary);
						if (num15 >= 0)
						{
							flag5 = true;
							if (num15 > num12)
							{
								num12 = num15;
							}
							int num16 = CustoDoNivel(receita2.Estacao, receita2.NivelEstacao, extensoes, dictionary2, dictionary);
							if (num16 > num12)
							{
								num12 = num16;
							}
						}
					}
					if (!flag5)
					{
						continue;
					}
					if (receita2.Piso > num12)
					{
						num12 = receita2.Piso;
					}
					bool flag6 = EhFraca(receita2.Estacao, dictionary2, dictionary);
					for (int num17 = 0; num17 < receita2.Ingredientes.Count; num17++)
					{
						if (flag6)
						{
							break;
						}
						if (EhFraca(receita2.Ingredientes[num17], dictionary2, dictionary))
						{
							flag6 = true;
						}
					}
					if (Melhor(dictionary2, receita2.Produto, num12, "receita", Descrever(receita2) + "  [parcial]", flag6))
					{
						flag4 = true;
					}
				}
				if (!flag4)
				{
					break;
				}
			}
			for (int num18 = 0; num18 < fontes.Count; num18++)
			{
				Fonte fonte3 = fontes[num18];
				if (fonte3 != null && fonte3.Fraca && !string.IsNullOrEmpty(fonte3.Prefab) && fonte3.Tier >= 0 && !dictionary2.ContainsKey(fonte3.Prefab))
				{
					int num19 = CustoFerramenta(fonte3.Ferramenta, fonte3.TipoFerramenta, ferramentas, dictionary);
					if (num19 >= 0)
					{
						int t2 = Math.Max(fonte3.Tier, num19);
						Melhor(dictionary2, fonte3.Prefab, t2, "bau", fonte3.Onde, fraca: true);
					}
				}
			}
			for (int num20 = 0; num20 < fontes.Count; num20++)
			{
				Fonte fonte4 = fontes[num20];
				if (fonte4 != null && fonte4.Piso && !string.IsNullOrEmpty(fonte4.Prefab) && (!dictionary2.TryGetValue(fonte4.Prefab, out var value2) || value2.Tier < fonte4.Tier))
				{
					dictionary2[fonte4.Prefab] = new Resultado(fonte4.Tier, "boss", fonte4.Onde);
				}
			}
			if (Igual(dictionary2, dictionary))
			{
				dictionary = dictionary2;
				break;
			}
			dictionary = dictionary2;
		}
		return dictionary;
	}

	private static int CustoDoNivel(string estacao, int nivel, List<Extensao> extensoes, Dictionary<string, Resultado> a, Dictionary<string, Resultado> b)
	{
		if (nivel <= 1 || extensoes.Count == 0)
		{
			return 0;
		}
		List<int> list = new List<int>();
		for (int i = 0; i < extensoes.Count; i++)
		{
			Extensao extensao = extensoes[i];
			if (extensao != null && string.Equals(extensao.Estacao, estacao, StringComparison.OrdinalIgnoreCase))
			{
				int num = TierDe(extensao.Prefab, a, b);
				if (num >= 0)
				{
					list.Add(num);
				}
			}
		}
		if (list.Count == 0)
		{
			return 0;
		}
		list.Sort();
		int num2 = nivel - 1;
		if (num2 > list.Count)
		{
			num2 = list.Count;
		}
		return list[num2 - 1];
	}

	private static bool SeAlimentaDeSi(Receita r)
	{
		for (int i = 0; i < r.Ingredientes.Count; i++)
		{
			if (string.Equals(r.Ingredientes[i], r.Produto, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static string Descrever(Receita r)
	{
		string text = string.Join(" + ", r.Ingredientes.ToArray());
		if (!string.IsNullOrEmpty(r.Estacao))
		{
			text = text + "  @" + r.Estacao;
			if (r.NivelEstacao > 1)
			{
				text = text + " nv" + r.NivelEstacao;
			}
		}
		if (r.Piso > 0)
		{
			text = text + "  so em " + NomeDoTier(r.Piso);
		}
		return text;
	}

	private static bool EhFraca(string nome, Dictionary<string, Resultado> a, Dictionary<string, Resultado> b)
	{
		if (string.IsNullOrEmpty(nome))
		{
			return false;
		}
		if (a != null && a.TryGetValue(nome, out var value))
		{
			return value.Fraca;
		}
		if (b != null && b.TryGetValue(nome, out value))
		{
			return value.Fraca;
		}
		return false;
	}

	private static int TierDe(string nome, Dictionary<string, Resultado> a, Dictionary<string, Resultado> b)
	{
		if (a != null && a.TryGetValue(nome, out var value))
		{
			return value.Tier;
		}
		if (b != null && b.TryGetValue(nome, out value))
		{
			return value.Tier;
		}
		return -1;
	}

	private static bool Melhor(Dictionary<string, Resultado> mapa, string prefab, int t, string origem, string onde)
	{
		return Melhor(mapa, prefab, t, origem, onde, fraca: false);
	}

	private static bool Melhor(Dictionary<string, Resultado> mapa, string prefab, int t, string origem, string onde, bool fraca)
	{
		if (_piso != null && _piso.TryGetValue(prefab, out var value) && value.Tier > t)
		{
			t = value.Tier;
			origem = value.Origem;
			onde = value.Onde;
		}
		if (mapa.TryGetValue(prefab, out var value2) && value2.Tier <= t)
		{
			if (value2.Fraca && !fraca && value2.Tier == t)
			{
				mapa[prefab] = new Resultado(t, origem, onde, fraca: false);
				return true;
			}
			return false;
		}
		mapa[prefab] = new Resultado(t, origem, onde, fraca);
		return true;
	}

	private static int CustoFerramenta(int nivel, int tipo, List<Ferramenta> ferramentas, Dictionary<string, Resultado> tier)
	{
		if (ferramentas == null)
		{
			return 0;
		}
		if (nivel <= 0 && tipo == 0)
		{
			return 0;
		}
		int num = int.MaxValue;
		for (int i = 0; i < ferramentas.Count; i++)
		{
			Ferramenta ferramenta = ferramentas[i];
			if (ferramenta != null && ferramenta.Nivel >= nivel && (tipo != 1 || ferramenta.Corta) && (tipo != 2 || ferramenta.Escava) && tier != null && tier.TryGetValue(ferramenta.Prefab, out var value) && !value.Fraca && value.Tier < num)
			{
				num = value.Tier;
			}
		}
		if (num != int.MaxValue)
		{
			return num;
		}
		return -1;
	}

	private static bool Igual(Dictionary<string, Resultado> a, Dictionary<string, Resultado> b)
	{
		if (a.Count != b.Count)
		{
			return false;
		}
		foreach (KeyValuePair<string, Resultado> item in a)
		{
			if (!b.TryGetValue(item.Key, out var value))
			{
				return false;
			}
			if (value.Tier != item.Value.Tier)
			{
				return false;
			}
		}
		return true;
	}

	public static string Conferir(Dictionary<string, Resultado> derivado, Dictionary<string, string> wap)
	{
		return Conferir(derivado, wap, null);
	}

	public static string Conferir(Dictionary<string, Resultado> derivado, Dictionary<string, string> wap, Func<string, string> efetiva)
	{
		StringBuilder stringBuilder = new StringBuilder();
		if (derivado == null || wap == null)
		{
			return "";
		}
		List<string> list = new List<string>();
		List<string> list2 = new List<string>();
		List<string> list3 = new List<string>();
		foreach (KeyValuePair<string, string> item in wap)
		{
			if (!derivado.TryGetValue(item.Key, out var value))
			{
				list3.Add(item.Key);
				continue;
			}
			string text = ChaveDoTier(value.Tier);
			bool flag = false;
			if (efetiva != null)
			{
				string text2 = efetiva(item.Key);
				if (text2 != text)
				{
					text = text2;
					flag = true;
				}
			}
			if (text == item.Value)
			{
				list.Add(item.Key);
				continue;
			}
			list2.Add(item.Key + ": derivado=" + (text ?? "livre") + " wap=" + item.Value + " (" + (flag ? "na mao" : (value.Origem + ": " + value.Onde)) + ")");
		}
		stringBuilder.AppendLine("conferencia com o WAP: " + list.Count + " iguais, " + list2.Count + " diferentes, " + list3.Count + " sem origem derivada");
		for (int i = 0; i < list2.Count; i++)
		{
			stringBuilder.AppendLine("    " + list2[i]);
		}
		if (list3.Count > 0)
		{
			stringBuilder.AppendLine("    sem origem: " + string.Join(", ", list3.ToArray()));
		}
		return stringBuilder.ToString();
	}
}
public static class Nucleo
{
	public const char Separador = ',';

	public static List<string> LerPendentes(string bruto)
	{
		List<string> list = new List<string>();
		if (string.IsNullOrEmpty(bruto))
		{
			return list;
		}
		string[] array = bruto.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0 && !list.Contains(text))
			{
				list.Add(text);
			}
		}
		return list;
	}

	public static string GravarPendentes(List<string> lista)
	{
		if (lista == null || lista.Count == 0)
		{
			return null;
		}
		return string.Join(','.ToString(), lista.ToArray());
	}

	public static bool Adicionar(List<string> lista, string prefab)
	{
		if (lista == null || string.IsNullOrEmpty(prefab))
		{
			return false;
		}
		if (lista.Contains(prefab))
		{
			return false;
		}
		lista.Add(prefab);
		return true;
	}

	public static bool DeveSegurar(string prefab, Func<string, string> chaveDe, Func<string, bool> temChave, out string chave)
	{
		chave = null;
		if (string.IsNullOrEmpty(prefab) || chaveDe == null || temChave == null)
		{
			return false;
		}
		chave = chaveDe(prefab);
		if (string.IsNullOrEmpty(chave))
		{
			return false;
		}
		return !temChave(chave);
	}

	public static void Separar(List<string> pendentes, Func<string, string> chaveDe, Func<string, bool> temChave, out List<string> liberar, out List<string> restantes)
	{
		liberar = new List<string>();
		restantes = new List<string>();
		if (pendentes == null)
		{
			return;
		}
		for (int i = 0; i < pendentes.Count; i++)
		{
			string text = pendentes[i];
			if (DeveSegurar(text, chaveDe, temChave, out var _))
			{
				restantes.Add(text);
			}
			else
			{
				liberar.Add(text);
			}
		}
	}

	public static List<string> EmChavesGlobais(string bruto, bool par)
	{
		List<string> list = new List<string>();
		List<string> list2 = LerPendentes(bruto);
		for (int i = 0; i < list2.Count; i++)
		{
			string text = list2[i].Trim();
			if (text.Length == 0)
			{
				continue;
			}
			if (par)
			{
				int num = text.IndexOf('=');
				if (num <= 0 || num == text.Length - 1)
				{
					continue;
				}
				string text2 = text.Substring(0, num).Trim();
				string text3 = text.Substring(num + 1).Trim();
				if (text2.Length == 0 || text3.Length == 0)
				{
					continue;
				}
				text = text2 + "-" + text3;
			}
			text = text.ToLowerInvariant();
			if (text.IndexOf(' ') < 0 && !list.Contains(text))
			{
				list.Add(text);
			}
		}
		return list;
	}

	public static string EmParDeIgual(string chave)
	{
		if (string.IsNullOrEmpty(chave))
		{
			return chave;
		}
		int num = chave.LastIndexOf('-');
		if (num <= 0 || num == chave.Length - 1)
		{
			return chave;
		}
		return chave.Substring(0, num) + "=" + chave.Substring(num + 1);
	}

	public static bool ReceitaPossivel(List<string> ingredientes, bool bastaUm, Func<string, bool> conhece)
	{
		if (ingredientes == null || ingredientes.Count == 0 || conhece == null)
		{
			return false;
		}
		bool result = false;
		for (int i = 0; i < ingredientes.Count; i++)
		{
			string text = ingredientes[i];
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			bool flag = conhece(text);
			if (bastaUm)
			{
				if (flag)
				{
					result = true;
				}
			}
			else if (!flag)
			{
				return false;
			}
		}
		if (!bastaUm)
		{
			return true;
		}
		return result;
	}
}
[BepInPlugin("raaotium.recipegate", "RecipeGate", "2.3.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public class RecipeGatePlugin : BaseUnityPlugin
{
	public const string GUID = "raaotium.recipegate";

	public const string NAME = "RecipeGate";

	public const string VERSION = "2.3.0";

	internal const string WapGuid = "com.orianaventure.mod.WorldAdvancementProgression";

	internal const string JotunnGuid = "com.jotunn.jotunn";

	private const string WapKeyManager = "VentureValheim.Progression.KeyManager";

	internal const string PendingKey = "recipegate_pending";

	internal const string EnforceKey = "recipegate_on";

	internal const string OvrPrefix = "recipegate_ovr-";

	internal const string LocPrefix = "recipegate_loc-";

	internal static RecipeGatePlugin Instancia;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> Verbose;

	internal static ConfigEntry<string> Overrides;

	internal static ConfigEntry<bool> DumpSpawn;

	internal static ConfigEntry<string> Explicar;

	internal static ConfigEntry<bool> GerarRelatorio;

	internal static ConfigEntry<string> IgnorarLocais;

	private static bool _wapChecked;

	private static object _keyManager;

	private static FieldInfo _bossItems;

	private static FieldInfo _materials;

	private static FieldInfo _foods;

	private Harmony _harmony;

	private FileSystemWatcher _watcher;

	private bool _travaPendente;

	private float _proximaTentativa;

	private float _proximaReafirmacao;

	private static volatile bool _recarregar;

	private static string _manuaisBruto;

	private static Dictionary<string, string> _manuais;

	private static int _frameDasChaves = -1;

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

	private static bool _estavaLigado = true;

	private static bool _mapaEstavaPronto;

	private static SpawnSystem _spawnGuardado;

	private static float _limiteDeEspera;

	private static bool _socorroFeito;

	private static StringBuilder _rastro;

	internal static string DiretorioDoCache()
	{
		return Path.Combine(Paths.BepInExRootPath, "cache", "raaotium.recipegate");
	}

	internal static string CaminhoDeRelatorio(string nome)
	{
		string text = Path.Combine(Paths.ConfigPath, "raaotium.recipegate", "reports");
		Directory.CreateDirectory(text);
		return Path.Combine(text, nome);
	}

	private static void PrepararArquivosGerados()
	{
		try
		{
			string path = DiretorioDoCache();
			Directory.CreateDirectory(path);
			string path2 = Path.Combine(Paths.ConfigPath, "recipegate_cache.txt");
			if (File.Exists(path2))
			{
				File.Delete(path2);
			}
			string[] files = Directory.GetFiles(path, "map-v2.cache.tmp-*");
			for (int i = 0; i < files.Length; i++)
			{
				File.Delete(files[i]);
			}
			string[] array = new string[5] { "spawns", "receitas", "ferramentas", "mapa", "simulacao" };
			for (int j = 0; j < array.Length; j++)
			{
				string text = Path.Combine(Paths.ConfigPath, "recipegate_" + array[j] + ".txt");
				if (File.Exists(text))
				{
					string text2 = CaminhoDeRelatorio(array[j] + ".txt");
					if (File.Exists(text2))
					{
						File.Delete(text);
					}
					else
					{
						File.Move(text, text2);
					}
				}
			}
		}
		catch (Exception ex)
		{
			Log.LogDebug((object)("organizacao dos arquivos gerados: " + ex.Message));
		}
	}

	internal static List<string> ChavesDoServidor(string prefixo)
	{
		List<string> list = new List<string>();
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return list;
		}
		List<string> globalKeys;
		try
		{
			globalKeys = instance.GetGlobalKeys();
		}
		catch
		{
			return list;
		}
		if (globalKeys == null)
		{
			return list;
		}
		for (int i = 0; i < globalKeys.Count; i++)
		{
			string text = globalKeys[i];
			if (!string.IsNullOrEmpty(text) && text.StartsWith(prefixo, StringComparison.OrdinalIgnoreCase))
			{
				string text2 = text.Substring(prefixo.Length).Trim();
				if (text2.Length > 0)
				{
					list.Add(text2);
				}
			}
		}
		list.Sort(StringComparer.OrdinalIgnoreCase);
		return list;
	}

	internal static bool Forcado()
	{
		if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
		{
			return Rede.Recebido;
		}
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance != (Object)null)
		{
			return instance.GetGlobalKey("recipegate_on");
		}
		return false;
	}

	private void Awake()
	{
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Expected O, but got Unknown
		Instancia = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "Ativo", true, "Desligue para as receitas voltarem ao normal do jogo. Em multiplayer esta opcao e autoritativa no servidor e chega aos clientes pelo canal seguro do RecipeGate.");
		Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "Log detalhado", false, "Registra no log cada decisao da trava e cada recalculo do mapa.");
		Overrides = ((BaseUnityPlugin)this).Config.Bind<string>("Mapa", "Materiais na mao", "DvergrKeyFragment=defeated_goblinking", "Prefab=chave, separado por virgula. Ganha de tudo. Serve para material que nenhuma tabela conhece. Em multiplayer vale exclusivamente a configuracao autenticada do servidor.");
		IgnorarLocais = ((BaseUnityPlugin)this).Config.Bind<string>("Mapa", "Ignorar localizacoes", "Dev", "Prefixos de nome de localizacao a ignorar ao montar o mapa, separados por virgula. Em multiplayer somente o servidor monta o mapa.");
		GerarRelatorio = ((BaseUnityPlugin)this).Config.Bind<bool>("Mapa", "Gerar relatorio", false, "Escreve BepInEx/config/raaotium.recipegate/reports/mapa.txt com o mapa derivado inteiro e a conferencia contra a tabela do WAP.");
		DumpSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostico", "Gerar dump de spawn", false, "Escreve arquivos de leitura em BepInEx/config/raaotium.recipegate/reports. Nao muda nada. Ligue, entre uma vez, desligue.");
		Explicar = ((BaseUnityPlugin)this).Config.Bind<string>("Diagnostico", "Explicar materiais", "", "Lista separada por virgula. Para cada material citado, o log mostra TODAS as fontes e receitas que falam dele, nao so a origem vencedora. Serve pra entender por que um item caiu no tier em que caiu. Ex: ChickenEgg,Chicken");
		PrepararArquivosGerados();
		try
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath));
			_watcher.Changed += delegate
			{
				_recarregar = true;
			};
			_watcher.IncludeSubdirectories = false;
			_watcher.NotifyFilter = NotifyFilters.LastWrite;
			_watcher.EnableRaisingEvents = true;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("sem recarga automatica do cfg: " + ex.Message));
		}
		Rede.Registrar();
		_harmony = new Harmony("raaotium.recipegate");
		_harmony.PatchAll(Assembly.GetExecutingAssembly());
		((BaseUnityPlugin)this).Logger.LogInfo((object)"RecipeGate 2.3.0 carregado — sync direto e falha fechada.");
	}

	private void OnDestroy()
	{
		if (_watcher != null)
		{
			_watcher.Dispose();
			_watcher = null;
		}
		Rede.Esquecer();
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}

	private void Update()
	{
		Rede.Atualizar();
		if (_travaPendente && Time.realtimeSinceStartup >= _proximaTentativa)
		{
			_proximaTentativa = Time.realtimeSinceStartup + 5f;
			if (PublicarTrava())
			{
				_travaPendente = false;
			}
		}
		if (Mapa.Pronto != _mapaEstavaPronto)
		{
			_mapaEstavaPronto = Mapa.Pronto;
			if (Ligado() && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				Trava.AtualizarReceitas(Player.m_localPlayer);
			}
		}
		if (_recarregar)
		{
			_recarregar = false;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Mapa.RefazerSePreciso();
				Detalhe("cfg relido em tempo real.");
				_proximaReafirmacao = 0f;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("falha ao reler o cfg: " + ex.Message));
			}
		}
		SocorroSeOMapaNaoVeio();
		bool flag = Ligado();
		if (flag != _estavaLigado)
		{
			_estavaLigado = flag;
			Log.LogWarning((object)("RecipeGate " + (flag ? "LIGADO" : "DESLIGADO") + " em tempo real."));
		}
		if (IsServer() && Time.realtimeSinceStartup >= _proximaReafirmacao)
		{
			_proximaReafirmacao = Time.realtimeSinceStartup + 30f;
			PublicarTrava();
		}
	}

	internal void PedirTrava()
	{
		_travaPendente = true;
		_proximaTentativa = 0f;
	}

	private static bool WapPronto()
	{
		if (_wapChecked)
		{
			return _keyManager != null;
		}
		_wapChecked = true;
		try
		{
			Type type = AccessTools.TypeByName("VentureValheim.Progression.KeyManager");
			if (type == null)
			{
				Detalhe("World Advancement Progression nao encontrado; sem conferencia, mas o mapa derivado basta.");
				return false;
			}
			PropertyInfo propertyInfo = AccessTools.Property(type, "Instance");
			_keyManager = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null);
			_bossItems = AccessTools.Field(type, "BossItemKeysList");
			_materials = AccessTools.Field(type, "MaterialKeysList");
			_foods = AccessTools.Field(type, "FoodKeysList");
			if (_keyManager == null || _materials == null)
			{
				Log.LogWarning((object)"o WAP esta aqui mas com formato diferente do esperado; RecipeGate fica sem a conferencia, mas o mapa derivado basta.");
				_keyManager = null;
				return false;
			}
			Detalhe("WAP encontrado; as tabelas dele entram so como conferencia do mapa derivado.");
			return true;
		}
		catch (Exception ex)
		{
			Log.LogError((object)("falha ao encaixar no WAP (" + ex.Message + "); RecipeGate inerte."));
			_keyManager = null;
			return false;
		}
	}

	private static Dictionary<string, string> Tabela(FieldInfo campo)
	{
		if (campo == null || _keyManager == null)
		{
			return null;
		}
		return campo.GetValue(_keyManager) as Dictionary<string, string>;
	}

	private static Dictionary<string, string> Manuais()
	{
		string text = (((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) ? Rede.OverridesServidor : Overrides.Value);
		text = text ?? "";
		if (_manuais != null && text == _manuaisBruto)
		{
			return _manuais;
		}
		_manuaisBruto = text;
		_manuais = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		List<string> list = Nucleo.LerPendentes(text);
		for (int i = 0; i < list.Count; i++)
		{
			string text2 = list[i];
			int num = text2.IndexOf('=');
			if (num <= 0)
			{
				Log.LogWarning((object)("ignorando '" + text2 + "': use 'Prefab=chave'"));
				continue;
			}
			string text3 = text2.Substring(0, num).Trim();
			string text4 = text2.Substring(num + 1).Trim();
			if (text3.Length > 0 && text4.Length > 0)
			{
				_manuais[text3] = text4;
			}
		}
		if (_manuais.Count > 0)
		{
			Detalhe(_manuais.Count + " material(is) mapeados na mao.");
		}
		return _manuais;
	}

	internal static Dictionary<string, string> TabelaDoWap()
	{
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		if (!WapPronto())
		{
			return dictionary;
		}
		FieldInfo[] array = new FieldInfo[3] { _bossItems, _materials, _foods };
		for (int i = 0; i < array.Length; i++)
		{
			Dictionary<string, string> dictionary2 = Tabela(array[i]);
			if (dictionary2 == null)
			{
				continue;
			}
			foreach (KeyValuePair<string, string> item in dictionary2)
			{
				dictionary[item.Key] = item.Value;
			}
		}
		return dictionary;
	}

	internal static string ChaveDoMaterial(string prefab)
	{
		if (string.IsNullOrEmpty(prefab))
		{
			return null;
		}
		if (Manuais().TryGetValue(prefab, out var value))
		{
			return value;
		}
		if (!Mapa.Pronto || Mapa.Derivado == null)
		{
			return null;
		}
		if (!Mapa.Derivado.TryGetValue(prefab, out var value2))
		{
			return null;
		}
		return MapaNucleo.ChaveDoTier(value2.Tier);
	}

	internal static bool TemChave(string chave)
	{
		if (string.IsNullOrEmpty(chave))
		{
			return true;
		}
		int frameCount = Time.frameCount;
		if (frameCount != _frameDasChaves)
		{
			_chavesDoFrame.Clear();
			_frameDasChaves = frameCount;
		}
		if (_chavesDoFrame.TryGetValue(chave, out var value))
		{
			return value;
		}
		try
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			bool globalKey = instance.GetGlobalKey(chave);
			_chavesDoFrame[chave] = globalKey;
			return globalKey;
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("nao consegui ler a chave '" + chave + "' (" + ex.Message + "); segurando por precaucao."));
			return false;
		}
	}

	internal static bool Ligado()
	{
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return Enabled.Value;
		}
		if (instance.IsServer())
		{
			return Enabled.Value;
		}
		if (!Rede.Recebido)
		{
			return true;
		}
		return Rede.ServidorLigado;
	}

	internal static void MigrarEspera(Player player)
	{
		if ((Object)(object)player == (Object)null || player.m_customData == null || !player.m_customData.TryGetValue("recipegate_pending", out var value))
		{
			return;
		}
		List<string> list = Nucleo.LerPendentes(value);
		player.m_customData.Remove("recipegate_pending");
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance == (Object)null || list.Count == 0)
		{
			return;
		}
		int num = 0;
		for (int i = 0; i < list.Count; i++)
		{
			try
			{
				GameObject itemPrefab = instance.GetItemPrefab(list[i]);
				ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
				if (!((Object)(object)val == (Object)null))
				{
					player.AddKnownItem(val.m_itemData);
					num++;
				}
			}
			catch
			{
			}
		}
		Log.LogWarning((object)("migracao da geracao 1: " + num + " material(is) da lista de espera voltaram a ser conhecidos. Quem tranca agora e a receita."));
	}

	internal static void GuardarSpawnSystem(SpawnSystem s)
	{
		_spawnGuardado = s;
		_limiteDeEspera = Time.realtimeSinceStartup + 30f;
	}

	internal static bool EsperandoPacote()
	{
		return (Object)(object)_spawnGuardado != (Object)null;
	}

	internal static void EsquecerMundo()
	{
		Mapa.Esquecer();
		Red