Plugin para Paper/Spigot 1.21 que implementa um sistema de "torre infinita" (dungeon PvE por andares progressivos), com modos Solo e Party, sistema de chaves de acesso, ranking/recordes, estatísticas de jogador, menus por inventário e um sistema de setup in-game para criar arenas sem editar YAML na mão.
- Grupo/artefato:
com.eternity:InfinityTower - Versão: 1.0
- Java: 21
- API: Paper 1.21 (
api-version: 1.21)
| Plugin | Tipo | Uso |
|---|---|---|
| PlaceholderAPI | depend (obrigatório) | Expõe os placeholders %infinitytower_...% |
| MythicMobs | soft-depend | Spawn de mobs customizados nos andares (mythic: no YAML da dungeon) |
| Citizens | soft-depend | Declarado no plugin.yml; sem integração de código no momento |
| EssentialsX | soft-depend | Sobrescreve o /back do Essentials ao sair da dungeon (EssentialsBackUtil). O plugin também bloqueia /back por alguns segundos por conta própria (tower.back_block_seconds), então essa proteção funciona mesmo sem o Essentials instalado |
| SQLite JDBC / MySQL Connector/J | embutido (shaded) | Persistência de stats, ranking e histórico de runs |
- Compile com Maven (
mvn package) ou baixe o.jarjá buildado emtarget/InfinityTower-1.0.jar. - Coloque o jar em
plugins/do servidor Paper, junto comPlaceholderAPI.jar(obrigatório). - Suba o servidor. Na primeira execução o plugin cria em
plugins/InfinityTower/:config.ymlmenus.ymllang/pt-BR.yml(idioma definido emconfig.yml: lang)dungeons/solo_10.ymledungeons/party_10.yml(templates de exemplo)
- Use
/tower admin setup <dungeonId>para configurar spawns e pontos de mob de uma dungeon direto in-game (veja Setup de arenas).
Comando raiz: /tower (alias: /torre).
| Comando | Descrição |
|---|---|
/tower ou /tower help |
Mostra a lista de comandos disponíveis |
/tower menu |
Abre o menu principal (escolha Solo/Party) |
/tower stats |
Abre o menu de estatísticas pessoais |
/tower party invite <player> |
Convida um jogador para a sua party |
/tower party accept |
Aceita o convite pendente |
/tower party leave |
Sai da party (sem cancelar a dungeon em andamento) |
/tower party disband |
Desfaz a party (somente líder) |
/tower party promote <player> |
Transfere a liderança (somente líder) |
/tower party kick <player> |
Expulsa um membro (somente líder) |
/tower invite <player> / /tower accept |
Aliases legados de party invite/party accept |
/tower leave |
Sai da dungeon/run atual |
/tower top <wins|time|floor> <dungeonId> [limit] |
Ranking de vitórias, melhor tempo ou andar mais alto alcançado (limite 1–50, padrão 10) |
/tower record <dungeonId> |
Recorde individual e de party daquela dungeon |
| Comando | Descrição |
|---|---|
/tower menu <solo|party> [player] |
Força a abertura de um submenu para si ou outro jogador |
/tower give key <player> <dungeonId> [amount] |
Dá chave(s) de acesso a um jogador online |
/tower giveall key <dungeonId> [amount] |
Dá chave(s) a todos os jogadores online |
/tower admin reload |
Recarrega todas as dungeons de dungeons/*.yml |
/tower admin list |
Lista as dungeons carregadas |
/tower admin create <dungeonId> |
Cria uma dungeon nova a partir do template (party_10.yml se o id começar com party, senão solo_10.yml) |
/tower admin setup <dungeonId> [arena] |
Inicia uma sessão de setup (arena padrão = 1) |
/tower admin where |
Mostra em qual setup você está |
/tower admin setspawn <solo|leader> |
Define o spawn único de solo ou do líder da party |
/tower admin addspawn <solo|members> |
Adiciona um spawn à lista (spawns solo ou membros da party) |
/tower admin clearspawns <solo|members> |
Limpa a lista de spawns |
/tower admin setreturn |
Define o ponto de retorno ao fim/saída da dungeon |
/tower admin setfloorspawn <floor> |
Define pra onde todo mundo é teleportado só naquele andar (ver Trocando de "arena" por andar) |
/tower admin mobspawn <add|set|clear> <floor> |
Gerencia os pontos de spawn de mob de um andar |
/tower admin save |
Salva e recarrega a dungeon em edição |
/tower admin cancel |
Cancela o setup atual sem salvar |
Tab-completion está implementado para todos os subcomandos (incluindo nomes de jogadores online e IDs de dungeon).
| Permissão | Default | Descrição |
|---|---|---|
infinitytower.player |
true |
Comandos básicos (menu, stats, party, top, record, leave) |
infinitytower.admin |
op |
Comandos administrativos e de setup |
infinitytower.* |
op |
Concede player + admin |
Identificador da expansion: infinitytower → use como %infinitytower_<param>%.
| Placeholder | Retorna |
|---|---|
%infinitytower_runs% |
Total de runs (solo + party) |
%infinitytower_wins% |
Total de vitórias |
%infinitytower_losses% |
Total de derrotas |
%infinitytower_solo_runs% / solo_wins / solo_losses |
Estatísticas apenas do modo solo |
%infinitytower_party_runs% / party_wins / party_losses |
Estatísticas apenas do modo party |
| Placeholder | Retorna |
|---|---|
%infinitytower_top_wins_<pos>_<dungeonId>_name% |
Nome do jogador na posição <pos> do ranking de vitórias daquela dungeon |
%infinitytower_top_wins_<pos>_<dungeonId>_value% |
Quantidade de vitórias dessa posição |
%infinitytower_top_floor_<pos>_<dungeonId>_name% |
Nome do jogador na posição <pos> do ranking de andar mais alto alcançado |
%infinitytower_top_floor_<pos>_<dungeonId>_value% |
Andar mais alto alcançado por essa posição |
%infinitytower_best_player_<dungeonId>_name% |
Nome do melhor tempo individual da dungeon |
%infinitytower_best_player_<dungeonId>_time_ms% |
Melhor tempo individual em milissegundos |
%infinitytower_best_party_<dungeonId>_leader% |
Líder da melhor run em party |
%infinitytower_best_party_<dungeonId>_time_ms% |
Melhor tempo em party (ms) |
Exemplo:
%infinitytower_top_wins_1_solo_10_name%— nome do #1 do ranking de vitórias da dungeonsolo_10. Posições sem registro retornam-(texto) ou0(valor numérico).<dungeonId>pode conter underscore (solo_10,party_10, etc.) sem problema — o parsing isola só a posição (<pos>) e o campo final (name/value/time_ms/leader), o resto vira o id da dungeon.
plugins/InfinityTower/
├── config.yml # configuração global do plugin
├── menus.yml # layout dos menus de inventário
├── lang/
│ └── pt-BR.yml # todas as mensagens (idioma definido em config.yml: lang)
├── dungeons/
│ ├── solo_10.yml # template padrão de dungeon solo
│ ├── party_10.yml # template padrão de dungeon party
│ └── <seus>.yml # criadas via /tower admin create
└── database.db # SQLite (se database.type: sqlite)
Principais chaves:
lang— código do idioma (procuralang/<código>.yml; cai parapt-BRse não achar).prefix— prefixo usado em mensagens gerais.tower.title_times—fade_in_ticks/stay_ticks/fade_out_ticksdos titles de andar/entrada/conclusão.tower.tracking.spawn_radius— raio (em blocos) para capturar minions gerados por mobs do MythicMobs (ex.: adds de um boss) como parte do andar.tower.tracking.allowed_spawn_reasons— lista deCreatureSpawnEvent.SpawnReasonque contam pro raio acima (padrão:[CUSTOM, SPAWNER]). Mobs selvagens comuns nascem com motivoNATURAL(fora da lista por padrão), então nunca entram no rastreamento só por estarem perto da arena — só invocações "artificiais" (skills do MythicMobs, spawners, etc.) contam.dungeon.command_whitelist— lista de comandos permitidos dentro da dungeon (o resto é bloqueado porCommandWhitelistListener).main-menu(oumain_menu) — controla o menu principal (/tower menu): botões Solo/Party, filler, título. Essa é a única fonte real desse menu (ver correção na seçãomenus.ymllogo abaixo).tower.enter_teleport_delay_seconds— delay global (segundos), aplicado a todas as dungeons, entre abrir o menu/clicar e teleportar o jogador para a arena (dá tempo de carregar chunks, HUD, etc.). Padrão:2se a chave não existir.tower.back_block_seconds— segundos em que/back(e variações:/eback,/cback,essentials:back,cmi:back) ficam bloqueados após o jogador sair/terminar a dungeon. Funciona independente de ter EssentialsX instalado. Padrão:5.spawn— spawn global de fallback quando uma dungeon não temreturn_spawn/player_spawnsconfigurado (ou o mundo configurado não existe).database.*— ver seção Banco de dados.debug.enabled— liga logs extras de depuração.
Controla duas telas (o menu principal não é uma delas — veja a nota abaixo):
player_stats_menu— tela do/tower stats.items.<qualquer_nome>vira um item no slot configurado, com placeholders{player} {uuid} {soloRuns} {soloWins} {soloLosses} {partyRuns} {partyWins} {partyLosses} {totalRuns} {totalWins} {totalLosses}disponíveis emtitle/name/lorede qualquer item (não sósolo/party/total, dá pra adicionar quantos quiser).menus.solo/menus.party— telas que listam as dungeons daquele modo nos slots definidos emdungeon-slots, usando o templatedungeon-itemcom placeholders{id} {display} {mode} {max_floors}.
Cada item aceita material, name, lore, custom_model_data e (nos menus de dungeon) hide_attributes. filler preenche os slots vazios do inventário.
Nota sobre o menu principal (
/tower menu): ele é controlado porconfig.yml: main-menu, não pormenus.yml. Até uma revisão recente,menus.ymltinha uma seçãomain_menu:econfig.ymltinha uma seçãostats-menu:que pareciam configurar essas telas, mas nenhuma das duas nunca era lida pelo código — eram sobras mortas de uma versão anterior. Ambas foram removidas dos arquivos padrão; o menu de stats agora é de fato lido demenus.yml: player_stats_menu(antes disso era todo fixo no Java, ignorando qualquer configuração).
Todas as mensagens do plugin, organizadas em seções: titles (titles de tela), messages (mensagens gerais), party (sistema de party), usage (mensagens de uso incorreto), help (texto de /tower help), database, setup e logs (mensagens de console). Edite livremente — os placeholders {...} de cada linha são substituídos pelo código na hora do envio.
Cada arquivo YAML dentro de plugins/InfinityTower/dungeons/ descreve um mapa/dungeon completo: identidade, chave de acesso, spawns, titles, e o conteúdo de cada andar (mobs + recompensas). O nome do arquivo (sem .yml) vira o dungeonId usado em /tower give key, /tower top, /tower record, /tower admin setup, etc. Um mesmo arquivo também pode conter arenas alternativas e dungeons extras — ver Arenas alternativas e dungeons extra mais abaixo.
| Chave | Tipo | Padrão | Descrição |
|---|---|---|---|
display |
texto | id do arquivo |
Nome exibido (aceita &cor e hex &#RRGGBB). Usado no menu, no item-chave, em /tower top//tower record e no placeholder {dungeon} do title de conclusão |
mode |
SOLO ou PARTY |
SOLO |
Define em qual menu (/tower menu solo ou /tower menu party) a dungeon aparece e as regras de entrada — ver Acesso e regras solo/party |
max_floors |
número | 10 |
Quantidade de andares; ao passar do último a dungeon é finalizada (onFloorCleared detecta floor > max_floors) |
allow_empty_floors |
true/false |
false |
Se false, um andar sem nenhum mob configurado (ou cujos mobs falharem ao spawnar) cancela a run com a mensagem floor_no_mobs/floor_spawn_failed. Se true, o andar é tratado como "vazio" e a run segue |
start_delay_seconds |
segundos | 10 |
Tempo entre o title on_enter (ao clicar pra entrar) e o início efetivo do andar 1 — durante esse tempo o title preparing também é mostrado |
next_floor_delay_seconds |
segundos | 5 |
Tempo de espera entre um andar ser limpo e o próximo começar |
allow_solo_in_party |
true/false |
true |
Só vale para dungeons mode: SOLO. Se true (padrão), um jogador em party ainda pode entrar sozinho numa dungeon SOLO (a run é só dele, o resto da party não é afetado). Se false, jogadores em party são bloqueados no menu SOLO (solo_requires_no_party) até saírem da party — veja a tabela da próxima seção |
access.* |
seção | — | Chave de acesso e limitador de entradas — ver Acesso e entrada |
menu-item.* |
seção | — | Ícone da dungeon nos menus solo/party — ver Aparência no menu |
player_spawns |
lista de locais | — | Pontos de spawn ao entrar sozinho (SOLO, ou PARTY sem estar em party) — um é sorteado aleatoriamente a cada entrada |
party_leader_spawn |
local único | — | Spawn do líder quando a run é iniciada em party |
party_member_spawns |
lista de locais | — | Spawns dos membros (não-líder) em uma run de party — um é sorteado aleatoriamente por membro |
return_spawn |
local único | cai no spawn global do config.yml |
Para onde o(s) jogador(es) voltam ao concluir a dungeon, morrer (fim de run) ou usar /tower leave |
titles.* |
seção | — | Textos de title/subtitle da dungeon — ver Titles e andar de boss |
floors.<n>.mobs |
lista | — | Mobs do andar n — ver Configuração de mobs |
floors.<n>.boss |
true/false |
false |
Marca o andar n como andar de boss — troca os titles usados (ver titles) |
floors.<n>.boss_name |
texto | "" |
Preenche o placeholder {boss_name} nos titles desse andar |
floors.<n>.floor_spawn |
local único | — (mantém o spawn de entrada) | Teleporta todo mundo pra um local diferente só nesse andar — ver Trocando de "arena" por andar |
floors.<n>.titles.* |
seção | — | Override de title somente para esse andar — mesmos grupos de titles.* (menos on_enter/preparing/completed, que são só de nível raiz) |
floors.<n>.rewards |
seção | — | Recompensas ao limpar o andar — ver Recompensas |
Cada uma dessas áreas é detalhada nas subseções abaixo.
Sequência real de eventos (código: DungeonSession), do clique no menu até o fim:
- Jogador clica na dungeon no menu → checagem de acesso (chave/limiter, ver abaixo) →
DungeonSession.start(). - Title
titles.on_enteré mostrado imediatamente. - Espera
tower.enter_teleport_delay_seconds(config.yml, global). - Jogador(es) são teleportados para
player_spawns(solo) ouparty_leader_spawn/party_member_spawns(party). - Title
titles.preparingé mostrado (placeholders{floor}=1,{delay}=start_delay_seconds). - Espera
start_delay_seconds. - Andar 1 começa: se
floors.1.floor_spawnestiver definido, todo mundo é teleportado pra lá primeiro (senão fica no spawn de entrada) — ver Trocando de "arena" por andar; mobs dofloors.1.mobssão spawnados; titlefloor_started/boss_floor_started(conformefloors.1.boss) é mostrado. - Quando todos os mobs do andar morrem:
floors.<n>.rewardsé entregue a quem está vivo → a cada 10 andares limpos (10, 20, 30…) é feito um broadcast pro servidor inteiro (messages.dungeon_broadcast_floor10) → titlefloor_cleared/next_is_boss(conforme o próximo andar ser boss) é mostrado → esperanext_floor_delay_seconds→ repete a partir do passo 7 para o próximo andar. - Se todos os jogadores online morrerem no mesmo andar antes de limpá-lo, a run falha (
dungeon_all_dead) e encerra. - Ao limpar o último andar (
floor > max_floors): titletitles.completed(placeholder{dungeon}=display) e teleporte parareturn_spawn. /tower leavea qualquer momento também encerra a run e teleporta parareturn_spawn.
O mode da dungeon decide o comportamento no menu (código: MenuListener):
mode |
Jogador sem party | Jogador em party (líder ou membro) |
|---|---|---|
SOLO |
Entra normalmente sozinho | Depende de allow_solo_in_party da dungeon: true (padrão) → entra sozinho normalmente, sem afetar o resto da party; false → bloqueado, mensagem solo_requires_no_party (precisa sair da party) |
PARTY |
Entra sozinho (run "solo" numa dungeon party) | Líder: inicia a run para toda a party. Membro: bloqueado no menu — só o líder inicia (party_only_leader_start_menu) |
O menu solo só lista dungeons mode: SOLO; o menu party só lista mode: PARTY — dá pra ter as duas versões da "mesma" dungeon (uma de cada mode) usando o recurso de dungeons extra no mesmo arquivo, se quiser.
access:
require_key: true # exige o item-chave no inventário pra entrar
consume_key: true # consome 1 unidade da chave ao entrar (ignorado se require_key: false)
key:
material: PAPER
custom-model-data: 10133
name: "&bChave da {display}"
lore:
- "&7Acesso: &f{id}"
- "&7Andares: &f{max_floors}"
glow: true # brilho falso (encantamento oculto), não é um encantamento real
limiter:
enabled: false
cooldown_seconds: 0 # 0 = sem cooldown entre entradas
max_entries_per_day: 0 # 0 = sem limite diário- A chave (
access.key) é só aparência de item (material/nome/lore/model data/glow) — a identificação real de "isso é a chave da dungeon X" é feita viaPersistentDataContainer, não pelo material.{id},{display}e{max_floors}são substituídos na hora de gerar o item (/tower give key,/tower giveall key). - Sem
access.keyconfigurado,/tower give keyavisakey_not_configurede não entrega nada. access.limiteré por jogador e por dungeon:cooldown_secondsbloqueia reentrada por N segundos após sair,max_entries_per_dayconta entradas que resetam à meia-noite (hora do servidor). Os dois podem ser usados juntos.- Ordem de checagem ao entrar: chave presente? → limiter (cooldown/limite diário) OK? → consome a chave (se
consume_key: true). Se qualquer etapa falhar, o jogador não entra e nada é consumido.
menu-item:
material: ENDER_EYE
custom-model-data: 0
name: "&a&l{display}"
lore:
- "&7Modo: &f{mode}"
- "&7Andares: &f{max_floors}"- Se a dungeon não tiver
menu-item, o menu usa o template padrão emmenus.yml(menus.solo.dungeon-item/menus.party.dungeon-item) — então normalmente você só precisa definirmenu-itemquando quiser um ícone diferente do padrão para aquela dungeon específica. - Truque para esconder uma dungeon do menu sem excluí-la: configure
menu-item.material: AIR(ou o template padrão comoAIR) — a dungeon é pulada na montagem do menu, mas continua jogável via/tower menu solo|partyadministrativo,/tower give key, etc. - Placeholders disponíveis no
name/lore:{id},{display},{mode},{max_floors}.
Dois grupos de titles existem: os de nível raiz (só podem ser definidos uma vez, no topo do arquivo) e os por-andar-com-fallback-pro-raiz (podem ser sobrescritos por andar específico em floors.<n>.titles.<grupo>, caindo para titles.<grupo> da raiz se o andar não tiver override, e caindo para um texto padrão do lang/pt-BR.yml se nem a raiz tiver).
| Grupo | Nível | Quando aparece | Placeholders |
|---|---|---|---|
titles.on_enter |
só raiz | Ao clicar pra entrar, antes do teleporte | {floor} (sempre 1), {delay} = start_delay_seconds |
titles.preparing |
só raiz | Logo após o teleporte, antes do andar 1 começar | {floor} (sempre 1), {delay} = start_delay_seconds |
titles.floor_started / floors.<n>.titles.floor_started |
raiz + por-andar | Início de um andar normal (floors.<n>.boss ausente/false) |
{floor}, {boss_name} |
titles.boss_floor_started / floors.<n>.titles.boss_floor_started |
raiz + por-andar | Início de um andar marcado boss: true |
{floor}, {boss_name} |
titles.floor_cleared / floors.<n>.titles.floor_cleared |
raiz + por-andar | Andar limpo e o próximo andar NÃO é boss | {floor} (andar limpo), {delay} = next_floor_delay_seconds, {boss_name} |
titles.next_is_boss / floors.<n>.titles.next_is_boss |
raiz + por-andar | Andar limpo e o próximo andar É boss (usa os textos do andar de boss que vem a seguir) | {floor} (andar limpo), {delay} = next_floor_delay_seconds, {boss_name} (do próximo andar) |
titles.completed |
só raiz | Dungeon inteira concluída (passou do max_floors) |
{dungeon} = display |
titles.session_ended_leave |
só raiz | O líder da sessão saiu (/tower party leave//tower leave) — run cancelada pra todo mundo |
— |
titles.session_ended_disconnect |
só raiz | O líder da sessão desconectou — run cancelada pra todo mundo | — |
titles.session_ended_shutdown |
só raiz | Plugin/servidor desligando com a run em andamento | — |
Os templates padrão (solo_10.yml/party_10.yml) só configuram on_enter, preparing, floor_cleared e completed — os demais (incluindo os três session_ended_*) funcionam com o texto padrão do idioma até você querer customizá-los.
Os três session_ended_* também mandam a mesma mensagem no chat (messages.session_ended_leave/_disconnect/_shutdown) pra quem não estiver olhando pro title. Isso resolve o problema de sair/desconectar como líder deixando os outros jogadores sem nenhum aviso do porquê a dungeon acabou de repente.
Andar de boss, exemplo:
floors:
10:
boss: true
boss_name: "&4&lRei Esqueleto"
titles:
boss_floor_started:
title: "&4&l{boss_name}"
subtitle: "&cSobreviva!"
mobs:
- mythic: SkeletonKing
amount: 1
spawns: [...]Configurar floors.<n>.boss: true sem titles próprios ainda troca automaticamente o title exibido para o grupo boss_floor_started/next_is_boss (usando o texto padrão de titles.boss_floor_started da raiz, se existir, senão o do lang/pt-BR.yml).
floors:
1:
rewards:
enabled: true
items:
- type: DIAMOND
amount: 1
commands:
- "give {player} emerald 2"
- "broadcast &6{player} completou o andar!"enabled— sefalse(ou a seçãorewardsausente), nada é entregue nesse andar.items— lista detype(nome deMaterial) +amount; é adicionado ao inventário do jogador, e o que não couber cai no chão aos pés dele.typeinválido logareward_invalid_iteme pula aquele item.commands— executados pelo console (Bukkit.dispatchCommandcom o console sender), um por linha, com{player}substituído pelo nome do jogador.- Só recebem recompensa os jogadores online e vivos no momento em que o andar foi limpo — quem morreu naquele andar (
deadThisFloor) fica de fora dessa rodada de recompensas (mas volta a receber nos andares seguintes, se reviver como espectador do resto da run normalmente).
Por padrão, o jogador entra na dungeon uma vez (spawn de player_spawns/party_leader_spawn/party_member_spawns) e fica naquele mesmo lugar físico em todos os andares — só os mobs mudam de acordo com floors.<n>.mobs. Isso é ótimo pra torres onde os andares são só "ondas" no mesmo espaço, mas às vezes você quer dar a impressão de estar subindo de verdade, trocando de sala/prédio a cada andar (ou reaproveitando um pequeno número de arenas construídas, alternando entre elas).
Pra isso existe floors.<n>.floor_spawn — um local (mesmo formato do return_spawn) que, se definido, teleporta todo mundo (solo ou party inteira) pra lá no início daquele andar específico, em vez de manter a posição de entrada. Andares sem floor_spawn continuam usando o comportamento padrão (ficam onde estavam).
floors:
1:
floor_spawn: { world: world, x: 0.5, y: 100.0, z: 0.5 } # "arena A"
mobs: [...]
2:
floor_spawn: { world: world, x: 0.5, y: 100.0, z: 100.5 } # "arena B"
mobs: [...]
3:
floor_spawn: { world: world, x: 0.5, y: 100.0, z: 0.5 } # volta pra "arena A"
mobs: [...]Configurando pelo comando (mais prático que editar o YAML na mão):
/tower admin setup solo_10
/tower admin setfloorspawn 1 # fica em pé na arena A e roda
/tower admin setfloorspawn 2 # fica em pé na arena B e roda
/tower admin setfloorspawn 3 # fica em pé na arena A de novo (ou outro lugar) e roda
/tower admin save
Cada floors.<n>.mobs[].spawns daquele andar deve ficar, claro, dentro da arena física correspondente ao floor_spawn do mesmo andar — do contrário os mobs spawnam num lugar e os jogadores ficam em outro. floor_spawn também respeita arenas alternativas: configure /tower admin setup <dungeonId> <arena> antes de rodar setfloorspawn pra editar a arena certa.
Já vem ativo nos templates padrão (
solo_10.yml/party_10.yml): os 10 andares alternam entre "arena A" (-47.5, 167, 45.5— oplayer_spawns/return_spawnde sempre) e "arena B" (0.5, 100, 0.5— coordenada de exemplo, reaproveitando um ponto que já existia no arquivo). São coordenadas ilustrativas pra você ver o recurso funcionando; troque pelas arenas de verdade que você construir usando/tower admin setfloorspawn <floor>, e ajustefloors.<n>.mobs[].spawnsde cada andar pra combinar.
Cada entrada de floors.<n>.mobs é um dos dois formatos abaixo (type para mob vanilla, mythic para MythicMobs). Se ambos os campos aparecerem na mesma entrada, mythic tem prioridade e type é ignorado — então nunca misture os dois, use um ou outro (código: DungeonSession.spawnFloorMobs).
Mob vanilla — use type com o nome do org.bukkit.entity.EntityType (maiúsculo, ex.: ZOMBIE, SKELETON, SPIDER, WITHER_SKELETON):
floors:
1:
mobs:
- type: ZOMBIE
amount: 3
equipment:
hand:
material: IRON_SWORD
enchantments:
sharpness: 3
fire_aspect: 1
helmet: IRON_HELMET
chestplate: IRON_CHESTPLATE
leggings: IRON_LEGGINGS
boots: IRON_BOOTS
spawns:
- world: world
x: 0.5
y: 100.0
z: 0.5equipment é opcional e só se aplica a mobs type (vanilla) — o MythicMobs já tem seu próprio sistema de equipamento. Slots aceitos, todos opcionais e independentes:
| Slot | Equivalente |
|---|---|
hand |
Item na mão principal (ex.: uma espada) |
offhand |
Item na mão secundária (ex.: um escudo) |
helmet |
Capacete |
chestplate |
Peitoral |
leggings |
Calça |
boots |
Botas |
Cada slot aceita dois formatos:
- Simples — só o material:
hand: DIAMOND_SWORD. - Com encantamentos — um mapa com
materialeenchantments:hand: material: DIAMOND_SWORD enchantments: sharpness: 5 unbreaking: 3
material é o nome de um Material válido (ex.: DIAMOND_SWORD, NETHERITE_CHESTPLATE). enchantments é um mapa nome_do_encantamento: nível, onde o nome é a chave vanilla em minúsculo (ex.: sharpness, protection, unbreaking, fire_aspect, knockback) — os mesmos nomes usados no /enchant do Minecraft. Os níveis não são limitados ao máximo vanilla (equivalente a um encantamento "unsafe": dá pra colocar sharpness: 10, por exemplo).
Um slot com material ou encantamento inválido é ignorado individualmente (loga dungeon.equipment_invalid_item / dungeon.equipment_invalid_enchantment) sem afetar os demais. O equipamento configurado não dropa quando o mob morre (drop chance forçado para 0 em todos os slots usados).
drops também é opcional e só se aplica a mobs type (vanilla) — mob mythic já tem o sistema nativo de Drops: do próprio MythicMobs (com suporte a mmoitem{...}), configurado direto no YAML do mob (plugins/MythicMobs/mobs/), não usa este recurso. É um item extra que aquele mob específico pode dropar ao morrer, além da recompensa de fim de andar (floors.<n>.rewards):
floors:
1:
mobs:
- type: ZOMBIE
amount: 3
drops:
- 'mmoitem{type=MATERIAL,id=FOSSILIZED_BONE,amount=1,chance=30}'
- 'mmoitem{type=MATERIAL,id=CORRUPTED_SPORE,amount=1-2,chance=25}'
- 'material{type=GOLD_NUGGET,amount=1,chance=10}'Cada entrada é uma string no mesmo estilo chave{param=valor,...} já usado nos ingredientes das crafting-stations do MMOItems (plugins/MMOItems/crafting-stations/*.yml):
| Chave | Uso |
|---|---|
mmoitem{type=<TIPO>,id=<ID>,amount=<N>,chance=<C>} |
Item do MMOItems — type é o net.Indyuce.mmoitems.api.Type (ex.: MATERIAL, SWORD, ARMOR), id é o ID do item nesse tipo |
material{type=<MATERIAL>,amount=<N>,chance=<C>} ou vanilla{...} (alias) |
Item vanilla puro — type é o nome de um org.bukkit.Material |
amount— número fixo (amount=2) ou faixamin-max(amount=1-3, sorteada por drop). Padrão1se omitido.chance—0–100, cada entrada da lista é rolada de forma independente (não é um sorteio único entre as opções — dá pra várias entradas dropar juntas, ou nenhuma). Padrão100(sempre dropa) se omitido.- Entrada com formato inválido,
type/idinválido, ou item do MMOItems não encontrado é ignorada individualmente e loga um aviso no console (dungeon.drop_*) sem afetar as outras entradas. - O item dropado cai no chão (
Location#getWorld().dropItemNaturally) na posição de morte do mob, igual um drop vanilla normal — não vai direto pro inventário do jogador.
Mob do MythicMobs — use mythic com o nome interno exato do mob (o mesmo definido nos arquivos de config do MythicMobs, ex.: mythicmobs/Mobs/*.yml):
floors:
2:
mobs:
- mythic: SkeletalKnight
amount: 3
spawns:
- world: world
x: -52.5
y: 167.0
z: 39.5Regras e comportamento de ambos os formatos:
amount— quantidade a spawnar; distribuída ciclicamente entre os pontos despawns(ex.: 6 mobs com 3 spawns → 2 em cada ponto).spawns— lista de coordenadas (world,x,y,z); se vazia, o mob cai como fallback na localização de um jogador online.- Se
typefor inválido (não existe noEntityType), a entrada é ignorada e logadungeon.vanilla_mob_invalidno console. - Se
mythicfor usado sem o plugin MythicMobs instalado, a entrada é ignorada e logadungeon.mythic_missingno console (o spawn é feito via reflexão, então o InfinityTower nem precisa do MythicMobs no classpath para compilar). - Um andar sem nenhum mob válido spawnado encerra a run automaticamente se
allow_empty_floors: false. - Em arenas alternativas (
<id>_arena2, etc.), a listafloors.<n>.mobsda arena normalmente só reescrevespawns(mesma ordem/índice da lista raiz) —type/mythic/amountcontinuam vindo da definição raiz. - Os mobs sempre nascem já perseguindo alguém da sessão — assim que spawnam (e a cada 1s depois, junto do monitor de andar), o plugin define via
Mob#setTarget(...)o jogador vivo mais próximo (dentre os da própria sessão) como alvo. Isso é forçado independente da IA vanilla de detecção por distância/visão — então, mesmo quefloor_spawn/return_spawndo jogador fique longe de onde o mob nasceu (spawns), o mob ainda vai perseguir o jogador em vez de ficar parado. Se o alvo morrer/sair, um novo alvo é escolhido no próximo tick do monitor. - Mobs da dungeon não brigam entre si.
MobFriendlyFireListenercancela qualquer dano entre dois mobs marcados como da dungeon (inclusive por flecha/projétil) — sem isso, a IA vanilla (HurtByTargetGoal) faria um esqueleto que acertasse um zumbi por acidente passar a mirar nele em vez de no jogador. - Actionbar de mobs restantes. Todo jogador da sessão recebe uma actionbar (
messages.floor_mobs_actionbar, placeholder{count}) com a contagem atual de mobs vivos daquele andar — atualizada assim que o andar começa e a cada 1s (junto do monitor). A contagem reflete só os mobs realmente rastreados pela sessão (ver tracking de mobs externos sobreallowed_spawn_reasons).
Um mesmo arquivo dungeons/<id>.yml pode conter mais conteúdo além da dungeon principal, através de seções top-level (mesmo nível de display/floors/etc.):
Arenas alternativas — seção <dungeonId>_arena2 (_arena3, _arena4, ...). Pense nisso como "a mesma dungeon, mas rodando em outro lugar do mapa" (útil pra ter N instâncias físicas simultâneas sem duplicar todo o YAML e sem duplicar o histórico/ranking, que continua contando pro dungeonId original). A arena sobrescreve só o que for informado por cima da configuração raiz — na prática, isso costuma ser:
solo_10_arena2:
player_spawns: [...]
return_spawn: {...}
party_leader_spawn: {...} # se for dungeon PARTY
party_member_spawns: [...] # se for dungeon PARTY
floors:
1:
mobs:
- spawns: [...] # só "spawns" — type/mythic/amount continuam vindo da raiz
2:
mobs:
- spawns: [...]- Em
floors.<n>.mobsda arena, cada entrada é combinada pelo índice com a lista raiz (a 1ª entrada da arena sobrescreve ospawnsda 1ª entrada da raiz, e assim por diante) — sóspawnsé lido da arena,type/mythic/amount/equipmentsempre vêm da definição raiz do andar. /tower admin setup <dungeonId> <arena>(ex.:arena2ou só2) abre a sessão de setup apontando pra essa seção, entãosetspawn/addspawn/setreturn/mobspawneditam a arena, não a raiz.- Se a seção da arena não existir ainda no arquivo,
/tower admin setup <dungeonId> <arena>cria ela vazia na hora de salvar.
Dungeons extra — qualquer outra seção top-level (que não siga o padrão ..._arenaN) contendo pelo menos uma destas chaves: player_spawns, return_spawn, floors, mode ou display. Vira uma dungeon completamente independente (com seu próprio dungeonId = nome da seção, seu próprio ranking, sua própria entrada em /tower admin list), mas reaproveitando como base tudo que não for sobrescrito na seção. Exemplo prático: uma versão "hard" da mesma torre, no mesmo arquivo:
# solo_10.yml
display: "&bTorre Solo"
mode: SOLO
floors: {...}
# ...
solo_10_hard:
display: "&cTorre Solo (Difícil)"
floors:
1:
mobs:
- type: ZOMBIE
amount: 10 # muito mais mobs que a versão normal
spawns: [...]Diferente da arena, aqui você normalmente quer sobrescrever floors inteiro (não só spawns), já que é uma dungeon com identidade própria, não uma cópia física da mesma.
Fluxo típico para configurar uma dungeon dentro do jogo, sem editar YAML manualmente:
/tower admin create minha_dungeon
/tower admin setup minha_dungeon
/tower admin setspawn solo # (ou: setspawn leader, para PARTY)
/tower admin addspawn members # repita para cada spawn de membro (modo PARTY)
/tower admin setreturn
/tower admin mobspawn set 1 # define os pontos de spawn de mob do andar 1
/tower admin mobspawn add 1 # adiciona mais um ponto ao mesmo andar
/tower admin save
/tower admin wheremostra em qual dungeon/arena você está editando./tower admin canceldescarta as mudanças da sessão atual.mobspawnexige que o andar já tenha ao menos um mob configurado no YAML (o comando só edita a listaspawns, não cria o mob em si).- Para editar uma arena alternativa:
/tower admin setup minha_dungeon arena2(ou apenas2).
- Tamanho máximo: 4 jogadores (fixo em código).
- Convites expiram após
party.invite_expire_seconds(config.yml, mínimo 20s, padrão 20s). - Desconexão do líder desfaz a party; desconexão de membro o remove (e desfaz se sobrar só 1).
- Ao sair um líder sem desfazer a party, a liderança passa automaticamente para outro membro.
PartyFriendlyFireListenerevita dano entre membros da mesma party.- Sair/ser expulso da party sincroniza com uma dungeon em andamento.
/tower party leave,/tower party kicke/tower party disbandchamam a mesma lógica de saída de sessão usada por/tower leave: se o jogador afetado era o líder da sessão (a run em PARTY), a dungeon é encerrada pra todo mundo; se era só um membro da sessão, ele sai sozinho dela (o resto continua). Sem isso, sair da party não tirava ninguém da run em andamento.
Configurado em config.yml: database:
database:
enabled: true
type: sqlite # sqlite ou mysql
sqlite:
file: "database.db"
mysql:
host: "localhost"
port: 3306
database: "infinitytower"
user: "root"
password: ""
useSSL: falseUsado para:
PlayerStatsRepository— estatísticas agregadas por jogador (runs/wins/losses solo e party) — alimenta os placeholders de stats e o menu de estatísticas.TowerStatsRepository— ranking de vitórias e melhores recordes (individual/party) por dungeon.RunHistoryRepository/RunLogger— histórico/log de runs e uso de chaves.
O schema é criado/migrado automaticamente ao conectar (colunas ausentes são adicionadas em runtime).
mvn clean packageGera target/InfinityTower-1.0.jar (shaded, já com SQLite/MySQL embutidos).
Projeto interno — sem licença pública definida.