RoguelikeSkillTreeVisualizer
Botao ao lado de Choose Powerups (Select Party do Roguelike) que abre a Skill Tree nativa em modo somente leitura, com o contexto real do personagem. Nenhum ponto e gasto.
| Date uploaded | 2 days ago |
| Version | 0.1.0 |
| Download link | DefRuivo_StolenRealmMods-RoguelikeSkillTreeVisualizer-0.1.0.zip |
| Downloads | 6 |
| Dependency string | DefRuivo_StolenRealmMods-RoguelikeSkillTreeVisualizer-0.1.0 |
This mod requires the following mods to function
BepInEx-BepInExPack
BepInEx pack for Mono Unity games. Preconfigured and ready to use.
Preferred version: 5.4.2305README
RoguelikeSkillTreeVisualizer
Status: IMPLEMENTADO (RSTV-2 + fase de diagnóstico). O botão e a árvore somente leitura estão prontos e o mod ainda não foi conferido em jogo pelo autor.
- GUID:
com.gumatos.roguelikeskilltreevisualizer - Versão: 0.1.0
- Compatível com: Stolen Realm v1.3.1 (versão mais recente do jogo em 30/09/2026).
- Dependências: nenhuma além do BepInEx 5 (que o r2modman já instala no perfil).
Este mod já é um pacote Thunderstore: a pasta tem o manifest.json, o icon.png (256×256, que o
Thunderstore exige), o README.md, o CHANGELOG.md e o código:
| arquivo | papel |
|---|---|
Plugin.cs |
[BepInPlugin], config (AtivarBotao) e aplicação dos ganchos um a um, com contagem real no log (o Awake NÃO cria GameObject: isso roda no chainloader) |
Patches.cs |
os 8 ganchos Harmony, cada um com a linha do decompilado no comentário |
SelectPartyButton.cs |
RSTV-2a: clona o roguelikePowerupButton, encolhe a linha, trata o clique e diagnostica por que o botão não entrou |
PartyTargets.cs |
RSTV-2b: ordem de adição local (o jogo não guarda essa ordem) |
SkillTreeReadOnly.cs |
RSTV-2c/2d: abre a Skill Tree nativa e mantém a sessão somente leitura |
RstvHost.cs |
MonoBehaviour criado de forma preguiçosa (Update do mod) |
Seguindo o padrão dos outros mods do repositório: uma pasta na raiz, com o .csproj,
o Plugin.cs e o alvo DeployToBepInEx (não existe src/, decisão registrada na PKG-1).
Alvo de compatibilidade: Stolen Realm v1.3.1 (versão mais recente do jogo em 30/09/2026) — é a
versão que o manifest.json deste pacote declara.
Para quem vai usar
O que o botão faz
- Aparece como um botão quadrado
Skills, colado à direita doChoose Powerups, na mesma linha (oChoose Powerupsencolhe o necessário e a linha não cresce; oAccept Partynão muda). - Abre a árvore de skills do personagem certo: o último que VOCÊ adicionou à party, resolvido no clique (o nível e o equipamento mostrados são os de agora, sem cache).
- Dentro da árvore dá para navegar, dar zoom, trocar de aba e ler os tooltips com os números do
seu personagem. As skills já aprendidas aparecem acesas; o resto cinza/
CannotObtain. - O botão fica desabilitado quando a sua party está vazia e só aparece em modo Roguelike
(segue o
Choose Powerups).
O que o botão NÃO faz
- Não gasta ponto de skill — o rodapé mostra
0 points available. - Não aprende nem remove skill — clicar num nó é bloqueado (
read-only) e oRespec Buildfica escondido. - Não altera o
Accept Party, a Current Party, os Powerups, o GameMode nem as regras do Roguelike. Nenhum arquivo do jogo é modificado. - Não mistura jogadores — cada jogador usa os seus próprios personagens; o que o outro adiciona à party não muda o que você vê.
O mod não reimplementa as Skill Trees: ele resolve o personagem certo e entrega a instância real para o sistema nativo. Não há compra de skill, gasto de ponto nem qualquer escrita.
Instalar
Pelo r2modman (recomendado): instale este pacote no perfil do Stolen Realm — o r2modman coloca a
DLL em BepInEx\plugins\RoguelikeSkillTreeVisualizer\.
Manual:
- Instale o BepInEx 5 x64 no jogo (ou dê "Start modded" uma vez no r2modman, que cria a pasta).
- Copie a pasta
RoguelikeSkillTreeVisualizerpara:
Deve ficar:%APPDATA%\r2modmanPlus-local\StolenRealm\profiles\Default\BepInEx\plugins\...\plugins\RoguelikeSkillTreeVisualizer\RoguelikeSkillTreeVisualizer.dll - Abra o jogo. No
LogOutput.logdeve aparecer:
Se vierRoguelike Skill Tree Visualizer 0.1.0 carregado (RSTV-2: botao ao lado do Choose Powerups + skill tree read-only). RSTV: patches Harmony aplicados (8/8 ganchos, 8 metodos do jogo).7/8(ou menos), o log diz qual gancho não entrou — veja Diagnóstico abaixo. - Vá em Roguelike → Select Party: o botão
Skillsfica colado à direita doChoose Powerups.
Só funciona no modo Roguelike e o botão só aparece na tela Select Party. Não é preciso entrar em partida.
Config (opcional)
BepInEx/config/com.gumatos.roguelikeskilltreevisualizer.cfg:
| seção | chave | padrão | o que faz |
|---|---|---|---|
Geral |
AtivarBotao |
true |
false = o mod carrega, registra os ganchos, mas não cria botão nenhum na tela |
O padrão é o comportamento de sempre: quem não abrir o arquivo de config não vê diferença nenhuma.
Desinstalar
- Feche o jogo.
- Apague a pasta do mod em
BepInEx/plugins/RoguelikeSkillTreeVisualizer/(no perfil do r2modman ou na instalação manual). Nada é criado fora dela. - Opcional: apague
BepInEx/config/com.gumatos.roguelikeskilltreevisualizer.cfg.
Desinstalar não deixa resíduo no jogo e não afeta save.
Diagnóstico — como saber o que aconteceu
O mod não falha em silêncio: cada gancho é aplicado e logado individualmente e cada caminho de
falha escreve o motivo. No LogOutput.log:
| linha | significa |
|---|---|
Roguelike Skill Tree Visualizer 0.1.0 carregado ... |
o plugin carregou |
RSTV: gancho aplicado — <Classe> (N metodo(s) do jogo). |
um gancho entrou (uma linha por gancho) |
RSTV: patches Harmony aplicados (8/8 ganchos, 8 metodos do jogo). |
contagem real de ganchos aplicados; se vier 7/8, o mesmo erro termina com GANCHOS QUE FALHARAM: e os nomes |
RSTV: FALHA ao aplicar o gancho <Classe> — <motivo> |
aquele gancho não entrou (assinatura que mudou de versão, por exemplo) |
RSTV DIAG: botao 'Skills' HABILITADO/DESABILITADO nesta sessao |
o config foi lido |
RSTV-2: botao 'Skills' injetado (NxN px) ... largura da linha A -> B px |
o botão foi criado; as duas larguras devem ser iguais |
RSTV DIAG: botao 'Skills' NAO injetado — <motivo> |
por que o botão não apareceu (botão nativo ausente, sem RectTransform, exceção, config desligado) |
RSTV DIAG: a tela Select Party esta aberta e o botao 'Skills' NAO foi injetado. motivo=... |
resumo com o estado da tela quando a injeção falha |
RSTV-2: clique no botao 'Skills' -> '<nome>' (nivel N). |
o clique resolveu o personagem |
RSTV-2: skill tree read-only aberta para '<nome>' (nivel N, M skills, 0 pontos) |
a árvore abriu |
RSTV-2: clique na arvore BLOQUEADO (read-only) |
o clique no nó foi barrado |
RSTV-2: ResetSkillPoints BLOQUEADO (read-only) |
o clique num botão que zeraria as skills do personagem foi barrado |
RSTV-2: modo somente leitura encerrado (nada foi gravado no personagem) |
fechou limpo |
RSTV-2: a tela Select Party fechou com a arvore aberta — fechando a skill tree read-only |
a tela de party foi fechada por fora (aceitar a party, loading, OpenTown) e a árvore foi fechada junto para não ficar presa por cima do jogo |
O que o mod faz (técnico)
Adiciona um visualizador de Skill Trees na tela Roguelike → Select Party: um botão
quadrado à direita do Choose Powerups que abre a interface de Skill Trees nativa do jogo
(a mesma do Campaign → Change Skills), em modo somente leitura, com o contexto real do
personagem — Level, equipamento, atributos e estado das skills atuais.
Regras que já estão decididas
| regra | o que significa |
|---|---|
| Personagem | o último que o jogador local adicionou à Current Party — nunca o último global |
| Multiplayer | cada jogador usa os seus próprios personagens; a ação de outro jogador não muda o meu contexto |
| Remoção | se o último for removido, cai para o último local ainda na party; party vazia = botão desabilitado |
| Contexto | resolvido no clique, não quando a tela abre (Level/equipamento sempre atuais, sem cache) |
| Read-only | navegar, zoom, tooltips e requisitos continuam funcionando; nada que persista muda |
| Intocáveis | Accept Party, a Current Party, os Powerups, o GameMode e as regras do Roguelike |
| UI | o botão é clonado de um botão nativo (mesmo sprite/estados); a largura do Choose Powerups encolhe o necessário e a linha não cresce |
| Falha | nenhum caminho pode falhar calado: ou o log diz por que o botão/gancho não entrou, ou o estado duvidoso é fechado |
Ícone
icon.png (256×256) é o ícone de pacote do Thunderstore — arte de verdade do autor
(redimensionada de uma arte 1254×1254), não um ícone genérico gerado pelo script do empacotador.
É PNG válido (RGBA) e passa na checagem de 256×256 do tools/pack-thunderstore.py.
Pendente: o arquivo original de 1254×1254 não está versionado neste repositório (dos outros
mods os originais ficaram em scratch/icons-orig/, mas o do RSTV não está lá). Se precisar
reexportar ou cortar outra versão, a arte-fonte tem de ser pedida de novo ao autor — nada de gerar
um PNG falso no lugar.
Especificação e aceite
A especificação completa (77 itens: investigação obrigatória, estratégia de injeção, rastreio de personagem, testes por caso) e os critérios de aceite estão registrados no quadro interno do projeto (não versionado).
Roteiro de conferência em jogo (RSTV-2)
- Compilar e instalar:
dotnet build RoguelikeSkillTreeVisualizer/RoguelikeSkillTreeVisualizer.csproj(sem a propriedadeDeployToBepInEx=falseele copia a DLL para o perfil do r2modman). - Abrir o jogo modded e ir em Roguelike → Select Party. No
LogOutput.logdevem aparecerRSTV: patches Harmony aplicados (8/8 ganchos, 8 metodos do jogo)eRSTV-2: botao 'Skills' injetado ... largura da linha N -> N px(as duas larguras devem bater). Se o botão não aparecer, o log já diz o motivo (RSTV DIAG: ..., com a tela, o botão nativo e a party local) — não precisa adivinhar. - Adicionar 2 personagens meus à party (clique nos tiles) → o log diz qual virou o alvo; clicar num terceiro e removê-lo → o alvo volta para o último que ficou.
- Clicar no botão quadrado Skills (colado à direita do
Choose Powerups). Esperado:RSTV-2: skill tree read-only aberta para '<nome>' (nivel N, M skills, 0 pontos)e a árvore nativa por cima da tela de party, com as skills aprendidas acesas e o resto cinza/CannotObtain. - Na árvore: passar o mouse nos nós (tooltip com os números do personagem real), trocar de aba,
tentar clicar num nó →
RSTV-2: clique na arvore BLOQUEADO (read-only). O botãoRespec Buildnão aparece; o rodapé mostra0 points available. - Fechar (X ou Esc) →
RSTV-2: modo somente leitura encerrado (nada foi gravado no personagem). Conferir queAccept Party, a Current Party e os Powerups continuam intactos. - Repetir abrindo/fechando 3× e depois aceitar a party — a árvore não pode aparecer presa atrás nem empurrar a UI.
- Opcional (config): com
AtivarBotao = falseno.cfg, a tela abre normal e sem botão nenhum; o log dizRSTV DIAG: botao 'Skills' DESABILITADO nesta sessao.
Riscos conhecidos (não verificáveis sem abrir o jogo)
- O sprite/âncora/
LayoutGroupda linha vivem no prefab (resources.assets, 1,7 GB, não aberto): o mod mede em runtime, compensa a largura e loga o modo de layout usado. Se o log mostrarchildForceExpandWidth, a linha não cresce mas os botões vizinhos se redistribuem. - Z-order: a Skill Tree é instanciada sob outro ramo do canvas. O mod compara os índices de irmão e, se a árvore estiver atrás da tela de party, move o ramo para o fim e restaura ao fechar (logado).
GameLogic.CurrentlySelectedCharacteré ajustado para o personagem alvo (é de ondeSkillTreeManagere oTooltipleem o contexto). Isso também marca aquele personagem comoIsPartyLeadere libera o lock da câmera — o mesmo que o jogo faz ao selecionar um personagem no mundo.- Fechamento de emergência: se a higienização do commit falhar, o mod não deixa o original
rodar e fecha a janela por conta própria (zero escrita) em vez de arriscar a gravação. Só
acontece num caminho de exceção, mas vale conferir que a tela de party volta ao normal depois
(o log diz
RSTV: falha ao higienizar AcceptSkillChanges ...). - Segundo caminho de gravação (achado na auditoria estática):
SkillTreeManager.ResetSkillPoints()(l.173274) é público, sem parâmetro e chamaCharacter.ResetSkills(), que zera todas as skills do personagem e enfileira o save dele (l.37474) — sem passar peloAcceptSkillChanges. O mod agora o bloqueia em read-only (gancho 8). O que fica para medir em jogo: qual botão do prefab chama esse método (o camporesetButton, l.172057, não está ligado em código) e se existe algum botão que chameCheckSkillDependancies(Character)(l.172328), que também grava (l.172344/172352) mas não tem nenhum chamador no assembly — esse não foi bloqueado de propósito (bloqueá-lo às cegas poderia atrapalhar um caminho legítimo do jogo). - Interceptor de Esc é um slot único (
UIWindowManager.CancelInterceptor, l.215982). OInventoryManager.OnEnable(l.125301) também o ocupa: se a mochila puder ser aberta por cima da árvore, o Esc deixa de fechar a árvore e ela só fecha no botão de fechar da janela (e, quando a tela de party fechar, pela guarda do host). Medir em jogo se isso é alcançável no Select Party. - Largura da linha com
childForceExpandWidth: o mod hoje não compensa a largura nesse modo ("o próprio grupo acomoda o filho"). Isso só vale se a linha tiver largura fixa; se ela for dimensionada pelo conteúdo (umContentSizeFitteracima), ela cresce com o clone e o log mostralargura da linha A -> Bcom A ≠ B. É exatamente o que a linha do log serve para flagrar no primeiro teste em jogo.
Correções da auditoria estática (contra o decompilado, sem rodar o jogo)
- Gancho 8:
SkillTreeManager.ResetSkillPointsbloqueado em read-only (segundo ponto de gravação). - A árvore agora fecha sozinha se a tela Select Party fechar por fora (aceitar a party muda o
GUIStatee o jogo fecha a tela em l.118416/l.133307/l.215520 sem fechar oSkillTreeManager): antes ela ficaria desenhada por cima do mapa, com o interceptor de Esc instalado.
CHANGELOG
Changelog — RoguelikeSkillTreeVisualizer
0.3.0 — aba "All Skill Trees" + botão/atalho + escala/posição nativa (RSTV-13..19)
RSTV-13 — o botao Skills do HUD volta a APARECER passeando no MAPA-MUNDO. O RunTargets.GateOk
aplicava ao mapa portoes que descrevem UMA BATALHA: Root.SpawnPlacementActive (so existe no setup da
batalha, l.112152 liga / l.112165 desliga) e HexCellManager.CurrentState == PlayerState.Action (a
mira de skill) eram checados em QUALQUER estado — no mapa eles escondiam o botao justamente enquanto o
jogador passeia (sintoma 5 do dono). O IsPlayerTurnAndReady ja estava dentro do ramo InBattle, mas
a separacao estava implicita. Agora ela e explicita: os portoes de batalha (mira, posicionamento
inicial, turno, AnyActingCharactersInBattle, AnyMovingCharactersInBattle) moram DENTRO do ramo
if (estado == GUIState.InBattle); no mapa/cidade valem so os universais (modo de apontar o hex, UI
aberta, alvo). A seguranca EM BATALHA nao mudou — cada portao continua bloqueando la.
RSTV-14 — o botao DENTRO da janela do level-up segue o CICLO DE VIDA (deixa de "voar"). O
LevelUpWindowButton era criado SO no postfix de OpenSkillSelectWindow e nunca reavaliado: o jogo
troca o estagio do level-up SEM fechar a janela (CurLevelUpStage, l.168697, liga
SkillSection/ItemSectionInventory/AttributeSection), o painel Content muda de metragem e o
clone — filho do SkillSelectWindow — continuava visivel no estagio de ATRIBUTOS, preso ao canto
ANTIGO do painel (sintoma 3). Agora um LevelUpWindowButton.Refresh() (chamado a cada quadro pelo
RstvHost.Update e tambem no fim do Ensure) mostra o clone SO com a janela ativa, da mesma
instancia, no estagio LevelUpStage.Skills — nos outros estagios (Items/Attributes/Currency) ele
some, e ao VOLTAR ao estagio de skills ele e re-ancorado no canto atual do Content. O Ensure
recusa injetar com a janela fechada ("nao injetar fora de hora"). O sintoma (6) ("aperto Skills e o
botao some") e o flapping do portao de "janela aberta", tratado na RSTV-5: a arvore read-only nao
deixa a janela presa em UIWindowManager.OpenedWindows, entao o botao do HUD VOLTA ao fechar.
- Testes:
t_rstv13_gate_mapa.py(suite pura; modelo puro do portao por estado) et_rstv14_botao_ciclo_levelup.py(suite pura; ciclo de vida + opcoes de estagio). Iscas:cp_rstv13_batalha_no_mapa.py,cp_rstv14_refresh_morto_no_host.py,cp_rstv14_refresh_sem_estagio.py. Prova de fogo FISICA: os tres defeitos foram plantados nos fontes REAIS (RunTargets.cs,RstvHost.cs,LevelUpWindowButton.cs), o teste saiuREPROVOU(exit 1) pelo motivo certo e os arquivos foram restaurados byte a byte (sha256 conferido). Log:tools/testes/rstv13-14-prova-reprovando.log. A RSTV-8 teve o modelo do portao ajustado (mira e spawn agora contam como portoes de batalha no cenario puro).
Nao publicado (RSTV-12) — precisa de conferencia em jogo
RSTV-12 — o clique dos botoes Skills passa a chegar ao mod (nao ao onClick do prefab). O log
de 02/10 mostrou os dois sintomas: o botao do HUD pingava em vez de abrir a arvore, e o da janela
do level-up disparava ConfirmLevelUpSelection (11 NullReferenceException). Causa raiz lida do IL
de UnityEngine.CoreModule.dll deste build: o UnityEvent guarda DUAS listas — m_PersistentCalls
(os listeners serializados no prefab, ex.: ButtonPressedPing/ConfirmLevelUpSelection) e
m_RuntimeCalls (os de script) — e RemoveAllListeners() chama InvokableCallList.Clear(), que
esvazia so a de runtime. Pior: PrepareInvoke() concatena as persistentes antes das de
runtime, entao o listener do jogo roda primeiro e, se ele lancar, o Invoke() aborta e o nosso
nem chega a rodar.
- Conserto: novo helper
SelectPartyButton.ClearClickListeners(Button)— troca a instancia do evento (button.onClick = new Button.ButtonClickedEvent()), descartando junto a lista persistente serializada; o listener do mod e instalado no evento limpo. Aplicado nos tres clones (HUD, janela do level-up e tela Select Party). - Nao era sobreposicao/raycast (o log mostra o NOSSO
OpenForTargetsendo chamado no clone do HUD) nem re-bind de componente (UIButtonController, l.336107, so troca cores — nao mexe em onClick). - Testes:
t_rstv12_onclick_roteamento.py(suite pura) +cp_rstv12_clique_no_original.py(isca). O modelo puro doUnityEventreproduz o clique que cai no original (pingouno HUD /levelup-nreno level-up) e aprova o conserto (abriu). Prova de fogo fisica: o defeito foi plantado noRunButton.cse noLevelUpWindowButton.csREAIS, o teste saiuREPROVOU(exit 1) e os fontes foram restaurados byte a byte (sha256 conferido). Log:tools/testes/rstv12-prova-reprovando.log.
Nao publicado (RSTV-11b) — precisa de conferencia em jogo
RSTV-11b — o botao Skills passa a existir DENTRO da janela do level-up. O re-parent da RSTV-9 foi
aposentado: o clone do HUD nao muda mais de casa (a entrada da RSTV-9 abaixo fica como historico). No
lugar dele, o postfix de RoguelikeManager.OpenSkillSelectWindow clona o AcceptButton como filho
direto do SkillSelectWindow (TELA CHEIA), fora do filho Content — que tem VerticalLayoutGroup
ContentSizeFitter, e um filho ali reflui o painel inteiro. O botao nasce no canto superior direito do painel visivel (oContent, ~293x536 px), com oonClickSERIALIZADO limpo (RemoveAllListeners— ele chamavaConfirmLevelUpSelection, escrita) e apontando para oRunButton.OpenForTarget. OEnsuree idempotente por janela (o gancho roda de novo a cada personagem da fila de level-up, sobre o mesmoSkillSelectWindow).
RSTV-11b — RunButton.OpenForTarget() e o corpo unico. Extraido do antigo OnClick, e reusado pelo
botao do HUD, pelo botao da janela do level-up e pelo atalho F10 (RSTV-11a), que antes duplicava o corpo.
A maquinaria do re-parent (HomeInLevelUp/HomeInRow/CaptureHome, campos _home*, constante
LadoMinimoLevelUp) saiu do RunButton.
Testes: t_rstv11b_botao_janela_levelup.py (suite pura) + cp_rstv11b_sem_idempotencia.py e
cp_rstv11b_clone_no_content.py (iscas). Os dois defeitos — 'sem idempotencia' (2o botao) e 'clone no
Content' (reflow) — foram plantados no fonte real, o teste saiu REPROVOU (exit 1) e o arquivo foi
restaurado byte a byte (sha256 conferido). Log: tools/testes/rstv11b-prova-reprovando.log.
Historico (RSTV-8 + RSTV-9) — precisa de conferencia em jogo
RSTV-8 — o botao da run deixa de ser escondido no level-up. O portao RunTargets.GateOk escondia o
botao com if (RoguelikeManager.IsNotNullAndIsActive) return false, mas isso significa "o manager foi
CARREGADO" — o GameObject dele fica ativo a run inteira depois do primeiro level-up. A janela de verdade
e o filho SkillSelectWindow.activeSelf (o mesmo criterio que o jogo usa em GUIManager.Update).
Agora o level-up libera o botao, e o alvo passa a sair do PROPRIO manager
(CurrentRoguelikeSkillSelectingCharacter, fallback CharactersWaitingForLevelUp[0].Character), nunca
do CurrentlySelectedCharacter que ele troca. Os demais portoes (mira/ping/janela/spawn/turno/animacao)
continuam.
RSTV-9 — o botao RENDERIZAR durante o level-up (caso a parte). [SUPERADO pela RSTV-11b — o re-parent
abaixo foi APOSENTADO; o level-up agora tem um botao PROPRIO dentro da janela, LevelUpWindowButton.]
Achado critico da RSTV-8: nesse
instante o jogo desliga o HUD INTEIRO (GUIManager.Update, l.120492, faz
CurrentCharacterUI.gameObject.SetActive(false) enquanto SkillSelectWindow esta ativo) e o clone da
RSTV-5 e filho desse HUD — o portao liberar era NECESSARIO, mas o botao NAO renderizava. Agora, enquanto
a janela do level-up esta aberta, o RunButton realoca o clone para DENTRO do SkillSelectWindow
(canto superior direito, ultimo irmao para desenhar/clicar por cima, lado no minimo LadoMinimoLevelUp
= 44 px) e o devolve para a linha do Ping Button quando ela fecha, restaurando a geometria original
(sem reaplicar o ApplySquareLayout). O caso normal — tela Select Party e run no mapa-mundo E em
combate — NAO muda: a realocacao so ocorre dentro do ramo de level-up; o tamanho da linha (Ping
Button) segue intacto.
Testes: t_rstv9_render_levelup.py (suite pura) + cp_rstv9_botao_no_hud_desligado.py (isca). A
prova de fogo fisica plantou o defeito no RunButton.cs real (o teste saiu REPROVOU, exit 1) e
restaurou o fonte byte a byte.
0.2.0
RSTV-5 — o botão Skills agora existe DURANTE A RUN, ancorado no Ping Button (o botão de
apontar o hex) do HUD, ao lado dele, na mesma linha. A janela é a MESMA Skill Tree nativa, no mesmo
modo somente leitura. Nada disto foi conferido em jogo ainda — o roteiro está no README.md.
- Botão no HUD da run: clone do botão nativo
CurrentCharacterUI.pingBtn(l.209464, handlerButtonPressedPingl.210027 →GUIManager.TogglePingModel.119299), injetado como irmão à direita dele com a mesma compensação de largura já validada na tela de party (reusa oSelectPartyButton.ApplySquareLayoutnos 3 modos de layout). Injeção 1× pelo postfix deCurrentCharacterUI.InitSingleton(l.209626, o ponto em que o jogo instancia o HUD) e espelhamento noRstvHost.Updatedo mod — nunca noUpdate/UIUpdatedo próprio HUD (l.209655, que roda todo frame e mexe noendTurnButton). - Alvo resolvido no clique: o personagem selecionado no momento; se o jogo estiver sem ninguém selecionado, o primeiro personagem local da party (fallback documentado).
- Portão de segurança (sempre ativo, mesmo com o botão ligado): o botão some e recusa a
abertura, com o motivo no log, quando há mira de skill ativa (
HexCellManager.CurrentState == Action), modo de apontar hex ligado, não é o turno do jogador, alguém agindo/movendo, posicionamento inicial, level-up pendente (RoguelikeManager), janela de UI aberta ouGUIStatefora deInBattle/InWorldMap/InTown. Em dúvida (até numa exceção ao ler o estado), não abre.
Blindagens implementadas (cada uma com a linha do decompilado no código)
- Ciclo de vida — a guarda deixou de ser "a tela de party está aberta" (durante a run ela está
desligada e a árvore fecharia no frame seguinte): agora é "a instância nativa ainda está ativa" +
whitelist de
GUIStatepara o contexto de run; a checagem da tela de party continua só no contexto de party. - Escrita de
GameLogic.CurrentlySelectedCharacter(setter l.108082) — só escreve quando é inofensivo: se o alvo já é o selecionado (o setter retorna cedo, l.108097 → zero efeito) ou se o jogo está sem ninguém selecionado (a metade perigosa do setter só roda com um personagem anterior). O valor anterior é guardado e devolvido no fechamento (padrão do próprio jogo, l.163711/163942). Com outro personagem selecionado, a abertura é recusada — se não, o setter marcariaIsPartyLeader, travaria a câmera, forçariaHexCellManager.CurrentState = Movement(cancelando a mira) e chamariaNotifyPlayerOfTheirTurnem outro personagem. - Slot único de
CancelInterceptor(UIWindowManager.CancelInterceptor, l.215982) — o mod fotografa quem tinha o slot antes de abrir (antes doSetActive(true), que dispara oOnEnabledo jogo, l.172890) e devolve esse delegate no fechamento (antes deixavanull). O Esc deixa de ser consumido quando a janela do topo não é a nossa, aCharacterMenusManagerou a tela de party (mesmo critério do jogo, l.172924) — a mochila por cima continua fechando no Esc. - Portão do botão — ver acima.
- Clique do hex — o branch de Action do
PlayerMovement.ProcessLeftMouseClick(l.153393-153397) não checaPointerOverUIObject(o de movimento checa, l.153342). Duas camadas: a janela passa a ser registrada emUIWindowManager.OpenedWindows(→GUIManager.InMenus, l.118297, fazProcessUpdateInputsl.152986 retornar antes do clique) e um prefixo Harmony emProcessLeftMouseClick(HexCell)(l.153277) consome o clique enquanto a sessão está ativa. Uma rede de segurança no host retira a entrada deOpenedWindowsse a janela fechar por fora, para o input de batalha não ficar morto. - Troca de personagem / de estado — se o jogo trocar o personagem selecionado ou sair de
InBattle/InWorldMap/InTown, a sessão é fechada (nada de janela read-only com contexto errado).
Config
Geral→AtivarBotaoNaRun(padrãotrue): liga/desliga só o botão da run. O portão 4 vale mesmo comtrue.Geral→AjustarZOrder(padrãotrue): ajusta o índice de irmão para a árvore ficar por cima do HUD na run. Desligue se o tooltip do jogo aparecer atrás da árvore.Geral→AtivarBotaocontinua sendo o da tela Select Party (inalterado).
Outras mudanças
- Ganchos: 8 → 10 (novos: postfix de
CurrentCharacterUI.InitSingleton, prefixo dePlayerMovement.ProcessLeftMouseClick). A contagem do boot vira10/10 ganchos, 10 metodos do jogo. - Diagnóstico: o botão da run diz por que está escondido, com throttle de 5 s por motivo, e
avisa quando volta a aparecer; a abertura registra a geometria da linha, o
targetGraphicdo clone e quantos gráficos herdados foram desligados. SkillTreeReadOnlyganhou contexto (PartyScreen/Run) e a sessão ganhou as guardas de ciclo de vida, a devolução da seleção e a limpeza deOpenedWindows.
0.1.0
Primeira versão. O mod ainda não foi conferido em jogo — a conferência está no roteiro do README.md.
- Botão
Skillsna tela Select Party do modo Roguelike: quadrado, na mesma linha e colado à direita do Choose Powerups. É um clone do botão nativo (mesmo sprite e estados); a largura do Choose Powerups encolhe só o necessário, a linha não cresce e o Accept Party não muda de tamanho. - Abre a Skill Tree NATIVA do jogo (a mesma de Campaign → Change Skills) com o contexto real do personagem: nível, equipamento, atributos e estado das skills. A janela nativa é reaproveitada — o mod não reimplementa a árvore.
- Modo somente leitura de verdade: tooltips (com os números do personagem certo), zoom, troca de abas e requisitos continuam funcionando; nada é aprendido, removido ou gasto. O clique no nó é bloqueado, o Respec Build fica escondido e o rodapé mostra
0 points available. - Alvo do botão: o último personagem que VOCÊ adicionou à party, resolvido no clique (nível e equipamento sempre atuais, sem cache). Cada jogador usa os seus; party local vazia = botão desabilitado.
- Config:
Geral→AtivarBotaoemBepInEx/config/com.gumatos.roguelikeskilltreevisualizer.cfg(padrãotrue). Só serve para desligar o botão — quem não abrir o arquivo não vê diferença. - Diagnóstico: cada gancho Harmony é aplicado e logado individualmente (um gancho que falha não derruba os outros — antes o
PatchAll()era tudo-ou-nada); o resumo do boot traz a contagem real (RSTV: patches Harmony aplicados (8/8 ganchos, 8 metodos do jogo)); e se o botão não for injetado o log diz por quê (botão nativo ausente, semRectTransform, exceção, config desligado) com o estado da tela.
Correções da auditoria estática (contra o decompilado do jogo, sem rodar o jogo)
- Segundo ponto de gravação fechado:
SkillTreeManager.ResetSkillPoints()(l.173274) chamaCharacter.ResetSkills()— que zera todas as skills do personagem e enfileira o save (l.37474) — sem passar peloAcceptSkillChanges. É gancho novo (8º) e é bloqueado no modo somente leitura. - Árvore órfã: se a tela Select Party fechar por fora com a árvore aberta (aceitar a party muda o
GUIStatee o jogo fecha a tela em l.118416/l.133307/l.215520), nada no jogo fechava oSkillTreeManager— a árvore ficaria por cima do mapa com o interceptor de Esc instalado. Agora o host fecha a árvore quando a tela de party não está mais aberta. - O que não mudou de propósito (precisa de teste em jogo, está em Riscos conhecidos do
README.md):CheckSkillDependancies(Character)(l.172328, sem chamador no assembly),resetButton/closeButton(ligados só no prefab) e o slot único deCancelInterceptor.
Notas de implementação
- O jogo não tem um modo somente leitura nativo: a garantia vem de higienizar o ponto de gravação antes que ele execute, e não de bloqueá-lo. Se essa higienização falhar, o mod não deixa o original rodar e fecha a janela por conta própria — zero escrita em qualquer estado.
- Nenhum arquivo do jogo é modificado; desinstalar é apagar a pasta do mod.
- Compatibilidade declarada: Stolen Realm v1.3.1.
- Ícone: arte do autor redimensionada para 256×256. O original de 1254×1254 não está versionado no repositório (ver a seção Ícone do
README.md).