Decompiled source of balrond secondchance v1.0.4

plugins/BalrondSecondChance.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Balrond.Shared;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using JetBrains.Annotations;
using LitJson2;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BalrondSecondChance")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.9.0")]
[assembly: AssemblyInformationalVersion("1.0.9")]
[assembly: AssemblyProduct("BalrondSecondChance")]
[assembly: AssemblyTitle("BalrondSecondChance")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.9.0")]
[module: UnverifiableCode]
namespace BalrondReviveSoul
{
	public class BalrondTranslator
	{
		public static Dictionary<string, Dictionary<string, string>> translations = new Dictionary<string, Dictionary<string, string>>();

		public static Dictionary<string, string> getLanguage(string language)
		{
			if (string.IsNullOrEmpty(language))
			{
				return null;
			}
			if (translations.TryGetValue(language, out var value))
			{
				return value;
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(AudioMan), "Awake")]
	internal static class BRSAudioManAwakePatch
	{
		private static void Postfix(AudioMan __instance)
		{
			AudioRoutingService.OnAudioManAwake(__instance);
		}
	}
	internal static class BRSConfig
	{
		internal static ConfigEntry<bool> LockConfiguration;

		internal static ConfigEntry<float> DownedSeconds;

		internal static ConfigEntry<bool> AutoReleaseWhenTimerEnds;

		internal static ConfigEntry<bool> AllowSelfPotionRevive;

		internal static ConfigEntry<bool> RequireItemForSelfRevive;

		internal static ConfigEntry<bool> RequirePotionForFriendRevive;

		internal static ConfigEntry<string> ResurrectionPotionPrefabName;

		internal static ConfigEntry<int> ResurrectionItemAmount;

		internal static ConfigEntry<float> ResurrectionCooldownMinutes;

		internal static ConfigEntry<float> ReviveHealthPercent;

		internal static ConfigEntry<float> ReviveDistance;

		internal static ConfigEntry<string> DownedPresentationMode;

		internal static void Init(Launch plugin)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			LockConfiguration = plugin.config("0 General", "Lock Configuration", value: true, "If on, server values are enforced.");
			DownedSeconds = plugin.config("0 General", "Downed Seconds", 120f, "How long player stays downed.");
			AutoReleaseWhenTimerEnds = plugin.config("0 General", "Auto Release When Timer Ends", value: true, "Auto death after timer.");
			ResurrectionCooldownMinutes = plugin.config("0 General", "Cooldown Minutes", 10f, "Cooldown after revive.");
			ReviveHealthPercent = plugin.config("0 General", "Revive Health Percent", 0.25f, "Health after revive.");
			ReviveDistance = plugin.config("0 General", "Revive Distance", 3f, "Max revive distance.");
			DownedPresentationMode = plugin.config("0 General", "Downed Presentation Mode", "Sit", new ConfigDescription("Server-synchronized pose/animation used for every player while downed.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Sit", "Bed", "Dance", "Headbang" }), Array.Empty<object>()));
			AllowSelfPotionRevive = plugin.config("1 Self Revive", "Allow Self Revive", value: true, "Allow self revive.");
			RequireItemForSelfRevive = plugin.config("1 Self Revive", "Require Item For Self Revive", value: true, "Require item for self revive.");
			RequirePotionForFriendRevive = plugin.config("2 Friend Revive", "Require Item For Friend Revive", value: true, "Require item for reviving others.");
			ResurrectionPotionPrefabName = plugin.config("3 Items", "Resurrection Item Prefab", "MeadHealthMinor", "Item prefab name.");
			ResurrectionItemAmount = plugin.config("3 Items", "Resurrection Item Amount", 1, "Amount required.");
		}
	}
	[HarmonyPatch(typeof(Player), "Awake")]
	internal static class BRSPlayerAwakePatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent<BRSDownedBehaviour>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<BRSDownedBehaviour>();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class BRSPlayerOnDeathPatch
	{
		private static bool Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (Launch.AllowOriginalDeath)
			{
				return true;
			}
			if ((Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsValid())
			{
				return true;
			}
			if (!((Character)__instance).IsOwner())
			{
				return true;
			}
			if (__instance.IsDowned())
			{
				return false;
			}
			if (BRSReviveRules.IsPotionReviveOnCooldown(__instance, out var _))
			{
				return true;
			}
			float num = Mathf.Max(1f, BRSConfig.DownedSeconds.Value);
			float until = Time.time + num;
			float health = Mathf.Max(1f, ((Character)__instance).GetMaxHealth() * 0.01f);
			__instance.SetDowned(value: true, until);
			__instance.ClearReleaseRequest();
			((Character)__instance).SetHealth(health);
			BRSPlayerPresentation.SetDownedPresentation(__instance, downed: true);
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, "$tag_brs_death_wait_message_bal", 0, (Sprite)null, false, true);
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Game), "UpdateRespawn")]
	internal static class BRSGameUpdateRespawnPatch
	{
		private static bool Prefix()
		{
			Player localPlayer = Player.m_localPlayer;
			return (Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned();
		}
	}
	[HarmonyPatch(typeof(Player), "CreateTombStone")]
	internal static class BRSCreateTombStonePatch
	{
		private static bool Prefix(Player __instance)
		{
			return (Object)(object)__instance == (Object)null || !__instance.IsDowned();
		}
	}
	internal static class BRSAIIgnoreDownedPlayers
	{
		internal static bool IsDownedPlayer(Character character)
		{
			Player val = (Player)(object)((character is Player) ? character : null);
			return (Object)(object)val != (Object)null && val.IsDowned();
		}

		internal static void ClearTargetIfDowned(MonsterAI ai)
		{
			if (!((Object)(object)ai == (Object)null) && IsDownedPlayer(((BaseAI)ai).GetTargetCreature()))
			{
				ai.SetTarget((Character)null);
				((BaseAI)ai).SetAlerted(false);
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "SetTarget")]
	internal static class BRSMonsterAISetTargetPatch
	{
		private static bool Prefix(MonsterAI __instance, Character __0)
		{
			if (!BRSAIIgnoreDownedPlayers.IsDownedPlayer(__0))
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)null)
			{
				((BaseAI)__instance).SetAlerted(false);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class BRSMonsterAIUpdateAIPatch
	{
		private static void Prefix(MonsterAI __instance)
		{
			BRSAIIgnoreDownedPlayers.ClearTargetIfDowned(__instance);
		}

		private static void Postfix(MonsterAI __instance)
		{
			BRSAIIgnoreDownedPlayers.ClearTargetIfDowned(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class BRSDownedPlayerDamagePatch
	{
		private static bool Prefix(Character __instance)
		{
			return !BRSAIIgnoreDownedPlayers.IsDownedPlayer(__instance);
		}
	}
	[HarmonyPatch]
	internal static class BRSBaseAIIsEnemyCharacterPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo method = AccessTools.Method(typeof(BaseAI), "IsEnemy", new Type[2]
			{
				typeof(Character),
				typeof(Character)
			}, (Type[])null);
			if (method != null)
			{
				yield return method;
			}
		}

		private static void Postfix(Character __0, Character __1, ref bool __result)
		{
			if (__result && (BRSAIIgnoreDownedPlayers.IsDownedPlayer(__0) || BRSAIIgnoreDownedPlayers.IsDownedPlayer(__1)))
			{
				__result = false;
			}
		}
	}
	internal sealed class BRSDownedBehaviour : MonoBehaviour, Hoverable, Interactable
	{
		private const string RpcReviveRequest = "BRS_ReviveRequest";

		private const string RpcSelfReviveRequest = "BRS_SelfReviveRequest";

		private Player _player;

		private ZNetView _nview;

		private float _nextOwnerTick;

		private float _nextPresentationTick;

		private bool _registered;

		private void Awake()
		{
			_player = ((Component)this).GetComponent<Player>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			TryRegisterRpc();
		}

		private void OnEnable()
		{
			TryRegisterRpc();
		}

		private void Update()
		{
			TryRegisterRpc();
			if ((Object)(object)_player == (Object)null || (Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return;
			}
			if (!_player.IsDowned())
			{
				BRSPlayerPresentation.SetDownedPresentation(_player, downed: false);
				return;
			}
			if (Time.time >= _nextPresentationTick)
			{
				_nextPresentationTick = Time.time + 0.1f;
				BRSPlayerPresentation.SetDownedPresentation(_player, downed: true);
			}
			if (((Character)_player).IsOwner() && !(Time.time < _nextOwnerTick))
			{
				_nextOwnerTick = Time.time + 0.1f;
				MaintainDownedOwnerState();
				if (_player.ReleaseRequested())
				{
					PerformRealDeath();
				}
				else if (BRSConfig.AutoReleaseWhenTimerEnds.Value && _player.GetRemainingDownedSeconds() <= 0f)
				{
					PerformRealDeath();
				}
			}
		}

		public string GetHoverName()
		{
			return ((Object)(object)_player != (Object)null) ? _player.GetPlayerName() : string.Empty;
		}

		public float GetHoverOffset()
		{
			return ((Object)(object)_player != (Object)null) ? ((Character)_player).GetHoverOffset() : 0f;
		}

		public string GetHoverText()
		{
			if ((Object)(object)_player == (Object)null || !_player.IsDowned())
			{
				return string.Empty;
			}
			Player localPlayer = Player.m_localPlayer;
			float remainingDownedSeconds = _player.GetRemainingDownedSeconds();
			string playerName = _player.GetPlayerName();
			string text = "$tag_brs_release_in_bal <color=orange>" + remainingDownedSeconds.ToString("0") + "s</color>";
			string reason;
			bool flag = BRSReviveRules.CanFriendRevive(localPlayer, _player, out reason);
			if (BRSConfig.RequirePotionForFriendRevive.Value)
			{
				string value = BRSConfig.ResurrectionPotionPrefabName.Value;
				int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value);
				if (flag)
				{
					return Localization.instance.Localize(playerName + "\n$tag_brs_hover_use_revive_bal\n" + num + "x " + value + "\n" + text);
				}
				return Localization.instance.Localize(playerName + "\n" + text + "\n" + reason);
			}
			if (flag)
			{
				return Localization.instance.Localize(playerName + "\n$tag_brs_hover_use_revive_bal\n" + text);
			}
			return Localization.instance.Localize(playerName + "\n" + text + "\n" + reason);
		}

		public bool Interact(Humanoid user, bool repeat, bool alt)
		{
			if (repeat || alt)
			{
				return false;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || (Object)(object)_player == (Object)null || !_player.IsDowned())
			{
				return false;
			}
			return TryReviveFromActor(val, null, fromUseItem: false);
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || item == null || (Object)(object)_player == (Object)null || !_player.IsDowned())
			{
				return false;
			}
			if (!IsResurrectionPotion(item))
			{
				return false;
			}
			return TryReviveFromActor(val, item, fromUseItem: true);
		}

		internal void TrySelfReviveLocal()
		{
			if ((Object)(object)_player == (Object)null || !((Character)_player).IsOwner() || !_player.IsDowned())
			{
				Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click rejected before rules: player/owner/downed state invalid.");
				return;
			}
			TryRegisterRpc();
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click rejected: ZNetView is not valid.");
				return;
			}
			if (!BRSReviveRules.CanSelfRevive(_player, out var reason))
			{
				Debug.LogWarning((object)("[BalrondSecondChance] Self-revive click rejected by rules: " + reason));
				return;
			}
			if (BRSConfig.RequireItemForSelfRevive.Value && !BRSItemUtils.ConsumeResurrectionPotion(_player))
			{
				Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click passed rules but potion consumption failed.");
				return;
			}
			Debug.Log((object)"[BalrondSecondChance] Self-revive validated; invoking BRS_SelfReviveRequest.");
			_nview.InvokeRPC("BRS_SelfReviveRequest", Array.Empty<object>());
		}

		private bool TryReviveFromActor(Player reviver, ItemData directItem, bool fromUseItem)
		{
			if (!BRSReviveRules.IsWithinReviveDistance(reviver, _player))
			{
				((Character)reviver).Message((MessageType)2, "$msg_toofar", 0, (Sprite)null, false);
				return true;
			}
			if (!BRSReviveRules.CanFriendRevive(reviver, _player, out var reason))
			{
				((Character)reviver).Message((MessageType)2, reason, 0, (Sprite)null, false);
				return true;
			}
			if (BRSConfig.RequirePotionForFriendRevive.Value && !(fromUseItem ? BRSItemUtils.ConsumeSpecificItem(reviver, directItem, Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value)) : BRSItemUtils.ConsumeResurrectionPotion(reviver)))
			{
				string text = "$tag_brs_need_item_bal " + Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value) + "x " + BRSConfig.ResurrectionPotionPrefabName.Value + ".";
				((Character)reviver).Message((MessageType)2, text, 0, (Sprite)null, false);
				return true;
			}
			_nview.InvokeRPC("BRS_ReviveRequest", Array.Empty<object>());
			return true;
		}

		private bool IsResurrectionPotion(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			string value = BRSConfig.ResurrectionPotionPrefabName.Value;
			if ((Object)(object)item.m_dropPrefab != (Object)null && string.Equals(((Object)item.m_dropPrefab).name, value, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name) && string.Equals(item.m_shared.m_name, value, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return false;
		}

		private void TryRegisterRpc()
		{
			if (!_registered && !((Object)(object)_nview == (Object)null) && _nview.IsValid())
			{
				_registered = true;
				_nview.Register("BRS_ReviveRequest", (Action<long>)RPC_ReviveRequest);
				_nview.Register("BRS_SelfReviveRequest", (Action<long>)RPC_SelfReviveRequest);
			}
		}

		private void RPC_ReviveRequest(long sender)
		{
			if (!((Object)(object)_player == (Object)null) && ((Character)_player).IsOwner() && _player.IsDowned())
			{
				ReviveHere(usedPotion: true, selfRevive: false);
			}
		}

		private void RPC_SelfReviveRequest(long sender)
		{
			if (!((Object)(object)_player == (Object)null) && ((Character)_player).IsOwner() && _player.IsDowned())
			{
				Debug.Log((object)"[BalrondSecondChance] BRS_SelfReviveRequest received by owner; reviving player.");
				ReviveHere(usedPotion: true, selfRevive: true);
			}
		}

		private void MaintainDownedOwnerState()
		{
			float num = Mathf.Max(1f, ((Character)_player).GetMaxHealth() * 0.01f);
			if (((Character)_player).GetHealth() > num)
			{
				((Character)_player).SetHealth(num);
			}
		}

		private void ReviveHere(bool usedPotion, bool selfRevive)
		{
			float health = Mathf.Max(1f, ((Character)_player).GetMaxHealth() * Mathf.Clamp01(BRSConfig.ReviveHealthPercent.Value));
			_player.SetDowned(value: false, 0f);
			_player.ClearReleaseRequest();
			((Character)_player).SetHealth(health);
			BRSPlayerPresentation.SetDownedPresentation(_player, downed: false);
			if (usedPotion)
			{
				BRSReviveRules.MarkPotionReviveUsed(_player);
			}
			if ((Object)(object)_player == (Object)(object)Player.m_localPlayer)
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, selfRevive ? "$tag_brs_self_revived_bal" : "$tag_brs_revived_by_player_bal", 0, (Sprite)null, false, true);
				}
			}
		}

		private void PerformRealDeath()
		{
			_player.SetDowned(value: false, 0f);
			_player.ClearReleaseRequest();
			BRSPlayerPresentation.SetDownedPresentation(_player, downed: false);
			Launch.AllowOriginalDeath = true;
			try
			{
				((Character)_player).SetHealth(0f);
				((Character)_player).OnDeath();
			}
			finally
			{
				Launch.AllowOriginalDeath = false;
			}
		}
	}
	internal sealed class BRSHudController : MonoBehaviour
	{
		private const string UnifiedPopupPath = "_GameMain/LoadingGUI/PixelFix/IngameGui/UnifiedPopup";

		private static BRSHudController _instance;

		private static string _cachedItemDisplayName;

		private GameObject _downedPopupRoot;

		private bool _isSetup;

		private bool _isBinding;

		private float _nextRefreshTime;

		private bool _suppressReleaseUntilEscapeUp;

		private bool _wantPopupCursor;

		private GameObject _popupBackground;

		private GameObject _popupPanel;

		private GameObject _popupBkg;

		private Component _headerText;

		private Component _bodyText;

		private Button _buttonYes;

		private Button _buttonOk;

		private Button _buttonNo;

		private Component _buttonYesText;

		private Component _buttonOkText;

		private Component _buttonNoText;

		private readonly List<Button> _extraPopupButtons = new List<Button>();

		private string _sourceLeftButtonPath;

		private string _sourceCenterButtonPath;

		private string _sourceRightButtonPath;

		private string _sourceConfirmButtonPath;

		private string _sourceLeftTextPath;

		private string _sourceCenterTextPath;

		private string _sourceRightTextPath;

		internal static BRSHudController Instance => EnsureInstance();

		internal static bool HasInstance => (Object)(object)_instance != (Object)null;

		internal static BRSHudController EnsureInstance()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if ((Object)(object)_instance != (Object)null)
			{
				return _instance;
			}
			GameObject val = new GameObject("BRS_HudController");
			Object.DontDestroyOnLoad((Object)(object)val);
			_instance = val.AddComponent<BRSHudController>();
			return _instance;
		}

		internal static void ClearCachedItemDisplayName()
		{
			_cachedItemDisplayName = null;
		}

		internal bool ShouldForceMouseActive()
		{
			return _wantPopupCursor;
		}

		internal void BindHud(Hud hud)
		{
			if (!((Object)(object)hud == (Object)null) && !_isBinding && !_isSetup)
			{
				((MonoBehaviour)this).StartCoroutine(SetupPopupWhenReady());
			}
		}

		internal void UnbindHud(Hud hud)
		{
			_isBinding = false;
			_isSetup = false;
			ReleaseInputCapture();
			if ((Object)(object)_downedPopupRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_downedPopupRoot);
				_downedPopupRoot = null;
			}
			_popupBackground = null;
			_popupPanel = null;
			_popupBkg = null;
			_headerText = null;
			_bodyText = null;
			_buttonYes = null;
			_buttonOk = null;
			_buttonNo = null;
			_buttonYesText = null;
			_buttonOkText = null;
			_buttonNoText = null;
			_extraPopupButtons.Clear();
			ClearSourcePopupSemanticPaths();
		}

		private IEnumerator SetupPopupWhenReady()
		{
			_isBinding = true;
			while (!_isSetup && !TrySetupPopup())
			{
				yield return (object)new WaitForSeconds(0.5f);
			}
			_isBinding = false;
		}

		private bool TrySetupPopup()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_009c: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			Transform val = FindUnifiedPopupTransform();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Transform val2 = val.Find("PopupBlockingBackground");
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			CaptureSourcePopupSemanticPaths(val, val2);
			if ((Object)(object)_downedPopupRoot == (Object)null)
			{
				_downedPopupRoot = new GameObject("BRS_DownedUnifiedPopup");
				_downedPopupRoot.transform.SetParent(val, false);
				_downedPopupRoot.transform.SetAsLastSibling();
				RectTransform val3 = _downedPopupRoot.AddComponent<RectTransform>();
				val3.anchorMin = Vector2.zero;
				val3.anchorMax = Vector2.one;
				val3.offsetMin = Vector2.zero;
				val3.offsetMax = Vector2.zero;
				((Transform)val3).localScale = Vector3.one;
				GameObject val4 = Object.Instantiate<GameObject>(((Component)val2).gameObject, _downedPopupRoot.transform, false);
				((Object)val4).name = "PopupBlockingBackground";
				AudioRoutingService.Process(_downedPopupRoot);
			}
			if (!CachePopupRefs(_downedPopupRoot.transform))
			{
				return false;
			}
			HideClonedPopupInputFields(_downedPopupRoot.transform);
			PrepareCloneForDownedUsage();
			WireButtons();
			_isSetup = true;
			HidePopup();
			return true;
		}

		private Transform FindUnifiedPopupTransform()
		{
			Transform[] array = Resources.FindObjectsOfTypeAll<Transform>();
			Transform val = null;
			foreach (Transform val2 in array)
			{
				if ((Object)(object)val2 == (Object)null || ((Object)val2).name != "UnifiedPopup")
				{
					continue;
				}
				Transform val3 = val2.Find("PopupBlockingBackground");
				if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.Find("Popup") == (Object)null))
				{
					string hierarchyPath = GetHierarchyPath(val2);
					if (hierarchyPath.Contains("_GameMain/LoadingGUI/PixelFix/IngameGui/UnifiedPopup"))
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null && hierarchyPath.Contains("/IngameGui/"))
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static string GetHierarchyPath(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return string.Empty;
			}
			string text = ((Object)t).name;
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}

		private bool CachePopupRefs(Transform root)
		{
			Transform val = root.Find("PopupBlockingBackground");
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Transform val2 = val.Find("Popup");
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			Transform val3 = val2.Find("bkg");
			Transform val4 = val2.Find("HeaderText");
			Transform val5 = val2.Find("BodyText");
			if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null)
			{
				return false;
			}
			_popupBackground = ((Component)val).gameObject;
			_popupPanel = ((Component)val2).gameObject;
			_popupBkg = (((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : null);
			_headerText = GetTextComponent(val4);
			_bodyText = GetTextComponent(val5);
			if (!ResolveActionButtons(val))
			{
				return false;
			}
			_buttonNoText = ResolveTextByRelativePath(val, _sourceLeftTextPath) ?? GetButtonTextComponent(_buttonNo);
			_buttonOkText = ResolveTextByRelativePath(val, _sourceCenterTextPath) ?? GetButtonTextComponent(_buttonOk);
			_buttonYesText = ResolveTextByRelativePath(val, _sourceRightTextPath) ?? GetButtonTextComponent(_buttonYes);
			LogResolvedPopupButtons();
			return (Object)(object)_popupBackground != (Object)null && (Object)(object)_popupPanel != (Object)null && (Object)(object)_headerText != (Object)null && (Object)(object)_bodyText != (Object)null && (Object)(object)_buttonYes != (Object)null && (Object)(object)_buttonNo != (Object)null && (Object)(object)_buttonOk != (Object)null;
		}

		private bool ResolveActionButtons(Transform clonedBackgroundRoot)
		{
			_buttonYes = null;
			_buttonOk = null;
			_buttonNo = null;
			_extraPopupButtons.Clear();
			Button[] componentsInChildren = ((Component)clonedBackgroundRoot).GetComponentsInChildren<Button>(true);
			List<Button> list = new List<Button>();
			foreach (Button val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<TMP_InputField>() != (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<InputField>() != (Object)null))
				{
					list.Add(val);
				}
			}
			if (list.Count < 2)
			{
				return false;
			}
			_buttonNo = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceLeftButtonPath);
			_buttonOk = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceCenterButtonPath);
			_buttonYes = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceRightButtonPath);
			Button val2 = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceConfirmButtonPath);
			if ((Object)(object)_buttonNo == (Object)null)
			{
				_buttonNo = FindButtonByAliases(list, "buttonLeft", "ButtonLeft", "ButtonNo", "buttonNo");
			}
			if ((Object)(object)_buttonOk == (Object)null)
			{
				_buttonOk = FindButtonByAliases(list, "buttonCenter", "ButtonCenter", "ButtonOk", "buttonOk");
			}
			if ((Object)(object)_buttonYes == (Object)null)
			{
				_buttonYes = FindButtonByAliases(list, "buttonRight", "ButtonRight", "ButtonYes", "buttonYes");
			}
			List<Button> list2 = new List<Button>(list);
			list2.Sort((Button a, Button b) => GetButtonScreenX(a).CompareTo(GetButtonScreenX(b)));
			if ((Object)(object)_buttonNo == (Object)null && list2.Count > 0)
			{
				_buttonNo = list2[0];
			}
			if ((Object)(object)_buttonYes == (Object)null && list2.Count > 1)
			{
				for (int num = list2.Count - 1; num >= 0; num--)
				{
					if ((Object)(object)list2[num] != (Object)(object)_buttonNo)
					{
						_buttonYes = list2[num];
						break;
					}
				}
			}
			if ((Object)(object)_buttonOk == (Object)null)
			{
				float num2 = (((Object)(object)_buttonNo != (Object)null) ? GetButtonScreenX(_buttonNo) : 0f);
				float num3 = (((Object)(object)_buttonYes != (Object)null) ? GetButtonScreenX(_buttonYes) : 0f);
				float num4 = (num2 + num3) * 0.5f;
				float num5 = float.MaxValue;
				for (int num6 = 0; num6 < list2.Count; num6++)
				{
					Button val3 = list2[num6];
					if (!((Object)(object)val3 == (Object)(object)_buttonNo) && !((Object)(object)val3 == (Object)(object)_buttonYes))
					{
						float num7 = Mathf.Abs(GetButtonScreenX(val3) - num4);
						if (num7 < num5)
						{
							num5 = num7;
							_buttonOk = val3;
						}
					}
				}
			}
			if ((Object)(object)_buttonOk == (Object)null)
			{
				_buttonOk = _buttonNo;
			}
			for (int num8 = 0; num8 < list.Count; num8++)
			{
				Button val4 = list[num8];
				if ((Object)(object)val4 != (Object)(object)_buttonNo && (Object)(object)val4 != (Object)(object)_buttonYes && (Object)(object)val4 != (Object)(object)_buttonOk && !_extraPopupButtons.Contains(val4))
				{
					_extraPopupButtons.Add(val4);
				}
			}
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)_buttonNo && (Object)(object)val2 != (Object)(object)_buttonYes && (Object)(object)val2 != (Object)(object)_buttonOk && !_extraPopupButtons.Contains(val2))
			{
				_extraPopupButtons.Add(val2);
			}
			return (Object)(object)_buttonNo != (Object)null && (Object)(object)_buttonYes != (Object)null && (Object)(object)_buttonOk != (Object)null;
		}

		private void CaptureSourcePopupSemanticPaths(Transform unifiedPopup, Transform sourceBackground)
		{
			ClearSourcePopupSemanticPaths();
			if (!((Object)(object)unifiedPopup == (Object)null) && !((Object)(object)sourceBackground == (Object)null))
			{
				UnifiedPopup component = ((Component)unifiedPopup).GetComponent<UnifiedPopup>();
				if (!((Object)(object)component == (Object)null))
				{
					Button privateFieldValue = GetPrivateFieldValue<Button>(component, "buttonLeft");
					Button privateFieldValue2 = GetPrivateFieldValue<Button>(component, "buttonCenter");
					Button privateFieldValue3 = GetPrivateFieldValue<Button>(component, "buttonRight");
					Button privateFieldValue4 = GetPrivateFieldValue<Button>(component, "buttonConfirm");
					Component privateFieldValue5 = GetPrivateFieldValue<Component>(component, "buttonLeftText");
					Component privateFieldValue6 = GetPrivateFieldValue<Component>(component, "buttonCenterText");
					Component privateFieldValue7 = GetPrivateFieldValue<Component>(component, "buttonRightText");
					_sourceLeftButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue != (Object)null) ? ((Component)privateFieldValue).transform : null);
					_sourceCenterButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue2 != (Object)null) ? ((Component)privateFieldValue2).transform : null);
					_sourceRightButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue3 != (Object)null) ? ((Component)privateFieldValue3).transform : null);
					_sourceConfirmButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue4 != (Object)null) ? ((Component)privateFieldValue4).transform : null);
					_sourceLeftTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue5 != (Object)null) ? privateFieldValue5.transform : null);
					_sourceCenterTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue6 != (Object)null) ? privateFieldValue6.transform : null);
					_sourceRightTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue7 != (Object)null) ? privateFieldValue7.transform : null);
				}
			}
		}

		private void ClearSourcePopupSemanticPaths()
		{
			_sourceLeftButtonPath = null;
			_sourceCenterButtonPath = null;
			_sourceRightButtonPath = null;
			_sourceConfirmButtonPath = null;
			_sourceLeftTextPath = null;
			_sourceCenterTextPath = null;
			_sourceRightTextPath = null;
		}

		private static T GetPrivateFieldValue<T>(object instance, string fieldName) where T : class
		{
			if (instance == null || string.IsNullOrEmpty(fieldName))
			{
				return null;
			}
			FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return (field != null) ? (field.GetValue(instance) as T) : null;
		}

		private static string GetRelativePath(Transform root, Transform target)
		{
			if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null)
			{
				return null;
			}
			if ((Object)(object)target == (Object)(object)root)
			{
				return string.Empty;
			}
			List<string> list = new List<string>();
			Transform val = target;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			if ((Object)(object)val != (Object)(object)root)
			{
				return null;
			}
			list.Reverse();
			return string.Join("/", list.ToArray());
		}

		private static Button ResolveButtonByRelativePath(Transform root, string path)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path))
			{
				return null;
			}
			Transform val = root.Find(path);
			return ((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Button>() : null;
		}

		private static Component ResolveTextByRelativePath(Transform root, string path)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path))
			{
				return null;
			}
			Transform t = root.Find(path);
			return GetTextComponent(t);
		}

		private static Button FindButtonByAliases(List<Button> buttons, params string[] aliases)
		{
			for (int i = 0; i < buttons.Count; i++)
			{
				Button val = buttons[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((Object)((Component)val).gameObject).name;
				for (int j = 0; j < aliases.Length; j++)
				{
					if (string.Equals(name, aliases[j], StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
				}
			}
			return null;
		}

		private static float GetButtonScreenX(Button button)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			return ((Object)(object)button != (Object)null) ? ((Component)button).transform.position.x : 0f;
		}

		private void DisableExtraPopupButtons()
		{
			for (int i = 0; i < _extraPopupButtons.Count; i++)
			{
				Button val = _extraPopupButtons[i];
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
		}

		private void LogResolvedPopupButtons()
		{
			Debug.Log((object)("[BalrondSecondChance] UnifiedPopup buttons resolved: left/release=" + GetButtonDebugName(_buttonNo) + ", center/single=" + GetButtonDebugName(_buttonOk) + ", right/self-revive=" + GetButtonDebugName(_buttonYes) + ", confirm-source-path=" + (_sourceConfirmButtonPath ?? "<none>") + ", disabled-extra-buttons=" + _extraPopupButtons.Count + "."));
		}

		private static string GetButtonDebugName(Button button)
		{
			return ((Object)(object)button != (Object)null) ? GetHierarchyPath(((Component)button).transform) : "<null>";
		}

		private static Component GetTextComponent(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return null;
			}
			TMP_Text component = ((Component)t).GetComponent<TMP_Text>();
			if ((Object)(object)component != (Object)null)
			{
				return (Component)(object)component;
			}
			Text component2 = ((Component)t).GetComponent<Text>();
			if ((Object)(object)component2 != (Object)null)
			{
				return (Component)(object)component2;
			}
			return null;
		}

		private static Component GetButtonTextComponent(Button button)
		{
			if ((Object)(object)button == (Object)null)
			{
				return null;
			}
			Component result = null;
			int num = int.MinValue;
			TMP_Text[] componentsInChildren = ((Component)button).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<TMP_InputField>() != (Object)null))
				{
					int num2 = ScoreButtonLabel(val.transform, ((Component)val).gameObject.activeSelf, val.rectTransform);
					if (num2 > num)
					{
						num = num2;
						result = (Component)(object)val;
					}
				}
			}
			Text[] componentsInChildren2 = ((Component)button).GetComponentsInChildren<Text>(true);
			foreach (Text val2 in componentsInChildren2)
			{
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent<InputField>() != (Object)null))
				{
					int num3 = ScoreButtonLabel(((Component)val2).transform, ((Component)val2).gameObject.activeSelf, ((Graphic)val2).rectTransform);
					if (num3 > num)
					{
						num = num3;
						result = (Component)(object)val2;
					}
				}
			}
			return result;
		}

		private static int ScoreButtonLabel(Transform textTransform, bool activeSelf, RectTransform rect)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			int num = 0;
			string text = (((Object)(object)textTransform != (Object)null) ? ((Object)textTransform).name : string.Empty);
			if (string.Equals(text, "Text", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "Label", StringComparison.OrdinalIgnoreCase))
			{
				num += 200;
			}
			else if (text.IndexOf("text", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("label", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				num += 100;
			}
			if (activeSelf)
			{
				num += 25;
			}
			if ((Object)(object)textTransform != (Object)null && (Object)(object)textTransform.parent != (Object)null && (Object)(object)((Component)textTransform.parent).GetComponent<Button>() != (Object)null)
			{
				num += 50;
			}
			if ((Object)(object)rect != (Object)null)
			{
				Rect rect2 = rect.rect;
				float width = ((Rect)(ref rect2)).width;
				rect2 = rect.rect;
				float num2 = Mathf.Abs(width * ((Rect)(ref rect2)).height);
				if (num2 > 1000f)
				{
					num += 30;
				}
				else if (num2 > 100f)
				{
					num += 10;
				}
			}
			return num;
		}

		private static void HideClonedPopupInputFields(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			TMP_InputField[] componentsInChildren = ((Component)root).GetComponentsInChildren<TMP_InputField>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if ((Object)(object)componentsInChildren[i] != (Object)null)
				{
					((Component)componentsInChildren[i]).gameObject.SetActive(false);
				}
			}
			InputField[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<InputField>(true);
			for (int j = 0; j < componentsInChildren2.Length; j++)
			{
				if ((Object)(object)componentsInChildren2[j] != (Object)null)
				{
					((Component)componentsInChildren2[j]).gameObject.SetActive(false);
				}
			}
		}

		private static void SetText(Component textComponent, string value)
		{
			if ((Object)(object)textComponent == (Object)null)
			{
				return;
			}
			if (Localization.instance != null)
			{
				value = Localization.instance.Localize(value);
			}
			TMP_Text val = (TMP_Text)(object)((textComponent is TMP_Text) ? textComponent : null);
			if ((Object)(object)val != (Object)null)
			{
				val.text = value;
				return;
			}
			Text val2 = (Text)(object)((textComponent is Text) ? textComponent : null);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.text = value;
			}
		}

		private static void SetButtonLabel(Button button, Component preferredText, string value)
		{
			if (!((Object)(object)button == (Object)null))
			{
				Component textComponent = preferredText ?? GetButtonTextComponent(button);
				SetText(textComponent, value);
			}
		}

		private void PrepareCloneForDownedUsage()
		{
			//IL_00cd: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_downedPopupRoot == (Object)null))
			{
				CanvasGroup val = _popupBackground.GetComponent<CanvasGroup>();
				if ((Object)(object)val == (Object)null)
				{
					val = _popupBackground.AddComponent<CanvasGroup>();
				}
				val.alpha = 1f;
				val.blocksRaycasts = true;
				val.interactable = true;
				DisableExtraPopupButtons();
				SetText(_headerText, "$tag_brs_popup_header_bal");
				SetText(_bodyText, "$tag_brs_popup_wait_for_revive_bal");
				SetButtonLabel(_buttonYes, _buttonYesText, "$tag_brs_button_resurrect_self_bal");
				SetButtonLabel(_buttonNo, _buttonNoText, "$tag_brs_button_release_soul_bal");
				SetButtonLabel(_buttonOk, _buttonOkText, "$tag_brs_button_release_soul_bal");
				Navigation navigation = ((Selectable)_buttonYes).navigation;
				((Navigation)(ref navigation)).mode = (Mode)0;
				((Selectable)_buttonYes).navigation = navigation;
				Navigation navigation2 = ((Selectable)_buttonNo).navigation;
				((Navigation)(ref navigation2)).mode = (Mode)0;
				((Selectable)_buttonNo).navigation = navigation2;
				Navigation navigation3 = ((Selectable)_buttonOk).navigation;
				((Navigation)(ref navigation3)).mode = (Mode)0;
				((Selectable)_buttonOk).navigation = navigation3;
				if ((Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes)
				{
					((Component)_buttonOk).gameObject.SetActive(false);
				}
			}
		}

		private void WireButtons()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			if ((Object)(object)_buttonYes != (Object)null)
			{
				_buttonYes.onClick = new ButtonClickedEvent();
				((UnityEvent)_buttonYes.onClick).AddListener(new UnityAction(OnSelfReviveClicked));
			}
			if ((Object)(object)_buttonNo != (Object)null)
			{
				_buttonNo.onClick = new ButtonClickedEvent();
				((UnityEvent)_buttonNo.onClick).AddListener(new UnityAction(OnReleaseSoulClicked));
			}
			if ((Object)(object)_buttonOk != (Object)null && (Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes)
			{
				_buttonOk.onClick = new ButtonClickedEvent();
				((UnityEvent)_buttonOk.onClick).AddListener(new UnityAction(OnReleaseSoulClicked));
			}
		}

		private void Update()
		{
			if (!_isSetup || (Object)(object)_downedPopupRoot == (Object)null || Time.time < _nextRefreshTime)
			{
				return;
			}
			_nextRefreshTime = Time.time + 0.05f;
			if (_suppressReleaseUntilEscapeUp && !Input.GetKey((KeyCode)27))
			{
				_suppressReleaseUntilEscapeUp = false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned())
			{
				HidePopup();
			}
			else if (Menu.IsVisible())
			{
				HidePopup();
				if ((Object)(object)EventSystem.current != (Object)null)
				{
					EventSystem.current.SetSelectedGameObject((GameObject)null);
				}
			}
			else
			{
				RefreshPopup(localPlayer);
			}
		}

		private void RefreshPopup(Player player)
		{
			ShowPopup();
			int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value);
			string resurrectionItemDisplayName = GetResurrectionItemDisplayName();
			bool value = BRSConfig.AllowSelfPotionRevive.Value;
			bool value2 = BRSConfig.RequireItemForSelfRevive.Value;
			ItemData foundItem;
			bool flag = BRSItemUtils.TryFindResurrectionPotion(player, out foundItem);
			bool flag2 = false;
			string reason = string.Empty;
			if (value)
			{
				flag2 = BRSReviveRules.CanSelfRevive(player, out reason);
			}
			else
			{
				reason = "$tag_brs_reason_self_revive_disabled_bal";
			}
			string text = "$tag_brs_popup_wait_for_revive_bal\n\n$tag_brs_release_in_bal " + player.GetRemainingDownedSeconds().ToString("0") + "s";
			if (value)
			{
				if (value2)
				{
					if (flag && flag2)
					{
						text = text + "\n\n$tag_brs_popup_self_revive_use_item_bal " + num + "x " + resurrectionItemDisplayName + " $tag_brs_popup_self_revive_suffix_bal";
					}
					else if (!string.IsNullOrEmpty(reason))
					{
						text = text + "\n\n" + reason;
					}
				}
				else if (flag2)
				{
					text += "\n\n$tag_brs_popup_self_revive_free_bal";
				}
				else if (!string.IsNullOrEmpty(reason))
				{
					text = text + "\n\n" + reason;
				}
			}
			DisableExtraPopupButtons();
			SetText(_headerText, "$tag_brs_popup_header_bal");
			SetText(_bodyText, text);
			SetButtonLabel(_buttonYes, _buttonYesText, "$tag_brs_button_resurrect_self_bal");
			SetButtonLabel(_buttonNo, _buttonNoText, "$tag_brs_button_release_soul_bal");
			SetButtonLabel(_buttonOk, _buttonOkText, "$tag_brs_button_release_soul_bal");
			bool flag3 = value;
			if ((Object)(object)_buttonYes != (Object)null)
			{
				((Component)_buttonYes).gameObject.SetActive(flag3);
				((Selectable)_buttonYes).interactable = flag2;
			}
			if ((Object)(object)_buttonNo != (Object)null)
			{
				((Component)_buttonNo).gameObject.SetActive(flag3 || (Object)(object)_buttonOk == (Object)(object)_buttonNo);
				((Selectable)_buttonNo).interactable = true;
			}
			if ((Object)(object)_buttonOk != (Object)null && (Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes)
			{
				((Component)_buttonOk).gameObject.SetActive(!flag3);
				((Selectable)_buttonOk).interactable = true;
			}
			bool flag4 = (Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus();
			if (!flag4)
			{
				_downedPopupRoot.transform.SetAsLastSibling();
			}
			if (flag4 || !((Object)(object)EventSystem.current != (Object)null))
			{
				return;
			}
			GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
			if ((Object)(object)currentSelectedGameObject == (Object)null || !currentSelectedGameObject.activeInHierarchy || !currentSelectedGameObject.transform.IsChildOf(_downedPopupRoot.transform))
			{
				if (flag3)
				{
					EventSystem.current.SetSelectedGameObject((GameObject)null);
				}
				else if ((Object)(object)_buttonOk != (Object)null && ((Component)_buttonOk).gameObject.activeInHierarchy)
				{
					EventSystem.current.SetSelectedGameObject(((Component)_buttonOk).gameObject);
				}
			}
		}

		private static string GetResurrectionItemDisplayName()
		{
			if (!string.IsNullOrEmpty(_cachedItemDisplayName))
			{
				return _cachedItemDisplayName;
			}
			string value = BRSConfig.ResurrectionPotionPrefabName.Value;
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null)
			{
				return value;
			}
			for (int i = 0; i < ObjectDB.instance.m_items.Count; i++)
			{
				GameObject val = ObjectDB.instance.m_items[i];
				if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val).name, value, StringComparison.OrdinalIgnoreCase))
				{
					ItemDrop component = val.GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
					{
						return value;
					}
					string name = component.m_itemData.m_shared.m_name;
					if (string.IsNullOrEmpty(name))
					{
						return value;
					}
					_cachedItemDisplayName = ((Localization.instance != null) ? Localization.instance.Localize(name) : name);
					return _cachedItemDisplayName;
				}
			}
			return value;
		}

		private void ShowPopup()
		{
			CaptureInput();
			if ((Object)(object)_downedPopupRoot != (Object)null && !_downedPopupRoot.activeSelf)
			{
				_downedPopupRoot.SetActive(true);
			}
			if ((Object)(object)_popupBackground != (Object)null && !_popupBackground.activeSelf)
			{
				_popupBackground.SetActive(true);
			}
			if ((Object)(object)_popupPanel != (Object)null && !_popupPanel.activeSelf)
			{
				_popupPanel.SetActive(true);
			}
			if ((Object)(object)_popupBkg != (Object)null && !_popupBkg.activeSelf)
			{
				_popupBkg.SetActive(true);
			}
		}

		private void HidePopup()
		{
			ReleaseInputCapture();
			if ((Object)(object)_popupBackground != (Object)null)
			{
				_popupBackground.SetActive(false);
			}
			if ((Object)(object)_downedPopupRoot != (Object)null)
			{
				_downedPopupRoot.SetActive(false);
			}
		}

		private void CaptureInput()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			_wantPopupCursor = true;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				localPlayer.SetMouseLook(Vector2.zero);
			}
			if ((Object)(object)GameCamera.instance != (Object)null && ((Behaviour)GameCamera.instance).enabled)
			{
				((Behaviour)GameCamera.instance).enabled = false;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
		}

		private void ReleaseInputCapture()
		{
			_wantPopupCursor = false;
			if (!Menu.IsVisible())
			{
				if ((Object)(object)GameCamera.instance != (Object)null && !((Behaviour)GameCamera.instance).enabled)
				{
					((Behaviour)GameCamera.instance).enabled = true;
				}
				Cursor.lockState = (CursorLockMode)1;
				Cursor.visible = false;
			}
		}

		private void OnSelfReviveClicked()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && localPlayer.IsDowned())
				{
					Debug.Log((object)"[BalrondSecondChance] Self-revive UI button clicked.");
					BRSDownedBehaviour component = ((Component)localPlayer).GetComponent<BRSDownedBehaviour>();
					if (!((Object)(object)component == (Object)null))
					{
						component.TrySelfReviveLocal();
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[BRSHudController] Exception in OnSelfReviveClicked: " + ex));
			}
		}

		private void OnReleaseSoulClicked()
		{
			try
			{
				if (!_suppressReleaseUntilEscapeUp)
				{
					Player localPlayer = Player.m_localPlayer;
					if (!((Object)(object)localPlayer == (Object)null) && localPlayer.IsDowned())
					{
						localPlayer.SetReleaseRequested(value: true);
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[BRSHudController] Exception in OnReleaseSoulClicked: " + ex));
			}
		}

		private void LateUpdate()
		{
			if (_isSetup && Input.GetKeyDown((KeyCode)27))
			{
				_suppressReleaseUntilEscapeUp = true;
				if ((Object)(object)EventSystem.current != (Object)null)
				{
					EventSystem.current.SetSelectedGameObject((GameObject)null);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "Awake")]
	internal static class BRSHudAwakePatch
	{
		private static void Postfix(Hud __instance)
		{
			BRSHudController.EnsureInstance();
			BRSHudController.Instance.BindHud(__instance);
		}
	}
	[HarmonyPatch(typeof(Hud), "OnDestroy")]
	internal static class BRSHudOnDestroyPatch
	{
		private static void Prefix(Hud __instance)
		{
			if (BRSHudController.HasInstance)
			{
				BRSHudController.Instance.UnbindHud(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class BRSPlayerControllerTakeInputPatch
	{
		private static bool Prefix(ref bool __result)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned())
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class BRSPlayerUpdatePatch
	{
		private static bool Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsValid() || !((Character)__instance).m_nview.IsOwner())
			{
				return true;
			}
			if (!__instance.IsDowned())
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UseHotbarItem")]
	internal static class BRSUseHotbarItemPatch
	{
		private static bool Prefix(Player __instance)
		{
			return (Object)(object)__instance == (Object)null || !__instance.IsDowned();
		}
	}
	[HarmonyPatch(typeof(Player), "HandleRadialInput")]
	internal static class BRSHandleRadialInputPatch
	{
		private static bool Prefix(Player __instance)
		{
			return (Object)(object)__instance == (Object)null || !__instance.IsDowned();
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	internal static class BRSUpdatePlacementPatch
	{
		private static bool Prefix(Player __instance)
		{
			return (Object)(object)__instance == (Object)null || !__instance.IsDowned();
		}
	}
	[HarmonyPatch(typeof(ZInput), "IsMouseActive")]
	internal static class BRSZInputIsMouseActivePatch
	{
		private static void Postfix(ref bool __result)
		{
			if (BRSHudController.HasInstance && BRSHudController.Instance.ShouldForceMouseActive())
			{
				__result = true;
			}
		}
	}
	internal static class BRSItemUtils
	{
		internal static bool TryFindResurrectionPotion(Player player, out ItemData foundItem)
		{
			foundItem = null;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			string value = BRSConfig.ResurrectionPotionPrefabName.Value;
			int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value);
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null)
				{
					bool flag = (Object)(object)allItem.m_dropPrefab != (Object)null && string.Equals(((Object)allItem.m_dropPrefab).name, value, StringComparison.OrdinalIgnoreCase);
					bool flag2 = allItem.m_shared != null && !string.IsNullOrEmpty(allItem.m_shared.m_name) && string.Equals(allItem.m_shared.m_name, value, StringComparison.OrdinalIgnoreCase);
					if ((flag || flag2) && allItem.m_stack >= num)
					{
						foundItem = allItem;
						return true;
					}
				}
			}
			return false;
		}

		internal static bool ConsumeResurrectionPotion(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!TryFindResurrectionPotion(player, out var foundItem))
			{
				return false;
			}
			return ConsumeSpecificItem(player, foundItem, Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value));
		}

		internal static bool ConsumeSpecificItem(Player player, ItemData item, int amount)
		{
			if ((Object)(object)player == (Object)null || item == null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			amount = Mathf.Max(1, amount);
			if (item.m_stack < amount)
			{
				return false;
			}
			inventory.RemoveItem(item, amount);
			player.OnInventoryChanged();
			return true;
		}
	}
	internal static class BRSKeys
	{
		public const string Downed = "brs_downed";

		public const string DownedUntil = "brs_downed_until";

		public const string DownedUntilNetworkTicks = "brs_downed_until_net_ticks";

		public const string ReleaseRequested = "brs_release_requested";

		public const string LastPotionReviveTime = "brs_last_potion_revive_time";
	}
	internal static class BRSPlayerExtensions
	{
		private static ZDO GetValidZdo(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid())
			{
				return null;
			}
			return ((Character)player).m_nview.GetZDO();
		}

		public static bool IsDowned(this Player player)
		{
			ZDO validZdo = GetValidZdo(player);
			return validZdo != null && validZdo.GetBool("brs_downed", false);
		}

		public static float GetDownedUntil(this Player player)
		{
			ZDO validZdo = GetValidZdo(player);
			return (validZdo != null) ? validZdo.GetFloat("brs_downed_until", 0f) : 0f;
		}

		public static long GetDownedUntilNetworkTicks(this Player player)
		{
			ZDO validZdo = GetValidZdo(player);
			return (validZdo != null) ? validZdo.GetLong("brs_downed_until_net_ticks", 0L) : 0;
		}

		public static void SetDowned(this Player player, bool value, float until)
		{
			ZDO validZdo = GetValidZdo(player);
			if (validZdo != null)
			{
				validZdo.Set("brs_downed", value);
				validZdo.Set("brs_downed_until", value ? until : 0f);
				long num = 0L;
				if (value && (Object)(object)ZNet.instance != (Object)null)
				{
					float num2 = Mathf.Max(0f, until - Time.time);
					num = ZNet.instance.GetTime().AddSeconds(num2).Ticks;
				}
				validZdo.Set("brs_downed_until_net_ticks", num);
				validZdo.Set("brs_release_requested", false);
			}
		}

		public static bool ReleaseRequested(this Player player)
		{
			ZDO validZdo = GetValidZdo(player);
			return validZdo != null && validZdo.GetBool("brs_release_requested", false);
		}

		public static void SetReleaseRequested(this Player player, bool value)
		{
			ZDO validZdo = GetValidZdo(player);
			if (validZdo != null)
			{
				validZdo.Set("brs_release_requested", value);
			}
		}

		public static void RequestRelease(this Player player)
		{
			player.SetReleaseRequested(value: true);
		}

		public static void ClearReleaseRequest(this Player player)
		{
			player.SetReleaseRequested(value: false);
		}

		public static long GetLastPotionReviveTicks(this Player player)
		{
			ZDO validZdo = GetValidZdo(player);
			return (validZdo != null) ? validZdo.GetLong("brs_last_potion_revive_time", 0L) : 0;
		}

		public static void SetLastPotionReviveTicks(this Player player, long ticks)
		{
			ZDO validZdo = GetValidZdo(player);
			if (validZdo != null)
			{
				validZdo.Set("brs_last_potion_revive_time", ticks);
			}
		}

		public static float GetRemainingDownedSeconds(this Player player)
		{
			long downedUntilNetworkTicks = player.GetDownedUntilNetworkTicks();
			if (downedUntilNetworkTicks > 0 && (Object)(object)ZNet.instance != (Object)null)
			{
				try
				{
					DateTime dateTime = new DateTime(downedUntilNetworkTicks);
					return Mathf.Max(0f, (float)(dateTime - ZNet.instance.GetTime()).TotalSeconds);
				}
				catch
				{
				}
			}
			return Mathf.Max(0f, player.GetDownedUntil() - Time.time);
		}

		public static bool HasActivePotionReviveCooldown(this Player player)
		{
			return player.GetRemainingPotionReviveCooldownMinutes() > 0f;
		}

		public static float GetRemainingPotionReviveCooldownMinutes(this Player player)
		{
			long lastPotionReviveTicks = player.GetLastPotionReviveTicks();
			if (lastPotionReviveTicks <= 0)
			{
				return 0f;
			}
			float value = BRSConfig.ResurrectionCooldownMinutes.Value;
			if (value <= 0f)
			{
				return 0f;
			}
			DateTime dateTime;
			try
			{
				dateTime = new DateTime(lastPotionReviveTicks, DateTimeKind.Utc);
			}
			catch
			{
				return 0f;
			}
			double totalMinutes = (DateTime.UtcNow - dateTime).TotalMinutes;
			return Mathf.Max(0f, value - (float)totalMinutes);
		}
	}
	[HarmonyPatch(typeof(Player), "GetHoverText")]
	internal static class BRSPlayerHoverTextPatch
	{
		private static bool Prefix(Player __instance, ref string __result)
		{
			if ((Object)(object)__instance == (Object)null || !__instance.IsDowned())
			{
				return true;
			}
			BRSDownedBehaviour component = ((Component)__instance).GetComponent<BRSDownedBehaviour>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			__result = component.GetHoverText();
			return false;
		}
	}
	internal static class BRSPlayerPresentation
	{
		private const string AttachRootName = "BRS_DownedAttachRoot";

		private const string AttachPointName = "attachpoint";

		private const string BedAnimation = "attach_bed";

		private const string SitAnimation = "emote_sit";

		private static readonly Vector3 DetachOffset = new Vector3(0f, 0.5f, 0f);

		private static readonly Dictionary<string, string> DownedAnimations = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Sit", "emote_sit" },
			{ "Bed", "attach_bed" },
			{ "Dance", "emote_dance" },
			{ "Headbang", "emote_headbang" }
		};

		internal static void SetDownedPresentation(Player player, bool downed)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid() && ((Character)player).IsOwner();
			if (!downed)
			{
				BRSDownedVisualState component = ((Component)player).GetComponent<BRSDownedVisualState>();
				if (!((Object)(object)component == (Object)null))
				{
					ClearObserverAnimation(player, component);
					if (flag)
					{
						DetachPlayer(player, component);
					}
					else
					{
						ClearLocalAttachBookkeeping(component);
					}
					DestroyAnchor(component);
				}
			}
			else
			{
				BRSDownedVisualState orCreateState = GetOrCreateState(player);
				string configuredAnimation = GetConfiguredAnimation();
				if (flag)
				{
					ClearObserverAnimation(player, orCreateState);
					EnsureAnchorAtPlayer(player, orCreateState);
					ApplyDownedPhysics(player);
					AttachPlayer(player, orCreateState, configuredAnimation);
				}
				else
				{
					ApplyObserverAnimation(player, orCreateState, configuredAnimation);
				}
			}
		}

		private static BRSDownedVisualState GetOrCreateState(Player player)
		{
			BRSDownedVisualState bRSDownedVisualState = ((Component)player).GetComponent<BRSDownedVisualState>();
			if ((Object)(object)bRSDownedVisualState == (Object)null)
			{
				bRSDownedVisualState = ((Component)player).gameObject.AddComponent<BRSDownedVisualState>();
			}
			return bRSDownedVisualState;
		}

		private static void EnsureAnchorAtPlayer(Player player, BRSDownedVisualState state)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			if ((Object)(object)state.AnchorRoot == (Object)null)
			{
				state.AnchorRoot = new GameObject("BRS_DownedAttachRoot");
				((Object)state.AnchorRoot).hideFlags = (HideFlags)61;
				state.AnchorRoot.layer = ((Component)player).gameObject.layer;
			}
			state.AnchorRoot.transform.position = ((Component)player).transform.position;
			state.AnchorRoot.transform.rotation = ((Component)player).transform.rotation;
			if ((Object)(object)state.AttachPoint == (Object)null)
			{
				GameObject val = new GameObject("attachpoint");
				((Object)val).hideFlags = (HideFlags)61;
				val.transform.SetParent(state.AnchorRoot.transform, false);
				state.AttachPoint = val.transform;
			}
			state.AttachPoint.localPosition = Vector3.zero;
			state.AttachPoint.localRotation = Quaternion.identity;
		}

		private static void ApplyDownedPhysics(Player player)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)((Character)player).m_body != (Object)null)
				{
					((Character)player).m_body.linearVelocity = Vector3.zero;
					((Character)player).m_body.angularVelocity = Vector3.zero;
				}
			}
			catch
			{
			}
		}

		private static void AttachPlayer(Player player, BRSDownedVisualState state, string wantedAnimation)
		{
			if ((Object)(object)state.AttachPoint == (Object)null)
			{
				return;
			}
			if (!state.Attached || !string.Equals(state.AnimationName, wantedAnimation, StringComparison.Ordinal))
			{
				if (state.Attached)
				{
					DetachPlayer(player, state);
				}
				string text = wantedAnimation;
				bool flag = TryAttach(player, state.AttachPoint, text);
				if (!flag && !string.Equals(text, "emote_sit", StringComparison.Ordinal))
				{
					text = "emote_sit";
					flag = TryAttach(player, state.AttachPoint, text);
				}
				if (flag)
				{
					SnapPlayerToAnchor(player, state.AttachPoint);
					state.Attached = true;
					state.AnimationName = text;
				}
			}
			else
			{
				SnapPlayerToAnchor(player, state.AttachPoint);
			}
		}

		private static string GetConfiguredAnimation()
		{
			string text = ((BRSConfig.DownedPresentationMode != null) ? BRSConfig.DownedPresentationMode.Value : "Sit");
			if (string.IsNullOrWhiteSpace(text))
			{
				return "emote_sit";
			}
			if (DownedAnimations.TryGetValue(text.Trim(), out var value))
			{
				return value;
			}
			return "emote_sit";
		}

		private static bool TryAttach(Player player, Transform attachPoint, string animationName)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				((Character)player).AttachStart(attachPoint, (GameObject)null, false, false, false, animationName, DetachOffset, (Transform)null);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void SnapPlayerToAnchor(Player player, Transform attachPoint)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			Quaternion rotation = attachPoint.rotation;
			Vector3 position = attachPoint.position;
			try
			{
				((Component)player).transform.position = position;
				((Component)player).transform.rotation = rotation;
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)((Character)player).m_body != (Object)null)
				{
					((Character)player).m_body.position = position;
					((Character)player).m_body.rotation = rotation;
					((Character)player).m_body.linearVelocity = Vector3.zero;
					((Character)player).m_body.angularVelocity = Vector3.zero;
				}
			}
			catch
			{
			}
		}

		private static void ApplyObserverAnimation(Player player, BRSDownedVisualState state, string wantedAnimation)
		{
			ZSyncAnimation val = (((Object)(object)player != (Object)null) ? ((Character)player).m_zanim : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string text = (HasBoolParameter(val, wantedAnimation) ? wantedAnimation : "emote_sit");
			if (HasBoolParameter(val, text))
			{
				if (!string.IsNullOrEmpty(state.ObserverAnimationName) && !string.Equals(state.ObserverAnimationName, text, StringComparison.Ordinal))
				{
					SetSyncedAnimatorBoolLocal(val, state.ObserverAnimationName, value: false);
					state.ObserverAnimationName = string.Empty;
				}
				SetSyncedAnimatorBoolLocal(val, text, value: true);
				state.ObserverAnimationName = text;
			}
		}

		private static void ClearObserverAnimation(Player player, BRSDownedVisualState state)
		{
			if (!string.IsNullOrEmpty(state.ObserverAnimationName))
			{
				ZSyncAnimation val = (((Object)(object)player != (Object)null) ? ((Character)player).m_zanim : null);
				if ((Object)(object)val != (Object)null)
				{
					SetSyncedAnimatorBoolLocal(val, state.ObserverAnimationName, value: false);
				}
				state.ObserverAnimationName = string.Empty;
			}
		}

		private static bool HasBoolParameter(ZSyncAnimation syncAnimation, string parameterName)
		{
			if ((Object)(object)syncAnimation == (Object)null || string.IsNullOrEmpty(parameterName))
			{
				return false;
			}
			try
			{
				return syncAnimation.HasParameter(parameterName, (AnimatorControllerParameterType)4);
			}
			catch
			{
				return false;
			}
		}

		private static void SetSyncedAnimatorBoolLocal(ZSyncAnimation syncAnimation, string parameterName, bool value)
		{
			if ((Object)(object)syncAnimation == (Object)null || string.IsNullOrEmpty(parameterName))
			{
				return;
			}
			try
			{
				if (HasBoolParameter(syncAnimation, parameterName))
				{
					syncAnimation.SetBool(parameterName, value);
				}
			}
			catch
			{
			}
		}

		private static void DetachPlayer(Player player, BRSDownedVisualState state)
		{
			if (state.Attached)
			{
				try
				{
					((Character)player).AttachStop();
				}
				catch
				{
				}
				state.Attached = false;
				state.AnimationName = string.Empty;
			}
		}

		private static void ClearLocalAttachBookkeeping(BRSDownedVisualState state)
		{
			state.Attached = false;
			state.AnimationName = string.Empty;
		}

		private static void DestroyAnchor(BRSDownedVisualState state)
		{
			state.AttachPoint = null;
			if ((Object)(object)state.AnchorRoot != (Object)null)
			{
				Object.Destroy((Object)(object)state.AnchorRoot);
				state.AnchorRoot = null;
			}
		}
	}
	internal sealed class BRSDownedVisualState : MonoBehaviour
	{
		internal GameObject AnchorRoot;

		internal Transform AttachPoint;

		internal bool Attached;

		internal string AnimationName = string.Empty;

		internal string ObserverAnimationName = string.Empty;

		private void OnDestroy()
		{
			if ((Object)(object)AnchorRoot != (Object)null)
			{
				Object.Destroy((Object)(object)AnchorRoot);
			}
		}
	}
	internal static class BRSReviveRules
	{
		internal static bool IsWithinReviveDistance(Player reviver, Player target)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)reviver == (Object)null || (Object)(object)target == (Object)null)
			{
				return false;
			}
			float num = Mathf.Max(0f, BRSConfig.ReviveDistance.Value);
			return Vector3.Distance(((Component)reviver).transform.position, ((Component)target).transform.position) <= num;
		}

		internal static bool IsPotionReviveOnCooldown(Player target, out float remainingMinutes)
		{
			remainingMinutes = 0f;
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			float num = Mathf.Max(0f, BRSConfig.ResurrectionCooldownMinutes.Value);
			if (num <= 0f)
			{
				return false;
			}
			long lastPotionReviveTicks = target.GetLastPotionReviveTicks();
			if (lastPotionReviveTicks <= 0)
			{
				return false;
			}
			DateTime dateTime;
			try
			{
				dateTime = new DateTime(lastPotionReviveTicks, DateTimeKind.Utc);
			}
			catch
			{
				return false;
			}
			double totalMinutes = (DateTime.UtcNow - dateTime).TotalMinutes;
			double num2 = (double)num - totalMinutes;
			if (num2 <= 0.0)
			{
				return false;
			}
			remainingMinutes = (float)num2;
			return true;
		}

		internal static bool CanFriendRevive(Player reviver, Player target, out string reason)
		{
			reason = string.Empty;
			if ((Object)(object)reviver == (Object)null)
			{
				reason = "$tag_brs_reason_no_reviver_bal";
				return false;
			}
			if ((Object)(object)target == (Object)null)
			{
				reason = "$tag_brs_reason_invalid_revive_target_bal";
				return false;
			}
			if ((Object)(object)reviver == (Object)(object)target)
			{
				reason = "$tag_brs_reason_use_self_revive_instead_bal";
				return false;
			}
			if (!target.IsDowned())
			{
				reason = "$tag_brs_reason_target_not_downed_bal";
				return false;
			}
			if (!IsWithinReviveDistance(reviver, target))
			{
				reason = "$msg_toofar";
				return false;
			}
			if (IsPotionReviveOnCooldown(target, out var remainingMinutes))
			{
				reason = BuildCooldownReason(remainingMinutes);
				return false;
			}
			if (BRSConfig.RequirePotionForFriendRevive.Value && !BRSItemUtils.TryFindResurrectionPotion(reviver, out var _))
			{
				reason = "$tag_brs_need_item_bal " + BRSConfig.ResurrectionPotionPrefabName.Value + ".";
				return false;
			}
			return true;
		}

		internal static bool CanSelfRevive(Player target, out string reason)
		{
			reason = string.Empty;
			if ((Object)(object)target == (Object)null)
			{
				reason = "$tag_brs_reason_invalid_player_bal";
				return false;
			}
			if (!target.IsDowned())
			{
				reason = "$tag_brs_reason_you_are_not_downed_bal";
				return false;
			}
			if (!BRSConfig.AllowSelfPotionRevive.Value)
			{
				reason = "$tag_brs_reason_self_revive_disabled_bal";
				return false;
			}
			if (IsPotionReviveOnCooldown(target, out var remainingMinutes))
			{
				reason = BuildCooldownReason(remainingMinutes);
				return false;
			}
			if (BRSConfig.RequireItemForSelfRevive.Value && !BRSItemUtils.TryFindResurrectionPotion(target, out var _))
			{
				reason = "$tag_brs_need_item_bal " + BRSConfig.ResurrectionPotionPrefabName.Value + " $tag_brs_in_your_inventory_bal";
				return false;
			}
			return true;
		}

		internal static void MarkPotionReviveUsed(Player target)
		{
			if (!((Object)(object)target == (Object)null))
			{
				target.SetLastPotionReviveTicks(DateTime.UtcNow.Ticks);
				BRSStatusEffectManager.ApplyCooldownVisual(target);
			}
		}

		internal static string BuildCooldownReason(float remainingMinutes)
		{
			return "$tag_brs_reason_soul_unstable_bal " + Mathf.Max(0f, remainingMinutes).ToString("0.0") + " $tag_brs_reason_cooldown_left_bal";
		}
	}
	internal static class BRSStatusEffectManager
	{
		private const string SourceEffectName = "SoftDeath";

		private const string CooldownEffectObjectName = "RessurectCooldown_bal";

		private const string CooldownDisplayName = "$tag_brs_ressurection_sickness_bal";

		private const string CooldownTooltip = "$tag_brs_ressurection_sickness_tooltip_bal";

		private static StatusEffect _cachedEffect;

		internal static void ResetCachedEffect()
		{
			_cachedEffect = null;
		}

		internal static StatusEffect EnsureCooldownEffect()
		{
			if ((Object)(object)_cachedEffect != (Object)null)
			{
				return _cachedEffect;
			}
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			StatusEffect val = FindEffectByObjectName("RessurectCooldown_bal");
			if ((Object)(object)val != (Object)null)
			{
				_cachedEffect = val;
				return _cachedEffect;
			}
			StatusEffect val2 = FindEffectByObjectName("SoftDeath");
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			StatusEffect val3 = Object.Instantiate<StatusEffect>(val2);
			((Object)val3).name = "RessurectCooldown_bal";
			val3.m_name = "$tag_brs_ressurection_sickness_bal";
			val3.m_tooltip = "$tag_brs_ressurection_sickness_tooltip_bal";
			val3.m_ttl = Mathf.Max(1f, BRSConfig.ResurrectionCooldownMinutes.Value * 60f);
			if (ObjectDB.instance.m_StatusEffects == null)
			{
				ObjectDB.instance.m_StatusEffects = new List<StatusEffect>();
			}
			ObjectDB.instance.m_StatusEffects.Add(val3);
			_cachedEffect = val3;
			return _cachedEffect;
		}

		internal static void ApplyCooldownVisual(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			EnsureCooldownEffect();
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null)
			{
				return;
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode("RessurectCooldown_bal");
			StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode);
			if ((Object)(object)statusEffect != (Object)null)
			{
				sEMan.RemoveStatusEffect(stableHashCode, true);
			}
			sEMan.AddStatusEffect(stableHashCode, true, 0, 0f, (short)(-1));
			StatusEffect statusEffect2 = sEMan.GetStatusEffect(stableHashCode);
			if ((Object)(object)statusEffect2 != (Object)null)
			{
				float num = player.GetRemainingPotionReviveCooldownMinutes();
				if (num <= 0f)
				{
					num = Mathf.Max(0f, BRSConfig.ResurrectionCooldownMinutes.Value);
				}
				statusEffect2.m_ttl = Mathf.Max(1f, num * 60f);
			}
		}

		private static StatusEffect FindEffectByObjectName(string objectName)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_StatusEffects == null)
			{
				return null;
			}
			for (int i = 0; i < ObjectDB.instance.m_StatusEffects.Count; i++)
			{
				StatusEffect val = ObjectDB.instance.m_StatusEffects[i];
				if (!((Object)(object)val == (Object)null) && ((Object)val).name == objectName)
				{
					return val;
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class BRSObjectDBAwakeStatusEffectPatch
	{
		private static void Postfix()
		{
			BRSStatusEffectManager.ResetCachedEffect();
			BRSStatusEffectManager.EnsureCooldownEffect();
			BRSHudController.ClearCachedItemDisplayName();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class BRSObjectDBCopyOtherDBStatusEffectPatch
	{
		private static void Postfix()
		{
			BRSStatusEffectManager.ResetCachedEffect();
			BRSStatusEffectManager.EnsureCooldownEffect();
			BRSHudController.ClearCachedItemDisplayName();
		}
	}
	public class JsonLoader
	{
		public string defaultPath = string.Empty;

		public void loadJson()
		{
			LoadTranslations();
			justDefaultPath();
		}

		public void justDefaultPath()
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondSecondChance-translation/");
			defaultPath = text;
		}

		public void createDefaultPath()
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondSecondChance-translation/");
			if (!Directory.Exists(text))
			{
				CreateFolder(text);
			}
			else
			{
				Debug.Log((object)("BalrondSecondChance: Folder already exists: " + text));
			}
			defaultPath = text;
		}

		private string[] jsonFilePath(string folderName, string extension)
		{
			string configPath = Paths.ConfigPath;
			string text = Path.Combine(configPath, "BalrondSecondChance-translation/");
			if (!Directory.Exists(text))
			{
				CreateFolder(text);
			}
			else
			{
				Debug.Log((object)("BalrondSecondChance: Folder already exists: " + text));
			}
			string[] files = Directory.GetFiles(text, extension);
			Debug.Log((object)("BalrondSecondChance:" + folderName + " Json Files Found: " + files.Length));
			return files;
		}

		private static void CreateFolder(string path)
		{
			try
			{
				Directory.CreateDirectory(path);
				Debug.Log((object)"BalrondSecondChance: Folder created successfully.");
			}
			catch (Exception ex)
			{
				Debug.Log((object)("BalrondSecondChance: Error creating folder: " + ex.Message));
			}
		}

		private void LoadTranslations()
		{
			int num = 0;
			string[] array = jsonFilePath("Translation", "*.json");
			foreach (string text in array)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				string json = File.ReadAllText(text);
				JsonData jsonData = JsonMapper.ToObject(json);
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				foreach (string key in jsonData.Keys)
				{
					dictionary[key] = jsonData[key].ToString();
				}
				if (dictionary != null)
				{
					BalrondTranslator.translations.Add(fileNameWithoutExtension, dictionary);
					Debug.Log((object)("BalrondSecondChance: Json Files Language: " + fileNameWithoutExtension));
					num++;
				}
				else
				{
					Debug.LogError((object)("BalrondSecondChance: Loading FAILED file: " + text));
				}
			}
			Debug.Log((object)("BalrondSecondChance: Translation JsonFiles Loaded: " + num));
		}
	}
	[BepInPlugin("balrond.astafaraios.BalrondSecondChance", "BalrondSecondChance", "1.0.4")]
	public class Launch : BaseUnityPlugin
	{
		private readonly Harmony harmony = new Harmony("balrond.astafaraios.BalrondSecondChance");

		public const string PluginGUID = "balrond.astafaraios.BalrondSecondChance";

		public const string PluginName = "BalrondSecondChance";

		public const string PluginVersion = "1.0.4";

		public static JsonLoader jsonLoader = new JsonLoader();

		internal static Launch Instance;

		internal static Harmony Harmony;

		internal static bool AllowOriginalDeath;

		internal static readonly ConfigSync configSync = new ConfigSync("balrond.astafaraios.BalrondSecondChance")
		{
			DisplayName = "BalrondSecondChance",
			CurrentVersion = "1.0.4",
			MinimumRequiredVersion = "1.0.4"
		};

		internal static Launch _self;

		internal ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
			SyncedConfigEntry<T> syncedConfigEntry = configSync.AddConfigEntry<T>(val);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val;
		}

		internal ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		private void Awake()
		{
			Instance = this;
			_self = this;
			AudioRoutingService.ConfigureLogging(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			}, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)message);
			});
			AudioRoutingService.DebugLogging = false;
			jsonLoader.loadJson();
			BRSConfig.Init(this);
			if (BRSConfig.LockConfiguration != null)
			{
				configSync.AddLockingConfigEntry<bool>(BRSConfig.LockConfiguration);
			}
			Harmony = harmony;
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"BalrondSecondChance loaded.");
		}

		private void OnDestroy()
		{
			Harmony obj = Harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch]
	internal static class TranslationPatches
	{
		[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
		private class FejdStartup_SetupGUI
		{
			private static void Postfix()
			{
				string selectedLanguage = Localization.instance.GetSelectedLanguage();
				Dictionary<string, string> translations = GetTranslations(selectedLanguage);
				AddTranslations(translations);
			}
		}

		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(Localization), "SetupLanguage")]
		private class Translation_SetupLanguage
		{
			private static void Prefix(Localization __instance, string language)
			{
				Dictionary<string, string> translations = GetTranslations(language);
				AddTranslations(translations, __instance);
			}
		}

		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(Localization), "LoadCSV")]
		private class Translation_LoadCSV
		{
			private static void Prefix(Localization __instance, string language)
			{
				Dictionary<string, string> translations = GetTranslations(language);
				AddTranslations(translations, __instance);
			}
		}

		private static Dictionary<string, string> GetTranslations(string language)
		{
			Dictionary<string, string> result = BalrondTranslator.getLanguage("English");
			if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase))
			{
				Dictionary<string, string> language2 = BalrondTranslator.getLanguage(language);
				if (language2 != null)
				{
					result = language2;
				}
				else
				{
					Debug.Log((object)("BalrondSecondChance: Did not find translation file for '" + language + "', loading English"));
				}
			}
			return result;
		}

		private static void AddTranslations(Dictionary<string, string> translations, Localization localizationInstance = null)
		{
			if (translations == null)
			{
				Debug.LogWarning((object)"BalrondSecondChance: No translation file found!");
				return;
			}
			if (localizationInstance != null)
			{
				foreach (KeyValuePair<string, string> translation in translations)
				{
					localizationInstance.AddWord(translation.Key, translation.Value);
				}
				return;
			}
			foreach (KeyValuePair<string, string> translation2 in translations)
			{
				Localization.instance.AddWord(translation2.Key, translation2.Value);
			}
		}
	}
}
namespace Balrond.Shared
{
	public static class AudioRoutingService
	{
		public enum AudioRoute
		{
			SFX,
			Ambient,
			Music,
			Master
		}

		private enum TargetKind
		{
			Route,
			NamedRuntimeGroup
		}

		private sealed class AudioTarget
		{
			public TargetKind Kind;

			public AudioRoute Route;

			public string RuntimeGroupName;

			public string Reason;
		}

		private sealed class AudioBinding
		{
			public AudioSource Source;

			public AudioTarget Target;
		}

		private sealed class ProcessedNode
		{
			public GameObject Root;

			public readonly List<AudioBinding> AudioBindings = new List<AudioBinding>();

			public readonly List<GameObject> ReferencedRoots = new List<GameObject>();

			public readonly HashSet<GameObject> ReferencedRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);
		}

		private sealed class ExplicitOverride
		{
			public bool UsesNamedGroup;

			public AudioRoute Route;

			public string RuntimeGroupName;
		}

		private sealed class RuntimeMixerCatalog
		{
			private readonly Dictionary<string, AudioMixerGroup> _groupsByName = new Dictionary<string, AudioMixerGroup>(StringComparer.OrdinalIgnoreCase);

			private AudioMixer _mixer;

			private AudioMixerGroup _master;

			private AudioMixerGroup _ambient;

			private AudioMixerGroup _music;

			private AudioMixerGroup _sfx;

			public bool IsReady => (Object)(object)_master != (Object)null || (Object)(object)_ambient != (Object)null || (Object)(object)_music != (Object)null || (Object)(object)_sfx != (Object)null;

			public void Rebuild(AudioMan audioMan)
			{
				_groupsByName.Clear();
				_mixer = null;
				_master = null;
				_ambient = null;
				_music = null;
				_sfx = null;
				if ((Object)(object)audioMan == (Object)null)
				{
					return;
				}
				_master = ReadAudioManGroup(audioMan, "m_masterMixer");
				_ambient = ReadAudioManGroup(audioMan, "m_ambientMixer");
				_music = ReadAudioManGroup(audioMan, "m_musicMixer");
				_mixer = ReadAudioManMixer(audioMan, "m_masterMixer") ?? GetMixer(_master) ?? GetMixer(_ambient) ?? GetMixer(_music);
				AddGroup(_master);
				AddGroup(_ambient);
				AddGroup(_music);
				if ((Object)(object)_mixer != (Object)null)
				{
					try
					{
						AudioMixerGroup[] array = _mixer.FindMatchingGroups(string.Empty);
						if (array != null)
						{
							for (int i = 0; i < array.Length; i++)
							{
								AddGroup(array[i]);
							}
						}
					}
					catch (Exception ex)
					{
						Warn("Could not enumerate runtime AudioMixer groups: " + ex.Message);
					}
				}
				if ((Object)(object)_master == (Object)null)
				{
					_master = FindNamedGroup("Master") ?? QueryExactGroup("Master");
				}
				if ((Object)(object)_ambient == (Object)null)
				{
					_ambient = FindNamedGroup("Ambient") ?? QueryExactGroup("Ambient");
				}
				if ((Object)(object)_music == (Object)null)
				{
					_music = FindNamedGroup("Music") ?? QueryExactGroup("Music");
				}
				_sfx = FindNamedGroup("SFX");
				if ((Object)(object)_sfx == (Object)null)
				{
					_sfx = QueryExactGroup("SFX");
				}
				if ((Object)(object)_sfx == (Object)null)
				{
					AudioMixerGroup val = FindNamedGroup("Effects");
					if ((Object)(object)val == (Object)null)
					{
						val = QueryExactGroup("Effects");
					}
					_sfx = val;
				}
				if ((Object)(object)_sfx == (Object)null)
				{
					_sfx = _master;
					WarnOnce("sfx-group-fallback", "Runtime SFX mixer group was not found. Falling back to the AudioMan master output group.");
				}
			}

			public AudioMixerGroup Resolve(AudioTarget target)
			{
				if (target == null)
				{
					return ResolveRoute(AudioRoute.SFX);
				}
				if (target.Kind == TargetKind.NamedRuntimeGroup)
				{
					AudioMixerGroup val = FindNamedGroup(target.RuntimeGroupName);
					if ((Object)(object)val == (Object)null)
					{
						val = QueryExactGroup(target.RuntimeGroupName);
					}
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
					return ResolveRoute(AudioRoute.SFX);
				}
				return ResolveRoute(target.Route);
			}

			public bool HasNamedGroup(string groupName)
			{
				if (string.IsNullOrEmpty(groupName))
				{
					return false;
				}
				return (Object)(object)FindNamedGroup(groupName) != (Object)null || (Object)(object)QueryExactGroup(groupName) != (Object)null;
			}

			private AudioMixerGroup ResolveRoute(AudioRoute route)
			{
				return (AudioMixerGroup)(route switch
				{
					AudioRoute.Ambient => _ambient ?? _sfx ?? _master, 
					AudioRoute.Music => _music ?? _sfx ?? _master, 
					AudioRoute.Master => _master ?? _sfx ?? _ambient ?? _music, 
					_ => _sfx ?? _master ?? _ambient ?? _music, 
				});
			}

			private AudioMixerGroup FindNamedGroup(string groupName)
			{
				if (!string.IsNullOrEmpty(groupName) && _groupsByName.TryGetValue(groupName, out var value))
				{
					return value;
				}
				return null;
			}

			private AudioMixerGroup QueryExactGroup(string groupName)
			{
				if ((Object)(object)_mixer == (Object)null || string.IsNullOrEmpty(groupName))
				{
					return null;
				}
				try
				{
					AudioMixerGroup[] array = _mixer.FindMatchingGroups(groupName);
					if (array == null)
					{
						return null;
					}
					foreach (AudioMixerGroup val in array)
					{
						AddGroup(val);
						if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, groupName, StringComparison.OrdinalIgnoreCase))
						{
							return val;
						}
					}
				}
				catch (Exception ex)
				{
					WarnOnce("query-group-" + groupName, "Could not query runtime AudioMixer group '" + groupName + "': " + ex.Message);
				}
				return null;
			}

			private void AddGroup(AudioMixerGroup group)
			{
				if (!((Object)(object)group == (Object)null) && !string.IsNullOrEmpty(((Object)group).name) && !_groupsByName.ContainsKey(((Object)group).name))
				{
					_groupsByName.Add(((Object)group).name, group);
				}
			}

			private static AudioMixer GetMixer(AudioMixerGroup group)
			{
				try
				{
					return ((Object)(object)group != (Object)null) ? group.audioMixer : null;
				}
				catch
				{
					return null;
				}
			}

			private static AudioMixer ReadAudioManMixer(AudioMan audioMan, string fieldName)
			{
				try
				{
					FieldInfo field = typeof(AudioMan).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null)
					{
						return null;
					}
					object value = field.GetValue(audioMan);
					AudioMixer val = (AudioMixer)((value is AudioMixer) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
					AudioMixerGroup val2 = (AudioMixerGroup)((value is AudioMixerGroup) ? value : null);
					if ((Object)(object)val2 != (Object)null)
					{
						return GetMixer(val2);
					}
					AudioSource val3 = (AudioSource)((value is AudioSource) ? value : null);
					if ((Object)(object)val3 != (Object)null)
					{
						return GetMixer(val3.outputAudioMixerGroup);
					}
				}
				catch (Exception ex)
				{
					WarnOnce("audioman-mixer-" + fieldName, "Could not read runtime AudioMixer from AudioMan." + fieldName + ": " + ex.Message);
				}
				return null;
			}

			private static AudioMixerGroup ReadAudioManGroup(AudioMan audioMan, string fieldName)
			{
				try
				{
					FieldInfo field = typeof(AudioMan).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null)
					{
						return null;
					}
					object value = field.GetValue(audioMan);
					AudioMixerGroup val = (AudioMixerGroup)((value is AudioMixerGroup) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
					AudioSource val2 = (AudioSource)((value is AudioSource) ? value : null);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2.outputAudioMixerGroup;
					}
				}
				catch (Exception ex)
				{
					WarnOnce("audioman-field-" + fieldName, "Could not read AudioMan." + fieldName + ": " + ex.Message);
				}
				return null;
			}
		}

		private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class
		{
			public static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>();

			public bool Equals(T x, T y)
			{
				return x == y;
			}

			public int GetHashCode(T obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		private const int MaxManagedDepth = 24;

		private const int MaxCollectionEntries = 4096;

		private static readonly RuntimeMixerCatalog MixerCatalog = new RuntimeMixerCatalog();

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

		private static readonly Dictionary<GameObject, ProcessedNode> ProcessedNodes = new Dictionary<GameObject, ProcessedNode>(ReferenceComparer<GameObject>.Instance);

		private static readonly Dictionary<AudioSource, AudioBinding> SourceBindings = new Dictionary<AudioSource, AudioBinding>(ReferenceComparer<AudioSource>.Instance);

		private static readonly Dictionary<Type, FieldInfo[]> TraversalFields = new Dictionary<Type, FieldInfo[]>();

		private static readonly HashSet<GameObject> RegisteredRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);

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

		private static readonly HashSet<GameObject> ActiveProcessing = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance);

		private static readonly HashSet<string> WarnedKeys = new HashSet<string>(StringComparer.Ordinal);

		private static readonly HashSet<string> ErrorKeys = new HashSet<string>(StringComparer.Ordinal);

		private static Action<string> _infoLogger;

		private static Action<string> _warningLogger;

		private static Action<string> _errorLogger;

		public static bool DebugLogging { get; set; }

		public static void ConfigureLogging(Action<string> infoLogger, Action<string> warningLogger, Action<string> errorLogger)
		{
			_infoLogger = infoLogger;
			_warningLogger = warningLogger;
			_errorLogger = errorLogger;
		}

		public static void SetOverride(string gameObjectName, AudioRoute route)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName))
			{
				Overrides[gameObjectName] = new ExplicitOverride
				{
					UsesNamedGroup = false,
					Route = route,
					RuntimeGroupName = null
				};
				InvalidateClassificationCaches();
			}
		}

		public static void SetOverrides(AudioRoute route, params string[] gameObjectNames)
		{
			if (gameObjectNames == null)
			{
				return;
			}
			bool flag = false;
			foreach (string text in gameObjectNames)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					Overrides[text] = new ExplicitOverride
					{
						UsesNamedGroup = false,
						Route = route,
						RuntimeGroupName = null
					};
					flag = true;
				}
			}
			if (flag)
			{
				InvalidateClassificationCaches();
			}
		}

		public static void SetMixerGroupOverride(string gameObjectName, string runtimeMixerGroupName)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName) && !string.IsNullOrWhiteSpace(runtimeMixerGroupName))
			{
				Overrides[gameObjectName] = new ExplicitOverride
				{
					UsesNamedGroup = true,
					Route = AudioRoute.SFX,
					RuntimeGroupName = runtimeMixerGroupName
				};
				InvalidateClassificationCaches();
			}
		}

		public static void RemoveOverride(string gameObjectName)
		{
			if (!string.IsNullOrWhiteSpace(gameObjectName) && Overrides.Remove(gameObjectName))
			{
				InvalidateClassificationCaches();
			}
		}

		public static void Process(GameObject root)
		{
			if (IsUsable((Object)(object)root))
			{
				RegisterRoot(root);
				if (MixerCatalog.IsReady)
				{
					ProcessRoot(root);
				}
			}
		}

		public static void Process(IEnumerable<GameObject> roots)
		{
			if (roots == null)
			{
				return;
			}
			foreach (GameObject root in roots)
			{
				if (IsUsable((Object)(object)root))
				{
					RegisterRoot(root);
				}
			}
			if (!MixerCatalog.IsReady)
			{
				return;
			}
			for (int i = 0; i < RegisteredRoots.Count; i++)
			{
				GameObject val = RegisteredRoots[i];
				if (IsUsable((Object)(object)val))
				{
					ProcessRoot(val);
				}
			}
		}

		public static void OnAudioManAwake(AudioMan audioMan)
		{
			if ((Object)(object)audioMan == (Object)null)
			{
				return;
			}
			MixerCatalog.Rebuild(audioMan);
			if (!MixerCatalog.IsReady)
			{
				ErrorOnce("runtime-mixer-unresolved", "AudioMan was available, but no usable runtime mixer group could be resolved.");
				return;
			}
			PruneRegisteredRoots();
			ProcessedNodes.Clear();
			SourceBindings.Clear();
			ActiveProcessing.Clear();
			for (int i = 0; i < RegisteredRoots.Count; i++)
			{
				GameObject val = RegisteredRoots[i];
				if (IsUsable((Object)(object)val))
				{
					ProcessRoot(val);
				}
			}
		}

		private static void RegisterRoot(GameObject root)
		{
			if (RegisteredRootSet.Add(root))
			{
				RegisteredRoots.Add(root);
			}
		}

		private static void ProcessRoot(GameObject root)
		{
			if (!MixerCatalog.IsReady || !IsUsable((Object)(object)root))
			{
				return;
			}
			if (ProcessedNodes.TryGetValue(root, out var value))
			{
				ValidateNodeGraph(value, new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance));
			}
			else
			{
				if (!ActiveProcessing.Add(root))
				{
					return;
				}
				ProcessedNode processedNode = new ProcessedNode
				{
					Root = root
				};
				ProcessedNodes[root] = processedNode;
				try
				{
					ScanAudioSources(root, processedNode);
				}
				catch (Exception ex)
				{
					ErrorOnce("process-root-" + ObjectKey((Object)(object)root), "Failed to process audio sources for '" + SafeName((Object)(object)root) + "': " + ex);
				}
				finally
				{
					ActiveProcessing.Remove(root);
				}
			}
		}

		private static void ScanAudioSources(GameObject root, ProcessedNode node)
		{
			AudioSource[] componentsInChildren;
			try
			{
				componentsInChildren = root.GetComponentsInChildren<AudioSource>(true);
			}
			catch (Exception ex)
			{
				WarnOnce("enumerate-audiosources-" + ObjectKey((Object)(object)root), "Could not enumerate AudioSources under '" + SafeName((Object)(object)root) + "': " + ex.Message);
				return;
			}
			if (componentsInChildren == null)
			{
				return;
			}
			foreach (AudioSource val in componentsInChildren)
			{
				if (IsUsable((Object)(object)val))
				{
					if (!SourceBindings.TryGetValue(val, out var value))
					{
						value = new AudioBinding
						{
							Source = val,
							Target = ClassifySource(root, val)
						};
						SourceBindings[val] = value;
					}
					if (!node.AudioBindings.Contains(value))
					{
						node.AudioBindings.Add(value);
					}
					ApplyBinding(value, root);
				}
			}
		}

		private static void ScanBehaviourReferences(GameObject root, ProcessedNode node)
		{
			MonoBehaviour[] componentsInChildren;
			try
			{
				componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true);
			}
			catch (Exception ex)
			{
				Warn("Could not enumerate MonoBehaviours under '" + SafeName((Object)(object)root) + "': " + ex.Message);
				return;
			}
			if (componentsInChildren == null)
			{
				return;
			}
			HashSet<object> managedVisited = new HashSet<object>(ReferenceComparer<object>.Instance);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (IsUsable((Object)(object)val) && !(val is ZSFX))
				{
					TraverseManagedObject(val, root, node, managedVisited, 0, isComponentRoot: true);
				}
			}
		}

		private static void TraverseManagedObject(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth, bool isComponentRoot)
		{
			if (value == null || depth > 24)
			{
				return;
			}
			Type type = value.GetType();
			if (!type.IsValueType && !managedVisited.Add(value))
			{
				return;
			}
			FieldInfo[] traversalFields = GetTraversalFields(type, isComponentRoot);
			foreach (FieldInfo fieldInfo in traversalFields)
			{
				object value2;
				try
				{
					value2 = fieldInfo.GetValue(value);
				}
				catch
				{
					continue;
				}
				TraverseValue(value2, ownerRoot, node, managedVisited, depth + 1);
			}
		}

		private static void TraverseValue(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth)
		{
			if (value == null || depth > 24)
			{
				return;
			}
			EffectList val = (EffectList)((value is EffectList) ? value : null);
			if (val != null)
			{
				TraverseEffectList(val, ownerRoot, node);
				return;
			}
			GameObject val2 = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val2 != (Object)null)
			{
				HandleReferencedGameObject(val2, ownerRoot, node);
				return;
			}
			Component val3 = (Component)((value is Component) ? value : null);
			if ((Object)(object)val3 != (Object)null)
			{
				if (IsUsable((Object)(object)val3))
				{
					HandleReferencedGameObject(val3.gameObject