Decompiled source of ClimbInfoTracker v0.1.3

plugins/com.github.LengSword.ClimbInfoTracker.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PEAKLib.UI;
using PEAKLib.UI.Elements;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using TMPro;
using TerrainRandomiser;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Core.Editor;
using Zorro.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.LengSword.ClimbInfoTracker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+a87564d50f3eb9e010b66677ece5c9eb3ccb3264")]
[assembly: AssemblyProduct("com.github.LengSword.ClimbInfoTracker")]
[assembly: AssemblyTitle("ClimbInfoTracker")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://codeberg.org/yls-peak-mods/ClimbInfoTracker")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T>
	{
		object IEnumerator.Current => _item;

		T IEnumerator<T>.Current => _item;

		public Enumerator(T item)
		{
			_item = item;
		}

		bool IEnumerator.MoveNext()
		{
			if (!_moveNextCalled)
			{
				return _moveNextCalled = true;
			}
			return false;
		}

		void IEnumerator.Reset()
		{
			_moveNextCalled = false;
		}

		void IDisposable.Dispose()
		{
		}
	}

	int ICollection.Count => 1;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => 1;

	T IReadOnlyList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
	}

	int ICollection<T>.Count => 1;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlySingleElementList(T item)
	{
		_item = item;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection.CopyTo(Array array, int index)
	{
		array.SetValue(_item, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return EqualityComparer<T>.Default.Equals(_item, (T)value);
	}

	int IList.IndexOf(object value)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, (T)value))
		{
			return -1;
		}
		return 0;
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return EqualityComparer<T>.Default.Equals(_item, item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		array[arrayIndex] = _item;
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, item))
		{
			return -1;
		}
		return 0;
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace ClimbInfoTracker
{
	internal static class ClimbOutcome
	{
		internal const string InProgress = "InProgress";

		internal const string Summited = "Summited";

		internal const string Failed = "Failed";

		internal const string Abandoned = "Abandoned";
	}
	internal static class ClimbNetworkMode
	{
		internal const string Offline = "Offline";

		internal const string Multiplayer = "Multiplayer";
	}
	[DataContract]
	internal sealed class ClimbRecord
	{
		[DataMember(Order = 1)]
		public string RunId { get; set; } = "";

		[DataMember(Order = 2)]
		public string StartedAtUtc { get; set; } = "";

		[DataMember(Order = 3)]
		public string EndedAtUtc { get; set; } = "";

		[DataMember(Order = 4)]
		public string Outcome { get; set; } = "InProgress";

		[DataMember(Order = 5)]
		public float DurationSeconds { get; set; }

		[DataMember(Order = 6)]
		public float CurrentHeightMeters { get; set; }

		[DataMember(Order = 7)]
		public int Ascent { get; set; }

		[DataMember(Order = 8)]
		public int PlayerCount { get; set; } = 1;

		[DataMember(Order = 9)]
		public bool IsCustomRun { get; set; }

		[DataMember(Order = 10)]
		public bool IsMiniRun { get; set; }

		[DataMember(Order = 11)]
		public string Level { get; set; } = "";

		[DataMember(Order = 12)]
		public string Biome { get; set; } = "";

		[DataMember(Order = 13)]
		public bool TerrainRandomiserEnabled { get; set; }

		[DataMember(Order = 14)]
		public int TerrainRandomiserSeed { get; set; } = -1;

		[DataMember(Order = 15)]
		public string TerrainRandomiserMapSettingsBrotliBase64 { get; set; } = "";

		[DataMember(Order = 16)]
		public string NetworkMode { get; set; } = "";

		[DataMember(Order = 17)]
		public string PhotonRoomName { get; set; } = "";

		[DataMember(Order = 18)]
		public List<MapBiomeInfo> MapBiomes { get; set; } = new List<MapBiomeInfo>();
	}
	[DataContract]
	internal sealed class MapBiomeInfo
	{
		[DataMember(Order = 1)]
		public BiomeType BiomeType { get; set; }

		[DataMember(Order = 2)]
		public string Variant { get; set; } = "";

		[DataMember(Order = 3)]
		public bool OpenTomb { get; set; }
	}
	[DataContract]
	internal sealed class ClimbHistoryData
	{
		[DataMember(Order = 1)]
		public int SchemaVersion { get; set; } = 6;

		[DataMember(Order = 2)]
		public List<ClimbRecord> Records { get; set; } = new List<ClimbRecord>();

		[DataMember(Order = 3, EmitDefaultValue = false)]
		public ClimbRecord? ActiveRecord { get; set; }
	}
	internal sealed class ClimbHistory
	{
		private const int CurrentSchemaVersion = 6;

		private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
		{
			ConstructorHandling = (ConstructorHandling)1,
			Converters = { (JsonConverter)new StringEnumConverter() }
		};

		private readonly string _filePath;

		private ClimbHistoryData _data;

		internal ClimbRecord? ActiveRecord => _data.ActiveRecord;

		internal ClimbHistory()
		{
			_filePath = Path.Combine(Application.persistentDataPath, "ClimbInfoTracker", "ClimbInfoTracker.json");
			_data = Load();
			Save();
		}

		internal IReadOnlyList<ClimbRecord> GetRecords()
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord == null)
			{
				return _data.Records.ToArray();
			}
			List<ClimbRecord> records = _data.Records;
			List<ClimbRecord> list = new List<ClimbRecord>(1 + records.Count);
			list.AddRange(records);
			list.Add(activeRecord);
			return list;
		}

		internal void Begin(ClimbRecord record)
		{
			if (_data.ActiveRecord != null)
			{
				if (_data.ActiveRecord.RunId == record.RunId)
				{
					return;
				}
				FinalizeActive("Abandoned", _data.ActiveRecord.DurationSeconds);
			}
			_data.ActiveRecord = record;
			Save();
		}

		internal bool TryResumeMultiplayerRoom(ClimbRecord currentRecord)
		{
			if (currentRecord.NetworkMode != "Multiplayer" || string.IsNullOrWhiteSpace(currentRecord.PhotonRoomName))
			{
				return false;
			}
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null && IsRoomResumeCandidate(activeRecord, currentRecord.PhotonRoomName))
			{
				RefreshResumedRecord(activeRecord, currentRecord);
				Save();
				return true;
			}
			for (int num = _data.Records.Count - 1; num >= 0; num--)
			{
				ClimbRecord climbRecord = _data.Records[num];
				if (IsRoomResumeCandidate(climbRecord, currentRecord.PhotonRoomName))
				{
					if (_data.ActiveRecord != null)
					{
						FinalizeActive("Abandoned", _data.ActiveRecord.DurationSeconds);
					}
					_data.Records.RemoveAt(num);
					RefreshResumedRecord(climbRecord, currentRecord);
					_data.ActiveRecord = climbRecord;
					Save();
					return true;
				}
			}
			return false;
		}

		internal void Snapshot(float durationSeconds, float currentHeightMeters, string biome, int playerCount, string networkMode, string photonRoomName)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null)
			{
				activeRecord.DurationSeconds = Math.Max(activeRecord.DurationSeconds, durationSeconds);
				activeRecord.CurrentHeightMeters = Math.Max(activeRecord.CurrentHeightMeters, currentHeightMeters);
				UpdateBiome(activeRecord, biome);
				activeRecord.PlayerCount = Math.Max(1, playerCount);
				UpdateNetworkState(activeRecord, networkMode, photonRoomName);
				Save();
			}
		}

		internal void EnsureActiveMapBiomes(IReadOnlyList<MapBiomeInfo> mapBiomes)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null && (activeRecord.MapBiomes == null || activeRecord.MapBiomes.Count <= 0) && mapBiomes.Count != 0)
			{
				activeRecord.MapBiomes = CopyMapBiomes(mapBiomes);
				Save();
			}
		}

		internal void UpdateActiveTerrainRandomiser(bool enabled, int seed, string mapSettingsBrotliBase64)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null)
			{
				activeRecord.TerrainRandomiserEnabled = enabled;
				activeRecord.TerrainRandomiserSeed = (enabled ? seed : (-1));
				activeRecord.TerrainRandomiserMapSettingsBrotliBase64 = (enabled ? mapSettingsBrotliBase64 : "");
				Save();
			}
		}

		internal void UpdateActiveRunId(string runId)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null && !string.IsNullOrEmpty(runId) && !(activeRecord.RunId == runId))
			{
				activeRecord.RunId = runId;
				Save();
			}
		}

		internal bool DeleteRecord(string runId)
		{
			if (string.IsNullOrWhiteSpace(runId))
			{
				return false;
			}
			bool flag = _data.Records.RemoveAll((ClimbRecord record) => record.RunId == runId) > 0;
			if (_data.ActiveRecord?.RunId == runId)
			{
				_data.ActiveRecord = null;
				flag = true;
			}
			if (flag)
			{
				Save();
			}
			return flag;
		}

		internal void Complete(string outcome, float durationSeconds, float currentHeightMeters, string biome, int playerCount, string networkMode, string photonRoomName)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null)
			{
				activeRecord.CurrentHeightMeters = Math.Max(activeRecord.CurrentHeightMeters, currentHeightMeters);
				UpdateBiome(activeRecord, biome);
				activeRecord.PlayerCount = Math.Max(1, playerCount);
				UpdateNetworkState(activeRecord, networkMode, photonRoomName);
				FinalizeActive(outcome, durationSeconds);
			}
		}

		private static void UpdateBiome(ClimbRecord record, string biome)
		{
			if (!string.IsNullOrWhiteSpace(biome))
			{
				record.Biome = biome;
			}
		}

		private static bool IsRoomResumeCandidate(ClimbRecord? record, string photonRoomName)
		{
			if (record != null && record.NetworkMode == "Multiplayer" && string.Equals(record.PhotonRoomName, photonRoomName, StringComparison.Ordinal))
			{
				if (!(record.Outcome == "InProgress"))
				{
					return record.Outcome == "Abandoned";
				}
				return true;
			}
			return false;
		}

		private static void RefreshResumedRecord(ClimbRecord record, ClimbRecord currentRecord)
		{
			string runId = record.RunId;
			string startedAtUtc = record.StartedAtUtc;
			record.RunId = runId;
			record.StartedAtUtc = startedAtUtc;
			record.EndedAtUtc = "";
			record.Outcome = "InProgress";
			record.DurationSeconds = currentRecord.DurationSeconds;
			record.CurrentHeightMeters = currentRecord.CurrentHeightMeters;
			record.Ascent = currentRecord.Ascent;
			record.PlayerCount = currentRecord.PlayerCount;
			record.IsCustomRun = currentRecord.IsCustomRun;
			record.IsMiniRun = currentRecord.IsMiniRun;
			record.Level = currentRecord.Level;
			record.Biome = currentRecord.Biome;
			record.MapBiomes = CopyMapBiomes(currentRecord.MapBiomes);
			record.TerrainRandomiserEnabled = false;
			record.TerrainRandomiserSeed = -1;
			record.TerrainRandomiserMapSettingsBrotliBase64 = "";
			record.NetworkMode = currentRecord.NetworkMode;
			record.PhotonRoomName = currentRecord.PhotonRoomName;
		}

		private static void UpdateNetworkState(ClimbRecord record, string networkMode, string photonRoomName)
		{
			if (!string.IsNullOrWhiteSpace(networkMode))
			{
				record.NetworkMode = networkMode;
				record.PhotonRoomName = ((networkMode == "Multiplayer") ? photonRoomName : "");
			}
		}

		internal void RecoverInterrupted()
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null)
			{
				FinalizeActive("Abandoned", activeRecord.DurationSeconds);
			}
		}

		private void FinalizeActive(string outcome, float durationSeconds)
		{
			ClimbRecord activeRecord = _data.ActiveRecord;
			if (activeRecord != null)
			{
				DateTimeOffset utcNow = DateTimeOffset.UtcNow;
				activeRecord.Outcome = outcome;
				activeRecord.DurationSeconds = Math.Max(0f, durationSeconds);
				activeRecord.EndedAtUtc = utcNow.ToString("O", CultureInfo.InvariantCulture);
				_data.Records.RemoveAll((ClimbRecord record) => record.RunId == activeRecord.RunId);
				_data.Records.Add(activeRecord);
				_data.ActiveRecord = null;
				Save();
			}
		}

		private ClimbHistoryData Load()
		{
			if (!File.Exists(_filePath))
			{
				return new ClimbHistoryData();
			}
			try
			{
				string text = File.ReadAllText(_filePath);
				ClimbHistoryData climbHistoryData = JsonConvert.DeserializeObject<ClimbHistoryData>(text, JsonSettings);
				if (climbHistoryData == null)
				{
					return new ClimbHistoryData();
				}
				climbHistoryData.SchemaVersion = 6;
				ClimbHistoryData climbHistoryData2 = climbHistoryData;
				if (climbHistoryData2.Records == null)
				{
					List<ClimbRecord> list = (climbHistoryData2.Records = new List<ClimbRecord>());
				}
				NormalizeRecords(climbHistoryData.Records);
				if (climbHistoryData.ActiveRecord != null)
				{
					NormalizeRecord(climbHistoryData.ActiveRecord);
				}
				return climbHistoryData;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to load climb history: {arg}");
				return new ClimbHistoryData();
			}
		}

		private void Save()
		{
			string text = _filePath + ".tmp";
			try
			{
				string directoryName = Path.GetDirectoryName(_filePath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				string contents = JsonConvert.SerializeObject((object)_data, (Formatting)0, JsonSettings);
				File.WriteAllText(text, contents);
				File.Copy(text, _filePath, overwrite: true);
				File.Delete(text);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to save climb history: {arg}");
				try
				{
					if (File.Exists(text))
					{
						File.Delete(text);
					}
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogDebug((object)("Failed to remove temporary climb history file: " + ex.Message));
				}
			}
		}

		private static List<MapBiomeInfo> CopyMapBiomes(IReadOnlyList<MapBiomeInfo> mapBiomes)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			List<MapBiomeInfo> list = new List<MapBiomeInfo>(mapBiomes.Count);
			foreach (MapBiomeInfo mapBiome in mapBiomes)
			{
				list.Add(new MapBiomeInfo
				{
					BiomeType = mapBiome.BiomeType,
					Variant = mapBiome.Variant,
					OpenTomb = mapBiome.OpenTomb
				});
			}
			return list;
		}

		private static void NormalizeRecords(List<ClimbRecord> records)
		{
			foreach (ClimbRecord record in records)
			{
				NormalizeRecord(record);
			}
		}

		private static void NormalizeRecord(ClimbRecord record)
		{
			if (record.MapBiomes == null)
			{
				List<MapBiomeInfo> list = (record.MapBiomes = new List<MapBiomeInfo>());
			}
		}
	}
	internal static class ClimbHistoryUi
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static BuilderDelegate <0>__BuildPauseMenu;

			public static Action <1>__OnLanguageChanged;

			public static Func<string, string> <2>__EscapeRichText;

			public static Func<string, string> <3>__LocalizeBiomeTextId;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__28_0;

			public static Func<ClimbRecord, bool> <>9__35_0;

			public static Func<ClimbRecord, DateTimeOffset> <>9__36_0;

			public static Func<string, bool> <>9__61_0;

			internal void <EnsureHistoryPage>b__28_0()
			{
				Plugin.Logger.LogDebug((object)("Climb history page OnOpen fired. " + DescribePageState() + ", " + $"frame={Time.frameCount}."));
				RefreshPage();
			}

			internal bool <RefreshPage>b__35_0(ClimbRecord record)
			{
				return record.Outcome == "Summited";
			}

			internal DateTimeOffset <RefreshRows>b__36_0(ClimbRecord record)
			{
				return ParseDate(record.StartedAtUtc);
			}

			internal bool <JoinFields>b__61_0(string field)
			{
				return !string.IsNullOrWhiteSpace(field);
			}
		}

		private static readonly List<GameObject> Rows = new List<GameObject>();

		private static bool _registered;

		private static bool _languageSubscribed;

		private static Transform? _pauseMenuParent;

		private static PauseMenuHandler? _pauseMenuHandler;

		private static PauseMenuMainPage? _parentPage;

		private static PeakChildPage? _historyPage;

		private static PeakMenuButton? _openButton;

		private static PeakMenuButton? _backButton;

		private static RectTransform? _rowsContent;

		private static TextMeshProUGUI? _titleText;

		private static TextMeshProUGUI? _summaryText;

		private static TextMeshProUGUI? _hintText;

		private static TextMeshProUGUI? _emptyText;

		private static TMP_FontAsset? _vanillaFont;

		private const float RowHeight = 132f;

		private const float RowActionRightPadding = 24f;

		private const float RowActionSpacing = 8f;

		private const float RowActionHeight = 44f;

		private const float RowMapActionWidth = 86f;

		private const float RowDeleteActionWidth = 92f;

		private const float RowActionFontSize = 20f;

		private static readonly IReadOnlyDictionary<BiomeType, string[]> BiomeTextIds = new Dictionary<BiomeType, string[]>
		{
			[(BiomeType)0] = new string[1] { "SHORE" },
			[(BiomeType)1] = new string[1] { "TROPICS" },
			[(BiomeType)2] = new string[1] { "ALPINE" },
			[(BiomeType)3] = new string[2] { "CALDERA", "THE KILN" },
			[(BiomeType)5] = new string[1] { "PEAK" },
			[(BiomeType)6] = new string[1] { "MESA" },
			[(BiomeType)7] = new string[1] { "ROOTS" },
			[(BiomeType)8] = new string[2] { "GLOOM", "THE CITADEL" }
		};

		private static readonly HashSet<BiomeType> HasVariants = new HashSet<BiomeType>
		{
			(BiomeType)0,
			(BiomeType)1,
			(BiomeType)2,
			(BiomeType)6,
			(BiomeType)7
		};

		private static readonly IReadOnlyDictionary<BiomeType, IReadOnlyDictionary<string, string>> SimplifiedChineseVariantNames = new Dictionary<BiomeType, IReadOnlyDictionary<string, string>>
		{
			[(BiomeType)0] = new Dictionary<string, string>
			{
				["Default"] = "默认",
				["SnakeBeach"] = "蛇岩",
				["RedBeach"] = "赤沙",
				["BlueBeach"] = "蓝沙",
				["JellyHell"] = "水母地狱",
				["BlackSand"] = "黑沙"
			},
			[(BiomeType)1] = new Dictionary<string, string>
			{
				["Default"] = "默认",
				["Lava"] = "熔岩管",
				["Pillars"] = "巨柱",
				["Thorny"] = "巨棘藤",
				["Bombs"] = "爆菇",
				["Ivy"] = "紫毒藤",
				["SkyJungle"] = "浮岛"
			},
			[(BiomeType)7] = new Dictionary<string, string>
			{
				["Default"] = "默认",
				["CaveMania"] = "洞窟丛生",
				["DeepWater"] = "赤水孢泽",
				["BombBeetle"] = "爆菇甲虫",
				["DeepWoods"] = "幽深密林",
				["Clearcut"] = "皆伐菌原"
			},
			[(BiomeType)2] = new Dictionary<string, string>
			{
				["Default"] = "默认",
				["Lava"] = "熔岩管",
				["Spiky"] = "冰刺",
				["GeyserHell"] = "间歇泉地狱"
			},
			[(BiomeType)6] = new Dictionary<string, string>
			{
				["Default"] = "默认",
				["ScorpionsHell"] = "毒蝎地狱",
				["CactusHell"] = "峭壁仙人掌地狱",
				["CactusForest"] = "高原仙人掌林",
				["DynamiteHell"] = "炸药地狱",
				["TornadoHell"] = "龙卷风地狱",
				["TumblerHell"] = "风滚草地狱"
			}
		};

		internal static void Register()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			Plugin.Logger.LogDebug((object)($"Registering climb history UI. registered={_registered}, " + $"languageSubscribed={_languageSubscribed}, frame={Time.frameCount}."));
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__BuildPauseMenu;
				if (obj == null)
				{
					BuilderDelegate val = BuildPauseMenu;
					<>O.<0>__BuildPauseMenu = val;
					obj = (object)val;
				}
				MenuAPI.AddToPauseMenu((BuilderDelegate)obj);
				Plugin.Logger.LogDebug((object)"Climb history pause menu builder registered.");
			}
			if (!_languageSubscribed)
			{
				LocalizedText.OnLangugageChanged = (Action)Delegate.Combine(LocalizedText.OnLangugageChanged, new Action(OnLanguageChanged));
				_languageSubscribed = true;
			}
		}

		internal static void Dispose()
		{
			if (_languageSubscribed)
			{
				LocalizedText.OnLangugageChanged = (Action)Delegate.Remove(LocalizedText.OnLangugageChanged, new Action(OnLanguageChanged));
				_languageSubscribed = false;
			}
		}

		private static void BuildPauseMenu(Transform parent)
		{
			if ((Object)(object)_pauseMenuParent == (Object)null || (Object)(object)_pauseMenuParent != (Object)(object)parent)
			{
				ResetBuiltUi();
			}
			_pauseMenuParent = parent;
			Plugin.Logger.LogDebug((object)("Building climb history pause menu. parent=" + DescribeTransform(parent) + ", " + $"existingPage={DescribeObject((Object?)(object)_historyPage)}, frame={Time.frameCount}."));
			_pauseMenuHandler = ((Component)parent).GetComponentInParent<PauseMenuHandler>();
			_parentPage = ((Component)parent).GetComponent<PauseMenuMainPage>();
			Plugin.Logger.LogDebug((object)("Resolved pause menu references. handler=" + DescribeObject((Object?)(object)_pauseMenuHandler) + ", parentPage=" + DescribeObject((Object?)(object)_parentPage) + "."));
			if ((Object)(object)_pauseMenuHandler == (Object)null || (Object)(object)_parentPage == (Object)null)
			{
				Plugin.Logger.LogError((object)"Failed to create climb history page: pause menu references missing.");
				return;
			}
			BuildOpenButton(parent);
			Plugin.Logger.LogDebug((object)("Finished building climb history pause menu entry. openButton=" + DescribeObject((Object?)(object)_openButton) + ", deferredPage=" + DescribeObject((Object?)(object)_historyPage) + "."));
		}

		private static bool EnsureHistoryPage()
		{
			//IL_00f0: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			if ((Object)(object)_historyPage != (Object)null && Object.op_Implicit((Object)(object)_historyPage))
			{
				Plugin.Logger.LogDebug((object)"Skipped climb history page build because it already exists.");
				return true;
			}
			if ((Object)(object)_parentPage == (Object)null || (Object)(object)_pauseMenuHandler == (Object)null)
			{
				Plugin.Logger.LogError((object)"Failed to create climb history page: pause menu references missing.");
				return false;
			}
			EnsureFonts();
			Plugin.Logger.LogDebug((object)("Creating deferred climb history child page. parentPage=" + DescribeObject((Object?)(object)_parentPage) + ", " + $"frame={Time.frameCount}."));
			_historyPage = MenuAPI.CreateChildPage("ClimbInfoTracker_HistoryPage", (UIPage)(object)_parentPage);
			Plugin.Logger.LogDebug((object)("Created climb history child page. " + DescribePageState() + ", " + $"frame={Time.frameCount}."));
			PeakChildPage obj = _historyPage.CreateBackground((Color?)new Color(0f, 0f, 0f, 0.87f));
			object obj2 = <>c.<>9__28_0;
			if (obj2 == null)
			{
				UnityAction val = delegate
				{
					Plugin.Logger.LogDebug((object)("Climb history page OnOpen fired. " + DescribePageState() + ", " + $"frame={Time.frameCount}."));
					RefreshPage();
				};
				<>c.<>9__28_0 = val;
				obj2 = (object)val;
			}
			obj.SetOnOpen((UnityAction)obj2);
			BuildHeader();
			BuildContent();
			BuildBackButton();
			Plugin.Logger.LogDebug((object)("Built climb history controls. title=" + DescribeObject((Object?)(object)_titleText) + ", rowsContent=" + DescribeRect(_rowsContent) + ", backButton=" + DescribeObject((Object?)(object)_backButton) + "."));
			((Component)_historyPage).gameObject.SetActive(false);
			Plugin.Logger.LogDebug((object)("Finished building climb history page and deactivated it. " + DescribePageState() + "."));
			return true;
		}

		private static void BuildHeader()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_historyPage == (Object)null))
			{
				PeakElement val = ElementExtensions.SetSize<PeakElement>(ElementExtensions.SetPivot<PeakElement>(ElementExtensions.SetPosition<PeakElement>(ElementExtensions.SetAnchorMinMax<PeakElement>(ElementExtensions.ParentTo(new GameObject("Header"), (Component)(object)_historyPage).AddComponent<PeakElement>(), new Vector2(0.5f, 1f)), new Vector2(0f, -32f)), new Vector2(0.5f, 1f)), new Vector2(900f, 120f));
				_titleText = CreateText(((Component)val).transform, "Title", 54f, (TextAlignmentOptions)514);
				StretchToParent(((TMP_Text)_titleText).rectTransform);
				((TMP_Text)_titleText).enableAutoSizing = true;
				((TMP_Text)_titleText).fontSizeMin = 30f;
				((TMP_Text)_titleText).fontSizeMax = 54f;
			}
		}

		private static void BuildContent()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: 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_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_historyPage == (Object)null))
			{
				PeakElement val = ElementExtensions.SetSize<PeakElement>(ElementExtensions.SetPosition<PeakElement>(ElementExtensions.SetAnchorMinMax<PeakElement>(ElementExtensions.SetPivot<PeakElement>(ElementExtensions.ParentTo<PeakElement>(new GameObject("Content").AddComponent<PeakElement>(), (Component)(object)_historyPage), new Vector2(0.5f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -140f)), new Vector2(1420f, 840f));
				PeakElement val2 = ElementExtensions.ExpandToParent<PeakElement>(ElementExtensions.ParentTo<PeakElement>(new GameObject("HistoryPanel").AddComponent<PeakElement>(), (Component)(object)val));
				((Graphic)((Component)val2).gameObject.AddComponent<Image>()).color = new Color(0.11f, 0.09f, 0.08f, 0.76f);
				_summaryText = CreateText(((Component)val2).transform, "Summary", 32f, (TextAlignmentOptions)514);
				((TMP_Text)_summaryText).enableAutoSizing = true;
				((TMP_Text)_summaryText).fontSizeMin = 20f;
				((TMP_Text)_summaryText).fontSizeMax = 32f;
				ConfigureTopText(((TMP_Text)_summaryText).rectTransform, 90f, -24f, -90f, -70f);
				_hintText = CreateText(((Component)val2).transform, "Hint", 22f, (TextAlignmentOptions)514);
				((Graphic)_hintText).color = new Color(0.88f, 0.84f, 0.76f, 0.92f);
				ConfigureTopText(((TMP_Text)_hintText).rectTransform, 90f, -78f, -90f, -114f);
				PeakElement val3 = ElementExtensions.SetSize<PeakElement>(ElementExtensions.SetPosition<PeakElement>(ElementExtensions.SetPivot<PeakElement>(ElementExtensions.SetAnchorMax<PeakElement>(ElementExtensions.SetAnchorMin<PeakElement>(ElementExtensions.ParentTo<PeakElement>(new GameObject("Divider").AddComponent<PeakElement>(), (Component)(object)val2), new Vector2(0f, 1f)), new Vector2(1f, 1f)), new Vector2(0.5f, 1f)), new Vector2(0f, -132f)), new Vector2(-120f, 2f));
				((Graphic)((Component)val3).gameObject.AddComponent<Image>()).color = new Color(0.78f, 0.63f, 0.35f, 0.9f);
				PeakScrollableContent val4 = ElementExtensions.SetOffsetMax<PeakScrollableContent>(ElementExtensions.SetOffsetMin<PeakScrollableContent>(ElementExtensions.SetAnchorMax<PeakScrollableContent>(ElementExtensions.SetAnchorMin<PeakScrollableContent>(ElementExtensions.ParentTo<PeakScrollableContent>(MenuAPI.CreateScrollableContent("ClimbHistoryRows"), (Component)(object)val2), Vector2.zero), Vector2.one), new Vector2(56f, 84f)), new Vector2(-80f, -156f));
				_rowsContent = val4.Content;
				ScrollRect component = ((Component)val4).GetComponent<ScrollRect>();
				component.verticalScrollbar = CreateRowsScrollbar(((Component)val2).transform);
				component.verticalScrollbarVisibility = (ScrollbarVisibility)0;
				_emptyText = CreateText(((Component)val2).transform, "Empty", 30f, (TextAlignmentOptions)514);
				((Graphic)_emptyText).color = new Color(0.94f, 0.9f, 0.82f, 0.95f);
				((TMP_Text)_emptyText).rectTransform.anchorMin = Vector2.zero;
				((TMP_Text)_emptyText).rectTransform.anchorMax = Vector2.one;
				((TMP_Text)_emptyText).rectTransform.offsetMin = new Vector2(120f, 160f);
				((TMP_Text)_emptyText).rectTransform.offsetMax = new Vector2(-120f, -170f);
			}
		}

		private static Scrollbar CreateRowsScrollbar(Transform parent)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ClimbHistoryScrollbar", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Scrollbar)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(1f, 0f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(1f, 0.5f);
			component.offsetMin = new Vector2(-72f, 84f);
			component.offsetMax = new Vector2(-56f, -156f);
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(0.04f, 0.035f, 0.03f, 0.55f);
			GameObject val2 = new GameObject("Sliding Area", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(val.transform, false);
			RectTransform component3 = val2.GetComponent<RectTransform>();
			component3.anchorMin = Vector2.zero;
			component3.anchorMax = Vector2.one;
			component3.offsetMin = new Vector2(2f, 2f);
			component3.offsetMax = new Vector2(-2f, -2f);
			GameObject val3 = new GameObject("Handle", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val3.transform.SetParent(val2.transform, false);
			RectTransform component4 = val3.GetComponent<RectTransform>();
			component4.anchorMin = Vector2.zero;
			component4.anchorMax = Vector2.one;
			component4.offsetMin = Vector2.zero;
			component4.offsetMax = Vector2.zero;
			Image component5 = val3.GetComponent<Image>();
			((Graphic)component5).color = new Color(0.78f, 0.63f, 0.35f, 0.95f);
			Scrollbar component6 = val.GetComponent<Scrollbar>();
			component6.direction = (Direction)2;
			((Selectable)component6).targetGraphic = (Graphic)(object)component5;
			component6.handleRect = component4;
			return component6;
		}

		private static void BuildBackButton()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			if (!((Object)(object)_historyPage == (Object)null) && !((Object)(object)_pauseMenuHandler == (Object)null) && !((Object)(object)_parentPage == (Object)null))
			{
				PauseMenuHandler pauseMenuHandler = _pauseMenuHandler;
				PauseMenuMainPage parentPage = _parentPage;
				_backButton = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(ElementExtensions.SetLocalizationIndex<PeakMenuButton>(MenuAPI.CreateMenuButton("Back"), "BACK").SetColor(new Color(0.5189f, 0.1297f, 0.1718f), true), (Component)(object)_historyPage), new Vector2(0.5f, 0f)), new Vector2(0.5f, 0f)), new Vector2(0f, 36f)).SetWidth(220f).OnClick((UnityAction)delegate
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0016: Expected O, but got Unknown
					((UIPageHandler)pauseMenuHandler).TransistionToPage((UIPage)(object)parentPage, (PageTransistion)new SetActivePageTransistion());
				});
				_historyPage.SetBackButton(_backButton.Button);
			}
		}

		private static void BuildOpenButton(Transform parent)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_openButton != (Object)null)
			{
				Plugin.Logger.LogDebug((object)"Skipped climb history open button build because it already exists.");
			}
			else
			{
				if ((Object)(object)_pauseMenuHandler == (Object)null)
				{
					return;
				}
				PauseMenuHandler pauseMenuHandler = _pauseMenuHandler;
				_openButton = ElementExtensions.ParentTo<PeakMenuButton>(MenuAPI.CreatePauseMenuButton(ClimbLocalization.Get(ClimbText.Button)), parent).OnClick((UnityAction)delegate
				{
					//IL_0062: Unknown result type (might be due to invalid IL or missing references)
					//IL_006c: Expected O, but got Unknown
					Plugin.Logger.LogDebug((object)("Climb history button clicked. Before transition: " + DescribePageState() + ", " + $"rows={Rows.Count}, frame={Time.frameCount}."));
					if (EnsureHistoryPage() && !((Object)(object)_historyPage == (Object)null))
					{
						((UIPageHandler)pauseMenuHandler).TransistionToPage((UIPage)(object)_historyPage, (PageTransistion)new SetActivePageTransistion());
					}
				});
				RectTransform rectTransform = ((PeakElement)_openButton).RectTransform;
				rectTransform.anchorMin = new Vector2(0.5f, 0f);
				rectTransform.anchorMax = new Vector2(0.5f, 0f);
				rectTransform.pivot = new Vector2(0.5f, 0f);
				rectTransform.sizeDelta = new Vector2(277f, rectTransform.sizeDelta.y);
				rectTransform.anchoredPosition = new Vector2(0f, 36f);
			}
		}

		private static void ResetBuiltUi()
		{
			foreach (GameObject row in Rows)
			{
				if ((Object)(object)row != (Object)null)
				{
					Object.Destroy((Object)(object)row);
				}
			}
			Rows.Clear();
			_historyPage = null;
			_openButton = null;
			_backButton = null;
			_rowsContent = null;
			_titleText = null;
			_summaryText = null;
			_hintText = null;
			_emptyText = null;
		}

		private static void RefreshPage()
		{
			Plugin.Logger.LogDebug((object)("Refreshing climb history page. " + DescribePageState() + ", " + $"rowsContent={DescribeRect(_rowsContent)}, existingRows={Rows.Count}, " + $"frame={Time.frameCount}."));
			EnsureFonts();
			Plugin.Tracker.SnapshotActiveRun();
			IReadOnlyList<ClimbRecord> records = Plugin.History.GetRecords();
			int num = records.Count((ClimbRecord record) => record.Outcome == "Summited");
			Plugin.Logger.LogDebug((object)($"Loaded climb history records for UI. records={records.Count}, " + $"summits={num}, language={ClimbLocalization.CurrentCulture.Name}."));
			SetLocalizedText((TMP_Text?)(object)_titleText, ClimbLocalization.Get(ClimbText.Title));
			SetLocalizedText((TMP_Text?)(object)_summaryText, ClimbLocalization.Format(ClimbText.Summary, records.Count, num));
			SetLocalizedText((TMP_Text?)(object)_hintText, ClimbLocalization.Get(ClimbText.Hint));
			SetLocalizedText((TMP_Text?)(object)_emptyText, ClimbLocalization.Get(ClimbText.Empty));
			if ((Object)(object)_openButton != (Object)null)
			{
				_openButton.SetText(ClimbLocalization.Get(ClimbText.Button));
				ApplyFont(((PeakLocalizableElement)_openButton).Text);
			}
			if ((Object)(object)_backButton != (Object)null)
			{
				ApplyFont(((PeakLocalizableElement)_backButton).Text);
			}
			RefreshRows(records);
			RebuildLayout();
			Plugin.Logger.LogDebug((object)("Finished refreshing climb history page. " + DescribePageState() + ", " + $"rowsContent={DescribeRect(_rowsContent)}, renderedRows={Rows.Count}, " + $"emptyVisible={(Object)(object)_emptyText != (Object)null && ((Component)_emptyText).gameObject.activeInHierarchy}, " + $"frame={Time.frameCount}."));
		}

		private static void RefreshRows(IReadOnlyList<ClimbRecord> records)
		{
			if ((Object)(object)_rowsContent == (Object)null)
			{
				Plugin.Logger.LogWarning((object)("Cannot refresh climb history rows because rows content is missing. " + $"records={records.Count}, frame={Time.frameCount}."));
				return;
			}
			foreach (GameObject row in Rows)
			{
				row.transform.SetParent((Transform)null, false);
				Object.Destroy((Object)(object)row);
			}
			Rows.Clear();
			List<ClimbRecord> list = records.OrderByDescending((ClimbRecord record) => ParseDate(record.StartedAtUtc)).ToList();
			if ((Object)(object)_emptyText != (Object)null)
			{
				((Component)_emptyText).gameObject.SetActive(list.Count == 0);
			}
			for (int num = 0; num < list.Count; num++)
			{
				GameObject val = CreateRow(list[num], num);
				val.transform.SetParent((Transform)(object)_rowsContent, false);
				Rows.Add(val);
			}
		}

		private static GameObject CreateRow(ClimbRecord record, int index)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0072: 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_00a2: 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_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ClimbRecordRow", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(LayoutElement)
			});
			((Graphic)val.GetComponent<Image>()).color = ((index % 2 == 0) ? new Color(0.2f, 0.15f, 0.11f, 0.72f) : new Color(0.16f, 0.12f, 0.09f, 0.72f));
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.preferredHeight = 132f;
			component.minHeight = 132f;
			RectTransform component2 = val.GetComponent<RectTransform>();
			component2.sizeDelta = new Vector2(component2.sizeDelta.x, 132f);
			bool flag = CanSaveMapSettings(record);
			bool flag2 = CanLoadMapSettings(record);
			TextMeshProUGUI val2 = CreateText(val.transform, "RecordText", 28f, (TextAlignmentOptions)257);
			((TMP_Text)val2).richText = true;
			((TMP_Text)val2).lineSpacing = 6f;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)0;
			((TMP_Text)val2).rectTransform.anchorMin = Vector2.zero;
			((TMP_Text)val2).rectTransform.anchorMax = Vector2.one;
			((TMP_Text)val2).rectTransform.offsetMin = new Vector2(24f, 8f);
			((TMP_Text)val2).rectTransform.offsetMax = new Vector2(0f - CalculateRowActionReservedWidth(flag2, flag), -8f);
			((TMP_Text)val2).text = BuildRowText(record);
			PeakMenuButton val3 = CreateDeleteButton(val.transform, record.RunId, 24f);
			ApplyFont((TMP_Text)(object)val2);
			ApplyFont(((PeakLocalizableElement)val3).Text);
			if (flag2)
			{
				PeakMenuButton val4 = CreateLoadMapSettingsButton(val.transform, record, 124f + (flag ? 94f : 0f));
				ApplyFont(((PeakLocalizableElement)val4).Text);
			}
			if (flag)
			{
				PeakMenuButton val5 = CreateSaveMapSettingsButton(val.transform, record, 124f);
				ApplyFont(((PeakLocalizableElement)val5).Text);
			}
			return val;
		}

		private static float CalculateRowActionReservedWidth(bool canLoadMapSettings, bool canSaveMapSettings)
		{
			float num = 140f;
			if (canSaveMapSettings)
			{
				num += 94f;
			}
			if (canLoadMapSettings)
			{
				num += 94f;
			}
			return num;
		}

		private static bool CanSaveMapSettings(ClimbRecord record)
		{
			if (TerrainRandomiserCompatibility.Loaded)
			{
				return !string.IsNullOrWhiteSpace(record.TerrainRandomiserMapSettingsBrotliBase64);
			}
			return false;
		}

		private static bool CanLoadMapSettings(ClimbRecord record)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (CanSaveMapSettings(record))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "Airport";
			}
			return false;
		}

		private static PeakMenuButton CreateLoadMapSettingsButton(Transform parent, ClimbRecord record, float rightOffset)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			PeakMenuButton val = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(MenuAPI.CreateMenuButton(ClimbLocalization.Get(ClimbText.LoadMapSettings)).SetColor(new Color(0.23f, 0.28f, 0.42f), true), parent), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(0f - rightOffset, 0f)).SetWidth(86f).OnClick((UnityAction)delegate
			{
				if (TerrainRandomiserCompatibility.LoadMapSettings(record.TerrainRandomiserMapSettingsBrotliBase64))
				{
					Plugin.Logger.LogInfo((object)"Loaded TerrainRandomiser map settings.");
				}
			});
			((PeakElement)val).RectTransform.sizeDelta = new Vector2(86f, 44f);
			val.SetText(ClimbLocalization.Get(ClimbText.LoadMapSettings));
			ConfigureRowActionButtonText(val);
			return val;
		}

		private static PeakMenuButton CreateSaveMapSettingsButton(Transform parent, ClimbRecord record, float rightOffset)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			PeakMenuButton val = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(MenuAPI.CreateMenuButton(ClimbLocalization.Get(ClimbText.SaveMapSettings)).SetColor(new Color(0.23f, 0.36f, 0.18f), true), parent), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(0f - rightOffset, 0f)).SetWidth(86f).OnClick((UnityAction)delegate
			{
				string text = TerrainRandomiserCompatibility.SaveMapSettings(record.TerrainRandomiserMapSettingsBrotliBase64, record.StartedAtUtc);
				if (text != null)
				{
					Plugin.Logger.LogInfo((object)("Saved TerrainRandomiser map settings to " + text + "."));
				}
			});
			((PeakElement)val).RectTransform.sizeDelta = new Vector2(86f, 44f);
			val.SetText(ClimbLocalization.Get(ClimbText.SaveMapSettings));
			ConfigureRowActionButtonText(val);
			return val;
		}

		private static PeakMenuButton CreateDeleteButton(Transform parent, string runId, float rightOffset)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			PeakMenuButton val = ElementExtensions.SetPosition<PeakMenuButton>(ElementExtensions.SetPivot<PeakMenuButton>(ElementExtensions.SetAnchorMinMax<PeakMenuButton>(ElementExtensions.ParentTo<PeakMenuButton>(MenuAPI.CreateMenuButton(ClimbLocalization.Get(ClimbText.Delete)).SetColor(new Color(0.5189f, 0.1297f, 0.1718f), true), parent), new Vector2(1f, 0.5f)), new Vector2(1f, 0.5f)), new Vector2(0f - rightOffset, 0f)).SetWidth(92f).OnClick((UnityAction)delegate
			{
				Plugin.Tracker.DeleteRecord(runId);
				RefreshPage();
			});
			((PeakElement)val).RectTransform.sizeDelta = new Vector2(92f, 44f);
			val.SetText(ClimbLocalization.Get(ClimbText.Delete));
			ConfigureRowActionButtonText(val);
			return val;
		}

		private static void ConfigureRowActionButtonText(PeakMenuButton button)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text text = ((PeakLocalizableElement)button).Text;
			text.enableAutoSizing = false;
			text.fontSize = 20f;
			text.alignment = (TextAlignmentOptions)514;
			text.textWrappingMode = (TextWrappingModes)0;
			text.overflowMode = (TextOverflowModes)1;
			text.rectTransform.anchorMin = Vector2.zero;
			text.rectTransform.anchorMax = Vector2.one;
			text.rectTransform.offsetMin = new Vector2(4f, 0f);
			text.rectTransform.offsetMax = new Vector2(-4f, 0f);
			((Transform)text.rectTransform).localScale = Vector3.one;
		}

		private static string BuildRowText(ClimbRecord record)
		{
			string value = FormatDate(record.StartedAtUtc) + " | " + ClimbLocalization.GetOutcome(record.Outcome);
			string value2 = BuildStatsLine(record);
			string text = BuildContextLine(record);
			string text2 = BuildMapBiomesLine(record);
			if (string.IsNullOrWhiteSpace(text2))
			{
				return string.Format(CultureInfo.InvariantCulture, "<size=28>{0}</size>\n<size=20><color=#E0D6C2>{1}</color></size>\n<size=20><color=#E0D6C2>{2}</color></size>", EscapeRichText(value), EscapeRichText(value2), text);
			}
			return string.Format(CultureInfo.InvariantCulture, "<size=28>{0}</size>\n<size=20><color=#E0D6C2>{1}</color></size>\n<size=20><color=#E0D6C2>{2}</color></size>\n<size=19><color=#E0D6C2>{3}</color></size>", EscapeRichText(value), EscapeRichText(value2), text, text2);
		}

		private static string EscapeRichText(string value)
		{
			return value.Replace("&", "&amp;", StringComparison.Ordinal).Replace("<", "&lt;", StringComparison.Ordinal).Replace(">", "&gt;", StringComparison.Ordinal);
		}

		private static string BuildStatsLine(ClimbRecord record)
		{
			string text = TimeSpan.FromSeconds(Math.Max(0f, record.DurationSeconds)).ToString("hh\\:mm\\:ss", CultureInfo.InvariantCulture);
			string text2 = ClimbLocalization.Format(ClimbText.Players, record.PlayerCount);
			return JoinFields(text, $"{Mathf.RoundToInt(record.CurrentHeightMeters)} m", text2, EscapeRichText(BuildNetworkModeText(record)));
		}

		private static string BuildContextLine(ClimbRecord record)
		{
			List<string> list = new List<string>(3)
			{
				EscapeRichText(record.Level),
				EscapeRichText(FormatBiomeName(record.Biome)),
				EscapeRichText(BuildModeText(record))
			};
			if (record.TerrainRandomiserEnabled && record.TerrainRandomiserSeed >= 0)
			{
				list.Add(BuildTerrainSeedText(record.TerrainRandomiserSeed));
			}
			return JoinFields(list);
		}

		private static string BuildMapBiomesLine(ClimbRecord record)
		{
			if (record.MapBiomes == null || record.MapBiomes.Count == 0)
			{
				return "";
			}
			List<string> list = new List<string>();
			foreach (MapBiomeInfo mapBiome in record.MapBiomes)
			{
				list.AddRange(BuildMapBiomeFields(mapBiome));
			}
			return JoinFields(list.Select(EscapeRichText));
		}

		private static IEnumerable<string> BuildMapBiomeFields(MapBiomeInfo mapBiome)
		{
			if ((int)mapBiome.BiomeType == 3 || (int)mapBiome.BiomeType == 8)
			{
				foreach (string biomeDisplayName in GetBiomeDisplayNames(mapBiome.BiomeType))
				{
					yield return biomeDisplayName;
				}
				yield break;
			}
			string text = GetBiomeDisplayNames(mapBiome.BiomeType).FirstOrDefault() ?? "";
			if (!string.IsNullOrWhiteSpace(text))
			{
				List<string> list = new List<string>();
				if (HasVariants.Contains(mapBiome.BiomeType) && !string.IsNullOrWhiteSpace(mapBiome.Variant))
				{
					list.Add(FormatVariantName(mapBiome.BiomeType, mapBiome.Variant));
				}
				if ((int)mapBiome.BiomeType == 6)
				{
					list.Add(ClimbLocalization.Get(mapBiome.OpenTomb ? ClimbText.TombOpen : ClimbText.TombClosed));
				}
				yield return (list.Count > 0) ? (text + " (" + string.Join(", ", list) + ")") : text;
			}
		}

		private static string FormatVariantName(BiomeType biomeType, string variant)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if ((int)LocalizedText.CURRENT_LANGUAGE == 9 && SimplifiedChineseVariantNames.TryGetValue(biomeType, out IReadOnlyDictionary<string, string> value) && value.TryGetValue(variant, out var value2))
			{
				return value2;
			}
			return variant;
		}

		private unsafe static IEnumerable<string> GetBiomeDisplayNames(BiomeType biomeType)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (BiomeTextIds.TryGetValue(biomeType, out string[] value))
			{
				return value.Select(LocalizeBiomeTextId);
			}
			return new <>z__ReadOnlySingleElementList<string>(((object)(*(BiomeType*)(&biomeType))/*cast due to .constrained prefix*/).ToString());
		}

		private static string LocalizeBiomeTextId(string textId)
		{
			try
			{
				string text = LocalizedText.GetText(textId, true);
				if (!string.IsNullOrWhiteSpace(text) && text != textId)
				{
					return text;
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogDebug((object)("Failed to localize biome text id '" + textId + "': " + ex.Message));
			}
			return FormatBiomeTextFallback(textId);
		}

		private static string FormatBiomeTextFallback(string textId)
		{
			string[] array = textId.Split(' ');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				if (text.Length != 0)
				{
					array[i] = text.Substring(0, 1).ToUpperInvariant() + text.Substring(1).ToLowerInvariant();
				}
			}
			return string.Join(" ", array);
		}

		private static string BuildTerrainSeedText(int seed)
		{
			return EscapeRichText(ClimbLocalization.Get(ClimbText.TerrainSeed)) + " <noparse><</noparse>" + seed.ToString(CultureInfo.InvariantCulture) + "<noparse>></noparse>";
		}

		private static string FormatBiomeName(string biomeTitle)
		{
			if (string.IsNullOrWhiteSpace(biomeTitle))
			{
				return "";
			}
			try
			{
				string text = LocalizedText.GetText(biomeTitle, true);
				return string.IsNullOrWhiteSpace(text) ? biomeTitle : text;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogDebug((object)("Failed to localize biome title '" + biomeTitle + "': " + ex.Message));
				return biomeTitle;
			}
		}

		private static string BuildModeText(ClimbRecord record)
		{
			if (record.IsCustomRun || record.IsMiniRun)
			{
				string text = ClimbLocalization.Get(ClimbText.CustomRun);
				if (!record.IsMiniRun)
				{
					return text;
				}
				return text + " (" + ClimbLocalization.Get(ClimbText.MiniRun) + ")";
			}
			string text2 = record.Ascent switch
			{
				-1 => ClimbLocalization.Get(ClimbText.Tenderfoot), 
				0 => ClimbLocalization.Get(ClimbText.Peak), 
				_ => ClimbLocalization.Format(ClimbText.Ascent, record.Ascent), 
			};
			return ClimbLocalization.Get(ClimbText.NormalRun) + " (" + text2 + ")";
		}

		private static string BuildNetworkModeText(ClimbRecord record)
		{
			string networkMode = record.NetworkMode;
			if (!(networkMode == "Offline"))
			{
				if (networkMode == "Multiplayer")
				{
					return FormatMultiplayerText(record.PhotonRoomName);
				}
				return "";
			}
			return ClimbLocalization.Get(ClimbText.OfflineMode);
		}

		private static string FormatMultiplayerText(string photonRoomName)
		{
			string text = ClimbLocalization.Get(ClimbText.MultiplayerMode);
			if (!string.IsNullOrWhiteSpace(photonRoomName))
			{
				return text + " (" + photonRoomName + ")";
			}
			return text;
		}

		private static string JoinFields(params string[] fields)
		{
			return JoinFields((IEnumerable<string>)fields);
		}

		private static string JoinFields(IEnumerable<string> fields)
		{
			return string.Join(" | ", fields.Where((string field) => !string.IsNullOrWhiteSpace(field)));
		}

		private static DateTimeOffset ParseDate(string value)
		{
			if (!DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
			{
				return DateTimeOffset.MinValue;
			}
			return result;
		}

		private static string FormatDate(string value)
		{
			DateTimeOffset dateTimeOffset = ParseDate(value);
			if (!(dateTimeOffset == DateTimeOffset.MinValue))
			{
				return dateTimeOffset.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
			}
			return value;
		}

		private static void SetLocalizedText(TMP_Text? text, string value)
		{
			if (!((Object)(object)text == (Object)null))
			{
				text.text = value;
				ApplyFont(text);
			}
		}

		private static TextMeshProUGUI CreateText(Transform parent, string objectName, float fontSize, TextAlignmentOptions alignment)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(objectName, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(TextMeshProUGUI)
			});
			val.transform.SetParent(parent, false);
			TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
			((TMP_Text)component).font = _vanillaFont;
			((TMP_Text)component).fontSize = fontSize;
			((TMP_Text)component).alignment = alignment;
			((Graphic)component).color = Color.white;
			((TMP_Text)component).overflowMode = (TextOverflowModes)1;
			((TMP_Text)component).textWrappingMode = (TextWrappingModes)0;
			return component;
		}

		private static void EnsureFonts()
		{
			if (!((Object)(object)_vanillaFont != (Object)null))
			{
				GUIManager instance = GUIManager.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.heroDayText == (Object)null))
				{
					_vanillaFont = ((TMP_Text)instance.heroDayText).font;
				}
			}
		}

		private static void ApplyFont(TMP_Text text)
		{
			EnsureFonts();
			if ((Object)(object)_vanillaFont != (Object)null)
			{
				text.font = _vanillaFont;
			}
		}

		private static void StretchToParent(RectTransform rectTransform)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			rectTransform.offsetMin = Vector2.zero;
			rectTransform.offsetMax = Vector2.zero;
		}

		private static void ConfigureTopText(RectTransform rectTransform, float left, float top, float right, float bottom)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			rectTransform.anchorMin = new Vector2(0f, 1f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.offsetMin = new Vector2(left, bottom);
			rectTransform.offsetMax = new Vector2(right, top);
		}

		private static void OnLanguageChanged()
		{
			Plugin.Logger.LogDebug((object)("Climb history language changed. language=" + ClimbLocalization.CurrentCulture.Name + ", " + $"frame={Time.frameCount}."));
			RefreshPage();
		}

		private static void RebuildLayout()
		{
			Canvas.ForceUpdateCanvases();
			if ((Object)(object)_rowsContent != (Object)null)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(_rowsContent);
			}
			if ((Object)(object)_historyPage != (Object)null)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)_historyPage).GetComponent<RectTransform>());
			}
			Canvas.ForceUpdateCanvases();
		}

		private static string DescribePageState()
		{
			if ((Object)(object)_historyPage == (Object)null)
			{
				return "page=null";
			}
			GameObject gameObject = ((Component)_historyPage).gameObject;
			RectTransform component = gameObject.GetComponent<RectTransform>();
			return $"page={((Object)gameObject).name}, activeSelf={gameObject.activeSelf}, " + $"activeInHierarchy={gameObject.activeInHierarchy}, rect={DescribeRect(component)}";
		}

		private static string DescribeTransform(Transform? transform)
		{
			if (!((Object)(object)transform == (Object)null))
			{
				return $"{((Object)transform).name}, activeSelf={((Component)transform).gameObject.activeSelf}, " + $"activeInHierarchy={((Component)transform).gameObject.activeInHierarchy}";
			}
			return "null";
		}

		private static string DescribeObject(Object? value)
		{
			if (!(value == (Object)null))
			{
				return value.name + " (" + ((object)value).GetType().Name + ")";
			}
			return "null";
		}

		private static string DescribeRect(RectTransform? rectTransform)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rectTransform == (Object)null)
			{
				return "null";
			}
			Rect rect = rectTransform.rect;
			return $"{((Object)rectTransform).name}[size={((Rect)(ref rect)).width:0.##}x{((Rect)(ref rect)).height:0.##}, " + $"position={rectTransform.anchoredPosition}, children={((Transform)rectTransform).childCount}]";
		}
	}
	internal enum ClimbText
	{
		Button,
		Title,
		Summary,
		Hint,
		Empty,
		InProgress,
		Summited,
		Failed,
		Abandoned,
		Players,
		Tenderfoot,
		Peak,
		Ascent,
		NormalRun,
		CustomRun,
		MiniRun,
		Delete,
		SaveMapSettings,
		LoadMapSettings,
		TerrainSeed,
		OfflineMode,
		MultiplayerMode,
		TombOpen,
		TombClosed
	}
	internal static class ClimbLocalization
	{
		private static readonly IReadOnlyDictionary<ClimbText, string> English = new Dictionary<ClimbText, string>
		{
			[ClimbText.Button] = "CLIMB RECORDS",
			[ClimbText.Title] = "Climb Records",
			[ClimbText.Summary] = "{0} records | {1} summits",
			[ClimbText.Hint] = "Your local climbing history, newest first",
			[ClimbText.Empty] = "No climbing records yet",
			[ClimbText.InProgress] = "In Progress",
			[ClimbText.Summited] = "Summited",
			[ClimbText.Failed] = "Failed",
			[ClimbText.Abandoned] = "Abandoned",
			[ClimbText.Players] = "{0} player(s)",
			[ClimbText.Tenderfoot] = "Tenderfoot",
			[ClimbText.Peak] = "Peak",
			[ClimbText.Ascent] = "Ascent {0}",
			[ClimbText.NormalRun] = "Normal Mode",
			[ClimbText.CustomRun] = "Custom Run",
			[ClimbText.MiniRun] = "Mini Run",
			[ClimbText.Delete] = "Delete",
			[ClimbText.SaveMapSettings] = "Export",
			[ClimbText.LoadMapSettings] = "Load",
			[ClimbText.TerrainSeed] = "TerrainRandomiser",
			[ClimbText.OfflineMode] = "Offline Mode",
			[ClimbText.MultiplayerMode] = "Multiplayer",
			[ClimbText.TombOpen] = "Tomb Open",
			[ClimbText.TombClosed] = "Tomb Closed"
		};

		private static readonly IReadOnlyDictionary<Language, IReadOnlyDictionary<ClimbText, string>> Translations = new Dictionary<Language, IReadOnlyDictionary<ClimbText, string>> { [(Language)9] = new Dictionary<ClimbText, string>
		{
			[ClimbText.Button] = "攀爬记录",
			[ClimbText.Title] = "攀爬记录",
			[ClimbText.Summary] = "共 {0} 次 | 登顶 {1} 次",
			[ClimbText.Hint] = "本地玩家的攀爬历史,按时间从新到旧排列",
			[ClimbText.Empty] = "还没有攀爬记录",
			[ClimbText.InProgress] = "进行中",
			[ClimbText.Summited] = "已登顶",
			[ClimbText.Failed] = "未登顶",
			[ClimbText.Abandoned] = "已中止",
			[ClimbText.Players] = "{0} 名玩家",
			[ClimbText.Tenderfoot] = "菜鸟新人",
			[ClimbText.Peak] = "顶峰",
			[ClimbText.Ascent] = "天阶 {0}",
			[ClimbText.NormalRun] = "正常模式",
			[ClimbText.CustomRun] = "自定义模式",
			[ClimbText.MiniRun] = "速通局",
			[ClimbText.Delete] = "删除",
			[ClimbText.SaveMapSettings] = "导出",
			[ClimbText.LoadMapSettings] = "读取",
			[ClimbText.TerrainSeed] = "随机地形",
			[ClimbText.OfflineMode] = "离线模式",
			[ClimbText.MultiplayerMode] = "多人模式",
			[ClimbText.TombOpen] = "古墓开放",
			[ClimbText.TombClosed] = "古墓关闭"
		} };

		internal static CultureInfo CurrentCulture
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Invalid comparison between Unknown and I4
				Language cURRENT_LANGUAGE = LocalizedText.CURRENT_LANGUAGE;
				if ((int)cURRENT_LANGUAGE == 9)
				{
					return GetCulture("zh-CN");
				}
				return CultureInfo.InvariantCulture;
			}
		}

		internal static string Get(ClimbText key)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Translations.TryGetValue(LocalizedText.CURRENT_LANGUAGE, out IReadOnlyDictionary<ClimbText, string> value) && value.TryGetValue(key, out var value2))
			{
				return value2;
			}
			return English[key];
		}

		internal static string Format(ClimbText key, params object[] arguments)
		{
			return string.Format(CurrentCulture, Get(key), arguments);
		}

		internal static string GetOutcome(string outcome)
		{
			return outcome switch
			{
				"InProgress" => Get(ClimbText.InProgress), 
				"Summited" => Get(ClimbText.Summited), 
				"Failed" => Get(ClimbText.Failed), 
				_ => Get(ClimbText.Abandoned), 
			};
		}

		private static CultureInfo GetCulture(string name)
		{
			try
			{
				return CultureInfo.GetCultureInfo(name);
			}
			catch (CultureNotFoundException)
			{
				return CultureInfo.InvariantCulture;
			}
		}
	}
	internal sealed class ClimbTracker : IDisposable
	{
		private const string AirportScene = "Airport";

		private const float TerrainRandomiserCaptureDelaySeconds = 5f;

		private readonly ClimbHistory _history;

		private bool _activeInThisProcess;

		private bool _localPlayerSummited;

		private bool _delayedTerrainRandomiserCaptureAttempted;

		private bool _uiTerrainRandomiserCaptureAttempted;

		private bool _preserveActiveRunId;

		private float _nextSnapshotTime;

		internal ClimbTracker(ClimbHistory history)
		{
			_history = history;
			GlobalEvents.OnLocalCharacterWonRun = (Action)Delegate.Combine(GlobalEvents.OnLocalCharacterWonRun, new Action(OnLocalCharacterWonRun));
			GlobalEvents.OnRunEnded = (Action)Delegate.Combine(GlobalEvents.OnRunEnded, new Action(OnRunEnded));
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		internal void BeginRun()
		{
			//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)
			if (!PluginConfig.EnableTracker.Value || _activeInThisProcess)
			{
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name == "Airport")
			{
				return;
			}
			RunManager instance = RunManager.Instance;
			float elapsedSeconds = GetElapsedSeconds(instance);
			(string NetworkMode, string PhotonRoomName) networkState = GetNetworkState();
			string item = networkState.NetworkMode;
			string item2 = networkState.PhotonRoomName;
			string text = (((Object)(object)instance != (Object)null && instance.RunId != Guid.Empty) ? instance.RunId.ToString("D") : Guid.NewGuid().ToString("D"));
			ClimbRecord activeRecord = _history.ActiveRecord;
			if (activeRecord != null && activeRecord.RunId == text)
			{
				_activeInThisProcess = true;
				_localPlayerSummited = false;
				_delayedTerrainRandomiserCaptureAttempted = false;
				_uiTerrainRandomiserCaptureAttempted = false;
				_preserveActiveRunId = false;
				ScheduleNextSnapshot();
				return;
			}
			ClimbRecord climbRecord = new ClimbRecord
			{
				RunId = text,
				StartedAtUtc = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromSeconds(elapsedSeconds)).ToString("O", CultureInfo.InvariantCulture),
				Outcome = "InProgress",
				DurationSeconds = elapsedSeconds,
				CurrentHeightMeters = GetCurrentHeightMeters(),
				Ascent = Ascents.currentAscent,
				PlayerCount = GetPlayerCount(),
				IsCustomRun = RunSettings.IsCustomRun,
				IsMiniRun = RunSettings.isMiniRun,
				Level = GetLevelName(),
				Biome = GetBiomeName(),
				NetworkMode = item,
				PhotonRoomName = item2,
				MapBiomes = CaptureCurrentMapBiomes()
			};
			bool flag = _history.TryResumeMultiplayerRoom(climbRecord);
			if (!flag)
			{
				_history.Begin(climbRecord);
			}
			_activeInThisProcess = true;
			_localPlayerSummited = false;
			_delayedTerrainRandomiserCaptureAttempted = false;
			_uiTerrainRandomiserCaptureAttempted = false;
			_preserveActiveRunId = flag;
			ScheduleNextSnapshot();
			Plugin.Logger.LogInfo((object)(flag ? ("Resumed climb tracking from Photon room record. room=" + item2 + ", runId=" + _history.ActiveRecord?.RunId + ".") : ("Started tracking climb " + text + ".")));
		}

		internal void Update()
		{
			if (_activeInThisProcess)
			{
				float elapsedSeconds = GetElapsedSeconds(RunManager.Instance);
				TryCaptureDelayedTerrainRandomiserMapSettings(elapsedSeconds);
				float value = PluginConfig.AutoSaveInterval.Value;
				if (value <= 0f)
				{
					_nextSnapshotTime = 0f;
				}
				else if (_nextSnapshotTime <= 0f)
				{
					_nextSnapshotTime = Time.unscaledTime + value;
				}
				else if (!(Time.unscaledTime < _nextSnapshotTime))
				{
					_history.Snapshot(elapsedSeconds, GetCurrentHeightMeters(), GetBiomeName(), GetPlayerCount(), GetNetworkMode(), GetPhotonRoomName());
					TryCaptureActiveMapBiomes();
					_nextSnapshotTime = Time.unscaledTime + value;
				}
			}
		}

		internal void SnapshotActiveRun()
		{
			if (_activeInThisProcess && _history.ActiveRecord != null)
			{
				float elapsedSeconds = GetElapsedSeconds(RunManager.Instance);
				_history.Snapshot(elapsedSeconds, GetCurrentHeightMeters(), GetBiomeName(), GetPlayerCount(), GetNetworkMode(), GetPhotonRoomName());
				TryCaptureActiveMapBiomes();
				TryCaptureUiTerrainRandomiserMapSettings(elapsedSeconds);
			}
		}

		internal void UpdateRunId(Guid runId)
		{
			if (_activeInThisProcess && !_preserveActiveRunId && runId != Guid.Empty)
			{
				_history.UpdateActiveRunId(runId.ToString("D"));
			}
		}

		internal void AbandonActiveRun()
		{
			CompleteActiveRun("Abandoned");
		}

		internal void DeleteRecord(string runId)
		{
			bool flag = _history.ActiveRecord?.RunId == runId;
			if (_history.DeleteRecord(runId) && flag)
			{
				_activeInThisProcess = false;
				_localPlayerSummited = false;
				_delayedTerrainRandomiserCaptureAttempted = false;
				_uiTerrainRandomiserCaptureAttempted = false;
				_preserveActiveRunId = false;
				_nextSnapshotTime = 0f;
			}
		}

		internal void Dispose()
		{
			GlobalEvents.OnLocalCharacterWonRun = (Action)Delegate.Remove(GlobalEvents.OnLocalCharacterWonRun, new Action(OnLocalCharacterWonRun));
			GlobalEvents.OnRunEnded = (Action)Delegate.Remove(GlobalEvents.OnRunEnded, new Action(OnRunEnded));
			SceneManager.sceneLoaded -= OnSceneLoaded;
			CompleteActiveRun("Abandoned");
		}

		private void OnLocalCharacterWonRun()
		{
			_localPlayerSummited = true;
		}

		private void OnRunEnded()
		{
			if (!_activeInThisProcess)
			{
				BeginRun();
			}
			CharacterStats val = Character.localCharacter?.refs?.stats;
			string outcome = ((_localPlayerSummited || ((Object)(object)val != (Object)null && val.won)) ? "Summited" : "Failed");
			CompleteActiveRun(outcome);
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (!(((Scene)(ref scene)).name != "Airport"))
			{
				if (_activeInThisProcess)
				{
					CompleteActiveRun("Abandoned");
				}
				else
				{
					_history.RecoverInterrupted();
				}
			}
		}

		private void CompleteActiveRun(string outcome)
		{
			if (_history.ActiveRecord != null)
			{
				float elapsedSeconds = GetElapsedSeconds(RunManager.Instance);
				TryCaptureActiveMapBiomes();
				_history.Complete(outcome, elapsedSeconds, GetCurrentHeightMeters(), GetBiomeName(), GetPlayerCount(), GetNetworkMode(), GetPhotonRoomName());
				_activeInThisProcess = false;
				_localPlayerSummited = false;
				_delayedTerrainRandomiserCaptureAttempted = false;
				_uiTerrainRandomiserCaptureAttempted = false;
				_preserveActiveRunId = false;
				Plugin.Logger.LogInfo((object)("Recorded climb result: " + outcome + "."));
			}
		}

		private void TryCaptureDelayedTerrainRandomiserMapSettings(float elapsedSeconds)
		{
			if (!_delayedTerrainRandomiserCaptureAttempted && !(elapsedSeconds < 5f))
			{
				_delayedTerrainRandomiserCaptureAttempted = true;
				ClimbRecord activeRecord = _history.ActiveRecord;
				if (activeRecord != null && string.IsNullOrWhiteSpace(activeRecord.TerrainRandomiserMapSettingsBrotliBase64))
				{
					TryCaptureTerrainRandomiserMapSettings("delayed", elapsedSeconds);
				}
			}
		}

		private void TryCaptureUiTerrainRandomiserMapSettings(float elapsedSeconds)
		{
			if (!_uiTerrainRandomiserCaptureAttempted)
			{
				_uiTerrainRandomiserCaptureAttempted = true;
				ClimbRecord activeRecord = _history.ActiveRecord;
				if (activeRecord != null && string.IsNullOrWhiteSpace(activeRecord.TerrainRandomiserMapSettingsBrotliBase64))
				{
					TryCaptureTerrainRandomiserMapSettings("ui", elapsedSeconds);
				}
			}
		}

		private bool TryCaptureTerrainRandomiserMapSettings(string trigger, float elapsedSeconds)
		{
			ClimbRecord activeRecord = _history.ActiveRecord;
			if (activeRecord == null || !string.IsNullOrWhiteSpace(activeRecord.TerrainRandomiserMapSettingsBrotliBase64) || !TerrainRandomiserCompatibility.Loaded)
			{
				return false;
			}
			MapSettings mapSettings = TerrainRandomiserCompatibility.GetMapSettings();
			if (!TerrainRandomiserCompatibility.IsRandomiserEnabled(mapSettings))
			{
				return false;
			}
			string text = ((mapSettings != null) ? TerrainRandomiserCompatibility.CompressMapSettings(mapSettings) : "");
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			_history.UpdateActiveTerrainRandomiser(enabled: true, TerrainRandomiserCompatibility.GetMapSeed(mapSettings), text);
			Plugin.Logger.LogInfo((object)("Captured TerrainRandomiser map settings for active climb. trigger=" + trigger + ", elapsedSeconds=" + elapsedSeconds.ToString("0.##", CultureInfo.InvariantCulture) + "."));
			return true;
		}

		private void TryCaptureActiveMapBiomes()
		{
			ClimbRecord activeRecord = _history.ActiveRecord;
			if (activeRecord != null && (activeRecord.MapBiomes == null || activeRecord.MapBiomes.Count <= 0))
			{
				_history.EnsureActiveMapBiomes(CaptureCurrentMapBiomes());
			}
		}

		private static List<MapBiomeInfo> CaptureCurrentMapBiomes()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new List<MapBiomeInfo>(MapBiomeCollector.Capture(SceneManager.GetActiveScene()));
		}

		private static float GetElapsedSeconds(RunManager? runManager)
		{
			if (!((Object)(object)runManager != (Object)null))
			{
				return 0f;
			}
			return Math.Max(0f, runManager.timeSinceRunStarted);
		}

		private void ScheduleNextSnapshot()
		{
			float value = PluginConfig.AutoSaveInterval.Value;
			_nextSnapshotTime = ((value > 0f) ? (Time.unscaledTime + value) : 0f);
		}

		private static float GetCurrentHeightMeters()
		{
			CharacterStats val = Character.localCharacter?.refs?.stats;
			if (!((Object)(object)val != (Object)null))
			{
				return 0f;
			}
			return Math.Max(0f, val.heightInMeters);
		}

		private static int GetPlayerCount()
		{
			if (!PhotonNetwork.InRoom)
			{
				return 1;
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			return Math.Max(1, (currentRoom == null) ? 1 : currentRoom.PlayerCount);
		}

		private static (string NetworkMode, string PhotonRoomName) GetNetworkState()
		{
			string networkMode = GetNetworkMode();
			return (NetworkMode: networkMode, PhotonRoomName: (networkMode == "Multiplayer") ? GetPhotonRoomName() : "");
		}

		private static string GetNetworkMode()
		{
			if (PhotonNetwork.OfflineMode)
			{
				return "Offline";
			}
			if (!PhotonNetwork.InRoom)
			{
				return "";
			}
			return "Multiplayer";
		}

		private static string GetPhotonRoomName()
		{
			if (!PhotonNetwork.InRoom)
			{
				return "";
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			return ((currentRoom != null) ? currentRoom.Name : null) ?? "";
		}

		private static string GetLevelName()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string name = ((Scene)(ref activeScene)).name;
			int num = LevelNameToIndex(name);
			if (num == -1)
			{
				return name;
			}
			return GetLevelName(num);
		}

		private static string GetBiomeName()
		{
			try
			{
				MountainProgressHandler instance = Singleton<MountainProgressHandler>.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return "";
				}
				ProgressPoint[] progressPoints = instance.progressPoints;
				if (progressPoints == null || progressPoints.Length == 0)
				{
					return "";
				}
				int num = Mathf.Clamp(instance.maxProgressPointReached, 0, progressPoints.Length - 1);
				return progressPoints[num].title;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogDebug((object)("Failed to resolve current biome: " + ex.Message));
				return "";
			}
		}

		private static string GetLevelName(int levelIndex)
		{
			levelIndex %= SingletonAsset<MapBaker>.Instance.ScenePaths.Length;
			return PathUtil.WithoutExtensions(PathUtil.GetFileName(SingletonAsset<MapBaker>.Instance.ScenePaths[levelIndex]));
		}

		private static int LevelNameToIndex(string levelName)
		{
			for (int i = 0; i < SingletonAsset<MapBaker>.Instance.ScenePaths.Length; i++)
			{
				string levelName2 = GetLevelName(i);
				if (string.Equals(levelName2, levelName, StringComparison.OrdinalIgnoreCase))
				{
					return i;
				}
			}
			return -1;
		}

		void IDisposable.Dispose()
		{
			Dispose();
		}
	}
	[HarmonyPatch(typeof(RunManager), "StartRun")]
	internal static class RunManagerStartRunPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			Plugin.Tracker.BeginRun();
		}
	}
	[HarmonyPatch(typeof(RunManager), "SetRunIdForLobby")]
	internal static class RunManagerSetRunIdPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Guid guid)
		{
			Plugin.Tracker.UpdateRunId(guid);
		}
	}
	[HarmonyPatch(typeof(RunManager), "RPC_ReceiveRunId")]
	internal static class RunManagerReceiveRunIdPatch
	{
		[HarmonyPostfix]
		private static void Postfix(RunManager __instance)
		{
			Plugin.Tracker.UpdateRunId(__instance.RunId);
		}
	}
	[HarmonyPatch(typeof(Player), "LeaveCurrentGame")]
	internal static class PlayerLeaveCurrentGamePatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			Plugin.Tracker.AbandonActiveRun();
		}
	}
	internal static class MapBiomeCollector
	{
		private static readonly HashSet<string> RootsIgnoreInVariant = new HashSet<string> { "Redwoods", "Redwood", "Variant" };

		internal static IReadOnlyList<MapBiomeInfo> Capture(Scene scene)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			List<MapBiomeInfo> list = new List<MapBiomeInfo>();
			try
			{
				GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
				Biome val2 = default(Biome);
				for (int i = 0; i < ((Scene)(ref scene)).rootCount && i < rootGameObjects.Length; i++)
				{
					GameObject val = rootGameObjects[i];
					if (((Object)val).name != "Map")
					{
						continue;
					}
					for (int j = 0; j < val.transform.childCount; j++)
					{
						Transform child = val.transform.GetChild(j);
						for (int k = 0; k < child.childCount; k++)
						{
							GameObject gameObject = ((Component)child.GetChild(k)).gameObject;
							if (gameObject.activeInHierarchy && gameObject.TryGetComponent<Biome>(ref val2))
							{
								MapBiomeInfo obj = new MapBiomeInfo
								{
									BiomeType = val2.biomeType
								};
								Component? obj2 = FindVariant(val2);
								obj.Variant = CleanVariant(((obj2 != null) ? ((Object)obj2).name : null) ?? "", val2.biomeType);
								obj.OpenTomb = CheckOpenTomb(val2) == true;
								list.Add(obj);
							}
						}
					}
					break;
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogDebug((object)$"Failed to collect map biome information: {arg}");
			}
			return list;
		}

		private static Component? FindVariant(Biome biome)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			BiomeType biomeType = biome.biomeType;
			switch ((int)biomeType)
			{
			case 6:
			case 7:
				return ((Component)biome).gameObject.GetComponentInChildrenActiveSelf<VariantObject>();
			case 0:
			case 1:
			case 2:
				return ((Component)biome).gameObject.GetComponentInChildrenActiveSelf<BiomeVariant>();
			case 3:
			case 5:
			case 8:
				return null;
			default:
				return ((Component)biome).gameObject.GetComponentInChildrenActiveSelf<VariantObject>() ?? ((Component)biome).gameObject.GetComponentInChildrenActiveSelf<BiomeVariant>();
			}
		}

		private static bool? CheckOpenTomb(Biome biome)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)biome.biomeType != 6)
			{
				return null;
			}
			Transform val = ((Component)biome).transform.QueryChildren("Desert_Segment/Platteau/Rocks/Timple/Enterences/2");
			Transform val2 = (((Object)(object)val != (Object)null && val.childCount > 0) ? val.GetChild(0) : null);
			return (Object)(object)val2 != (Object)null && val2.childCount == 2;
		}

		private static string CleanVariant(string variant, BiomeType biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)biome != 6)
			{
				if ((int)biome == 7)
				{
					return CleanRootsVariant(variant);
				}
				return variant;
			}
			return CleanMesaVariant(variant);
		}

		private static string CleanMesaVariant(string variant)
		{
			if (variant.Equals("NoVariant", StringComparison.Ordinal))
			{
				return "Default";
			}
			if (variant.Equals("CacusHell", StringComparison.Ordinal))
			{
				return "CactusHell";
			}
			return variant;
		}

		private static string CleanRootsVariant(string variant)
		{
			string[] array = variant.Split(' ', '-');
			if (array.Length <= 1)
			{
				return variant;
			}
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				if (text.Length > 0)
				{
					text = text.Substring(0, 1).ToUpperInvariant() + text.Substring(1).ToLowerInvariant();
				}
				if (RootsIgnoreInVariant.Contains(text))
				{
					text = "";
				}
				array[i] = text;
			}
			return string.Join("", array);
		}

		private static Component? GetComponentInChildrenActiveSelf<T>(this GameObject gameObject) where T : Component
		{
			Component[] componentsInChildren = (Component[])(object)gameObject.GetComponentsInChildren<T>(true);
			Component[] array = componentsInChildren;
			Component[] array2 = array;
			foreach (Component val in array2)
			{
				if (val.gameObject.activeSelf)
				{
					return val;
				}
			}
			return null;
		}

		private static Transform? QueryChildren(this Transform current, string queryOrPath)
		{
			string[] array = queryOrPath.Split('/');
			string[] array2 = array;
			foreach (string text in array2)
			{
				Transform val = current.Find(text);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				current = val;
			}
			return current;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.github.LengSword.ClimbInfoTracker", "ClimbInfoTracker", "0.1.3")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Id = "com.github.LengSword.ClimbInfoTracker";

		internal static ManualLogSource Logger { get; private set; }

		internal static ClimbHistory History { get; private set; }

		internal static ClimbTracker Tracker { get; private set; }

		public static string Name => "ClimbInfoTracker";

		public static string Version => "0.1.3";

		private void Awake()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)("Plugin " + Name + " is loaded!"));
			PluginConfig.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			History = new ClimbHistory();
			Tracker = new ClimbTracker(History);
			Harmony val = new Harmony("com.github.LengSword.ClimbInfoTracker");
			val.PatchAll();
		}

		private void Start()
		{
			ClimbHistoryUi.Register();
		}

		private void Update()
		{
			Tracker.Update();
		}

		private void OnDestroy()
		{
			ClimbHistoryUi.Dispose();
			Tracker.Dispose();
		}
	}
	public static class PluginConfig
	{
		internal static ConfigEntry<bool> EnableTracker { get; private set; }

		internal static ConfigEntry<float> AutoSaveInterval { get; private set; }

		internal static ConfigEntry<bool> UseActualMapSettings { get; private set; }

		public static void Initialize(ConfigFile config, ManualLogSource logger)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			EnableTracker = config.Bind<bool>("General", "Enable Tracker", true, "Enable or disable recording new climb history.");
			AutoSaveInterval = config.Bind<float>("General", "AutoSaveInterval", 60f, new ConfigDescription("Seconds between in-progress climb snapshots. Set to 0 to save only at the start and end of a climb.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, float.MaxValue), Array.Empty<object>()));
			UseActualMapSettings = config.Bind<bool>("TerrainRandomiser", "UseActualMapSettings", true, "When loading TerrainRandomiser map settings from climb records, use the final generated settings by disabling randomBiome and randomVariant for overridden biomes.");
			logger.LogInfo((object)"Plugin Config Loaded.");
		}
	}
	[Serializable]
	internal class MapSettings
	{
		public bool enableRandomiser;

		public int seed;

		public bool autoRandomSeed;

		public bool fullyRandom;

		public List<BiomeSettings> biomes = new List<BiomeSettings>();
	}
	[Serializable]
	internal class BiomeSettings
	{
		public bool overrideEnabled;

		public string sectionName = "";

		public BiomeData selectedBiome = new BiomeData();

		public string selectedVariant = "";

		public bool randomBiome;

		public bool randomVariant;
	}
	[Serializable]
	internal class BiomeData
	{
		public string biomeName = "";

		public BiomeType biomeType;

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

		public VariantSelectionType variantSelectionType;
	}
	public enum VariantSelectionType
	{
		BiomeVariant,
		VariantObject
	}
	internal static class TerrainRandomiserCompatibility
	{
		internal static bool Loaded => Chainloader.PluginInfos.ContainsKey("com.snosz.terrainrandomiser");

		internal static MapSettings? GetMapSettings()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!Loaded)
			{
				return null;
			}
			if (PhotonNetwork.OfflineMode)
			{
				return GetLocalMapSettingsNoInlining();
			}
			try
			{
				SteamLobbyHandler service = GameHandler.GetService<SteamLobbyHandler>();
				string lobbyData = SteamMatchmaking.GetLobbyData(service.LobbySteamId, "TR_roomMapSettings");
				return string.IsNullOrWhiteSpace(lobbyData) ? null : JsonConvert.DeserializeObject<MapSettings>(lobbyData);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to read TerrainRandomiser lobby map settings: {arg}");
				return null;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static MapSettings? GetLocalMapSettingsNoInlining()
		{
			try
			{
				MapSettings val = Plugin.Instance?.mapSettings;
				if (val == null)
				{
					return null;
				}
				string text = JsonConvert.SerializeObject((object)val);
				return JsonConvert.DeserializeObject<MapSettings>(text);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to read local TerrainRandomiser map settings: {arg}");
				return null;
			}
		}

		internal static bool IsRandomiserEnabled(MapSettings? mapSettings)
		{
			return mapSettings?.enableRandomiser ?? false;
		}

		internal static int GetMapSeed(MapSettings? mapSettings)
		{
			return mapSettings?.seed ?? (-1);
		}

		internal static string CompressMapSettings(MapSettings mapSettings)
		{
			try
			{
				string s = JsonConvert.SerializeObject((object)mapSettings, (Formatting)0);
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				using MemoryStream memoryStream = new MemoryStream();
				using (BrotliStream brotliStream = new BrotliStream(memoryStream, CompressionLevel.Optimal, leaveOpen: true))
				{
					brotliStream.Write(bytes, 0, bytes.Length);
				}
				return Convert.ToBase64String(memoryStream.ToArray());
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to compress TerrainRandomiser map settings: {arg}");
				return "";
			}
		}

		internal static MapSettings? DecompressMapSettings(string compressedMapSettings)
		{
			if (string.IsNullOrWhiteSpace(compressedMapSettings))
			{
				return null;
			}
			try
			{
				byte[] buffer = Convert.FromBase64String(compressedMapSettings);
				using MemoryStream stream = new MemoryStream(buffer);
				using BrotliStream brotliStream = new BrotliStream(stream, CompressionMode.Decompress);
				using MemoryStream memoryStream = new MemoryStream();
				brotliStream.CopyTo(memoryStream);
				string text = Encoding.UTF8.GetString(memoryStream.ToArray());
				return JsonConvert.DeserializeObject<MapSettings>(text);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to decompress TerrainRandomiser map settings: {arg}");
				return null;
			}
		}

		internal static string? SaveMapSettings(string compressedMapSettings, string startedAtUtc)
		{
			MapSettings mapSettings = DecompressMapSettings(compressedMapSettings);
			if (mapSettings == null)
			{
				return null;
			}
			try
			{
				string text = Path.Combine(Application.persistentDataPath, "TerrainRandomiser");
				Directory.CreateDirectory(text);
				string text2 = FormatStartedAtTimestamp(startedAtUtc);
				string text3 = Path.Combine(text, "MapSettings_" + text2 + ".json");
				string contents = JsonConvert.SerializeObject((object)mapSettings, (Formatting)1);
				File.WriteAllText(text3, contents, Encoding.UTF8);
				return text3;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to save TerrainRandomiser map settings: {arg}");
				return null;
			}
		}

		internal static bool LoadMapSettings(string compressedMapSettings)
		{
			MapSettings mapSettings = DecompressMapSettings(compressedMapSettings);
			if (mapSettings == null)
			{
				return false;
			}
			try
			{
				if (PluginConfig.UseActualMapSettings.Value)
				{
					ApplyActualGeneratedMapSettings(mapSettings);
				}
				return LoadMapSettingsNoInlining(mapSettings);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to load TerrainRandomiser map settings: {arg}");
				return false;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool LoadMapSettingsNoInlining(MapSettings mapSettings)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Plugin.Logger.LogError((object)"Failed to load TerrainRandomiser map settings: plugin missing.");
				return false;
			}
			string text = JsonConvert.SerializeObject((object)mapSettings);
			MapSettings val = JsonConvert.DeserializeObject<MapSettings>(text);
			if (val == null)
			{
				Plugin.Logger.LogError((object)"Failed to convert local map settings to TerrainRandomiser map settings.");
				return false;
			}
			instance.mapSettings = val;
			return true;
		}

		private static void ApplyActualGeneratedMapSettings(MapSettings mapSettings)
		{
			if (mapSettings.biomes == null)
			{
				return;
			}
			foreach (BiomeSettings biome in mapSettings.biomes)
			{
				if (biome.overrideEnabled)
				{
					biome.randomBiome = false;
					biome.randomVariant = false;
				}
			}
		}

		private static string FormatStartedAtTimestamp(string startedAtUtc)
		{
			if (DateTimeOffset.TryParse(startedAtUtc, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
			{
				return result.ToLocalTime().ToString("yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture);
			}
			if (string.IsNullOrWhiteSpace(startedAtUtc))
			{
				return "UnknownStart";
			}
			StringBuilder stringBuilder = new StringBuilder(startedAtUtc.Length);
			foreach (char c in startedAtUtc)
			{
				stringBuilder.Append((Array.IndexOf(Path.GetInvalidFileNameChars(), c) >= 0) ? '-' : c);
			}
			return stringBuilder.ToString();
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ConstantExpectedAttribute : Attribute
	{
		public object? Min { get; set; }

		public object? Max { get; set; }
	}
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ExperimentalAttribute : Attribute
	{
		public string DiagnosticId { get; }

		public string? UrlFormat { get; set; }

		public ExperimentalAttribute(string diagnosticId)
		{
			DiagnosticId = diagnosticId;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullAttribute : Attribute
	{
		public string[] Members { get; }

		public MemberNotNullAttribute(string member)
		{
			Members = new string[1] { member };
		}

		public MemberNotNullAttribute(params string[] members)
		{
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public string[] Members { get; }

		public MemberNotNullWhenAttribute(bool returnValue, string member)
		{
			ReturnValue = returnValue;
			Members = new string[1] { member };
		}

		public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
		{
			ReturnValue = returnValue;
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SetsRequiredMembersAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class StringSyntaxAttribute : Attribute
	{
		public const string CompositeFormat = "CompositeFormat";

		public const string DateOnlyFormat = "Dat