Decompiled source of RollOfTheScoutClient v1.0.8

plugins/com.peakmodding.RollOfTheScoutClient.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Peak;
using PeakNativeUi;
using PeakOverheadCompat;
using Photon.Pun;
using Photon.Realtime;
using RollOfTheScout.Shared;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.peakmodding.RollOfTheScoutClient")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyInformationalVersion("1.0.8")]
[assembly: AssemblyProduct("com.peakmodding.RollOfTheScoutClient")]
[assembly: AssemblyTitle("RollOfTheScoutClient")]
[assembly: AssemblyVersion("1.0.8.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RollOfTheScout.Shared
{
	public enum MilesLocalGemUsage
	{
		Unknown,
		SelfUse,
		FeedNormal,
		FeedDowned
	}
	public sealed class MilesSoulLocal : MonoBehaviour, IOnEventCallback
	{
		private sealed class PendingReduce
		{
			public int Amount;

			public float FireAt;
		}

		private sealed class PendingZero
		{
			public int Target;

			public float FireAt;

			public float Until;

			public float NextWrite;

			public float Stamina;

			public bool StaminaApplied;
		}

		public const string ModId = "com.peakmodding.RollOfTheScout";

		public const byte EventCode = 197;

		public const byte KindClientHello = 7;

		public const byte KindGameplayState = 8;

		public const byte KindUsageReport = 9;

		public const byte KindClientReady = 10;

		public const string AmuletHealing = "Amulet_Healing";

		public const string AmuletSuperJump = "Amulet_SuperJump";

		public const string AmuletClone = "Amulet_Clone";

		public const string AmuletInfiniteStamina = "Amulet_InfiniteStamina";

		private const float UseDedupWindow = 0.3f;

		private const float ReduceDelay = 0.35f;

		private const float ZeroCorrectionInterval = 0.25f;

		private const float ReadyAckInterval = 8f;

		private Action<string> _info;

		private Action<string> _warning;

		private bool _hasState;

		private int _classCode;

		private float _nextHello;

		private string _lastStateData;

		private float _nextAckTime;

		private float _nextSceneCheck;

		private bool _cachedAirport;

		private float _healingPetrify;

		private float _healingHunger;

		private float _superJumpPetrify;

		private float _superJumpHunger;

		private float _superJumpStamina;

		private float _clonePetrify;

		private float _cloneHunger;

		private float _cloneStamina;

		private float _ambitionPetrify;

		private float _ambitionHunger;

		private float _ambitionStamina;

		private bool _ruleZeroAssist;

		private float _ruleZeroStamina;

		private string _guardGem;

		private float _guardUntil;

		private int _preUsePetrify = -1;

		private float _preUseAt;

		private PendingReduce _reduce;

		private PendingZero _zero;

		public static MilesSoulLocal Instance { get; private set; }

		public void Configure(Action<string> info, Action<string> warning)
		{
			_info = info;
			_warning = warning;
		}

		private void Awake()
		{
			Instance = this;
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			if (Time.time >= _nextSceneCheck)
			{
				_nextSceneCheck = Time.time + 0.5f;
				_cachedAirport = IsInAirport();
			}
			if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient || _cachedAirport)
			{
				ResetRun();
				return;
			}
			bool isInGameplayScene = GameHandler.IsInGameplayScene;
			if (isInGameplayScene && Time.time >= _nextHello)
			{
				_nextHello = Time.time + 2f;
				SendToMaster(7, "1");
			}
			if (isInGameplayScene && IsActiveFor(Character.localCharacter))
			{
				TickReduce();
				TickZero();
			}
		}

		private static bool IsInAirport()
		{
			//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();
			return ((Scene)(ref activeScene)).name.IndexOf("Airport", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private void ResetRun()
		{
			_hasState = false;
			_classCode = 0;
			_nextHello = 0f;
			_lastStateData = null;
			_nextAckTime = 0f;
			_guardGem = null;
			_guardUntil = 0f;
			_preUsePetrify = -1;
			_reduce = null;
			_zero = null;
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent == null || photonEvent.Code != 197 || PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient == null || photonEvent.Sender != masterClient.ActorNumber)
			{
				return;
			}
			try
			{
				if (photonEvent.CustomData is object[] array && array.Length >= 3 && array[0] is string text && !(text != "com.peakmodding.RollOfTheScout") && Convert.ToByte(array[1]) == 8)
				{
					ApplyGameplayState(array[2] as string);
				}
			}
			catch (Exception ex)
			{
				_warning?.Invoke("残魂本机状态同步失败:" + ex.Message);
			}
		}

		private void ApplyGameplayState(string data)
		{
			if (string.IsNullOrEmpty(data))
			{
				return;
			}
			if (string.Equals(data, _lastStateData, StringComparison.Ordinal))
			{
				if (_classCode == 26 && Time.time >= _nextAckTime)
				{
					SendReady();
				}
				return;
			}
			string[] array = data.Split('|');
			if (array.Length != 14 || !int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return;
			}
			float[] array2 = new float[12];
			int[] array3 = new int[12]
			{
				1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
				11, 13
			};
			for (int i = 0; i < array2.Length; i++)
			{
				if (!float.TryParse(array[array3[i]], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return;
				}
			}
			if (int.TryParse(array[12], out var result2))
			{
				if (_classCode != result)
				{
					_reduce = null;
					_zero = null;
					_guardGem = null;
					_preUsePetrify = -1;
				}
				_classCode = result;
				_healingPetrify = array2[0];
				_healingHunger = array2[1];
				_superJumpPetrify = array2[2];
				_superJumpHunger = array2[3];
				_superJumpStamina = array2[4];
				_clonePetrify = array2[5];
				_cloneHunger = array2[6];
				_cloneStamina = array2[7];
				_ambitionPetrify = array2[8];
				_ambitionHunger = array2[9];
				_ambitionStamina = array2[10];
				_ruleZeroAssist = result2 != 0;
				_ruleZeroStamina = array2[11];
				_hasState = true;
				_lastStateData = data;
				if (_classCode == 26)
				{
					SendReady();
				}
			}
		}

		private void SendReady()
		{
			_nextAckTime = Time.time + 8f;
			SendToMaster(10, "26");
		}

		private static void SendToMaster(byte kind, string data)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient == null)
			{
				return;
			}
			try
			{
				object[] obj = new object[3] { "com.peakmodding.RollOfTheScout", kind, data };
				RaiseEventOptions val = new RaiseEventOptions();
				val.TargetActors = new int[1] { masterClient.ActorNumber };
				PhotonNetwork.RaiseEvent((byte)197, (object)obj, val, SendOptions.SendReliable);
			}
			catch
			{
			}
		}

		public static bool TryParseUsageReport(string data, out string gemName, out int usage)
		{
			gemName = null;
			usage = 0;
			if (string.IsNullOrEmpty(data))
			{
				return false;
			}
			int num = data.LastIndexOf('|');
			if (num <= 0 || num + 1 >= data.Length)
			{
				return false;
			}
			if (!int.TryParse(data.Substring(num + 1), out usage))
			{
				return false;
			}
			gemName = data.Substring(0, num);
			if (IsGem(gemName) && usage >= 0)
			{
				return usage <= 3;
			}
			return false;
		}

		public static bool IsGem(string name)
		{
			switch (name)
			{
			default:
				return name == "Amulet_InfiniteStamina";
			case "Amulet_Healing":
			case "Amulet_SuperJump":
			case "Amulet_Clone":
				return true;
			}
		}

		private bool IsActiveFor(Character character)
		{
			if (!_hasState || _classCode != 26 || PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			if ((Object)(object)character == (Object)null || (Object)(object)character != (Object)(object)Character.localCharacter)
			{
				return false;
			}
			if ((Object)(object)((MonoBehaviourPun)character).photonView != (Object)null)
			{
				return ((MonoBehaviourPun)character).photonView.IsMine;
			}
			return false;
		}

		public void SnapshotPreUse(Item item)
		{
			if (!((Object)(object)item == (Object)null))
			{
				Character holderCharacter = item.holderCharacter;
				if (IsActiveFor(holderCharacter) && TryGemName(item, out var _) && (_preUsePetrify < 0 || !(Time.time - _preUseAt < 0.5f)))
				{
					_preUsePetrify = GetPetrify(holderCharacter);
					_preUseAt = Time.time;
				}
			}
		}

		public void NotifyGemUsed(Item item, Character holder, MilesLocalGemUsage usage, bool cloneSuccess = false)
		{
			if (!((Object)(object)item == (Object)null) && IsActiveFor(holder) && TryGemName(item, out var gemName))
			{
				OnGemUsed(holder, gemName, usage, cloneSuccess);
			}
		}

		public void NotifyCloneUsed(Character holder, bool success)
		{
			if (success && IsActiveFor(holder))
			{
				OnGemUsed(holder, "Amulet_Clone", MilesLocalGemUsage.SelfUse, cloneSuccess: true);
			}
		}

		private static bool TryGemName(Item item, out string gemName)
		{
			gemName = null;
			Item val = default(Item);
			if ((Object)(object)item == (Object)null || !ItemDatabase.TryGetItem(item.itemID, ref val) || (Object)(object)val == (Object)null)
			{
				return false;
			}
			gemName = ((Object)((Component)val).gameObject).name;
			return IsGem(gemName);
		}

		private void OnGemUsed(Character character, string gemName, MilesLocalGemUsage usage, bool cloneSuccess)
		{
			if ((gemName == "Amulet_Clone" && !cloneSuccess) || (_guardGem == gemName && Time.time < _guardUntil))
			{
				return;
			}
			_guardGem = gemName;
			_guardUntil = Time.time + 0.3f;
			bool flag = _preUsePetrify >= 0 && Time.time - _preUseAt <= 1.5f;
			int num = (flag ? _preUsePetrify : GetPetrify(character));
			_preUsePetrify = -1;
			switch (gemName)
			{
			case "Amulet_Healing":
				if (usage == MilesLocalGemUsage.FeedNormal)
				{
					_info?.Invoke("童军的毅力:喂给未昏迷队友,无职业效果。");
				}
				else if (usage == MilesLocalGemUsage.FeedDowned && _ruleZeroAssist)
				{
					if (!flag)
					{
						_warning?.Invoke("零号守则:未记录到使用前的石化值,本次无法等量清除(请检查宝石动作 / 喂食前缀补丁是否生效)。");
						break;
					}
					_zero = new PendingZero
					{
						Target = Mathf.Clamp(num, 0, 100),
						FireAt = Time.time + 0.35f,
						Until = Time.time + 3f,
						Stamina = _ruleZeroStamina
					};
				}
				else
				{
					AdjustStatus(character, (STATUSTYPE)1, 0f - _healingHunger);
					QueueReduce(_healingPetrify);
				}
				break;
			case "Amulet_SuperJump":
				AdjustStatus(character, (STATUSTYPE)1, 0f - _superJumpHunger);
				QueueReduce(_superJumpPetrify);
				BoostStamina(character, _superJumpStamina);
				break;
			case "Amulet_Clone":
				AdjustStatus(character, (STATUSTYPE)1, 0f - _cloneHunger);
				QueueReduce(_clonePetrify);
				BoostStamina(character, _cloneStamina);
				break;
			case "Amulet_InfiniteStamina":
				AdjustStatus(character, (STATUSTYPE)1, 0f - _ambitionHunger);
				QueueReduce(_ambitionPetrify);
				BoostStamina(character, _ambitionStamina);
				break;
			}
			int num2 = (int)usage;
			SendToMaster(9, gemName + "|" + num2);
		}

		private void QueueReduce(float amount)
		{
			int num = Mathf.RoundToInt(Mathf.Max(0f, amount) * 100f);
			if (num > 0)
			{
				if (_reduce == null)
				{
					_reduce = new PendingReduce();
				}
				_reduce.Amount += num;
				_reduce.FireAt = Time.time + 0.35f;
			}
		}

		private void TickReduce()
		{
			if (_reduce == null || Time.time < _reduce.FireAt)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			if (!IsActiveFor(localCharacter))
			{
				_reduce = null;
				return;
			}
			int petrify = GetPetrify(localCharacter);
			int num = Mathf.Clamp(petrify - _reduce.Amount, 0, 100);
			if (num != petrify)
			{
				localCharacter.data.SetPetrify(num);
			}
			_reduce = null;
		}

		private void TickZero()
		{
			if (_zero == null || Time.time < _zero.FireAt)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			if (!IsActiveFor(localCharacter))
			{
				_zero = null;
				return;
			}
			int num = GetPetrify(localCharacter);
			if (num > _zero.Target && Time.time >= _zero.NextWrite)
			{
				localCharacter.data.SetPetrify(_zero.Target);
				_zero.NextWrite = Time.time + 0.25f;
				num = _zero.Target;
			}
			if (num <= _zero.Target && !_zero.StaminaApplied)
			{
				BoostStamina(localCharacter, _zero.Stamina);
				_zero.StaminaApplied = true;
			}
			if (Time.time >= _zero.Until)
			{
				if (!_zero.StaminaApplied)
				{
					BoostStamina(localCharacter, _zero.Stamina);
					_zero.StaminaApplied = true;
					_warning?.Invoke($"零号守则:石化未在 3 秒窗口内回到使用前基准(当前 {num} > 基准 {_zero.Target})," + "已在窗口结束时兜底发放额外体力(可能受石化上限影响)。");
				}
				_zero = null;
			}
		}

		private static int GetPetrify(Character character)
		{
			if (!((Object)(object)character == (Object)null) && !((Object)(object)character.data == (Object)null))
			{
				return Mathf.Clamp(character.data.petrifyAmount, 0, 100);
			}
			return 0;
		}

		private static void AdjustStatus(Character character, STATUSTYPE type, float amount)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)character == (Object)null) && character.refs != null && !((Object)(object)character.refs.afflictions == (Object)null) && !Mathf.Approximately(amount, 0f))
			{
				character.refs.afflictions.AdjustStatus(type, amount, false);
			}
		}

		private static void BoostStamina(Character character, float amount)
		{
			if (!((Object)(object)character == (Object)null) && !Mathf.Approximately(amount, 0f))
			{
				character.MoraleBoost(amount, 1);
			}
		}

		private static bool IsDowned(Character character)
		{
			if ((Object)(object)character != (Object)null && (Object)(object)character.data != (Object)null && character.data.fullyPassedOut)
			{
				return !character.data.dead;
			}
			return false;
		}

		internal static MilesLocalGemUsage UsageOfFeedTarget(Character target)
		{
			if (!IsDowned(target))
			{
				return MilesLocalGemUsage.FeedNormal;
			}
			return MilesLocalGemUsage.FeedDowned;
		}
	}
	public static class MilesLocalClonePatch
	{
		private static readonly FieldInfo ItemField = AccessTools.Field(typeof(ItemActionBase), "item");

		public static void Postfix(Action_CloneSelectedItem __instance, bool success)
		{
			try
			{
				if (success && !((Object)(object)MilesSoulLocal.Instance == (Object)null))
				{
					object? obj = ItemField?.GetValue(__instance);
					Item val = (Item)((obj is Item) ? obj : null);
					if (!((Object)(object)val == (Object)null))
					{
						MilesSoulLocal.Instance.NotifyGemUsed(val, val.holderCharacter, MilesLocalGemUsage.SelfUse, cloneSuccess: true);
					}
				}
			}
			catch
			{
			}
		}
	}
	public static class MilesLocalGemUsePatch
	{
		private static readonly FieldInfo ItemField = AccessTools.Field(typeof(ItemActionBase), "item");

		public static void ToggleGemPrefix(AmuletBase __instance)
		{
			MilesSoulLocal.Instance?.SnapshotPreUse(((ItemComponent)__instance).item);
		}

		public static void ToggleGemPostfix(AmuletBase __instance)
		{
			MilesSoulLocal.Instance?.NotifyGemUsed(((ItemComponent)__instance).item, ((ItemComponent)__instance).item.holderCharacter, MilesLocalGemUsage.SelfUse);
		}

		public static void HealingGemPrefix(Action_HealingGem __instance)
		{
			FromActionPrefix((ItemActionBase)(object)__instance);
		}

		public static void SuperJumpPrefix(Action_SuperJumpAmulet __instance)
		{
			FromActionPrefix((ItemActionBase)(object)__instance);
		}

		public static void HealingGemPostfix(Action_HealingGem __instance)
		{
			FromAction((ItemActionBase)(object)__instance);
		}

		public static void SuperJumpPostfix(Action_SuperJumpAmulet __instance)
		{
			FromAction((ItemActionBase)(object)__instance);
		}

		private static void FromActionPrefix(ItemActionBase action)
		{
			object? obj = ItemField?.GetValue(action);
			Item item = (Item)((obj is Item) ? obj : null);
			MilesSoulLocal.Instance?.SnapshotPreUse(item);
		}

		private static void FromAction(ItemActionBase action)
		{
			object? obj = ItemField?.GetValue(action);
			Item val = (Item)((obj is Item) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				MilesSoulLocal.Instance?.NotifyGemUsed(val, val.holderCharacter, MilesLocalGemUsage.SelfUse);
			}
		}
	}
	public static class MilesLocalFeedPatch
	{
		private static readonly FieldInfo BestCharacterField = AccessTools.Field(typeof(Interaction), "bestCharacter");

		private static Character FeedTarget()
		{
			if ((Object)(object)Interaction.instance == (Object)null || BestCharacterField == null)
			{
				return null;
			}
			object? value = BestCharacterField.GetValue(Interaction.instance);
			CharacterInteractible val = (CharacterInteractible)((value is CharacterInteractible) ? value : null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.character;
		}

		public static void Prefix(Item __instance, out MilesLocalGemUsage __state)
		{
			__state = MilesLocalGemUsage.Unknown;
			if (!((Object)(object)__instance == (Object)null) && __instance.canUseOnFriend)
			{
				Character val = FeedTarget();
				if (!((Object)(object)val == (Object)null))
				{
					__state = MilesSoulLocal.UsageOfFeedTarget(val);
					MilesSoulLocal.Instance?.SnapshotPreUse(__instance);
				}
			}
		}

		public static void Postfix(Item __instance, MilesLocalGemUsage __state)
		{
			if (__state != MilesLocalGemUsage.Unknown && !((Object)(object)__instance == (Object)null))
			{
				MilesSoulLocal.Instance?.NotifyGemUsed(__instance, __instance.holderCharacter, __state);
			}
		}
	}
}
namespace PeakNativeUi
{
	public static class NativeFont
	{
		private static TMP_FontAsset _resolved;

		public static TMP_FontAsset Resolve()
		{
			if ((Object)(object)_resolved != (Object)null)
			{
				return _resolved;
			}
			TMP_FontAsset val = null;
			TMP_FontAsset val2 = null;
			TMP_FontAsset val3 = null;
			try
			{
				FontFallbackSwapper instance = FontFallbackSwapper.instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.mainBaseFont != (Object)null)
				{
					val = instance.mainBaseFont;
				}
			}
			catch
			{
			}
			try
			{
				TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
				foreach (TMP_FontAsset val4 in array)
				{
					if (!((Object)(object)val4 == (Object)null) && HasCjk(val4))
					{
						if ((Object)(object)val == (Object)null && ((Object)val4).name != null && ((Object)val4).name.IndexOf("Daruma", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							val = val4;
						}
						if ((Object)(object)val2 == (Object)null && HasChar(val4, 'A'))
						{
							val2 = val4;
						}
						if ((Object)(object)val3 == (Object)null && HasChar(val4, 'A') && LooksThin(val4))
						{
							val3 = val4;
						}
					}
				}
			}
			catch
			{
			}
			if ((Object)(object)val3 == (Object)null && (Object)(object)val2 == (Object)null)
			{
				try
				{
					PlayerName[] array2 = Resources.FindObjectsOfTypeAll<PlayerName>();
					foreach (PlayerName val5 in array2)
					{
						if (!((Object)(object)val5 == (Object)null) && !((Object)(object)val5.text == (Object)null) && !((Object)(object)((TMP_Text)val5.text).font == (Object)null) && HasCjk(((TMP_Text)val5.text).font) && HasChar(((TMP_Text)val5.text).font, 'A'))
						{
							val2 = ((TMP_Text)val5.text).font;
							if ((Object)(object)val3 == (Object)null && LooksThin(((TMP_Text)val5.text).font))
							{
								val3 = ((TMP_Text)val5.text).font;
							}
							break;
						}
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val3 != (Object)null)
			{
				return _resolved = val3;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				return _resolved = val2;
			}
			if ((Object)(object)val != (Object)null && HasCjk(val) && HasChar(val, 'A'))
			{
				return _resolved = val;
			}
			try
			{
				TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
				if ((Object)(object)defaultFontAsset != (Object)null && HasCjk(defaultFontAsset))
				{
					return _resolved = defaultFontAsset;
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool LooksThin(TMP_FontAsset f)
		{
			if ((Object)(object)f == (Object)null || string.IsNullOrEmpty(((Object)f).name))
			{
				return false;
			}
			string name = ((Object)f).name;
			if (name.IndexOf("Daruma", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return false;
			}
			if (name.IndexOf("Noto", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Sans", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("SourceHan", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Source Han", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("CJK", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Hei", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf("Gothic", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static bool HasChar(TMP_FontAsset font, char c)
		{
			try
			{
				return (Object)(object)font != (Object)null && font.HasCharacter(c, true, false);
			}
			catch
			{
				return false;
			}
		}

		private static bool HasCjk(TMP_FontAsset font)
		{
			try
			{
				return (Object)(object)font != (Object)null && font.HasCharacter('职', true, false);
			}
			catch
			{
				return false;
			}
		}
	}
	public static class NativeOverlay
	{
		private sealed class Block
		{
			public RectTransform Root;

			public Image Bg;

			public TextMeshProUGUI Text;

			public float WidthFraction;

			public bool Bold;

			public string Last;

			public float LastSize;

			public int LastScreenHeight;

			public int LastVersion = -1;
		}

		private static bool _ready;

		private static float _nextTry;

		private static GameObject _root;

		private static Block _toast;

		private static Block _panel;

		private static Block _intro;

		public static bool Ready
		{
			get
			{
				if (_ready && (Object)(object)_root == (Object)null)
				{
					_ready = false;
				}
				return _ready;
			}
		}

		public static bool Ensure()
		{
			if (Ready)
			{
				return true;
			}
			if (Time.time < _nextTry)
			{
				return false;
			}
			_nextTry = Time.time + 1f;
			try
			{
				TMP_FontAsset val = NativeFont.Resolve();
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				Build(val);
				_ready = true;
				return true;
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[PeakNativeUi] 创建原生字体叠加层失败(将回退旧绘制):" + ex.Message));
				return false;
			}
		}

		public static void SetToast(IList<string> lines)
		{
			if (Ready)
			{
				SetBlock(_toast, Join(lines, null), _toast.Bold);
				PositionToast();
			}
		}

		public static void SetToast(IList<string> lines, int version)
		{
			if (Ready)
			{
				Block toast = _toast;
				if (toast != null && (version < 0 || version != toast.LastVersion))
				{
					toast.LastVersion = version;
					SetBlock(toast, Join(lines, null), toast.Bold);
					PositionToast();
				}
			}
		}

		public static void SetPanel(string title, IList<string> lines, bool visible)
		{
			SetPanel(title, lines, visible, -1);
		}

		public static void SetPanel(string title, IList<string> lines, bool visible, int version)
		{
			if (Ready)
			{
				Block panel = _panel;
				if (!visible)
				{
					SetBlock(panel, null, panel.Bold);
					panel.LastVersion = int.MinValue;
					SetBlock(_intro, null, _intro.Bold);
					_intro.LastVersion = int.MinValue;
					PositionToast();
				}
				else if (version < 0 || version != panel.LastVersion)
				{
					panel.LastVersion = version;
					SetBlock(panel, Join(lines, title), panel.Bold);
				}
			}
		}

		public static void SetPanelHidden()
		{
			if (Ready)
			{
				SetBlock(_panel, null, _panel.Bold);
				_panel.LastVersion = int.MinValue;
				SetBlock(_intro, null, _intro.Bold);
				_intro.LastVersion = int.MinValue;
				PositionToast();
			}
		}

		public static void SetClassIntro(string title, IList<string> lines, bool visible)
		{
			SetClassIntro(title, lines, visible, -1);
		}

		public static void SetClassIntro(string title, IList<string> lines, bool visible, int version)
		{
			if (!Ready)
			{
				return;
			}
			Block intro = _intro;
			if (!visible)
			{
				SetBlock(intro, null, intro.Bold);
				intro.LastVersion = int.MinValue;
				PositionToast();
				return;
			}
			if (version < 0 || version != intro.LastVersion)
			{
				intro.LastVersion = version;
				SetBlock(intro, Join(lines, title), intro.Bold);
			}
			PositionToast();
		}

		public static void SetClassIntroHidden()
		{
			if (Ready)
			{
				SetBlock(_intro, null, _intro.Bold);
				_intro.LastVersion = int.MinValue;
				PositionToast();
			}
		}

		private static void PositionToast()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Block toast = _toast;
			if (toast != null && !((Object)(object)toast.Root == (Object)null))
			{
				float num = 12f;
				if (_intro != null && (Object)(object)_intro.Root != (Object)null && ((Component)_intro.Root).gameObject.activeSelf)
				{
					num += _intro.Root.sizeDelta.y + 8f;
				}
				toast.Root.anchoredPosition = new Vector2(12f, 0f - num);
			}
		}

		public static void Dispose()
		{
			try
			{
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
				}
			}
			catch
			{
			}
			_root = null;
			_toast = null;
			_panel = null;
			_intro = null;
			_ready = false;
			_nextTry = 0f;
		}

		private static void Build(TMP_FontAsset font)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			_root = new GameObject("RollOfTheScout.NativeOverlay", new Type[1] { typeof(RectTransform) });
			Object.DontDestroyOnLoad((Object)(object)_root);
			Canvas obj = _root.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 32000;
			_toast = CreateBlock("Toast", font, rightSide: false, 0.45f, bold: false);
			_panel = CreateBlock("ClassPanel", font, rightSide: true, 0.32f, bold: false);
			_intro = CreateBlock("ClassIntro", font, rightSide: false, 0.4f, bold: false);
		}

		private static Block CreateBlock(string name, TMP_FontAsset font, bool rightSide, float widthFraction, bool bold)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_root.transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			float num = (rightSide ? 1f : 0f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(num, 1f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.pivot = new Vector2(num, 1f);
			component.anchoredPosition = (rightSide ? new Vector2(-12f, -12f) : new Vector2(12f, -12f));
			component.sizeDelta = new Vector2(400f, 40f);
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = new Color(0f, 0f, 0f, 0.6f);
			((Graphic)val3).raycastTarget = false;
			GameObject val4 = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent(val.transform, false);
			RectTransform component2 = val4.GetComponent<RectTransform>();
			((Vector2)(ref val2))..ctor(0f, 1f);
			component2.anchorMax = val2;
			component2.anchorMin = val2;
			component2.pivot = new Vector2(0f, 1f);
			component2.anchoredPosition = new Vector2(10f, -6f);
			TextMeshProUGUI val5 = val4.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val5).font = font;
			if ((Object)(object)((TMP_Asset)font).material != (Object)null)
			{
				((TMP_Text)val5).fontSharedMaterial = ((TMP_Asset)font).material;
			}
			((Graphic)val5).raycastTarget = false;
			((TMP_Text)val5).alignment = (TextAlignmentOptions)257;
			((TMP_Text)val5).textWrappingMode = (TextWrappingModes)1;
			((TMP_Text)val5).overflowMode = (TextOverflowModes)0;
			((Graphic)val5).color = Color.white;
			((TMP_Text)val5).fontStyle = (FontStyles)(bold ? 1 : 0);
			return new Block
			{
				Root = component,
				Bg = val3,
				Text = val5,
				WidthFraction = widthFraction,
				Bold = bold
			};
		}

		private static void SetBlock(Block b, string content, bool bold)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			if (b == null || (Object)(object)b.Root == (Object)null)
			{
				return;
			}
			if (string.IsNullOrEmpty(content))
			{
				if (((Component)b.Root).gameObject.activeSelf)
				{
					((Component)b.Root).gameObject.SetActive(false);
				}
				b.Last = null;
				return;
			}
			if (!((Component)b.Root).gameObject.activeSelf)
			{
				((Component)b.Root).gameObject.SetActive(true);
			}
			float num = CurrentFontSize();
			if (!(content == b.Last) || !Mathf.Approximately(num, b.LastSize) || Screen.height != b.LastScreenHeight)
			{
				b.Last = content;
				b.LastSize = num;
				b.LastScreenHeight = Screen.height;
				float num2 = (float)Screen.width * b.WidthFraction;
				TextMeshProUGUI text = b.Text;
				((TMP_Text)text).fontSize = num;
				((TMP_Text)text).fontStyle = (FontStyles)(bold ? 1 : 0);
				((TMP_Text)text).text = content;
				RectTransform rectTransform = ((TMP_Text)text).rectTransform;
				rectTransform.sizeDelta = new Vector2(num2 - 20f, 4000f);
				((TMP_Text)text).ForceMeshUpdate(false, false);
				float preferredHeight = ((TMP_Text)text).preferredHeight;
				rectTransform.sizeDelta = new Vector2(num2 - 20f, preferredHeight);
				b.Root.sizeDelta = new Vector2(num2, preferredHeight + 12f);
			}
		}

		private static float CurrentFontSize()
		{
			return Mathf.Max(16, Mathf.RoundToInt((float)Screen.height / 48f));
		}

		private static string Join(IList<string> lines, string title)
		{
			if ((lines == null || lines.Count == 0) && string.IsNullOrEmpty(title))
			{
				return null;
			}
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrEmpty(title))
			{
				stringBuilder.Append("<color=#FFE666>").Append(title).Append("</color>");
			}
			if (lines != null)
			{
				for (int i = 0; i < lines.Count; i++)
				{
					if (!string.IsNullOrEmpty(lines[i]))
					{
						if (stringBuilder.Length > 0)
						{
							stringBuilder.Append('\n');
						}
						stringBuilder.Append(lines[i]);
					}
				}
			}
			if (stringBuilder.Length != 0)
			{
				return stringBuilder.ToString();
			}
			return null;
		}
	}
}
namespace PeakOverheadCompat
{
	public static class ThirdPartyOverheadNameCompat
	{
		internal static class BpdExtractNamePatch
		{
			public static void Postfix(string objName, ref string __result)
			{
				try
				{
					if (Active && !string.IsNullOrEmpty(__result) && TryActorFromObjName(objName, out var actor) && !string.IsNullOrEmpty(ClassNameOf(actor)))
					{
						__result = Rebuild(__result, actor);
					}
				}
				catch
				{
				}
			}
		}

		internal static class BpdLabelPatch
		{
			private sealed class LabelState
			{
				public string Original;

				public string Desired;
			}

			private static readonly ConditionalWeakTable<object, LabelState> States = new ConditionalWeakTable<object, LabelState>();

			public static void Postfix(object __instance)
			{
				try
				{
					if (__instance == null || _bpdDisplayNameField == null || _bpdCharacterField == null || _bpdLastContentField == null)
					{
						return;
					}
					object? value = _bpdCharacterField.GetValue(__instance);
					Character val = (Character)((value is Character) ? value : null);
					if (!((Object)(object)val == (Object)null) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.Owner != null)
					{
						int actorNumber = ((MonoBehaviourPun)val).photonView.Owner.ActorNumber;
						LabelState orCreateValue = States.GetOrCreateValue(__instance);
						if (orCreateValue.Original == null)
						{
							orCreateValue.Original = Strip(_bpdDisplayNameField.GetValue(__instance) as string);
						}
						string text = (string.IsNullOrEmpty(orCreateValue.Original) ? orCreateValue.Original : Rebuild(orCreateValue.Original, actorNumber));
						if (!(orCreateValue.Desired == text))
						{
							orCreateValue.Desired = text;
							_bpdDisplayNameField.SetValue(__instance, text);
							_bpdLastContentField.SetValue(__instance, null);
						}
					}
				}
				catch
				{
				}
			}
		}

		internal static class SodRefreshPatch
		{
			public static void Prefix(object __instance, ref string name)
			{
				try
				{
					if (__instance == null || string.IsNullOrEmpty(name) || !Active || _sodEntriesField == null || _sodEntryLabelField == null || (_sodInstanceProp == null && _sodInstanceField == null))
					{
						return;
					}
					object obj = ((_sodInstanceProp != null) ? _sodInstanceProp.GetValue(null) : _sodInstanceField.GetValue(null));
					if (obj == null || !(_sodEntriesField.GetValue(obj) is IDictionary dictionary))
					{
						return;
					}
					foreach (DictionaryEntry item in dictionary)
					{
						if (_sodEntryLabelField.GetValue(item.Value) == __instance)
						{
							object key = item.Key;
							Character val = (Character)((key is Character) ? key : null);
							if (val != null && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.Owner != null)
							{
								name = Rebuild(name, ((MonoBehaviourPun)val).photonView.Owner.ActorNumber);
							}
							break;
						}
					}
				}
				catch
				{
				}
			}
		}

		private const string BpdDll = "BetterPlayerDistance.dll";

		private const string SodDll = "SenseOfDirection.dll";

		private static Func<int, string> _classNameByActor;

		private static Action<string> _warn;

		private static bool _initialized;

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

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

		private static float _nextTry;

		private static bool _bpdDone;

		private static bool _sodDone;

		private static bool _bpdApplied;

		private static bool _sodApplied;

		private static bool _loggedApplied;

		private static FieldInfo _bpdCharacterField;

		private static FieldInfo _bpdDisplayNameField;

		private static FieldInfo _bpdLastContentField;

		private static FieldInfo _sodEntriesField;

		private static FieldInfo _sodEntryLabelField;

		private static PropertyInfo _sodInstanceProp;

		private static FieldInfo _sodInstanceField;

		private const string ColorFallback = "FFFFFF";

		private const float ColorRetrySeconds = 0.5f;

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

		internal static bool Active
		{
			get
			{
				if (_initialized)
				{
					return _classNameByActor != null;
				}
				return false;
			}
		}

		public static void Initialize(Func<int, string> classNameByActor, Action<string> warn)
		{
			_classNameByActor = classNameByActor;
			_warn = warn;
			_initialized = true;
		}

		public static void Tick(Harmony harmony)
		{
			if (_initialized && harmony != null && (!_bpdDone || !_sodDone) && !(Time.time < _nextTry))
			{
				_nextTry = Time.time + 1f;
				if (!_bpdDone)
				{
					_bpdDone = !Installed("BetterPlayerDistance.dll") || ApplyBpd(harmony);
				}
				if (!_sodDone)
				{
					_sodDone = !Installed("SenseOfDirection.dll") || ApplySod(harmony);
				}
				if (_bpdApplied && _sodApplied && !_loggedApplied)
				{
					_loggedApplied = true;
					Warn("[兼容] 已挂载第三方头顶id显示模组兼容补丁(BetterPlayerDistance / Sense of Direction)。");
				}
			}
		}

		private static bool Installed(string dllFileName)
		{
			try
			{
				string pluginPath = Paths.PluginPath;
				if (!string.IsNullOrEmpty(pluginPath) && Directory.Exists(pluginPath))
				{
					return Directory.GetFiles(pluginPath, dllFileName, SearchOption.AllDirectories).Length != 0;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void Warn(string msg)
		{
			try
			{
				_warn?.Invoke(msg);
			}
			catch
			{
			}
		}

		private static Type FindType(string assemblyName, string simpleName)
		{
			try
			{
				Assembly assembly = FindAssembly(assemblyName);
				if (assembly != null)
				{
					Type[] types = assembly.GetTypes();
					foreach (Type type in types)
					{
						if (type != null && type.Name == simpleName)
						{
							return type;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static Assembly FindAssembly(string simpleName)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if (assembly != null && assembly.GetName().Name == simpleName)
					{
						return assembly;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool ApplyBpd(Harmony harmony)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			try
			{
				Type type = FindType("BetterPlayerDistance", "Tracking");
				Type type2 = FindType("BetterPlayerDistance", "PlayerLabel");
				if (type == null || type2 == null)
				{
					return false;
				}
				MethodInfo method = type.GetMethod("ExtractDisplayName", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo method2 = type2.GetMethod("UpdateState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_bpdCharacterField = type2.GetField("_character", BindingFlags.Instance | BindingFlags.NonPublic);
				_bpdDisplayNameField = type2.GetField("_displayName", BindingFlags.Instance | BindingFlags.NonPublic);
				_bpdLastContentField = type2.GetField("_lastContent", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null || method2 == null || _bpdCharacterField == null || _bpdDisplayNameField == null || _bpdLastContentField == null)
				{
					return false;
				}
				harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(BpdExtractNamePatch), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)method2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(BpdLabelPatch), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_bpdApplied = true;
				return true;
			}
			catch (Exception ex)
			{
				Warn("[兼容] BetterPlayerDistance 补丁失败:" + ex.Message);
				return true;
			}
		}

		private static bool ApplySod(Harmony harmony)
		{
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			try
			{
				Type type = FindType("SenseOfDirection", "PlayerLabelController");
				Type type2 = FindType("SenseOfDirection", "PlayerLabel");
				if (type == null || type2 == null)
				{
					return false;
				}
				_sodEntriesField = type.GetField("_entries", BindingFlags.Instance | BindingFlags.NonPublic);
				_sodInstanceProp = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_sodInstanceField = ((_sodInstanceProp == null) ? type.GetField("_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null);
				_sodEntryLabelField = type.GetNestedType("Entry", BindingFlags.NonPublic)?.GetField("Label", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo methodInfo = null;
				MethodInfo[] methods = type2.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (!(methodInfo2.Name != "Refresh"))
					{
						ParameterInfo[] parameters = methodInfo2.GetParameters();
						if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(string))
						{
							methodInfo = methodInfo2;
							break;
						}
					}
				}
				if (methodInfo == null || _sodEntriesField == null || (_sodInstanceProp == null && _sodInstanceField == null) || _sodEntryLabelField == null)
				{
					return false;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(SodRefreshPatch), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_sodApplied = true;
				return true;
			}
			catch (Exception ex)
			{
				Warn("[兼容] Sense of Direction 补丁失败:" + ex.Message);
				return true;
			}
		}

		internal static string ClassNameOf(int actor)
		{
			if (_classNameByActor == null)
			{
				return null;
			}
			try
			{
				return _classNameByActor(actor);
			}
			catch
			{
				return null;
			}
		}

		internal static Character CharacterOf(int actor)
		{
			if (_characterByActor.TryGetValue(actor, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			List<Character> allCharacters = Character.AllCharacters;
			if (allCharacters != null)
			{
				for (int i = 0; i < allCharacters.Count; i++)
				{
					Character val = allCharacters[i];
					if (!((Object)(object)val == (Object)null) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.Owner != null && ((MonoBehaviourPun)val).photonView.Owner.ActorNumber == actor)
					{
						_characterByActor[actor] = val;
						return val;
					}
				}
			}
			return null;
		}

		internal static string ColorHexOf(int actor)
		{
			Character val = CharacterOf(actor);
			int num = (((Object)(object)val != (Object)null && (Object)(object)((MonoBehaviourPun)val).photonView != (Object)null) ? ((MonoBehaviourPun)val).photonView.ViewID : 0);
			if (num != 0 && _colorByViewId.TryGetValue(num, out var value))
			{
				return value;
			}
			if (_colorRetryAt.TryGetValue(actor, out var value2) && Time.time < value2)
			{
				return "FFFFFF";
			}
			if (TryReadColorOf(val, out var hex))
			{
				if (num != 0)
				{
					_colorByViewId[num] = hex;
				}
				return hex;
			}
			_colorRetryAt[actor] = Time.time + 0.5f;
			return "FFFFFF";
		}

		private static bool TryReadColorOf(Character ch, out string hex)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			hex = "FFFFFF";
			try
			{
				if ((Object)(object)ch == (Object)null || ch.refs == null || (Object)(object)ch.refs.customization == (Object)null)
				{
					return false;
				}
				hex = ColorUtility.ToHtmlStringRGB(ch.refs.customization.PlayerColor);
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static string BuildPrefix(int actor)
		{
			if (!Active)
			{
				return null;
			}
			string text = ClassNameOf(actor);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			return "<color=#" + ColorHexOf(actor) + ">【" + text + "】</color>";
		}

		internal static string Strip(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (!text.StartsWith("<color=#", StringComparison.Ordinal))
			{
				return text;
			}
			int num = text.IndexOf("</color>", StringComparison.Ordinal);
			if (num < 0)
			{
				return text;
			}
			int num2 = text.IndexOf('】');
			if (num2 < 0 || num2 > num)
			{
				return text;
			}
			return text.Substring(num + "</color>".Length);
		}

		internal static string Rebuild(string text, int actor)
		{
			string text2 = Strip(text);
			string text3 = BuildPrefix(actor);
			if (text3 != null)
			{
				return text3 + text2;
			}
			return text2;
		}

		internal static bool TryActorFromObjName(string objName, out int actor)
		{
			actor = 0;
			if (string.IsNullOrEmpty(objName))
			{
				return false;
			}
			int num = objName.IndexOf('[');
			int num2 = objName.LastIndexOf(':');
			if (num == -1 || num2 == -1 || num2 <= num)
			{
				return false;
			}
			return int.TryParse(objName.Substring(num2 + 1).Trim().TrimEnd(']')
				.Trim(), out actor);
		}
	}
}
namespace RollOfTheScoutClient
{
	public sealed class ClassDisplayReceiver : MonoBehaviour, IOnEventCallback
	{
		private const string ModId = "com.peakmodding.RollOfTheScout";

		private const byte EventCode = 197;

		private const byte KindClassSnapshot = 1;

		private const byte KindToast = 2;

		private const byte KindClassNames = 3;

		private const byte KindClassKeywords = 4;

		private const byte KindClassIntro = 5;

		private const byte KindTargetedToast = 6;

		private const byte KindModeSnapshot = 11;

		private const byte KindModeOff = 12;

		private readonly List<KeyValuePair<int, int>> _remoteClasses = new List<KeyValuePair<int, int>>();

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

		private bool _hasRemoteSnapshot;

		private readonly List<KeyValuePair<int, int>> _remoteModeStatuses = new List<KeyValuePair<int, int>>();

		private bool _hasRemoteMode;

		private readonly Dictionary<int, string> _classNameByCode = new Dictionary<int, string>();

		private readonly Dictionary<int, string> _remoteKeywords = new Dictionary<int, string>();

		private readonly Dictionary<int, string> _remoteIntro = new Dictionary<int, string>();

		private readonly List<string> _toasts = new List<string>();

		private readonly List<float> _toastExpiries = new List<float>();

		private const float ToastDuration = 8f;

		private const int ToastMaxCount = 5;

		private bool _classListHeld;

		private bool _classListToggled;

		private float _classListPressUntil;

		private const float ClassListPressDuration = 5f;

		private GUIStyle _toastStyle;

		private readonly List<float> _toastHeights = new List<float>();

		private readonly GUIContent _toastMeasure = new GUIContent();

		private GUIStyle _classListStyle;

		private GUIStyle _classListTitleStyle;

		private GUIStyle _classIntroStyle;

		private GUIStyle _classIntroTitleStyle;

		private float _imguiIntroBottom;

		private int _classIntroCacheCode = -1;

		private string _classIntroCacheTitle;

		private List<string> _classIntroCacheLines;

		private int _introVersion = 1;

		private int _introCacheVersion;

		private int _panelSignature = int.MinValue;

		private List<string> _panelLinesCache;

		public static ClassDisplayReceiver Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private static bool IsHostNow()
		{
			if (PhotonNetwork.InRoom)
			{
				return PhotonNetwork.IsMasterClient;
			}
			return false;
		}

		private void Update()
		{
			ThirdPartyOverheadNameCompat.Tick(Plugin.HarmonyInstance);
			if (IsHostNow())
			{
				ClearReceivedData();
				_toasts.Clear();
				_toastExpiries.Clear();
				PushNativeOverlay();
				return;
			}
			ExpireToasts();
			UpdateClassListInput();
			if (!IsInGameplayScene() && (_remoteClasses.Count > 0 || _toasts.Count > 0))
			{
				ClearReceivedData();
				_toasts.Clear();
				_toastExpiries.Clear();
			}
			PushNativeOverlay();
		}

		private void ClearReceivedData()
		{
			_remoteClasses.Clear();
			_remoteClassMap.Clear();
			_remoteModeStatuses.Clear();
			_remoteKeywords.Clear();
			_remoteIntro.Clear();
			_hasRemoteSnapshot = false;
			_hasRemoteMode = false;
			_panelSignature = int.MinValue;
			_panelLinesCache = null;
			InvalidateIntroCache();
		}

		private void PushNativeOverlay()
		{
			if (NativeOverlay.Ensure())
			{
				NativeOverlay.SetToast(_toasts);
				bool flag = ClassListVisible() && (_hasRemoteMode || (_hasRemoteSnapshot && _remoteClasses.Count > 0));
				List<string> list = (flag ? BuildPanelLines() : null);
				if (list != null && list.Count == 0)
				{
					list = null;
					flag = false;
				}
				string text = "[职业分配 / Class List] " + ClientConfig.ClassListKey.Value;
				NativeOverlay.SetPanel(text, list, flag, _panelSignature * 31 + text.GetHashCode());
				if (flag && TryBuildClassIntro(LocalClassCode(), out var title, out var lines))
				{
					NativeOverlay.SetClassIntro(title, lines, visible: true, _introVersion);
				}
				else
				{
					NativeOverlay.SetClassIntroHidden();
				}
			}
		}

		private int LocalClassCode()
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null)
			{
				return 0;
			}
			return ClassCodeOf(localCharacter);
		}

		private bool TryBuildClassIntro(int code, out string title, out List<string> lines)
		{
			title = null;
			lines = null;
			if (code <= 0)
			{
				return false;
			}
			if (code == _classIntroCacheCode && _introCacheVersion == _introVersion && _classIntroCacheLines != null)
			{
				title = _classIntroCacheTitle;
				lines = _classIntroCacheLines;
				return true;
			}
			string text = ClassIntroText.KeywordsLine(code);
			string text2 = ClassIntroText.IntroLine(code);
			if (string.IsNullOrEmpty(text) && _remoteKeywords.TryGetValue(code, out var value))
			{
				text = value;
			}
			if (string.IsNullOrEmpty(text2) && _remoteIntro.TryGetValue(code, out var value2))
			{
				text2 = value2;
			}
			if (string.IsNullOrEmpty(text) && string.IsNullOrEmpty(text2))
			{
				return false;
			}
			List<string> list = new List<string>(2);
			if (!string.IsNullOrEmpty(text))
			{
				list.Add(text);
			}
			if (!string.IsNullOrEmpty(text2))
			{
				list.Add(text2);
			}
			_classIntroCacheCode = code;
			_introCacheVersion = _introVersion;
			_classIntroCacheTitle = ClassIntroText.TitlePrefix + NameOf(code);
			_classIntroCacheLines = list;
			title = _classIntroCacheTitle;
			lines = list;
			return true;
		}

		private void InvalidateIntroCache()
		{
			_introVersion++;
			_classIntroCacheCode = -1;
			_classIntroCacheLines = null;
		}

		private List<string> BuildPanelLines()
		{
			int num = ComputePanelSignature();
			if (num == _panelSignature && _panelLinesCache != null)
			{
				return _panelLinesCache;
			}
			List<KeyValuePair<int, int>> list = (_hasRemoteMode ? _remoteModeStatuses : _remoteClasses);
			List<string> list2 = new List<string>(list.Count);
			Room currentRoom = PhotonNetwork.CurrentRoom;
			for (int i = 0; i < list.Count; i++)
			{
				KeyValuePair<int, int> keyValuePair = list[i];
				Player val = ((currentRoom != null) ? currentRoom.GetPlayer(keyValuePair.Key, false) : null);
				string text = ((val != null && !string.IsNullOrEmpty(val.NickName)) ? val.NickName : $"#{keyValuePair.Key}");
				list2.Add(text + ":" + (_hasRemoteMode ? ModeStatusName(keyValuePair.Value) : NameOf(keyValuePair.Value)));
			}
			_panelSignature = num;
			_panelLinesCache = list2;
			return list2;
		}

		private int ComputePanelSignature()
		{
			int num = 17;
			num = num * 31 + (_hasRemoteMode ? 1 : 0);
			Room currentRoom = PhotonNetwork.CurrentRoom;
			List<KeyValuePair<int, int>> list = (_hasRemoteMode ? _remoteModeStatuses : _remoteClasses);
			for (int i = 0; i < list.Count; i++)
			{
				KeyValuePair<int, int> keyValuePair = list[i];
				num = num * 31 + keyValuePair.Key;
				num = num * 31 + keyValuePair.Value;
				Player val = ((currentRoom != null) ? currentRoom.GetPlayer(keyValuePair.Key, false) : null);
				if (val != null && !string.IsNullOrEmpty(val.NickName))
				{
					num = num * 31 + val.NickName.GetHashCode();
				}
			}
			return num;
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent == null || photonEvent.Code != 197 || IsHostNow())
			{
				return;
			}
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient == null || photonEvent.Sender != masterClient.ActorNumber)
			{
				return;
			}
			try
			{
				if (!(photonEvent.CustomData is object[] array) || array.Length < 3 || !(array[0] is string text) || text != "com.peakmodding.RollOfTheScout")
				{
					return;
				}
				byte b = Convert.ToByte(array[1]);
				string text2 = array[2] as string;
				if (!string.IsNullOrEmpty(text2))
				{
					switch (b)
					{
					case 1:
						SetSnapshot(text2);
						break;
					case 11:
						SetModeSnapshot(text2);
						break;
					case 12:
						ClearModeSnapshot();
						break;
					case 3:
						SetClassNames(text2);
						break;
					case 4:
					case 5:
						SetClassText(b, text2);
						break;
					case 6:
						AddTargetedToast(text2);
						break;
					case 2:
						AddToast(text2);
						break;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[RollOfTheScoutClient] 处理职业显示事件时异常:" + ex.Message));
			}
		}

		private void SetSnapshot(string data)
		{
			_remoteClasses.Clear();
			_remoteClassMap.Clear();
			string[] array = data.Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrEmpty(text))
				{
					int num = text.IndexOf('=');
					if (num > 0 && int.TryParse(text.Substring(0, num), out var result) && int.TryParse(text.Substring(num + 1), out var result2))
					{
						_remoteClasses.Add(new KeyValuePair<int, int>(result, result2));
						_remoteClassMap[result] = result2;
					}
				}
			}
			_hasRemoteSnapshot = _remoteClasses.Count > 0;
		}

		private void SetModeSnapshot(string data)
		{
			_remoteModeStatuses.Clear();
			string[] array = data.Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrEmpty(text))
				{
					int num = text.IndexOf('=');
					if (num > 0 && int.TryParse(text.Substring(0, num), out var result) && int.TryParse(text.Substring(num + 1), out var result2))
					{
						_remoteModeStatuses.Add(new KeyValuePair<int, int>(result, result2));
					}
				}
			}
			_hasRemoteMode = _remoteModeStatuses.Count > 0;
			_panelSignature = int.MinValue;
			_panelLinesCache = null;
		}

		private void ClearModeSnapshot()
		{
			_remoteModeStatuses.Clear();
			_hasRemoteMode = false;
			_panelSignature = int.MinValue;
			_panelLinesCache = null;
		}

		private static string ModeStatusName(int status)
		{
			return status switch
			{
				0 => "Alive · 存活", 
				1 => "Dead · 死亡", 
				_ => "Error · 异常", 
			};
		}

		private void SetClassNames(string data)
		{
			string[] array = data.Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrEmpty(text))
				{
					int num = text.IndexOf('=');
					if (num > 0 && num + 1 < text.Length && int.TryParse(text.Substring(0, num), out var result))
					{
						_classNameByCode[result] = text.Substring(num + 1);
					}
				}
			}
			InvalidateIntroCache();
		}

		private void SetClassText(byte kind, string data)
		{
			Dictionary<int, string> dictionary = ((kind == 4) ? _remoteKeywords : _remoteIntro);
			string[] array = data.Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrEmpty(text))
				{
					int num = text.IndexOf('=');
					if (num > 0 && num + 1 < text.Length && int.TryParse(text.Substring(0, num), out var result))
					{
						dictionary[result] = text.Substring(num + 1);
					}
				}
			}
			InvalidateIntroCache();
		}

		public string NameOf(int code)
		{
			if (_classNameByCode.TryGetValue(code, out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			return ClassNames.Describe(code);
		}

		public string ClassNameOrEmptyOfActor(int actor)
		{
			int num = ClassCodeOfActor(actor);
			if (num != 0)
			{
				return NameOf(num);
			}
			return string.Empty;
		}

		public int ClassCodeOf(Character character)
		{
			Player val = (((Object)(object)character != (Object)null && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null) ? ((MonoBehaviourPun)character).photonView.Owner : null);
			if (val != null && _remoteClassMap.TryGetValue(val.ActorNumber, out var value))
			{
				return value;
			}
			return 0;
		}

		public int ClassCodeOfActor(int actor)
		{
			if (!_remoteClassMap.TryGetValue(actor, out var value))
			{
				return 0;
			}
			return value;
		}

		public void OnOverheadNameTick(UIPlayerNames ui, int index)
		{
			if (!ClientConfig.NamePrefix.Value || (Object)(object)ui == (Object)null)
			{
				return;
			}
			PlayerName[] playerNameText = ui.playerNameText;
			if (playerNameText == null || index < 0 || index >= playerNameText.Length)
			{
				return;
			}
			PlayerName val = playerNameText[index];
			if (!((Object)(object)val == (Object)null))
			{
				CharacterInteractible characterInteractable = val.characterInteractable;
				Character val2 = (((Object)(object)characterInteractable != (Object)null) ? characterInteractable.character : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					OverheadNamePrefix.Apply(val, val2, OverheadClassNameOf);
				}
			}
		}

		private string OverheadClassNameOf(Character character)
		{
			int num = ClassCodeOf(character);
			if (num != 0)
			{
				return NameOf(num);
			}
			return string.Empty;
		}

		private void AddTargetedToast(string data)
		{
			int num = data.IndexOf('\t');
			if (num > 0 && int.TryParse(data.Substring(0, num), out var result) && PhotonNetwork.LocalPlayer != null && PhotonNetwork.LocalPlayer.ActorNumber == result)
			{
				AddToast(data.Substring(num + 1));
			}
		}

		private void AddToast(string text)
		{
			if (!string.IsNullOrEmpty(text))
			{
				if (_toasts.Count >= 5)
				{
					_toasts.RemoveAt(0);
					_toastExpiries.RemoveAt(0);
				}
				_toasts.Add(text);
				_toastExpiries.Add(Time.time + 8f);
			}
		}

		private void ExpireToasts()
		{
			if (_toasts.Count == 0)
			{
				return;
			}
			float time = Time.time;
			for (int num = _toasts.Count - 1; num >= 0; num--)
			{
				if (time >= _toastExpiries[num])
				{
					_toasts.RemoveAt(num);
					_toastExpiries.RemoveAt(num);
				}
			}
		}

		private static bool IsInGameplayScene()
		{
			try
			{
				return GameHandler.IsInGameplayScene;
			}
			catch
			{
				return false;
			}
		}

		private void UpdateClassListInput()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyCode val = ClientConfig.ParseKey();
			int num = ClientConfig.ClassListModeKind();
			bool keyDown = Input.GetKeyDown(val);
			bool key = Input.GetKey(val);
			switch (num)
			{
			case 1:
				_classListHeld = key;
				break;
			case 3:
				if (keyDown)
				{
					_classListToggled = !_classListToggled;
				}
				break;
			default:
				if (keyDown)
				{
					_classListPressUntil = Time.time + 5f;
				}
				break;
			}
		}

		private bool ClassListVisible()
		{
			return ClientConfig.ClassListModeKind() switch
			{
				1 => _classListHeld, 
				3 => _classListToggled, 
				_ => Time.time < _classListPressUntil, 
			};
		}

		private void OnGUI()
		{
			if (!NativeOverlay.Ready)
			{
				_imguiIntroBottom = 0f;
				DrawClassList();
				DrawToasts();
			}
		}

		private void DrawClassList()
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			if (!IsHostNow() && ClassListVisible() && _hasRemoteSnapshot)
			{
				List<string> list = new List<string>(_remoteClasses.Count + 1);
				list.Add("[职业分配 / Class List] " + ClientConfig.ClassListKey.Value);
				list.AddRange(BuildPanelLines());
				if (_classListStyle == null)
				{
					int fontSize = Mathf.Max(16, Mathf.RoundToInt((float)Screen.height / 48f));
					_classListTitleStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = fontSize,
						fontStyle = (FontStyle)0,
						alignment = (TextAnchor)3
					};
					_classListTitleStyle.normal.textColor = new Color(1f, 0.9f, 0.4f);
					_classListStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = fontSize,
						alignment = (TextAnchor)3
					};
					_classListStyle.normal.textColor = Color.white;
				}
				float num = (float)Screen.width * 0.32f;
				float num2 = (float)_classListStyle.fontSize * 1.6f;
				float num3 = num2 * (float)list.Count + 12f;
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)Screen.width - num - 12f, 12f, num, num3);
				GUI.color = new Color(0f, 0f, 0f, 0.6f);
				GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = Color.white;
				for (int i = 0; i < list.Count; i++)
				{
					GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 6f + num2 * (float)i, ((Rect)(ref val)).width - 20f, num2), list[i], (i == 0) ? _classListTitleStyle : _classListStyle);
				}
				DrawClassIntro();
			}
		}

		private void DrawClassIntro()
		{
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if (TryBuildClassIntro(LocalClassCode(), out var title, out var lines))
			{
				if (_classIntroStyle == null)
				{
					int fontSize = Mathf.Max(16, Mathf.RoundToInt((float)Screen.height / 48f));
					_classIntroTitleStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = fontSize,
						fontStyle = (FontStyle)0,
						alignment = (TextAnchor)3
					};
					_classIntroTitleStyle.normal.textColor = new Color(1f, 0.9f, 0.4f);
					_classIntroStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = fontSize,
						alignment = (TextAnchor)3,
						wordWrap = true
					};
					_classIntroStyle.normal.textColor = Color.white;
				}
				float num = (float)Screen.width * 0.4f;
				float num2 = (float)_classIntroStyle.fontSize * 1.6f;
				GUIContent val = new GUIContent(string.Join("\n", lines));
				float num3 = _classIntroStyle.CalcHeight(val, num - 20f);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(12f, 12f, num, num2 + num3 + 16f);
				GUI.color = new Color(0f, 0f, 0f, 0.6f);
				GUI.DrawTexture(val2, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = Color.white;
				GUI.Label(new Rect(((Rect)(ref val2)).x + 10f, ((Rect)(ref val2)).y + 6f, ((Rect)(ref val2)).width - 20f, num2), title, _classIntroTitleStyle);
				GUI.Label(new Rect(((Rect)(ref val2)).x + 10f, ((Rect)(ref val2)).y + 6f + num2, ((Rect)(ref val2)).width - 20f, num3), val, _classIntroStyle);
				_imguiIntroBottom = ((Rect)(ref val2)).yMax;
			}
		}

		private void DrawToasts()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			if (_toasts.Count != 0)
			{
				if (_toastStyle == null)
				{
					_toastStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = Mathf.Max(16, Mathf.RoundToInt((float)Screen.height / 48f)),
						fontStyle = (FontStyle)0,
						alignment = (TextAnchor)3,
						wordWrap = true
					};
					_toastStyle.normal.textColor = Color.white;
				}
				float num = (float)Screen.width * 0.45f;
				float num2 = num - 20f;
				float num3 = (float)_toastStyle.fontSize * 1.6f;
				_toastHeights.Clear();
				float num4 = 0f;
				for (int i = 0; i < _toasts.Count; i++)
				{
					_toastMeasure.text = _toasts[i];
					float num5 = Mathf.Max(num3, _toastStyle.CalcHeight(_toastMeasure, num2) + 2f);
					_toastHeights.Add(num5);
					num4 += num5;
				}
				float num6 = ((_imguiIntroBottom > 0f) ? (_imguiIntroBottom + 8f) : 12f);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(12f, num6, num, num4 + 12f);
				GUI.color = new Color(0f, 0f, 0f, 0.6f);
				GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = Color.white;
				float num7 = ((Rect)(ref val)).y + 6f;
				for (int j = 0; j < _toasts.Count; j++)
				{
					float num8 = _toastHeights[j];
					GUI.Label(new Rect(((Rect)(ref val)).x + 10f, num7, num2, num8), _toasts[j], _toastStyle);
					num7 += num8;
				}
			}
		}
	}
	internal static class ClassIntroText
	{
		public static string TitlePrefix => "[职业介绍 / Class Intro] ";

		public static string Keywords(int code)
		{
			string text = KeywordsEn(code);
			string text2 = KeywordsZh(code);
			if (string.IsNullOrEmpty(text) && string.IsNullOrEmpty(text2))
			{
				return string.Empty;
			}
			if (string.IsNullOrEmpty(text2))
			{
				return "Keywords: " + text;
			}
			if (string.IsNullOrEmpty(text))
			{
				return "关键词:" + text2;
			}
			return "Keywords: " + text + "\n关键词:" + text2;
		}

		public static string Intro(int code)
		{
			string text = IntroEn(code);
			string text2 = IntroZh(code);
			if (string.IsNullOrEmpty(text))
			{
				return text2;
			}
			if (string.IsNullOrEmpty(text2))
			{
				return text;
			}
			return text + "\n" + text2;
		}

		public static string KeywordsLine(int code)
		{
			return Keywords(code);
		}

		public static string IntroLine(int code)
		{
			return Intro(code);
		}

		private static string KeywordsZh(int code)
		{
			return code switch
			{
				1 => "每关随机道具、一次性昏迷复活、高饥饿消耗、无限周期加速", 
				2 => "范围增益、开局负债、绑定全员生死、亡语激励、额外神秘物品", 
				3 => "周期随机补给、开局背包、初始高石化", 
				4 => "每关医疗物资、范围减伤解毒、队友死亡自伤惩罚", 
				5 => "初始武器、匕首吹箭、拾取转化", 
				6 => "范围潘多拉、拾取转化、概率消失、持续自增诅咒", 
				7 => "骸骨之书、死亡增益、骷髅强化、初始高诅咒、诅咒与增益运营", 
				8 => "负面状态转化、无负面周期惩罚、周期负面耐受", 
				9 => "毒孢抗性、自然食物、周期粉蕉、禁用工具", 
				10 => "开局随机物资腰包、独行流血、集体行动、同伴增益", 
				11 => "检查点旗标、插旗范围清负、自身插旗石化、全员随机增益、团队爆发", 
				12 => "跳舞回体、强制共舞、周期后空翻、摔倒风险", 
				13 => "周期产蛋、传染转职、同类大范围回血、周期后空翻", 
				14 => "禁用物品、拾取消失、周期重置、受伤放大、职业免疫", 
				15 => "周期卷轴、双形态、随机效果、负面代价", 
				16 => "初始高负面、自带黄金抓钩、抓人高惩罚、潘多拉免疫", 
				17 => "便携烤炉、烤物解饿、烤多中毒、非食物奖励、持炉炎热", 
				18 => "便携烤炉、生食烹饪、群体增益、熟食增益、生食惩罚、饥饿昏迷", 
				19 => "踢击替代拉人、踢中回体、踢空困倦、初始诅咒", 
				20 => "开局背包、物品囤积、负重奖励、周期加速、额外体力", 
				21 => "昏迷救援、传送无敌、救起加速、背人治疗、死亡惩罚", 
				22 => "防晒喷雾、每关刷新、强制覆盖、防晒回体、用完额外效果", 
				23 => "独行奖励、结伴惩罚、半径检测", 
				24 => "童子军大炮、每关与周期获得、受伤炸药、主手风险", 
				25 => "喇叭乐器、演奏范围增益、拾取转化、库存奖励、初始诅咒、特殊转职", 
				26 => "童军宝石、诅咒锁定、拾取消失、特殊增益、零号守则、天底解放", 
				27 => "早起虫儿、攀爬高频脱手、分解垃圾奖励、感染转职、同类增益、免疫孢子、食虫惩罚", 
				28 => "周期绑气球、拾取转化、只吃包装熟食、冷热抵抗、额外负面", 
				29 => "周期荔莓、食用奖励、非莓惩罚、进食损伤、特殊转职", 
				30 => "周期获得昆虫、额外体力溢出取模、夜间加速、独行清负、职业效果复制", 
				31 => "传送、曲迁菇、负面状态、周期检测", 
				32 => "石化、石头、动态增益、拾取转化、随机效果、范围守护", 
				33 => "工具转化、随机负面、食物炼化、周期强力炼制、背人炼成", 
				34 => "周期范围发糖、满物品栏捣蛋、拾取转化、吃糖惩罚", 
				_ => string.Empty, 
			};
		}

		private static string IntroZh(int code)
		{
			return code switch
			{
				1 => "【开路先锋】每关获得随机道具背包;首次昏迷将完全清除负面复活;*高代谢*迫使你不断管理饥饿与冲刺。", 
				2 => "【团队核心】周期强力范围增益,使玩家获得额外体力、治疗损伤、补充饥饿、减少困倦;自身诅咒石化随全员生死波动;自身死亡触发全员激励;全员存活时,自己开启雕像可获额外物品。", 
				3 => "【移动补给】开局自带随机背包;定时获得随机物资;初始诅咒与较高石化需谨慎管理;与【炊事员】有特殊羁绊。", 
				4 => "【随队医护】每关获得随机医疗物资背包;近身小范围持续减伤解毒;队友阵亡会触发自伤惩罚。", 
				5 => "【生死问题】开局自带仪式匕首与吹箭筒;拾取物品概率变为武器;杀人还是救人,这是一个问题。", 
				6 => "【混沌之子】持续向周围散播潘多拉效果;拾取物品半变魔盒半消失;自身诅咒持续叠加。", 
				7 => "【骷髅之王】致敬本模组最不小团体的职业;通过玩家死亡、与骷髅配合降低自身诅咒;持续为周围的人类玩家施加中毒、骷髅玩家获得额外体力与无敌。", 
				8 => "【负面转化】受到绝大部分负面状态时,等量转化额外体力;身上无负面状态则周期损失额外体力;对特定负面状态有耐受。", 
				9 => "【自然力量】自身对中毒与孢子有较强抗性;只能食用生的自然食物,否则获得损伤;定时获得粉莓蕉;自身无法主动使用攀爬道具,拾取会直接变为木棍。", 
				10 => "【结伴而行】开局自带随机物资腰包;落单独行持续流血与饥饿;需和玩家结伴同行缓解,与同职业【童军】或【领袖】有加强羁绊。", 
				11 => "【团队旗手】每关获得检查点旗标;自身插旗立即范围清除负面,三秒后全员获得随机增益效果;自身每次插旗增加石化。", 
				12 => "【一起跳舞】自身范围内玩家跳舞获得额外体力;自身跳舞强制周围玩家共舞;定时强制后空翻且必定摔倒。", 
				13 => "【模因传播】语音与产蛋周期获得额外体力;传染式转职,可主动通过喂食煎蛋强制同化他人;较大范围内,同类周期回血减伤,但定时强制后空翻且必定摔倒。", 
				14 => "【请输入文本】禁用食物及绝大部分道具,拾取物品直接消失;每关饥饿值清零重置;周期回满额外体力;受伤放大即倒;免疫【潘多拉之子】【死灵法师】【舞王】【区】职业效果。", 
				15 => "【随机施法】定时获得卷轴,打开随机触发效果并增加自身石化/诅咒;人类形态效果偏辅助,骷髅形态效果高收益高代价。", 
				16 => "【立体机动】初始高诅咒高石化需谨慎管理;开局自带黄金抓钩与滑翔翼;使用抓钩勾中玩家会立即触发高损伤惩罚;免疫【潘多拉之子】且拾取的潘多拉餐盒直接消失。", 
				17 => "【烤炉大师】定时获得便携烤炉;烤任意物品都能解饿;烤多会中毒;周期烤非食物(道具)可获额外体力;手持便携烤炉时有高频炎热。", 
				18 => "【饮食保障】定时获得便携烤炉;烤熟生食可为周围回饥饿与体力;食用熟食有额外收益,食用生食有较大惩罚;附近有人饥饿会致困昏迷;与【后勤】有特殊羁绊。", 
				19 => "【并非和平】右键拉人变为踢人;每踢中一名玩家+5额外体力,踢空则-5额外体力并+20困倦;开局自带诅咒。", 
				20 => "【负重前行】开局自带背包;负重过半达标后,周期获得加速与额外体力。", 
				21 => "【火线救援】队友昏迷将自动传送并获得一次无敌,背起再获得一次加速与大量额外体力;背起玩家时治疗双方;有玩家死亡会获得石化惩罚。", 
				22 => "【精致防晒】每关获得三瓶防晒喷雾,强制覆盖物品栏;获得防晒奖励额外体力,每次用完防晒喷雾会额外奖励额外体力并积累石化。", 
				23 => "【单人模式】离人够远时周期奖励额外体力;靠近他人持续困倦。", 
				24 => "【重火力者】每关获得童子军大炮;定时获得童子军大炮;受伤就会获得炸药,且物品栏满时会直接塞入主手。", 
				25 => "【演奏乐队】拾取物品概率变乐器,库存与演奏可范围奖励额外体力;库存集齐三种乐器有额外加速奖励,且给予他人有低概率将其转职。", 
				26 => "【领队残魂】初始诅咒锁定,仅能通过获取童军宝石道具降低;使用童军宝石道具有专属特殊增益;禁用童军宝石道具以外物品,拾取物品直接消失;即时传送救援,不可放弃他人;集齐所有童军宝石道具有额外奖励;抵达天底解放灵魂全员奖励。", 
				27 => "【蠕动之区】攀爬时高频强制脱手;靠拾取分解垃圾物品、获取积攒早起虫儿积累收益;感染式转职,周期对附近施加孢子,过半会使他人强制同化,并获得早起虫儿;同类聚集有加速与无限体力效果奖励;自身免疫孢子;食用早期虫儿有诅咒惩罚。", 
				28 => "【太空漂流】身上定时绑上气球;无法拾取气球/一束气球;拾取物品概率变为火箭背包/喷气背包;只吃包装食品或熟食,否则中毒;对冷热有一定抗性,但部分负面状态会额外加重。", 
				29 => "【大果成瘾】定时获得青/紫荔莓;食用青荔莓获得冲刺效果、紫荔莓则给无限体力;食用其他食物加困倦;每次进食固定损伤;他人多次连吃荔莓会被强制同化。", 
				30 => "【代码之力】定时获得随机虫类,获得时大概率中毒;额外体力溢出会取模重置;夜晚周期获得加速;独行会周期清负面;无负面且无虫时会复制在场他人职业效果,并持续生效一段时间。", 
				31 => "【混乱传送】周期检测自身状态,若检测的负面状态相比上一次增加,则与随机一名其他玩家互换位置,否则获得曲迁菇;拾取物品有较高概率直接消失并触发潘多拉;饮用运动饮料或万灵药、接触水体都会带来额外惩罚;积攒过多曲迁菇会受到惩罚。", 
				32 => "【活体石像】站立不动会持续积蓄石化,移动则缓慢消退;自身石化越高,增益效果越强;若石化偏低,白天会畏光叠加困倦;周期内石化达到峰值时会强制破壳,触发一次性强力增益,且能影响周围;拾取食物会变石头,而石头可能带来随机增益。", 
				33 => "【等价炼成】拾取工具时获得随机负面并转化为随机物品;拾取可食用物品会转化为负面清除与额外体力,每12次会有特殊奖励;每炼制出12石头将触发一次特殊强力炼制;背起玩家会将其变为骷髅,或将其炼化为尸。", 
				34 => "【万圣敲门】定时给范围内的玩家发糖,成功使自身获得额外体力;若对方物品栏已满则捣蛋:随机抢走一件物品或连施潘多拉;拾取食物会变糖,但吃糖有蛀牙的损伤惩罚。", 
				_ => string.Empty, 
			};
		}

		private static string KeywordsEn(int code)
		{
			return code switch
			{
				1 => "Random items each level · One-time faint revive · High hunger drain · Periodic infinite sprint", 
				2 => "Area buffs · Opening debt · Bound to everyone's life · Death-triggered rally · Extra mystery item", 
				3 => "Periodic random supply · Starting backpack · High starting petrify", 
				4 => "Medical supplies each level · Area injury/poison relief · Self-injury on teammate death", 
				5 => "Starting weapons · Dagger and blowgun · Pickup conversion", 
				6 => "Area Pandora · Pickup conversion · Chance to vanish · Steadily rising curse", 
				7 => "Book of Bones · Death buffs · Skeleton empowerment · High starting curse · Curse/gain management", 
				8 => "Debuff conversion · Penalty without debuffs · Periodic debuff tolerance", 
				9 => "Poison/spore resistance · Natural food only · Periodic pink banana · Tools disabled", 
				10 => "Starting random fanny pack · Solo bleeding · Group action · Companion buffs", 
				11 => "Checkpoint flag · Area debuff purge on plant · Self petrify on plant · Random team buffs · Team burst", 
				12 => "Dance for stamina · Forced group dance · Periodic backflip · Fall risk", 
				13 => "Periodic eggs · Contagious reclass · Same-class area healing · Periodic backflip", 
				14 => "Items disabled · Pickups vanish · Periodic reset · Amplified injury · Class immunity", 
				15 => "Periodic scrolls · Two forms · Random effects · Negative cost", 
				16 => "High starting debuffs · Golden grapnel · Heavy grab penalty · Pandora immunity", 
				17 => "Portable stove · Cooking sates hunger · Cooking builds poison · Non-food rewards · Heat while holding", 
				18 => "Portable stove · Cooking raw food · Group buffs · Cooked-food bonus · Raw-food penalty · Hunger-induced drowsiness", 
				19 => "Kick replaces grab · Hits restore stamina · Misses cause drowsiness · Starting curse", 
				20 => "Starting backpack · Item hoarding · Load reward · Periodic sprint · Extra stamina", 
				21 => "Faint rescue · Teleport invincibility · Post-rescue sprint · Carrying heals both · Death penalty", 
				22 => "Sunscreen · Refreshed each level · Forced overwrite · Sunscreen stamina · Per-bottle bonus", 
				23 => "Solo reward · Company penalty · Radius check", 
				24 => "Scout cannon · Each level and periodic · Injury dynamite · Main-hand risk", 
				25 => "Horns and instruments · Performance area buff · Pickup conversion · Inventory bonus · Starting curse · Special reclass", 
				26 => "Scout gems · Locked curse · Pickups vanish · Special bonuses · Rule Zero · Nadir liberation", 
				27 => "Early bug · Frequent climbing slips · Decomposing junk rewards · Contagious reclass · Same-class buffs · Spore immunity · Bug-eating penalty", 
				28 => "Periodic balloons · Pickup conversion · Packaged/cooked food only · Heat/cold resistance · Extra debuffs", 
				29 => "Periodic kingberries · Eating rewards · Non-berry penalty · Injury per meal · Special reclass", 
				30 => "Periodic insects · Extra stamina overflow modulo · Night sprint · Solo debuff cleanse · Class-effect copy", 
				31 => "Teleport · Warp Fungus · Negative statuses · Periodic checks", 
				32 => "Petrify · Stones · Dynamic buffs · Pickup conversion · Random effects · Area guard", 
				33 => "Tool transmutation · Random debuff · Food conversion · Forced periodic refining · Carrying taboo", 
				34 => "Periodic area candy · Trick on full inventories · Pickup conversion · Sugar penalty", 
				_ => string.Empty, 
			};
		}

		private static string IntroEn(int code)
		{
			return code switch
			{
				1 => "【Pioneer】 a random-item backpack every level; your first faint fully clears debuffs as you revive; *Hyper-metabolism* forces you to keep managing hunger and sprints.", 
				2 => "【Team core】 periodic strong area buffs that grant players extra stamina, heal injury, restore hunger and reduce drowsiness; your curse and petrify swing with everyone's life and death; your death rallies everyone; while all are alive, opening an Ancient Idol yourself yields an extra item.", 
				3 => "【Mobile supply】 starts with a random backpack; receives random supplies on a timer; your starting curse and high petrify need careful management; special bond with the Cook.", 
				4 => "【Field medic】 a random medical backpack every level; sustained close-range injury and poison relief; a teammate's death triggers a self-injury penalty.", 
				5 => "【A matter of life and death】 starts with a ritual dagger and a blowgun; picked-up items may become weapons; to kill or to save, that is the question.", 
				6 => "【Child of chaos】 keeps spreading the Pandora effect around you; half of the items you pick up become boxes and half vanish; your curse keeps stacking.", 
				7 => "【King of skeletons】 a tribute to the mod's least small group; lower your curse through player deaths and skeleton allies; keep poisoning nearby humans while skeletons gain extra stamina and invincibility.", 
				8 => "【Negative conversion】 most debuffs you take convert into an equal amount of extra stamina; with no debuffs you periodically lose extra stamina; tolerant against specific debuffs.", 
				9 => "【Power of nature】 strong resistance to poison and spores; you can only eat raw natural food or take injury; gains a pink banana on a timer; climbing tools are unusable — picked-up ones turn into wooden sticks.", 
				10 => "【Better together】 starts with a random-item fanny pack; alone you bleed and starve; you need company to ease it; strengthened bond with fellow Scouts or Leaders.", 
				11 => "【Team standard-bearer】 a checkpoint flag every level; planting it immediately clears debuffs in range and 3 s later grants everyone random buffs; each plant adds petrify to yourself.", 
				12 => "【Dance together】 players dancing in your range grant extra stamina; your own dance forces nearby players to dance; periodic forced backflips that always end in a fall.", 
				13 => "【Meme spread】 voice and egg cycles grant extra stamina; contagious reclassing — you can force-merge others by feeding fried eggs; same-class players heal and take less injury over a larger range, but periodic backflips always end in a fall.", 
				14 => "【Input text here】 food and most items are disabled and picked-up items vanish; hunger resets to zero every level; extra stamina refills periodically; any injury is amplified into a knockdown; immune to the Pandora, Necromancer, Dancer and Qu class effects.", 
				15 => "【Random casting】 scrolls on a timer; opening one triggers a random effect and adds petrify/curse; the human form leans support, the skeleton form trades a high cost for high gain.", 
				16 => "【Aerial mobility】 high starting curse and petrify need careful management; starts with a golden grapnel and a glider; hooking a player with the grapnel immediately triggers a heavy injury penalty; immune to Pandora effects and picked-up Pandora lunchboxes vanish.", 
				17 => "【Grill master】 portable stoves on a timer; cooking anything sates hunger; cooking too much poisons you; periodically cooking non-food (tools) grants extra stamina; holding a portable stove causes frequent heat.", 
				18 => "【Food security】 portable stoves on a timer; cooking raw food restores hunger and stamina around you; cooked food pays extra, raw food punishes hard; nearby hungry players make you drowsy; special bond with the Logistician.", 
				19 => "【Not so peaceful】 the right-click grab becomes a kick; each hit grants +5 extra stamina, while a miss costs 5 extra stamina and adds 20 drowsiness; starts with curse.", 
				20 => "【Carrying the weight】 starts with a backpack; once your load passes the threshold you periodically gain a sprint and extra stamina.", 
				21 => "【Fireline rescue】 a teammate's faint auto-teleports you with invincibility; picking them up grants a sprint and a large amount of extra stamina; carrying a player heals both; a player's death gives you petrify.", 
				22 => "【Exquisite sunscreen】 three sunscreens every level, force-overwriting your inventory; gaining sunscreen grants extra stamina, and finishing each bottle grants more extra stamina plus petrify.", 
				23 => "【Single-player mode】 periodically rewarded with extra stamina when far from others; constant drowsiness when someone is close.", 
				24 => "【Heavy gunner】 a scout cannon every level; scout cannons on a timer; taking damage grants dynamite, which is forced into your main hand when the inventory is full.", 
				25 => "【The playing band】 picked-up items may become instruments; inventory and performance grant area extra stamina; holding all three instruments grants an extra sprint, and giving them to others has a low chance to reclass them.", 
				26 => "【Scoutmaster's soul】 starting curse is locked and only Scout gem items can lower it; Scout gems grant exclusive bonuses; all non-gem items are disabled and picked-up items vanish; instant teleport rescue, never abandon others; collecting every Scout gem grants an extra reward; freeing the soul at the Nadir rewards everyone.", 
				27 => "【Wriggling Qu】 frequent forced let-go while climbing; profit from decomposing junk items and accumulating early bugs; contagious reclassing — periodically applies spores nearby, and past the threshold forces others to merge and grants early bugs; same-class clusters get sprint and infinite stamina rewards; immune to spores; eating early bugs costs curse.", 
				28 => "【Space drift】 balloons are tied on periodically; balloons and bunches of balloons cannot be picked up; picked-up items may become rocket packs or jetpacks; only packaged or cooked food, otherwise poison; some heat/cold resistance but certain debuffs hit harder.", 
				29 => "【Big-berry addiction】 green/purple kingberries on a timer; green grants a sprint, purple grants infinite stamina; other food adds drowsiness; every meal costs injury; others who chain-eat kingberries are forced to merge.", 
				30 => "【Power of code】 random insects on a timer, usually with poison; extra stamina overflow wraps around; periodic sprint at night; solo play periodically cleanses debuffs; with no debuffs and no insects you copy another present class's effect for a while.", 
				31 => "【Chaos Teleport】periodically checks your own status; if the selected negative status increased, swap positions with a random other player; otherwise gain a Warp Fungus; picked-up items may vanish and trigger Pandora; sports drinks, Cure-All and touching water add extra punishment; hoarding too many Warp Fungi is punished.", 
				32 => "【Living Statue】Standing still builds petrify while moving slowly sheds it; the higher your petrify, the stronger your buffs; when petrify is low, daylight adds drowsiness; when petrify peaks within a cycle you are forced to break the shell, triggering a one-time powerful buff that also affects those around you; food you pick up turns to stone, and stones may bring random buffs.", 
				33 => "【Equivalent alchemy】picked-up tools gain a random debuff and are turned into random items; picked-up edible items are converted into debuff relief and extra stamina, with a special reward every 12 triggers; every 12 stones refined triggers one special powerful transmutation; carrying a player turns them into a skeleton, or refines them into a corpse.", 
				34 => "【Halloween knocker】candy to players in range on a timer, granting you extra stamina when it lands; a full inventory gets tricked — one item is snatched or Pandora strikes repeatedly; edible pickups turn into candy, but eating candy costs injury from cavities.", 
				_ => string.Empty, 
			};
		}
	}
	public static class ClassNames
	{
		public static string Describe(int code)
		{
			return code switch
			{
				1 => "Explorer · 先驱", 
				2 => "Leader · 领袖", 
				3 => "Logistician · 后勤", 
				4 => "Doctor · 医生", 
				5 => "Assassin · 刺客", 
				6 => "Pandora's Child · 潘多拉之子", 
				7 => "Necromancer · 死灵法师", 
				8 => "M · M", 
				9 => "Savage · 野人", 
				10 => "Scout · 童军", 
				11 => "StandardBearer · 旗手", 
				12 => "Dancer · 舞王", 
				13 => "Daihao · 待皓", 
				14 => "Saint · 圣人", 
				15 => "Mage · 法师", 
				16 => "Challenger · 挑战者", 
				17 => "MasterChef · 特级厨师", 
				18 => "Cook · 炊事员", 
				19 => "Pacifist · 和平主义者", 
				20 => "Hamster · 仓鼠", 
				21 => "Firefighter · 救火队员", 
				22 => "Narcissist · 自恋狂", 
				23 => "LoneWolf · 独狼", 
				24 => "Gunner · 炮手", 
				25 => "Band · 乐队成员", 
				26 => "MilesSoul · 迈尔斯残魂", 
				27 => "Qu · 区", 
				28 => "Astronaut · 太空人", 
				29 => "Langwang · 榔王", 
				30 => "Programmer · 程序员", 
				31 => "ChaosEnderman · 混乱末影人", 
				32 => "Gargoyle · 石像鬼", 
				33 => "Alchemist · 炼金术士", 
				34 => "Trickster · 捣蛋鬼", 
				_ => "None · 无职业", 
			};
		}
	}
	public static class ClientConfig
	{
		private static string _cachedKeyRaw;

		private static KeyCode _cachedKey = (KeyCode)9;

		private static string _cachedModeRaw;

		private static int _cachedModeKind;

		public static ConfigEntry<string> ClassListKey { get; private set; }

		public static ConfigEntry<string> ClassListMode { get; private set; }

		public static ConfigEntry<bool> NamePrefix { get; private set; }

		public static void Initialize(ConfigFile config)
		{
			ConfigLocalization.DetectLanguage();
			ClassListKey = BindL(config, "通用", "职业显示-按键", "Tab", "按哪个键在【客机自己的屏幕】上显示「当前职业分配情况」(由主机广播而来)。\n填写 Unity 的 KeyCode 名,例如 Tab、F1、LeftAlt、BackQuote、Mouse2。");
			ClassListMode = BindL(config, "通用", "职业显示-模式", "1", "职业分配面板的显示方式(1 / 2 / 3):\n1 = 按住该键时显示,松开立即隐藏;\n2 = 按一下显示 5 秒后自动隐藏;\n3 = 每按一次在「显示 / 隐藏」之间切换。\n默认 1。旧版文字值仍兼容。");
			NamePrefix = BindL(config, "通用", "头顶职业前缀", defaultValue: false, "是否把「头顶玩家名称」改写成「【职业】昵称」,例如「张三」→「【先驱】张三」。\n「【职业】」用该玩家进入游戏时的皮肤色着色;「昵称」维持原版颜色不变。\n职业数据由主机广播而来;本开关只影响【本机屏幕】上看到的名字。\n⚠ **默认关闭**:该功能每帧、每个玩家都会执行;若同时装有其它的头顶名称模组\n(例如 AlwaysDisplayPlayerName),双方会每帧互相改写文本,导致明显掉帧。\n默认 false(关闭)。");
			config.Save();
		}

		private static ConfigEntry<T> BindL<T>(ConfigFile config, string zhSection, string zhKey, T defaultValue, string zhDescription)
		{
			return config.Bind<T>(ConfigLocalization.Section(zhSection), ConfigLocalization.Key(zhSection, zhKey), defaultValue, ConfigLocalization.Desc(zhSection, zhKey, zhDescription));
		}

		public static KeyCode ParseKey()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			string text = ((ClassListKey != null) ? ClassListKey.Value : null);
			if (string.Equals(text, _cachedKeyRaw))
			{
				return _cachedKey;
			}
			_cachedKeyRaw = text;
			_cachedKey = (KeyCode)((string.IsNullOrEmpty(text) || !Enum.TryParse<KeyCode>(text.Trim(), ignoreCase: true, out KeyCode result)) ? 9 : ((int)result));
			return _cachedKey;
		}

		public static int ClassListModeKind()
		{
			string text = ((ClassListMode != null) ? (ClassListMode.Value ?? string.Empty) : string.Empty);
			if (string.Equals(text, _cachedModeRaw))
			{
				return _cachedModeKind;
			}
			_cachedModeRaw = text;
			string text2 = text.ToLowerInvariant();
			_cachedModeKind = ((text.Trim() == "1" || text.Contains("按住") || text2.Contains("hold")) ? 1 : ((text.Trim() == "3" || text.Contains("切换") || text2.Contains("toggle")) ? 3 : 2));
			return _cachedModeKind;
		}
	}
	internal static class ConfigLocalization
	{
		private static readonly Dictionary<string, string> Sections = new Dictionary<string, string> { { "通用", "General" } };

		private static readonly Dictionary<string, (string Key, string Desc)> Entries = new Dictionary<string, (string, string)>
		{
			{
				"通用|职业显示-按键",
				("classDisplayKey", "Key that shows the current class assignment (broadcast by the host) on the client's own screen. Enter a Unity KeyCode name, e.g. Tab, F1, LeftAlt, BackQuote, Mouse2.")
			},
			{
				"通用|职业显示-模式",
				("classDisplayMode", "Display mode of the class panel (values 1 / 2 / 3): 1 = show while the key is held and hide immediately on release; 2 = show for 5 seconds after a single press, then auto-hide; 3 = each press toggles between show and hide. Default 1. Legacy text values are still accepted.")
			},
			{
				"通用|头顶职业前缀",
				("overheadClassPrefix", "Whether to rewrite the overhead player name as '[Class] Nickname', e.g. Alice -> [Explorer] Alice. '[Class]' is colored with that player's skin color; the nickname keeps its vanilla color. Class data comes from the host's broadcast; this switch only affects names seen on the local screen. Warning: this runs per frame and per player; if another overhead-name mod is installed (e.g. AlwaysDisplayPlayerName), both rewrite the text every frame and cause noticeable frame drops. Default false (off).")
			}
		};

		internal static bool UseEnglish { get; private set; }

		internal static void DetectLanguage()
		{
			//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
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			bool flag;
			try
			{
				Language cURRENT_LANGUAGE = LocalizedText.CURRENT_LANGUAGE;
				flag = (int)cURRENT_LANGUAGE == 9 || (int)cURRENT_LANGUAGE == 10 || ((int)cURRENT_LANGUAGE == 0 && IsSystemChinese());
			}
			catch
			{
				flag = IsSystemChinese();
			}
			UseEnglish = !flag;
		}

		private static bool IsSystemChinese()
		{
			//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
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			SystemLanguage systemLanguage = Application.systemLanguage;
			if ((int)systemLanguage != 40)
			{
				return (int)systemLanguage == 41;
			}
			return true;
		}

		internal static string Section(string zhSection)
		{
			if (Sections.TryGetValue(zhSection, out var value) && !string.IsNullOrEmpty(value) && !string.Equals(value, zhSection, StringComparison.Ordinal))
			{
				return value + zhSection;
			}
			return zhSection;
		}

		internal static string Key(string zhSection, string zhKey)
		{
			if (!Entries.TryGetValue(Index(zhSection, zhKey), out (string, string) value) || string.IsNullOrEmpty(value.Item1))
			{
				return zhKey;
			}
			if (string.Equals(value.Item1, zhKey, StringComparison.OrdinalIgnoreCase))
			{
				return zhKey;
			}
			return BuildBilingualKey(zhKey, value.Item1) + zhKey;
		}

		private static string BuildBilingualKey(string zhKey, string enKey)
		{
			int num = zhKey.Split('-').Length;
			List<string> list = SplitCamelWord