Decompiled source of RoguelikeSkillTreeVisualizer v0.3.5

plugins/RoguelikeSkillTreeVisualizer/RoguelikeSkillTreeVisualizer.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Burst2Flame;
using Burst2Flame.Observable;
using Gumatos.RealmApi;
using Gumatos.RealmApi.UI;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("RoguelikeSkillTreeVisualizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds a 'Skills' button (run HUD bottom bar, Remove Skill Trees modal and the level-up window) that opens the game's native skill tree read-only for the character in view, plus the shortcut F10. No skill point is spent; no gameplay change.")]
[assembly: AssemblyFileVersion("0.3.5.0")]
[assembly: AssemblyInformationalVersion("0.3.5")]
[assembly: AssemblyProduct("RoguelikeSkillTreeVisualizer")]
[assembly: AssemblyTitle("RoguelikeSkillTreeVisualizer")]
[assembly: AssemblyVersion("0.3.5.0")]
namespace RoguelikeSkillTreeVisualizer;

internal static class LevelUpWindowButton
{
	internal const string ButtonName = "RstvLevelUpSkillsButton";

	internal const string Label = "Skills";

	internal const string LabelChildName = "RstvLevelUpLabel";

	internal const float Lado = 44f;

	private const float Margem = 8f;

	private static GameObject _janela;

	private static GameObject _button;

	private static string _reasonLogged;

	internal static void Ensure(RoguelikeManager rok)
	{
		try
		{
			EnsureInternal(rok);
			Refresh();
		}
		catch (Exception ex)
		{
			Fail("excecao durante a injecao (" + ex.GetType().Name + "): " + ex.Message, aviso: true);
			Plugin.Log.LogError((object)("RSTV: falha ao injetar o botao da janela do level-up: " + ex));
		}
	}

	private static void EnsureInternal(RoguelikeManager rok)
	{
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Expected O, but got Unknown
		if ((Object)(object)rok == (Object)null)
		{
			Fail("a instancia do RoguelikeManager chegou nula", aviso: false);
			return;
		}
		if (!Plugin.RunBotaoLigado)
		{
			Fail(Plugin.MotivoDoBotaoDaRunDesligado, aviso: false);
			return;
		}
		GameObject skillSelectWindow = rok.SkillSelectWindow;
		if ((Object)(object)skillSelectWindow == (Object)null)
		{
			Fail("RoguelikeManager.SkillSelectWindow esta nulo (o campo do prefab nao carregou)", aviso: true);
		}
		else if (!skillSelectWindow.activeSelf)
		{
			Fail("a janela do level-up (" + ((Object)skillSelectWindow).name + ") NAO esta ativa — nao injetar fora de hora", aviso: false);
		}
		else
		{
			if ((Object)(object)_janela == (Object)(object)skillSelectWindow && (Object)(object)_button != (Object)null)
			{
				return;
			}
			if ((Object)(object)_button != (Object)null && (Object)(object)_janela != (Object)(object)skillSelectWindow)
			{
				Object.Destroy((Object)(object)_button);
				_button = null;
			}
			Button acceptButton = rok.AcceptButton;
			if ((Object)(object)acceptButton == (Object)null)
			{
				Fail("RoguelikeManager.AcceptButton esta nulo — sem molde nao ha clone", aviso: true);
				return;
			}
			if ((Object)(object)((Component)acceptButton).transform == (Object)null)
			{
				Fail("o 'Accept Btn' molde esta sem Transform", aviso: true);
				return;
			}
			RectTransform component = skillSelectWindow.GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null)
			{
				Fail("o SkillSelectWindow esta sem RectTransform (nao da para ancorar o botao)", aviso: true);
				return;
			}
			Transform parent = ((Component)acceptButton).transform.parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				Fail("o pai do 'Accept Btn' (o painel Content) nao e RectTransform — sem ele nao sei onde fica o canto do painel", aviso: true);
				return;
			}
			Transform val2 = skillSelectWindow.transform.Find("RstvLevelUpSkillsButton");
			GameObject val3;
			if ((Object)(object)val2 != (Object)null)
			{
				val3 = ((Component)val2).gameObject;
			}
			else
			{
				val3 = Object.Instantiate<GameObject>(((Component)acceptButton).gameObject, skillSelectWindow.transform);
				((Object)val3).name = "RstvLevelUpSkillsButton";
			}
			RectTransform component2 = val3.GetComponent<RectTransform>();
			if ((Object)(object)component2 == (Object)null)
			{
				Fail("o clone do 'Accept Btn' ficou sem RectTransform", aviso: true);
				return;
			}
			NativeUiHelpers.DisableLocalizers(val3);
			EnsureLabel(val3);
			AnchorToContentCorner(component2, component, val);
			Button component3 = val3.GetComponent<Button>();
			if ((Object)(object)component3 == (Object)null)
			{
				Fail("o clone do 'Accept Btn' ficou sem componente Button", aviso: true);
				return;
			}
			NativeUiHelpers.ClearClickListeners(component3);
			((UnityEvent)component3.onClick).AddListener(new UnityAction(RunButton.OpenForTarget));
			((Selectable)component3).interactable = true;
			_janela = skillSelectWindow;
			_button = val3;
			_reasonLogged = null;
			Plugin.Log.LogInfo((object)("RSTV-11b: botao 'Skills' injetado DENTRO da janela do level-up (" + ((Object)skillSelectWindow).name + "), filho direto (fora do Content, sem reflow); " + 44f.ToString("0.#") + "x" + 44f.ToString("0.#") + " px no canto superior direito do painel Content; onClick limpo -> RunButton.OpenForTarget()."));
		}
	}

	internal static void Refresh()
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Invalid comparison between Unknown and I4
		try
		{
			GameObject button = _button;
			GameObject janela = _janela;
			if ((Object)(object)button == (Object)null || (Object)(object)janela == (Object)null)
			{
				return;
			}
			RoguelikeManager instance = RoguelikeManager.Instance;
			bool flag = (Object)(object)instance != (Object)null && (Object)(object)instance.SkillSelectWindow == (Object)(object)janela && janela.activeSelf && (int)instance.CurLevelUpStage == 0;
			if (button.activeSelf == flag)
			{
				return;
			}
			button.SetActive(flag);
			if (flag)
			{
				RectTransform component = button.GetComponent<RectTransform>();
				RectTransform component2 = janela.GetComponent<RectTransform>();
				Button val = (((Object)(object)instance != (Object)null) ? instance.AcceptButton : null);
				RectTransform val2 = (RectTransform)(((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
				if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && (Object)(object)val2 != (Object)null)
				{
					AnchorToContentCorner(component, component2, val2);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV: falha ao sincronizar o botao da janela do level-up (" + ex.GetType().Name + "): " + ex.Message));
		}
	}

	private static void EnsureLabel(GameObject clone)
	{
		TextMeshProUGUI componentInChildren = clone.GetComponentInChildren<TextMeshProUGUI>(true);
		if ((Object)(object)componentInChildren == (Object)null)
		{
			Fail("o clone do 'Accept Btn' ficou SEM TextMeshProUGUI — botao sem rotulo", aviso: true);
		}
		else
		{
			((TMP_Text)componentInChildren).text = OptionsManager.Localize("Skills", (LanguageGender)0, (Gender)2);
		}
	}

	internal static void AnchorToContentCorner(RectTransform rt, RectTransform janela, RectTransform content)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		Canvas.ForceUpdateCanvases();
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		content.GetWorldCorners(array);
		Vector3 val = ((Transform)janela).InverseTransformPoint(array[2]);
		Rect rect = janela.rect;
		float num = (0f - ((Rect)(ref rect)).width) * janela.pivot.x;
		rect = janela.rect;
		Vector2 val2 = default(Vector2);
		((Vector2)(ref val2))..ctor(num, (0f - ((Rect)(ref rect)).height) * janela.pivot.y);
		rt.anchorMin = Vector2.zero;
		rt.anchorMax = Vector2.zero;
		rt.pivot = new Vector2(1f, 1f);
		((Transform)rt).localScale = Vector3.one;
		((Transform)rt).localRotation = Quaternion.identity;
		rt.sizeDelta = new Vector2(44f, 44f);
		Vector2 val3 = default(Vector2);
		((Vector2)(ref val3))..ctor(val.x - 8f, val.y - 8f);
		rt.anchoredPosition = val3 - val2;
		((Transform)rt).SetAsLastSibling();
	}

	private static void Fail(string motivo, bool aviso)
	{
		if (!(_reasonLogged == motivo))
		{
			_reasonLogged = motivo;
			string text = "RSTV DIAG: botao 'Skills' da janela do level-up NAO injetado — " + motivo + ".";
			if (aviso)
			{
				Plugin.Log.LogWarning((object)text);
			}
			else
			{
				Plugin.Log.LogInfo((object)text);
			}
		}
	}
}
internal static class NativeUiHelpers
{
	internal static string DescribeGroup(Transform parent)
	{
		if ((Object)(object)parent == (Object)null)
		{
			return "nenhum";
		}
		LayoutGroup component = ((Component)parent).GetComponent<LayoutGroup>();
		if (!((Object)(object)component == (Object)null))
		{
			return ((object)component).GetType().Name;
		}
		return "nenhum";
	}

	internal static void DisableLocalizers(GameObject root)
	{
		Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
		foreach (Component val in componentsInChildren)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			string name = ((object)val).GetType().Name;
			if (name.IndexOf("Localiz", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Localize", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null);
				if ((Object)(object)val2 != (Object)null)
				{
					val2.enabled = false;
					Plugin.Log.LogInfo((object)("RSTV: localizador '" + name + "' desativado no botao clonado."));
				}
			}
		}
	}

	internal static void ClearClickListeners(Button button)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		if (!((Object)(object)button == (Object)null))
		{
			button.onClick = new ButtonClickedEvent();
		}
	}
}
internal static class PartyTargets
{
	private static readonly List<Character> AdditionOrder = new List<Character>();

	internal static bool IsLocal(Character character)
	{
		try
		{
			return character != null && character.Owned;
		}
		catch (Exception)
		{
			return false;
		}
	}

	internal static void NoteAddition(Character character)
	{
		if (IsLocal(character))
		{
			AdditionOrder.RemoveAll((Character c) => c == null || c == character || !IsLocal(c));
			AdditionOrder.Add(character);
		}
	}

	internal static void NoteRemoval(Character character)
	{
		if (character != null)
		{
			AdditionOrder.Remove(character);
		}
	}

	internal static int LocalPartyCount()
	{
		try
		{
			return NetworkingManager.Instance.MyPartyCharactersUnaccepted?.Count ?? 0;
		}
		catch (Exception)
		{
			return 0;
		}
	}

	internal static Character Resolve()
	{
		try
		{
			List<Character> myPartyCharactersUnaccepted = NetworkingManager.Instance.MyPartyCharactersUnaccepted;
			if (myPartyCharactersUnaccepted == null || myPartyCharactersUnaccepted.Count == 0)
			{
				return null;
			}
			for (int num = AdditionOrder.Count - 1; num >= 0; num--)
			{
				Character val = AdditionOrder[num];
				if (val != null && myPartyCharactersUnaccepted.Contains(val))
				{
					return val;
				}
			}
			return myPartyCharactersUnaccepted[myPartyCharactersUnaccepted.Count - 1];
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao resolver o personagem da party: " + ex.Message));
			return null;
		}
	}
}
[HarmonyPatch(typeof(CharacterChoiceManager), "ToggleSelectedCharacter", new Type[] { typeof(int) })]
internal static class CharacterChoiceTogglePatch
{
	private static Character _subject;

	private static bool _firstCallLogged;

	[HarmonyPrefix]
	private static void Prefix(CharacterChoiceManager __instance)
	{
		try
		{
			_subject = (((Object)(object)__instance != (Object)null && (Object)(object)__instance.selectedCharacterChoiceItem != (Object)null) ? __instance.selectedCharacterChoiceItem.Character : null);
		}
		catch (Exception)
		{
			_subject = null;
		}
	}

	[HarmonyPostfix]
	private static void Postfix(CharacterChoiceManager __instance, int controllerPlayerId)
	{
		try
		{
			if (!_firstCallLogged)
			{
				_firstCallLogged = true;
				Plugin.Log.LogInfo((object)("RSTV-2: gancho de adicao/remocao da party ATIVO (ToggleSelectedCharacter, controllerPlayerId=" + controllerPlayerId + ")."));
			}
			Character subject = _subject;
			_subject = null;
			if (subject != null && PartyTargets.IsLocal(subject))
			{
				if (subject.SelectedForBattle)
				{
					PartyTargets.NoteAddition(subject);
					Plugin.Log.LogInfo((object)("RSTV-2: '" + subject.CharacterName + "' entrou na MINHA party — novo alvo do botao."));
				}
				else
				{
					PartyTargets.NoteRemoval(subject);
					Character val = PartyTargets.Resolve();
					Plugin.Log.LogInfo((object)("RSTV-2: '" + subject.CharacterName + "' saiu da minha party — alvo agora: " + ((val != null) ? val.CharacterName : "(nenhum)")));
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de ToggleSelectedCharacter: " + ex));
		}
	}
}
[HarmonyPatch(typeof(SkillTreeManager), "AcceptSkillChanges", new Type[] { typeof(bool) })]
internal static class SkillTreeManagerAcceptSkillChangesPatch
{
	[HarmonyPrefix]
	private static bool Prefix(SkillTreeManager __instance, bool closeMenu)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Invalid comparison between Unknown and I4
		try
		{
			if (!ReadOnlySession.Active || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if ((Object)(object)GUIManager.instance != (Object)null && (int)GUIManager.instance.CurrentGuiState == 1)
			{
				return true;
			}
			Character target = ReadOnlySession.Target;
			__instance.SkillsToAdd.Clear();
			__instance.SkillsRemoved.Clear();
			__instance.SkillsOnEnter.Clear();
			HashSet<SkillInfo> hashSet = new HashSet<SkillInfo>();
			if (target != null)
			{
				hashSet.UnionWith(target.SkillsFromPoints);
			}
			Character val = (((Object)(object)GameLogic.instance != (Object)null) ? GameLogic.instance.CurrentlySelectedCharacter : null);
			if (val != null && val != target)
			{
				hashSet.UnionWith(val.SkillsFromPoints);
			}
			__instance.SkillsOnEnter.AddRange(hashSet);
			Plugin.Log.LogInfo((object)("RSTV-2: commit higienizado (read-only, closeMenu=" + closeMenu + ") — nenhuma escrita no personagem (" + hashSet.Count + " skills no snapshot)."));
			return true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao higienizar AcceptSkillChanges — o original NAO vai rodar (zero escrita); fechando a janela para nao prender a UI: " + ex));
			try
			{
				ReadOnlySession.Close();
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("RSTV: e o fechamento de emergencia tambem falhou: " + ex2.Message));
			}
			return false;
		}
	}
}
[HarmonyPatch(typeof(SkillTreeManager), "Initialize", new Type[]
{
	typeof(List<SkillInfo>),
	typeof(int),
	typeof(bool)
})]
internal static class SkillTreeManagerInitializePatch
{
	[HarmonyPostfix]
	private static void Postfix(SkillTreeManager __instance, bool creating)
	{
		try
		{
			if (!ReadOnlySession.Active || (Object)(object)__instance == (Object)null || creating)
			{
				return;
			}
			Character target = ReadOnlySession.Target;
			if (target != null)
			{
				bool num = __instance.startingUnspentPoints != 0 || __instance.SkillsToAdd.Count > 0 || __instance.SkillsRemoved.Count > 0 || __instance.SkillsOnEnter.Count != target.SkillsFromPoints.Count;
				__instance.SkillsToAdd.Clear();
				__instance.SkillsRemoved.Clear();
				__instance.SkillsOnEnter.Clear();
				__instance.SkillsOnEnter.AddRange(target.SkillsFromPoints);
				__instance.startingUnspentPoints = 0;
				__instance.RefreshRespecFooter();
				if (num)
				{
					__instance.UpdateCurrentTree();
					Plugin.Log.LogInfo((object)"RSTV-2: estado interno reafirmado (read-only) depois de um Initialize do jogo.");
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao reafirmar o modo somente leitura — fechando a arvore para nao deixar uma janela interativa em estado desconhecido: " + ex));
			try
			{
				ReadOnlySession.Close();
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("RSTV: e o fechamento de emergencia tambem falhou: " + ex2.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(SkillTreeManager), "RefreshRespecFooter", new Type[] { })]
internal static class SkillTreeManagerRespecFooterPatch
{
	[HarmonyPostfix]
	private static void Postfix(SkillTreeManager __instance)
	{
		try
		{
			if (ReadOnlySession.Active && (Object)(object)__instance != (Object)null && (Object)(object)__instance.RespecButton != (Object)null && __instance.RespecButton.activeSelf)
			{
				__instance.RespecButton.SetActive(false);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao esconder o RespecButton: " + ex));
		}
	}
}
[HarmonyPatch(typeof(SkillTreeItem), "ToggleAddToSkillToAddList", new Type[] { })]
internal static class SkillTreeItemTogglePatch
{
	private static bool _firstBlockLogged;

	[HarmonyPrefix]
	private static bool Prefix(SkillTreeItem __instance)
	{
		bool flag;
		try
		{
			flag = ReadOnlySession.Active || SkillTreesTab.Aberto;
		}
		catch (Exception)
		{
			return true;
		}
		if (!flag)
		{
			return true;
		}
		try
		{
			if (!_firstBlockLogged)
			{
				_firstBlockLogged = true;
				string text = (((Object)(object)__instance != (Object)null && (Object)(object)__instance.SkillInfo != (Object)null) ? __instance.SkillInfo.SkillName : "?");
				Plugin.Log.LogInfo((object)("RSTV-2: clique na arvore BLOQUEADO (read-only) — primeira skill clicada: " + text + "."));
			}
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogWarning((object)("RSTV: nao deu para logar o bloqueio do clique (o clique continua bloqueado): " + ex2.Message));
		}
		return false;
	}
}
[HarmonyPatch(typeof(SkillTreeManager), "ResetSkillPoints", new Type[] { })]
internal static class SkillTreeManagerResetSkillPointsPatch
{
	private static bool _firstBlockLogged;

	[HarmonyPrefix]
	private static bool Prefix()
	{
		try
		{
			if (!ReadOnlySession.Active)
			{
				return true;
			}
			if (!_firstBlockLogged)
			{
				_firstBlockLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-2: ResetSkillPoints BLOQUEADO (read-only) — nada foi zerado no personagem.");
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao avaliar o bloqueio de ResetSkillPoints — mantendo o reset BLOQUEADO por seguranca: " + ex));
			return false;
		}
		return false;
	}
}
[HarmonyPatch(typeof(CharacterMenusManager), "OpenWindow", new Type[] { })]
internal static class CharacterMenusManagerOpenWindowPatch
{
	private static bool _firstCallLogged;

	[HarmonyPostfix]
	private static void Postfix(CharacterMenusManager __instance)
	{
		try
		{
			if (!_firstCallLogged)
			{
				_firstCallLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-16: gancho da aba do inventario ATIVO (CharacterMenusManager.OpenWindow).");
			}
			RstvHost.Ensure();
			SkillTreesTab.Ensure(__instance);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-16: falha no postfix de CharacterMenusManager.OpenWindow: " + ex));
		}
	}
}
[HarmonyPatch(typeof(SkillTreeManager), "OnDisable", new Type[] { })]
internal static class SkillTreeManagerOnDisablePatch
{
	[HarmonyPostfix]
	private static void Postfix(SkillTreeManager __instance)
	{
		try
		{
			if (ReadOnlySession.Active)
			{
				ReadOnlySession.End();
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de OnDisable: " + ex));
		}
	}
}
[HarmonyPatch(typeof(CharacterMenusManager), "CloseSkillTreeMenu", new Type[] { })]
internal static class CharacterMenusManagerCloseSkillTreePatch
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		try
		{
			if (ReadOnlySession.Active)
			{
				ReadOnlySession.End();
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de CharacterMenusManager.CloseSkillTreeMenu: " + ex));
		}
	}
}
[HarmonyPatch(typeof(CurrentCharacterUI), "InitSingleton", new Type[] { })]
internal static class CurrentCharacterUIInitPatch
{
	private static bool _firstCallLogged;

	[HarmonyPostfix]
	private static void Postfix(CurrentCharacterUI __instance)
	{
		try
		{
			if (!_firstCallLogged)
			{
				_firstCallLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-5: gancho do HUD da run ATIVO (CurrentCharacterUI.InitSingleton).");
			}
			RstvHost.Ensure();
			RunButton.Ensure(__instance);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de CurrentCharacterUI.InitSingleton: " + ex));
		}
	}
}
[HarmonyPatch(typeof(PlayerMovement), "ProcessLeftMouseClick", new Type[] { typeof(HexCell) })]
internal static class PlayerMovementProcessLeftMouseClickPatch
{
	private static bool _firstBlockLogged;

	[HarmonyPrefix]
	private static bool Prefix()
	{
		try
		{
			if (!ReadOnlySession.Active)
			{
				return true;
			}
			if (!_firstBlockLogged)
			{
				_firstBlockLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-5: clique no hex CONSUMIDO enquanto a arvore read-only esta aberta (o branch de Action NAO checa PointerOverUIObject).");
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao avaliar o bloqueio do clique do hex — deixando o jogo tratar o clique: " + ex));
			return true;
		}
		return false;
	}
}
[HarmonyPatch(typeof(KeybindManager), "Update", new Type[] { })]
internal static class KeybindManagerUpdatePatch
{
	[HarmonyPostfix]
	private static void Postfix(KeybindManager __instance)
	{
		try
		{
			SkillTreeShortcut.Handle(__instance);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no gancho do atalho (KeybindManager.Update): " + ex));
		}
	}
}
[HarmonyPatch(typeof(RoguelikeManager), "OpenSkillSelectWindow", new Type[]
{
	typeof(CharacterLevelUpInfo),
	typeof(bool)
})]
internal static class RoguelikeManagerOpenSkillSelectWindowPatch
{
	private static bool _firstCallLogged;

	[HarmonyPostfix]
	private static void Postfix(RoguelikeManager __instance)
	{
		try
		{
			if (!_firstCallLogged)
			{
				_firstCallLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-11b: gancho da janela do level-up ATIVO (RoguelikeManager.OpenSkillSelectWindow).");
			}
			LevelUpWindowButton.Ensure(__instance);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de OpenSkillSelectWindow: " + ex));
		}
	}
}
[HarmonyPatch(typeof(RoguelikeSkillTreeRemovalWindow), "Open", new Type[] { typeof(Character) })]
internal static class RoguelikeSkillTreeRemovalWindowOpenPatch
{
	private static bool _firstCallLogged;

	[HarmonyPostfix]
	private static void Postfix(RoguelikeSkillTreeRemovalWindow __instance)
	{
		try
		{
			if (!_firstCallLogged)
			{
				_firstCallLogged = true;
				Plugin.Log.LogInfo((object)"RSTV-20: gancho do modal 'Remove Skill Trees' ATIVO (RoguelikeSkillTreeRemovalWindow.Open).");
			}
			RstvHost.Ensure();
			RemovalWindowSkillsButton.Ensure(__instance);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha no postfix de RoguelikeSkillTreeRemovalWindow.Open: " + ex));
		}
	}
}
[HarmonyPatch(typeof(Tooltip), "get_TooltipCharacter")]
internal static class TooltipCharacterRodapePatch
{
	private static Character _alvoDoModal;

	private static bool _primeiroPreenchimentoLogado;

	internal static void ComAlvo(Character alvo)
	{
		_alvoDoModal = alvo;
	}

	internal static void SemAlvo()
	{
		_alvoDoModal = null;
	}

	[HarmonyPostfix]
	private static void Postfix(ref Character __result)
	{
		try
		{
			if (__result != null)
			{
				return;
			}
			Character alvoDoModal = _alvoDoModal;
			if (alvoDoModal != null)
			{
				__result = alvoDoModal;
				if (!_primeiroPreenchimentoLogado)
				{
					_primeiroPreenchimentoLogado = true;
					Plugin.Log.LogInfo((object)("RSTV-27: rodape da tooltip resolvido pelo personagem do MODAL (TooltipCharacter era null) — alvo=" + (alvoDoModal.CharacterName ?? "?") + "."));
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-27: falha no postfix de Tooltip.TooltipCharacter: " + ex.Message));
		}
	}
}
[BepInPlugin("com.gumatos.roguelikeskilltreevisualizer", "Roguelike Skill Tree Visualizer", "0.3.5")]
[BepInDependency("com.gumatos.realmapi", "0.1.1")]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "com.gumatos.roguelikeskilltreevisualizer";

	public const string Version = "0.3.5";

	public const string ApiGuid = "com.gumatos.realmapi";

	public const string ApiVersionMinima = "0.1.1";

	internal static ManualLogSource Log { get; private set; }

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

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

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

	internal static ConfigEntry<KeyCode> TeclaAtalhoSkillTree { get; private set; }

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

	internal static bool BotaoLigado
	{
		get
		{
			if (AtivarBotao != null)
			{
				return AtivarBotao.Value;
			}
			return true;
		}
	}

	internal static bool RunBotaoLigado
	{
		get
		{
			if (BotaoLigado)
			{
				if (AtivarBotaoNaRun != null)
				{
					return AtivarBotaoNaRun.Value;
				}
				return true;
			}
			return false;
		}
	}

	internal static bool AtalhoLigado
	{
		get
		{
			if (AtalhoSkillTree != null)
			{
				return AtalhoSkillTree.Value;
			}
			return true;
		}
	}

	internal static bool RemocaoBotaoLigado
	{
		get
		{
			if (BotaoLigado)
			{
				if (AtivarBotaoNaRemocao != null)
				{
					return AtivarBotaoNaRemocao.Value;
				}
				return true;
			}
			return false;
		}
	}

	internal static string MotivoDoBotaoDaRunDesligado
	{
		get
		{
			if (!BotaoLigado)
			{
				return "AtivarBotao=false no arquivo de config (chave-mestra: desliga o botao da run, o da janela de level-up e o do modal)";
			}
			return "AtivarBotaoNaRun=false no arquivo de config";
		}
	}

	internal static string MotivoDoBotaoDoModalDesligado
	{
		get
		{
			if (!BotaoLigado)
			{
				return "AtivarBotao=false no arquivo de config (chave-mestra: desliga o botao da run, o da janela de level-up e o do modal)";
			}
			return "AtivarBotaoNaRemocao=false no arquivo de config";
		}
	}

	internal static KeyCode TeclaAtalho
	{
		get
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (TeclaAtalhoSkillTree == null)
			{
				return (KeyCode)291;
			}
			return TeclaAtalhoSkillTree.Value;
		}
	}

	private void Awake()
	{
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		try
		{
			AtivarBotao = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "AtivarBotao", true, "CHAVE-MESTRA dos botoes 'Skills' que restam (RSTV-29): o do HUD da run, o da janela de LEVEL-UP e o do modal 'Remove Skill Trees'. Padrao: true. Com false o mod carrega e registra os ganchos, mas NAO cria botao nenhum em lugar nenhum — desliga o HUD da run, o botao da janela de level-up E o modal de uma vez (para desligar so' uma superficie use a chave especifica dela). A tela Select Party nao tem mais botao (a superficie foi removida).");
			AtivarBotaoNaRun = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "AtivarBotaoNaRun", true, "Injeta o botao 'Skills' no HUD DURANTE A RUN, na BARRA DE BAIXO — na mesma linha dos botoes nativos de inventario e de skills, no vao livre medido antes do controle que rotaciona a barra de skills (RSTV-30). Padrao: true. Com false (ou com a chave-mestra AtivarBotao=false) o mod nao cria esse botao. ATENCAO: o portao da run (RunTargets.GateOk) vale mesmo com true — em mira de hex ou de skill, com uma janela de UI aberta ou no posicionamento inicial da batalha o botao fica SEM CLIQUE (continua visivel, na barra) e o log diz por que.");
			AtalhoSkillTree = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "AtalhoSkillTree", true, "Liga o atalho de TECLADO que abre a mesma skill tree read-only (sem depender do botao do HUD). Padrao: true. A tecla nao dispara durante chat, remap de tecla, janela de evento ou menu sem alvo: os guards do KeybindManager.Update sao replicados no mod.");
			TeclaAtalhoSkillTree = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Geral", "TeclaAtalhoSkillTree", (KeyCode)291, "A tecla do atalho da skill tree. Padrao: F10. Aceita qualquer KeyCode do Unity (F9, K, Mouse4, ...). Nao usa a acao 10 nativa, entao NAO abre a janela vanilla de skill tree junto.");
			AtivarBotaoNaRemocao = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "AtivarBotaoNaRemocao", true, "Injeta o botao 'Skills' no CABECALHO do modal 'Remove Skill Trees' (canto superior direito do titulo), que abre a arvore read-only do personagem do proprio modal. Padrao: true. Com false (ou com a chave-mestra AtivarBotao=false) o mod nao cria esse botao.");
		}
		catch (Exception ex)
		{
			Log.LogError((object)("RSTV: falha ao ler/criar o arquivo de config — seguindo com o padrao (botoes LIGADOS): " + ex.Message));
		}
		Log.LogInfo((object)"Roguelike Skill Tree Visualizer 0.3.5 carregado (RSTV-29: botoes 'Skills' no HUD da run e no modal 'Remove Skill Trees', mais o atalho de teclado; a arvore nativa abre em skill tree read-only).");
		AplicarPatches();
		Log.LogInfo((object)("RSTV DIAG: chave-mestra AtivarBotao=" + BotaoLigado + " — botao 'Skills' do HUD da run " + (RunBotaoLigado ? "HABILITADO" : "DESABILITADO") + " (AtivarBotaoNaRun=" + (AtivarBotaoNaRun == null || AtivarBotaoNaRun.Value) + "; RSTV-30: na BARRA DE BAIXO, visivel com o HUD — o portao da run decide so' o CLIQUE); botao 'Skills' do modal 'Remove Skill Trees' " + (RemocaoBotaoLigado ? "HABILITADO" : "DESABILITADO") + " (AtivarBotaoNaRemocao=" + (AtivarBotaoNaRemocao == null || AtivarBotaoNaRemocao.Value) + "); atalho " + (AtalhoLigado ? ("LIGADO na tecla " + ((object)TeclaAtalho/*cast due to .constrained prefix*/).ToString()) : "DESABILITADO") + " (RSTV-11a: guards do KeybindManager replicados; sem acao nativa)."));
	}

	private static void AplicarPatches()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		Harmony val = new Harmony("com.gumatos.roguelikeskilltreevisualizer");
		List<string> list = new List<string>();
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		Type[] types;
		try
		{
			types = typeof(Plugin).Assembly.GetTypes();
		}
		catch (Exception ex)
		{
			Log.LogError((object)("RSTV: nao deu para listar os tipos do mod — nenhum gancho aplicado: " + ex));
			return;
		}
		foreach (Type type in types)
		{
			if (EhClasseDeGancho(type))
			{
				num++;
				try
				{
					int num4 = val.CreateClassProcessor(type).Patch()?.Count ?? 0;
					num3 += num4;
					num2++;
					Log.LogInfo((object)("RSTV: gancho aplicado — " + type.Name + " (" + num4 + " metodo(s) do jogo)."));
				}
				catch (Exception ex2)
				{
					list.Add(type.Name);
					Log.LogError((object)("RSTV: FALHA ao aplicar o gancho " + type.Name + " — " + ex2.Message));
				}
			}
		}
		string text = "RSTV: patches Harmony aplicados (" + num2 + "/" + num + " ganchos, " + num3 + " metodos do jogo).";
		if (list.Count == 0)
		{
			Log.LogInfo((object)text);
		}
		else
		{
			Log.LogError((object)(text + " GANCHOS QUE FALHARAM: " + string.Join(", ", list.ToArray()) + ". O mod continua de pe, mas o recurso que dependia deles nao existe nesta sessao."));
		}
	}

	private static bool EhClasseDeGancho(Type tipo)
	{
		try
		{
			if (tipo == null || !tipo.IsClass)
			{
				return false;
			}
			if (tipo.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length == 0)
			{
				return false;
			}
			MethodInfo[] methods = tipo.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.GetCustomAttributes(typeof(HarmonyPrefix), inherit: false).Length != 0 || methodInfo.GetCustomAttributes(typeof(HarmonyPostfix), inherit: false).Length != 0)
				{
					return true;
				}
			}
			return false;
		}
		catch (Exception)
		{
			return false;
		}
	}
}
internal static class RemovalWindowSkillsButton
{
	internal const string ButtonName = "RstvRemovalSkillsButton";

	internal const string Label = "Skills";

	internal const string TituloEsperado = "Remove Skill Trees";

	internal const float Lado = 44f;

	private const float Margem = 8f;

	private const float LarguraMinimaDaFaixa = 120f;

	private static RoguelikeSkillTreeRemovalWindow _window;

	private static GameObject _button;

	private static string _reasonLogged;

	internal static void Ensure(RoguelikeSkillTreeRemovalWindow window)
	{
		try
		{
			EnsureInternal(window);
		}
		catch (Exception ex)
		{
			Fail("excecao durante a injecao (" + ex.GetType().Name + "): " + ex.Message, aviso: true);
			Plugin.Log.LogError((object)("RSTV-20: falha ao injetar o botao do modal 'Remove Skill Trees': " + ex));
		}
	}

	private static void EnsureInternal(RoguelikeSkillTreeRemovalWindow window)
	{
		//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_031f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0335: Unknown result type (might be due to invalid IL or missing references)
		//IL_039c: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a6: Expected O, but got Unknown
		if ((Object)(object)window == (Object)null)
		{
			Fail("a instancia do modal 'Remove Skill Trees' chegou nula", aviso: false);
			return;
		}
		if (!Plugin.RemocaoBotaoLigado)
		{
			Fail(Plugin.MotivoDoBotaoDoModalDesligado, aviso: false);
			return;
		}
		if ((Object)(object)_window == (Object)(object)window && (Object)(object)_button != (Object)null)
		{
			Reafirmar();
			return;
		}
		if ((Object)(object)_button != (Object)null && (Object)(object)_window != (Object)(object)window)
		{
			Object.Destroy((Object)(object)_button);
			_button = null;
		}
		Button val = null;
		string text = null;
		if (window.CancelButton != null && (Object)(object)window.CancelButton.Root != (Object)null)
		{
			val = ((Component)window.CancelButton.Root).GetComponent<Button>();
			text = "CancelButton";
		}
		if ((Object)(object)val == (Object)null && window.AcceptButton != null && (Object)(object)window.AcceptButton.Root != (Object)null)
		{
			val = ((Component)window.AcceptButton.Root).GetComponent<Button>();
			text = "AcceptButton";
		}
		if ((Object)(object)val == (Object)null)
		{
			Button[] componentsInChildren = ((Component)window).GetComponentsInChildren<Button>(true);
			if (componentsInChildren != null && componentsInChildren.Length != 0)
			{
				val = componentsInChildren[0];
				text = "primeiro Button filho do modal";
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			Fail("o modal nao tem nenhum Button nativo para servir de molde (CancelButton/AcceptButton ausentes)", aviso: true);
			return;
		}
		if ((Object)(object)((Component)val).transform == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
		{
			Fail("o botao molde ('" + text + "') esta sem Transform ou sem pai na hierarquia", aviso: true);
			return;
		}
		RectTransform val2 = null;
		string text2 = null;
		string textoAchado;
		RectTransform val3 = ProbeFaixaDoTitulo(window, out textoAchado);
		if ((Object)(object)val3 != (Object)null)
		{
			val2 = val3;
			text2 = "probe do titulo renderizado (\"" + textoAchado + "\")";
		}
		else if ((Object)(object)window.Panel != (Object)null)
		{
			val2 = window.Panel;
			text2 = "Panel (fallback: o probe do titulo nao achou uma faixa larga)";
		}
		else if ((Object)(object)window.TitleText != (Object)null && (Object)(object)((TMP_Text)window.TitleText).rectTransform != (Object)null)
		{
			Transform parent = ((Transform)((TMP_Text)window.TitleText).rectTransform).parent;
			val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			text2 = "pai do TitleText (fallback)";
		}
		else if ((Object)(object)window.TitleDivider != (Object)null)
		{
			Transform parent2 = ((Transform)((Graphic)window.TitleDivider).rectTransform).parent;
			val2 = (RectTransform)(object)((parent2 is RectTransform) ? parent2 : null);
			text2 = "pai da TitleDivider (fallback)";
		}
		else
		{
			Transform transform = ((Component)window).transform;
			val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			text2 = "transform do proprio modal (fallback)";
		}
		if ((Object)(object)val2 == (Object)null)
		{
			Fail("nao foi possivel resolver a area do cabecalho (probe do titulo/Panel/TitleText/TitleDivider nulos)", aviso: true);
			return;
		}
		Transform val4 = Procurar(((Component)window).transform);
		GameObject val5;
		if ((Object)(object)val4 != (Object)null)
		{
			val5 = ((Component)val4).gameObject;
			if ((Object)(object)val5.transform.parent != (Object)(object)val2)
			{
				val5.transform.SetParent((Transform)(object)val2, false);
			}
		}
		else
		{
			val5 = RstvApiUi.Clonar(((Component)val).gameObject, (Transform)(object)val2, "RstvRemovalSkillsButton");
		}
		val5.transform.SetAsLastSibling();
		RectTransform component = val5.GetComponent<RectTransform>();
		if ((Object)(object)component == (Object)null)
		{
			Fail("o clone do botao ficou sem RectTransform", aviso: true);
			return;
		}
		component.anchorMin = new Vector2(1f, 1f);
		component.anchorMax = new Vector2(1f, 1f);
		component.pivot = new Vector2(1f, 1f);
		((Transform)component).localScale = Vector3.one;
		((Transform)component).localRotation = Quaternion.identity;
		component.sizeDelta = new Vector2(44f, 44f);
		component.anchoredPosition = new Vector2(-8f, -8f);
		LayoutElement component2 = val5.GetComponent<LayoutElement>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.ignoreLayout = true;
		}
		NativeUiHelpers.DisableLocalizers(val5);
		EnsureLabel(val5);
		Button component3 = val5.GetComponent<Button>();
		if ((Object)(object)component3 == (Object)null)
		{
			Fail("o clone do botao ficou sem componente Button", aviso: true);
			return;
		}
		NativeUiHelpers.ClearClickListeners(component3);
		RstvApiUi.LigarClique(((Component)component3).gameObject, new UnityAction(OnClick));
		((Selectable)component3).interactable = true;
		val5.SetActive(((Component)window).gameObject.activeSelf);
		_window = window;
		_button = val5;
		_reasonLogged = null;
		Plugin.Log.LogInfo((object)("RSTV-20: botao 'Skills' injetado no cabecalho do modal 'Remove Skill Trees' (" + 44f.ToString("0.#") + "x" + 44f.ToString("0.#") + " px, canto superior direito de '" + ((Object)val2).name + "' via " + text2 + "), clone de '" + ((Object)((Component)val).gameObject).name + "' (" + text + "; onClick limpo -> SkillTreesTab.AbrirJanela(CurrentCharacter, RemocaoDeArvores) [RSTV-21: janela propria, sem inventario]; janela=" + ((Object)window).name + "."));
	}

	private static void Reafirmar()
	{
		if ((Object)(object)_button == (Object)null || (Object)(object)_window == (Object)null)
		{
			return;
		}
		try
		{
			if (_button.activeSelf != ((Component)_window).gameObject.activeSelf)
			{
				_button.SetActive(((Component)_window).gameObject.activeSelf);
			}
			EnsureLabel(_button);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-20: nao deu para reafirmar o botao do modal (" + ex.GetType().Name + "): " + ex.Message));
		}
	}

	private static void OnClick()
	{
		try
		{
			RoguelikeSkillTreeRemovalWindow window = _window;
			Character val = (((Object)(object)window != (Object)null) ? window.CurrentCharacter : null);
			if (val == null)
			{
				Plugin.Log.LogWarning((object)"RSTV-20: clique no botao 'Skills' sem personagem no modal (CurrentCharacter nulo) — nada foi aberto.");
				return;
			}
			Plugin.Log.LogInfo((object)("RSTV-20: clique no botao 'Skills' do modal -> '" + val.CharacterName + "' (nivel " + val.Level + ") na JANELA read-only de todas as arvores (RSTV-21)."));
			SkillTreesTab.AbrirJanela(val, ReadOnlyContext.RemocaoDeArvores, ((Component)window).transform);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-20: falha no clique do botao do modal: " + ex));
		}
	}

	private static void EnsureLabel(GameObject clone)
	{
		TextMeshProUGUI componentInChildren = clone.GetComponentInChildren<TextMeshProUGUI>(true);
		if ((Object)(object)componentInChildren == (Object)null)
		{
			Fail("o clone do botao ficou SEM TextMeshProUGUI — botao sem rotulo", aviso: true);
		}
		else
		{
			((TMP_Text)componentInChildren).text = OptionsManager.Localize("Skills", (LanguageGender)0, (Gender)2);
		}
	}

	private static RectTransform ProbeFaixaDoTitulo(RoguelikeSkillTreeRemovalWindow window, out string textoAchado)
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		textoAchado = null;
		try
		{
			string text = OptionsManager.Localize("Remove Skill Trees", (LanguageGender)0, (Gender)2);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			TextMeshProUGUI[] componentsInChildren = ((Component)window).GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && ((TMP_Text)val).text != null && !((Object)(object)((TMP_Text)val).rectTransform == (Object)null) && !(((TMP_Text)val).text.Trim() != text.Trim()))
				{
					Transform parent = ((Transform)((TMP_Text)val).rectTransform).parent;
					RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
					if ((Object)(object)val2 == (Object)null)
					{
						return null;
					}
					Rect rect = val2.rect;
					float num = ((Rect)(ref rect)).width;
					if (num <= 1f)
					{
						num = val2.sizeDelta.x;
					}
					if (num < 120f)
					{
						Plugin.Log.LogInfo((object)("RSTV-20: o probe achou o titulo renderizado, mas o container dele e estreito (" + num.ToString("0.#") + " px < " + 120f.ToString("0.#") + ") — usando o `Panel`."));
						return null;
					}
					textoAchado = ((TMP_Text)val).text;
					return val2;
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-20: falha no probe do titulo renderizado (" + ex.GetType().Name + "): " + ex.Message));
		}
		return null;
	}

	private static Transform Procurar(Transform raiz)
	{
		if ((Object)(object)raiz == (Object)null)
		{
			return null;
		}
		Transform[] componentsInChildren = ((Component)raiz).GetComponentsInChildren<Transform>(true);
		foreach (Transform val in componentsInChildren)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name == "RstvRemovalSkillsButton")
			{
				return val;
			}
		}
		return null;
	}

	private static void Fail(string motivo, bool aviso)
	{
		if (!(_reasonLogged == motivo))
		{
			_reasonLogged = motivo;
			string text = "RSTV DIAG: botao 'Skills' do modal 'Remove Skill Trees' NAO injetado — " + motivo + ".";
			if (aviso)
			{
				Plugin.Log.LogWarning((object)text);
			}
			else
			{
				Plugin.Log.LogInfo((object)text);
			}
		}
	}
}
internal static class RstvApiUi
{
	internal static bool Vinculada => UiFamily.IsBound;

	internal static string UltimoMotivo { get; private set; }

	internal static void AsegurarVinculo()
	{
		//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_001a: Expected O, but got Unknown
		if (UiFamily.IsBound)
		{
			return;
		}
		try
		{
			UiHosts val = new UiHosts
			{
				GuiManager = GUIManager.instance
			};
			UiFamily.Bind(typeof(GUIManager).Assembly, val, true);
			UltimoMotivo = null;
			Plugin.Log.LogInfo((object)"RSTV INT-R1-C4: familia 'UI' da API vinculada (allowMutation=true) — clone/clique de UI passam a vir de Gumatos.RealmApi.UI.");
		}
		catch (Exception ex)
		{
			UltimoMotivo = ex.GetType().Name + ": " + ex.Message;
			Plugin.Log.LogError((object)("RSTV INT-R1-C4: NAO foi possivel vincular a familia 'UI' da API — a plumbing de UI desta chamada nao sera executada (" + UltimoMotivo + "). O mod depende de com.gumatos.realmapi; confira a versao instalada."));
			throw;
		}
	}

	internal static GameObject Clonar(GameObject molde, Transform pai, string nome)
	{
		AsegurarVinculo();
		return UIFactory.Clone(molde, pai, nome);
	}

	internal static void LigarClique(GameObject alvo, UnityAction acao)
	{
		AsegurarVinculo();
		UIFactory.BindClick(alvo, acao);
	}
}
internal class RstvHost : MonoBehaviour
{
	private static RstvHost _instance;

	private float _nextRunMirror;

	private float _lastErrorLog;

	internal static RstvHost Ensure()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//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("RstvHost");
		Object.DontDestroyOnLoad((Object)val);
		_instance = val.AddComponent<RstvHost>();
		Plugin.Log.LogInfo((object)"RSTV: host criado (cena viva) — Update() ativo.");
		return _instance;
	}

	private void Update()
	{
		try
		{
			SkillTreesTab.Tick();
			LevelUpWindowButton.Refresh();
			if (Time.realtimeSinceStartup >= _nextRunMirror)
			{
				_nextRunMirror = Time.realtimeSinceStartup + 0.1f;
				RunButton.Mirror();
			}
		}
		catch (Exception ex)
		{
			if (!(Time.realtimeSinceStartup - _lastErrorLog < 5f))
			{
				_lastErrorLog = Time.realtimeSinceStartup;
				Plugin.Log.LogError((object)("RSTV: falha no Update do host: " + ex));
			}
		}
	}
}
internal static class RunButton
{
	internal const string ButtonName = "RstvRunSkillTreeButton";

	internal const string Label = "Skills";

	internal const string LabelChildName = "RstvRunLabel";

	private const float Margem = 2f;

	private const float LarguraMinima = 10f;

	private static CurrentCharacterUI _owner;

	private static GameObject _button;

	private static Button _mold;

	private static CurrentCharacterUI _triedFor;

	private static float _proximaTentativa;

	private const float IntervaloDeReinjecao = 2f;

	private static string _reason;

	private static string _reasonLogged;

	private static float _nextReasonLog;

	private static bool _clicavelLogado;

	internal static void Ensure(CurrentCharacterUI ui)
	{
		try
		{
			EnsureInternal(ui);
		}
		catch (Exception ex)
		{
			Fail("excecao durante a injecao (" + ex.GetType().Name + "): " + ex.Message, aviso: true);
			Plugin.Log.LogError((object)("RSTV: falha ao injetar o botao da run: " + ex));
		}
	}

	private static void EnsureInternal(CurrentCharacterUI ui)
	{
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Expected O, but got Unknown
		if ((Object)(object)ui == (Object)null)
		{
			Fail("a instancia do HUD (CurrentCharacterUI) chegou nula", aviso: false);
			return;
		}
		if (!Plugin.RunBotaoLigado)
		{
			Fail(Plugin.MotivoDoBotaoDaRunDesligado, aviso: false);
			return;
		}
		string origem;
		Button val = EscolherMolde(ui, out origem);
		if ((Object)(object)val == (Object)null)
		{
			Fail("nenhum botao nativo da barra de baixo serve de molde (CurrentCharacterUI.skillsBtn / characterBtn / pingBtn ausentes)", aviso: true);
		}
		else if ((Object)(object)((Component)val).transform == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
		{
			Fail("o botao nativo molde '" + ((Object)val).name + "' esta sem Transform ou sem pai na hierarquia", aviso: true);
		}
		else
		{
			if ((Object)(object)_owner == (Object)(object)ui && (Object)(object)_button != (Object)null && (Object)(object)_mold == (Object)(object)val)
			{
				return;
			}
			if ((Object)(object)_button != (Object)null && ((Object)(object)_owner != (Object)(object)ui || (Object)(object)_mold != (Object)(object)val))
			{
				Object.Destroy((Object)(object)_button);
				_button = null;
			}
			Transform parent = ((Component)val).transform.parent;
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null)
			{
				Fail("o botao nativo molde '" + ((Object)val).name + "' nao tem RectTransform (sem ele nao da para medir o vao livre)", aviso: true);
				return;
			}
			Transform val2 = parent.Find("RstvRunSkillTreeButton");
			GameObject val3 = ((!((Object)(object)val2 != (Object)null)) ? RstvApiUi.Clonar(((Component)val).gameObject, parent, "RstvRunSkillTreeButton") : ((Component)val2).gameObject);
			val3.transform.SetAsLastSibling();
			LayoutElement val4 = val3.GetComponent<LayoutElement>();
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = val3.AddComponent<LayoutElement>();
			}
			val4.ignoreLayout = true;
			NativeUiHelpers.DisableLocalizers(val3);
			AparenciaDoClone(val3);
			RectTransform component2 = val3.GetComponent<RectTransform>();
			if ((Object)(object)component2 == (Object)null)
			{
				Fail("o clone do botao ficou sem RectTransform", aviso: true);
				return;
			}
			ColocarNaLinha(parent, component, component2, out var medidas);
			val3.SetActive(((Component)val).gameObject.activeSelf);
			Button component3 = val3.GetComponent<Button>();
			if ((Object)(object)component3 != (Object)null)
			{
				NativeUiHelpers.ClearClickListeners(component3);
				((UnityEvent)component3.onClick).AddListener(new UnityAction(OpenForTarget));
				((Selectable)component3).interactable = false;
			}
			_owner = ui;
			_button = val3;
			_mold = val;
			_triedFor = ui;
			_reason = null;
			_reasonLogged = null;
			_clicavelLogado = false;
			Plugin.Log.LogInfo((object)("RSTV-30: botao 'Skills' injetado na BARRA DE BAIXO do HUD (" + medidas + "); pai=" + ((Object)parent).name + " (o container dos botoes de inventario e de skills); molde=" + origem + "; grupo do pai=" + NativeUiHelpers.DescribeGroup(parent) + " — o clone esta com ignoreLayout, entao o grupo NAO reflui por nossa causa."));
		}
	}

	private static Button EscolherMolde(CurrentCharacterUI ui, out string origem)
	{
		if ((Object)(object)ui.skillsBtn != (Object)null && (Object)(object)((Component)ui.skillsBtn).transform != (Object)null)
		{
			origem = "CurrentCharacterUI.skillsBtn ('Skills Tree Btn' da barra de baixo)";
			return ui.skillsBtn;
		}
		if ((Object)(object)ui.characterBtn != (Object)null && (Object)(object)((Component)ui.characterBtn).transform != (Object)null)
		{
			origem = "CurrentCharacterUI.characterBtn (fallback: 'Character Btn', a mesma linha)";
			return ui.characterBtn;
		}
		if ((Object)(object)ui.pingBtn != (Object)null && (Object)(object)((Component)ui.pingBtn).transform != (Object)null)
		{
			origem = "CurrentCharacterUI.pingBtn (ultimo fallback: o molde da RSTV-5)";
			return ui.pingBtn;
		}
		origem = null;
		return null;
	}

	internal static void Mirror()
	{
		try
		{
			CurrentCharacterUI instance = CurrentCharacterUI.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (!Plugin.RunBotaoLigado)
			{
				Hide(Plugin.MotivoDoBotaoDaRunDesligado, aviso: false);
				return;
			}
			if ((Object)(object)_button == (Object)null)
			{
				if ((Object)(object)_triedFor == (Object)(object)instance && Time.realtimeSinceStartup < _proximaTentativa)
				{
					return;
				}
				_proximaTentativa = Time.realtimeSinceStartup + 2f;
				if ((Object)(object)_triedFor == (Object)(object)instance)
				{
					Fail("o clone do botao foi DESTRUIDO com o mesmo HUD ainda vivo (fake null) — reinjetando (uma tentativa a cada 2 s)", aviso: true);
				}
				Ensure(instance);
				if ((Object)(object)_button == (Object)null)
				{
					return;
				}
			}
			if (!((Object)(object)_owner != (Object)(object)instance))
			{
				if ((Object)(object)_mold == (Object)null)
				{
					Hide("o botao nativo molde da barra de baixo sumiu do HUD", aviso: true);
					return;
				}
				EspelharVisibilidade();
				AplicarClique();
			}
		}
		catch (Exception ex)
		{
			if (!(Time.realtimeSinceStartup < _nextReasonLog))
			{
				_nextReasonLog = Time.realtimeSinceStartup + 5f;
				Plugin.Log.LogError((object)("RSTV: falha no Mirror do botao da run: " + ex));
			}
		}
	}

	private static void EspelharVisibilidade()
	{
		bool activeSelf = ((Component)_mold).gameObject.activeSelf;
		if (_button.activeSelf != activeSelf)
		{
			_button.SetActive(activeSelf);
		}
	}

	private static void AplicarClique()
	{
		string motivo = null;
		int num;
		if ((Object)(object)_mold != (Object)null)
		{
			num = (((Selectable)_mold).interactable ? 1 : 0);
			if (num != 0)
			{
				goto IL_0025;
			}
		}
		else
		{
			num = 0;
		}
		motivo = "o botao nativo 'Skills' do HUD esta desabilitado pelo jogo (GUIManager.Update l.120491: CurrentGuiState fora de InTown/InWorldMap/InCutscene/InBattle, ou Root.HideCharacterButtons ligado)";
		goto IL_0025;
		IL_0025:
		bool flag = false;
		if (num != 0)
		{
			flag = RunTargets.GateOk(out motivo);
		}
		Character val = null;
		if (((uint)num & (flag ? 1u : 0u)) != 0)
		{
			val = RunTargets.Resolve(out motivo);
		}
		bool flag2 = ((uint)num & (flag ? 1u : 0u)) != 0 && val != null;
		Button val2 = (((Object)(object)_button != (Object)null) ? _button.GetComponent<Button>() : null);
		if ((Object)(object)val2 != (Object)null && ((Selectable)val2).interactable != flag2)
		{
			((Selectable)val2).interactable = flag2;
		}
		if (flag2)
		{
			if (!_clicavelLogado)
			{
				_clicavelLogado = true;
				_reason = null;
				_reasonLogged = null;
				Plugin.Log.LogInfo((object)("RSTV DIAG: botao 'Skills' do HUD VISIVEL e clicavel — alvo '" + val.CharacterName + "' (nivel " + val.Level + ")."));
			}
		}
		else
		{
			_clicavelLogado = false;
			string text = ((motivo != null) ? motivo : "sem alvo local na party");
			if (!(_reasonLogged == text))
			{
				_reasonLogged = text;
				_reason = text;
				Plugin.Log.LogWarning((object)("RSTV DIAG: botao 'Skills' do HUD VISIVEL na barra de baixo e SEM clique agora — " + text + " (o botao NAO e escondido: quem manda na visibilidade e' o HUD)."));
			}
		}
	}

	private static void ColocarNaLinha(Transform container, RectTransform moldeRt, RectTransform novo, out string medidas)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		RectTransform val = (RectTransform)(object)((container is RectTransform) ? container : null);
		moldeRt.GetWorldCorners(array);
		float x = container.InverseTransformPoint(array[0]).x;
		float x2 = container.InverseTransformPoint(array[3]).x;
		float num = x2 - x;
		if (num <= 1f)
		{
			num = moldeRt.sizeDelta.x;
		}
		Rect rect = moldeRt.rect;
		float num2 = ((Rect)(ref rect)).height;
		if (num2 <= 1f)
		{
			num2 = moldeRt.sizeDelta.y;
		}
		float y = moldeRt.anchoredPosition.y;
		float num3 = float.MinValue;
		for (int i = 0; i < container.childCount; i++)
		{
			Transform child = container.GetChild(i);
			if (!((Object)(object)child == (Object)null) && !((Object)(object)child == (Object)(object)((Component)novo).transform) && ((Component)child).gameObject.activeSelf)
			{
				RectTransform val2 = (RectTransform)(object)((child is RectTransform) ? child : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.GetWorldCorners(array);
					num3 = Mathf.Max(num3, container.InverseTransformPoint(array[3]).x);
				}
			}
		}
		if (num3 == float.MinValue)
		{
			num3 = x2;
		}
		float num4 = float.MinValue;
		string text = "nenhum";
		Transform parent = container.parent;
		if ((Object)(object)parent != (Object)null && (Object)(object)val != (Object)null)
		{
			val.GetWorldCorners(array);
			float x3 = container.InverseTransformPoint(array[3]).x;
			float num5 = float.MaxValue;
			for (int j = 0; j < parent.childCount; j++)
			{
				Transform child2 = parent.GetChild(j);
				if ((Object)(object)child2 == (Object)null || (Object)(object)child2 == (Object)(object)container || (Object)(object)child2 == (Object)(object)((Component)novo).transform || !((Component)child2).gameObject.activeSelf)
				{
					continue;
				}
				RectTransform val3 = (RectTransform)(object)((child2 is RectTransform) ? child2 : null);
				if (!((Object)(object)val3 == (Object)null))
				{
					val3.GetWorldCorners(array);
					float x4 = container.InverseTransformPoint(array[0]).x;
					if (x4 >= x3 - 0.5f && x4 < num5)
					{
						num5 = x4;
						text = ((Object)child2).name;
					}
				}
			}
			if (num5 < float.MaxValue)
			{
				num4 = num5;
			}
		}
		if (num4 == float.MinValue)
		{
			num4 = num3 + num + 2f;
			text = "nenhum (limite = fim dos nativos + largura do molde)";
		}
		float num6 = num4 - num3 - 4f;
		float num7 = Mathf.Min(num, num6);
		if (num7 < 1f)
		{
			num7 = 1f;
		}
		float num8 = num4 - 2f - num7 * 0.5f;
		float num9 = num3 + 2f + num7 * 0.5f;
		if (num8 < num9)
		{
			num8 = num9;
		}
		novo.anchorMin = new Vector2(0f, 1f);
		novo.anchorMax = new Vector2(0f, 1f);
		novo.pivot = new Vector2(0.5f, 0.5f);
		novo.sizeDelta = new Vector2(num7, num2);
		float num10;
		if (!((Object)(object)val != (Object)null))
		{
			num10 = 0f;
		}
		else
		{
			rect = val.rect;
			num10 = ((Rect)(ref rect)).xMin;
		}
		float num11 = num10;
		novo.anchoredPosition = new Vector2(num8 - num11, y);
		medidas = "largura=" + num7.ToString("0.#") + " px (nativo " + num.ToString("0.#") + " px), altura=" + num2.ToString("0.#") + " px; borda direita dos nativos=" + num3.ToString("0.#") + " px, limite do vizinho '" + text + "'=" + num4.ToString("0.#") + " px, vao livre=" + num6.ToString("0.#") + " px, margem=" + 2f.ToString("0.#") + " px no espaco local de '" + ((Object)container).name + "'" + ((num7 < 10f) ? (" [AVISO: vao livre abaixo do minimo de conforto (" + 10f.ToString("0.#") + " px) — o botao ficou com o que cabe SEM sobrepor nenhum nativo]") : string.Empty);
	}

	private static void AparenciaDoClone(GameObject clone)
	{
		try
		{
			Button component = clone.GetComponent<Button>();
			Graphic val = (((Object)(object)component != (Object)null) ? ((Selectable)component).targetGraphic : null);
			Graphic[] componentsInChildren = clone.GetComponentsInChildren<Graphic>(true);
			int num = 0;
			List<string> list = new List<string>();
			foreach (Graphic val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					if (val2 is TextMeshProUGUI)
					{
						((Behaviour)val2).enabled = false;
						num++;
					}
					else if ((Object)(object)val2 == (Object)(object)val || (Object)(object)((Component)val2).transform == (Object)(object)clone.transform || ((Object)((Component)val2).gameObject).name.StartsWith("Icon"))
					{
						list.Add(((object)val2).GetType().Name + " em '" + ((Object)((Component)val2).gameObject).name + "'");
					}
					else
					{
						((Behaviour)val2).enabled = false;
						num++;
					}
				}
			}
			Plugin.Log.LogInfo((object)("RSTV-30: aparencia do clone — graficos desligados: " + num + "; mantidos: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "nenhum") + " (targetGraphic do clone: " + (((Object)(object)val != (Object)null) ? (((object)val).GetType().Name + " em '" + ((Object)((Component)val).gameObject).name + "'") : "nenhum") + ")."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV: nao deu para limpar a aparencia do clone (" + ex.Message + ")."));
		}
	}

	private static void Hide(string motivo, bool aviso)
	{
		_reason = motivo;
		try
		{
			if ((Object)(object)_button != (Object)null && _button.activeSelf)
			{
				_button.SetActive(false);
			}
			if ((Object)(object)_button != (Object)null)
			{
				Button component = _button.GetComponent<Button>();
				if ((Object)(object)component != (Object)null && ((Selectable)component).interactable)
				{
					((Selectable)component).interactable = false;
				}
			}
		}
		catch (Exception)
		{
		}
		if (!(_reasonLogged == motivo))
		{
			_reasonLogged = motivo;
			_clicavelLogado = false;
			string text = "RSTV DIAG: botao 'Skills' do HUD escondido — " + motivo + ".";
			if (aviso)
			{
				Plugin.Log.LogWarning((object)text);
			}
			else
			{
				Plugin.Log.LogInfo((object)text);
			}
		}
	}

	private static void Fail(string motivo, bool aviso)
	{
		_reason = motivo;
		if (!(_reasonLogged == motivo))
		{
			_reasonLogged = motivo;
			if (aviso)
			{
				Plugin.Log.LogWarning((object)("RSTV DIAG: botao 'Skills' da run NAO injetado — " + motivo + "."));
			}
			else
			{
				Plugin.Log.LogInfo((object)("RSTV DIAG: botao 'Skills' da run desativado — " + motivo + "."));
			}
		}
	}

	internal static void OpenForTarget()
	{
		try
		{
			if (!RunTargets.GateOk(out var motivo))
			{
				Plugin.Log.LogWarning((object)("RSTV-5: abertura RECUSADA — " + motivo + ". Nada foi aberto."));
				return;
			}
			Character val = RunTargets.Resolve(out motivo);
			if (val == null)
			{
				Plugin.Log.LogWarning((object)("RSTV-5: abertura sem alvo — " + motivo + "."));
				return;
			}
			Plugin.Log.LogInfo((object)("RSTV-16: abrindo a visualizacao read-only -> '" + val.CharacterName + "' (nivel " + val.Level + ") na aba 'All Skill Trees' do inventario."));
			SkillTreesTab.Abrir(val, ReadOnlyContext.Run);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao abrir a skill tree da run: " + ex));
		}
	}
}
internal enum ReadOnlyContext
{
	PartyScreen,
	Run,
	Inventario,
	RemocaoDeArvores
}
internal static class RunTargets
{
	private static string _fallbackLogado;

	internal static bool Roguelike
	{
		get
		{
			try
			{
				Root val = GameRoot();
				return val != null && val.PlayingRoguelike;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}

	internal static bool EstadoPermiteMenuDePersonagem(GUIState estado)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Invalid comparison between Unknown and I4
		if ((int)estado != 4 && (int)estado != 3 && (int)estado != 6)
		{
			return (int)estado == 2;
		}
		return true;
	}

	internal static Root GameRoot()
	{
		try
		{
			NetworkingManager instance = NetworkingManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.NetworkManager == null)
			{
				return null;
			}
			return ((Observer<Root>)(object)instance.NetworkManager).Root;
		}
		catch (Exception)
		{
			return null;
		}
	}

	private static bool LevelUpAberto()
	{
		try
		{
			RoguelikeManager instance = RoguelikeManager.Instance;
			return (Object)(object)instance != (Object)null && (Object)(object)instance.SkillSelectWindow != (Object)null && instance.SkillSelectWindow.activeSelf;
		}
		catch (Exception)
		{
			return false;
		}
	}

	internal static bool JanelaDoLevelUpAberta()
	{
		return LevelUpAberto();
	}

	internal static bool GateOk(out string motivo)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Invalid comparison between Unknown and I4
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Invalid comparison between Unknown and I4
		motivo = null;
		try
		{
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				motivo = "GUIManager.instance ausente";
				return false;
			}
			if (!Roguelike)
			{
				motivo = "fora do modo Roguelike (Root.PlayingRoguelike=false)";
				return false;
			}
			if (LevelUpAberto())
			{
				return true;
			}
			if (instance.PingModeActive)
			{
				motivo = "modo de apontar o hex ligado (GUIManager.PingModeActive)";
				return false;
			}
			UIWindowManager instance2 = UIWindowManager.Instance;
			if ((Object)(object)instance2 != (Object)null && instance2.AnyUIWindowOpen)
			{
				motivo = "ja existe uma janela de UI aberta (UIWindowManager.OpenedWindows)";
				return false;
			}
			Root val = GameRoot();
			if (val == null)
			{
				motivo = "Root ausente (NetworkingManager/NetworkManager nulos)";
				return false;
			}
			if ((int)instance.CurrentGuiState == 2)
			{
				HexCellManager instance3 = HexCellManager.instance;
				if ((Object)(object)instance3 != (Object)null && (int)instance3.CurrentState == 2)
				{
					motivo = "mira de skill ativa (HexCellManager.CurrentState=Action): abrir aqui cancelaria o apontar hex";
					return false;
				}
				if (val.SpawnPlacementActive)
				{
					motivo = "posicionamento inicial em andamento (Root.SpawnPlacementActive)";
					return false;
				}
			}
			return true;
		}
		catch (Exception ex)
		{
			motivo = "falha ao avaliar o estado do jogo (" + ex.GetType().Name + ": " + ex.Message + ")";
			return false;
		}
	}

	private static Character AlvoDoLevelUp()
	{
		try
		{
			if (!LevelUpAberto())
			{
				return null;
			}
			RoguelikeManager instance = RoguelikeManager.Instance;
			Character currentRoguelikeSkillSelectingCharacter = instance.CurrentRoguelikeSkillSelectingCharacter;
			if (currentRoguelikeSkillSelectingCharacter != null)
			{
				return currentRoguelikeSkillSelectingCharacter;
			}
			List<CharacterLevelUpInfo> charactersWaitingForLevelUp = instance.CharactersWaitingForLevelUp;
			if (charactersWaitingForLevelUp != null && charactersWaitingForLevelUp.Count > 0 && charactersWaitingForLevelUp[0] != null)
			{
				return charactersWaitingForLevelUp[0].Character;
			}
			return null;
		}
		catch (Exception)
		{
			return null;
		}
	}

	internal static Character Resolve(out string motivo)
	{
		motivo = null;
		try
		{
			GameLogic instance = GameLogic.instance;
			if ((Object)(object)instance == (Object)null)
			{
				motivo = "GameLogic.instance ausente";
				return null;
			}
			Character val = AlvoDoLevelUp();
			if (val != null)
			{
				_fallbackLogado = null;
				if (!val.Owned)
				{
					motivo = "'" + val.CharacterName + "' esta em level-up mas e de outro jogador (Owned=false)";
					return null;
				}
				return val;
			}
			Character currentlySelectedCharacter = instance.CurrentlySelectedCharacter;
			if (currentlySelectedCharacter != null)
			{
				_fallbackLogado = null;
				if (!currentlySelectedCharacter.Owned)
				{
					motivo = "'" + currentlySelectedCharacter.CharacterName + "' e de outro jogador (Owned=false)";
					return null;
				}
				return currentlySelectedCharacter;
			}
			Character val2 = FirstOwned(LocalParty());
			if (val2 == null)
			{
				motivo = "nenhum personagem SEU na party e nenhum selecionado no jogo";
				return null;
			}
			if (_fallbackLogado != val2.CharacterName)
			{
				_fallbackLogado = val2.CharacterName;
				Plugin.Log.LogInfo((object)("RSTV-5: o jogo esta sem personagem selecionado — usando o fallback documentado '" + val2.CharacterName + "' (primeiro personagem local da party)."));
			}
			return val2;
		}
		catch (Exception ex)
		{
			motivo = "falha ao resolver o alvo (" + ex.GetType().Name + ": " + ex.Message + ")";
			return null;
		}
	}

	private static List<Character> LocalParty()
	{
		NetworkingManager instance = NetworkingManager.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return null;
		}
		List<Character> myPartyCharacters = instance.MyPartyCharacters;
		if (myPartyCharacters != null && myPartyCharacters.Count > 0)
		{
			return myPartyCharacters;
		}
		return instance.MyPartyCharactersUnaccepted;
	}

	private static Character FirstOwned(List<Character> lista)
	{
		if (lista == null)
		{
			return null;
		}
		for (int i = 0; i < lista.Count; i++)
		{
			Character val = lista[i];
			if (val != null && val.Owned)
			{
				return val;
			}
		}
		return null;
	}
}
internal static class ReadOnlySession
{
	internal static bool Active { get; private set; }

	internal static Character Target { get; private set; }

	internal static ReadOnlyContext Context { get; private set; }

	internal static void Begin(Character target, ReadOnlyContext context)
	{
		Active = true;
		Target = target;
		Context = context;
		Plugin.Log.LogInfo((object)("RSTV-16: modo somente leitura ATIVO (contexto=" + context.ToString() + ", alvo=" + ((target != null) ? target.CharacterName : "nenhum") + ") — a vida agora e a do hospedeiro ativo (a aba do inventario ou a janela propria, RSTV-21)."));
	}

	internal static void End()
	{
		if (Active)
		{
			Active = false;
			Target = null;
			Plugin.Log.LogInfo((object)"RSTV-16: modo somente leitura encerrado (nada foi gravado no personagem).");
		}
	}

	internal static void Close()
	{
		End();
		try
		{
			CharacterMenusManager instance = CharacterMenusManager.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				((UIWindow)instance).CloseWindow();
			}
			if ((Object)(object)GUIManager.instance != (Object)null && (Object)(object)GUIManager.instance.tooltip != (Object)null)
			{
				GUIManager.instance.tooltip.HideTooltip();
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-16: falha ao fechar a sessao read-only: " + ex.Message));
		}
	}
}
internal static class SkillTreeShortcut
{
	internal unsafe static void Handle(KeybindManager kb)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!Plugin.AtalhoLigado)
			{
				return;
			}
			KeyCode teclaAtalho = Plugin.TeclaAtalho;
			if ((int)teclaAtalho != 0 && Input.GetKeyDown(teclaAtalho))
			{
				if (!Liberado(kb, out var motivo))
				{
					Plugin.Log.LogInfo((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&teclaAtalho))/*cast due to .constrained prefix*/).ToString() + " IGNORADO — " + motivo + "."));
				}
				else
				{
					Abrir(teclaAtalho);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV: falha ao tratar o atalho da skill tree: " + ex));
		}
	}

	private unsafe static bool Liberado(KeybindManager kb, out string motivo)
	{
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Invalid comparison between Unknown and I4
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Invalid comparison between Unknown and I4
		motivo = null;
		if ((Object)(object)kb == (Object)null)
		{
			motivo = "KeybindManager ainda nao existe";
			return false;
		}
		if (kb.DisableKeybinds)
		{
			motivo = "keybinds desabilitados pelo jogo (KeybindManager.DisableKeybinds)";
			return false;
		}
		OptionsManager instance = OptionsManager.instance;
		if ((Object)(object)instance == (Object)null || instance.CurrentlyBindingControls)
		{
			motivo = "remap de tecla em andamento (OptionsManager.CurrentlyBindingControls)";
			return false;
		}
		if (FocoDeTexto())
		{
			motivo = "campo de texto em foco (chat/inventario/mochila/fortune)";
			return false;
		}
		if (EventWindow.IsNotNullAndIsActive)
		{
			motivo = "janela de evento aberta (EventWindow)";
			return false;
		}
		GUIManager instance2 = GUIManager.instance;
		if ((Object)(object)instance2 == (Object)null)
		{
			motivo = "GUIManager.instance ausente";
			return false;
		}
		GUIState currentGuiState = instance2.CurrentGuiState;
		if ((int)currentGuiState == 1 || (int)currentGuiState == 5)
		{
			motivo = "GUIState " + ((object)(*(GUIState*)(&currentGuiState))/*cast due to .constrained prefix*/).ToString() + " (menu sem alvo para a arvore)";
			return false;
		}
		return true;
	}

	private static bool FocoDeTexto()
	{
		try
		{
			MessageWindowManager instance = MessageWindowManager.instance;
			if ((Object)(object)instance != (Object)null && instance.TextInputIsFocused)
			{
				return true;
			}
			if (ItemStashManager.IsNotNullAndIsActive && LoadableUIWindow<ItemStashManager>.Instance.StashInventory.TextInputIsFocused)
			{
				return true;
			}
			if (ItemStashManager.IsNotNullAndIsActive && LoadableUIWindow<ItemStashManager>.Instance.PlayerInventory.TextInputIsFocused)
			{
				return true;
			}
			DebugWindow instance2 = DebugWindow.Instance;
			if ((Object)(object)instance2 != (Object)null && instance2.AllItemsInventory.TextInputIsFocused)
			{
				return true;
			}
			if (InventoryManager.IsNotNullAndIsActive && LoadableUIWindow<InventoryManager>.Instance.Inventory.TextInputIsFocused)
			{
				return true;
			}
			if (FortuneWindow.IsNotNullAndIsActive && LoadableUIWindow<FortuneWindow>.Instance.TextInputIsFocused)
			{
				return true;
			}
			return false;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-11: nao deu para ler o foco de texto (" + ex.Message + ") — atalho bloqueado por seguranca."));
			return true;
		}
	}

	private unsafe static void Abrir(KeyCode tecla)
	{
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		RstvHost.Ensure();
		if ((Object)(object)(CharacterChoiceManager.IsNotNullAndIsActive ? CharacterChoiceManager.Instance : null) != (Object)null)
		{
			Character val = PartyTargets.Resolve();
			if (val == null)
			{
				Plugin.Log.LogWarning((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " na tela Select Party sem alvo (nenhum personagem SEU na party) — nada foi aberto."));
				return;
			}
			Plugin.Log.LogInfo((object)("RSTV-16: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " -> aba 'All Skill Trees' do inventario para '" + val.CharacterName + "' (caminho da tela Select Party)."));
			SkillTreesTab.Abrir(val, ReadOnlyContext.PartyScreen);
			return;
		}
		GUIManager instance = GUIManager.instance;
		if ((Object)(object)instance == (Object)null)
		{
			Plugin.Log.LogWarning((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " ignorado — GUIManager.instance ausente."));
		}
		else if (!RunTargets.EstadoPermiteMenuDePersonagem(instance.CurrentGuiState))
		{
			Plugin.Log.LogWarning((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " ignorado — GUIState " + ((object)instance.CurrentGuiState/*cast due to .constrained prefix*/).ToString() + " nao permite abrir o menu de personagem (InTown/InWorldMap/InCutscene/InBattle)."));
		}
		else
		{
			UIWindowManager instance2 = UIWindowManager.Instance;
			if (!RunTargets.JanelaDoLevelUpAberta() && (Object)(object)instance2 != (Object)null && instance2.AnyUIWindowOpenOtherThan((UIWindow)(object)RoguelikeManager.Instance))
			{
				Plugin.Log.LogWarning((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " ignorado — ja existe uma janela de UI aberta (UIWindowManager.OpenedWindows)."));
				return;
			}
			Plugin.Log.LogInfo((object)("RSTV-11: atalho " + ((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString() + " -> despacho da run (RunButton.OpenForTarget)."));
			RunButton.OpenForTarget();
		}
	}
}
internal static class SkillTreesTab
{
	private sealed class BotaoManualDeClasse
	{
		internal SkillType Tipo;

		internal Image Highlight;
	}

	internal const string TabName = "RstvAllSkillTreesTab";

	internal const string TabLabel = "All Trees";

	internal const string PanelName = "RstvAllSkillTreesPanel";

	internal const string ClassBarName = "RstvClassBar";

	internal const string TreeAreaName = "RstvTreeArea";

	internal const string NodeContainerName = "RstvNodeContainer";

	private const float MargemSuperior = 6f;

	private const float MargemInferior = 8f;

	private const float MargemLateral = 8f;

	private const float AlturaDaBarraDeClasses = 84f;

	private const float LadoDoBotaoDeClasse = 64f;

	private const float LadoDoIconeDaClasse = 56f;

	private const float EspacamentoDaBarraDeClasses = 9.6f;

	private const float LarguraDoRotuloDaClasse = 37.86f;

	private const float AlturaDoRotuloDaClasse = 40f;

	private const float EscalaDoRotuloDaClasse = 0.7f;

	private const float YDoRotuloDaClasse = -27.1f;

	private const float DeslocamentoDoIconeDaClasse = 4f;

	private const float TamanhoDaFonteDoRotulo = 15f;

	private const float LarguraDoRotuloManual = 64f;

	private const float AlturaDoRotuloManual = 16f;

	private const float YDoRotuloManual = -29f;

	private const float PaddingHorizontalPadrao = 47f;

	private const float PaddingVerticalPadrao = 35f;

	private const float EscalaDoContainerDeNos = 1.7f;

	private const float LarguraDoContainerDeNos = 1000f;

	private const float AlturaDoContainerDeNos = 800f;

	private const float YDoContainerDeNos = 26.7f;

	private const float LadoDoNoDoPrefab = 30f;

	private const float EscalaDoNoDoPrefab = 0.9f;

	private static CharacterMenusManager _menu;

	private static MenuTabManager _mgr;

	private static MenuTab _aba;

	private static TextMeshProUGUI _rotuloAba;

	private static GameObject _painel;

	private static RectTransform _areaDaArvore;

	private static RectTransform _containerDeNos;

	private static GameObject _barraDeClasses;

	private static SkillType _tipoAtual = (SkillType)0;

	private static SkillType _construidoPara = (SkillType)(-1);

	private static readonly List<SkillTreeItem> _itens = new List<SkillTreeItem>();

	private static readonly List<SkillTreeTab> _botoesDeClasse = new List<SkillTreeTab>();

	private static readonly List<BotaoManualDeClasse> _botoesManuais = new List<BotaoManualDeClasse>();

	private static bool _barraConstruida;

	private static bool _modoJanela;

	private static bool _pedido;

	private static float _pedidoEm;

	private static ReadOnlyContext _contextoPendente = ReadOnlyContext.Inventario;

	private static string _ultimoAviso;

	private static Character _alvoDoPedido;

	private static Vector2? _underlineOriginal;

	private static bool _underlineAjustado;

	internal static bool Aberto
	{
		get
		{
			if ((Object)(object)_painel != (Object)null)
			{
				return _painel.activeSelf;
			}
			return false;
		}
	}

	internal static void Ensure(CharacterMenusManager menu)
	{
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Expected O, but got Unknown
		try
		{
			if ((Object)(object)menu == (Object)null)
			{
				Aviso("CharacterMenusManager nulo — impossivel injetar a aba");
				return;
			}
			_menu = menu;
			MenuTabManager componentInChildren = ((Component)menu).GetComponentInChildren<MenuTabManager>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Aviso("MenuTabManager nao encontrado dentro do CharacterMenusManager (a barra de abas mudou?)");
				return;
			}
			_mgr = componentInChildren;
			MenuTab val = Procurar(componentInChildren);
			if ((Object)(object)val != (Object)null)
			{
				_aba = val;
				_rotuloAba = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
				FixarLarguraDaAba(((Component)val).gameObject, componentInChildren);
				return;
			}
			if ((Object)(object)menu.TabSkillTree == (Object)null)
			{
				Aviso("menu.TabSkillTree nulo — sem o molde nativo nao ha como clonar a aba");
				return;
			}
			Transform parent = ((Component)menu.TabSkillTree).transform.parent;
			GameObject obj = Object.Instantiate<GameObject>(((Component)menu.TabSkillTree).gameObject, parent);
			((Object)obj).name = "RstvAllSkillTreesTab";
			obj.SetActive(true);
			obj.transform.SetAsLastSibling();
			NativeUiHelpers.DisableLocalizers(obj);
			TextMeshProUGUI componentInChildren2 = obj.GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				((TMP_Text)componentInChildren2).text = OptionsManager.Localize("All Trees", (LanguageGender)0, (Gender)2);
				_rotuloAba = componentInChildren2;
			}
			else
			{
				Aviso("o clone da aba ficou SEM TextMeshProUGUI — a aba aparece sem rotulo");
			}
			FixarLarguraDaAba(obj, componentInChildren);
			MenuTab component = obj.GetComponent<MenuTab>();
			if ((Object)(object)component == (Object)null)
			{
				Aviso("o clone da aba ficou SEM o componente MenuTab (o proprio jogo o reinicializa no Update da barra?)");
			}
			Button component2 = obj.GetComponent<Button>();
			if ((Object)(object)component2 != (Object)null)
			{
				NativeUiHelpers.ClearClickListeners(component2);
				((UnityEvent)component2.onClick).AddListener(new UnityAction(SelecionarEMostrar));
			}
			else
			{
				Aviso("o clone da aba ficou SEM Button — a aba nao seria clicavel");
			}
			MenuTab[] array = componentInChildren.MenuTabs;
			if (array == null)
			{
				array = (MenuTab[])(object)new MenuTab[0];
			}
			MenuTab[] array2 = (MenuTab[])(object)new MenuTab[array.Length + 1];
			Array.Copy(array, array2, array.Length);
			array2[array.Length] = component;
			componentInChildren.MenuTabs = array2;
			_aba = component;
			Plugin.Log.LogInfo((object)("RSTV-16: aba 'All Trees' injetada na barra '" + (((Object)(object)parent != (Object)null) ? ((Object)parent).name : "?") + "' (clone de '" + ((Object)((Component)menu.TabSkillTree).gameObject).name + "'); abas agora: " + array2.Length + "; rotulo=" + (((Object)(object)componentInChildren2 != (Object)null) ? ((TMP_Text)componentInChildren2).text : "(sem TMP)") + "; onClick limpo -> SkillTreesTab.SelecionarEMostrar; MenuTabs inclui a aba."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-16: falha ao injetar a aba 'All Trees': " + ex));
		}
	}

	private static MenuTab Procurar(MenuTabManager mgr)
	{
		if ((Object)(object)mgr == (Object)null)
		{
			return null;
		}
		MenuTab[] menuTabs = mgr.MenuTabs;
		if (menuTabs != null)
		{
			foreach (MenuTab val in menuTabs)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && ((Object)((Component)val).gameObject).name == "RstvAllSkillTreesTab")
				{
					return val;
				}
			}
		}
		Transform val2 = ((Component)mgr).transform.Find("RstvAllSkillTreesTab");
		if ((Object)(object)val2 != (Object)null)
		{
			return ((Component)val2).GetComponent<MenuTab>();
		}
		return null;
	}

	private static void FixarLarguraDaAba(GameObject clone, MenuTabManager mgr)
	{
		try
		{
			float num = LarguraDaAbaNativa(mgr, clone);
			if (!(num <= 1f))
			{
				LayoutElement val = clone.GetComponent<LayoutElement>();
				if ((Object)(object)val == (Object)null)
				{
					val = clone.AddComponent<LayoutElement>();
				}
				val.preferredWidth = num;
				val.minWidth = 0f;
				val.flexibleWidth = 0f;
				Plugin.Log.LogInfo((object)("RSTV-17: largura da aba 'All Trees' travada em " + num.ToString("0.##") + "u (a maior aba nativa) para o grupo centrado nao desequilibrar."));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-17: nao deu para fixar a largura da aba: " + ex.Message));
		}
	}

	private static float LarguraDaAbaNativa(MenuTabManager mgr, GameObject nova)
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		float num = 0f;
		MenuTab[] array = (((Object)(object)mgr != (Object)null) ? mgr.MenuTabs : null);
		if (array != null)
		{
			foreach (MenuTab val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || (Object)(object)((Component)val).gameObject == (Object)(object)nova)
				{
					continue;
				}
				float num2 = 0f;
				TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					num2 = ((TMP_Text)componentInChildren).preferredWidth;
				}
				if (num2 <= 1f)
				{
					Transform transform = ((Component)val).transform;
					RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if ((Object)(object)val2 != (Object)null)
					{
						Rect rect = val2.rect;
						num2 = ((Rect)(ref rect)).width;
					}
				}
				if (num2 > num)
				{
					num = num2;
				}
			}
		}
		return num;
	}

	internal static void Abrir(Character alvo, ReadOnlyContext contexto)
	{
		try
		{
			CharacterMenusManager instance = CharacterMenusManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Plugin.Log.LogError((object)"RSTV-16: CharacterMenusManager.Instance ausente — sem o menu nao ha aba; nada foi aberto.");
				return;
			}
			RstvHost.Ensure();
			SairDoModoJanela();
			_contextoPendente = contexto;
			_alvoDoPedido = alvo;
			Ensure(instance);
			if ((Object)(object)_mgr == (Object)null || (Object)(object)_aba == (Object)null)
			{
				Plugin.Log.LogWarning((object)"RSTV-16: a aba nao esta na barra — o inventario sera aberto, mas a aba 'All Trees' nao existe nesta sessao.");
			}
			if (((Component)instance).gameObject.activeSelf)
			{
				Plugar();
				return;
			}
			_pedido = true;
			_pedidoEm = Time.realtimeSinceStartup;
			Plugin.Log.LogInfo((object)("RSTV-16: abrindo o inventario (CharacterMenusManager.OpenCharacterMenu) para selecionar a aba 'All Trees' (alvo=" + ((alvo != null) ? alvo.CharacterName : "nenhum") + ")."));
			instance.OpenCharacterMenu();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-16: falha ao abrir a aba 'All Trees': " + ex));
		}
	}

	private static void Plugar()
	{
		if ((Object)(object)_mgr == (Object)null || (Object)(object)_aba == (Object)null)
		{
			_pedido = false;
			return;
		}
		_pedido = false;
		_mgr.SelectButtonAndInvoke(_aba);
	}

	internal static void AbrirJanela(Character alvo, ReadOnlyContext contexto, Transform dono)
	{
		try
		{
			RstvHost.Ensure();
			SkillTreesWindow.Fechou = AoFecharJanela;
			_contextoPendente = contexto;
			_alvoDoPedido = alvo;
			if ((Object)(object)SkillTreesWindow.Garantir(dono) == (Object)null)
			{
				Plugin.Log.LogError((object)"RSTV-21: nao foi possivel criar a janela read-only — nada foi aberto.");
				return;
			}
			if (!_modoJanela)
			{
				DestruirPainel();
			}
			_modoJanela = true;
			IniciarSessaoReadOnly();
			Mostrar();
			Plugin.Log.LogInfo((object)("RSTV-21: janela read-only ABERTA com TODAS as arvores (alvo=" + ((alvo != null) ? alvo.CharacterName : "nenhum") + ", contexto=" + contexto.ToString() + ") — sem depender do inventario."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-21: falha ao abrir a janela read-only: " + ex));
		}
	}

	private static void SairDoModoJanela()
	{
		if (_modoJanela)
		{
			_modoJanela = false;
			SkillTreesWindow.Fechar();
			DestruirPainel();
		}
	}

	private static void AoFecharJanela()
	{
		if (_modoJanela)
		{
			_modoJanela = false;
			DestruirPainel();
			if (ReadOnlySession.Active)
			{
				ReadOnlySession.End();
				Plugin.Log.LogInfo((object)"RSTV-21: a janela read-only fechou — modo janela largado e a sessao read-only encerrada.");
			}
		}
	}

	private static void DestruirPainel()
	{
		try
		{
			LimparItens();
			AjustarUnderline(ligado: false);
			if ((Object)(object)_painel != (Object)null)
			{
				Object.Destroy((Object)(object)_painel);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-21: falha ao desmontar o painel das arvores: " + ex.Message));
		}
		_painel = null;
		_areaDaArvore = null;
		_containerDeNos = null;
		_barraDeClasses = null;
		_barraConstruida = false;
		_botoesDeClasse.Clear();
		_botoesManuais.Clear();
	}

	internal static void SelecionarEMostrar()
	{
		try
		{
			RstvHost.Ensure();
			SairDoModoJanela();
			IniciarSessaoReadOnly();
			if ((Object)(object)_mgr != (Object)null && (Object)(object)_aba != (Object)null)
			{
				_mgr.SelectButton(_aba);
			}
			FecharConteudosNativos();
			Mostrar();
			AjustarUnderline(ligado: true);
			Plugin.Log.LogInfo((object)("RSTV-16: aba 'All Trees' selecionada — arvores read-only visiveis (classe=" + ((object)Unsafe.As<SkillType, SkillType>(ref _tipoAtual)/*cast due to .constrained prefix*/).ToString() + ")."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("RSTV-16: falha ao tratar o clique da aba 'All Trees': " + ex));
		}
	}

	private static void IniciarSessaoReadOnly()
	{
		if (ReadOnlySession.Active)
		{
			return;
		}
		Character val = null;
		string motivo = null;
		Character alvoDoPedido = _alvoDoPedido;
		_alvoDoPedido = null;
		if (alvoDoPedido != null)
		{
			val = alvoDoPedido;
			motivo = "alvo explicito do pedido (" + alvoDoPedido.CharacterName + ")";
		}
		else
		{
			try
			{
				val = RunTargets.Resolve(out motivo);
			}
			catch (Exception ex)
			{
				motivo = ex.GetType().Name + ": " + ex.Message;
			}
		}
		if (val == null)
		{
			Plugin.Log.LogInfo((object)("RSTV-16: nenhum personagem local resolvido para o contexto (" + motivo + ") — a aba mostra TODAS as arvores; o bloqueio de clique segue ativo pela sessao."));
		}
		ReadOnlySession.Begin(val, _contextoPendente);
	}

	private static void FecharConteudosNativos()
	{
		CharacterMenusManager val = (((Object)(object)_menu != (Object)null) ? _menu : CharacterMenusManager.Instance);
		if (!((Object)(object)val == (Object)null))
		{
			val.CloseCharacterMenu();
			val.CloseSkillTreeMenu();
			val.CloseFortuneMenu();
		}
	}

	internal static void Tick()
	{
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			SkillTreesWindow.Atualizar();
			if (_pedido)
			{
				CharacterMenusManager val = (((Object)(object)_menu != (Object)null) ? _menu : CharacterMenusManager.Instance);
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy)
				{
					Plugar();
				}
				else if (Time.realtimeSinceStartup - _pedidoEm > 5f)
				{
					_pedido = false;
					Plugin.Log.LogWarning((object)"RSTV-16: o inventario nao abriu em 5 s (CharacterMenusManager seguiu inativo) — o pedido da aba 'All Trees' foi descartado.");
				}
			}
			if ((Object)(object)_painel == (Object)null)
			{
				return;
			}
			bool flag = DeviaEstarVisivel();
			if (_painel.activeSelf == flag)
			{
				if (flag && _construidoPara != _tipoAtual)
				{
					Popular(_tipoAtual);
				}
				return;
			}
			_painel.SetActive(flag);
			if (flag)
			{
				AjustarUnderline(ligado: true);
				if (_construidoPara != _tipoAtual)
				{
					Popular(_tipoAtual);
				}
				return;
			}
			AjustarUnderline(ligado: false);
			if (ReadOnlySession.Active)
			{
				ReadOnlySession.End();
				Plugin.Log.LogInfo((object)(_modoJanela ? "RSTV-21: a janela read-only deixou de estar aberta — sessao read-only encerrada." : "RSTV-16: a aba 'All Trees' deixou de ser a selecionada — sessao read-only encerrada."));
			}
		}
		catch (Exception ex)
		{
			if (!(_ultimoAviso == "tick"))
			{
				_ultimoAviso = "tick";
				Plugin.Log.LogError((object)("RSTV-16: falha no Tick da aba: " + ex));
			}
		}
	}

	private static bool DeviaEstarVisivel()
	{
		if (_modoJanela)
		{
			return SkillTreesWindow.Aberta;
		}
		if ((Object)(object)_aba == (Object)null || (Object)(object)_mgr == (Object)null)
		{
			return false;
		}
		CharacterMenusManager val = (((Object)(object)_menu != (Object)null) ? _menu : CharacterMenusManager.Instance);
		if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy)
		{
			return false;
		}
		return (Object)(object)_mgr.SelectedMenuTab == (Object)(object)_aba;
	}

	private static void AjustarUnderline(bool ligado)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (_modoJanela || (Object)(object)_mgr == (Object)null || (Object)(object)_mgr.Underline == (Object)null)
			{
				return;
			}
			if (ligado)
			{
				if (!_underlineOriginal.HasValue)
				{
					_underlineOriginal = _mgr.Underline.sizeDelta;
				}
				float num = LarguraDoRotulo();
				_mgr.Underline.sizeDelta = new Vector2(num, _underlineOriginal.Value.y);
				_underlineAjustado = true;
			}
			else if (_underlineAjustado && _underlineOriginal.HasValue)
			{
				_mgr.Underline.sizeDelta = _underlineOriginal.Value;
				_underlineAjustado = false;
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RSTV-16: nao deu para ajustar a Underline das abas: " + ex.Message));
		}
	}

	private static float LarguraDoRotulo()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)_rotuloAba != (Object)null)
			{
				float preferredWidth = ((TMP_Text)_rotuloAba).preferredWidth;
				if (preferredWidth > 1f)
				{
					return preferredWidth;
				}
				Vector2 preferredValues = ((TMP_Text)_rotuloAba).GetPreferredValues(((TMP_Text)_rotuloAba).text);
				if (preferredValues.x > 1f)
				{
					return preferredValues.x;
				}
			}
		}
		catch (Exception)
		{
		}
		return 130f;
	}

	private static void Mostrar()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_painel == (Object)null)
		{
			ConstruirPainel();
		}
		if (!((Object)(object)_painel == (Object)null))
		{
			_painel.SetActive(true);
			if (!_barraConstruida)
			{
				ConstruirBarraDeClasses();
			}
			if (_construidoPara != _tipoAtual)
			{
				Popular(_tipoAtual);
			}
		}
	}

	internal static List<SkillType> TiposDaAba()
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Invalid comparison between Unknown and I4
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Invalid comparison between Unknown and I4
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Invalid comparison between Unknown and I4
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		List<SkillType> list = new List<SkillType>();
		try
		{
			GlobalSettingsManager instance = GlobalSettingsManager.instance;
			SteamManager instance2 = SteamManager.instance;
			Game instance3 = Game.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.miscSettings == (Object)null || instance.miscSettings.SkillTabTypes == null || (Object)(object)instance2 == (Object)null || (Object)(object)instance3 == (Object)null || instance3.Skills == null)
			{
				Aviso("GlobalSettingsManager/SteamManager/Game.Skills indisponivel — sem isso nao ha classes para montar");
				return list;
			}
			List<SkillType> skillTabTypes = instance.miscSettings.SkillTabTypes;
			for (int i = 0; i < skillTabTypes.Count; i++)
			{
				SkillType val = skillTabTypes[i];
				if ((int)val != 13 && (int)val != 8 && (int)val != 9 && instance2.MeetsDLCRequirements(val) && TemSkillsVisiveis(val) && !list.Contains(val))
				{
					list.Add(val);
				}
			}
		}
		catch (Exception ex)
		{
			Aviso("nao deu para montar a lista de classes: " + ex.Message);
		}
		return list;
	}

	private static bool TemSkillsVisiveis(SkillType tipo)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Game instance = Game.Instance;
			if ((Object)(object)instance == (Object)null || instance.Skills == null)
			{
				return false;
			}
			List<SkillInfo> skills = instance.Skills;
			for (int i = 0; i < skills.Count; i++)
			{
				SkillInfo val = skill