Decompiled source of Utangard v1.4.0

plugins/Utangard/Utangard.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Outside the fence: a biome you have not earned will not sustain you.")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: AssemblyInformationalVersion("1.4.0+fb613c5505482ae4557e77d64c651dd8f0eb6ece")]
[assembly: AssemblyProduct("Utangard")]
[assembly: AssemblyTitle("Utangard")]
[assembly: AssemblyVersion("1.4.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 Utangard
{
	internal static class BiomeGate
	{
		private static readonly Vector2[] Compass = (Vector2[])(object)new Vector2[8]
		{
			new Vector2(1f, 0f),
			new Vector2(0.7071f, 0.7071f),
			new Vector2(0f, 1f),
			new Vector2(-0.7071f, 0.7071f),
			new Vector2(-1f, 0f),
			new Vector2(-0.7071f, -0.7071f),
			new Vector2(0f, -1f),
			new Vector2(0.7071f, -0.7071f)
		};

		private static Vector3 _sampledAt;

		private static float _sampledMargin = -1f;

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

		private static readonly List<Biome> _sampledBiomes = new List<Biome>(2);

		private const float ResampleDistance = 0.25f;

		public static string GatingKey(Player player)
		{
			Biome biome;
			return GatingKey(player, out biome);
		}

		public static string GatingKey(Player player, out Biome biome)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected I4, but got Unknown
			biome = (Biome)0;
			if ((Object)(object)player == (Object)null || !UtangardConfig.Enabled.Value)
			{
				return null;
			}
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return null;
			}
			if (!Seams.PenaltyIsEscapable())
			{
				return null;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			Biome currentBiome = player.GetCurrentBiome();
			string text = UtangardConfig.RequiredKeyFor(currentBiome);
			if (text != null && !Earned(instance, text))
			{
				biome = (Biome)(int)currentBiome;
				return text;
			}
			return NearbyGatingKey(player, instance, out biome);
		}

		public static bool RulingBiomes(Player player, List<Biome> into)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			into.Clear();
			if ((Object)(object)player == (Object)null || !UtangardConfig.Enabled.Value)
			{
				return false;
			}
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!Seams.PenaltyIsEscapable())
			{
				return false;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Biome currentBiome = player.GetCurrentBiome();
			string text = UtangardConfig.RequiredKeyFor(currentBiome);
			if (text != null && !Earned(instance, text))
			{
				into.Add(currentBiome);
			}
			if (SampleMargin(player))
			{
				for (int i = 0; i < _sampledKeys.Count; i++)
				{
					if (!into.Contains(_sampledBiomes[i]) && !Earned(instance, _sampledKeys[i]))
					{
						into.Add(_sampledBiomes[i]);
					}
				}
			}
			return into.Count > 0;
		}

		public static bool IsWithered(Player player)
		{
			return GatingKey(player) != null;
		}

		public static string BlockersHere(Player player)
		{
			if (!UtangardConfig.GateOnGroup.Value)
			{
				return null;
			}
			string text = GatingKey(player);
			if (text != null)
			{
				return Progression.BlockersFor(text, excludeSelf: true);
			}
			return null;
		}

		public static bool Earned(ZoneSystem zone, string key)
		{
			if (!UtangardConfig.GateOnGroup.Value)
			{
				return zone.GetGlobalKey(key);
			}
			return Progression.GroupHasKey(key);
		}

		private static string NearbyGatingKey(Player player, ZoneSystem zone, out Biome biome)
		{
			biome = (Biome)0;
			if (!SampleMargin(player))
			{
				return null;
			}
			return FirstUnearned(zone, out biome);
		}

		private static bool SampleMargin(Player player)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			float value = UtangardConfig.BorderMargin.Value;
			if (value <= 0f)
			{
				return false;
			}
			Vector3 position = ((Component)player).transform.position;
			if (value == _sampledMargin)
			{
				Vector3 val = position - _sampledAt;
				if (((Vector3)(ref val)).sqrMagnitude < 0.0625f)
				{
					return true;
				}
			}
			_sampledAt = position;
			_sampledMargin = value;
			_sampledKeys.Clear();
			_sampledBiomes.Clear();
			for (int i = 0; i < Compass.Length; i++)
			{
				Biome val2 = Heightmap.FindBiome(new Vector3(position.x + Compass[i].x * value, position.y, position.z + Compass[i].y * value));
				string text = UtangardConfig.RequiredKeyFor(val2);
				if (text != null && !_sampledBiomes.Contains(val2))
				{
					_sampledKeys.Add(text);
					_sampledBiomes.Add(val2);
				}
			}
			return true;
		}

		private static string FirstUnearned(ZoneSystem zone, out Biome biome)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			for (int i = 0; i < _sampledKeys.Count; i++)
			{
				if (!Earned(zone, _sampledKeys[i]))
				{
					biome = (Biome)(int)_sampledBiomes[i];
					return _sampledKeys[i];
				}
			}
			biome = (Biome)0;
			return null;
		}
	}
	internal static class BlockedEffects
	{
		private const string GuardianPrefix = "GP_";

		private static readonly HashSet<int> Blocked = new HashSet<int>();

		private static bool _built;

		public static void Rebuild()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			Blocked.Clear();
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null)
				{
					StatusEffect consumeStatusEffect = component.m_itemData.m_shared.m_consumeStatusEffect;
					if ((Object)(object)consumeStatusEffect != (Object)null)
					{
						Add(consumeStatusEffect);
					}
				}
			}
			foreach (StatusEffect statusEffect in instance.m_StatusEffects)
			{
				if (!((Object)(object)statusEffect == (Object)null) && ((Object)statusEffect).name != null && ((Object)statusEffect).name.StartsWith("GP_"))
				{
					Add(statusEffect);
				}
			}
			if (UtangardConfig.BlockRested.Value)
			{
				Blocked.Add(SEMan.s_statusEffectRested);
				Blocked.Add(SEMan.s_statusEffectResting);
			}
			foreach (string alsoBlockName in UtangardConfig.AlsoBlockNames)
			{
				Blocked.Add(StringExtensionMethods.GetStableHashCode(alsoBlockName));
			}
			foreach (string neverBlockName in UtangardConfig.NeverBlockNames)
			{
				Blocked.Remove(StringExtensionMethods.GetStableHashCode(neverBlockName));
			}
			_built = true;
			UtangardPlugin.Log.LogInfo((object)("Buff set rebuilt: " + Blocked.Count + " status effects."));
			if (UtangardConfig.LogBlockedEffects.Value)
			{
				LogNames(instance);
			}
		}

		private static void Add(StatusEffect effect)
		{
			if (!UtangardConfig.NeverBlockNames.Contains(((Object)effect).name))
			{
				Blocked.Add(effect.NameHash());
			}
		}

		public static bool IsBlocked(StatusEffect effect)
		{
			if (!_built || (Object)(object)effect == (Object)null)
			{
				return false;
			}
			if (effect is UtangardMarker || effect is SappedEffect)
			{
				return false;
			}
			return Blocked.Contains(effect.NameHash());
		}

		public static bool IsBlockedHash(int nameHash)
		{
			if (!_built)
			{
				return false;
			}
			if (nameHash == UtangardEffectsRegistry.MarkerHash)
			{
				return false;
			}
			if (nameHash == UtangardEffectsRegistry.SappedHash)
			{
				return false;
			}
			return Blocked.Contains(nameHash);
		}

		private static void LogNames(ObjectDB db)
		{
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			foreach (StatusEffect statusEffect in db.m_StatusEffects)
			{
				if (!((Object)(object)statusEffect == (Object)null))
				{
					(Blocked.Contains(statusEffect.NameHash()) ? list : list2).Add(((Object)statusEffect).name);
				}
			}
			list.Sort();
			list2.Sort();
			UtangardPlugin.Log.LogInfo((object)"Treated as buffs (blocked and drained):");
			UtangardPlugin.Log.LogInfo((object)("  " + Join(list)));
			UtangardPlugin.Log.LogInfo((object)"Left alone:");
			UtangardPlugin.Log.LogInfo((object)("  " + Join(list2)));
		}

		private static string Join(List<string> names)
		{
			if (names.Count == 0)
			{
				return "(none)";
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < names.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(names[i]);
			}
			return stringBuilder.ToString();
		}
	}
	internal static class BossAltars
	{
		private static bool _saidFailure;

		internal static string Unearned(Vector3 position)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!UtangardConfig.Enabled.Value || !UtangardConfig.BlockBossSummons.Value)
			{
				return null;
			}
			if (!Seams.PenaltyIsEscapable())
			{
				return null;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			string text = UtangardConfig.RequiredKeyFor(Heightmap.FindBiome(position));
			if (text == null || BiomeGate.Earned(instance, text))
			{
				return null;
			}
			return text;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(OfferingBowl), "InitiateSpawnBoss")]
		private static bool Refuse(OfferingBowl __instance)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return true;
				}
				string text = Unearned(((Component)__instance).transform.position);
				if (text == null)
				{
					return true;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).Message((MessageType)2, UtangardConfig.BossBlockedMessage.Value, 0, (Sprite)null, false);
				}
				if (UtangardConfig.Verbose.Value)
				{
					UtangardPlugin.Log.LogInfo((object)("Refused an offering at " + ((Object)__instance).name + ": its biome needs " + text + ", and the group has not earned it."));
				}
				return false;
			}
			catch (Exception ex)
			{
				if (!_saidFailure)
				{
					_saidFailure = true;
					UtangardPlugin.Log.LogWarning((object)("Could not check a boss altar against the gate, so it is allowed: " + ex));
				}
				return true;
			}
		}
	}
	internal static class BossDoors
	{
		private static bool _saidFailure;

		private static string _parsedFrom;

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

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Door), "Interact")]
		private static bool RefuseInteract(Door __instance, Humanoid __0, bool __1, ref bool __result)
		{
			if (__1)
			{
				return true;
			}
			if (!Refused(__instance, __0))
			{
				return true;
			}
			__result = true;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Door), "UseItem")]
		private static bool RefuseUseItem(Door __instance, Humanoid __0, ItemData __1, ref bool __result)
		{
			if ((Object)(object)__instance == (Object)null || __1 == null || __1.m_shared == null || (Object)(object)__instance.m_keyItem == (Object)null)
			{
				return true;
			}
			if (__1.m_shared.m_name != __instance.m_keyItem.m_itemData.m_shared.m_name)
			{
				return true;
			}
			if (!Refused(__instance, __0))
			{
				return true;
			}
			__result = true;
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static void ReportGuarded(ZNetScene __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || __instance.m_prefabs == null || string.IsNullOrEmpty((UtangardConfig.BossDoorKeys.Value ?? "").Trim()))
				{
					return;
				}
				List<string> list = new List<string>();
				Door val = default(Door);
				foreach (GameObject prefab in __instance.m_prefabs)
				{
					if (!((Object)(object)prefab == (Object)null) && prefab.TryGetComponent<Door>(ref val) && !((Object)(object)val.m_keyItem == (Object)null) && Guarded(val.m_keyItem))
					{
						list.Add(((Object)prefab).name + " (" + ((Object)((Component)val.m_keyItem).gameObject).name + ")");
					}
				}
				if (list.Count > 0)
				{
					UtangardPlugin.Log.LogInfo((object)("Boss doors kept sealed in a locked biome: " + string.Join(", ", list.ToArray())));
				}
				else
				{
					UtangardPlugin.Log.LogWarning((object)("BossDoorKeys (" + UtangardConfig.BossDoorKeys.Value + ") matches no door in this world, so no boss door is guarded."));
				}
			}
			catch (Exception ex)
			{
				UtangardPlugin.Log.LogWarning((object)("Could not list the boss doors: " + ex.Message));
			}
		}

		private static bool Refused(Door door, Humanoid who)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)door == (Object)null || (Object)(object)door.m_keyItem == (Object)null)
				{
					return false;
				}
				if (!Guarded(door.m_keyItem))
				{
					return false;
				}
				ZNetView val = default(ZNetView);
				if (!((Component)door).TryGetComponent<ZNetView>(ref val) || !val.IsValid())
				{
					return false;
				}
				if (val.GetZDO().GetInt(ZDOVars.s_state, 0) != 0)
				{
					return false;
				}
				string text = BossAltars.Unearned(((Component)door).transform.position);
				if (text == null)
				{
					return false;
				}
				door.m_lockedEffects.Create(((Component)door).transform.position, ((Component)door).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				if ((Object)(object)who != (Object)null && (Object)(object)who == (Object)(object)Player.m_localPlayer)
				{
					((Character)who).Message((MessageType)2, UtangardConfig.BossDoorBlockedMessage.Value, 0, (Sprite)null, false);
				}
				if (UtangardConfig.Verbose.Value)
				{
					UtangardPlugin.Log.LogInfo((object)("Kept " + ((Object)door).name + " shut: its biome needs " + text + ", and the group has not earned it."));
				}
				return true;
			}
			catch (Exception ex)
			{
				if (!_saidFailure)
				{
					_saidFailure = true;
					UtangardPlugin.Log.LogWarning((object)("Could not check a boss door against the gate, so it opens: " + ex));
				}
				return false;
			}
		}

		private static bool Guarded(ItemDrop keyItem)
		{
			string text = UtangardConfig.BossDoorKeys.Value ?? "";
			if (!string.Equals(text, _parsedFrom, StringComparison.Ordinal))
			{
				Keys.Clear();
				string[] array = text.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.Length > 0)
					{
						Keys.Add(text2);
					}
				}
				_parsedFrom = text;
			}
			if (Keys.Count > 0)
			{
				return Keys.Contains(((Object)((Component)keyItem).gameObject).name);
			}
			return false;
		}
	}
	internal static class CompendiumPanel
	{
		private sealed class Cell
		{
			public Biome Biome;

			public GameObject Go;

			public Image Edge;

			public RectTransform Face;

			public TMP_Text Label;

			public float EdgeWidth = -1f;
		}

		private sealed class Box
		{
			public TMP_Text Percent;

			public RectTransform Fill;

			public TMP_Text State;
		}

		[HarmonyPatch]
		internal static class Show
		{
			[HarmonyTargetMethod]
			private static MethodBase Target()
			{
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(TextsDialog)))
				{
					if (!(declaredMethod.Name != "ShowText"))
					{
						ParameterInfo[] parameters = declaredMethod.GetParameters();
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(TextInfo))
						{
							return declaredMethod;
						}
					}
				}
				UtangardPlugin.Log.LogError((object)"TextsDialog has no ShowText(TextInfo) any more - the Utangard page in the compendium stays the plain text version.");
				return null;
			}

			[HarmonyPostfix]
			private static void Postfix(TextsDialog __instance, TextInfo __0)
			{
				Shown(__instance, __0);
			}
		}

		private static readonly Color PanelFace = Rgb(2038292);

		private static readonly Color PanelEdge = Rgb(5917238);

		private static readonly Color BodyText = Rgb(15129282);

		private static readonly Color Muted = Rgb(10720906);

		private static readonly Color Orange = Rgb(16753920);

		private static readonly Color OpenText = Rgb(8831614);

		private static readonly Color OpenEdge = Rgb(5995087);

		private static readonly Color LockedText = Rgb(13794926);

		private static readonly Color LockedEdge = Rgb(7227968);

		private static readonly Color BoxEdge = Rgb(4865326);

		private static readonly Color Track = Rgb(3813673);

		private static readonly Color FightingFill = Rgb(12551498);

		private static readonly Color DiscoveryFill = Rgb(8232888);

		private const float BodySize = 14f;

		private const float TitleSize = 20f;

		private const float SmallSize = 13f;

		private const float StripSize = 12f;

		private const float LineHeight = 1.55f;

		private const float RefreshSeconds = 1f;

		internal static TextInfo Page;

		private static TextsDialog _dialog;

		private static GameObject _root;

		private static RectTransform _host;

		private static LayoutElement _floor;

		private static float _fitScale = -1f;

		private static Vector2 _fitSize;

		private static float _fitPixels = -1f;

		private static float _pixelsPerUnit = 1f;

		private static readonly List<RectTransform> Rims = new List<RectTransform>();

		private static TextsDialog _failedFor;

		private static Biome _selected = (Biome)0;

		private static float _nextRefresh;

		private static TMP_Text _proto;

		private static float _naturalPerEm;

		private static readonly List<Cell> Cells = new List<Cell>();

		private static TMP_Text _title;

		private static TMP_Text _waiting;

		private static TMP_Text _rules;

		private static GameObject _barsGap;

		private static GameObject _bars;

		private static GameObject _rulesGap;

		private static Box _fighting;

		private static Box _discovery;

		private static void Shown(TextsDialog dialog, TextInfo text)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (Page == null || text != Page || !UtangardConfig.ShowCompendiumPanel.Value)
			{
				if ((Object)(object)_root != (Object)null)
				{
					_root.SetActive(false);
				}
			}
			else
			{
				if ((Object)(object)dialog == (Object)null || dialog == _failedFor)
				{
					return;
				}
				try
				{
					if ((Object)(object)_root == (Object)null || _dialog != dialog)
					{
						Build(dialog);
					}
					_selected = Default();
					_root.transform.SetAsLastSibling();
					_root.SetActive(true);
					Fit();
					Draw();
					dialog.m_textArea.text = "";
					_nextRefresh = Time.unscaledTime + 1f;
				}
				catch (Exception e)
				{
					Abandon(dialog, e);
				}
			}
		}

		internal static void Tick()
		{
			if ((Object)(object)_root == (Object)null || !_root.activeInHierarchy)
			{
				return;
			}
			try
			{
				int num = 0;
				if (ZInput.IsExclusiveGamepadActive())
				{
					if (ZInput.GetButtonDown("JoyDPadLeft"))
					{
						num = -1;
					}
					else if (ZInput.GetButtonDown("JoyDPadRight"))
					{
						num = 1;
					}
				}
				if (num != 0)
				{
					Step(num);
				}
				else if (Time.unscaledTime < _nextRefresh)
				{
					return;
				}
				_nextRefresh = Time.unscaledTime + 1f;
				Fit();
				Draw();
			}
			catch (Exception e)
			{
				Abandon(_dialog, e);
			}
		}

		private static void Select(Biome biome)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			try
			{
				_selected = biome;
				Draw();
			}
			catch (Exception e)
			{
				Abandon(_dialog, e);
			}
		}

		private static void Step(int step)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			int num = -1;
			for (int i = 0; i < Cells.Count; i++)
			{
				if (Cells[i].Biome == _selected)
				{
					num = i;
				}
			}
			for (int j = 0; j < Cells.Count; j++)
			{
				num += step;
				if (num < 0 || num >= Cells.Count)
				{
					break;
				}
				if (Cells[num].Go.activeSelf)
				{
					_selected = Cells[num].Biome;
					break;
				}
			}
		}

		private static Biome Default()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Biome val = Foothold.Frontier();
			if ((int)val != 0)
			{
				return val;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				Biome currentBiome = localPlayer.GetCurrentBiome();
				if (Listed(currentBiome))
				{
					return currentBiome;
				}
			}
			return UtangardConfig.GateableBiomes[0];
		}

		private static bool Listed(Biome biome)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (Array.IndexOf(UtangardConfig.GateableBiomes, biome) < 0)
			{
				return false;
			}
			if ((int)biome == 256)
			{
				return UtangardConfig.RequiredKeyFor(biome) != null;
			}
			return true;
		}

		private static void Abandon(TextsDialog dialog, Exception e)
		{
			_failedFor = dialog;
			UtangardPlugin.Log.LogError((object)("The Utangard compendium panel failed, so the page is back to plain text for this world. Nothing else is affected. " + e));
			try
			{
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
				}
				_root = null;
				if ((Object)(object)dialog != (Object)null && (Object)(object)dialog.m_textArea != (Object)null && Page != null)
				{
					dialog.m_textArea.text = ((Localization.instance != null) ? Localization.instance.Localize(Page.m_text) : Page.m_text);
				}
			}
			catch (Exception ex)
			{
				UtangardPlugin.Log.LogError((object)("Could not put the Utangard text page back either: " + ex));
			}
		}

		private static void Draw()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			List<GateReport.Row> list = GateReport.Rows();
			foreach (Cell cell in Cells)
			{
				bool flag = Listed(cell.Biome);
				SetActive(cell.Go, flag);
				if (flag)
				{
					GateReport.Row row = RowOf(list, cell.Biome);
					bool flag2 = row.Key == null || row.Open;
					bool flag3 = cell.Biome == _selected;
					SetText(cell.Label, GateReport.BiomeName(cell.Biome));
					SetColor((Graphic)(object)cell.Label, flag3 ? Orange : (flag2 ? OpenText : LockedText));
					SetColor((Graphic)(object)cell.Edge, flag3 ? Orange : (flag2 ? OpenEdge : LockedEdge));
					SetEdgeWidth(cell, flag3 ? 2f : 1f);
				}
			}
			GateReport.Row row2 = RowOf(list, _selected);
			bool num = row2.Key != null && !row2.Open;
			SetText(_title, "The " + GateReport.BiomeName(_selected));
			SetText(_waiting, Wrapped((list.Count == 0) ? "No world loaded yet." : GateReport.WaitingLine(row2)));
			bool flag4 = num && UtangardConfig.FootholdEnabled.Value;
			SetActive(_barsGap, flag4);
			SetActive(_bars, flag4);
			if (flag4)
			{
				DrawBars(Foothold.Read(_selected));
			}
			bool warning = false;
			string text = (num ? Rules(flag4, out warning) : null);
			bool flag5 = !string.IsNullOrEmpty(text);
			SetActive(_rulesGap, flag5);
			SetActive(((Component)_rules).gameObject, flag5);
			if (flag5)
			{
				SetText(_rules, Wrapped(text));
				SetColor((Graphic)(object)_rules, warning ? LockedText : Muted);
			}
		}

		private static void DrawBars(Foothold.Standing s)
		{
			int num = (s.FightingAvailable ? s.FightingPercent : 0);
			SetText(_fighting.Percent, s.FightingAvailable ? (num + "%") : "?");
			SetFill(_fighting.Fill, (float)num / 100f);
			if (!UtangardConfig.BlockEating.Value || s.CanEat)
			{
				SetState(_fighting.State, "Eating allowed", good: true);
			}
			else if (!s.FightingAvailable)
			{
				SetState(_fighting.State, "Kills are not being counted", good: false);
			}
			else if (s.EatAtPercent > 100)
			{
				SetState(_fighting.State, "No eating here", good: false);
			}
			else
			{
				SetState(_fighting.State, "Eating at " + s.EatAtPercent + "%", good: false);
			}
			int num2 = (s.DiscoveryAvailable ? Mathf.FloorToInt(s.DiscoveryPercent) : 0);
			SetText(_discovery.Percent, s.DiscoveryAvailable ? (num2 + "%") : "?");
			SetFill(_discovery.Fill, (float)num2 / 100f);
			if (UtangardConfig.HealthRegenMultiplier.Value >= 1f || s.CanHeal)
			{
				SetState(_discovery.State, "Healing allowed", good: true);
			}
			else if (!s.DiscoveryAvailable)
			{
				SetState(_discovery.State, "The map cannot be read", good: false);
			}
			else if (s.DiscoveryCounting)
			{
				SetState(_discovery.State, "Still reading your map", good: false);
			}
			else
			{
				SetState(_discovery.State, "Healing needs both full", good: false);
			}
		}

		private static string Rules(bool bars, out bool warning)
		{
			warning = !Seams.PenaltyIsEscapable();
			if (warning)
			{
				return "Nothing is enforced right now. A game update moved the code that records boss kills, so the mod has stopped until it is updated.";
			}
			return GateReport.RulesLine(!bars && UtangardConfig.BlockEating.Value);
		}

		private static GateReport.Row RowOf(List<GateReport.Row> rows, Biome biome)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			foreach (GateReport.Row row in rows)
			{
				if (row.Biome == biome)
				{
					return row;
				}
			}
			return new GateReport.Row
			{
				Biome = biome,
				Open = true,
				SecondsLeft = -1L
			};
		}

		private static void SetState(TMP_Text label, string text, bool good)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			SetText(label, text);
			SetColor((Graphic)(object)label, good ? OpenText : Muted);
		}

		private static string Wrapped(string text)
		{
			return "<line-height=" + 1.55f.ToString(CultureInfo.InvariantCulture) + "em><noparse>" + text + "</noparse>";
		}

		private static void SetText(TMP_Text label, string text)
		{
			if (label.text != text)
			{
				label.text = text;
			}
		}

		private static void SetColor(Graphic graphic, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (graphic.color != color)
			{
				graphic.color = color;
			}
		}

		private static void SetActive(GameObject go, bool active)
		{
			if (go.activeSelf != active)
			{
				go.SetActive(active);
			}
		}

		private static void SetFill(RectTransform fill, float amount)
		{
			//IL_0009: 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)
			amount = Mathf.Clamp01(amount);
			if (!Mathf.Approximately(fill.anchorMax.x, amount))
			{
				fill.anchorMax = new Vector2(amount, 1f);
			}
		}

		private static void SetEdgeWidth(Cell cell, float width)
		{
			float num = Edge(width);
			if (!Mathf.Approximately(cell.EdgeWidth, num))
			{
				cell.EdgeWidth = num;
				Inset(cell.Face, num);
			}
		}

		private static float Edge(float width)
		{
			return Mathf.Max(1f, Mathf.Round(width * _pixelsPerUnit)) / _pixelsPerUnit;
		}

		private static void Rim(RectTransform face)
		{
			Rims.Add(face);
			Inset(face, Edge(1f));
		}

		private static void Build(TextsDialog dialog)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			Forget();
			TMP_Text textArea = dialog.m_textArea;
			if ((Object)(object)textArea == (Object)null)
			{
				throw new InvalidOperationException("TextsDialog.m_textArea is not set.");
			}
			string passed;
			RectTransform val = Host(dialog, textArea, out passed);
			GameObject val2 = new GameObject("Utangard_Panel", new Type[1] { typeof(RectTransform) });
			val2.SetActive(false);
			RectTransform val3 = (RectTransform)val2.transform;
			((Transform)val3).SetParent((Transform)(object)val, false);
			_floor = val2.AddComponent<LayoutElement>();
			_floor.ignoreLayout = true;
			_root = val2;
			_dialog = dialog;
			_host = val;
			Paint(val3, PanelEdge, raycast: true);
			RectTransform obj = Child("Face", (Transform)(object)val3);
			((Component)obj).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			Rim(obj);
			Paint(obj, PanelFace, raycast: false);
			VerticalLayoutGroup obj2 = val2.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(1, 1, 1, 1);
			Stack(obj2, fillHeight: false);
			val2.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			GameObject obj3 = Object.Instantiate<GameObject>(((Component)textArea).gameObject, (Transform)(object)val3);
			((Object)obj3).name = "Utangard_Label";
			Strip(obj3);
			obj3.SetActive(false);
			_proto = obj3.GetComponent<TMP_Text>();
			if ((Object)(object)_proto == (Object)null)
			{
				throw new InvalidOperationException("The compendium's text carried no TextMeshPro component.");
			}
			Prime(_proto);
			val2.SetActive(true);
			_naturalPerEm = Measure(val3);
			val2.AddComponent<CompendiumPanelTicker>();
			RectTransform val4 = Child("Body", (Transform)(object)val3);
			((Component)val4).gameObject.AddComponent<LayoutElement>().flexibleHeight = 0f;
			VerticalLayoutGroup obj4 = ((Component)val4).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj4).padding = new RectOffset(22, 22, 18, 18);
			Stack(obj4, fillHeight: false);
			_fitScale = -1f;
			Fit();
			RectTransform val5 = Child("Biomes", (Transform)(object)val4);
			HorizontalLayoutGroup obj5 = ((Component)val5).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj5).spacing = 6f;
			Row(obj5, even: true);
			int num = 0;
			Biome[] gateableBiomes = UtangardConfig.GateableBiomes;
			foreach (Biome biome in gateableBiomes)
			{
				Cell cell = MakeCell(val5, biome);
				if (Sound(cell.Go, dialog))
				{
					num++;
				}
				Cells.Add(cell);
			}
			Gap((Transform)(object)val4, 12f);
			_title = Label((Transform)(object)val4, 20f, Orange, (TextAlignmentOptions)257);
			Gap((Transform)(object)val4, 4f);
			_waiting = Label((Transform)(object)val4, 14f, Muted, (TextAlignmentOptions)257);
			_barsGap = Gap((Transform)(object)val4, 12f);
			RectTransform obj6 = Child("Bars", (Transform)(object)val4);
			HorizontalLayoutGroup obj7 = ((Component)obj6).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj7).spacing = 14f;
			Row(obj7, even: true);
			_bars = ((Component)obj6).gameObject;
			_fighting = MakeBox(obj6, "Fighting", FightingFill);
			_discovery = MakeBox(obj6, "Discovery", DiscoveryFill);
			_rulesGap = Gap((Transform)(object)val4, 12f);
			_rules = Label((Transform)(object)val4, 13f, Muted, (TextAlignmentOptions)257);
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			Rect rect = val.rect;
			UtangardPlugin.Log.LogInfo((object)("Compendium panel built over '" + ((Object)val).name + "'" + ((passed.Length > 0) ? (" (past " + passed + ", sized by what is in it)") : "") + ", " + Mathf.RoundToInt(((Rect)(ref rect)).width) + " x " + Mathf.RoundToInt(((Rect)(ref rect)).height) + " at scale " + _fitScale.ToString("0.###", invariantCulture) + ", " + _pixelsPerUnit.ToString("0.###", invariantCulture) + " screen pixels to a unit, the compendium's own text at size " + textArea.fontSize.ToString("0.#", invariantCulture) + ", one line of text at " + _naturalPerEm.ToString("0.00", invariantCulture) + " em, " + num + " of " + Cells.Count + " biome buttons with vanilla's click sound."));
		}

		private static void Fit()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_root == (Object)null || (Object)(object)_host == (Object)null || (Object)(object)_floor == (Object)null)
			{
				return;
			}
			float num = HostScale(_host);
			Rect rect = _host.rect;
			Vector2 size = ((Rect)(ref rect)).size;
			float num2 = PixelsPerUnit(_host);
			if (Mathf.Approximately(num, _fitScale) && size == _fitSize && Mathf.Approximately(num2, _fitPixels))
			{
				return;
			}
			_fitScale = num;
			_fitSize = size;
			_fitPixels = num2;
			float num3 = ((num >= 0.99f) ? 1f : num);
			float num4 = 1f / num3;
			RectTransform val = (RectTransform)_root.transform;
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(0f, 1f);
			val.pivot = new Vector2(0f, 1f);
			((Transform)val).localScale = new Vector3(num4, num4, 1f);
			val.anchoredPosition = Vector2.zero;
			val.SetSizeWithCurrentAnchors((Axis)0, size.x * num3);
			_floor.minHeight = Mathf.Max(0f, size.y * num3);
			_pixelsPerUnit = num2 * num4;
			foreach (RectTransform rim in Rims)
			{
				Inset(rim, Edge(1f));
			}
		}

		private static float PixelsPerUnit(RectTransform rect)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Canvas componentInParent = ((Component)rect).GetComponentInParent<Canvas>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return 1f;
			}
			Canvas rootCanvas = componentInParent.rootCanvas;
			float x = ((Component)rootCanvas).transform.lossyScale.x;
			if (x <= 0f || rootCanvas.scaleFactor <= 0f)
			{
				return 1f;
			}
			float num = rootCanvas.scaleFactor * ((Transform)rect).lossyScale.x / x;
			if (!(num > 0.1f) || !(num < 20f))
			{
				return 1f;
			}
			return num;
		}

		private static float HostScale(RectTransform host)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			Canvas componentInParent = ((Component)host).GetComponentInParent<Canvas>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return 1f;
			}
			float x = ((Component)componentInParent.rootCanvas).transform.lossyScale.x;
			if (x <= 0f)
			{
				return 1f;
			}
			float num = ((Transform)host).lossyScale.x / x;
			if (!(num > 0.2f) || !(num < 5f))
			{
				return 1f;
			}
			return num;
		}

		private static bool Sound(GameObject button, TextsDialog dialog)
		{
			try
			{
				return CopySound(button, dialog);
			}
			catch (Exception ex)
			{
				UtangardPlugin.Log.LogWarning((object)("Compendium panel: the biome buttons stay silent. " + ex.Message));
				return false;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool CopySound(GameObject button, TextsDialog dialog)
		{
			if ((Object)(object)dialog.m_elementPrefab == (Object)null)
			{
				return false;
			}
			ButtonSfx componentInChildren = dialog.m_elementPrefab.GetComponentInChildren<ButtonSfx>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return false;
			}
			ButtonSfx obj = button.AddComponent<ButtonSfx>();
			obj.m_sfxPrefab = componentInChildren.m_sfxPrefab;
			obj.m_sfxPrefabVibrationOnly = componentInChildren.m_sfxPrefabVibrationOnly;
			obj.m_selectSfxPrefab = componentInChildren.m_selectSfxPrefab;
			obj.m_selectSfxPrefabVibrationOnly = componentInChildren.m_selectSfxPrefabVibrationOnly;
			obj.m_enterSfxPrefab = componentInChildren.m_enterSfxPrefab;
			obj.m_enterSfxPrefabVibrationOnly = componentInChildren.m_enterSfxPrefabVibrationOnly;
			return true;
		}

		private unsafe static Cell MakeCell(RectTransform strip, Biome biome)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Child("Biome_" + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString(), (Transform)(object)strip);
			Image val2 = Paint(val, LockedEdge, raycast: true);
			LayoutElement obj = ((Component)val).gameObject.AddComponent<LayoutElement>();
			obj.minWidth = 0f;
			obj.preferredWidth = 0f;
			obj.flexibleWidth = 1f;
			VerticalLayoutGroup obj2 = ((Component)val).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(2, 2, 2, 2);
			Stack(obj2, fillHeight: true);
			RectTransform val3 = Child("Face", (Transform)(object)val);
			((Component)val3).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			Paint(val3, PanelFace, raycast: false);
			TMP_Text val4 = Label((Transform)(object)val, 12f, OpenText, (TextAlignmentOptions)514);
			float num = Lead(12f);
			val4.margin = new Vector4(2f, 6f + num, 2f, 6f + num);
			Button obj3 = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)obj3).transition = (Transition)0;
			((Selectable)obj3).targetGraphic = (Graphic)(object)val2;
			Navigation navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)0;
			((Selectable)obj3).navigation = navigation;
			Biome chosen = biome;
			((UnityEvent)obj3.onClick).AddListener((UnityAction)delegate
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				Select(chosen);
			});
			Cell obj4 = new Cell
			{
				Biome = biome,
				Go = ((Component)val).gameObject,
				Edge = val2,
				Face = val3,
				Label = val4
			};
			SetEdgeWidth(obj4, 1f);
			return obj4;
		}

		private static Box MakeBox(RectTransform parent, string name, Color fill)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Child(name, (Transform)(object)parent);
			Paint(val, BoxEdge, raycast: false);
			LayoutElement obj = ((Component)val).gameObject.AddComponent<LayoutElement>();
			obj.minWidth = 0f;
			obj.preferredWidth = 0f;
			obj.flexibleWidth = 1f;
			VerticalLayoutGroup obj2 = ((Component)val).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(11, 11, 11, 11);
			Stack(obj2, fillHeight: false);
			RectTransform obj3 = Child("Face", (Transform)(object)val);
			((Component)obj3).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			Rim(obj3);
			Paint(obj3, PanelFace, raycast: false);
			RectTransform obj4 = Child("Head", (Transform)(object)val);
			HorizontalLayoutGroup obj5 = ((Component)obj4).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj5).spacing = 8f;
			Row(obj5, even: false);
			TMP_Text obj6 = Label((Transform)(object)obj4, 14f, BodyText, (TextAlignmentOptions)257);
			obj6.text = name;
			LayoutElement obj7 = ((Component)obj6).gameObject.AddComponent<LayoutElement>();
			obj7.minWidth = 0f;
			obj7.flexibleWidth = 1f;
			TMP_Text val2 = Label((Transform)(object)obj4, 14f, BodyText, (TextAlignmentOptions)260);
			val2.textWrappingMode = (TextWrappingModes)0;
			Gap((Transform)(object)val, 6f);
			RectTransform val3 = Child("Track", (Transform)(object)val);
			Paint(val3, Track, raycast: false);
			LayoutElement obj8 = ((Component)val3).gameObject.AddComponent<LayoutElement>();
			obj8.minHeight = 12f;
			obj8.preferredHeight = 12f;
			RectTransform val4 = Child("Fill", (Transform)(object)val3);
			val4.anchorMin = Vector2.zero;
			val4.anchorMax = new Vector2(0f, 1f);
			val4.offsetMin = Vector2.zero;
			val4.offsetMax = Vector2.zero;
			Paint(val4, fill, raycast: false);
			Gap((Transform)(object)val, 6f);
			TMP_Text state = Label((Transform)(object)val, 13f, Muted, (TextAlignmentOptions)257);
			return new Box
			{
				Percent = val2,
				Fill = val4,
				State = state
			};
		}

		private static RectTransform Host(TextsDialog dialog, TMP_Text donor, out string passed)
		{
			Transform parent = donor.transform.parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("The compendium's text has no parent to draw over.");
			}
			passed = "";
			while (!Clips(val) && FollowsContent(val))
			{
				Transform parent2 = ((Transform)val).parent;
				RectTransform val2 = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null);
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)((Component)dialog).transform || !((Transform)val2).IsChildOf(((Component)dialog).transform) || Brings((Transform)(object)val, (Transform)(object)val2, (Component)(object)dialog.m_textAreaTopic) || Brings((Transform)(object)val, (Transform)(object)val2, (Component)(object)dialog.m_rightScrollbar) || Brings((Transform)(object)val, (Transform)(object)val2, (Component)(object)dialog.m_listRoot))
				{
					break;
				}
				passed = passed + ((passed.Length > 0) ? ", '" : "'") + ((Object)val).name + "'";
				val = val2;
			}
			return val;
		}

		private static bool FollowsContent(RectTransform rect)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			ContentSizeFitter[] components = ((Component)rect).GetComponents<ContentSizeFitter>();
			foreach (ContentSizeFitter val in components)
			{
				if (((Behaviour)val).enabled && (int)val.verticalFit != 0)
				{
					return true;
				}
			}
			ScrollRect[] componentsInParent = ((Component)rect).GetComponentsInParent<ScrollRect>(true);
			for (int i = 0; i < componentsInParent.Length; i++)
			{
				if ((Object)(object)componentsInParent[i].content == (Object)(object)rect)
				{
					return true;
				}
			}
			Transform parent = ((Transform)rect).parent;
			if ((Object)(object)parent == (Object)null)
			{
				return false;
			}
			HorizontalOrVerticalLayoutGroup[] components2 = ((Component)parent).GetComponents<HorizontalOrVerticalLayoutGroup>();
			foreach (HorizontalOrVerticalLayoutGroup val2 in components2)
			{
				if (((Behaviour)val2).enabled && val2.childControlHeight)
				{
					return true;
				}
			}
			return false;
		}

		private static bool Clips(RectTransform rect)
		{
			RectMask2D component = ((Component)rect).GetComponent<RectMask2D>();
			if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled)
			{
				return true;
			}
			Mask component2 = ((Component)rect).GetComponent<Mask>();
			if ((Object)(object)component2 != (Object)null)
			{
				return ((Behaviour)component2).enabled;
			}
			return false;
		}

		private static bool Brings(Transform from, Transform to, Component part)
		{
			if ((Object)(object)part != (Object)null && part.transform.IsChildOf(to))
			{
				return !part.transform.IsChildOf(from);
			}
			return false;
		}

		private static void Strip(GameObject go)
		{
			for (int i = 0; i < 2; i++)
			{
				Component[] components = go.GetComponents<Component>();
				foreach (Component val in components)
				{
					if (!((Object)(object)val == (Object)null) && !(val is RectTransform) && !(val is CanvasRenderer) && !(val is TMP_Text))
					{
						Object.DestroyImmediate((Object)(object)val);
					}
				}
			}
			for (int num = go.transform.childCount - 1; num >= 0; num--)
			{
				Object.DestroyImmediate((Object)(object)((Component)go.transform.GetChild(num)).gameObject);
			}
		}

		private static void Prime(TMP_Text label)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			label.text = "";
			label.enableAutoSizing = false;
			label.richText = true;
			((Graphic)label).raycastTarget = false;
			label.overrideColorTags = false;
			label.enableVertexGradient = false;
			label.textWrappingMode = (TextWrappingModes)1;
			label.overflowMode = (TextOverflowModes)0;
			label.margin = Vector4.zero;
			label.lineSpacing = 0f;
			label.paragraphSpacing = 0f;
			label.alignment = (TextAlignmentOptions)257;
			((Graphic)label).color = BodyText;
		}

		private static float Measure(RectTransform parent)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(((Component)_proto).gameObject, (Transform)(object)parent);
			try
			{
				val.SetActive(true);
				TMP_Text component = val.GetComponent<TMP_Text>();
				component.fontSize = 100f;
				component.textWrappingMode = (TextWrappingModes)0;
				float num = component.GetPreferredValues("Hg").y / 100f;
				if (num > 0.6f && num < 2.5f)
				{
					return num;
				}
				UtangardPlugin.Log.LogWarning((object)("Compendium panel: measured a line of " + num + " em, which cannot be right; spacing the text as if it were 1.2."));
			}
			finally
			{
				Object.DestroyImmediate((Object)(object)val);
			}
			return 1.2f;
		}

		private static float Lead(float size)
		{
			return Mathf.Max(0f, (1.55f - _naturalPerEm) * size * 0.5f);
		}

		private static TMP_Text Label(Transform parent, float size, Color color, TextAlignmentOptions align)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)_proto).gameObject, parent);
			((Object)obj).name = "Text";
			obj.SetActive(true);
			TMP_Text component = obj.GetComponent<TMP_Text>();
			component.fontSize = size;
			((Graphic)component).color = color;
			component.alignment = align;
			float num = Lead(size);
			component.margin = new Vector4(0f, num, 0f, num);
			return component;
		}

		private static RectTransform Child(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		private static Image Paint(RectTransform rect, Color color, bool raycast)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)rect).gameObject.AddComponent<Image>();
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = raycast;
			return obj;
		}

		private static void Inset(RectTransform rect, float by)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.pivot = new Vector2(0.5f, 0.5f);
			rect.offsetMin = new Vector2(by, by);
			rect.offsetMax = new Vector2(0f - by, 0f - by);
		}

		private static GameObject Gap(Transform parent, float height)
		{
			RectTransform obj = Child("Gap", parent);
			LayoutElement obj2 = ((Component)obj).gameObject.AddComponent<LayoutElement>();
			obj2.minHeight = height;
			obj2.preferredHeight = height;
			return ((Component)obj).gameObject;
		}

		private static void Stack(VerticalLayoutGroup group, bool fillHeight)
		{
			((HorizontalOrVerticalLayoutGroup)group).spacing = 0f;
			((LayoutGroup)group).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)group).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)group).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)group).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)group).childForceExpandHeight = fillHeight;
		}

		private static void Row(HorizontalLayoutGroup group, bool even)
		{
			((LayoutGroup)group).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)group).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)group).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)group).childForceExpandWidth = even;
			((HorizontalOrVerticalLayoutGroup)group).childForceExpandHeight = even;
		}

		private static void Forget()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_dialog = null;
			_host = null;
			_floor = null;
			_fitScale = -1f;
			_fitPixels = -1f;
			Rims.Clear();
			_proto = null;
			_title = null;
			_waiting = null;
			_rules = null;
			_bars = null;
			_barsGap = null;
			_rulesGap = null;
			_fighting = null;
			_discovery = null;
			Cells.Clear();
		}

		private static Color Rgb(int hex)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)((hex >> 16) & 0xFF) / 255f, (float)((hex >> 8) & 0xFF) / 255f, (float)(hex & 0xFF) / 255f, 1f);
		}
	}
	internal sealed class CompendiumPanelTicker : MonoBehaviour
	{
		private void Update()
		{
			CompendiumPanel.Tick();
		}
	}
	internal static class DefeatKeys
	{
		private static readonly Dictionary<string, List<string>> Setters = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

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

		private static ZNetScene _scannedScene;

		private static void Scan()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || instance == _scannedScene)
			{
				return;
			}
			Setters.Clear();
			Names.Clear();
			_scannedScene = instance;
			List<GameObject> prefabs = instance.m_prefabs;
			if (prefabs == null)
			{
				return;
			}
			Character val2 = default(Character);
			for (int i = 0; i < prefabs.Count; i++)
			{
				GameObject val = prefabs[i];
				if ((Object)(object)val == (Object)null || !val.TryGetComponent<Character>(ref val2))
				{
					continue;
				}
				string defeatSetGlobalKey = val2.m_defeatSetGlobalKey;
				if (!string.IsNullOrEmpty(defeatSetGlobalKey))
				{
					if (!Setters.TryGetValue(defeatSetGlobalKey, out var value))
					{
						value = new List<string>(1);
						Setters[defeatSetGlobalKey] = value;
					}
					value.Add(((Object)val).name);
					if (!string.IsNullOrEmpty(val2.m_name) && (!Names.ContainsKey(defeatSetGlobalKey) || val2.m_boss))
					{
						Names[defeatSetGlobalKey] = val2.m_name;
					}
				}
			}
		}

		public static string NameFor(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			Scan();
			if (!Names.TryGetValue(key, out var value))
			{
				return null;
			}
			Localization instance = Localization.instance;
			if (instance == null)
			{
				return null;
			}
			string text = instance.Localize(value);
			if (!string.IsNullOrEmpty(text) && !(text == value))
			{
				return text;
			}
			return null;
		}

		public static bool IsSetByACreature(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			Scan();
			return Setters.ContainsKey(key);
		}

		public unsafe static void Report()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Scan();
			if (Setters.Count == 0)
			{
				UtangardPlugin.Log.LogInfo((object)"No prefab scene to read defeat keys from yet - gate keys unverified.");
				return;
			}
			if (UtangardConfig.LogDefeatKeys.Value)
			{
				LogAll();
			}
			List<string> list = new List<string>();
			Biome[] gateableBiomes = UtangardConfig.GateableBiomes;
			for (int i = 0; i < gateableBiomes.Length; i++)
			{
				Biome biome = gateableBiomes[i];
				string text = UtangardConfig.RequiredKeyFor(biome);
				if (text != null && !Setters.ContainsKey(text))
				{
					list.Add(((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString() + " -> '" + text + "'");
				}
			}
			if (list.Count == 0)
			{
				UtangardPlugin.Log.LogInfo((object)"Gate keys verified against this world's creatures: every gated biome names a key something here actually sets on death.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("These gate rows name a key no creature in this world sets: ");
			for (int j = 0; j < list.Count; j++)
			{
				if (j > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(list[j]);
			}
			stringBuilder.Append(". That is fine if the key comes from somewhere else - another mod, or a location - and is a permanently shut biome if it is a typo, which looks exactly the same from inside the game. The keys creatures here do set are: ").Append(BossKeys());
			UtangardPlugin.Log.LogWarning((object)stringBuilder.ToString());
		}

		private static void LogAll()
		{
			UtangardPlugin.Log.LogInfo((object)("Defeat keys set by creatures in this world (" + Setters.Count + "):"));
			List<string> list = new List<string>(Setters.Keys);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			foreach (string item in list)
			{
				UtangardPlugin.Log.LogInfo((object)("  " + item + " <- " + string.Join(", ", Setters[item].ToArray())));
			}
		}

		private static string BossKeys()
		{
			List<string> list = new List<string>();
			foreach (string key in Setters.Keys)
			{
				if (key.StartsWith("defeated_", StringComparison.OrdinalIgnoreCase))
				{
					list.Add(key);
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			if (list.Count != 0)
			{
				return string.Join(", ", list.ToArray());
			}
			return "(none begin with 'defeated_'; turn on Diagnostics.LogDefeatKeys for the full list)";
		}
	}
	internal static class DevConsole
	{
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		internal static class Hook
		{
			private static void Postfix()
			{
				Register();
			}
		}

		private sealed class DeathCount
		{
			internal int AsOwner;

			internal int NotOwner;

			internal int BossCredit;
		}

		private sealed class Creature
		{
			internal string Prefab;

			internal string Token;

			internal float Health;

			internal bool Unconditional;

			internal readonly List<string> Rows = new List<string>();
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__OnCommand;
		}

		private static bool _registered;

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

		private const float DeathsReach = 40f;

		private static readonly Biome[] Order;

		private static readonly string[] LeftOut;

		private static void Register()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__OnCommand;
				if (obj == null)
				{
					ConsoleEvent val = OnCommand;
					<>O.<0>__OnCommand = val;
					obj = (object)val;
				}
				new ConsoleCommand("utangard", "utangard foothold | biomes | creatures | deaths [<creature> [since <notowner>]] - your foothold in each locked biome, the raw numbers behind it, and which machine ran a creature's death", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				RegisterTest();
			}
		}

		private static void RegisterTest()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			new ConsoleCommand("utangardtest", "utangardtest kills <creature> <count> - set how many of that creature Utangard has counted you killing in this world, for tests", new ConsoleEventFailable(OnTest), true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static object OnTest(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if ((Object)(object)context == (Object)null)
			{
				return "no console to answer in";
			}
			if (((args.Length > 1) ? args[1].ToLowerInvariant() : "") == "kills")
			{
				return SetKills(context, args);
			}
			context.AddString("utangardtest kills <creature> <count> - set this character's count of that creature in this world, as if it had killed that many. 0 clears it.");
			context.AddString("<creature> is a prefab name from a points line: JotunWarrior, Troll, Greydwarf...");
			return true;
		}

		private static object SetKills(Terminal term, ConsoleEventArgs args)
		{
			if (args.Length < 4)
			{
				return "utangardtest kills <creature> <count>";
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return "no world loaded yet";
			}
			if (!int.TryParse(args[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0)
			{
				return "'" + args[3] + "' is not a count";
			}
			GameObject prefab = instance.GetPrefab(args[2]);
			Character val = default(Character);
			if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<Character>(ref val))
			{
				return "no creature called " + args[2];
			}
			string text = val.m_name ?? "";
			if (!Foothold.Pays(text))
			{
				if (result != 0)
				{
					return args[2] + " (" + text + ") is worth nothing on any points line, so Utangard does not count it";
				}
				Say(term, "utangardtest kills: " + ((Object)prefab).name + " (" + text + ") is worth nothing on any points line, so its count is 0 already.");
				return true;
			}
			if (!KillTally.Set(text, result, out var had))
			{
				return "there is no character in a world to set it on";
			}
			Say(term, "utangardtest kills: " + ((Object)prefab).name + " (" + text + ") was " + had + ", now " + result + ", for " + Player.m_localPlayer.GetPlayerName() + " in this world.");
			return true;
		}

		private static void OnCommand(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (!((Object)(object)context == (Object)null))
			{
				switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "")
				{
				case "foothold":
					FootholdReport(context);
					return;
				case "biomes":
					Biomes(context);
					return;
				case "creatures":
					Creatures(context);
					return;
				case "deaths":
					DeathsReport(context, args);
					return;
				}
				context.AddString("utangard foothold - per biome: your Fighting and Discovery bars, what each kind of creature put in, and what is unlocked");
				context.AddString("utangard biomes - per biome: its creatures, the kills of each Utangard counted for you in this world (only creatures a points line pays for), and how much of it you have explored");
				context.AddString("utangard creatures - every creature in the foothold tables, checked against the game");
				context.AddString("utangard deaths <creature> - whether it dies through its animation, how many this machine saw die this session and whether it had them, and who has the nearest live one");
				context.AddString("utangard deaths - every creature that dies through its animation; utangard deaths <creature> since <notowner> - whether this machine ran one it did not own since notowner stood there");
			}
		}

		internal static void SawDeath(Character creature)
		{
			try
			{
				ZNetView val = default(ZNetView);
				if (!((Object)(object)creature == (Object)null) && !creature.IsPlayer() && ((Component)creature).TryGetComponent<ZNetView>(ref val) && val.IsValid())
				{
					DeathCount deathCount = DeathsOf(Utils.GetPrefabName(((Component)creature).gameObject));
					if (val.IsOwner())
					{
						deathCount.AsOwner++;
					}
					else
					{
						deathCount.NotOwner++;
					}
				}
			}
			catch (Exception)
			{
			}
		}

		internal static void RanBossCredit(Character creature)
		{
			try
			{
				if (!((Object)(object)creature == (Object)null))
				{
					DeathsOf(Utils.GetPrefabName(((Component)creature).gameObject)).BossCredit++;
				}
			}
			catch (Exception)
			{
			}
		}

		private static DeathCount DeathsOf(string prefab)
		{
			string key = prefab ?? "";
			if (!Deaths.TryGetValue(key, out var value))
			{
				value = (Deaths[key] = new DeathCount());
			}
			return value;
		}

		private static void DeathsReport(Terminal term, ConsoleEventArgs args)
		{
			ZNetScene instance = ZNetScene.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				Say(term, "utangard deaths: no character in a world yet.");
				return;
			}
			if (args.Length < 3)
			{
				Say(term, DeathAnimations(instance));
				Say(term, "utangard deaths <creature> [since <notowner>] - one of them by prefab name: Greydwarf, Eikthyr...");
				return;
			}
			GameObject prefab = instance.GetPrefab(args[2]);
			Character val = default(Character);
			if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<Character>(ref val))
			{
				Say(term, "utangard deaths: no creature called " + args[2] + ".");
				return;
			}
			string name = ((Object)prefab).name;
			string text = "utangard deaths " + name + ": ";
			string defeatSetGlobalKey = val.m_defeatSetGlobalKey;
			string text2 = (string.IsNullOrEmpty(defeatSetGlobalKey) ? "none" : (UtangardConfig.IsGateKey(defeatSetGlobalKey) ? "yes" : "no"));
			Say(term, text + "deathanim=" + (val.m_deathAnimation ? "yes" : "no") + " gatekey=" + text2 + "   (" + (val.m_deathAnimation ? "it dies through its animation, so every machine animating it reaches OnDeath" : "it dies straight from CheckDeath, so only the machine that has it reaches OnDeath") + (string.IsNullOrEmpty(defeatSetGlobalKey) ? "" : ("; its death writes " + defeatSetGlobalKey)) + ")");
			Deaths.TryGetValue(name, out var value);
			string text3 = (Seams.FootholdKills ? "" : " The death hook did not go on this session, so asowner and notowner cannot move.") + ((Seams.KillCredit && UtangardConfig.Enabled.Value && UtangardConfig.GateOnGroup.Value) ? "" : " The boss credit is off this session (its hook, Enabled or GateOnGroup), so bosscredit cannot move.");
			Say(term, text + "asowner=" + (value?.AsOwner ?? 0) + " notowner=" + (value?.NotOwner ?? 0) + " bosscredit=" + (value?.BossCredit ?? 0) + "   (deaths this machine ran this session, by whether it had the creature, and the boss credits it ran for one)" + text3);
			Say(term, text + NearestAlive(name, localPlayer));
			Say(term, text + KillsOf(val));
			if (args.Length > 4 && string.Equals(args[3], "since", StringComparison.OrdinalIgnoreCase) && int.TryParse(args[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				Say(term, text + NonOwnerSince(value?.NotOwner ?? 0, result, val.m_deathAnimation));
			}
		}

		private static string NonOwnerSince(int now, int since, bool deathAnimation)
		{
			int num = now - since;
			string text = " since notowner=" + since.ToString(CultureInfo.InvariantCulture);
			if (num < 0)
			{
				return "nonowner=unknown" + text + "   (notowner is " + now + " now, below that, so the number given was not read in this session)";
			}
			if (num > 0)
			{
				return "nonowner=" + num + text + "   (this machine ran " + num + " death(s) of it that it did not own, so the refusal of a machine that does not have it was exercised here)";
			}
			return "nonowner=0" + text + "   (this machine ran no death of it that it did not own, so the refusal of a machine that does not have it was NOT exercised here" + (deathAnimation ? ")" : ", and none can: it dies straight from CheckDeath, so only the machine that has it reaches OnDeath)");
		}

		private static string DeathAnimations(ZNetScene scene)
		{
			List<string> list = new List<string>();
			if (scene.m_prefabs != null)
			{
				Character val = default(Character);
				foreach (GameObject prefab in scene.m_prefabs)
				{
					if (!((Object)(object)prefab == (Object)null) && prefab.TryGetComponent<Character>(ref val) && !(val is Player) && val.m_deathAnimation)
					{
						list.Add(((Object)prefab).name);
					}
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return "utangard deaths: deathanim=" + list.Count + "   (creatures that die through their animation, so a machine that does not have one can still run its death" + ((list.Count == 0) ? "; there are none in this world)" : ("): " + string.Join(", ", list.ToArray())));
		}

		private static string NearestAlive(string prefab, Player player)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			List<Character> allCharacters = Character.GetAllCharacters();
			float num = 1600f;
			float num2 = num;
			int num3 = 0;
			Character val = null;
			if (allCharacters != null)
			{
				foreach (Character item in allCharacters)
				{
					if ((Object)(object)item == (Object)null || item.IsDead() || !string.Equals(Utils.GetPrefabName(((Component)item).gameObject), prefab, StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					Vector3 val2 = ((Component)item).transform.position - ((Component)player).transform.position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (!(sqrMagnitude > num))
					{
						num3++;
						if (!(sqrMagnitude > num2))
						{
							num2 = sqrMagnitude;
							val = item;
						}
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return "nearest=none alive=0   (none alive within " + 40f.ToString("0", CultureInfo.InvariantCulture) + " m)";
			}
			ZNetView val3 = default(ZNetView);
			string text = ((!((Component)val).TryGetComponent<ZNetView>(ref val3) || !val3.IsValid()) ? "nobody" : (val3.IsOwner() ? "here" : "elsewhere"));
			return "nearest=" + text + " alive=" + num3 + "   (the nearest live one, " + Mathf.Sqrt(num2).ToString("0.0", CultureInfo.InvariantCulture) + " m away, belongs to " + ((text == "here") ? "this machine" : ((text == "elsewhere") ? "another machine" : "no machine")) + ")";
		}

		private static string KillsOf(Character creature)
		{
			string text = creature.m_name ?? "";
			if (!Foothold.Pays(text))
			{
				return "kills=unpaid   (" + text + " is on no points line, so Utangard does not count it)";
			}
			Dictionary<string, int> dictionary = KillTally.Here();
			if (dictionary == null)
			{
				return "kills=unreadable   (" + (KillTally.WhyNot() ?? "the kill tally cannot be read") + ")";
			}
			dictionary.TryGetValue(text, out var value);
			return "kills=" + value.ToString(CultureInfo.InvariantCulture) + "   (your kills of " + text + " that Utangard counted in this world, assists included)";
		}

		private static void FootholdReport(Terminal term)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Say(term, "utangard foothold: no character in a world yet.");
				return;
			}
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			int num = Discovery.FullPixels();
			float num2 = Discovery.PixelKm2();
			int full = Foothold.FullBar();
			Say(term, "utangard foothold: " + localPlayer.GetPlayerName() + ". " + (UtangardConfig.FootholdEnabled.Value ? "" : "FootholdEnabled is OFF, so nothing below unlocks anything. ") + "A full Fighting bar is " + full + " points. Eating at " + Foothold.EatAtPercent() + "% (" + Foothold.EatAtPoints(full) + " points), healing at a full bar and full discovery. No kind above " + UtangardConfig.MaxFromOneKindPercent.Value + "% (" + Foothold.KindCap(full) + " points).");
			string text = KillTally.WhyNot();
			Say(term, (text != null) ? ("utangard foothold: kills cannot be counted right now: " + text + ". Every Fighting bar reads as empty.") : "utangard foothold: kills are the ones Utangard saw you make or help with in this world. Not from other worlds or before this version, not of tamed animals, and not with devcommands.");
			if (!Discovery.Available())
			{
				Say(term, "utangard foothold: the explored map cannot be read this session, so every Discovery bar is empty.");
			}
			else
			{
				Say(term, "utangard foothold: full discovery is " + UtangardConfig.DiscoveryFullKm2.Value.ToString("0.##", invariantCulture) + " km2 = " + num + " map pixels of " + (num2 * 1000000f).ToString("0", invariantCulture) + " m2. " + (Discovery.Counting() ? ("Still counting this world's map (" + (Discovery.Progress() * 100f).ToString("0", invariantCulture) + "%), so discovery can only go up from here.") : ("Map counted. " + Discovery.Describe())));
			}
			foreach (Foothold.Standing item in Foothold.ReadAll())
			{
				if (!item.Gated && item.Kinds.Count == 0)
				{
					continue;
				}
				string text2 = ((!item.Gated) ? "ungated" : (item.Locked ? "LOCKED" : "open"));
				if (item.Here)
				{
					text2 += ", its rules apply to you now";
				}
				string text3 = ((!item.Locked) ? "nothing to unlock" : ((item.CanEat ? "eating allowed" : ("eating at " + item.EatAtPercent + "%")) + ", " + (item.CanHeal ? "healing allowed" : "healing needs both full")));
				string text4 = (item.FightingAvailable ? (item.Fighting + "/" + item.Full + " (" + item.FightingPercent.ToString(invariantCulture) + "%)") : "unreadable");
				string text5 = (item.DiscoveryAvailable ? (item.Discovered + "/" + item.DiscoveryFull + " px (" + Math.Floor(item.DiscoveryPercent).ToString("0", invariantCulture) + "%" + (item.DiscoveryCounting ? ", still counting" : "") + ")") : "unreadable");
				Say(term, ((object)Unsafe.As<Biome, Biome>(ref item.Biome)/*cast due to .constrained prefix*/).ToString() + " (" + text2 + "): fighting " + text4 + ", discovery " + text5 + " - " + text3 + ".");
				if (item.Kinds.Count == 0)
				{
					Say(term, "    no creatures on this biome's points line, so its Fighting bar cannot fill.");
					continue;
				}
				List<string> list = new List<string>();
				foreach (Foothold.Kind kind in item.Kinds)
				{
					string text6 = kind.Name + " " + kind.Points + "pt x" + kind.Kills + " = " + kind.Contribution;
					if (kind.Capped)
					{
						text6 = text6 + " (capped at " + item.KindCap + ", " + kind.Earned + " earned)";
					}
					list.Add(text6);
				}
				Say(term, "    " + string.Join(", ", list.ToArray()));
			}
		}

		private unsafe static void Biomes(Terminal term)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				Say(term, "utangard biomes: no character in a world yet.");
				return;
			}
			Dictionary<string, int> dictionary = KillTally.Here() ?? new Dictionary<string, int>();
			Dictionary<Biome, Dictionary<string, Creature>> dictionary2 = SpawnTables();
			Dictionary<Biome, int> area;
			Dictionary<Biome, int> seen;
			float pixelKm;
			string text = Exploration(out area, out seen, out pixelKm);
			string text2 = KillTally.WhyNot();
			Say(term, "utangard biomes: " + val.GetName() + ". " + text + ((text2 != null) ? (" Kills cannot be read: " + text2 + ".") : ""));
			Biome[] order = Order;
			for (int i = 0; i < order.Length; i++)
			{
				Biome key = order[i];
				area.TryGetValue(key, out var value);
				seen.TryGetValue(key, out var value2);
				float num = (float)value * pixelKm;
				float num2 = (float)value2 * pixelKm;
				float num3 = ((value > 0) ? (100f * (float)value2 / (float)value) : 0f);
				dictionary2.TryGetValue(key, out var value3);
				List<Creature> obj = ((value3 != null) ? value3.Values.OrderBy<Creature, string>((Creature c) => c.Prefab, StringComparer.Ordinal).ToList() : new List<Creature>());
				int num4 = 0;
				int num5 = 0;
				long num6 = 0L;
				List<string> list = new List<string>();
				foreach (Creature item in obj)
				{
					dictionary.TryGetValue(item.Token ?? "", out var value4);
					if (item.Unconditional)
					{
						num4++;
					}
					if (value4 > 0)
					{
						num5++;
						num6 += value4;
					}
					list.Add(item.Prefab + (item.Unconditional ? "" : "*") + " " + item.Health.ToString("0", CultureInfo.InvariantCulture) + "hp x" + value4.ToString(CultureInfo.InvariantCulture));
				}
				Say(term, ((object)(*(Biome*)(&key))/*cast due to .constrained prefix*/).ToString() + ": " + num.ToString("0.0", CultureInfo.InvariantCulture) + " km2 on this map, you explored " + num2.ToString("0.00", CultureInfo.InvariantCulture) + " km2 (" + num3.ToString("0.0", CultureInfo.InvariantCulture) + "%). Creatures: " + num4 + " kinds you can always meet. Utangard counted " + num5 + " kinds, " + num6.ToString(CultureInfo.InvariantCulture) + " kills.");
				if (list.Count > 0)
				{
					Say(term, "    " + string.Join(", ", list.ToArray()));
				}
			}
			Say(term, "utangard biomes: * = only spawns behind a world key, a weather or a persistent event. Spawn tables only - creatures a dungeon or a camp places itself are not in them. Each entry is health, then your kills: the ones Utangard counted in this world, assists included, and only for creatures a points line pays for.");
			Dictionary<string, Creature> dictionary3 = new Dictionary<string, Creature>();
			Dictionary<string, int> dictionary4 = new Dictionary<string, int>();
			foreach (KeyValuePair<Biome, Dictionary<string, Creature>> item2 in dictionary2)
			{
				foreach (Creature value6 in item2.Value.Values)
				{
					dictionary3[value6.Prefab] = value6;
					dictionary4.TryGetValue(value6.Prefab, out var value5);
					dictionary4[value6.Prefab] = value5 + 1;
				}
			}
			foreach (KeyValuePair<string, int> item3 in dictionary4.Where((KeyValuePair<string, int> p) => p.Value >= 5).OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal))
			{
				Say(term, "  in " + item3.Value + " biomes: " + item3.Key + ", rows:");
				foreach (string row in dictionary3[item3.Key].Rows)
				{
					Say(term, "    " + row);
				}
			}
		}

		private static Dictionary<Biome, Dictionary<string, Creature>> SpawnTables()
		{
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Biome, Dictionary<string, Creature>> dictionary = new Dictionary<Biome, Dictionary<string, Creature>>();
			SpawnSystem val = Object.FindObjectOfType<SpawnSystem>();
			if ((Object)(object)val == (Object)null || val.m_spawnLists == null)
			{
				return dictionary;
			}
			Character val2 = default(Character);
			foreach (SpawnSystemList spawnList in val.m_spawnLists)
			{
				if ((Object)(object)spawnList == (Object)null || spawnList.m_spawners == null)
				{
					continue;
				}
				foreach (SpawnData spawner in spawnList.m_spawners)
				{
					if (spawner == null || !spawner.m_enabled || spawner.m_devDisabled || (Object)(object)spawner.m_prefab == (Object)null || !spawner.m_prefab.TryGetComponent<Character>(ref val2) || val2.IsBoss())
					{
						continue;
					}
					bool flag = !string.IsNullOrEmpty(spawner.m_requiredGlobalKey) || !string.IsNullOrEmpty(spawner.m_requiredPersistentEvent) || (spawner.m_requiredEnvironments != null && spawner.m_requiredEnvironments.Count > 0);
					string item = "'" + spawner.m_name + "' biomes=" + ((object)Unsafe.As<Biome, Biome>(ref spawner.m_biome)/*cast due to .constrained prefix*/).ToString() + " area=" + ((object)Unsafe.As<BiomeArea, BiomeArea>(ref spawner.m_biomeArea)/*cast due to .constrained prefix*/).ToString() + " key='" + spawner.m_requiredGlobalKey + "' event='" + spawner.m_requiredPersistentEvent + "' env=" + ((spawner.m_requiredEnvironments != null) ? spawner.m_requiredEnvironments.Count : 0) + " chance=" + spawner.m_spawnChance.ToString("0.#", CultureInfo.InvariantCulture) + " max=" + spawner.m_maxSpawned + " day=" + spawner.m_spawnAtDay + " night=" + spawner.m_spawnAtNight;
					Biome[] order = Order;
					foreach (Biome val3 in order)
					{
						if ((spawner.m_biome & val3) != 0)
						{
							if (!dictionary.TryGetValue(val3, out var value))
							{
								value = (dictionary[val3] = new Dictionary<string, Creature>());
							}
							if (!value.TryGetValue(((Object)spawner.m_prefab).name, out var value2))
							{
								Dictionary<string, Creature> dictionary3 = value;
								string name = ((Object)spawner.m_prefab).name;
								Creature obj = new Creature
								{
									Prefab = ((Object)spawner.m_prefab).name,
									Token = val2.m_name,
									Health = val2.m_health
								};
								value2 = obj;
								dictionary3[name] = obj;
							}
							if (!flag)
							{
								value2.Unconditional = true;
							}
							if (!value2.Rows.Contains(item))
							{
								value2.Rows.Add(item);
							}
						}
					}
				}
			}
			return dictionary;
		}

		private static string Exploration(out Dictionary<Biome, int> area, out Dictionary<Biome, int> seen, out float pixelKm2)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			area = new Dictionary<Biome, int>();
			seen = new Dictionary<Biome, int>();
			pixelKm2 = 0f;
			Minimap instance = Minimap.instance;
			WorldGenerator instance2 = WorldGenerator.instance;
			if ((Object)(object)instance == (Object)null || instance2 == null)
			{
				return "No minimap or world generator, so no exploration.";
			}
			BitArray bitArray = null;
			try
			{
				bitArray = Traverse.Create((object)instance).Field("m_explored").GetValue<BitArray>();
			}
			catch (Exception)
			{
				bitArray = null;
			}
			int textureSize = instance.m_textureSize;
			float pixelSize = instance.m_pixelSize;
			int num = ((textureSize < 1024) ? 1 : 2);
			int num2 = textureSize / 2;
			for (int i = 0; i < textureSize; i += num)
			{
				for (int j = 0; j < textureSize; j += num)
				{
					float num3 = (float)(j - num2) * pixelSize;
					float num4 = (float)(i - num2) * pixelSize;
					if (!(num3 * num3 + num4 * num4 > 100000000f))
					{
						Biome biome = instance2.GetBiome(num3, num4, 0.02f, false);
						area.TryGetValue(biome, out var value);
						area[biome] = value + 1;
						if (bitArray != null && bitArray[i * textureSize + j])
						{
							seen.TryGetValue(biome, out var value2);
							seen[biome] = value2 + 1;
						}
					}
				}
			}
			pixelKm2 = (float)(num * num) * pixelSize * pixelSize / 1000000f;
			return "Map " + textureSize + "x" + textureSize + " at " + pixelSize.ToString("0.#", CultureInfo.InvariantCulture) + " m a pixel, explore radius " + instance.m_exploreRadius.ToString("0", CultureInfo.InvariantCulture) + " m" + ((bitArray == null) ? ", and the explored bits could not be read." : ".");
		}

		private unsafe static void Creatures(Terminal term)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Say(term, "utangard creatures: no world loaded yet.");
				return;
			}
			Dictionary<string, int> kills = KillTally.Here();
			int full = Foothold.FullBar();
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
			Biome[] gateableBiomes = UtangardConfig.GateableBiomes;
			for (int i = 0; i < gateableBiomes.Length; i++)
			{
				Biome biome = gateableBiomes[i];
				List<KeyValuePair<string, int>> list = Foothold.Parse(UtangardConfig.PointsLineFor(biome));
				if (list.Count == 0)
				{
					continue;
				}
				Say(term, "utangard creatures: " + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString() + ", " + list.Count + " kinds, full bar " + full + " points, no kind above " + Foothold.KindCap(full) + ".");
				foreach (KeyValuePair<string, int> item in list)
				{
					Say(term, "    " + item.Value + " pt  " + Describe(instance, item.Key, kills, dictionary, ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString() + " " + item.Key + "=" + item.Value));
				}
			}
			Say(term, "utangard creatures: left out on purpose -");
			string[] leftOut = LeftOut;
			foreach (string text in leftOut)
			{
				Say(term, "    -     " + Describe(instance, text, kills, dictionary, "left out " + text));
			}
			int num = 0;
			foreach (KeyValuePair<string, List<string>> item2 in dictionary)
			{
				if (item2.Value.Count >= 2)
				{
					num++;
					Say(term, "utangard creatures: SHARED TOKEN " + item2.Key + " - " + string.Join("; ", item2.Value.ToArray()));
				}
			}
			Say(term, (num == 0) ? "utangard creatures: every creature has a token of its own." : ("utangard creatures: " + num + " token(s) shared - those creatures cannot be scored apart."));
			int num2 = 0;
			foreach (Foothold.Entry item3 in Foothold.Resolve(instance))
			{
				if (!string.IsNullOrEmpty(item3.Dropped))
				{
					num2++;
					Say(term, "utangard creatures: DROPPED " + ((object)Unsafe.As<Biome, Biome>(ref item3.Biome)/*cast due to .constrained prefix*/).ToString() + " " + item3.Prefab + " - " + item3.Dropped);
				}
			}
			Say(term, (num2 == 0) ? "utangard creatures: after the shared-name rule, every entry in the tables counts." : ("utangard creatures: after the shared-name rule, " + num2 + " entr(ies) do not count."));
		}

		private static string Describe(ZNetScene scene, string prefabName, Dictionary<string, int> kills, Dictionary<string, List<string>> byToken, string label)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			GameObject prefab = scene.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				return prefabName + " - NOT FOUND in ZNetScene";
			}
			Character val = default(Character);
			if (!prefab.TryGetComponent<Character>(ref val))
			{
				return prefabName + " - not a creature";
			}
			string text = "no AI";
			if ((Object)(object)prefab.GetComponent<MonsterAI>() != (Object)null)
			{
				text = "monster";
			}
			else if ((Object)(object)prefab.GetComponent<AnimalAI>() != (Object)null)
			{
				text = "ANIMAL";
			}
			string text2 = (((Object)(object)prefab.GetComponent<Tameable>() != (Object)null) ? ", tameable" : "");
			string text3 = ((text == "ANIMAL" || (int)val.m_faction == 1) ? "  <- prey" : "");
			string text4 = val.m_name ?? "";
			if (!byToken.TryGetValue(text4, out var value))
			{
				value = (byToken[text4] = new List<string>());
			}
			value.Add(label);
			int value2 = 0;
			string text5 = ((kills != null && kills.TryGetValue(text4, out value2)) ? value2.ToString(CultureInfo.InvariantCulture) : ((kills != null) ? "0" : "?"));
			return prefabName + " " + text4 + " " + val.m_health.ToString("0", CultureInfo.InvariantCulture) + "hp " + ((object)Unsafe.As<Faction, Faction>(ref val.m_faction)/*cast due to .constrained prefix*/).ToString() + " " + text + text2 + ", your kills here " + text5 + text3;
		}

		private static void Say(Terminal term, string line)
		{
			term.AddString(line);
			UtangardPlugin.Log.LogInfo((object)line);
		}

		static DevConsole()
		{
			Biome[] array = new Biome[9];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Order = (Biome[])(object)array;
			LeftOut = new string[11]
			{
				"Deer", "Bat", "Hare", "SeekerBrood", "Seal", "Seal_Pup", "Seal_pup", "Moose_calf", "Asksvin_hatchling", "AsksvinHatchling",
				"Skeleton_Swamps"
			};
		}
	}
	internal static class Discovery
	{
		[HarmonyPatch]
		internal static class Uncover
		{
			[HarmonyTargetMethod]
			private static MethodBase Target()
			{
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Minimap)))
				{
					if (!(declaredMethod.Name != "Explore") && !(declaredMethod.ReturnType != typeof(bool)))
					{
						ParameterInfo[] parameters = declaredMethod.GetParameters();
						if (parameters.Length == 2 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(int))
						{
							return declaredMethod;
						}
					}
				}
				UtangardPlugin.Log.LogError((object)"Minimap has no Explore(int, int) any more - the Discovery bar cannot follow new exploration, so healing stays locked in a locked biome.");
				return null;
			}

			[HarmonyPostfix]
			private static void Postfix(Minimap __instance, int __0, int __1, bool __result)
			{
				if (!__result || _gaveUp)
				{
					return;
				}
				try
				{
					if (__instance != _map)
					{
						Restart(__instance);
					}
					int num = __1 * __instance.m_textureSize + __0;
					if (_done || num < _cursor)
					{
						WorldGenerator instance = WorldGenerator.instance;
						if (instance != null)
						{
							Count(__instance, instance, __0, __1);
						}
					}
				}
				catch (Exception ex)
				{
					GiveUp(ex.ToString());
				}
			}
		}

		[HarmonyPatch]
		internal static class Rewrite
		{
			private static readonly string[] Names = new string[3] { "SetMapData", "Reset", "ExploreAll" };

			[HarmonyTargetMethods]
			private static IEnumerable<MethodBase> Targets()
			{
				List<MethodBase> list = new List<MethodBase>();
				string[] names = Names;
				foreach (string text in names)
				{
					bool flag = false;
					foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Minimap)))
					{
						if (!(declaredMethod.Name != text))
						{
							list.Add(declaredMethod);
							flag = true;
						}
					}
					if (!flag)
					{
						throw new MissingMethodException("Minimap", text);
					}
				}
				return list;
			}

			[HarmonyPrefix]
			private static void Prefix(Minimap __instance)
			{
				if ((Object)(object)__instance != (Object)null)
				{
					Restart(__instance);
				}
			}
		}

		private static Minimap _map;

		private static int _cursor;

		private static bool _done;

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

		private static readonly long Budget = Stopwatch.Frequency / 1000;

		private const int Slice = 256;

		private const float Edge = 10500f;

		private static bool _saidFailure;

		private static bool _gaveUp;

		private static FieldRef<Minimap, BitArray> _exploredOf;

		private static bool _exploredBound;

		internal static bool Available()
		{
			if (Seams.MapExplore && Seams.FoodTick && !_gaveUp)
			{
				return ExploredOf() != null;
			}
			return false;
		}

		internal static bool Counting()
		{
			return !_done;
		}

		internal static int Pixels(Biome biome)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || instance != _map)
			{
				return 0;
			}
			if (!Counts.TryGetValue(biome, out var value))
			{
				return 0;
			}
			return value;
		}

		internal static int FullPixels()
		{
			float value = UtangardConfig.DiscoveryFullKm2.Value;
			if (value <= 0f)
			{
				return 0;
			}
			float num = PixelKm2();
			if (num <= 0f)
			{
				return int.MaxValue;
			}
			return (int)Math.Ceiling(value / num);
		}

		internal static float PixelKm2()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0f;
			}
			float pixelSize = instance.m_pixelSize;
			return pixelSize * pixelSize / 1000000f;
		}

		internal static float Progress()
		{
			if (_done)
			{
				return 1f;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || instance != _map)
			{
				return 0f;
			}
			int num = instance.m_textureSize * instance.m_textureSize;
			if (num > 0)
			{
				return (float)_cursor / (float)num;
			}
			return 0f;
		}

		internal static void Step()
		{
			if (_gaveUp || !Seams.MapExplore || !UtangardConfig.Enabled.Value || !UtangardConfig.FootholdEnabled.Value)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (instance != _map)
			{
				Restart(instance);
			}
			if (_done)
			{
				return;
			}
			try
			{
				BitArray bitArray = Bits(instance);
				WorldGenerator instance2 = WorldGenerator.instance;
				if (bitArray == null || instance2 == null)
				{
					return;
				}
				int textureSize = instance.m_textureSize;
				int length = bitArray.Length;
				if (textureSize <= 0 || textureSize * textureSize != length)
				{