using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Burst2Flame;
using HarmonyLib;
using TMPro;
using UnityEngine;
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("Botao na tela Select Party do Roguelike que abre a Skill Tree nativa em modo somente leitura.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("RoguelikeSkillTreeVisualizer")]
[assembly: AssemblyTitle("RoguelikeSkillTreeVisualizer")]
[assembly: AssemblyVersion("0.1.0.0")]
namespace RoguelikeSkillTreeVisualizer;
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), "OpenCharacterChoiceManager", new Type[] { })]
internal static class CharacterChoiceOpenPatch
{
private static bool _firstCallLogged;
[HarmonyPostfix]
private static void Postfix(CharacterChoiceManager __instance)
{
try
{
if (!_firstCallLogged)
{
_firstCallLogged = true;
Plugin.Log.LogInfo((object)"RSTV-2: gancho da tela Select Party ATIVO (OpenCharacterChoiceManager).");
}
RstvHost.Ensure();
SelectPartyButton.Ensure(__instance);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("RSTV: falha no postfix de OpenCharacterChoiceManager: " + ex));
}
}
}
[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 active;
try
{
active = ReadOnlySession.Active;
}
catch (Exception)
{
return true;
}
if (!active)
{
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()
{
if (!ReadOnlySession.Active)
{
return true;
}
if (!_firstBlockLogged)
{
_firstBlockLogged = true;
Plugin.Log.LogInfo((object)"RSTV-2: ResetSkillPoints BLOQUEADO (read-only) — nada foi zerado no personagem.");
}
return false;
}
}
[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));
}
}
}
[BepInPlugin("com.gumatos.roguelikeskilltreevisualizer", "Roguelike Skill Tree Visualizer", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
public const string Guid = "com.gumatos.roguelikeskilltreevisualizer";
public const string Version = "0.1.0";
internal static ManualLogSource Log { get; private set; }
internal static ConfigEntry<bool> AtivarBotao { get; private set; }
internal static bool BotaoLigado
{
get
{
if (AtivarBotao != null)
{
return AtivarBotao.Value;
}
return true;
}
}
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
try
{
AtivarBotao = ((BaseUnityPlugin)this).Config.Bind<bool>("Geral", "AtivarBotao", true, "Injeta o botao 'Skills' na tela Select Party do modo Roguelike. Padrao: true. Com false o mod carrega e registra os ganchos, mas nao cria botao nenhum na tela.");
}
catch (Exception ex)
{
Log.LogError((object)("RSTV: falha ao ler/criar o arquivo de config — seguindo com o padrao (botao LIGADO): " + ex.Message));
}
Log.LogInfo((object)"Roguelike Skill Tree Visualizer 0.1.0 carregado (RSTV-2: botao ao lado do Choose Powerups + skill tree read-only).");
AplicarPatches();
Log.LogInfo((object)("RSTV DIAG: botao 'Skills' " + (BotaoLigado ? "HABILITADO" : "DESABILITADO") + " nesta sessao (AtivarBotao=" + BotaoLigado + " no config)."));
}
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 class RstvHost : MonoBehaviour
{
private static RstvHost _instance;
private float _nextMirror;
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
{
SkillTreeReadOnly.Tick();
if (!(Time.realtimeSinceStartup < _nextMirror))
{
_nextMirror = Time.realtimeSinceStartup + 0.2f;
SelectPartyButton.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 SelectPartyButton
{
internal const string ButtonName = "RstvSkillTreeButton";
internal const string Label = "Skills";
private static CharacterChoiceManager _owner;
private static GameObject _button;
private static string _reason;
private static string _reasonLogged;
private static CharacterChoiceManager _diagnosedFor;
internal static void Ensure(CharacterChoiceManager manager)
{
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Expected O, but got Unknown
try
{
if ((Object)(object)manager == (Object)null)
{
Fail("a instancia da tela Select Party chegou nula", aviso: false);
return;
}
if (!Plugin.BotaoLigado)
{
Fail("AtivarBotao=false no arquivo de config", aviso: false);
return;
}
Button roguelikePowerupButton = manager.roguelikePowerupButton;
if ((Object)(object)roguelikePowerupButton == (Object)null)
{
Fail("o botao nativo do qual o clone nasce (roguelikePowerupButton / 'Choose Powerups') nao existe nesta tela", aviso: true);
}
else if ((Object)(object)((Component)roguelikePowerupButton).transform == (Object)null || (Object)(object)((Component)roguelikePowerupButton).transform.parent == (Object)null)
{
Fail("o botao nativo 'Choose Powerups' esta sem Transform ou sem pai na hierarquia", aviso: true);
}
else
{
if ((Object)(object)_owner == (Object)(object)manager && (Object)(object)_button != (Object)null)
{
return;
}
Transform parent = ((Component)roguelikePowerupButton).transform.parent;
RectTransform component = ((Component)roguelikePowerupButton).GetComponent<RectTransform>();
if ((Object)(object)component == (Object)null)
{
Fail("o botao nativo 'Choose Powerups' nao tem RectTransform (sem isso nao da para medir/repor a largura da linha)", aviso: true);
return;
}
float num = RowWidth(parent);
float num2 = WorldWidth(component);
if (num2 <= 1f)
{
num2 = component.sizeDelta.x;
}
float side = SquareSide(roguelikePowerupButton, component);
Transform val = parent.Find("RstvSkillTreeButton");
GameObject val2;
if ((Object)(object)val != (Object)null)
{
val2 = ((Component)val).gameObject;
}
else
{
val2 = Object.Instantiate<GameObject>(((Component)roguelikePowerupButton).gameObject, parent);
((Object)val2).name = "RstvSkillTreeButton";
}
val2.transform.SetSiblingIndex(((Component)roguelikePowerupButton).transform.GetSiblingIndex() + 1);
RectTransform component2 = val2.GetComponent<RectTransform>();
if ((Object)(object)component2 == (Object)null)
{
Fail("o clone do botao ficou sem RectTransform", aviso: true);
return;
}
TextMeshProUGUI componentInChildren = val2.GetComponentInChildren<TextMeshProUGUI>(true);
if ((Object)(object)componentInChildren != (Object)null)
{
((TMP_Text)componentInChildren).text = OptionsManager.Localize("Skills", (LanguageGender)0, (Gender)2);
}
DisableLocalizers(val2);
val2.SetActive(((Component)roguelikePowerupButton).gameObject.activeSelf);
ApplySquareLayout(roguelikePowerupButton, component, val2, component2, side, num2, parent);
Button component3 = val2.GetComponent<Button>();
if ((Object)(object)component3 != (Object)null)
{
((UnityEventBase)component3.onClick).RemoveAllListeners();
((UnityEvent)component3.onClick).AddListener(new UnityAction(OnClick));
((Selectable)component3).interactable = PartyTargets.Resolve() != null;
}
_owner = manager;
_button = val2;
_reason = null;
_reasonLogged = null;
_diagnosedFor = manager;
Plugin.Log.LogInfo((object)("RSTV-2: botao 'Skills' injetado (" + side.ToString("0.#") + "x" + side.ToString("0.#") + " px) a direita do Choose Powerups — largura da linha " + num.ToString("0.#") + " -> " + RowWidth(parent).ToString("0.#") + " px; pai=" + ((Object)parent).name + "; grupo=" + DescribeGroup(parent)));
}
}
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: " + ex));
}
}
internal static void Mirror()
{
CharacterChoiceManager instance = CharacterChoiceManager.Instance;
if ((Object)(object)instance == (Object)null || !Plugin.BotaoLigado)
{
return;
}
if ((Object)(object)_button == (Object)null)
{
if ((Object)(object)_diagnosedFor != (Object)(object)instance && ((Component)instance).gameObject.activeInHierarchy)
{
_diagnosedFor = instance;
DumpDiagnosis(instance);
}
}
else
{
if ((Object)(object)instance != (Object)(object)_owner)
{
return;
}
Button roguelikePowerupButton = instance.roguelikePowerupButton;
if ((Object)(object)roguelikePowerupButton == (Object)null)
{
return;
}
bool activeSelf = ((Component)roguelikePowerupButton).gameObject.activeSelf;
if (_button.activeSelf != activeSelf)
{
_button.SetActive(activeSelf);
}
Button component = _button.GetComponent<Button>();
if ((Object)(object)component != (Object)null)
{
bool flag = PartyTargets.Resolve() != null;
if (((Selectable)component).interactable != flag)
{
((Selectable)component).interactable = flag;
}
}
}
}
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' NAO injetado — " + motivo + "."));
}
else
{
Plugin.Log.LogInfo((object)("RSTV DIAG: botao 'Skills' desativado — " + motivo + "."));
}
}
}
private static void DumpDiagnosis(CharacterChoiceManager manager)
{
string text = (((Object)(object)manager != (Object)null && (Object)(object)((Component)manager).gameObject != (Object)null) ? ((Object)((Component)manager).gameObject).name : "AUSENTE");
bool flag = (Object)(object)manager != (Object)null && (Object)(object)((Component)manager).gameObject != (Object)null && ((Component)manager).gameObject.activeInHierarchy;
Button val = (((Object)(object)manager != (Object)null) ? manager.roguelikePowerupButton : null);
Transform val2 = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.parent : null);
Plugin.Log.LogWarning((object)("RSTV DIAG: a tela Select Party esta aberta e o botao 'Skills' NAO foi injetado. motivo=" + ((_reason != null) ? _reason : "desconhecido") + "; tela=" + text + "; ativaNaHierarquia=" + flag + "; botao nativo do clone=" + (((Object)(object)val != (Object)null) ? "presente" : "AUSENTE") + "; paiDoBotao=" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "AUSENTE") + "; grupo=" + DescribeGroup(val2) + "; party local=" + PartyTargets.LocalPartyCount() + " personagem(ns)."));
}
private static void OnClick()
{
try
{
Character val = PartyTargets.Resolve();
if (val == null)
{
Plugin.Log.LogWarning((object)"RSTV: clique sem personagem local na party — nada a abrir.");
return;
}
Plugin.Log.LogInfo((object)("RSTV-2: clique no botao 'Skills' -> '" + val.CharacterName + "' (nivel " + val.Level + ")."));
SkillTreeReadOnly.Open(val);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("RSTV: falha no clique do botao: " + ex));
}
}
private static float SquareSide(Button original, RectTransform rect)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
Rect rect2 = rect.rect;
float num = ((Rect)(ref rect2)).height;
if (num <= 1f)
{
num = rect.sizeDelta.y;
}
if (num <= 1f)
{
LayoutElement component = ((Component)original).GetComponent<LayoutElement>();
if ((Object)(object)component != (Object)null && component.preferredHeight > 0f)
{
num = component.preferredHeight;
}
}
if (num <= 1f)
{
num = 44f;
}
return num;
}
private static void ApplySquareLayout(Button original, RectTransform srcRt, GameObject clone, RectTransform newRt, float side, float widthBefore, Transform parent)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_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_01d4: Unknown result type (might be due to invalid IL or missing references)
//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_0165: Unknown result type (might be due to invalid IL or missing references)
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
//IL_0185: Unknown result type (might be due to invalid IL or missing references)
Rect rect = srcRt.rect;
float num;
if (!(((Rect)(ref rect)).height > 1f))
{
num = side;
}
else
{
rect = srcRt.rect;
num = ((Rect)(ref rect)).height;
}
float num2 = num;
LayoutElement val = EnsureLayoutElement(((Component)original).gameObject);
LayoutElement obj = EnsureLayoutElement(clone);
obj.preferredWidth = side;
obj.minWidth = side;
obj.flexibleWidth = 0f;
obj.preferredHeight = num2;
obj.minHeight = num2;
HorizontalOrVerticalLayoutGroup val2 = FindRowGroup(parent, ((Component)original).transform);
bool flag = (Object)(object)val2 != (Object)null && val2.childControlWidth && !val2.childForceExpandWidth;
float num3 = (((Object)(object)val2 != (Object)null) ? val2.spacing : 0f);
if ((Object)(object)val2 != (Object)null && val2.childForceExpandWidth)
{
Plugin.Log.LogInfo((object)"RSTV: linha usa LayoutGroup com childForceExpandWidth — o proprio grupo acomoda o filho novo (sem compensacao manual de largura).");
}
else if (flag)
{
float num4 = ((val.preferredWidth > 0f) ? val.preferredWidth : widthBefore);
float num5 = side + num3;
float num6 = Mathf.Max(1f, num4 - num5);
val.minWidth = Mathf.Min((val.minWidth > 0f) ? val.minWidth : num6, num6);
val.preferredWidth = num6;
srcRt.sizeDelta = new Vector2(Mathf.Max(0f, srcRt.sizeDelta.x - num5), srcRt.sizeDelta.y);
newRt.sizeDelta = new Vector2(Mathf.Max(0f, newRt.sizeDelta.x - (widthBefore - side)), newRt.sizeDelta.y);
LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((parent is RectTransform) ? parent : null));
}
else
{
float num7 = ((num3 > 0f) ? num3 : 4f);
float num8 = side + num7;
ShrinkKeepingLeftEdge(srcRt, Mathf.Max(1f, widthBefore - num8));
newRt.sizeDelta = new Vector2(Mathf.Max(0f, newRt.sizeDelta.x - (widthBefore - side)), newRt.sizeDelta.y);
Canvas.ForceUpdateCanvases();
PlaceToTheRight(srcRt, newRt, num7);
}
Plugin.Log.LogInfo((object)("[RSTV-2] geometria: original=" + WorldWidth(srcRt).ToString("0.#") + " clone=" + WorldWidth(newRt).ToString("0.#") + " linha=" + (((Object)(object)((parent is RectTransform) ? parent : null) != (Object)null) ? RowWidth(parent).ToString("0.#") : "?")));
}
private static float WorldWidth(RectTransform rect)
{
if ((Object)(object)rect == (Object)null)
{
return 0f;
}
Vector3[] array = (Vector3[])(object)new Vector3[4];
rect.GetWorldCorners(array);
return array[3].x - array[0].x;
}
private static float RowWidth(Transform parent)
{
RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
if ((Object)(object)val == (Object)null || ((Transform)val).childCount == 0)
{
return 0f;
}
Vector3[] array = (Vector3[])(object)new Vector3[4];
float num = float.MaxValue;
float num2 = float.MinValue;
for (int i = 0; i < ((Transform)val).childCount; i++)
{
Transform child = ((Transform)val).GetChild(i);
RectTransform val2 = (RectTransform)(object)((child is RectTransform) ? child : null);
if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeSelf)
{
val2.GetWorldCorners(array);
num = Mathf.Min(num, array[0].x);
num2 = Mathf.Max(num2, array[3].x);
}
}
if (!(num2 > num))
{
return 0f;
}
return num2 - num;
}
private static void ShrinkKeepingLeftEdge(RectTransform rect, float newWidth)
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
Vector3[] array = (Vector3[])(object)new Vector3[4];
rect.GetWorldCorners(array);
float x = array[0].x;
float num = array[3].x - array[0].x;
if (!(num <= 1f))
{
rect.sizeDelta = new Vector2(rect.sizeDelta.x - (num - newWidth), rect.sizeDelta.y);
Canvas.ForceUpdateCanvases();
Vector3[] array2 = (Vector3[])(object)new Vector3[4];
rect.GetWorldCorners(array2);
float num2 = x - array2[0].x;
if (Mathf.Abs(num2) > 0.01f)
{
((Transform)rect).position = ((Transform)rect).position + new Vector3(num2, 0f, 0f);
}
}
}
private static void PlaceToTheRight(RectTransform left, RectTransform right, float gap)
{
//IL_003a: 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_004f: Unknown result type (might be due to invalid IL or missing references)
Vector3[] array = (Vector3[])(object)new Vector3[4];
Vector3[] array2 = (Vector3[])(object)new Vector3[4];
left.GetWorldCorners(array);
right.GetWorldCorners(array2);
float num = array[3].x + gap - array2[0].x;
((Transform)right).position = ((Transform)right).position + new Vector3(num, 0f, 0f);
}
private static HorizontalOrVerticalLayoutGroup FindRowGroup(Transform parent, Transform source)
{
if ((Object)(object)parent == (Object)null)
{
return null;
}
HorizontalOrVerticalLayoutGroup component = ((Component)parent).GetComponent<HorizontalOrVerticalLayoutGroup>();
if ((Object)(object)component != (Object)null && (Object)(object)source.parent == (Object)(object)((Component)component).transform)
{
return component;
}
Transform parent2 = parent.parent;
if ((Object)(object)parent2 != (Object)null)
{
HorizontalOrVerticalLayoutGroup component2 = ((Component)parent2).GetComponent<HorizontalOrVerticalLayoutGroup>();
if ((Object)(object)component2 != (Object)null && (Object)(object)source.parent == (Object)(object)((Component)component2).transform)
{
return component2;
}
}
return null;
}
private 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";
}
private static LayoutElement EnsureLayoutElement(GameObject go)
{
LayoutElement component = go.GetComponent<LayoutElement>();
if (!((Object)(object)component != (Object)null))
{
return go.AddComponent<LayoutElement>();
}
return component;
}
private 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 class SkillTreeReadOnly
{
private static Character _pendingTarget;
private static float _pendingSince;
private const float PendingTimeout = 15f;
internal static void Open(Character target)
{
try
{
OpenInternal(target);
}
catch (Exception ex)
{
_pendingTarget = null;
ReadOnlySession.End();
Plugin.Log.LogError((object)("RSTV: falha ao abrir a skill tree read-only: " + ex));
}
}
private static void OpenInternal(Character target)
{
if (target == null)
{
Plugin.Log.LogWarning((object)"RSTV: pedido de abrir a arvore sem personagem — nada a fazer.");
return;
}
if ((Object)(object)GameLogic.instance == (Object)null)
{
Plugin.Log.LogError((object)"RSTV: GameLogic.instance ausente — sem ele a arvore mostraria o personagem errado; abertura cancelada.");
return;
}
GameLogic.instance.CurrentlySelectedCharacter = target;
SkillTreeManager instance = LoadableUIWindow<SkillTreeManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
_pendingTarget = target;
_pendingSince = Time.realtimeSinceStartup;
ReferenceLoader instance2 = ReferenceLoader.Instance;
if ((Object)(object)instance2 == (Object)null)
{
Plugin.Log.LogError((object)"RSTV: ReferenceLoader ausente — nao da para carregar a skill tree.");
_pendingTarget = null;
}
else
{
Plugin.Log.LogInfo((object)"RSTV-2: SkillTreeManager ainda nao existia — carregando a instancia nativa.");
LoadableUIWindow<SkillTreeManager>.LoadInstanceReference(((Component)instance2.SkillTreeManager).gameObject, (Transform)(object)instance2.SkillTreeManagerPlaceholder);
}
}
else
{
Finish(target, instance);
}
}
internal static void Tick()
{
if (ReadOnlySession.Active && !PartyScreenIsOpen())
{
Plugin.Log.LogInfo((object)"RSTV-2: a tela Select Party fechou com a arvore aberta — fechando a skill tree read-only (nada foi gravado no personagem).");
ReadOnlySession.Close();
}
else if (_pendingTarget != null)
{
SkillTreeManager instance = LoadableUIWindow<SkillTreeManager>.Instance;
if ((Object)(object)instance != (Object)null)
{
Character pendingTarget = _pendingTarget;
_pendingTarget = null;
Finish(pendingTarget, instance);
}
else if (Time.realtimeSinceStartup - _pendingSince > 15f)
{
_pendingTarget = null;
Plugin.Log.LogError((object)"RSTV: timeout esperando o SkillTreeManager carregar (15s).");
}
}
}
private static bool PartyScreenIsOpen()
{
CharacterChoiceManager instance = CharacterChoiceManager.Instance;
if ((Object)(object)instance != (Object)null && (Object)(object)((Component)instance).gameObject != (Object)null)
{
return ((Component)instance).gameObject.activeSelf;
}
return false;
}
private static void Finish(Character target, SkillTreeManager instance)
{
try
{
if (target != null && !((Object)(object)instance == (Object)null))
{
ReadOnlySession.Begin(target);
((Component)instance).gameObject.SetActive(true);
instance.Initialize(target.SkillsFromPoints.ToList(), 0, false);
ZOrder.EnsureAbove(((Component)instance).transform, ((Object)(object)CharacterChoiceManager.Instance != (Object)null) ? ((Component)CharacterChoiceManager.Instance).transform : null);
ReadOnlySession.InstallCancelInterceptor();
Plugin.Log.LogInfo((object)("RSTV-2: skill tree read-only aberta para '" + target.CharacterName + "' (nivel " + target.Level + ", " + target.SkillsFromPoints.Count + " skills, 0 pontos). ativaNaHierarquia=" + ((Component)instance).gameObject.activeInHierarchy));
}
}
catch (Exception ex)
{
ReadOnlySession.End();
try
{
if ((Object)(object)instance != (Object)null)
{
((Component)instance).gameObject.SetActive(false);
}
}
catch (Exception)
{
}
Plugin.Log.LogError((object)("RSTV: falha ao abrir a skill tree: " + ex));
}
}
}
internal static class ReadOnlySession
{
private static Func<bool> _interceptor;
internal static bool Active { get; private set; }
internal static Character Target { get; private set; }
internal static void Begin(Character target)
{
Active = true;
Target = target;
Plugin.Log.LogInfo((object)"RSTV-2: modo somente leitura ATIVO.");
}
internal static void End()
{
if (Active)
{
Active = false;
Target = null;
RemoveCancelInterceptor();
ZOrder.Restore();
Plugin.Log.LogInfo((object)"RSTV-2: modo somente leitura encerrado (nada foi gravado no personagem).");
}
}
internal static void InstallCancelInterceptor()
{
_interceptor = OnCancelPressed;
UIWindowManager.CancelInterceptor = _interceptor;
}
internal static void RemoveCancelInterceptor()
{
if (_interceptor != null && UIWindowManager.CancelInterceptor == _interceptor)
{
UIWindowManager.CancelInterceptor = null;
}
_interceptor = null;
}
internal static void Close()
{
SkillTreeManager instance = LoadableUIWindow<SkillTreeManager>.Instance;
End();
try
{
if ((Object)(object)instance != (Object)null)
{
instance.SkillsToAdd.Clear();
instance.SkillsRemoved.Clear();
((Component)instance).gameObject.SetActive(false);
}
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: falha ao fechar a skill tree read-only: " + ex.Message));
}
}
private static bool OnCancelPressed()
{
if (!Active)
{
return false;
}
Plugin.Log.LogInfo((object)"RSTV-2: Esc/B consumido — fechando a skill tree read-only.");
Close();
return true;
}
}
internal static class ZOrder
{
private static Transform _movedBranch;
private static int _movedBranchOldIndex = -1;
internal static void EnsureAbove(Transform above, Transform reference)
{
try
{
Restore();
if ((Object)(object)above == (Object)null || (Object)(object)reference == (Object)null)
{
return;
}
Transform val = CommonAncestor(above, reference);
if ((Object)(object)val == (Object)null)
{
return;
}
Transform val2 = BranchUnder(val, above);
Transform val3 = BranchUnder(val, reference);
if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null))
{
if (val2.GetSiblingIndex() < val3.GetSiblingIndex())
{
_movedBranch = val2;
_movedBranchOldIndex = val2.GetSiblingIndex();
val2.SetAsLastSibling();
Plugin.Log.LogInfo((object)("RSTV-2: z-order — ramo '" + ((Object)val2).name + "' estava em " + _movedBranchOldIndex + " (atras de '" + ((Object)val3).name + "', " + val3.GetSiblingIndex() + "); movido para o fim."));
}
if ((Object)(object)above.parent == (Object)(object)reference.parent && above.GetSiblingIndex() < reference.GetSiblingIndex())
{
above.SetAsLastSibling();
}
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("RSTV: nao deu para garantir o z-order da arvore: " + ex.Message));
}
}
internal static void Restore()
{
try
{
if ((Object)(object)_movedBranch != (Object)null && _movedBranchOldIndex >= 0)
{
_movedBranch.SetSiblingIndex(_movedBranchOldIndex);
}
}
catch (Exception)
{
}
finally
{
_movedBranch = null;
_movedBranchOldIndex = -1;
}
}
private static Transform CommonAncestor(Transform a, Transform b)
{
HashSet<Transform> hashSet = new HashSet<Transform>();
Transform val = a;
while ((Object)(object)val != (Object)null)
{
hashSet.Add(val);
val = val.parent;
}
Transform val2 = b;
while ((Object)(object)val2 != (Object)null)
{
if (hashSet.Contains(val2))
{
return val2;
}
val2 = val2.parent;
}
return null;
}
private static Transform BranchUnder(Transform ancestor, Transform node)
{
Transform val = node;
while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)(object)ancestor)
{
val = val.parent;
}
return val;
}
}