Pular para o conteúdo principal

Níveis e Mapas do EternalTD

Um "nível" no EternalTD é um config YAML em plugins/EternalTD/levels/ emparelhado com uma pasta de mundo template em plugins/EternalTD/worlds/. Quando um jogador entra, o EternalTD clona o mundo template para o contêiner de mundos do servidor e executa a sessão nessa cópia clonada.

Campos do Config do Nível

CampoTipoPadrãoNotas
isEnabledbooltrueNíveis desativados são ignorados durante o carregamento
levelNamestringnullNome de exibição mostrado em mensagens, scoreboards e menus de NPC
levelDescriptionlista de strings[]Linhas mostradas em menus de NPC. Suporta os placeholders $highscoreWave e $highscorePlayer
worldNamestringnullO nome da pasta template em plugins/EternalTD/worlds/
startLocationlista de stringsnullLista de localizações serializadas onde os mobs spawnam
endLocationlista de stringsnullLista de localizações serializadas para as quais os mobs caminham (as casas "vermelhas")
levelLocationslista de stringsnullToda casa de grade caminhável no nível; no build atual elas precisam vir do pacote ou ser escritas manualmente no YAML
wavesConfigFilestringobrigatórioNome do arquivo de config de onda vinculado (waves/<name>.yml)
highscoreWaveint0Melhor onda alcançada neste nível
highscorePlayerNamestring"no one"Nome de exibição do jogador que estabeleceu o recorde
environmentenumNORMALAmbiente de mundo usado quando o mundo clonado é carregado

O tamanho de grade usado em todo lugar é 3 blocos por casa lógica (constante GRID_SIZE no código).

waveCount não existe mais. Arquivos de nível antigos que ainda carregam a chave são simplesmente ignorados — o plugin nunca a lê e nunca a escreve de volta.

Formato de String de Localização

As localizações no EternalTD são serializadas como strings separadas por vírgulas no formato:

worldName,x,y,z,yaw,pitch

Pacotes baixados normalmente fornecem esses valores. Para um mapa personalizado, os comandos atuais de seleção e registro não salvam levelLocations, então você precisa escrever à mão as localizações de grade geradas no YAML do nível.

Ciclo de Vida do Mundo

Quando um jogador entra em um nível:

  1. O EternalTD verifica se este jogador já não tem uma cópia em andamento. Um segundo /etd join enquanto a primeira ainda está copiando é recusado com "Your level is already being prepared."
  2. Ele procura a pasta template por worldName em plugins/EternalTD/worlds/ e rejeita a entrada se a pasta do mundo estiver ausente, se o arquivo de ondas vinculado falhou ao carregar, ou se o nome do mundo não for um nome de pasta seguro.
  3. Ele reserva o próximo sufixo numérico livre (<worldName>_0, <worldName>_1, ...). A reserva é sincronizada e lembra nomes que estão reservados mas ainda não estão em disco, então dois jogadores entrando ao mesmo tempo não podem colidir no mesmo nome de instância.
  4. O template é copiado para o contêiner de mundos do servidor fora da thread principal, então o servidor não trava com um mapa grande.
  5. O mundo clonado é carregado como um mundo temporário do tipo void através do TemporaryWorldManager do MagmaCore, então a migração do Paper 26.1+ fica em quarentena e chunks ausentes voltam como void em vez de terreno recém-gerado.
  6. O jogador é teletransportado para o novo mundo; um InstanceProtector interno aplica as regras de proteção do EternalTD.

Requisitos do Modelo

A cópia é validada antes de qualquer coisa ser escrita, e um modelo é rejeitado se:

  • não for um diretório, ou for um link simbólico
  • não tiver level.dat na sua raiz
  • contiver um link simbólico em qualquer lugar dentro dele
  • contiver um arquivo chamado .eternaltd-instance (esse nome é reservado, veja abaixo)

Uma cópia que falha é revertida e o jogador é informado de que o nível não foi iniciado.

Marcadores de Propriedade da Instância

Todo clone recebe um arquivo .eternaltd-instance escrito na sua raiz, registrando o nome do modelo e o nome da instância. O EternalTD só deleta uma pasta de mundo cujo marcador corresponda à instância que ele acha que está deletando. É essa a proteção que impede que uma colisão de nome obsoleta, uma pasta feita à mão ou uma sessão descasada apaguem um mundo que o EternalTD não criou — se o marcador estiver ausente ou não corresponder, a pasta é deixada no lugar e um aviso é registrado.

Instâncias obsoletas que sobraram de um crash são limpas quando os níveis carregam, e mesmo assim apenas para pastas que carreguem um marcador válido e não estejam carregadas no momento.

Validação de Início de Sessão

Uma sessão só se torna jogável se todas as condições a seguir valerem. Qualquer falha aborta a run, restaura o jogador (inventário, modo de jogo, voo, scoreboard) e deleta o mundo temporário:

  • levelLocations, startLocation e endLocation estão todos presentes e não vazios — caso contrário, "This level doesn't have a complete play area, start, and end configuration!"
  • a própria área de jogo é analisada como uma grade válida — caso contrário, "This level's play area is invalid!"
  • toda casa inicial tem tanto um caminho terrestre quanto um caminho aéreo até alguma casa final — caso contrário, "This level does not have complete land and air paths from every start to an end!"

Quando a sessão termina:

  • Quaisquer jogadores restantes no mundo clonado são teletransportados de volta para a localização de spawn do config.yml, ou expulsos se nenhum spawn estiver configurado.
  • As regras de proteção do mundo são removidas e todas as torres restantes são desmontadas (uma torre que lança erro ao ser removida é registrada e pulada, em vez de deixar o restante travado).
  • Os chunk tickets mantidos sobre a área de jogo são liberados (veja Validação de Caminho).
  • O mundo clonado é descarregado e deletado do disco (TemporaryWorldManager.permanentlyDeleteWorld), tanto no layout legado quanto no layout migrado do Paper 26.1+.
  • O encerramento é idempotente — um segundo end() (por exemplo, /etd quit imediatamente seguido de /etd hub) não faz nada.

Regras de Proteção da Instância

Enquanto um nível está ativo, o mundo clonado tem estas regras aplicadas:

  • Explosões desabilitadas
  • Fluxo de líquidos desabilitado
  • Elytra desabilitado
  • Alternância de voo bloqueada
  • Fogo amigo bloqueado
  • Spawn de mobs do vanilla bloqueado

Fluxo de Autoria de Mapa

O fluxo atual de autoria de mapas usa as ferramentas dentro do jogo:

  1. Coloque uma pasta de mundo template em plugins/EternalTD/worlds/<worldName>/.
  2. Crie ou baixe um YAML de nível correspondente em plugins/EternalTD/levels/.
  3. Execute /etd reload e entre no mundo do nível manualmente (ou abra-o em single-player para configurar).
  4. Use /etd selectfloor e clique com os botões direito/esquerdo em dois cantos para marcar a área jogável, ou use /etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2> para fornecê-los diretamente.
  5. Execute /etd showselection <level> para confirmar que a seleção parece correta.
  6. Execute /etd register <level> para limpar a seleção. Note que, no build atual, nem register nem showselection de fato persistem a região de piso — o auxiliar que salvaria levelLocations (LevelsConfigFields#addLevelLocations) está definido mas nunca é invocado por um comando. Atualmente você precisa escrever levelLocations no YAML do nível à mão se ele ainda não estiver populado por um pacote baixado.
  7. Fique em pé em uma casa de spawn inicial e execute /etd register <level> start. Repita para cada casa inicial (este comando de fato persiste em startLocation).
  8. Fique em pé em uma casa final e execute /etd register <level> end. Repita para cada casa final (este comando de fato persiste em endLocation).
  9. Recarregue novamente e teste o nível entrando nele pelo menu do NPC ou por /etd join <level>.

Os comandos de seleção geram casas de grade usando:

size = abs(corner1 - corner2 + 1) / 3

Uma casa é pulada quando o bloco de piso dela é passável, ou quando o bloco diretamente acima do piso não é passável. Em outras palavras, o piso precisa ser sólido e o espaço acima dele precisa estar livre para a casa se registrar como jogável — selecione o piso, não o ar acima dele.

Validação de Caminho

O EternalTD armazena em cache, para cada casa inicial, o caminho A* mais barato até alguma casa final — um caminho terrestre e um caminho aéreo. Com várias casas finais, ele compara os caminhos concluídos pelo custo e mantém o mais curto, então um mapa com várias saídas encaminha os inimigos para a mais próxima que seja alcançável, e não para a primeira encontrada.

O cache terrestre é reconstruído quando a sessão inicia, sempre que uma torre é colocada e sempre que uma torre é vendida. O cache aéreo é computado apenas uma vez, na primeira reconstrução: caminhos aéreos ignoram torres por completo, então nada que o jogador construa pode invalidá-los.

Inimigos aéreos usam esse caminho aéreo separado, que ignora completamente o bloqueio por torres e em vez disso segue o offset aéreo (4 blocos acima do caminho configurado).

Carregar a área de jogo também força o carregamento de todo chunk que contenha uma casa de grade, para que uma run longa não trave em um chunk descarregado. Esses chunk tickets são liberados quando a sessão termina, logo antes de o mundo da instância ser deletado.

O cache também é o critério que define se um nível é jogável: se qualquer casa inicial estiver sem o caminho terrestre ou sem o caminho aéreo, a sessão é recusada no momento da entrada, em vez de iniciar uma run que os inimigos não conseguem terminar. A mesma verificação roda quando uma torre é colocada, então uma torre que isolaria uma casa inicial é rejeitada e o ouro não é gasto.

Uma localização inicial ou final que não fique no centro da sua casa 3x3 da grade é descartada com um aviso no console, o que é a causa habitual de um nível reportar um conjunto de caminhos incompleto.

Toda casa inicial e final resolvida também é marcada como não construível, para que os jogadores não possam bloquear um spawn ou uma saída construindo em cima dela. Essas casas aparecem em azul-claro sob o destaque de posicionamento.

NPCs e Menus de Nível

Configs de NPC em plugins/EternalTD/npcs/ vinculam NPCs aldeões a um ou mais níveis. Clicar com o botão direito no NPC abre um inventário de 9 slots listando cada nível como um vidro tingido verde rotulado com o nome e a descrição do nível.

CampoTipoPadrãoNotas
isEnabledbooltrueNPCs desativados são ignorados
levelIDslista de stringsobrigatórioNomes de arquivo dos níveis que este NPC oferece
locationstringnullLocalização de spawn no formato padrão worldName,x,y,z,yaw,pitch
namestring"Default Name"Nome de exibição do NPC
difficultystring"Difficulty: Not Set"Rótulo de dificuldade mostrado acima do NPC
disguisestringnullDescritor do LibsDisguises (ex.: custom:etd_tutorial_npc)
customDisguiseDatastringnullDados extras de comando do LibsDisguises — geralmente a string longa de skin de jogador

O aldeão é spawnado invulnerável, com IA desabilitada, persistente e marcado com a chave de namespace de NPC do EternalTD. Se o LibsDisguises estiver instalado e tanto disguise quanto customDisguiseData estiverem definidos, o aldeão é disfarçado no spawn.

Um armor stand flutuante com o rótulo difficulty é spawnado 2.3 blocos acima do NPC.

Comportamento de Spawn

O DefaultConfig controla como os jogadores são gerenciados no mundo do hub:

  • setupDone — flag que rastreia se a orientação de configuração de primeira vez foi concluída (padrão false).
  • spawnLocations — padrão para etd_spawn,0,65,0,0,0 e é sempre escrito na configuração. Só é resolvido e usado quando o mundo etd_spawn existe.
  • manageSpawn — padrão true. Quando habilitado, jogadores que entram são teletransportados para a localização de spawn 1 tick após o login.
  • playerGuide — o texto do livro guia dentro do jogo.
  • nightbreak.autoDownloadPluginUpdates — configuração compartilhada do MagmaCore (padrão false). Quando habilitada, as atualizações de plugin e conteúdo são baixadas automaticamente na inicialização.

Quando manageSpawn é true e o mundo de spawn está carregado, todo jogador que entra no servidor é teletransportado para spawnLocations. Jogadores que já estavam conectados enquanto o EternalTD ainda inicializava são levados para o mesmo spawn assim que a inicialização termina, a menos que já estejam no mundo do hub.