Decompiled source of DeepTip EpicLoot v1.2.0

DeepTip_EpicLoot.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DeepTip;
using EpicLoot;
using EpicLoot.Abilities;
using EpicLoot.Adventure;
using EpicLoot.Biomes;
using EpicLoot.Crafting;
using EpicLoot.CraftingV2;
using EpicLoot.GatedItemType;
using EpicLoot.LegendarySystem;
using EpicLoot.Magic.MagicItemEffects.Helpers;
using EpicLoot.ShardStones;
using EpicLoot_UnityLib;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+cee680c46d08ef37c1b926a7ddeecc20cf774f6c")]
[assembly: AssemblyProduct("DeepTip EpicLoot")]
[assembly: AssemblyTitle("DeepTip_EpicLoot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DeepTipEpicLoot
{
	internal static class AdventureGlossary
	{
		private sealed class Entry
		{
			public readonly string Key;

			public readonly string Title;

			public readonly string Short;

			public readonly string Body;

			public readonly string[] See;

			public Entry(string key, string title, string shortText, string body, string[] see)
			{
				Key = key;
				Title = title;
				Short = shortText;
				Body = body;
				See = see;
			}
		}

		private sealed class Lang
		{
			public string Code = "";

			public CultureInfo Culture = CultureInfo.InvariantCulture;

			public string Percent = "{0}%";

			public string[] RarityNames = new string[6];

			[Own]
			public Dictionary<string, string> Biomes = new Dictionary<string, string>();

			public string IronTokens = "";

			public string GoldTokens = "";

			public string ForestTokens = "";

			public string Coins = "";

			public string NoReward = "";

			public string Minions = "";

			public string IronBounty = "";

			public string GoldBounty = "";

			public string BountyIndexTitle = "";

			public string BountyIndexShort = "";

			public string BountyIndexIntro = "";

			public string BountyIndexLine = "";

			public string MapIndexTitle = "";

			public string MapIndexShort = "";

			public string MapIndexIntro = "";

			public string BountyTitle = "";

			public string BountyShort = "";

			public string BountyIntro = "";

			public string IronStrength = "";

			public string GoldStrength = "";

			public string AddsStrength = "";

			public string StrengthNote = "";

			public string NoStar = "";

			public string OneStar = "";

			public string StarsFormat = "";

			public string OnlyDiscovered = "";

			public string GatedCurrent = "";

			public string GatedNext = "";

			public string MapTitle = "";

			public string MapShort = "";

			public string MapIntro = "";

			public string MapScaled = "";

			public string MapRolls = "";

			public string PerRoll = "";

			public string MapExtra = "";

			public string MapNote = "";

			public string Nothing = "";

			public string Or = "";

			public string UniqueShard = "";

			public string Chisel = "";

			public string TierGear = "";

			public string PricesTitle = "";

			public string PricesShort = "";

			public string PricesIntro = "";

			public string PricesRange = "";

			public string PricesMissing = "";

			public string TokenPrices = "";

			public string TierTitle = "";

			public string TierShort = "";

			public string TierIntro = "";

			public string TierLabel = "";

			public string NoPrice = "";

			public string EveryDay = "";

			public string EveryDays = "";

			public string On = "";

			public string Off = "";

			public string NonMagic = "";

			internal static Lang For(string locale)
			{
				return Texts.Load<Lang>("adventure", locale);
			}
		}

		private const string Owner = "DeepTip_EpicLoot.Abenteuer";

		private const string ValuePrefix = "el_adv_";

		private const int MissingGamblePrice = 10000;

		private static readonly string[] NamePrefixes = new string[3] { "enemy_", "item_", "biome_" };

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

		internal static void Hook(Harmony harmony)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			try
			{
				AdventureDataManager.OnSetupAdventureData += Rebuild;
				LootRoller.OnSetupLootTables += Rebuild;
				BiomeDataManager.OnBiomeDataInitialized += Rebuild;
				HarmonyMethod val = new HarmonyMethod(typeof(AdventureGlossary), "Refresh", (Type[])null);
				MethodInfo[] array = new MethodInfo[2]
				{
					AccessTools.Method(typeof(Game), "Start", (Type[])null, (Type[])null),
					AccessTools.Method(typeof(GatedItemTypeHelper), "Initialize", (Type[])null, (Type[])null)
				};
				foreach (MethodInfo methodInfo in array)
				{
					if (methodInfo != null)
					{
						harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
				RegisterValues();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EpicLoot-Abenteuer: Haken nicht gesetzt: {arg}");
			}
			if (AdventureDataManager.Config != null)
			{
				Rebuild();
			}
		}

		private static void Refresh()
		{
			Rebuild();
		}

		internal static void Rebuild()
		{
			try
			{
				AdventureDataConfig config = AdventureDataManager.Config;
				if (config == null)
				{
					return;
				}
				List<string> locales = Texts.Locales;
				LoadNames(locales.Select(Texts.Language));
				List<(Lang, List<Entry>)> list = (from l in locales
					select Lang.For(l) into l
					select (l: l, Build(l, config))).ToList();
				Api.Unregister("DeepTip_EpicLoot.Abenteuer");
				foreach (var (lang, entries) in list)
				{
					Api.Register("DeepTip_EpicLoot.Abenteuer", ToJson(lang, entries), lang.Code);
				}
				List<Entry> item = list[0].Item2;
				int num = item.Count((Entry e) => e.Key.StartsWith("epicloot.adventure.bounty.biome.", StringComparison.Ordinal));
				int num2 = item.Count((Entry e) => e.Key.StartsWith("epicloot.adventure.treasuremap.biome.", StringComparison.Ordinal));
				int num3 = item.Count((Entry e) => e.Key.StartsWith("epicloot.adventure.gamble.prices", StringComparison.Ordinal));
				Plugin.Log.LogInfo((object)$"EpicLoot-Abenteuer erklärt: {item.Count} Einträge ({num} Biome mit Kopfgeldern, {num2} mit Schatzkarten, {num3} zu Glücksspiel-Preisen)");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EpicLoot-Abenteuer nicht erklärt: {arg}");
			}
		}

		private static List<Entry> Build(Lang lang, AdventureDataConfig config)
		{
			List<Entry> list = new List<Entry>();
			Section(list, "Kopfgelder", () => Bounties(lang, config));
			Section(list, "Schatzkarten", () => TreasureMaps(lang, config));
			Section(list, "Glücksspiel-Preise", () => GamblePrices(lang, config));
			return list;
		}

		private static void Section(List<Entry> entries, string name, Func<IEnumerable<Entry>> build)
		{
			try
			{
				entries.AddRange(build().ToList());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("EpicLoot-Abenteuer, Teil " + name + " übersprungen: " + ex.Message));
			}
		}

		private static IEnumerable<Entry> Bounties(Lang lang, AdventureDataConfig config)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			BountiesConfig bounties = config.Bounties;
			if (bounties?.Targets == null)
			{
				return Enumerable.Empty<Entry>();
			}
			List<(Biome, BountyTargetConfig)> source = (from t in bounties.Targets
				where t != null && !string.IsNullOrEmpty(t.TargetID)
				select (t.GetBiome(), t: t) into x
				where (int)x.Item1 > 0
				select x).ToList();
			List<Entry> list = new List<Entry>();
			StringBuilder stringBuilder = new StringBuilder(lang.BountyIndexIntro);
			foreach (Biome biome in InOrder(source.Select<(Biome, BountyTargetConfig), Biome>(((Biome Biome, BountyTargetConfig Target) x) => x.Biome)))
			{
				List<BountyTargetConfig> list2 = (from x in source
					where x.Biome == biome
					select x.Target).ToList();
				Entry entry = BountyEntry(lang, bounties, biome, list2);
				list.Add(entry);
				string arg = Range(list2.Select((BountyTargetConfig t) => t.RewardCoins), lang) ?? "0";
				stringBuilder.Append("\n- [[").Append(entry.Key).Append('|')
					.Append(BiomeTitle(biome, lang))
					.Append("]]: ")
					.Append(string.Format(lang.BountyIndexLine, list2.Count, list2.Count((BountyTargetConfig t) => t.RewardGold > 0), arg));
			}
			if (list.Count > 0)
			{
				list.Insert(0, new Entry("epicloot.adventure.bounty.biomes", lang.BountyIndexTitle, lang.BountyIndexShort, stringBuilder.ToString(), new string[2] { "epicloot.adventure.bounty.offers", "epicloot.adventure.bounty" }));
			}
			return list;
		}

		private static Entry BountyEntry(Lang lang, BountiesConfig b, Biome biome, List<BountyTargetConfig> targets)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			string text = BiomeTitle(biome, lang);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format(lang.BountyIntro, BiomeLink(biome, text), Pct(100f / (float)targets.Count, lang)));
			foreach (BountyTargetConfig target in targets)
			{
				bool flag = target.RewardGold > 0;
				stringBuilder.Append("\n- **").Append(MonsterName(target.TargetID, lang)).Append("** (")
					.Append(flag ? lang.GoldBounty : lang.IronBounty)
					.Append("): ");
				stringBuilder.Append(Reward(target.RewardIron, target.RewardGold, target.RewardCoins, lang));
				List<string> list = (from a in target.Adds ?? new List<BountyTargetAddConfig>()
					where a != null && a.Count > 0 && !string.IsNullOrEmpty(a.ID)
					select $"{a.Count}× {MonsterName(a.ID, lang)}").ToList();
				if (list.Count > 0)
				{
					stringBuilder.Append("; ").Append(lang.Minions).Append(' ')
						.Append(string.Join(", ", list));
				}
			}
			List<string> list2 = new List<string>();
			if (targets.Any((BountyTargetConfig t) => t.RewardGold <= 0))
			{
				list2.Add(string.Format(lang.IronStrength, Stars(b.IronMinLevel, b.IronMaxLevel, lang), Num(b.IronHealthMultiplier, lang)));
			}
			if (targets.Any((BountyTargetConfig t) => t.RewardGold > 0))
			{
				list2.Add(string.Format(lang.GoldStrength, Stars(b.GoldMinLevel, b.GoldMaxLevel, lang), Num(b.GoldHealthMultiplier, lang)));
			}
			if (targets.Any((BountyTargetConfig t) => t.Adds?.Any((BountyTargetAddConfig a) => a != null && a.Count > 0) ?? false))
			{
				list2.Add(string.Format(lang.AddsStrength, Stars(b.AddsMinLevel, b.AddsMaxLevel, lang), Num(b.AddsHealthMultiplier, lang)));
			}
			stringBuilder.Append("\n\n").Append(string.Join(". ", list2)).Append(". ")
				.Append(lang.StrengthNote);
			stringBuilder.Append("\n\n").Append(Gating(lang));
			return new Entry("epicloot.adventure.bounty.biome." + Slug(biome), string.Format(lang.BountyTitle, text), Cut(string.Format(lang.BountyShort, targets.Count, text)), stringBuilder.ToString(), new string[3] { "epicloot.adventure.bounty.offers", "epicloot.adventure.bounty.rewards", "epicloot.adventure.bounty.target" });
		}

		private static string Reward(int iron, int gold, int coins, Lang lang)
		{
			List<string> list = new List<string>();
			if (iron > 0)
			{
				list.Add($"{iron} {lang.IronTokens}");
			}
			if (gold > 0)
			{
				list.Add($"{gold} {lang.GoldTokens}");
			}
			if (coins > 0)
			{
				list.Add($"{coins} {lang.Coins}");
			}
			if (list.Count <= 0)
			{
				return lang.NoReward;
			}
			return string.Join(", ", list);
		}

		private static string Stars(int minLevel, int maxLevel, Lang lang)
		{
			int num = Math.Max(0, minLevel - 1);
			int num2 = Math.Max(num, maxLevel - 1);
			if (num2 == 0)
			{
				return lang.NoStar;
			}
			string arg = ((num == num2) ? num.ToString(lang.Culture) : $"{num}–{num2}");
			return string.Format((num2 == 1 && num == 1) ? lang.OneStar : lang.StarsFormat, arg);
		}

		private static string Gating(Lang lang)
		{
			object? obj = ElConfig("BossBountyMode");
			string onlyDiscovered = lang.OnlyDiscovered;
			string text = obj?.ToString();
			if (!(text == "BossKillUnlocksCurrentBiomeBounties"))
			{
				if (text == "BossKillUnlocksNextBiomeBounties")
				{
					return onlyDiscovered + " " + lang.GatedNext;
				}
				return onlyDiscovered;
			}
			return onlyDiscovered + " " + lang.GatedCurrent;
		}

		private static IEnumerable<Entry> TreasureMaps(Lang lang, AdventureDataConfig config)
		{
			List<Entry> list = MapEntries(lang, config).ToList();
			if (list.Count == 0)
			{
				return list;
			}
			StringBuilder stringBuilder = new StringBuilder(lang.MapIndexIntro);
			foreach (Entry item in list)
			{
				stringBuilder.Append("\n- [[").Append(item.Key).Append('|')
					.Append(item.Title)
					.Append("]]: ")
					.Append(item.Short);
			}
			list.Insert(0, new Entry("epicloot.adventure.treasuremap.biomes", lang.MapIndexTitle, lang.MapIndexShort, stringBuilder.ToString(), new string[2] { "epicloot.adventure.treasuremap", "epicloot.adventure.location" }));
			return list;
		}

		private static IEnumerable<Entry> MapEntries(Lang lang, AdventureDataConfig config)
		{
			TreasureMapConfig maps = config.TreasureMap;
			if (maps?.BiomeInfo == null)
			{
				yield break;
			}
			float scale = (maps.ScaleRadiiToWorldSize ? RadiusScale() : 1f);
			float area = Mathf.Max(0f, maps.MinimapAreaRadius) * 2.1f * 0.5f;
			HashSet<Biome> done = new HashSet<Biome>();
			foreach (TreasureMapBiomeInfoConfig item in maps.BiomeInfo)
			{
				if (item == null)
				{
					continue;
				}
				Biome biome = item.GetBiome();
				if ((int)biome != 0 && done.Add(biome) && item.Cost > 0)
				{
					List<LootTable> lootTable = LootRoller.GetLootTable("TreasureMapChest_" + BiomeDataManager.GetName(biome));
					if (lootTable != null && lootTable.Count != 0)
					{
						yield return TreasureEntry(lang, maps, item, biome, lootTable[0], scale, area);
					}
				}
			}
		}

		private static Entry TreasureEntry(Lang lang, TreasureMapConfig maps, TreasureMapBiomeInfoConfig info, Biome biome, LootTable table, float scale, float area)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			string text = BiomeTitle(biome, lang);
			string text2 = Num(Mathf.Min(info.MinRadius, info.MaxRadius) * scale, lang, "0");
			string text3 = Num(Mathf.Max(info.MinRadius, info.MaxRadius) * scale, lang, "0");
			string text4 = Every(maps.RefreshInterval, lang) ?? "";
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format(lang.MapIntro, BiomeLink(biome, text), info.Cost, text4, text2, text3, Num(area, lang, "0")));
			if (!Mathf.Approximately(scale, 1f))
			{
				stringBuilder.Append(' ').Append(string.Format(lang.MapScaled, Num(scale, lang)));
			}
			LeveledLootDef? obj = (from l in table.LeveledLoot?.Where((LeveledLootDef l) => l != null)
				orderby l.Level
				select l).FirstOrDefault();
			float[][] drops = obj?.Drops ?? table.Drops;
			object obj2 = obj?.Loot ?? table.Loot;
			string arg = DropCounts(drops, lang);
			if (obj2 == null)
			{
				obj2 = Array.Empty<LootDrop>();
			}
			List<LootDrop> list = ((IEnumerable<LootDrop>)obj2).Where((LootDrop d) => d != null && !string.IsNullOrEmpty(d.Item) && d.Weight > 0f).ToList();
			if (list.Count > 0)
			{
				float num = list.Sum((LootDrop d) => d.Weight);
				stringBuilder.Append("\n\n").Append(string.Format(lang.MapRolls, arg));
				foreach (LootDrop item in list)
				{
					stringBuilder.Append("\n- ").Append(LootName(item.Item, lang)).Append(": ")
						.Append(string.Format(lang.PerRoll, Pct(item.Weight / num * 100f, lang)));
					string text5 = Rarities(item.Rarity, lang);
					if (text5.Length > 0)
					{
						stringBuilder.Append("; ").Append(text5);
					}
				}
			}
			List<string> list2 = new List<string>();
			if (info.ForestTokens > 0)
			{
				list2.Add($"{info.ForestTokens} {lang.ForestTokens}");
			}
			if (info.IronTokens > 0)
			{
				list2.Add($"{info.IronTokens} {lang.IronTokens}");
			}
			if (info.GoldTokens > 0)
			{
				list2.Add($"{info.GoldTokens} {lang.GoldTokens}");
			}
			if (info.Coins > 0)
			{
				list2.Add($"{info.Coins} {lang.Coins}");
			}
			stringBuilder.Append("\n\n");
			if (list2.Count > 0)
			{
				stringBuilder.Append(string.Format(lang.MapExtra, string.Join(", ", list2))).Append(' ');
			}
			stringBuilder.Append(lang.MapNote);
			return new Entry("epicloot.adventure.treasuremap.biome." + Slug(biome), string.Format(lang.MapTitle, text), Cut(string.Format(lang.MapShort, info.Cost, text4, text2, text3)), stringBuilder.ToString(), new string[3] { "epicloot.adventure.treasuremap", "epicloot.adventure.treasurechest", "epicloot.adventure.location" });
		}

		private static string DropCounts(float[][]? drops, Lang lang)
		{
			List<(int, float)> list = (from d in drops ?? Array.Empty<float[]>()
				where d != null && d.Length >= 2 && d[1] > 0f
				select ((int)d[0], d[1])).ToList();
			float total = list.Sum<(int, float)>(((int Count, float Weight) p) => p.Weight);
			if (list.Count == 0 || total <= 0f)
			{
				return "?";
			}
			List<string> list2 = list.Select<(int, float), string>(((int Count, float Weight) p) => ((p.Count == 0) ? lang.Nothing : p.Count.ToString(lang.Culture)) + " (" + Pct(p.Weight / total * 100f, lang) + ")").ToList();
			if (list2.Count != 1)
			{
				return string.Join(", ", list2.Take(list2.Count - 1)) + " " + lang.Or + " " + list2[list2.Count - 1];
			}
			return list2[0];
		}

		private static string Rarities(float[]? rarity, Lang lang)
		{
			if (rarity == null || rarity.Length == 0)
			{
				return "";
			}
			float num = rarity.Where((float r) => r > 0f).Sum();
			if (num <= 0f)
			{
				return "";
			}
			List<string> list = new List<string>();
			for (int num2 = 0; num2 < rarity.Length && num2 < lang.RarityNames.Length; num2++)
			{
				if (rarity[num2] > 0f)
				{
					list.Add(lang.RarityNames[num2] + " " + Pct(rarity[num2] / num * 100f, lang));
				}
			}
			return string.Join(", ", list);
		}

		private static string LootName(string item, Lang lang)
		{
			if (item == "ShardUnique")
			{
				return lang.UniqueShard;
			}
			if (item == "ShardSlotChisel")
			{
				return lang.Chisel;
			}
			if (item.StartsWith("Tier", StringComparison.Ordinal) && item.EndsWith("Everything", StringComparison.Ordinal))
			{
				string arg = item.Substring(4, item.Length - 4 - "Everything".Length);
				return string.Format(lang.TierGear, arg) + " (`" + item + "`)";
			}
			return ItemName(item, lang);
		}

		private static float RadiusScale()
		{
			try
			{
				return ((typeof(AdventureDataManager).Assembly.GetType("EpicLoot.Adventure.WorldExtent")?.GetProperty("RadiusScale", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) is float num && num > 0f) ? num : 1f;
			}
			catch
			{
				return 1f;
			}
		}

		private static IEnumerable<Entry> GamblePrices(Lang lang, AdventureDataConfig config)
		{
			GambleConfig gamble = config.Gamble;
			if (gamble?.Gambles == null || GatedItemTypeHelper.ItemsByTypeAndBoss == null || GatedItemTypeHelper.ItemsByTypeAndBoss.Count == 0)
			{
				yield break;
			}
			Dictionary<string, HashSet<string>> dictionary = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);
			foreach (string item3 in gamble.Gambles.Where((string t) => !string.IsNullOrEmpty(t)).Distinct())
			{
				if (!GatedItemTypeHelper.ItemsByTypeAndBoss.TryGetValue(item3, out var value) || value == null)
				{
					continue;
				}
				foreach (KeyValuePair<string, List<string>> item4 in value)
				{
					if (item4.Value != null && !string.Equals(item4.Key, "none", StringComparison.OrdinalIgnoreCase))
					{
						if (!dictionary.TryGetValue(item4.Key, out var value2))
						{
							value2 = (dictionary[item4.Key] = new HashSet<string>(StringComparer.Ordinal));
						}
						value2.UnionWith(item4.Value.Where((string i) => !string.IsNullOrEmpty(i)));
					}
				}
			}
			if (dictionary.Count == 0)
			{
				yield break;
			}
			Dictionary<string, int> prices = new Dictionary<string, int>(StringComparer.Ordinal);
			foreach (SecretStashItemConfig item5 in gamble.GambleCosts ?? new List<SecretStashItemConfig>())
			{
				if (item5 != null && !string.IsNullOrEmpty(item5.Item) && !prices.ContainsKey(item5.Item))
				{
					prices[item5.Item] = item5.CoinsCost;
				}
			}
			List<string> list = BossOrder(dictionary.Keys);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(lang.PricesIntro);
			List<Entry> tiersOut = new List<Entry>();
			foreach (string item6 in list)
			{
				List<string> list2 = dictionary[item6].Where((string i) => i.Length > 0).ToList();
				if (list2.Count == 0)
				{
					continue;
				}
				(string Slug, string Label) tuple = BossTier(item6, lang);
				string item = tuple.Slug;
				string item2 = tuple.Label;
				string text = "epicloot.adventure.gamble.prices." + item;
				int value4;
				List<(string, int, bool)> list3 = (from i in list2
					select (ItemName(i, lang), prices.TryGetValue(i, out value4) ? value4 : 10000, !prices.ContainsKey(i)) into r
					orderby r.Item2
					select r).ThenBy<(string, int, bool), string>(((string, int, bool) r) => r.Item1, StringComparer.CurrentCulture).ToList();
				int num = list3.Min<(string, int, bool)>(((string Name, int Price, bool Missing) r) => r.Price);
				int num2 = list3.Max<(string, int, bool)>(((string Name, int Price, bool Missing) r) => r.Price);
				string text2 = ((num == num2) ? num.ToString(lang.Culture) : $"{num}–{num2}");
				stringBuilder.Append("\n- [[").Append(text).Append('|')
					.Append(item2)
					.Append("]]: ")
					.Append(string.Format(lang.PricesRange, text2, list3.Count));
				StringBuilder stringBuilder2 = new StringBuilder();
				stringBuilder2.Append(string.Format(lang.TierIntro, item2));
				foreach (var (value3, num3, flag) in list3)
				{
					stringBuilder2.Append("\n- ").Append(value3).Append(": ")
						.Append(num3.ToString(lang.Culture))
						.Append(' ')
						.Append(lang.Coins);
					if (flag)
					{
						stringBuilder2.Append(' ').Append(lang.NoPrice);
					}
				}
				stringBuilder2.Append("\n\n").Append(TokenPrices(gamble, lang));
				tiersOut.Add(new Entry(text, string.Format(lang.TierTitle, item2), Cut(string.Format(lang.TierShort, item2, text2)), stringBuilder2.ToString(), new string[2] { "epicloot.adventure.gamble.prices", "epicloot.adventure.gamble.offers" }));
			}
			stringBuilder.Append("\n\n").Append(TokenPrices(gamble, lang)).Append(' ')
				.Append(string.Format(lang.PricesMissing, 10000.ToString("N0", lang.Culture)));
			yield return new Entry("epicloot.adventure.gamble.prices", lang.PricesTitle, lang.PricesShort, stringBuilder.ToString(), new string[3] { "epicloot.adventure.gamble.offers", "epicloot.adventure.gamble.chance", "epicloot.loot.gating" });
			foreach (Entry item7 in tiersOut)
			{
				yield return item7;
			}
		}

		private static string TokenPrices(GambleConfig g, Lang lang)
		{
			string text = ((g.ForestTokenGambleCostMin == g.ForestTokenGambleCostMax) ? g.ForestTokenGambleCostMin.ToString(lang.Culture) : $"{g.ForestTokenGambleCostMin}–{g.ForestTokenGambleCostMax}");
			return string.Format(lang.TokenPrices, Num(g.ForestTokenGambleCoinsCost, lang), text, Num(g.IronBountyGambleCoinsCost, lang), g.IronBountyGambleCost, Num(g.GoldBountyGambleCoinsCost, lang), g.GoldBountyGambleCost);
		}

		private static List<string> BossOrder(IEnumerable<string> keys)
		{
			List<string> order = new List<string>();
			foreach (BiomeDefinition item in BiomeDataManager.BiomesInOrder ?? Array.Empty<BiomeDefinition>())
			{
				foreach (string item2 in item?.BossDefeatedKeys ?? Array.Empty<string>())
				{
					if (!order.Contains(item2))
					{
						order.Add(item2);
					}
				}
			}
			HashSet<string> hashSet = new HashSet<string>(keys, StringComparer.Ordinal);
			return order.Where(hashSet.Contains).Concat(hashSet.Where((string k) => !order.Contains(k)).OrderBy<string, string>((string k) => k, StringComparer.Ordinal)).ToList();
		}

		private static (string Slug, string Label) BossTier(string bossKey, Lang lang)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			BiomeDefinition val = ((IEnumerable<BiomeDefinition>)(BiomeDataManager.BiomesInOrder ?? Array.Empty<BiomeDefinition>())).FirstOrDefault((Func<BiomeDefinition, bool>)((BiomeDefinition d) => d?.BossDefeatedKeys != null && d.BossDefeatedKeys.Contains(bossKey)));
			if (val != null)
			{
				return (Slug: Slug(val.Biome), Label: string.Format(lang.TierLabel, BiomeTitle(val.Biome, lang)));
			}
			string text = new string((from c in bossKey.ToLowerInvariant()
				where char.IsLetterOrDigit(c) || c == '_' || c == '-'
				select c).ToArray());
			return (Slug: (text.Length > 0) ? text : "unknown", Label: bossKey);
		}

		private static void RegisterValues()
		{
			Value("stash_every", (AdventureDataConfig c, Lang l) => Every(c.SecretStash?.RefreshInterval, l));
			Value("gamble_every", (AdventureDataConfig c, Lang l) => Every(c.Gamble?.RefreshInterval, l));
			Value("map_every", (AdventureDataConfig c, Lang l) => Every(c.TreasureMap?.RefreshInterval, l));
			Value("bounty_every", (AdventureDataConfig c, Lang l) => Every(c.Bounties?.RefreshInterval, l));
			Value("stash_prices", StashPrices);
			Value("runestone_prices", (AdventureDataConfig c, Lang l) => Range((from i in c.TreasureMap?.SaleItems?.Where((SecretStashItemConfig i) => i != null && i.ForestTokenCost > 0)
				select i.ForestTokenCost), l));
			Value("andvaranaut_price", (AdventureDataConfig c, Lang l) => c.SecretStash?.OtherItems?.FirstOrDefault((Func<SecretStashItemConfig, bool>)((SecretStashItemConfig i) => i?.Item == "Andvaranaut"))?.CoinsCost.ToString(l.Culture));
			Value("andvaranaut_range", (AdventureDataConfig c, Lang l) => EpicLoot.GetAndvaranautRange().ToString(l.Culture));
			Value("gamble_coins_count", (AdventureDataConfig c, Lang l) => c.Gamble?.GamblesCount.ToString(l.Culture));
			Value("gamble_forest_count", (AdventureDataConfig c, Lang l) => c.Gamble?.ForestTokenGamblesCount.ToString(l.Culture));
			Value("gamble_iron_count", (AdventureDataConfig c, Lang l) => c.Gamble?.IronBountyGamblesCount.ToString(l.Culture));
			Value("gamble_gold_count", (AdventureDataConfig c, Lang l) => c.Gamble?.GoldBountyGamblesCount.ToString(l.Culture));
			Value("gamble_nonmagic", (AdventureDataConfig c, Lang l) => GambleNonMagic(c.Gamble, l));
			Value("gamble_coinfactor", (AdventureDataConfig c, Lang l) => (c.Gamble != null) ? Num(c.Gamble.ForestTokenGambleCoinsCost, l) : null);
			Value("gamble_forest_cost", (AdventureDataConfig c, Lang l) => (c.Gamble != null) ? ((c.Gamble.ForestTokenGambleCostMin != c.Gamble.ForestTokenGambleCostMax) ? $"{c.Gamble.ForestTokenGambleCostMin}–{c.Gamble.ForestTokenGambleCostMax}" : c.Gamble.ForestTokenGambleCostMin.ToString(l.Culture)) : null);
			Value("gamble_iron_cost", (AdventureDataConfig c, Lang l) => c.Gamble?.IronBountyGambleCost.ToString(l.Culture));
			Value("gamble_gold_cost", (AdventureDataConfig c, Lang l) => c.Gamble?.GoldBountyGambleCost.ToString(l.Culture));
			Value("gamble_removepurchased", delegate(AdventureDataConfig c, Lang l)
			{
				object obj = ElConfig("RemovePurchasedGambles");
				if (!(obj is bool))
				{
					return (string?)null;
				}
				return (!(bool)obj) ? l.Off : l.On;
			});
			Value("map_prices", (AdventureDataConfig c, Lang l) => Range((from i in c.TreasureMap?.BiomeInfo?.Where((TreasureMapBiomeInfoConfig i) => i != null && i.Cost > 0)
				select i.Cost), l));
			Value("map_tokens", (AdventureDataConfig c, Lang l) => Range((from i in c.TreasureMap?.BiomeInfo?.Where((TreasureMapBiomeInfoConfig i) => i != null && i.Cost > 0)
				select i.ForestTokens), l));
			Value("bounty_iron_stars", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? StarCount(c.Bounties.IronMinLevel, c.Bounties.IronMaxLevel, l) : null);
			Value("bounty_gold_stars", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? StarCount(c.Bounties.GoldMinLevel, c.Bounties.GoldMaxLevel, l) : null);
			Value("bounty_iron_health", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? Num(c.Bounties.IronHealthMultiplier, l) : null);
			Value("bounty_gold_health", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? Num(c.Bounties.GoldHealthMultiplier, l) : null);
			Value("bounty_adds_stars", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? StarCount(c.Bounties.AddsMinLevel, c.Bounties.AddsMaxLevel, l) : null);
			Value("bounty_adds_health", (AdventureDataConfig c, Lang l) => (c.Bounties != null) ? Num(c.Bounties.AddsHealthMultiplier, l) : null);
			Value("bounty_specialname", (AdventureDataConfig c, Lang l) => (c.Bounties?.Names != null) ? Pct(c.Bounties.Names.ChanceForSpecialName * 100f, l) : null);
			Value("map_area", (AdventureDataConfig c, Lang l) => (c.TreasureMap != null) ? Num(Mathf.Max(0f, c.TreasureMap.MinimapAreaRadius) * 2.1f * 0.5f, l, "0") : null);
			Value("gamble_chance_coins", (AdventureDataConfig c, Lang l) => Chances(c.Gamble?.GambleRarityChance, l));
			Value("gamble_chance_forest", (AdventureDataConfig c, Lang l) => Chances(Row(c.Gamble, (ItemRarity)1), l));
			Value("gamble_chance_iron", (AdventureDataConfig c, Lang l) => Chances(Row(c.Gamble, (ItemRarity)2), l));
			Value("gamble_chance_gold", (AdventureDataConfig c, Lang l) => Chances(Row(c.Gamble, (ItemRarity)3), l));
			Value("bounty_limit", delegate(AdventureDataConfig c, Lang l)
			{
				object obj = ElConfig("EnableLimitedBountiesInProgress");
				if (!(obj is bool))
				{
					return (string?)null;
				}
				return (!(bool)obj || !(ElConfig("MaxInProgressBounties") is int num)) ? l.Off : num.ToString(l.Culture);
			});
		}

		private static void Value(string name, Func<AdventureDataConfig, Lang, string?> provider)
		{
			Api.SetValue("el_adv_" + name, (Func<string>)delegate
			{
				AdventureDataConfig config = AdventureDataManager.Config;
				return ((config == null) ? null : provider(config, Lang.For(Texts.Normalize(Api.Locale)))) ?? "?";
			});
		}

		private static string? StashPrices(AdventureDataConfig c, Lang lang)
		{
			List<SecretStashItemConfig> list = c.SecretStash?.Materials;
			if (list == null)
			{
				return null;
			}
			SortedDictionary<int, List<int>> sortedDictionary = new SortedDictionary<int, List<int>>();
			foreach (SecretStashItemConfig item in list)
			{
				if (item == null || string.IsNullOrEmpty(item.Item))
				{
					continue;
				}
				int? num = StashRarity(item.Item);
				if (num.HasValue)
				{
					int valueOrDefault = num.GetValueOrDefault();
					if (!sortedDictionary.TryGetValue(valueOrDefault, out var value))
					{
						value = (sortedDictionary[valueOrDefault] = new List<int>());
					}
					value.Add(item.CoinsCost);
				}
			}
			if (sortedDictionary.Count != 0)
			{
				return string.Join(", ", from r in sortedDictionary
					where r.Key >= 0 && r.Key < lang.RarityNames.Length
					select lang.RarityNames[r.Key] + " " + Range(r.Value, lang));
			}
			return null;
		}

		private static int? StashRarity(string item)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected I4, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected I4, but got Unknown
			foreach (ItemRarity value in Enum.GetValues(typeof(ItemRarity)))
			{
				if (item.EndsWith(((object)value/*cast due to .constrained prefix*/).ToString(), StringComparison.Ordinal))
				{
					return (int)value;
				}
			}
			object obj;
			if (!((Object)(object)ObjectDB.instance != (Object)null))
			{
				obj = null;
			}
			else
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item);
				obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			}
			ItemDrop val2 = (ItemDrop)obj;
			if (val2?.m_itemData == null)
			{
				return null;
			}
			try
			{
				return (int)ItemDataExtensions.GetRarity(val2.m_itemData);
			}
			catch (ArgumentException)
			{
				return null;
			}
		}

		private static float[]? Row(GambleConfig? g, ItemRarity rarity)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			float[][] array = g?.GambleRarityChanceByRarity;
			if (array == null || (int)rarity >= array.Length)
			{
				return null;
			}
			return array[rarity];
		}

		private static string? Chances(float[]? row, Lang lang)
		{
			if (row == null || row.Length == 0)
			{
				return null;
			}
			float num = row.Where((float r) => r > 0f).Sum();
			if (num <= 0f)
			{
				return null;
			}
			List<string> list = new List<string>();
			for (int num2 = 0; num2 < row.Length && num2 <= lang.RarityNames.Length; num2++)
			{
				float num3 = row[num2] / num * 100f;
				if (num3 >= 0.01f)
				{
					list.Add(Pct(num3, lang) + " " + ((num2 == 0) ? lang.NonMagic : lang.RarityNames[num2 - 1]));
				}
			}
			return string.Join(", ", list);
		}

		private static string? GambleNonMagic(GambleConfig? g, Lang lang)
		{
			float[] array = g?.GambleRarityChance;
			if (array == null || array.Length == 0)
			{
				return null;
			}
			float num = array.Sum();
			if (!(num <= 0f))
			{
				return Pct(array[0] / num * 100f, lang);
			}
			return null;
		}

		private static string? Range(IEnumerable<int>? values, Lang lang)
		{
			List<int> list = values?.ToList() ?? new List<int>();
			if (list.Count == 0)
			{
				return null;
			}
			int num = list.Min();
			int num2 = list.Max();
			if (num != num2)
			{
				return num.ToString(lang.Culture) + "–" + num2.ToString(lang.Culture);
			}
			return num.ToString(lang.Culture);
		}

		private static string StarCount(int minLevel, int maxLevel, Lang lang)
		{
			int num = Math.Max(0, minLevel - 1);
			int num2 = Math.Max(num, maxLevel - 1);
			if (num != num2)
			{
				return $"{num}–{num2}";
			}
			return num.ToString(lang.Culture);
		}

		private static string? Every(int? days, Lang lang)
		{
			if (!days.HasValue)
			{
				return null;
			}
			if (!(days <= 1))
			{
				return string.Format(lang.EveryDays, days.Value);
			}
			return lang.EveryDay;
		}

		private static object? ElConfig(string field)
		{
			try
			{
				object? obj = typeof(AdventureDataManager).Assembly.GetType("EpicLoot.Config.ELConfig")?.GetField(field, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
				object? obj2 = ((obj is ConfigEntryBase) ? obj : null);
				return (obj2 != null) ? ((ConfigEntryBase)obj2).BoxedValue : null;
			}
			catch
			{
				return null;
			}
		}

		private static void LoadNames(IEnumerable<string> languages)
		{
			List<string> list = (from l in languages.Distinct()
				where !names.ContainsKey(l)
				select l).ToList();
			if (list.Count == 0)
			{
				return;
			}
			foreach (string item in list)
			{
				names[item] = new Dictionary<string, string>(StringComparer.Ordinal);
			}
			try
			{
				LocalizationSettings val = Resources.Load<LocalizationSettings>("LocalizationSettings");
				if ((Object)(object)val == (Object)null || val.Localizations == null)
				{
					Plugin.Log.LogWarning((object)"Valheims Sprachtabellen nicht gefunden; Namen kommen aus der Spielsprache");
					return;
				}
				foreach (TextAsset localization in val.Localizations)
				{
					if ((Object)(object)localization != (Object)null && !string.IsNullOrEmpty(localization.text))
					{
						ReadNames(localization.text, list);
					}
				}
				Plugin.Log.LogInfo((object)("Namen aus Valheims Sprachtabellen: " + string.Join(", ", list.Select((string l) => $"{names[l].Count} {l}"))));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Valheims Sprachtabellen nicht gelesen (" + ex.Message + "); Namen kommen aus der Spielsprache"));
			}
		}

		private static void ReadNames(string csv, List<string> languages)
		{
			int num = csv.IndexOf('\n');
			if (num < 0)
			{
				return;
			}
			string[] header = (from h in csv.Substring(0, num).Split(',')
				select h.Trim().Trim('"', '*', '\r', ' ')).ToArray();
			int english = Array.IndexOf(header, "English");
			if (english < 0)
			{
				return;
			}
			List<(Dictionary<string, string>, int)> list = languages.Select((string l) => (names[l], (l == "English") ? english : Array.IndexOf(header, l))).ToList();
			foreach (List<string> item3 in Rows(csv, num + 1))
			{
				if (item3.Count == 0)
				{
					continue;
				}
				string key = item3[0].Trim();
				if (key.Length == 0 || key.StartsWith("//", StringComparison.Ordinal) || !NamePrefixes.Any((string p) => key.StartsWith(p, StringComparison.Ordinal)))
				{
					continue;
				}
				string text = ((item3.Count > english) ? item3[english].Trim() : "");
				foreach (var item4 in list)
				{
					Dictionary<string, string> item = item4.Item1;
					int item2 = item4.Item2;
					string text2 = ((item2 >= 0 && item3.Count > item2) ? item3[item2].Trim() : "");
					if (text2.Length == 0)
					{
						text2 = text;
					}
					if (text2.Length > 0)
					{
						item[key] = text2;
					}
				}
			}
		}

		private static IEnumerable<List<string>> Rows(string text, int start)
		{
			List<string> list = new List<string>();
			StringBuilder cell = new StringBuilder();
			bool quoted = false;
			for (int i = start; i < text.Length; i++)
			{
				char c = text[i];
				switch (c)
				{
				case '"':
					if (quoted && i + 1 < text.Length && text[i + 1] == '"')
					{
						cell.Append('"');
						i++;
					}
					else
					{
						quoted = !quoted;
					}
					continue;
				case ',':
					if (!quoted)
					{
						list.Add(cell.ToString());
						cell.Length = 0;
						continue;
					}
					break;
				}
				if (c == '\n' && !quoted)
				{
					list.Add(cell.ToString());
					cell.Length = 0;
					yield return list;
					list = new List<string>();
				}
				else
				{
					cell.Append(c);
				}
			}
			list.Add(cell.ToString());
			yield return list;
		}

		private static string Localized(string? token, Lang lang, string fallback)
		{
			if (string.IsNullOrEmpty(token))
			{
				return fallback;
			}
			if (names.TryGetValue(Texts.Language(lang.Code), out Dictionary<string, string> value) && value.TryGetValue(token.TrimStart('$'), out var value2) && value2.Length > 0)
			{
				return EffectGlossary.Plain(value2);
			}
			if (Localization.instance != null && token.StartsWith("$", StringComparison.Ordinal))
			{
				string text = Localization.instance.Localize(token);
				if (text.Length > 0 && !text.StartsWith("[", StringComparison.Ordinal) && !text.Contains("$"))
				{
					return EffectGlossary.Plain(text);
				}
			}
			return fallback;
		}

		private static string MonsterName(string prefab, Lang lang)
		{
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null);
			return Localized((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<Character>()?.m_name, lang, prefab);
		}

		private static string ItemName(string prefab, Lang lang)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null);
			return Localized((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name, lang, prefab);
		}

		private unsafe static string BiomeTitle(Biome biome, Lang lang)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			string key = Slug(biome);
			if (lang.Biomes.TryGetValue(key, out string value))
			{
				return value;
			}
			string name = BiomeDataManager.GetName(biome);
			return Localized(BiomeDataManager.GetLocalizationToken(biome), lang, string.IsNullOrEmpty(name) ? ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString() : name);
		}

		private static string BiomeLink(Biome biome, string title)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			string text = "valheim.biome." + Slug(biome);
			if (!Api.Has(text))
			{
				return title;
			}
			return "[[" + text + "|" + title + "]]";
		}

		private unsafe static string Slug(Biome biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected I4, but got Unknown
			string text = BiomeDataManager.GetName(biome);
			if (string.IsNullOrEmpty(text))
			{
				text = ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString();
			}
			string text2 = new string((from c in text.ToLowerInvariant()
				where (c >= 'a' && c <= 'z') || char.IsDigit(c) || c == '_' || c == '-'
				select c).ToArray());
			if (text2.Length <= 0)
			{
				return ((int)biome).ToString(CultureInfo.InvariantCulture);
			}
			return text2;
		}

		private static List<Biome> InOrder(IEnumerable<Biome> biomes)
		{
			HashSet<Biome> hashSet = new HashSet<Biome>(biomes);
			List<Biome> order = (from d in BiomeDataManager.BiomesInOrder ?? Array.Empty<BiomeDefinition>()
				where d != null
				select d.Biome).Where(hashSet.Contains).Distinct().ToList();
			order.AddRange(from b in hashSet
				where !order.Contains(b)
				orderby (int)b
				select b);
			return order;
		}

		private static string Num(float value, Lang lang, string format = "0.##")
		{
			return value.ToString(format, lang.Culture);
		}

		private static string Pct(float value, Lang lang)
		{
			return string.Format(lang.Percent, value.ToString("0.#", lang.Culture));
		}

		private static string Cut(string text)
		{
			if (text.Length > 140)
			{
				return text.Substring(0, 137).TrimEnd() + "…";
			}
			return text;
		}

		private static string ToJson(Lang lang, List<Entry> entries)
		{
			HashSet<string> own = new HashSet<string>(entries.Select((Entry e) => e.Key), StringComparer.Ordinal);
			StringBuilder stringBuilder = new StringBuilder("{\n\"$locale\": ");
			stringBuilder.Append(Json.Quote(lang.Code));
			foreach (Entry entry in entries)
			{
				stringBuilder.Append(",\n").Append(Json.Quote(entry.Key)).Append(": {\"title\": ")
					.Append(Json.Quote(entry.Title));
				stringBuilder.Append(", \"short\": ").Append(Json.Quote(entry.Short));
				stringBuilder.Append(", \"body\": ").Append(Json.Quote(entry.Body));
				List<string> list = entry.See.Where((string k) => own.Contains(k) || Api.Has(k)).ToList();
				if (list.Count > 0)
				{
					stringBuilder.Append(", \"see\": [").Append(string.Join(", ", list.Select(Json.Quote))).Append(']');
				}
				stringBuilder.Append('}');
			}
			return stringBuilder.Append("\n}").ToString();
		}
	}
	internal static class Concepts
	{
		private const string Owner = "DeepTip_EpicLoot.Begriffe";

		private const string Prefix = "DeepTipEpicLoot.begriffe.";

		internal static void Register()
		{
			List<string> locales = (from n in Assembly.GetExecutingAssembly().GetManifestResourceNames()
				where n.StartsWith("DeepTipEpicLoot.begriffe.", StringComparison.Ordinal) && n.EndsWith(".json", StringComparison.Ordinal)
				select n.Substring("DeepTipEpicLoot.begriffe.".Length, n.Length - "DeepTipEpicLoot.begriffe.".Length - ".json".Length)).OrderBy<string, string>((string l) => l, StringComparer.Ordinal).ToList();
			foreach (string item in new string[2] { "de", "en" }.Where((string l) => !locales.Contains(l)))
			{
				Plugin.Log.LogWarning((object)("begriffe." + item + ".json fehlt in der DLL"));
			}
			foreach (string item2 in locales)
			{
				string text = Resource("begriffe." + item2 + ".json");
				if (text != null)
				{
					Api.Register("DeepTip_EpicLoot.Begriffe", text, item2);
				}
			}
		}

		internal static string? Resource(string file)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("DeepTipEpicLoot." + file);
			if (stream == null)
			{
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}
	}
	internal static class EffectGlossary
	{
		internal sealed class Lang
		{
			public string Code = "";

			public CultureInfo Culture = CultureInfo.InvariantCulture;

			public string Percent = "{0}%";

			public string[] Rarities = new string[6];

			public string Values = "";

			public string Steps = "";

			public string Appears = "";

			public string NotOn = "";

			public string NotWith = "";

			public string OnlyWith = "";

			public string Multiple = "";

			public string NoRoll = "";

			public string Note = "";

			public string Table = "";

			public string NoAugment = "";

			public string NoDisenchant = "";

			public string NoRune = "";

			public string SkillWeapons = "{0}";

			public string OnlyRarity = "{0}";

			public Dictionary<string, string> ItemTypes = new Dictionary<string, string>();

			public Dictionary<string, string> Skills = new Dictionary<string, string>();

			public Dictionary<string, (string Yes, string No)> Flags = new Dictionary<string, (string, string)>();

			public string Pct(string number)
			{
				return string.Format(Percent, number);
			}

			public string ItemType(string type)
			{
				if (!ItemTypes.TryGetValue(type, out string value))
				{
					return type;
				}
				return value;
			}

			public string Skill(string skill)
			{
				if (!Skills.TryGetValue(skill, out string value))
				{
					return skill;
				}
				return value;
			}

			public string Flag(string field, bool value)
			{
				if (!Flags.TryGetValue(field, out (string, string) value2))
				{
					return field + (value ? "" : " = false");
				}
				if (!value)
				{
					return value2.Item2;
				}
				return value2.Item1;
			}

			internal static Lang For(string locale)
			{
				return Texts.Load<Lang>("effect", locale);
			}
		}

		private const string Owner = "DeepTip_EpicLoot.Effekte";

		private static readonly Regex Placeholder = new Regex("\\{[^}]*\\}");

		private static readonly Regex Tags = new Regex("<[^>]+>");

		private static readonly Regex Spaces = new Regex("\\s+");

		private static readonly Regex Brackets = new Regex("\\([^)]*\\)");

		private static readonly char[] NameTrim = new char[22]
		{
			' ', '+', '-', '%', ':', '(', ')', '[', ']', '/',
			',', '.', '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9'
		};

		private static readonly Regex Markup = new Regex("\\*\\*|\\[\\[(?:[^\\]|]+\\|)?([^\\]]+)\\]\\]");

		internal static void Rebuild()
		{
			try
			{
				List<MagicItemEffectDefinition> list = MagicItemEffectDefinitions.AllDefinitions.Values.Where((MagicItemEffectDefinition d) => d != null).OrderBy<MagicItemEffectDefinition, string>((MagicItemEffectDefinition d) => d.Type, StringComparer.Ordinal).ToList();
				List<string> locales = Texts.Locales;
				Api.Unregister("DeepTip_EpicLoot.Effekte");
				foreach (string item in locales)
				{
					Api.Register("DeepTip_EpicLoot.Effekte", Build(Lang.For(item), list, Texts.EpicLoot(item), Texts.Effects(item)), item);
				}
				int num = list.Count((MagicItemEffectDefinition d) => Texts.Effects(locales[0]).ContainsKey(d.Type));
				Plugin.Log.LogInfo((object)$"{list.Count} EpicLoot-Effekte erklärt, davon {num} mit am Code geprüftem Text");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EpicLoot-Effekte nicht erklärt: {arg}");
			}
		}

		internal static string Key(string type)
		{
			return "epicloot.effect." + type.ToLowerInvariant();
		}

		internal static Dictionary<string, object?> EpicLootLanguage(string language)
		{
			using Stream stream = typeof(MagicItemEffectDefinitions).Assembly.GetManifestResourceStream("EpicLoot.localizations." + language + ".json");
			if (stream == null)
			{
				Plugin.Log.LogWarning((object)("EpicLoot hat keine Sprachdatei " + language));
				return new Dictionary<string, object>();
			}
			using StreamReader streamReader = new StreamReader(stream);
			return Json.Parse(streamReader.ReadToEnd());
		}

		private static string Build(Lang lang, List<MagicItemEffectDefinition> defs, Dictionary<string, object?> epicLoot, Dictionary<string, object?> texts)
		{
			Dictionary<string, string> dictionary = (from d in defs
				group d by d.Type).ToDictionary((IGrouping<string, MagicItemEffectDefinition> g) => g.Key, (IGrouping<string, MagicItemEffectDefinition> g) => Title(Display(g.First(), epicLoot)));
			Dictionary<string, List<string>> dictionary2 = (from d in defs
				group d by d.Type).ToDictionary((IGrouping<string, MagicItemEffectDefinition> g) => g.Key, (IGrouping<string, MagicItemEffectDefinition> g) => Aliases(g.First(), Display(g.First(), epicLoot), epicLoot));
			Dictionary<string, string> owners = Owners(dictionary2);
			StringBuilder stringBuilder = new StringBuilder("{\n\"$locale\": ");
			stringBuilder.Append(Json.Quote(lang.Code));
			List<string> list = ((IEnumerable<string>)new string[3] { "epicloot.rarity", "epicloot.rollrange", "epicloot.stars" }).Where((Func<string, bool>)Api.Has).ToList();
			foreach (MagicItemEffectDefinition def in defs)
			{
				string display = Display(def, epicLoot);
				object value;
				Dictionary<string, object> text = (texts.TryGetValue(def.Type, out value) ? (value as Dictionary<string, object>) : null);
				string text2 = Field(text, "text");
				string text3 = Field(text, "abweichung");
				string text4 = ((text2.Length > 0) ? text2 : Plain(Lookup(epicLoot, DescriptionKey(def)) ?? ""));
				List<string> list2 = new List<string>();
				if (text4.Length > 0)
				{
					list2.Add(text4);
				}
				if (text3.Length > 0)
				{
					list2.Add("**" + lang.Note + "** " + text3);
				}
				bool flag = ShardGlossary.IsShardOnly(def);
				if (!flag)
				{
					string text5 = Values(def, display, lang);
					if (text5 != null)
					{
						list2.Add(text5);
					}
				}
				if (!flag)
				{
					list2.AddRange(Requirements(def, lang, dictionary));
					string text6 = TableFlags(def, lang);
					if (text6 != null)
					{
						list2.Add(text6);
					}
				}
				string text7 = ShardGlossary.StonesLine(def.Type, lang.Code, epicLoot, flag);
				if (text7 != null)
				{
					list2.Add(text7);
				}
				stringBuilder.Append(",\n").Append(Json.Quote(Key(def.Type))).Append(": {\"title\": ")
					.Append(Json.Quote(dictionary[def.Type]));
				stringBuilder.Append(", \"short\": ").Append(Json.Quote(Short(text4, dictionary[def.Type])));
				stringBuilder.Append(", \"body\": ").Append(Json.Quote(string.Join("\n\n", list2)));
				string value2;
				List<string> list3 = dictionary2[def.Type].Where((string a) => owners.TryGetValue(a, out value2) && value2 == def.Type).ToList();
				if (list3.Count > 0)
				{
					stringBuilder.Append(", \"aliases\": [").Append(string.Join(", ", list3.Select(Json.Quote))).Append(']');
				}
				if (list.Count > 0)
				{
					stringBuilder.Append(", \"see\": [").Append(string.Join(", ", list.Select(Json.Quote))).Append(']');
				}
				stringBuilder.Append('}');
			}
			return stringBuilder.Append("\n}").ToString();
		}

		internal static string? Lookup(Dictionary<string, object?> table, string key)
		{
			if (!table.TryGetValue(key.TrimStart('$'), out object value))
			{
				return null;
			}
			return value as string;
		}

		private static string DisplayKey(MagicItemEffectDefinition def)
		{
			if (!string.IsNullOrEmpty(def.DisplayText))
			{
				return def.DisplayText.TrimStart('$');
			}
			return "mod_epicloot_me_" + def.Type.ToLowerInvariant() + "_display";
		}

		private static string DescriptionKey(MagicItemEffectDefinition def)
		{
			if (!string.IsNullOrEmpty(def.Description))
			{
				return def.Description.TrimStart('$');
			}
			string text = DisplayKey(def);
			return (text.EndsWith("_display") ? text.Substring(0, text.Length - "_display".Length) : text) + "_desc";
		}

		internal static string Display(MagicItemEffectDefinition def, Dictionary<string, object?> table)
		{
			string text = Lookup(table, DisplayKey(def));
			if (string.IsNullOrEmpty(text) && Localization.instance != null && !string.IsNullOrEmpty(def.DisplayText))
			{
				string text2 = Localization.instance.Localize(def.DisplayText);
				text = (text2.StartsWith("[") ? null : text2);
			}
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return def.Type;
		}

		internal static string Plain(string text)
		{
			return Spaces.Replace(Tags.Replace(text, ""), " ").Trim();
		}

		internal static string Title(string display)
		{
			return Spaces.Replace(Placeholder.Replace(Plain(display), "X"), " ").Trim();
		}

		private static List<string> Aliases(MagicItemEffectDefinition def, string display, Dictionary<string, object?> table)
		{
			List<string> list = new List<string>();
			Add(list, Name(display));
			string text = DisplayKey(def);
			string arg = (text.EndsWith("_display") ? text.Substring(0, text.Length - "_display".Length) : text);
			for (int i = 1; i <= 9; i++)
			{
				Add(list, Lookup(table, $"{arg}_prefix{i}"));
				Add(list, Lookup(table, $"{arg}_suffix{i}"));
			}
			return list;
		}

		internal static string Name(string display)
		{
			string text = Brackets.Replace(Plain(display), " ");
			int num = text.IndexOf('{');
			string text2 = ((num >= 0) ? text.Substring(0, num) : text);
			int num2 = text2.IndexOf(':');
			text2 = ((num2 > 0) ? text2.Substring(0, num2) : text2).Trim(NameTrim);
			string text3;
			if (text2.Length <= 0)
			{
				text3 = (from p in Placeholder.Split(text)
					select p.Trim(NameTrim) into p
					orderby p.Length descending
					select p).FirstOrDefault();
				if (text3 == null)
				{
					return "";
				}
			}
			else
			{
				text3 = text2;
			}
			return text3;
		}

		private static Dictionary<string, string> Owners(Dictionary<string, List<string>> candidates)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			foreach (IGrouping<string, string> item in candidates.SelectMany<KeyValuePair<string, List<string>>, (string, string)>((KeyValuePair<string, List<string>> c) => c.Value.Select((string alias) => (alias: alias, type: c.Key))).GroupBy<(string, string), string, string>(((string alias, string type) x) => x.alias, ((string alias, string type) x) => x.type, StringComparer.OrdinalIgnoreCase))
			{
				List<string> types = item.Distinct().ToList();
				string text = ((types.Count == 2) ? types.FirstOrDefault((string t) => types.Contains(t + "LowHealth")) : null);
				if (types.Count == 1 || text != null)
				{
					dictionary[item.Key] = text ?? types[0];
				}
			}
			return dictionary;
		}

		private static void Add(List<string> aliases, string? alias)
		{
			if (alias != null)
			{
				string text = Plain(alias);
				if (text.Length >= 4 && !aliases.Contains<string>(text, StringComparer.OrdinalIgnoreCase))
				{
					aliases.Add(text);
				}
			}
		}

		private static string Field(Dictionary<string, object?>? text, string name)
		{
			if (text == null || !text.TryGetValue(name, out object value) || !(value is string text2))
			{
				return "";
			}
			return text2.Trim();
		}

		private static string Short(string main, string title)
		{
			string text = Markup.Replace(main, (Match m) => (!m.Groups[1].Success) ? "" : m.Groups[1].Value);
			int num = text.IndexOf(". ", StringComparison.Ordinal);
			string text2 = ((num > 0) ? text.Substring(0, num + 1) : text).Trim();
			if (text2.Length == 0)
			{
				return title;
			}
			if (text2.Length > 140)
			{
				return text2.Substring(0, 137).TrimEnd() + "…";
			}
			return text2;
		}

		private static string? Values(MagicItemEffectDefinition def, string display, Lang lang)
		{
			if (MagicItemEffectDefinitions.IsValuelessEffect(def.Type) || def.ValuesPerRarity == null)
			{
				return null;
			}
			ValuesPerRarityDef valuesPerRarity = def.ValuesPerRarity;
			ValueDef[] array = (ValueDef[])(object)new ValueDef[6] { valuesPerRarity.Magic, valuesPerRarity.Rare, valuesPerRarity.Epic, valuesPerRarity.Legendary, valuesPerRarity.Mythic, valuesPerRarity.Ancient };
			bool flag = display.Contains("%");
			List<string> list = new List<string>();
			float? num = null;
			bool flag2 = true;
			for (int i = 0; i < array.Length; i++)
			{
				ValueDef val = array[i];
				if (val != null)
				{
					string text = (Mathf.Approximately(val.MinValue, val.MaxValue) ? Num(val.MinValue, lang) : (Num(val.MinValue, lang) + "–" + Num(val.MaxValue, lang)));
					list.Add(lang.Rarities[i] + " " + (flag ? lang.Pct(text) : text));
					if (!num.HasValue)
					{
						num = val.Increment;
					}
					else if (!Mathf.Approximately(num.Value, val.Increment))
					{
						flag2 = false;
					}
				}
			}
			if (list.Count == 0)
			{
				return null;
			}
			string text2 = "**" + lang.Values + "** " + string.Join(", ", list);
			if (flag2 && num.HasValue)
			{
				float valueOrDefault = num.GetValueOrDefault();
				if (valueOrDefault > 0f && !Mathf.Approximately(valueOrDefault, 1f))
				{
					text2 = text2 + " (" + string.Format(lang.Steps, Num(valueOrDefault, lang)) + ")";
				}
			}
			return text2 + ".";
		}

		internal static string Num(float value, Lang lang)
		{
			return value.ToString("0.##", lang.Culture);
		}

		private unsafe static IEnumerable<string> Requirements(MagicItemEffectDefinition def, Lang lang, Dictionary<string, string> titles)
		{
			MagicItemEffectRequirements q = def.Requirements;
			if (q == null)
			{
				yield break;
			}
			if (q.NoRoll)
			{
				yield return lang.NoRoll;
			}
			List<string> list = new List<string>();
			List<string> allowedItemTypes = q.AllowedItemTypes;
			if (allowedItemTypes != null && allowedItemTypes.Count > 0)
			{
				list.Add(string.Join(", ", q.AllowedItemTypes.Select(lang.ItemType)));
			}
			List<SkillType> allowedSkillTypes = q.AllowedSkillTypes;
			if (allowedSkillTypes != null && allowedSkillTypes.Count > 0)
			{
				list.Add(string.Format(lang.SkillWeapons, string.Join(", ", q.AllowedSkillTypes.Select((SkillType t) => lang.Skill(((object)(*(SkillType*)(&t))/*cast due to .constrained prefix*/).ToString())))));
			}
			List<string> allowedItemNames = q.AllowedItemNames;
			if (allowedItemNames != null && allowedItemNames.Count > 0)
			{
				list.Add(string.Join(", ", q.AllowedItemNames.Select(ItemName)));
			}
			List<ItemRarity> allowedRarities = q.AllowedRarities;
			if (allowedRarities != null && allowedRarities.Count > 0)
			{
				list.Add(string.Format(lang.OnlyRarity, string.Join(", ", q.AllowedRarities.Select((ItemRarity r) => lang.Rarities[r]))));
			}
			FieldInfo[] fields = typeof(MagicItemEffectRequirements).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(bool?) && fieldInfo.GetValue(q) is bool value)
				{
					list.Add(lang.Flag(fieldInfo.Name, value));
				}
			}
			if (list.Count > 0)
			{
				yield return "**" + lang.Appears + "** " + string.Join("; ", list) + ".";
			}
			List<string> list2 = new List<string>();
			List<string> excludedItemTypes = q.ExcludedItemTypes;
			if (excludedItemTypes != null && excludedItemTypes.Count > 0)
			{
				list2.Add(string.Join(", ", q.ExcludedItemTypes.Select(lang.ItemType)));
			}
			List<SkillType> excludedSkillTypes = q.ExcludedSkillTypes;
			if (excludedSkillTypes != null && excludedSkillTypes.Count > 0)
			{
				list2.Add(string.Format(lang.SkillWeapons, string.Join(", ", q.ExcludedSkillTypes.Select((SkillType t) => lang.Skill(((object)(*(SkillType*)(&t))/*cast due to .constrained prefix*/).ToString())))));
			}
			List<string> excludedItemNames = q.ExcludedItemNames;
			if (excludedItemNames != null && excludedItemNames.Count > 0)
			{
				list2.Add(string.Join(", ", q.ExcludedItemNames.Select(ItemName)));
			}
			List<ItemRarity> excludedRarities = q.ExcludedRarities;
			if (excludedRarities != null && excludedRarities.Count > 0)
			{
				list2.Add(string.Format(lang.OnlyRarity, string.Join(", ", q.ExcludedRarities.Select((ItemRarity r) => lang.Rarities[r]))));
			}
			if (list2.Count > 0)
			{
				yield return "**" + lang.NotOn + "** " + string.Join("; ", list2) + ".";
			}
			List<string> exclusiveEffectTypes = q.ExclusiveEffectTypes;
			if (exclusiveEffectTypes != null && exclusiveEffectTypes.Count > 0)
			{
				yield return "**" + lang.NotWith + "** " + string.Join(", ", q.ExclusiveEffectTypes.Select((string t) => Link(t, titles))) + ".";
			}
			List<string> mustHaveEffectTypes = q.MustHaveEffectTypes;
			if (mustHaveEffectTypes != null && mustHaveEffectTypes.Count > 0)
			{
				yield return "**" + lang.OnlyWith + "** " + string.Join(", ", q.MustHaveEffectTypes.Select((string t) => Link(t, titles))) + ".";
			}
			if (!q.ExclusiveSelf)
			{
				yield return lang.Multiple;
			}
		}

		private static string Link(string type, Dictionary<string, string> titles)
		{
			if (!titles.TryGetValue(type, out string value))
			{
				return type;
			}
			return "[[" + Key(type) + "|" + value + "]]";
		}

		internal static string ItemName(string prefab)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null);
			string text = ((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name);
			if (text == null || Localization.instance == null)
			{
				return prefab;
			}
			return Localization.instance.Localize(text);
		}

		private static string? TableFlags(MagicItemEffectDefinition def, Lang lang)
		{
			List<string> list = new List<string>();
			if (!def.CanBeAugmented)
			{
				list.Add(lang.NoAugment);
			}
			if (!def.CanBeDisenchanted)
			{
				list.Add(lang.NoDisenchant);
			}
			if (!def.CanBeRunified)
			{
				list.Add(lang.NoRune);
			}
			if (list.Count != 0)
			{
				return "**" + lang.Table + "** " + string.Join(", ", list) + ".";
			}
			return null;
		}
	}
	internal static class Json
	{
		internal static Dictionary<string, object?> Parse(string text)
		{
			int i = 0;
			return (Value(text.TrimStart('\ufeff'), ref i) as Dictionary<string, object>) ?? new Dictionary<string, object>();
		}

		private static void Skip(string s, ref int i)
		{
			while (i < s.Length && char.IsWhiteSpace(s[i]))
			{
				i++;
			}
		}

		private static object? Value(string s, ref int i)
		{
			Skip(s, ref i);
			if (i >= s.Length)
			{
				return null;
			}
			switch (s[i])
			{
			case '{':
				return Object(s, ref i);
			case '[':
				return Array(s, ref i);
			case '"':
				return String(s, ref i);
			default:
			{
				int num = i;
				while (i < s.Length && ",}] \t\r\n".IndexOf(s[i]) < 0)
				{
					i++;
				}
				string text = s.Substring(num, i - num);
				if (!(text == "null"))
				{
					return text;
				}
				return null;
			}
			}
		}

		private static Dictionary<string, object?> Object(string s, ref int i)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			i++;
			while (true)
			{
				Skip(s, ref i);
				if (i >= s.Length || s[i] == '}')
				{
					break;
				}
				if (s[i] == ',')
				{
					i++;
					continue;
				}
				string key = String(s, ref i);
				Skip(s, ref i);
				i++;
				dictionary[key] = Value(s, ref i);
			}
			i++;
			return dictionary;
		}

		private static List<object?> Array(string s, ref int i)
		{
			List<object> list = new List<object>();
			i++;
			while (true)
			{
				Skip(s, ref i);
				if (i >= s.Length || s[i] == ']')
				{
					break;
				}
				if (s[i] == ',')
				{
					i++;
				}
				else
				{
					list.Add(Value(s, ref i));
				}
			}
			i++;
			return list;
		}

		private static string String(string s, ref int i)
		{
			StringBuilder stringBuilder = new StringBuilder();
			i++;
			while (i < s.Length && s[i] != '"')
			{
				char c = s[i++];
				if (c != '\\' || i >= s.Length)
				{
					stringBuilder.Append(c);
					continue;
				}
				char c2 = s[i++];
				switch (c2)
				{
				case 'n':
					stringBuilder.Append('\n');
					break;
				case 'r':
					stringBuilder.Append('\r');
					break;
				case 't':
					stringBuilder.Append('\t');
					break;
				case 'b':
					stringBuilder.Append('\b');
					break;
				case 'f':
					stringBuilder.Append('\f');
					break;
				case 'u':
				{
					if (i + 4 <= s.Length && int.TryParse(s.Substring(i, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
					{
						stringBuilder.Append((char)result);
						i += 4;
					}
					break;
				}
				default:
					stringBuilder.Append(c2);
					break;
				}
			}
			i++;
			return stringBuilder.ToString();
		}

		internal static string Quote(string value)
		{
			StringBuilder stringBuilder = new StringBuilder("\"");
			foreach (char c in value)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				case '\r':
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4", CultureInfo.InvariantCulture));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.Append('"').ToString();
		}
	}
	internal static class LegendaryGlossary
	{
		private sealed class Counts
		{
			public int Uniques;

			public int Pieces;

			public int Sets;

			public int Failed;
		}

		private sealed class Entry
		{
			public string Title = "";

			public string Short = "";

			public string Body = "";

			public List<string> See = new List<string>();
		}

		private sealed class Words
		{
			public EffectGlossary.Lang Lang = new EffectGlossary.Lang();

			public string Minutes = "{0} min";

			public string Seconds = "{0} s";

			public string UniqueIntro = "";

			public string UniqueShort = "";

			public string PieceIntro = "";

			public string PieceShort = "";

			public string PieceRoll = "";

			public string SetShort = "";

			public string SetPieces = "";

			public string SetBonuses = "";

			public string Base = "";

			public string GuaranteedLabel = "";

			public string None = "";

			public string AllTiers = "";

			public string CountLabel = "";

			public string CountFixed = "";

			public string CountRolled = "";

			public string CountRolledPlain = "";

			public string CountAtLeast = "";

			public string Aura = "";

			public string Texture = "";

			public string Rivals = "";

			public string From = "";

			public string Undefined = "";

			public string MorePieces = "";

			public string Looks = "";

			public string AnyItem = "";

			public string Except = "";

			public string Ability = "";

			public string OnKey = "";

			public string ByItself = "";

			public string Passive = "";

			public string Cooldown = "";

			public string OwnKey = "";

			public string NoKey = "";

			public string LookTint = "";

			public string LookWeapon = "";

			public string LookHands = "";

			public string LookArrow = "";

			public string LookAura = "";

			public string LookParticles = "";

			public string UniqueIndexTitle = "";

			public string UniqueIndexShort = "";

			public string UniqueIndexIntro = "";

			public string SetIndexTitle = "";

			public string SetIndexShort = "";

			public string SetIndexIntro = "";

			public string SetIndexLine = "";

			internal static Words For(string locale)
			{
				Words words = Texts.Load<Words>("legendary", locale);
				words.Lang = EffectGlossary.Lang.For(locale);
				return words;
			}
		}

		private const string Owner = "DeepTip_EpicLoot.Legendaer";

		private const string UniquePrefix = "epicloot.legendary.";

		private const string SetPrefix = "epicloot.set.";

		private const int ShortLength = 140;

		private static readonly Dictionary<string, string> SetHotkeys = new Dictionary<string, string>
		{
			["OverwhelmingLaunch"] = "OverwhelmingLaunchKey",
			["Earthshaker"] = "EarthshakerKey",
			["EitrInfusion"] = "EitrInfusionKey",
			["ShieldThrow"] = "ShieldThrowKey",
			["SpiritAnimal"] = "SpiritAnimalKey"
		};

		private static bool dirty;

		private static bool builtWithLocalization;

		private static Type? elConfig;

		internal static void MarkDirty()
		{
			dirty = true;
		}

		internal static void Patch(Harmony harmony)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			try
			{
				HarmonyMethod val = new HarmonyMethod(typeof(LegendaryGlossary), "MarkDirty", (Type[])null);
				MethodInfo methodInfo = AccessTools.Method(typeof(UniqueLegendaryHelper), "RefreshDerived", (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(AbilityDefinitions), "Initialize", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					Plugin.Log.LogWarning((object)"UniqueLegendaryHelper.RefreshDerived fehlt; Unikate und Sets werden nur beim Start erklärt.");
				}
				if (methodInfo2 != null)
				{
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Unikat-Erklärungen nicht angemeldet: {arg}");
			}
			MarkDirty();
		}

		internal static void Update()
		{
			if (!builtWithLocalization && Localization.instance != null)
			{
				dirty = true;
			}
			if (dirty)
			{
				dirty = false;
				builtWithLocalization = Localization.instance != null;
				Rebuild();
			}
		}

		internal static string UniqueKey(string id)
		{
			return "epicloot.legendary." + SafeId(id);
		}

		internal static string SetKey(string id)
		{
			return "epicloot.set." + SafeId(id);
		}

		private static string SafeId(string id)
		{
			return new string(id.ToLowerInvariant().Select(delegate(char c)
			{
				bool flag;
				switch (c)
				{
				case '-':
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
				case '_':
				case 'a':
				case 'b':
				case 'c':
				case 'd':
				case 'e':
				case 'f':
				case 'g':
				case 'h':
				case 'i':
				case 'j':
				case 'k':
				case 'l':
				case 'm':
				case 'n':
				case 'o':
				case 'p':
				case 'q':
				case 'r':
				case 's':
				case 't':
				case 'u':
				case 'v':
				case 'w':
				case 'x':
				case 'y':
				case 'z':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				return (!flag) ? '_' : c;
			}).ToArray());
		}

		private static void Rebuild()
		{
			try
			{
				if (UniqueLegendaryHelper.AllUniques.Count == 0 && UniqueLegendaryHelper.AllSets.Count == 0)
				{
					return;
				}
				List<LegendaryInfo> uniques = UniqueLegendaryHelper.AllUniques.Values.Where((LegendaryInfo u) => u != null && !string.IsNullOrEmpty(u.ID)).OrderBy<LegendaryInfo, string>((LegendaryInfo u) => u.ID, StringComparer.Ordinal).ToList();
				List<LegendarySetInfo> sets = UniqueLegendaryHelper.AllSets.Values.Where((LegendarySetInfo s) => s != null && !string.IsNullOrEmpty(s.ID)).OrderBy<LegendarySetInfo, string>((LegendarySetInfo s) => s.ID, StringComparer.Ordinal).ToList();
				Api.Unregister("DeepTip_EpicLoot.Legendaer");
				Counts counts = null;
				foreach (string locale in Texts.Locales)
				{
					Counts counts2 = new Counts();
					Api.Register("DeepTip_EpicLoot.Legendaer", Build(Words.For(locale), uniques, sets, Texts.EpicLoot(locale), counts2), locale);
					if (counts == null)
					{
						counts = counts2;
					}
				}
				Counts counts3 = counts ?? new Counts();
				Plugin.Log.LogInfo((object)($"{counts3.Uniques} Unikate, {counts3.Pieces} Set-Teile und {counts3.Sets} Sets erklärt" + ((counts3.Failed > 0) ? $", {counts3.Failed} übersprungen (Fehler oben)" : "")));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Unikate und Sets nicht erklärt: {arg}");
			}
		}

		private static string Build(Words w, List<LegendaryInfo> uniques, List<LegendarySetInfo> sets, Dictionary<string, object?> table, Counts counts)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (LegendaryInfo unique in uniques)
			{
				dictionary[UniqueKey(unique.ID)] = Name(unique.Name, unique.ID, table);
			}
			foreach (LegendarySetInfo set in sets)
			{
				dictionary[SetKey(set.ID)] = Name(set.Name, set.ID, table);
			}
			HashSet<string> hashSet = new HashSet<string>(from g in dictionary.Values.GroupBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase)
				where g.Count() > 1
				select g.Key, StringComparer.OrdinalIgnoreCase);
			foreach (MagicItemEffectDefinition item in MagicItemEffectDefinitions.AllDefinitions.Values.Where((MagicItemEffectDefinition d) => d != null))
			{
				hashSet.Add(EffectGlossary.Name(EffectGlossary.Display(item, table)));
			}
			Dictionary<string, string> titles = new Dictionary<string, string>();
			HashSet<string> own = new HashSet<string>(dictionary.Keys) { "epicloot.legendary.index", "epicloot.set.index" };
			StringBuilder stringBuilder = new StringBuilder("{\n\"$locale\": ");
			stringBuilder.Append(Json.Quote(w.Lang.Code));
			float setChance = SetChance();
			foreach (LegendaryInfo unique2 in uniques)
			{
				try
				{
					string key = UniqueKey(unique2.ID);
					bool num = UniqueLegendaryHelper.IsSetPiece(unique2);
					Entry e = (num ? Piece(w, unique2, dictionary, table, setChance, titles) : Unique(w, unique2, uniques, dictionary, table, titles));
					Append(stringBuilder, key, e, dictionary[key], hashSet, own);
					if (num)
					{
						counts.Pieces++;
					}
					else
					{
						counts.Uniques++;
					}
				}
				catch (Exception ex)
				{
					counts.Failed++;
					Plugin.Log.LogWarning((object)("Unikat " + unique2.ID + " nicht erklärt: " + ex.Message));
				}
			}
			foreach (LegendarySetInfo set2 in sets)
			{
				try
				{
					string key2 = SetKey(set2.ID);
					Append(stringBuilder, key2, Set(w, set2, dictionary, table, titles), dictionary[key2], hashSet, own);
					counts.Sets++;
				}
				catch (Exception ex2)
				{
					counts.Failed++;
					Plugin.Log.LogWarning((object)("Set " + set2.ID + " nicht erklärt: " + ex2.Message));
				}
			}
			Append(stringBuilder, "epicloot.legendary.index", UniqueIndex(w, uniques, dictionary), null, hashSet, own);
			Append(stringBuilder, "epicloot.set.index", SetIndex(w, sets, dictionary), null, hashSet, own);
			return stringBuilder.Append("\n}").ToString();
		}

		private static void Append(StringBuilder json, string key, Entry e, string? alias, HashSet<string> shared, HashSet<string> own)
		{
			json.Append(",\n").Append(Json.Quote(key)).Append(": {\"title\": ")
				.Append(Json.Quote(e.Title));
			json.Append(", \"short\": ").Append(Json.Quote(Clamp(e.Short)));
			json.Append(", \"body\": ").Append(Json.Quote(e.Body));
			if (alias != null && alias.Length >= 4 && !shared.Contains(alias))
			{
				json.Append(", \"aliases\": [").Append(Json.Quote(alias)).Append(']');
			}
			List<string> list = e.See.Where((string k) => own.Contains(k) || Api.Has(k)).Distinct().ToList();
			if (list.Count > 0)
			{
				json.Append(", \"see\": [").Append(string.Join(", ", list.Select(Json.Quote))).Append(']');
			}
			json.Append('}');
		}

		private static Entry Unique(Words w, LegendaryInfo u, List<LegendaryInfo> all, Dictionary<string, string> names, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			string text = names[UniqueKey(u.ID)];
			List<ItemRarity> tiers = UniqueLegendaryHelper.GetRarities(u);
			string text2 = Tiers(tiers, w);
			string text3 = Basis(u.Requirements, w);
			List<string> list = GuaranteedTitles(u, table, titles);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format(w.UniqueIntro, Esc(text), text2)).Append('\n');
			stringBuilder.Append("**" + w.Base + "** ").Append(Esc(text3)).Append('.');
			stringBuilder.Append("\n\n").Append(Guaranteed(w, u, tiers, table, titles));
			stringBuilder.Append('\n').Append(EffectCount(w, u, tiers)).Append(Looks(w, u));
			string signature = Signature(u.Requirements);
			List<string> list2 = (from o in all
				where o != u && !UniqueLegendaryHelper.IsSetPiece(o) && Signature(o.Requirements) == signature && UniqueLegendaryHelper.GetRarities(o).Intersect(tiers).Any()
				select "[[" + UniqueKey(o.ID) + "|" + Label(names[UniqueKey(o.ID)]) + "]]").ToList();
			if (list2.Count > 0)
			{
				stringBuilder.Append(' ').Append(string.Format(w.Rivals, string.Join(", ", list2)));
			}
			return new Entry
			{
				Title = text,
				Short = string.Format(w.UniqueShort, text2, text3, (list.Count > 0) ? string.Join(", ", list) : w.None),
				Body = stringBuilder.ToString(),
				See = new List<string> { "epicloot.unique", "epicloot.unique.roll", "epicloot.unique.guaranteed", "epicloot.legendary.index" }
			};
		}

		private static Entry Piece(Words w, LegendaryInfo u, Dictionary<string, string> names, Dictionary<string, object?> table, float setChance, Dictionary<string, string> titles)
		{
			string text = names[UniqueKey(u.ID)];
			string text2 = UniqueLegendaryHelper.GetSetForLegendaryItem(u) ?? "";
			string text3 = SetKey(text2);
			string value;
			string text4 = (names.TryGetValue(text3, out value) ? value : text2);
			List<ItemRarity> rarities = UniqueLegendaryHelper.GetRarities(u);
			string text5 = Tiers(rarities, w);
			string text6 = Basis(u.Requirements, w);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format(w.PieceIntro, Esc(text), "[[" + text3 + "|" + Label(text4) + "]]", text5)).Append('\n');
			stringBuilder.Append("**" + w.Base + "** ").Append(Esc(text6)).Append('.');
			stringBuilder.Append("\n\n").Append(Guaranteed(w, u, rarities, table, titles));
			stringBuilder.Append('\n').Append(EffectCount(w, u, rarities)).Append(Looks(w, u));
			stringBuilder.Append("\n\n").Append(string.Format(w.PieceRoll, Pct(setChance, w)));
			return new Entry
			{
				Title = text,
				Short = string.Format(w.PieceShort, text4, text5, text6),
				Body = stringBuilder.ToString(),
				See = new List<string> { text3, "epicloot.sets", "epicloot.sets.roll", "epicloot.unique.guaranteed" }
			};
		}

		private static Entry Set(Words w, LegendarySetInfo s, Dictionary<string, string> names, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			string title = names[SetKey(s.ID)];
			List<ItemRarity> rarities = UniqueLegendaryHelper.GetRarities(s);
			string text = Tiers(rarities, w);
			int fullSetCount = UniqueLegendaryHelper.GetFullSetCount(s);
			List<string> list = (s.LegendaryIDs ?? new List<string>()).Where((string id) => !string.IsNullOrEmpty(id)).Distinct().ToList();
			List<SetBonusInfo> list2 = (from b in s.SetBonuses ?? new List<SetBonusInfo>()
				where b?.Effect != null && !string.IsNullOrEmpty(b.Effect.Type)
				orderby b.Count
				select b).ToList();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format(w.SetPieces, fullSetCount));
			foreach (string item in list)
			{
				stringBuilder.Append("\n- ");
				if (UniqueLegendaryHelper.AllUniques.TryGetValue(item, out var value) && value != null)
				{
					stringBuilder.Append("[[" + UniqueKey(item) + "|" + Label(names[UniqueKey(item)]) + "]] – ").Append(Esc(Basis(value.Requirements, w)));
				}
				else
				{
					stringBuilder.Append(Esc(item)).Append(' ').Append(w.Undefined);
				}
			}
			stringBuilder.Append("\n\n").Append(w.SetBonuses);
			foreach (SetBonusInfo item2 in list2)
			{
				stringBuilder.Append("\n- ").Append(string.Format(w.From, item2.Count)).Append(' ')
					.Append(Bonus(w, item2, rarities, table, titles));
			}
			string text2 = SetLooks(w, s);
			bool flag = fullSetCount > 0 && list.Count > fullSetCount;
			if (text2.Length > 0 || flag)
			{
				stringBuilder.Append("\n\n").Append(text2);
				if (flag)
				{
					stringBuilder.Append((text2.Length > 0) ? " " : "").Append(string.Format(w.MorePieces, list.Count, fullSetCount));
				}
			}
			string text3 = string.Join(", ", list2.Select((SetBonusInfo b) => b.Count).Distinct());
			Entry entry = new Entry();
			entry.Title = title;
			entry.Short = string.Format(w.SetShort, list.Count, text, fullSetCount, (text3.Length > 0) ? text3 : "–");
			entry.Body = stringBuilder.ToString();
			entry.See = new List<string> { "epicloot.sets", "epicloot.sets.bonus", "epicloot.sets.count", "epicloot.sets.values", "epicloot.set.index" };
			return entry;
		}

		private static Entry UniqueIndex(Words w, List<LegendaryInfo> uniques, Dictionary<string, string> names)
		{
			List<LegendaryInfo> list = uniques.Where((LegendaryInfo u) => !UniqueLegendaryHelper.IsSetPiece(u)).OrderBy<LegendaryInfo, string>((LegendaryInfo u) => names[UniqueKey(u.ID)], StringComparer.CurrentCultureIgnoreCase).ToList();
			StringBuilder stringBuilder = new StringBuilder(string.Format(w.UniqueIndexIntro, list.Count));
			foreach (LegendaryInfo item in list)
			{
				stringBuilder.Append("\n- ").Append("[[" + UniqueKey(item.ID) + "|" + Label(names[UniqueKey(item.ID)]) + "]] – ").Append(Esc(Basis(item.Requirements, w)))
					.Append(" (")
					.Append(Tiers(UniqueLegendaryHelper.GetRarities(item), w))
					.Append(')');
			}
			return new Entry
			{
				Title = w.UniqueIndexTitle,
				Short = string.Format(w.UniqueIndexShort, list.Count),
				Body = stringBuilder.ToString(),
				See = new List<string> { "epicloot.unique", "epicloot.set.index" }
			};
		}

		private static Entry SetIndex(Words w, List<LegendarySetInfo> sets, Dictionary<string, string> names)
		{
			StringBuilder stringBuilder = new StringBuilder(string.Format(w.SetIndexIntro, sets.Count));
			foreach (LegendarySetInfo item in sets.OrderBy<LegendarySetInfo, string>((LegendarySetInfo s) => names[SetKey(s.ID)], StringComparer.CurrentCultureIgnoreCase))
			{
				int num = (item.LegendaryIDs ?? new List<string>()).Where((string id) => !string.IsNullOrEmpty(id)).Distinct().Count();
				stringBuilder.Append("\n- ").Append("[[" + SetKey(item.ID) + "|" + Label(names[SetKey(item.ID)]) + "]] – ").Append(string.Format(w.SetIndexLine, num, Tiers(UniqueLegendaryHelper.GetRarities(item), w)));
			}
			return new Entry
			{
				Title = w.SetIndexTitle,
				Short = string.Format(w.SetIndexShort, sets.Count),
				Body = stringBuilder.ToString(),
				See = new List<string> { "epicloot.sets", "epicloot.legendary.index" }
			};
		}

		private static string Guaranteed(Words w, LegendaryInfo u, List<ItemRarity> tiers, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			List<GuaranteedMagicEffect> list = (u.GuaranteedMagicEffects ?? new List<GuaranteedMagicEffect>()).Where((GuaranteedMagicEffect g) => g != null && !string.IsNullOrEmpty(g.Type)).ToList();
			if (list.Count == 0)
			{
				return "**" + w.GuaranteedLabel + "** " + w.None + ".";
			}
			StringBuilder stringBuilder = new StringBuilder("**" + w.GuaranteedLabel + "**");
			MagicItemEffectDefinition val = default(MagicItemEffectDefinition);
			foreach (GuaranteedMagicEffect item in list)
			{
				stringBuilder.Append("\n- ");
				if (!MagicItemEffectDefinitions.TryGet(item.Type, ref val))
				{
					stringBuilder.Append(Esc(item.Type));
					continue;
				}
				string text = EffectGlossary.Display(val, table);
				stringBuilder.Append("[[" + EffectGlossary.Key(val.Type) + "|" + Label(EffectTitle(val, table, titles)) + "]]");
				if (!text.Contains("{"))
				{
					continue;
				}
				bool percent = text.Contains("%");
				List<string> list2 = new List<string>();
				List<string> list3 = new List<string>();
				foreach (ItemRarity tier in tiers)
				{
					ValueDef rollRange = MagicItemEffectDefinitions.GetRollRange(val, tier, u.ID, (ValueDef)null);
					if (rollRange != null)
					{
						string text2 = Range(rollRange.MinValue, rollRange.MaxValue, percent, w);
						list3.Add(text2);
						list2.Add(w.Lang.Rarities[tier] + " " + text2);
					}
				}
				if (list2.Count > 0)
				{
					stringBuilder.Append(": ").Append((list3.Distinct().Count() == 1 && list2.Count > 1) ? (list3[0] + " " + w.AllTiers) : string.Join(", ", list2));
				}
			}
			return stringBuilder.ToString();
		}

		private static string EffectCount(Words w, LegendaryInfo u, List<ItemRarity> tiers)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			if (u.GuaranteedEffectCount > 0)
			{
				return "**" + w.CountLabel + "** " + string.Format(w.CountFixed, u.GuaranteedEffectCount) + ".";
			}
			int num = u.GuaranteedMagicEffects?.Count((GuaranteedMagicEffect g) => g != null) ?? 0;
			List<string> list = new List<string>();
			int num2 = int.MaxValue;
			foreach (ItemRarity tier in tiers)
			{
				List<int> list2;
				try
				{
					list2 = (from c in LootRoller.GetEffectCountsPerRarity(tier, false)
						where c.Value > 0f
						select c.Key).ToList();
				}
				catch (Exception)
				{
					continue;
				}
				if (list2.Count != 0)
				{
					num2 = Math.Min(num2, list2.Min());
					list.Add(w.Lang.Rarities[tier] + " " + ((list2.Min() == list2.Max()) ? list2.Min().ToString() : (list2.Min() + "–" + list2.Max())));
				}
			}
			string text = "**" + w.CountLabel + "** " + ((list.Count > 0) ? string.Format(w.CountRolled, string.Join(", ", list)) : w.CountRolledPlain);
			if (num > 0 && num > ((num2 != int.MaxValue) ? num2 : 0))
			{
				text += string.Format(w.CountAtLeast, num);
			}
			return text + ".";
		}

		private static string Looks(Words w, LegendaryInfo u)
		{
			string text = "";
			if (!string.IsNullOrEmpty(u.EquipFx))
			{
				text = text + " " + w.Aura;
			}
			List<TextureReplacement> textureReplacements = u.TextureReplacements;
			if (textureReplacements != null && textureReplacements.Count > 0)
			{
				text = text + " " + w.Texture;
			}
			return text;
		}

		private unsafe static string Bonus(Words w, SetBonusInfo bonus, List<ItemRarity> tiers, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Invalid comparison between Unknown and I4
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Invalid comparison between Unknown and I4
			MagicItemEffectDefinition val = default(MagicItemEffectDefinition);
			if (!MagicItemEffectDefinitions.TryGet(bonus.Effect.Type, ref val))
			{
				return Esc(bonus.Effect.Type);
			}
			StringBuilder stringBuilder = new StringBuilder("[[" + EffectGlossary.Key(val.Type) + "|" + Label(EffectTitle(val, table, titles)) + "]]");
			if (EffectGlossary.Display(val, table).Contains("{") && tiers.Count > 0)
			{
				List<float> values = tiers.Select((ItemRarity r) => SetBonusEvaluator.GetBonusValue(bonus, r)).ToList();
				stringBuilder.Append(", X = ");
				stringBuilder.Append((values.Distinct().Count() == 1) ? EffectGlossary.Num(values[0], w.Lang) : string.Join(" / ", tiers.Select((ItemRarity r, int i) => w.Lang.Rarities[r] + " " + EffectGlossary.Num(values[i], w.Lang))));
			}
			AbilityDefinition val2 = default(AbilityDefinition);
			string value3;
			if (!string.IsNullOrEmpty(val.Ability) && AbilityDefinitions.TryGetAbilityDef(val.Ability, ref val2) && val2 != null)
			{
				string text = "epicloot.ability." + SafeId(val2.ID);
				string value = "[[" + (Api.Has(text) ? text : "epicloot.ability") + "|" + w.Ability + "]]";
				AbilityActivationMode activationMode = val2.ActivationMode;
				string text2 = (((int)activationMode == 1) ? w.ByItself : (((int)activationMode != 2) ? w.Passive : string.Format(w.OnKey, AbilityKeys())));
				string value2 = text2;
				stringBuilder.Append("; ").Append(value).Append(", ")
					.Append(value2);
				if (val2.Cooldown > 0f)
				{
					stringBuilder.Append(", ").Append(string.Format(w.Cooldown, Duration(val2.Cooldown, w)));
				}
			}
			else if (SetHotkeys.TryGetValue(val.Type, out value3))
			{
				string arg = ((ConfigValue(value3) is KeyCode val3 && (int)val3 != 0) ? ((object)(*(KeyCode*)(&val3))/*cast due to .constrained prefix*/).ToString() : w.NoKey);
				stringBuilder.Append("; ").Append(string.Format(w.OwnKey, arg));
			}
			return stringBuilder.ToString();
		}

		private static string SetLooks(Words w, LegendarySetInfo s)
		{
			List<string> list = new List<string>();
			if (s.ArmorTint != null)
			{
				list.Add(w.LookTint);
			}
			if (s.WeaponGlow != null)
			{
				list.Add(w.LookWeapon);
			}
			if (s.HandGlow != null)
			{
				list.Add(w.LookHands);
			}
			if (s.DrawArrow != null)
			{
				list.Add(w.LookArrow);
			}
			if (s.BodyAura != null)
			{
				list.Add(w.LookAura);
			}
			List<string> fullSetFx = s.FullSetFx;
			if (fullSetFx != null && fullSetFx.Count > 0)
			{
				list.Add(w.LookParticles);
			}
			if (list.Count != 0)
			{
				return string.Format(w.Looks, string.Join(", ", list));
			}
			return "";
		}

		private unsafe static string Basis(MagicItemEffectRequirements? q, Words w)
		{
			if (q == null)
			{
				return w.AnyItem;
			}
			EffectGlossary.Lang lang = w.Lang;
			List<string> list = new List<string>();
			List<string> allowedItemTypes = q.AllowedItemTypes;
			string text = ((allowedItemTypes != null && allowedItemTypes.Count > 0) ? string.Join(", ", q.AllowedItemTypes.Distinct().Select(lang.ItemType)) : "");
			List<SkillType> allowedSkillTypes = q.AllowedSkillTypes;
			string text2 = ((allowedSkillTypes != null && allowedSkillTypes.Count > 0) ? string.Join(", ", from t in q.AllowedSkillTypes.Distinct()
				select lang.Skill(((object)(*(SkillType*)(&t))/*cast due to .constrained prefix*/).ToString())) : "");
			if (text.Length > 0)
			{
				list.Add((text2.Length > 0) ? (text + " (" + text2 + ")") : text);
			}
			else if (text2.Length > 0)
			{
				list.Add(string.Format(lang.SkillWeapons, text2));
			}
			List<string> allowedItemNames = q.AllowedItemNames;
			if (allowedItemNames != null && allowedItemNames.Count > 0)
			{
				list.Add(string.Join(", ", q.AllowedItemNames.Select(ItemName)));
			}
			List<ItemRarity> allowedRarities = q.AllowedRarities;
			if (allowedRarities != null && allowedRarities.Count > 0)
			{
				list.Add(string.Format(lang.OnlyRarity, string.Join(", ", q.AllowedRarities.Select((ItemRarity r) => lang.Rarities[r]))));
			}
			FieldInfo[] fields = typeof(MagicItemEffectRequirements).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(bool?) && fieldInfo.GetValue(q) is bool value)
				{
					list.Add(lang.Flag(fieldInfo.Name, value));
				}
			}
			string text3 = ((list.Count > 0) ? string.Join("; ", list) : w.AnyItem);
			List<string> list2 = new List<string>();
			List<string> excludedItemTypes = q.ExcludedItemTypes;
			if (excludedItemTypes != null && excludedItemTypes.Count > 0)
			{
				list2.Add(string.Join(", ", q.ExcludedItemTypes.Select(lang.ItemType)));
			}
			List<SkillType> excludedSkillTypes = q.ExcludedSkillTypes;
			if (excludedSkillTypes != null && excludedSkillTypes.Count > 0)
			{
				list2.Add(string.Join(", ", q.ExcludedSkillTypes.Select((SkillType t) => lang.Skill(((object)(*(SkillType*)(&t))/*cast due to .constrained prefix*/).ToString()))));
			}
			List<string> excludedItemNames = q.ExcludedItemNames;
			if (excludedItemNames != null && excludedItemNames.Count > 0)
			{
				list2.Add(string.Join(", ", q.ExcludedItemNames.Select(ItemName)));
			}
			if (list2.Count <= 0)
			{
				return text3;
			}
			return text3 + "; " + w.Except + " " + string.Join(", ", list2);
		}

		private static string Signature(MagicItemEffectRequirements? q)
		{
			if (q == null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			FieldInfo[] fields = typeof(MagicItemEffectRequirements).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				object value = fieldInfo.GetValue(q);
				string text = ((value == null) ? "" : ((value is string text2) ? text2 : ((!(value is IEnumerable source)) ? value.ToString() : string.Join(",", (from object o in source
					select o?.ToString() ?? "").OrderBy<string, string>((string x) => x, StringComparer.Ordinal)))));
				string value2 = text;
				stringBuilder.Append(fieldInfo.Name).Append('=').Append(value2)
					.Append(';');
			}
			return stringBuilder.ToString();
		}

		private static string ItemName(string item)
		{
			if (item.StartsWith("$") && Localization.instance != null)
			{
				string text = Localization.instance.Localize(item);
				if (!text.StartsWith("["))
				{
					return text;
				}
				return item;
			}
			return EffectGlossary.ItemName(item);
		}

		private static string Name(string? token, string id, Dictionary<string, object?> table)
		{
			if (string.IsNullOrEmpty(token))
			{
				return id;
			}
			string text = EffectGlossary.Lookup(table, token);
			if (string.IsNullOrEmpty(text) && Localization.instance != null)
			{
				string text2 = Localization.instance.Localize(token);
				text = (text2.StartsWith("[") ? null : text2);
			}
			if (string.IsNullOrEmpty(text))
			{
				text = (token.StartsWith("$") ? id : token);
			}
			string text3 = EffectGlossary.Plain(text);
			if (text3.Length <= 0)
			{
				return id;
			}
			return text3;
		}

		private static string EffectTitle(MagicItemEffectDefinition def, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			if (!titles.TryGetValue(def.Type, out string value))
			{
				value = EffectGlossary.Title(EffectGlossary.Display(def, table));
				titles[def.Type] = value;
			}
			return value;
		}

		private static List<string> GuaranteedTitles(LegendaryInfo u, Dictionary<string, object?> table, Dictionary<string, string> titles)
		{
			MagicItemEffectDefinition def = default(MagicItemEffectDefinition);
			return (from g in u.GuaranteedMagicEffects ?? new List<GuaranteedMagicEffect>()
				where g != null && !string.IsNullOrEmpty(g.Type)
				select (!MagicItemEffectDefinitions.TryGet(g.Type, ref def)) ? g.Type : EffectTitle(def, table, titles)).ToList();
		}

		private static string Tiers(List<ItemRarity> tiers, Words w)
		{
			if (tiers.Count != 0)
			{
				return string.Join(", ", tiers.Select((ItemRarity r) => w.Lang.Rarities[r]));
			}
			return "–";
		}

		private static string Range(float min, float max, bool percent, Words w)
		{
			string text = (Mathf.Approximately(min, max) ? EffectGlossary.Num(min, w.Lang) : (EffectGlossary.Num(min, w.Lang) + "–" + EffectGlossary.Num(max, w.Lang)));
			if (!percent)
			{
				return text;
			}
			return w.Lang.Pct(text);
		}

		private static string Pct(float fraction, Words w)
		{
			return w.Lang.Pct(EffectGlossary.Num(fraction * 100f, w.Lang));
		}

		private static string Duration(float seconds, Words w)
		{
			if (!(seconds >= 120f) || !Mathf.Approximately(seconds % 60f, 0f))
			{
				return string.Format(w.Seconds, EffectGlossary.Num(seconds, w.Lang));
			}
			return string.Format(w.Minutes, EffectGlossary.Num(seconds / 60f, w.Lang));
		}

		private static string AbilityKeys()
		{
			try
			{
				if ((object)elConfig == null)
				{
					elConfig = AccessTools.TypeByName("EpicLoot.Config.ELConfig");
				}
				if (elConfig != null && AccessTools.Field(elConfig, "AbilityKeyCodes")?.GetValue(null) is Array source)
				{
					List<string> list = (from k in source.Cast<object>().Select(delegate(object e)
						{
							object obj = ((e is ConfigEntryBase) ? e : null);
							return ((obj != null) ? ((ConfigEntryBase)obj).BoxedValue : null) as string;
						})
						where !string.IsNullOrEmpty(k)
						select k.ToUpperInvariant()).ToList();
					if (list.Count > 0)
					{
						return string.Join("/", list);
					}
				}
			}
			catch (Exception)
			{
			}
			return "G/H/J";
		}

		private static object? ConfigValue(string field)
		{
			try
			{
				if ((object)elConfig == null)
				{
					elConfig = AccessTools.TypeByName("EpicLoot.Config.ELConfig");
				}
				object result;
				if (!(elConfig == null))
				{
					object? obj = AccessTools.Field(elConfig, field)?.GetValue(null);
					object? obj2 = ((obj is ConfigEntryBase) ? obj : null);
					result = ((obj2 != null) ? ((ConfigEntryBase)obj2).BoxedValue : null);
				}
				else
				{
					result = null;
				}
				return result;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static float SetChance()
		{
			object obj = ConfigValue("SetItemDropChance");
			if (obj is float)
			{
				return (float)obj;
			}
			return 0.15f;
		}

		private static string Label(string text)
		{
			return text.Replace('[', '(').Replace(']', ')').Replace('|', '/');
		}

		private static string Esc(string text)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			foreach (char value in text)
			{
				if ("\\*`[]{}|".IndexOf(value) >= 0)
				{
					stringBuilder.Append('\\');
				}
				stringBuilder.Append(value);
			}
			return stringBuilder.ToString();
		}

		private static string Clamp(string text)
		{
			string text2 = EffectGlossary.Plain(text);
			if (text2.Length > 140)
			{
				return text2.Substring(0, 137).TrimEnd() + "…";
			}
			return text2;
		}
	}
	[BepInPlugin("gorduan.deeptip.epicloot", "DeepTip EpicLoot", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "gorduan.deeptip.epicloot";

		public const string Name = "DeepTip EpicLoot";

		public const string Version = "1.2.0";

		private const string EpicLootGuid = "randyknapp.mods.epicloot";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing