Decompiled source of TrueFinalAscent v1.0.4

BepInEx/plugins/CptPICHU-TrueFinalAscent/TrueFinalAscent.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Peak;
using Peak.Afflictions;
using Peak.LevelGeneration;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using Unity.Collections;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Rendering.UnifiedRayTracing;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TrueFinalAscent")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TrueFinalAscent")]
[assembly: AssemblyTitle("TrueFinalAscent")]
[assembly: AssemblyVersion("1.0.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 TrueFinalAscent
{
	[HarmonyPatch(typeof(UIPlayerNames), "UpdateName")]
	internal static class AscentNameColorPatch
	{
		private static void Postfix(UIPlayerNames __instance, int index, bool visible)
		{
			if (!visible || __instance.playerNameText == null || index < 0 || index >= __instance.playerNameText.Length)
			{
				return;
			}
			PlayerName val = __instance.playerNameText[index];
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val.text == (Object)null) && !((Object)(object)val.characterInteractable == (Object)null))
			{
				Character character = val.characterInteractable.character;
				if (!((Object)(object)character == (Object)null))
				{
					AscentNameColor.Apply((TMP_Text)(object)val.text, character);
				}
			}
		}
	}
	[HarmonyPatch(typeof(AudioLevelSlider), "Update")]
	internal static class AscentAudioSliderColorPatch
	{
		private static void Postfix(AudioLevelSlider __instance)
		{
			if (__instance.player != null && !((Object)(object)__instance.playerName == (Object)null))
			{
				Character playerCharacter = PlayerHandler.GetPlayerCharacter(__instance.player);
				if (!((Object)(object)playerCharacter == (Object)null))
				{
					AscentNameColor.Apply((TMP_Text)(object)__instance.playerName, playerCharacter);
				}
			}
		}
	}
	[HarmonyPatch(typeof(EndScreenScoutWindow), "Init", new Type[] { typeof(Character) })]
	internal static class AscentEndScreenNamePatch
	{
		private static void Postfix(EndScreenScoutWindow __instance, Character character)
		{
			if (!((Object)(object)character == (Object)null) && !((Object)(object)__instance.scoutName == (Object)null))
			{
				AscentNameColor.Apply(__instance.scoutName, character);
			}
		}
	}
	internal static class AscentNameColor
	{
		internal const int TotalAscents = 8;

		private const float FontSizeMultiplier = 1.15f;

		private static readonly Dictionary<TMP_Text, float> ConfiguredTexts = new Dictionary<TMP_Text, float>();

		internal static int GetClearedCount(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return 0;
			}
			AscentSync component = ((Component)character).GetComponent<AscentSync>();
			return Mathf.Clamp(((Object)(object)component != (Object)null) ? component.Cleared : 0, 0, 8);
		}

		internal static void Apply(TMP_Text text, Character character)
		{
			EnsureBaseSetup(text);
			AscentSync ascentSync = (((Object)(object)character != (Object)null) ? ((Component)character).GetComponent<AscentSync>() : null);
			int clearedCount = GetClearedCount(character);
			NameStyle wanted = (((Object)(object)ascentSync != (Object)null) ? ascentSync.Style : NameStyle.Auto);
			bool final = (Object)(object)ascentSync != (Object)null && ascentSync.FinalDone;
			AscentTitle.Paint(text, AscentTitle.Resolve(wanted, clearedCount, final));
		}

		private static void EnsureBaseSetup(TMP_Text text)
		{
			if (!ConfiguredTexts.ContainsKey(text))
			{
				float fontSize = text.fontSize;
				ConfiguredTexts[text] = fontSize;
				text.fontSize = fontSize * 1.15f;
			}
		}
	}
	[HarmonyPatch(typeof(PauseMenuHandler), "OnEnable")]
	internal static class AscentOverviewPatch
	{
		private static void Postfix(PauseMenuHandler __instance)
		{
			AscentOverviewPanel.EnsureCreated(((Component)__instance).transform);
		}
	}
	internal static class AscentOverviewPanel
	{
		private const string GreenHex = "#4CD964";

		private const string GrayHex = "#585858";

		private static readonly Color Slate = new Color(0.13f, 0.13f, 0.145f, 1f);

		private static GameObject _root;

		private static TextMeshProUGUI _text;

		private static TMP_Dropdown _pick;

		private static readonly List<NameStyle> Offered = new List<NameStyle>();

		internal static void EnsureCreated(Transform parent)
		{
			if ((Object)(object)_root == (Object)null)
			{
				Build(parent);
			}
			((TMP_Text)_text).text = BuildText();
			FillChoices(parent);
		}

		private static void Build(Transform parent)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			_root = new GameObject("PiCHU_AscentOverview", new Type[1] { typeof(RectTransform) });
			_root.transform.SetParent(parent, false);
			RectTransform component = _root.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0f);
			component.anchorMax = new Vector2(0f, 0f);
			component.pivot = new Vector2(0f, 0f);
			component.anchoredPosition = new Vector2(32f, 32f);
			component.sizeDelta = new Vector2(340f, 270f);
			GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_root.transform, false);
			RectTransform component2 = val.GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(0f, 0f);
			component2.anchorMax = new Vector2(0f, 0f);
			component2.pivot = new Vector2(0f, 0f);
			component2.anchoredPosition = Vector2.zero;
			component2.sizeDelta = new Vector2(340f, 180f);
			_text = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_text).fontSize = 34f;
			((TMP_Text)_text).lineSpacing = 8f;
			((TMP_Text)_text).alignment = (TextAlignmentOptions)1025;
			((TMP_Text)_text).textWrappingMode = (TextWrappingModes)0;
			((Graphic)_text).raycastTarget = false;
			ApplyPeakFont(_text, parent);
			_pick = FinalAscentUiKit.Drop(_root.transform, 0f, 0f, 300f, 58f);
			RectTransform component3 = ((Component)_pick).GetComponent<RectTransform>();
			component3.anchorMin = new Vector2(0f, 0f);
			component3.anchorMax = new Vector2(0f, 0f);
			component3.pivot = new Vector2(0f, 0f);
			component3.anchoredPosition = new Vector2(0f, 196f);
			component3.sizeDelta = new Vector2(300f, 58f);
			Darken(_pick);
			_root.AddComponent<AscentChoicePreview>().Bind(_pick, Offered);
		}

		private static void Darken(TMP_Dropdown drop)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Image component = ((Component)drop).GetComponent<Image>();
			if ((Object)(object)component != (Object)null)
			{
				((Graphic)component).color = Slate;
			}
			if ((Object)(object)drop.template != (Object)null)
			{
				Image component2 = ((Component)drop.template).GetComponent<Image>();
				if ((Object)(object)component2 != (Object)null)
				{
					((Graphic)component2).color = Slate;
				}
			}
			TMP_Text[] componentsInChildren = ((Component)drop).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && val.text == "▾")
				{
					((Graphic)val).color = new Color(0.78f, 0.78f, 0.8f, 1f);
				}
			}
		}

		private static void FillChoices(Transform parent)
		{
			int cleared = AscentTitle.ClearedLocally();
			bool finalDone = AscentTitle.FinalDone;
			Offered.Clear();
			List<string> list = new List<string>();
			NameStyle[] array = new NameStyle[6]
			{
				NameStyle.Auto,
				NameStyle.Plain,
				NameStyle.Silver,
				NameStyle.Gold,
				NameStyle.Diamond,
				NameStyle.Rainbow
			};
			foreach (NameStyle nameStyle in array)
			{
				if (AscentTitle.Unlocked(nameStyle, cleared, finalDone))
				{
					Offered.Add(nameStyle);
					list.Add(AscentTitle.TitleOf(nameStyle));
				}
			}
			((UnityEventBase)_pick.onValueChanged).RemoveAllListeners();
			_pick.ClearOptions();
			_pick.AddOptions(list);
			int num = Offered.IndexOf(AscentTitle.Chosen);
			_pick.value = ((num >= 0) ? num : 0);
			_pick.RefreshShownValue();
			((UnityEvent<int>)(object)_pick.onValueChanged).AddListener((UnityAction<int>)delegate(int picked)
			{
				if (picked >= 0 && picked < Offered.Count)
				{
					AscentTitle.Choose(Offered[picked]);
				}
			});
			ApplyNameLook(parent);
		}

		private static void ApplyNameLook(Transform parent)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = null;
			AudioLevelSlider[] array = (((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentsInChildren<AudioLevelSlider>(true) : null);
			if (array != null)
			{
				AudioLevelSlider[] array2 = array;
				foreach (AudioLevelSlider val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null && (Object)(object)val2.playerName != (Object)null)
					{
						val = (TMP_Text)(object)val2.playerName;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				AudioLevelSlider val3 = Object.FindFirstObjectByType<AudioLevelSlider>();
				val = (TMP_Text)(object)(((Object)(object)val3 != (Object)null && (Object)(object)val3.playerName != (Object)null) ? val3.playerName : _text);
			}
			TMP_Text[] componentsInChildren = ((Component)_pick).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val4 in componentsInChildren)
			{
				if (!((Object)(object)val4 == (Object)null) && !(val4.text == "▾"))
				{
					if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null)
					{
						val4.font = val.font;
						val4.fontSharedMaterial = val.fontSharedMaterial;
						val4.fontStyle = val.fontStyle;
					}
					val4.enableAutoSizing = true;
					val4.fontSizeMin = 16f;
					val4.fontSizeMax = 30f;
				}
			}
		}

		private static void ApplyPeakFont(TextMeshProUGUI text, Transform searchRoot)
		{
			TMP_Text val = ((Component)searchRoot).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)val == (Object)null)
			{
				val = Object.FindFirstObjectByType<TMP_Text>();
			}
			if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null)
			{
				((TMP_Text)text).font = val.font;
				((TMP_Text)text).fontSharedMaterial = val.fontSharedMaterial;
			}
		}

		private static string BuildText()
		{
			int num = AscentTitle.ClearedLocally();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("ASCENT\n");
			for (int i = 1; i <= 8; i++)
			{
				bool flag = i <= num;
				stringBuilder.Append("<color=").Append(flag ? "#4CD964" : "#585858").Append('>')
					.Append(i)
					.Append("</color>");
				if (i == 4)
				{
					stringBuilder.Append('\n');
				}
				else if (i < 8)
				{
					stringBuilder.Append(' ');
				}
			}
			stringBuilder.Append("\n<color=").Append(AscentTitle.FinalDone ? "#4CD964" : "#585858").Append(">FINAL</color>");
			return stringBuilder.ToString();
		}
	}
	internal class AscentChoicePreview : MonoBehaviour
	{
		private TMP_Dropdown _pick;

		private List<NameStyle> _offered;

		internal void Bind(TMP_Dropdown pick, List<NameStyle> offered)
		{
			_pick = pick;
			_offered = offered;
		}

		private void LateUpdate()
		{
			if ((Object)(object)_pick == (Object)null || _offered == null || _offered.Count == 0)
			{
				return;
			}
			int cleared = AscentTitle.ClearedLocally();
			bool finalDone = AscentTitle.FinalDone;
			if ((Object)(object)_pick.captionText != (Object)null)
			{
				int index = Mathf.Clamp(_pick.value, 0, _offered.Count - 1);
				AscentTitle.Paint(_pick.captionText, AscentTitle.Resolve(_offered[index], cleared, finalDone));
			}
			int num = 0;
			Toggle[] componentsInChildren = ((Component)_pick).GetComponentsInChildren<Toggle>(true);
			foreach (Toggle val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeSelf && (!((Object)(object)_pick.template != (Object)null) || !((Component)val).transform.IsChildOf((Transform)(object)_pick.template)))
				{
					if (num >= _offered.Count)
					{
						break;
					}
					TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						AscentTitle.Paint(componentInChildren, AscentTitle.Resolve(_offered[num], cleared, finalDone));
					}
					num++;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class AscentSyncAttachPatch
	{
		private static void Postfix(Character __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<AscentSync>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<AscentSync>();
			}
		}
	}
	public class AscentSync : MonoBehaviour
	{
		private const string Tag = "[FinalAscent]";

		private const string Key = "FA_T";

		private static string _published;

		private Character _character;

		private string _seen;

		private int _cleared;

		private NameStyle _style;

		private bool _final;

		internal int Cleared
		{
			get
			{
				Refresh();
				return _cleared;
			}
		}

		internal NameStyle Style
		{
			get
			{
				Refresh();
				return _style;
			}
		}

		internal bool FinalDone
		{
			get
			{
				Refresh();
				return _final;
			}
		}

		private void Awake()
		{
			_character = ((Component)this).GetComponent<Character>();
		}

		private void Start()
		{
			if ((Object)(object)_character != (Object)null && _character.IsLocal)
			{
				((MonoBehaviour)this).StartCoroutine(AnnounceRoutine());
			}
		}

		internal static void BroadcastLocal()
		{
			Publish();
			Courtesy();
		}

		private static void Publish()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_006a: Expected O, but got Unknown
			string text = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}", AscentTitle.ClearedLocally(), (int)AscentTitle.Chosen, AscentTitle.FinalDone ? 1 : 0);
			if (!(text == _published))
			{
				_published = text;
				if (PhotonNetwork.LocalPlayer != null)
				{
					Player localPlayer = PhotonNetwork.LocalPlayer;
					Hashtable val = new Hashtable();
					((Dictionary<object, object>)val).Add((object)"FA_T", (object)text);
					localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
					Plugin.Detail("[FinalAscent] Namensfarbe im Raum hinterlegt: " + text + ".");
				}
			}
		}

		private static void Courtesy()
		{
			Character localCharacter = Character.localCharacter;
			if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)((MonoBehaviourPun)localCharacter).photonView == (Object)null) && ((MonoBehaviourPun)localCharacter).photonView.IsMine)
			{
				((MonoBehaviourPun)localCharacter).photonView.RPC("RPC_ReceiveTitle", (RpcTarget)1, new object[3]
				{
					AscentTitle.ClearedLocally(),
					(int)AscentTitle.Chosen,
					AscentTitle.FinalDone
				});
			}
		}

		private IEnumerator AnnounceRoutine()
		{
			float timeout = 5f;
			while (timeout > 0f && ((Object)(object)((MonoBehaviourPun)_character).photonView == (Object)null || !((MonoBehaviourPun)_character).photonView.IsMine))
			{
				timeout -= Time.deltaTime;
				yield return null;
			}
			BroadcastLocal();
			for (int i = 0; i < 10; i++)
			{
				yield return (object)new WaitForSeconds(1f);
				if ((Object)(object)_character == (Object)null)
				{
					break;
				}
				Publish();
			}
		}

		private void Refresh()
		{
			if (!((Object)(object)_character == (Object)null) && !((Object)(object)((MonoBehaviourPun)_character).photonView == (Object)null))
			{
				Player owner = ((MonoBehaviourPun)_character).photonView.Owner;
				if (owner != null && owner.CustomProperties != null && ((Dictionary<object, object>)(object)owner.CustomProperties).TryGetValue((object)"FA_T", out object value) && value is string text && !(text == _seen))
				{
					_seen = text;
					Parse(text);
				}
			}
		}

		private void Parse(string line)
		{
			string[] array = line.Split('|');
			if (array.Length >= 3 && int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3))
			{
				_cleared = result;
				_style = (NameStyle)result2;
				_final = result3 != 0;
			}
		}

		[PunRPC]
		private void RPC_ReceiveTitle(int cleared, int style, bool final)
		{
			if (_seen == null)
			{
				_cleared = cleared;
				_style = (NameStyle)style;
				_final = final;
			}
		}
	}
	internal enum NameStyle
	{
		Auto,
		Plain,
		Silver,
		Gold,
		Diamond,
		Rainbow
	}
	internal static class AscentTitle
	{
		private const string Tag = "[FinalAscent]";

		private const string FileName = "TrueFinalAscent.Namensfarbe.txt";

		private const string SealFileName = "TrueFinalAscent.Siegel.txt";

		private const string OldFileName = "PiCHUPatches.Namensfarbe.txt";

		internal const int TotalAscents = 8;

		private static string _sealText;

		private static bool _confirmed;

		private static bool _warned;

		private static float _lastTry = -99f;

		private static bool _frozen;

		private static string[] _who;

		private static readonly Color32 SilverColor = new Color32((byte)202, (byte)214, (byte)230, byte.MaxValue);

		private static readonly Color32 GoldColor = new Color32(byte.MaxValue, (byte)178, (byte)0, byte.MaxValue);

		private static readonly Color32 DiamondColor = new Color32((byte)150, (byte)232, byte.MaxValue, byte.MaxValue);

		private static string Path => FinalAscentStore.State("TrueFinalAscent.Namensfarbe.txt");

		private static string SealPath => FinalAscentStore.State("TrueFinalAscent.Siegel.txt");

		private static string ReadPath => FinalAscentStore.ReadPath("TrueFinalAscent.Namensfarbe.txt", "PiCHUPatches.Namensfarbe.txt");

		private static string SealReadPath => FinalAscentStore.ReadPath("TrueFinalAscent.Siegel.txt");

		internal static bool FinalDone
		{
			get
			{
				if (_confirmed)
				{
					return true;
				}
				if (string.IsNullOrEmpty(_sealText) || _sealText == "-")
				{
					return false;
				}
				if (_frozen && _lastTry > -99f)
				{
					return false;
				}
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (realtimeSinceStartup - _lastTry < 1f)
				{
					return false;
				}
				_lastTry = realtimeSinceStartup;
				if (Matches(_sealText))
				{
					_confirmed = true;
					Plugin.Log.LogInfo((object)"[FinalAscent] Siegel bestaetigt - der Finale Aufstieg gilt als geschafft, der Regenbogen ist waehlbar.");
					return true;
				}
				if (_frozen && !_warned)
				{
					_warned = true;
					Plugin.Log.LogWarning((object)"[FinalAscent] Das Siegel in TrueFinalAscent.Siegel.txt passt zu keiner Kennung dieses Kontos - der Finale Aufstieg gilt als offen. Das passiert, wenn die Zeile von Hand geaendert oder von einem anderen Konto uebernommen wurde. Die Zeile bleibt trotzdem stehen; sie wird nie ueberschrieben.");
				}
				return false;
			}
		}

		internal static NameStyle Chosen { get; private set; } = NameStyle.Auto;

		internal static int ClearedLocally()
		{
			AchievementManager instance = Singleton<AchievementManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			return Mathf.Clamp(instance.GetMaxAscent() - 1, 0, 8);
		}

		internal static bool Unlocked(NameStyle style, int cleared, bool final)
		{
			switch (style)
			{
			case NameStyle.Auto:
			case NameStyle.Plain:
				return true;
			case NameStyle.Silver:
				return cleared >= 1;
			case NameStyle.Gold:
				return cleared >= 4;
			case NameStyle.Diamond:
				return cleared >= 8;
			case NameStyle.Rainbow:
				return final;
			default:
				return false;
			}
		}

		internal static NameStyle Resolve(NameStyle wanted, int cleared, bool final)
		{
			if (wanted != NameStyle.Auto && Unlocked(wanted, cleared, final))
			{
				return wanted;
			}
			if (final)
			{
				return NameStyle.Rainbow;
			}
			if (cleared >= 8)
			{
				return NameStyle.Diamond;
			}
			if (cleared >= 4)
			{
				return NameStyle.Gold;
			}
			if (cleared < 1)
			{
				return NameStyle.Plain;
			}
			return NameStyle.Silver;
		}

		internal static string TitleOf(NameStyle style)
		{
			return style switch
			{
				NameStyle.Plain => FinalAscentText.Get("name.plain"), 
				NameStyle.Silver => FinalAscentText.Get("name.silver"), 
				NameStyle.Gold => FinalAscentText.Get("name.gold"), 
				NameStyle.Diamond => FinalAscentText.Get("name.diamond"), 
				NameStyle.Rainbow => FinalAscentText.Get("name.rainbow"), 
				_ => FinalAscentText.Get("name.auto"), 
			};
		}

		internal static void Choose(NameStyle style)
		{
			if (Chosen != style)
			{
				Chosen = style;
				Save();
				AscentSync.BroadcastLocal();
				Plugin.Log.LogInfo((object)("[FinalAscent] Namensfarbe gewaehlt: " + TitleOf(style) + "."));
			}
		}

		internal static void MarkFinalDone()
		{
			if (!FinalDone)
			{
				_confirmed = true;
				Save();
				AscentSync.BroadcastLocal();
				Plugin.Log.LogInfo((object)"[FinalAscent] FINALER AUFSTIEG GESCHAFFT - der Regenbogen ist ab jetzt waehlbar, und er bleibt es ueber Spielstarts hinweg (TrueFinalAscent.Siegel.txt).");
			}
		}

		internal static void Load()
		{
			bool flag = Read(SealReadPath, SealPath);
			if (Read(ReadPath, Path))
			{
				flag = true;
			}
			Plugin.Log.LogInfo((object)("[FinalAscent] Namensfarbe uebernommen: " + TitleOf(Chosen) + ", Finaler Aufstieg " + (FinalDone ? "geschafft" : "offen") + "."));
			if (flag)
			{
				Save();
			}
		}

		private static bool Read(string from, string want)
		{
			string fileName = System.IO.Path.GetFileName(from);
			try
			{
				if (!File.Exists(from))
				{
					return false;
				}
				string[] array = File.ReadAllLines(from, Encoding.UTF8);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					int num = text.IndexOf('=');
					if (text.Length == 0 || text[0] == '#' || num <= 0)
					{
						continue;
					}
					string text2 = text.Substring(0, num).Trim();
					string text3 = text.Substring(num + 1).Trim();
					switch (text2)
					{
					case "siegel":
						if (text3 != "-" && !string.IsNullOrEmpty(text3))
						{
							_sealText = text3;
						}
						break;
					case "final":
						if (text3 == "1")
						{
							_confirmed = true;
						}
						break;
					case "farbe":
					{
						if (Enum.TryParse<NameStyle>(text3, ignoreCase: true, out var result))
						{
							Chosen = result;
						}
						break;
					}
					}
				}
				return !string.Equals(from, want, StringComparison.OrdinalIgnoreCase);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] " + fileName + " nicht lesbar (" + ex.Message + ") - Vorgaben gelten."));
				return false;
			}
		}

		internal static void Save()
		{
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# Der geschaffte Finale Aufstieg. Wird geschrieben,");
				stringBuilder.AppendLine("# sobald er lebend auf PEAK oder hoeher beendet wurde.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# Die Zahlen darin sind an dieses Steam-Konto gebunden");
				stringBuilder.AppendLine("# und lassen sich nicht sinnvoll von Hand setzen - wer es");
				stringBuilder.AppendLine("# versucht, bekommt beim naechsten Start eine Warnung im");
				stringBuilder.AppendLine("# Log und einen offenen Finalen Aufstieg.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# Eigene Datei, damit das Zuruecksetzen der Namensfarbe");
				stringBuilder.AppendLine("# nicht die Auszeichnung mitnimmt.");
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("siegel = " + SealLine());
				File.WriteAllText(SealPath, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
				StringBuilder stringBuilder2 = new StringBuilder();
				stringBuilder2.AppendLine("# Die gewaehlte Namensfarbe. Wird geschrieben, sobald");
				stringBuilder2.AppendLine("# im Pausenmenue eine ausgesucht wird.");
				stringBuilder2.AppendLine("#");
				stringBuilder2.AppendLine("# farbe: Auto, Plain, Silver, Gold, Diamond, Rainbow");
				stringBuilder2.AppendLine("# Auto nimmt immer die hoechste freigeschaltete.");
				stringBuilder2.AppendLine("#");
				stringBuilder2.AppendLine("# Diese Datei liegt in AppData und NICHT in BepInEx/config:");
				stringBuilder2.AppendLine("# ein geteilter r2modman-Profilcode ueberschreibt config,");
				stringBuilder2.AppendLine("# und daran soll kein erkletterter Aufstieg zerbrechen.");
				stringBuilder2.AppendLine();
				stringBuilder2.AppendLine($"farbe = {Chosen}");
				File.WriteAllText(Path, stringBuilder2.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
				FinalAscentStore.Retire("TrueFinalAscent.Siegel.txt");
				FinalAscentStore.Retire("TrueFinalAscent.Namensfarbe.txt", "PiCHUPatches.Namensfarbe.txt");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] TrueFinalAscent.Namensfarbe.txt nicht schreibbar: " + ex.Message));
			}
		}

		private static string[] Who()
		{
			if (_who != null)
			{
				return _who;
			}
			string text = SteamId();
			List<string> list = new List<string>(2);
			if (text != null)
			{
				list.Add("steam:" + text);
			}
			list.Add(Environment.MachineName + "|" + Environment.UserName);
			string[] array = list.ToArray();
			if (text != null)
			{
				_who = array;
				_frozen = true;
			}
			return array;
		}

		private static IEnumerable<string> Identities()
		{
			return Who();
		}

		private static string SteamId()
		{
			try
			{
				Type type = AccessTools.TypeByName("Steamworks.SteamUser");
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "GetSteamID", (Type[])null, (Type[])null) : null);
				object obj = ((methodInfo != null) ? methodInfo.Invoke(null, null) : null);
				if (obj == null)
				{
					return null;
				}
				FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "m_SteamID");
				string text = ((fieldInfo != null) ? Convert.ToString(fieldInfo.GetValue(obj)) : obj.ToString());
				if (string.IsNullOrEmpty(text) || text.Length < 6)
				{
					return null;
				}
				string text2 = text;
				foreach (char c in text2)
				{
					if (c < '0' || c > '9')
					{
						return null;
					}
				}
				return (text == "0") ? null : text;
			}
			catch
			{
				return null;
			}
		}

		private static string Seal(string identity)
		{
			try
			{
				using HMACSHA256 hMACSHA = new HMACSHA256(Encoding.UTF8.GetBytes("PiCHUPatches/FinalAscent/Siegel/v1"));
				byte[] array = hMACSHA.ComputeHash(Encoding.UTF8.GetBytes("final|" + identity));
				StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
				byte[] array2 = array;
				foreach (byte b in array2)
				{
					stringBuilder.Append(b.ToString("x2"));
				}
				return stringBuilder.ToString();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] Siegel nicht zu rechnen: " + ex.Message));
				return "-";
			}
		}

		private static string SealLine()
		{
			List<string> list = new List<string>();
			if (!string.IsNullOrEmpty(_sealText) && _sealText != "-")
			{
				string[] array = _sealText.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length > 0 && !list.Contains(text))
					{
						list.Add(text);
					}
				}
			}
			if (_confirmed)
			{
				string[] array = Who();
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = Seal(array[i]);
					if (text2 != "-" && !list.Contains(text2))
					{
						list.Add(text2);
					}
				}
			}
			if (list.Count == 0)
			{
				return "-";
			}
			_sealText = string.Join(",", list.ToArray());
			return _sealText;
		}

		private static string Seals()
		{
			List<string> list = new List<string>();
			foreach (string item in Identities())
			{
				string text = Seal(item);
				if (text != "-" && !list.Contains(text))
				{
					list.Add(text);
				}
			}
			if (list.Count != 0)
			{
				return string.Join(",", list.ToArray());
			}
			return "-";
		}

		private static bool Matches(string stored)
		{
			if (string.IsNullOrEmpty(stored) || stored == "-")
			{
				return false;
			}
			foreach (string item in Identities())
			{
				string text = Seal(item);
				if (text == "-")
				{
					continue;
				}
				string[] array = stored.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i].Trim() == text)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static void Paint(TMP_Text text, NameStyle style)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)text == (Object)null)
			{
				return;
			}
			((Graphic)text).color = Color.white;
			text.ForceMeshUpdate(false, false);
			TMP_TextInfo textInfo = text.textInfo;
			int characterCount = textInfo.characterCount;
			if (characterCount == 0)
			{
				return;
			}
			for (int i = 0; i < characterCount; i++)
			{
				TMP_CharacterInfo val = textInfo.characterInfo[i];
				if (val.isVisible)
				{
					Color32 val2 = ColorFor(style, i, characterCount);
					Color32[] colors = textInfo.meshInfo[val.materialReferenceIndex].colors32;
					int vertexIndex = val.vertexIndex;
					colors[vertexIndex] = val2;
					colors[vertexIndex + 1] = val2;
					colors[vertexIndex + 2] = val2;
					colors[vertexIndex + 3] = val2;
				}
			}
			for (int j = 0; j < textInfo.meshInfo.Length; j++)
			{
				textInfo.meshInfo[j].mesh.colors32 = textInfo.meshInfo[j].colors32;
				text.UpdateGeometry(textInfo.meshInfo[j].mesh, j);
			}
		}

		private static Color32 ColorFor(NameStyle style, int index, int count)
		{
			//IL_0023: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			switch (style)
			{
			case NameStyle.Silver:
				return Shimmer(SilverColor, index, time, 2f, 0.2f);
			case NameStyle.Gold:
				return Shimmer(GoldColor, index, time, 2.5f, 0.35f);
			case NameStyle.Diamond:
			{
				float num = Mathf.Max(count, 1);
				float num2 = time * 0.9f % 1.6f * num - num * 0.3f;
				float num3 = Mathf.Abs((float)index - num2);
				float num4 = Mathf.Exp((0f - num3) * num3 * 0.9f);
				float num6 = default(float);
				float num7 = default(float);
				float num5 = default(float);
				Color.RGBToHSV(Color32.op_Implicit(DiamondColor), ref num5, ref num6, ref num7);
				num5 = Mathf.Repeat(num5 + Mathf.Sin(time * 1.3f + (float)index * 0.25f) * 0.045f, 1f);
				return Color32.Lerp(Color32.op_Implicit(Color.HSVToRGB(num5, num6 * (1f - 0.75f * num4), num7)), Color32.op_Implicit(Color.white), num4 * 0.85f);
			}
			case NameStyle.Rainbow:
				return Color32.op_Implicit(Color.HSVToRGB(Mathf.Repeat(time * 0.35f + (float)index * 0.085f, 1f), 0.82f, 1f));
			default:
				return Color32.op_Implicit(Color.white);
			}
		}

		private static Color32 Shimmer(Color32 baseColor, int index, float now, float speed, float intensity)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			float num = 0.5f + 0.5f * Mathf.Sin(now * speed + (float)index * 0.4f);
			return Color32.Lerp(baseColor, Color32.op_Implicit(Color.white), num * intensity);
		}
	}
	[HarmonyPatch(typeof(Character), "RPCEndGame")]
	internal static class AscentFinalWinPatch
	{
		private static void Postfix()
		{
			try
			{
				if (!FinalAscent.Active || AscentTitle.FinalDone)
				{
					return;
				}
				Character localCharacter = Character.localCharacter;
				if ((Object)(object)localCharacter == (Object)null)
				{
					return;
				}
				if ((Object)(object)localCharacter.data != (Object)null && localCharacter.data.dead)
				{
					Plugin.Log.LogInfo((object)"[FinalAscent] Finaler Aufstieg gewonnen - aber nicht von diesem Spieler: er war am Ziel tot. Kein Regenbogen.");
				}
				else if (Character.CheckWinCondition(localCharacter))
				{
					if (Ascents.currentAscent < 0)
					{
						Plugin.Log.LogInfo((object)("[FinalAscent] Finaler Aufstieg geschafft - aber auf " + FinalAscentDifficulty.TitleOf(Ascents.currentAscent) + ". Der Regenbogen wird ab PEAK vergeben, nicht auf Tenderfoot."));
					}
					else
					{
						AscentTitle.MarkFinalDone();
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] Siegpruefung fuer den Regenbogen: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "AddAffliction", new Type[]
	{
		typeof(Affliction),
		typeof(bool)
	})]
	internal static class EnergyDrinkComboPatch
	{
		internal static bool SuppressCombo;

		private static bool Prefix(CharacterAfflictions __instance, Affliction affliction, bool fromRPC)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			if (Plugin.EnergyComboOn != null && !Plugin.EnergyComboOn.Value)
			{
				return true;
			}
			if (SuppressCombo || fromRPC || affliction == null)
			{
				return true;
			}
			if ((int)affliction.GetAfflictionType() != 2)
			{
				return true;
			}
			if ((Object)(object)__instance.character == (Object)null || !__instance.character.IsLocal)
			{
				return true;
			}
			Affliction val = default(Affliction);
			if (!__instance.HasAfflictionType((AfflictionType)2, ref val))
			{
				return true;
			}
			EnergyDrinkComboEffect.Trigger(__instance, affliction);
			return false;
		}
	}
	internal class EnergyDrinkComboEffect : MonoBehaviour
	{
		private const float ComboSeconds = 13f;

		private static EnergyDrinkComboEffect _instance;

		private void Awake()
		{
			_instance = this;
		}

		public static void Trigger(CharacterAfflictions afflictions, Affliction incomingEnergyDrink)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"EnergyDrinkComboEffect nicht bereit, Combo übersprungen.");
				}
			}
			else
			{
				((MonoBehaviour)_instance).StartCoroutine(_instance.ComboRoutine(afflictions, incomingEnergyDrink));
			}
		}

		private IEnumerator ComboRoutine(CharacterAfflictions afflictions, Affliction incomingEnergyDrink)
		{
			Affliction_FasterBoi val = (Affliction_FasterBoi)incomingEnergyDrink.Copy();
			((Affliction)val).totalTime = 13f;
			EnergyDrinkComboPatch.SuppressCombo = true;
			try
			{
				afflictions.AddAffliction((Affliction)(object)val, false);
				afflictions.AddAffliction((Affliction)new Affliction_InfiniteStamina(13f), false);
			}
			finally
			{
				EnergyDrinkComboPatch.SuppressCombo = false;
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"Energy-Combo: {13f:0}s Energy + Rainbow Stamina aktiviert.");
			}
			yield return (object)new WaitForSeconds(13f);
			for (int i = 0; i < 10; i++)
			{
				if ((Object)(object)afflictions == (Object)null || (Object)(object)afflictions.character == (Object)null)
				{
					yield break;
				}
				afflictions.AddStatus((STATUSTYPE)6, 0.1f, false, true, true, false);
				yield return (object)new WaitForSeconds(1f);
			}
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"Energy-Combo: Sleep-Aufbau abgeschlossen (100).");
			}
		}
	}
	internal static class FinalAscent
	{
		internal const int AscentMarker = 1337;

		private const int MarkerSpan = 64;

		internal const string SceneName = "WilIsland";

		internal static bool Active { get; set; }

		internal static int Seed { get; set; }

		internal static bool Marked(int ascent)
		{
			if (ascent >= 1329)
			{
				return ascent <= 1401;
			}
			return false;
		}

		internal static int GradeOf(int ascent)
		{
			return Mathf.Clamp(ascent - 1337, -1, 64);
		}
	}
	public class FinalAscentSeedSync : MonoBehaviour
	{
		[PunRPC]
		public void RPC_FinalAscentSetup(int seed, string variants)
		{
			FinalAscent.Seed = seed;
			FinalAscentSetup.RandomVariants = string.IsNullOrEmpty(variants);
			FinalAscentSetup.Deserialize(variants);
			Plugin.Log.LogInfo((object)($"[FinalAscent] Vom Host uebernommen: Seed {seed}, Varianten " + (FinalAscentSetup.RandomVariants ? "zufaellig." : variants)));
		}
	}
	[HarmonyPatch(typeof(AirportCheckInKiosk), "Awake")]
	internal static class FinalAscentSeedSyncAttachPatch
	{
		private static void Postfix(AirportCheckInKiosk __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<FinalAscentSeedSync>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<FinalAscentSeedSync>();
			}
		}
	}
	[HarmonyPatch(typeof(AirportCheckInKiosk), "LoadIslandMaster")]
	internal static class FinalAscentLoadIslandPatch
	{
		private static bool Prefix(AirportCheckInKiosk __instance, int ascent, byte[] serializedRunSettings)
		{
			if (ascent != 1337 || !PhotonNetwork.IsMasterClient || !FinalAscentSetupWindow.Confirmed)
			{
				return true;
			}
			FinalAscent.Seed = FinalAscentSetup.ChosenSeed;
			Plugin.Log.LogInfo((object)string.Format("[FinalAscent] Lade {0} fuer Final Ascent, Seed={1}.", "WilIsland", FinalAscent.Seed));
			string text = (FinalAscentSetup.RandomVariants ? "" : FinalAscentSetup.Serialize());
			((MonoBehaviourPun)__instance).photonView.RPC("RPC_FinalAscentSetup", (RpcTarget)0, new object[2]
			{
				FinalAscent.Seed,
				text
			});
			FinalAscentRoom.Publish(FinalAscent.Seed, text);
			FinalAscentDifficulty.Publish(FinalAscentDifficulty.Chosen);
			((MonoBehaviourPun)__instance).photonView.RPC("BeginIslandLoadRPC", (RpcTarget)0, new object[3]
			{
				"WilIsland",
				1337 + FinalAscentDifficulty.Chosen,
				serializedRunSettings
			});
			return false;
		}
	}
	[HarmonyPatch(typeof(AirportCheckInKiosk), "BeginIslandLoadRPC")]
	internal static class FinalAscentBeginLoadPatch
	{
		private static void Prefix(string sceneName, ref int ascent)
		{
			FinalAscent.Active = FinalAscent.Marked(ascent);
			if (!FinalAscent.Active)
			{
				FinalAscentRoom.ClearRoom("ein normaler Lauf beginnt");
				FinalAscentHurtWatch.Reset();
				return;
			}
			ascent = FinalAscent.GradeOf(ascent);
			FinalAscentHurtWatch.Reset();
			Plugin.Log.LogInfo((object)("[FinalAscent] Lauf als Final Ascent markiert, Schwierigkeit " + FinalAscentDifficulty.TitleOf(ascent) + " (Ascent " + ascent + ")."));
		}
	}
	internal static class FinalAscentAmbience
	{
		private const string Tag = "[FinalAscent]";

		private const float Unreachable = 999999f;

		private static readonly FieldInfo PlayedTropicsField = AccessTools.Field(typeof(AmbienceAudio), "playedTropicsOrRoots");

		private static readonly FieldInfo PlayedAlpineField = AccessTools.Field(typeof(AmbienceAudio), "playedAlpineOrMesa");

		private static readonly FieldInfo PlayedCalderaField = AccessTools.Field(typeof(AmbienceAudio), "playedCalderaOrGloom");

		private static readonly FieldInfo PlayedKilnField = AccessTools.Field(typeof(AmbienceAudio), "playedKiln");

		private static float _tropicsOriginal;

		private static float _alpineOriginal;

		private static float _calderaOriginal;

		private static float _kilnOriginal;

		private static float _peakOriginal;

		private static bool _captured;

		private static float _tropics = 999999f;

		private static float _alpine = 999999f;

		private static float _caldera = 999999f;

		private static float _kiln = 999999f;

		private static float _peak = 999999f;

		private static bool _armed;

		internal static float TombCeiling { get; private set; } = 450f;

		internal static void Reset()
		{
			_captured = false;
			_armed = false;
			TombCeiling = 450f;
		}

		private static AmbienceAudio[] FindAll()
		{
			return Object.FindObjectsByType<AmbienceAudio>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		}

		internal static void Apply()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			AmbienceAudio[] array = FindAll();
			if (array.Length != 0)
			{
				Capture(array[0]);
			}
			ArmStart();
			TombCeiling = 450f + Offset((BiomeType)6).y;
			int num = PushAll();
			Plugin.Log.LogInfo((object)(string.Format("{0} Musik: Originale tropics={1:F0} alpine={2:F0} ", "[FinalAscent]", _tropicsOriginal, _alpineOriginal) + $"caldera={_calderaOriginal:F0} kilnY={_kilnOriginal:F0} peak={_peakOriginal:F0}; " + $"hoehere Etappen gesperrt, {num} Instanzen gesetzt. " + $"Gruft-Decke {TombCeiling:F0} statt 450 (Mesa-Versatz)."));
		}

		internal static void Adopt(AmbienceAudio ambience)
		{
			if (!((Object)(object)ambience == (Object)null))
			{
				Capture(ambience);
				if (!_armed)
				{
					ArmStart();
				}
				Push(ambience);
				Plugin.Log.LogInfo((object)(string.Format("{0} Musik uebernommen fuer '{1}': tropics={2:F0} ", "[FinalAscent]", ((Object)ambience).name, ambience.tropicsStingerZ) + $"alpine={ambience.alpineStingerZ:F0} caldera={ambience.calderaStingerZ:F0} " + $"kilnY={ambience.kilnStingerY:F0} peak={ambience.peaksTingerZ:F0}"));
			}
		}

		private static void Capture(AmbienceAudio source)
		{
			if (!_captured && !((Object)(object)source == (Object)null))
			{
				_tropicsOriginal = source.tropicsStingerZ;
				_alpineOriginal = source.alpineStingerZ;
				_calderaOriginal = source.calderaStingerZ;
				_kilnOriginal = source.kilnStingerY;
				_peakOriginal = source.peaksTingerZ;
				_captured = true;
			}
		}

		private static void ArmStart()
		{
			_tropics = _tropicsOriginal;
			_alpine = 999999f;
			_caldera = 999999f;
			_kiln = 999999f;
			_peak = 999999f;
			_armed = true;
		}

		private static void Push(AmbienceAudio ambience)
		{
			if (!((Object)(object)ambience == (Object)null) && _armed)
			{
				ambience.tropicsStingerZ = _tropics;
				ambience.alpineStingerZ = _alpine;
				ambience.calderaStingerZ = _caldera;
				ambience.kilnStingerY = _kiln;
				ambience.peaksTingerZ = _peak;
			}
		}

		private static int PushAll()
		{
			AmbienceAudio[] array = FindAll();
			AmbienceAudio[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Push(array2[i]);
			}
			return array.Length;
		}

		internal static void OnStageAdvanced(BiomeType from, BiomeType to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_002c: 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_0069: Invalid comparison between Unknown and I4
			//IL_004e: 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)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo fieldInfo;
			if ((int)to == 7)
			{
				_tropics = _tropicsOriginal + Offset((BiomeType)7).z;
				fieldInfo = PlayedTropicsField;
			}
			else if ((int)to == 6)
			{
				_alpine = _alpineOriginal + Offset((BiomeType)6).z;
				fieldInfo = PlayedAlpineField;
			}
			else if ((int)to == 8)
			{
				_caldera = _calderaOriginal + Offset((BiomeType)8).z;
				fieldInfo = PlayedCalderaField;
			}
			else
			{
				if ((int)to != 9)
				{
					return;
				}
				_kiln = _kilnOriginal + Offset((BiomeType)8).y;
				_peak = _peakOriginal + Offset((BiomeType)8).z;
				fieldInfo = PlayedKilnField;
			}
			AmbienceAudio[] array = FindAll();
			foreach (AmbienceAudio val in array)
			{
				fieldInfo?.SetValue(val, false);
				Push(val);
			}
			Plugin.Detail(string.Format("{0}   Musik fuer {1} scharf (Schwelle und Flag zurueckgesetzt).", "[FinalAscent]", to));
		}

		internal static void ArmForRestore(IList<BiomeType> chain, int target)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (_captured && chain != null && chain.Count != 0)
			{
				if (target < 0)
				{
					target = 0;
				}
				if (target >= chain.Count)
				{
					target = chain.Count - 1;
				}
				ArmStart();
				for (int i = 1; i <= target; i++)
				{
					Advance(chain[i]);
				}
				AmbienceAudio[] array = FindAll();
				foreach (AmbienceAudio obj in array)
				{
					MarkHeard(obj, chain, target);
					Push(obj);
				}
				Plugin.Log.LogInfo((object)(string.Format("{0} Musik auf die wiederhergestellte Etappe gestellt ({1}): ", "[FinalAscent]", chain[target]) + $"tropics={_tropics:F0} alpine={_alpine:F0} caldera={_caldera:F0} " + $"kilnY={_kiln:F0} peak={_peak:F0}. Alles darunter gilt als gehoert - " + "sonst spielten alle verpassten Stinger auf einmal."));
			}
		}

		private static void Advance(BiomeType to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_000a: 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_0038: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if ((int)to == 7)
			{
				_tropics = _tropicsOriginal + Offset((BiomeType)7).z;
			}
			else if ((int)to == 6)
			{
				_alpine = _alpineOriginal + Offset((BiomeType)6).z;
			}
			else if ((int)to == 8)
			{
				_caldera = _calderaOriginal + Offset((BiomeType)8).z;
			}
			else if ((int)to == 9)
			{
				_kiln = _kilnOriginal + Offset((BiomeType)8).y;
				_peak = _peakOriginal + Offset((BiomeType)8).z;
			}
		}

		private static void MarkHeard(AmbienceAudio one, IList<BiomeType> chain, int target)
		{
			Heard(one, PlayedTropicsField, chain, target, (BiomeType)7);
			Heard(one, PlayedAlpineField, chain, target, (BiomeType)6);
			Heard(one, PlayedCalderaField, chain, target, (BiomeType)8);
			Heard(one, PlayedKilnField, chain, target, (BiomeType)9);
		}

		private static void Heard(AmbienceAudio one, FieldInfo flag, IList<BiomeType> chain, int target, BiomeType triggeredBy)
		{
			//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)
			if (flag == null)
			{
				return;
			}
			int num = -1;
			for (int i = 0; i < chain.Count; i++)
			{
				if (chain[i] == triggeredBy)
				{
					num = i;
					break;
				}
			}
			flag.SetValue(one, num >= 0 && num < target);
		}

		internal static void OnSegmentEntered(Segment segment)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected I4, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			switch (segment - 2)
			{
			default:
				return;
			case 0:
				_alpine = _alpineOriginal + Offset((BiomeType)2).z;
				break;
			case 1:
				_caldera = _calderaOriginal + Offset((BiomeType)3).z;
				break;
			case 2:
				_kiln = _kilnOriginal + Offset((BiomeType)3).y;
				_peak = 999999f;
				break;
			}
			int num = PushAll();
			Plugin.Detail(string.Format("{0}   Musik fuer Segment {1} scharf ({2} Instanzen): ", "[FinalAscent]", segment, num) + $"alpine={_alpine:F0} caldera={_caldera:F0} kilnY={_kiln:F0} peak={_peak:F0}");
		}

		private static Vector3 Offset(BiomeType biome)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!FinalAscentBuilder.Offsets.TryGetValue(biome, out var value))
			{
				return Vector3.zero;
			}
			return value;
		}
	}
	[HarmonyPatch(typeof(AmbienceAudio), "Update")]
	internal static class FinalAscentTombCeiling
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> code)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(code);
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (item.opcode == OpCodes.Ldc_R4 && item.operand is float num2 && Mathf.Approximately(num2, 450f))
				{
					num++;
				}
			}
			if (num != 1)
			{
				Plugin.Log.LogWarning((object)($"[FinalAscent] Gruft-Decke: {num} Stellen mit 450 statt genau einer - " + "es wird NICHTS getauscht, die Gruft bleibt wie in Vanilla."));
				foreach (CodeInstruction item2 in list)
				{
					yield return item2;
				}
				yield break;
			}
			foreach (CodeInstruction item3 in list)
			{
				if (item3.opcode == OpCodes.Ldc_R4 && item3.operand is float num3 && Mathf.Approximately(num3, 450f))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.PropertyGetter(typeof(FinalAscentAmbience), "TombCeiling"));
				}
				else
				{
					yield return item3;
				}
			}
		}
	}
	[HarmonyPatch(typeof(GameUtils), "InstantiateAndGrab")]
	internal static class FinalAscentGrabProbe
	{
		private static void Prefix(Item item, Character character)
		{
			string text = "unlesbar";
			try
			{
				StackTrace stackTrace = new StackTrace(1, fNeedFileInfo: false);
				for (int i = 0; i < stackTrace.FrameCount && i < 5; i++)
				{
					MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod();
					if (!(methodBase?.DeclaringType == null) && !(methodBase.DeclaringType.Namespace == "HarmonyLib"))
					{
						text = methodBase.DeclaringType.Name + "." + methodBase.Name;
						break;
					}
				}
			}
			catch
			{
			}
			Plugin.Log.LogWarning((object)("[FinalAscent] GEGENSTAND ERHALTEN (" + (PhotonNetwork.IsMasterClient ? "HOST" : "GAST") + "): '" + (((Object)(object)item != (Object)null) ? ((Object)((Component)item).gameObject).name : "NICHTS") + "' fuer " + (((Object)(object)character != (Object)null) ? ((Object)character).name : "?") + ", bestimmt von " + text + "."));
		}
	}
	[HarmonyPatch(typeof(Spawner), "GetObjectsToSpawn")]
	internal static class FinalAscentAmuletProbe
	{
		private const string Tag = "[FinalAscent]";

		private const int Limit = 400;

		private static int _said;

		internal static void Reset()
		{
			_said = 0;
		}

		private static void Postfix(Spawner __instance, int spawnCount, List<GameObject> __result)
		{
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Invalid comparison between Unknown and I4
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			string text;
			try
			{
				text = FinalAscentDelivery.ScenePathOf(((Component)__instance).transform);
			}
			catch
			{
				text = ((Object)__instance).name;
			}
			if (_said >= 400)
			{
				if (_said == 400)
				{
					_said++;
					Plugin.Log.LogWarning((object)string.Format("{0} STATUEN-GRIFF: ab hier still ({1} Zeilen erreicht).", "[FinalAscent]", 400));
				}
				return;
			}
			_said++;
			StringBuilder stringBuilder = new StringBuilder();
			if (__result == null)
			{
				stringBuilder.Append("NICHTS (null)");
			}
			else
			{
				foreach (GameObject item in __result)
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(((Object)(object)item == (Object)null) ? "LEER" : ((Object)item).name);
				}
				if (stringBuilder.Length == 0)
				{
					stringBuilder.Append("leere Liste");
				}
			}
			Plugin.Log.LogWarning((object)("[FinalAscent] STATUEN-GRIFF (" + (PhotonNetwork.IsMasterClient ? "HOST" : "GAST") + "): '" + text + "'"));
			Plugin.Log.LogWarning((object)(string.Format("{0}   Betriebsart {1}, Anzahl {2}, ", "[FinalAscent]", __instance.spawnMode, spawnCount) + $"Topf {__instance.spawnPool}, Rueckfall " + "'" + (((Object)(object)__instance.fallbackSpawn != (Object)null) ? ((Object)__instance.fallbackSpawn).name : "keiner") + "', Einzelstueck '" + (((Object)(object)__instance.spawnedObjectPrefab != (Object)null) ? ((Object)__instance.spawnedObjectPrefab).name : "keins") + "'."));
			Plugin.Log.LogWarning((object)string.Format("{0}   HERAUS KAM: {1}", "[FinalAscent]", stringBuilder));
			if ((int)__instance.spawnMode != 2 || __instance.heightBasedSpawnPools == null)
			{
				return;
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			foreach (HeightBasedSpawnListEntry heightBasedSpawnPool in __instance.heightBasedSpawnPools)
			{
				if (heightBasedSpawnPool != null)
				{
					if (stringBuilder2.Length > 0)
					{
						stringBuilder2.Append(" | ");
					}
					stringBuilder2.Append(heightBasedSpawnPool.spawnPool).Append(" ab z=").Append(heightBasedSpawnPool.minimumZ.ToString("F0"))
						.Append(" y=")
						.Append(heightBasedSpawnPool.minimumHeight.ToString("F0"));
					if (heightBasedSpawnPool.hasBiomeRequirement)
					{
						stringBuilder2.Append(" nur ").Append(heightBasedSpawnPool.biomeRequirement);
					}
				}
			}
			Plugin.Log.LogWarning((object)("[FinalAscent]   Hoehen-Tabelle (Spawner steht auf z=" + $"{((Component)__instance).transform.position.z:F0}): {stringBuilder2}"));
		}
	}
	internal static class FinalAscentApocalypse
	{
		private const string Tag = "[FinalAscent]";

		internal const string RoomKey = "APO";

		private static bool _seen;

		private static bool _everSeen;

		internal const bool Shipped = false;

		internal static bool Wanted { get; private set; }

		internal static bool Active => false;

		internal static bool FromRoom(out bool value)
		{
			value = false;
			if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || ((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties == null)
			{
				return false;
			}
			if (!((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"APO", out object value2) || !(value2 is bool))
			{
				return false;
			}
			value = (bool)value2;
			return true;
		}

		internal static void Restore(bool on)
		{
			Wanted = on;
		}

		internal static void Choose(bool on)
		{
			if (Wanted != on || !_everSeen || _seen != on)
			{
				Wanted = on;
				FinalAscentSetupMemory.Save();
				Publish();
				Plugin.Log.LogInfo((object)("[FinalAscent] Apokalypse-Modus " + (on ? "EIN" : "AUS") + " - gilt fuer jeden Lauf, der aus diesem Raum startet."));
			}
		}

		private static void Publish()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0035: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null)
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)"APO", (object)Wanted);
				currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
		}

		internal static void Claim()
		{
			if (!FromRoom(out var _))
			{
				Publish();
			}
		}

		internal static bool Adopt()
		{
			if (!FromRoom(out var value))
			{
				return false;
			}
			if (_everSeen && _seen == value)
			{
				return false;
			}
			_everSeen = true;
			_seen = value;
			if (Wanted == value)
			{
				return false;
			}
			Wanted = value;
			Plugin.Log.LogInfo((object)("[FinalAscent] Apokalypse-Modus aus dem Raum uebernommen: " + (value ? "EIN" : "AUS") + "."));
			return true;
		}
	}
	[HarmonyPatch(typeof(AscentUI), "Start")]
	internal static class FinalAscentApocalypseLabelPatch
	{
		private static void Postfix(AscentUI __instance)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.text == (Object)null) && FinalAscentApocalypse.Active)
			{
				((Graphic)__instance.text).color = FinalAscentUiKit.Orange;
				Plugin.Detail("[FinalAscent] Moduslabel orange - der Apokalypse-Modus gilt" + (string.IsNullOrEmpty(((TMP_Text)__instance.text).text) ? ", aber Vanilla laesst das Label bei Ascent 0 leer." : "."));
			}
		}
	}
	[HarmonyPatch(typeof(AirportCheckInKiosk), "BeginIslandLoadRPC")]
	internal static class FinalAscentApocalypseStartPatch
	{
		private static void Postfix()
		{
			Plugin.Log.LogInfo((object)("[FinalAscent] Apokalypse-Modus: " + (FinalAscentApocalypse.Active ? "EIN" : "AUS") + " (aus dem Raum: " + (RoomHas() ? "ja" : "nein, eigene Erinnerung") + ")."));
		}

		private static bool RoomHas()
		{
			bool value;
			return FinalAscentApocalypse.FromRoom(out value);
		}
	}
	[HarmonyPatch(typeof(BoardingPass), "UpdateAscent")]
	internal static class FinalAscentApocalypsePassPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(BoardingPass __instance)
		{
		}
	}
	internal sealed class FinalAscentApocalypsePanel : MonoBehaviour
	{
		private const string Tag = "[FinalAscent]";

		private const string NodeName = "FA_Apocalypse";

		private const float DropLines = 2.8f;

		private const float RowLines = 2.3f;

		private static FinalAscentApocalypsePanel _instance;

		private static Canvas _canvas;

		private BoardingPass _pass;

		private RectTransform _rect;

		private RectTransform _card;

		private CanvasGroup _group;

		private Toggle _toggle;

		private TextMeshProUGUI _label;

		private float _size = 24f;

		private float _lastLook;

		private bool _told;

		private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

		internal static void Ensure(BoardingPass pass)
		{
			if (!((Object)(object)pass == (Object)null) && !((Object)(object)pass.ascentDesc == (Object)null))
			{
				FinalAscentApocalypse.Claim();
				if ((Object)(object)_instance == (Object)null || (Object)(object)_instance._toggle == (Object)null)
				{
					Build(pass);
				}
				if ((Object)(object)_instance != (Object)null)
				{
					_instance._pass = pass;
					_instance._card = CardOf(pass);
					FinalAscentUiKit.PutOnTop(_canvas);
					_instance.Show();
					_instance.Follow();
				}
			}
		}

		private static void Build(BoardingPass pass)
		{
			//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_0071: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: 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_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: 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)
			FinalAscentUiKit.Sample(pass);
			RectTransform val = CardOf(pass);
			Retire(((Component)pass).transform, ((Transform)pass.ascentDesc.rectTransform).parent);
			float num = ((pass.ascentDesc.fontSize > 1f) ? pass.ascentDesc.fontSize : 24f);
			float num2 = num * 2.3f;
			GameObject val2 = new GameObject("FA_ApocalypseCanvas");
			Object.DontDestroyOnLoad((Object)val2);
			Canvas val3 = val2.AddComponent<Canvas>();
			val3.renderMode = (RenderMode)0;
			val2.AddComponent<GraphicRaycaster>();
			_canvas = val3;
			FinalAscentUiKit.PutOnTop(val3);
			Transform transform = val2.transform;
			GameObject val4 = new GameObject("FA_Apocalypse", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent(transform, false);
			RectTransform component = val4.GetComponent<RectTransform>();
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(0.5f, 0.5f);
			component.anchorMax = val5;
			component.anchorMin = val5;
			component.pivot = new Vector2(0.5f, 1f);
			Rect rect = val.rect;
			component.sizeDelta = new Vector2(((Rect)(ref rect)).width, num2);
			_instance = val4.AddComponent<FinalAscentApocalypsePanel>();
			_instance._rect = component;
			_instance._card = val;
			_instance._size = num;
			_instance._group = val4.AddComponent<CanvasGroup>();
			_instance._group.alpha = 0f;
			FinalAscentApocalypsePanel instance = _instance;
			Transform transform2 = val4.transform;
			rect = val.rect;
			instance._toggle = FinalAscentUiKit.Check(transform2, 0f, 0f, ((Rect)(ref rect)).width, num2, Caption(), num);
			_instance._label = ((Component)_instance._toggle).GetComponentInChildren<TextMeshProUGUI>(true);
			((UnityEvent<bool>)(object)_instance._toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
			{
				FinalAscentApocalypse.Choose(on);
				_instance.Show();
			});
			Plugin.Detail("[FinalAscent] Apokalypse-Haken gebaut an '" + ((Object)transform).name + "', gemessen an " + $"'{((Object)val).name}'. Schrift {num:0.#}, Zeile {component.sizeDelta}. " + "Beschnitten von: " + Clipper(val4.transform) + ".");
		}

		private void Reposition()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_rect == (Object)null) && !((Object)(object)_card == (Object)null))
			{
				Transform parent = ((Transform)_rect).parent;
				if (!((Object)(object)parent == (Object)null))
				{
					_card.GetWorldCorners(_corners);
					Vector3 val = _corners[0];
					Vector3 val2 = _corners[3];
					float num = Vector3.Distance(parent.InverseTransformPoint(val), parent.InverseTransformPoint(val2));
					_rect.sizeDelta = new Vector2(num, _size * 2.3f);
					((Transform)_rect).position = (val + val2) * 0.5f;
					RectTransform rect = _rect;
					rect.anchoredPosition -= new Vector2(0f, _size * 2.8f);
				}
			}
		}

		private void Follow()
		{
			if (!((Object)(object)_group == (Object)null))
			{
				bool num = (Object)(object)_pass != (Object)null && ((Component)_pass).gameObject.activeInHierarchy;
				float num2 = 0f;
				if (num)
				{
					num2 = (((Object)(object)_pass.canvasGroup != (Object)null) ? _pass.canvasGroup.alpha : 1f);
				}
				_group.alpha = num2;
				_group.blocksRaycasts = num2 > 0.5f;
				_group.interactable = num2 > 0.5f;
				if (num2 > 0f)
				{
					Reposition();
					Report();
				}
			}
		}

		private void Report()
		{
			//IL_0033: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (!_told && !((Object)(object)_rect == (Object)null))
			{
				_told = true;
				ManualLogSource log = Plugin.Log;
				string text = string.Format("{0} Apokalypse-Haken sichtbar: Mitte {1}, Groesse ", "[FinalAscent]", ((Transform)_rect).position);
				Rect rect = _rect.rect;
				log.LogInfo((object)(text + $"{((Rect)(ref rect)).size}, Bildschirm {Screen.width}x{Screen.height}, " + $"Deckkraft {_group.alpha:0.00}, Sortierung " + string.Format("{0}, Text '{1}'.", ((Object)(object)_canvas != (Object)null) ? _canvas.sortingOrder : (-1), ((Object)(object)_label != (Object)null) ? ((TMP_Text)_label).text.Replace("\n", " / ") : "-")));
			}
		}

		private void Show()
		{
			//IL_009e: 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)
			if (!((Object)(object)_toggle == (Object)null))
			{
				_toggle.SetIsOnWithoutNotify(FinalAscentApocalypse.Wanted);
				Transform val = ((Component)_toggle).transform.Find("Box");
				Transform val2 = (((Object)(object)val != (Object)null) ? val.Find("Mark") : null);
				if ((Object)(object)val2 != (Object)null)
				{
					((Component)val2).gameObject.SetActive(_toggle.isOn);
				}
				if ((Object)(object)_label != (Object)null)
				{
					((TMP_Text)_label).text = Caption();
					((Graphic)_label).color = (FinalAscentApocalypse.Wanted ? FinalAscentUiKit.Orange : FinalAscentUiKit.Chalk);
				}
			}
		}

		private static string Caption()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			string text = ColorUtility.ToHtmlStringRGB(FinalAscentUiKit.ChalkDim);
			return FinalAscentText.Get("apo.label") + "\n<size=68%><color=#" + text + ">" + FinalAscentText.Get("apo.note") + "</color></size>";
		}

		private void Update()
		{
			Follow();
			if (!(Time.unscaledTime - _lastLook < 1f))
			{
				_lastLook = Time.unscaledTime;
				if (FinalAscentApocalypse.Adopt())
				{
					Show();
				}
			}
		}

		private static RectTransform CardOf(BoardingPass pass)
		{
			Transform obj = ((Component)pass).transform.Find("Panel");
			RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (!((Object)(object)val != (Object)null))
			{
				return pass.ascentDesc.rectTransform;
			}
			return val;
		}

		private static void Retire(params Transform[] places)
		{
			foreach (Transform val in places)
			{
				Transform val2 = (((Object)(object)val != (Object)null) ? val.Find("FA_Apocalypse") : null);
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val2).gameObject);
				}
			}
		}

		private static string Clipper(Transform from)
		{
			Transform parent = from.parent;
			while ((Object)(object)parent != (Object)null)
			{
				if ((Object)(object)((Component)parent).GetComponent<RectMask2D>() != (Object)null)
				{
					return "RectMask2D an '" + ((Object)parent).name + "'";
				}
				if ((Object)(object)((Component)parent).GetComponent<Mask>() != (Object)null)
				{
					return "Mask an '" + ((Object)parent).name + "'";
				}
				parent = parent.parent;
			}
			return "niemandem";
		}
	}
	internal static class FinalAscentBaked
	{
		private const string Tag = "[FinalAscent]";

		private const string Magic = "PiCHUInsel 1|";

		internal const string FileName = "TrueFinalAscent.Insel.dat";

		internal static void Save(byte[] packed, int objects)
		{
			if (packed == null || packed.Length == 0)
			{
				return;
			}
			try
			{
				string s = "PiCHUInsel 1|" + FinalAscent.Seed.ToString(CultureInfo.InvariantCulture) + "|" + objects.ToString(CultureInfo.InvariantCulture) + "|" + FinalAscentSetup.SerializeChosen() + "|" + FinalAscentBuilder.SummitNote() + "\n";
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				byte[] array = new byte[bytes.Length + packed.Length];
				Buffer.BlockCopy(bytes, 0, array, 0, bytes.Length);
				Buffer.BlockCopy(packed, 0, array, bytes.Length, packed.Length);
				if (Plugin.Diagnosing)
				{
					File.WriteAllBytes(FinalAscentStore.Note("TrueFinalAscent.Insel.dat"), array);
					Plugin.Log.LogInfo((object)(string.Format("{0} GEBACKENE INSEL abgelegt: {1} KB ", "[FinalAscent]", array.Length / 1024) + string.Format("({0} Objekte, Seed {1}) als {2}. ", objects, FinalAscent.Seed, "TrueFinalAscent.Insel.dat") + "Zum Vergleich: PEAKs eigene Karten wiegen 154 bis 177 MB je Stueck."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] Gebackene Insel liess sich nicht ablegen: " + ex.Message));
			}
		}

		internal static string Find()
		{
			try
			{
				string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "TrueFinalAscent.Insel.dat");
				if (File.Exists(text))
				{
					return text;
				}
				string text2 = FinalAscentStore.Note("TrueFinalAscent.Insel.dat");
				if (File.Exists(text2))
				{
					return text2;
				}
				string text3 = Path.Combine(Paths.ConfigPath, "TrueFinalAscent.Insel.dat");
				if (File.Exists(text3))
				{
					return text3;
				}
			}
			catch (Exception ex)
			{
				Plugin.Detail("[FinalAscent] Suche nach gebackener Insel misslungen: " + ex.Message);
			}
			return null;
		}

		internal static bool TryLoad(out byte[] packed, out string choices, out string summit)
		{
			packed = null;
			choices = null;
			summit = null;
			string text = Find();
			if (text == null)
			{
				return false;
			}
			try
			{
				byte[] array = File.ReadAllBytes(text);
				int num = Array.IndexOf(array, (byte)10);
				if (num <= 0)
				{
					Plugin.Log.LogWarning((object)("[FinalAscent] '" + text + "' hat keine lesbare Kopfzeile - sie wird uebergangen."));
					return false;
				}
				string text2 = Encoding.UTF8.GetString(array, 0, num);
				if (!text2.StartsWith("PiCHUInsel 1|", StringComparison.Ordinal))
				{
					Plugin.Log.LogWarning((object)("[FinalAscent] '" + text + "' ist keine gebackene Insel dieser Fassung - sie wird uebergangen."));
					return false;
				}
				string[] array2 = text2.Substring("PiCHUInsel 1|".Length).Split('|');
				if (array2.Length < 4)
				{
					Plugin.Log.LogWarning((object)("[FinalAscent] '" + text + "' hat eine unvollstaendige Kopfzeile " + $"({array2.Length} Felder) - sie wird uebergangen."));
					return false;
				}
				choices = array2[2];
				summit = array2[3];
				packed = new byte[array.Length - num - 1];
				Buffer.BlockCopy(array, num + 1, packed, 0, packed.Length);
				Plugin.Log.LogInfo((object)(string.Format("{0} GEBACKENE INSEL gefunden: {1} ({2} KB, ", "[FinalAscent]", text, array.Length / 1024) + array2[1] + " Objekte, gebacken mit Seed " + array2[0] + "). Auf diesem Rechner wird weder generiert noch gewartet - es wird nur ausgepackt."));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] '" + text + "' liess sich nicht lesen: " + ex.Message));
				return false;
			}
		}

		internal static void Announce()
		{
			string text = Find();
			if (text == null)
			{
				Plugin.Log.LogInfo((object)"[FinalAscent] Keine gebackene Insel vorhanden - der Berg entsteht wie bisher: der Host generiert, die anderen bekommen sein Paket geschickt. Der Host legt nach dem Packen eine Datei 'TrueFinalAscent.Insel.dat' ab; wer sie auf ALLE Rechner kopiert, laedt danach ueberall dieselbe Insel.");
			}
			else
			{
				Plugin.Log.LogInfo((object)("[FinalAscent] Gebackene Insel liegt bereit: " + text));
			}
		}
	}
	internal static class FinalAscentBanner
	{
		private const string Tag = "[FinalAscent]";

		private const float Seconds = 4f;

		private static AudioClip _squeak;

		private static bool _looked;

		internal static void Show(string text, Color colour, AudioClip stinger)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.fogRises == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[FinalAscent] Keine Einblendung moeglich - GUIManager oder 'fogRises' fehlt.");
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(instance.fogRises, instance.fogRises.transform.parent, false);
			((Object)val).name = "FA_Banner";
			val.SetActive(true);
			int num = 0;
			TextMeshProUGUI[] componentsInChildren = val.GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI obj in componentsInChildren)
			{
				((TMP_Text)obj).text = text;
				((Graphic)obj).color = colour;
				num++;
			}
			Text[] componentsInChildren2 = val.GetComponentsInChildren<Text>(true);
			foreach (Text obj2 in componentsInChildren2)
			{
				obj2.text = text;
				((Graphic)obj2).color = colour;
				num++;
			}
			if ((Object)(object)stinger != (Object)null)
			{
				instance.ForcePlayStinger(stinger);
			}
			Object.Destroy((Object)(object)val, 4f);
			Plugin.Log.LogInfo((object)(string.Format("{0} Einblendung '{1}' - {2} Schriftzug(e) umgeschrieben, ", "[FinalAscent]", text, num) + "Klang " + (((Object)(object)stinger != (Object)null) ? ((Object)stinger).name : "keiner") + "."));
		}

		internal static AudioClip Squeak()
		{
			if (_looked)
			{
				return _squeak;
			}
			_looked = true;
			SFX_Instance[] array = Resources.FindObjectsOfTypeAll<SFX_Instance>();
			string text = "";
			SFX_Instance val = null;
			SFX_Instance[] array2 = array;
			foreach (SFX_Instance val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null || val2.clips == null || val2.clips.Length == 0)
				{
					continue;
				}
				string text2 = ((Object)val2).name.ToLowerInvariant();
				if (text2.Contains("bing") || text2.Contains("bong") || text2.Contains("squeak") || text2.Contains("squeeze") || text2.Contains("squish") || text2.Contains("honk"))
				{
					text = text + ((text.Length > 0) ? ", " : "") + ((Object)val2).name;
					if ((Object)(object)val == (Object)null || text2.Contains("bing"))
					{
						val = val2;
					}
				}
			}
			_squeak = (((Object)(object)val != (Object)null) ? val.GetClip() : null);
			Plugin.Log.LogInfo((object)(string.Format("{0} Quietscher gesucht in {1} Klaengen. Kandidaten: ", "[FinalAscent]", array.Length) + ((text.Length > 0) ? text : "keine") + ". Genommen: " + (((Object)(object)val != (Object)null) ? ((Object)val).name : "keiner") + "."));
			return _squeak;
		}
	}
	internal static class FinalAscentBiomeAudit
	{
		private const string Tag = "[FinalAscent]";

		private const string KeyPrefix = "FAbio";

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

		private static bool _running;

		internal static void Begin()
		{
			if (!_running && !((Object)(object)Plugin.Instance == (Object)null))
			{
				_running = true;
				_done.Clear();
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Watch());
			}
		}

		private static IEnumerator Watch()
		{
			WaitForSecondsRealtime beat = new WaitForSecondsRealtime(1f);
			while (FinalAscent.Active)
			{
				yield return beat;
				if (FinalAscentBuilder.Building || !FinalAscentBuilder.Ready)
				{
					continue;
				}
				FinalAscentStage here = FinalAscentSegments.CurrentStage;
				if (here != null)
				{
					int key = FinalAscentSegments.KeyOf(here);
					if (_done.Add(key))
					{
						yield return Settled(here);
						yield return (object)new WaitForSecondsRealtime(5f);
						yield return Audit(here, key);
					}
				}
			}
			_running = false;
		}

		private static IEnumerator Settled(FinalAscentStage stage)
		{
			WaitForSecondsRealtime beat = new WaitForSecondsRealtime(1f);
			int lastSeen = -1;
			int steady = 0;
			float waited = 0f;
			while (waited < 60f)
			{
				bool num = (Object)(object)stage.Root != (Object)null && stage.Root.activeInHierarchy;
				int num2 = (num ? VisibleIn(stage.Presence) : 0);
				if (num && num2 > 0 && num2 == lastSeen)
				{
					steady++;
					if (steady >= 2)
					{
						yield break;
					}
				}
				else
				{
					steady = 0;
				}
				lastSeen = num2;
				waited += 1f;
				yield return beat;
			}
			Plugin.Log.LogWarning((object)("[FinalAscent] '" + stage.Title + "' steht nach einer Minute immer noch nicht vollstaendig - gemessen wird trotzdem, die Zahlen sagen den Rest."));
		}

		private static int VisibleIn(BiomeType type)
		{
			//IL_001c: 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)
			int num = 0;
			Biome[] array = Object.FindObjectsByType<Biome>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Biome val in array)
			{
				if ((Object)(object)val == (Object)null || val.biomeType != type)
				{
					continue;
				}
				Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren)
				{
					if ((Object)(object)val2 != (Object)null && val2.enabled && ((Component)val2).gameObject.activeInHierarchy)
					{
						num++;
					}
				}
			}
			return num;
		}

		private static IEnumerator Audit(FinalAscentStage stage, int key)
		{
			Dictionary<Transform, Vector3> still = Snapshot(stage.Presence);
			yield return (object)new WaitForSecondsRealtime(1f);
			string text = PrintOf(stage.Presence, still);
			Plugin.Log.LogInfo((object)(string.Format("{0} ETAPPE STEHT - '{1}' ({2}) gemessen: {3} ", "[FinalAscent]", stage.Title, stage.Presence, text) + "(Props/Pruefsumme/sichtbar/fest/bewegliche-ausgenommen)"));
			StringBuilder stringBuilder = new StringBuilder();
			Transform[] array = Object.FindObjectsByType<Transform>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Transform val in array)
			{
				if (!((Object)(object)val == (Object)null) && ((Object)val).name.EndsWith("_Campfire") && !((Object)(object)val.parent == (Object)null))
				{
					stringBuilder.Append((stringBuilder.Length > 0) ? ", " : "").Append(((Object)val).name).Append(' ')
						.Append(((Component)val).gameObject.activeInHierarchy ? "AN" : (((Component)val).gameObject.activeSelf ? "elternteil-aus" : "AUS"));
				}
			}
			if (stringBuilder.Length > 0)
			{
				Plugin.Log.LogInfo((object)string.Format("{0}   Lagerfeuer-Plaetze: {1}", "[FinalAscent]", stringBuilder));
			}
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null)
			{
				string text2 = "FAbio" + key;
				if (PhotonNetwork.IsMasterClient)
				{
					Room currentRoom = PhotonNetwork.CurrentRoom;
					Hashtable val2 = new Hashtable();
					((Dictionary<object, object>)val2).Add((object)text2, (object)text);
					currentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
				}
				else if ((Object)(object)Plugin.Instance != (Object)null)
				{
					((MonoBehaviour)Plugin.Instance).StartCoroutine(CompareLater(stage, text2, text));
				}
			}
		}

		private static IEnumerator CompareLater(FinalAscentStage stage, string prop, string print)
		{
			WaitForSecondsRealtime beat = new WaitForSecondsRealtime(2f);
			float waited = 0f;
			while (waited < 120f)
			{
				Hashtable val = ((PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null) ? ((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties : null);
				if (val != null && ((Dictionary<object, object>)(object)val).TryGetValue((object)prop, out object value) && value is string text)
				{
					if (text == print)
					{
						Plugin.Log.LogInfo((object)("[FinalAscent]   '" + stage.Title + "' DECKUNGSGLEICH mit dem Host - dieselbe Landschaft, im selben Spielmoment gemessen."));
						yield break;
					}
					Plugin.Log.LogError((object)("[FinalAscent]   '" + stage.Title + "' WEICHT AB: Host " + text + ", hier " + print));
					yield break;
				}
				waited += 2f;
				yield return beat;
			}
			Plugin.Log.LogInfo((object)("[FinalAscent]   Der Host hat fuer '" + stage.Title + "' auch nach zwei Minuten nichts hinterlegt - er war dort wohl wirklich noch nicht."));
		}

		private static string PrintOf(BiomeType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return PrintOf(type, null);
		}

		private static string PrintOf(BiomeType type, Dictionary<Transform, Vector3> still)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			long num2 = 0L;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			Biome[] array = Object.FindObjectsByType<Biome>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Biome val in array)
			{
				if ((Object)(object)val == (Object)null || val.biomeType != type)
				{
					continue;
				}
				PropSpawner[] componentsInChildren = ((Component)val).GetComponentsInChildren<PropSpawner>(true);
				foreach (PropSpawner val2 in componentsInChildren)
				{
					for (int k = 0; k < ((Component)val2).transform.childCount; k++)
					{
						Transform child = ((Component)val2).transform.GetChild(k);
						if (((Object)child).name.StartsWith("MushroomZombieSpawner"))
						{
							continue;
						}
						num++;
						Vector3 val3 = ((Component)val).transform.InverseTransformPoint(child.position);
						if (still != null)
						{
							if (still.TryGetValue(child, out var value))
							{
								Vector3 val4 = value - val3;
								if (!(((Vector3)(ref val4)).sqrMagnitude > 0.0001f))
								{
									goto IL_00ce;
								}
							}
							num5++;
							continue;
						}
						goto IL_00ce;
						IL_00ce:
						long num6 = 1469598103934665603L;
						num6 = (num6 ^ Mathf.RoundToInt(val3.x * 100f)) * 1099511628211L;
						num6 = (num6 ^ Mathf.RoundToInt(val3.y * 100f)) * 1099511628211L;
						num6 = (num6 ^ Mathf.RoundToInt(val3.z * 100f)) * 1099511628211L;
						num2 += num6;
					}
				}
				Renderer[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val5 in componentsInChildren2)
				{
					if ((Object)(object)val5 != (Object)null && val5.enabled && ((Component)val5).gameObject.activeInHierarchy)
					{
						num3++;
					}
				}
				Collider[] componentsInChildren3 = ((Component)val).GetComponentsInChildren<Collider>(true);
				foreach (Collider val6 in componentsInChildren3)
				{
					if ((Object)(object)val6 != (Object)null && val6.enabled && ((Component)val6).gameObject.activeInHierarchy)
					{
						num4++;
					}
				}
			}
			if (still != null)
			{
				return $"{num}/{num2 & 0xFFFFFF:X6}/{num3}s/{num4}k/{num5}b";
			}
			return $"{num}/{num2 & 0xFFFFFF:X6}/{num3}s/{num4}k";
		}

		private static Dictionary<Transform, Vector3> Snapshot(BiomeType type)
		{
			//IL_0023: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Transform, Vector3> dictionary = new Dictionary<Transform, Vector3>();
			Biome[] array = Object.FindObjectsByType<Biome>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Biome val in array)
			{
				if ((Object)(object)val == (Object)null || val.biomeType != type)
				{
					continue;
				}
				PropSpawner[] componentsInChildren = ((Component)val).GetComponentsInChildren<PropSpawner>(true);
				foreach (PropSpawner val2 in componentsInChildren)
				{
					for (int k = 0; k < ((Component)val2).transform.childCount; k++)
					{
						Transform child = ((Component)val2).transform.GetChild(k);
						dictionary[child] = ((Component)val).transform.InverseTransformPoint(child.position);
					}
				}
			}
			return dictionary;
		}
	}
	[HarmonyPatch(typeof(DayNightManager), "Update")]
	internal static class FinalAscentDayHold
	{
		private const string Tag = "[FinalAscent]";

		private static float _held;

		private static bool _holding;

		private static bool _announced;

		private static bool ShouldHold()
		{
			if (FinalAscent.Active && !FinalAscentRoom.JoinedLate)
			{
				return FinalAscentBuilder.Building;
			}
			return false;
		}

		private static void Prefix(DayNightManager __instance)
		{
			_holding = ShouldHold();
			if (_holding && !((Object)(object)__instance == (Object)null))
			{
				_held = __instance.timeOfDay;
				if (!_announced)
				{
					_announced = true;
					Plugin.Log.LogInfo((object)("[FinalAscent] Die Tageszeit steht still, solange der Berg entsteht " + $"(bei {_held:F1} Uhr). Nachzuegler halten sie nicht an."));
				}
			}
		}

		private static void Postfix(DayNightManager __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				if (_holding)
				{
					__instance.timeOfDay = _held;
				}
				else if (_announced)
				{
					_announced = false;
					Plugin.Log.LogInfo((object)("[FinalAscent] Der Berg steht - die Tageszeit laeuft wieder " + $"(bei {__instance.timeOfDay:F1} Uhr)."));
				}
			}
		}
	}
	internal sealed class FinalAscentBloodMoon : MonoBehaviour
	{
		private const string Tag = "[FinalAscent]";

		private const float Nearest = 12f;

		private const float Furthest = 25f;

		private const int Fewest = 2;

		private const int Most = 3;

		private const int Tries = 8;

		private bool _called;

		private readonly List<int> _brood = new List<int>();

		private static readonly HashSet<string> Refused = new HashSet<string>();

		private static FinalAscentBloodMoon _instance;

		private static bool _enlisted;

		private const float Angry = 22f;

		private const float Faster = 1.35f;

		internal static void Run(bool on)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			if (on && (Object)(object)_instance == (Object)null)
			{
				GameObject val = new GameObject("FA_BloodMoon");
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<FinalAscentBloodMoon>();
			}
			if (!((Object)(object)_instance == (Object)null))
			{
				if (on)
				{
					_instance._called = false;
					Enlist();
				}
				else
				{
					_instance.Dismiss();
				}
			}
		}

		private void Update()
		{
			if (FinalAscentBloodMoonSky.Wanted || !(FinalAscentBloodMoonSky.Lift <= 0f))
			{
				Character localCharacter = Character.localCharacter;
				DayNightManager instance = DayNightManager.instance;
				if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)instance == (Object)null) && !((Object)(object)instance.moon == (Object)null) && !(instance.moon.intensity < 1f) && !_called && PhotonNetwork.IsMasterClient)
				{
					_called = true;
					Call();
				}
			}
		}

		private static void Enlist()
		{
			if (_enlisted)
			{
				return;
			}
			object prefabPool = PhotonNetwork.PrefabPool;
			if (prefabPool == null)
			{
				Plugin.Log.LogInfo((object)"[FinalAscent] Blutmond: PUNs Vorlagenverwaltung steht noch nicht - wird beim naechsten Anlauf erneut versucht.");
				return;
			}
			List<GameObject> list = Pool();
			if (list.Count == 0)
			{
				Plugin.Log.LogInfo((object)"[FinalAscent] Blutmond: noch keine Vorlagen zum Anmelden gefunden.");
				return;
			}
			MethodInfo method = prefabPool.GetType().GetMethod("TryRegisterPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(string),
				typeof(GameObject)
			}, null);
			Dictionary<string, GameObject> dictionary = null;
			if (method == null)
			{
				FieldInfo[] fields = prefabPool.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				for (int i = 0; i < fields.Length; i++)
				{
					dictionary = fields[i].GetValue(prefabPool) as Dictionary<string, GameObject>;
					if (dictionary != null)
					{
						break;
					}
				}
			}
			if (method == null && dictionary == null)
			{
				Plugin.Log.LogWarning((object)("[FinalAscent] Blutmond: an '" + prefabPool.GetType().Name + "' fuehrt weder eine Anmeldung noch ein Verzeichnis vorbei - es bleibt beim Zombie."));
				return;
			}
			string text = "";
			foreach (GameObject item in list)
			{
				if (method != null)
				{
					method.Invoke(prefabPool, new object[2]
					{
						((Object)item).name,
						item
					});
				}
				else
				{
					dictionary[((Object)item).name] = item;
				}
				text = text + ((text.Length > 0) ? ", " : "") + ((Object)item).name;
			}
			text += ((method != null) ? " (angemeldet)" : " (eingetragen)");
			_enlisted = true;
			Refused.Clear();
			Plugin.Log.LogInfo((object)("[FinalAscent] Blutmond: Vorlagen bei PUN angemeldet - " + text + "."));
		}

		private void Call()
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			Enlist();
			List<GameObject> list = Pool();
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)"[FinalAscent] Blutmond: keine Kreaturenvorlagen gefunden - es bleibt bei rotem Licht.");
				return;
			}
			List<Character> list2 = Living();
			if (list2.Count == 0)
			{
				Plugin.Log.LogWarning((object)"[FinalAscent] Blutmond: niemand auf den Beinen - es bleibt bei rotem Licht.");
				return;
			}
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < list2.Count; i++)
			{
				Character val = list2[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int num3 = Random.Range(2, 4);
				num += num3;
				for (int j = 0; j < num3; j++)
				{
					GameObject val2 = list[Random.Range(0, list.Count)];
					if (Refused.Contains(((Object)val2).name) || !Ground(val.Center, out var where))
					{
						continue;
					}
					GameObject val3 = Birth(val2, where);
					if ((Object)(object)val3 != (Object)null)
					{
						Rouse(val3);
						PhotonView componentInChildren = val3.GetComponentInChildren<PhotonView>(true);
						if ((Object)(object)componentInChildren != (Object)null)
						{
							_brood.Add(componentInChildren.ViewID);
						}
						num2++;
					}
				}
			}
			Plugin.Log.LogInfo((object)(string.Format("{0} Blutmond ruft: {1} auf den Beinen, {2} ", "[FinalAscent]", list2.Count, num) + $"gewuerfelt, {num2} abgesetzt, Vorrat {list.Count} Arten."));
		}

		private static List<Character> Living()
		{
			List<Character> allPlayerCharacters = PlayerHandler.GetAllPlayerCharacters();
			List<Character> list = new List<Character>();
			for (int i = 0; i < allPlayerCharacters.Count; i++)
			{
				Character val = allPlayerCharacters[i];
				if ((Object)(object)val != (Object)null && (Object)(object)val.data != (Object)null && !val.data.dead && !val.data.fullyPassedOut)
				{
					list.Add(val);
				}
			}
			if (list.Count == 0 && (Object)(object)Character.localCharacter != (Object)null)
			{
				list.Add(Character.localCharacter);
			}
			return list;
		}

		private static void Rouse(GameObject born)
		{
			MushroomZombie componentInChildren = born.GetComponentInChildren<MushroomZombie>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.currentState = (State)1;
				return;
			}
			Mob[] componentsInChildren = born.GetComponentsInChildren<Mob>(true);
			foreach (Mob obj in componentsInChildren)
			{
				obj.aggroDistance = Mathf.Max(obj.aggroDistance, 22f);
				obj.movementSpeed *= 1.35f;
				obj.turnRate *= 1.35f;
				obj.sleeping = false;
			}
			VenusFlyTrap[] componentsInChildren2 = born.GetComponentsInChildren<VenusFlyTrap>(true);
			foreach (VenusFlyTrap obj2 in componentsInChildren2)
			{
				obj2.cooldownTime *= 0.5f;
				obj2.poisonDamage *= 1.5f;
			}
		}

		private static bool Ground(Vector3 hub, out Vector3 where)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return FinalAscentSpot.OnGround(hub, 12f, 25f, 8, out where);
		}

		private static GameObject Birth(GameObject prefab, Vector3 where)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject obj = PhotonNetwork.Instantiate(((Object)prefab).name, where, Quaternion.Euler(0f, Random.value * 360f, 0f), (byte)0, (object[])null);
				if ((Object)(object)obj == (Object)null)
				{
					Refused.Add(((Object)prefab).name);
					Plugin.Log.LogWarning((object)("[FinalAscent] Blutmond: '" + ((Object)prefab).name + "' liess sich nicht absetzen."));
				}
				return obj;
			}
			catch (Exception ex)
			{
				Refused.Add(((Object)prefab).name);
				Plugin.Log.LogWarning((object)("[FinalAscent] Blutmond: '" + ((Object)prefab).name + "' nicht vernetzbar (" + ex.GetType().Name + ") - faellt fuer diese Sitzung aus dem Vorrat."));
				return null;
			}
		}

		private static List<GameObject> Pool()
		{
			List<GameObject> list = new List<GameObject>();
			Gather<MushroomZombie>(list);
			Gather<Beetle>(list);
			Gather<Scorpion>(list);
			Gather<VenusFlyTrap>(list);
			return list;
		}

		private static void Gather<T>(List<GameObject> into) where T : Component
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			T[] array = Resources.FindObjectsOfTypeAll<T>();
			foreach (T val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (!((Scene)(ref scene)).IsValid() && !Refu