Saltar para o conteúdo principal

Níveis e mapas do EternalTD

Um nível EternalTD combina um ficheiro YAML em plugins/EternalTD/levels/ com uma pasta de mundo de origem em plugins/EternalTD/worlds/. Quando um jogador entra, o EternalTD copia esse mundo para a pasta de mundos do servidor e executa a sessão na cópia.

Campos da configuração do nível​

CampoTipoPredefiniçãoObservações
isEnabledbooleanotrueNíveis desativados são ignorados no carregamento
levelNametextonullNome apresentado nas mensagens, painéis de pontuação e menus de NPCs
levelDescriptionlista de textos[]Linhas dos menus de NPCs. Aceita $highscoreWave e $highscorePlayer
worldNametextonullNome da pasta de origem em plugins/EternalTD/worlds/
startLocationlista de textosnullPosições serializadas onde aparecem os mobs
endLocationlista de textosnullPosições serializadas para onde caminham os mobs, as células vermelhas
levelLocationslista de textosnullTodas as células transitáveis do nível. Os pacotes incluem-na; num mapa personalizado, /etd register <level> escreve-a a partir de uma seleção do chão, ou pode escrevê-la manualmente
wavesConfigFiletextoobrigatórioNome do ficheiro de vagas associado, waves/<name>.yml
highscoreWaveinteiro0Melhor vaga alcançada no nível
highscorePlayerNametexto"No one"Nome apresentado do detentor do recorde
environmentenumNORMALAmbiente usado ao carregar o mundo copiado

Cada célula lógica tem 3 blocos de lado, conforme a constante GRID_SIZE.

waveCount não é usado. A sua presença num ficheiro antigo não define o número de vagas; o EternalTD usa a configuração de vagas associada.

Formato das posições​

As posições são serializadas como texto separado por vírgulas:

worldName,x,y,z,pitch,yaw

Também são aceites textos de quatro partes worldName,x,y,z, com pitch e yaw a zero, e textos de cinco partes x,y,z,pitch,yaw, sem mundo. Para a grelha, os inícios e os fins de uma sessão, o EternalTD substitui o mundo indicado pelo da instância. As posições do hub e dos NPCs precisam de ter o respetivo mundo carregado.

Os pacotes transferidos normalmente incluem estes valores. Num mapa personalizado, levelLocations contém uma coordenada inteira do bloco central por célula de chão jogável de 3×3, com três blocos de intervalo. O carregamento soma 0.5 a X e Z para obter o centro no mundo. /etd register <level> escreve esta lista a partir de uma seleção do chão (consulte Procedimento de criação de mapas); também a pode escrever manualmente.

Ciclo de vida do mundo​

Quando um jogador entra num nível:

  1. O EternalTD procura worldName nas subpastas diretas de plugins/EternalTD/worlds/. Nomes com qualquer . são excluídos. Mundos de origem em falta, configurações de vagas indisponíveis e nomes inseguros são rejeitados. Também são rejeitados níveis cujo levelLocations, startLocation ou endLocation esteja em falta, vazio ou contenha uma entrada que não possa ser interpretada: "This level has missing or invalid floor, entrance, or exit coordinates. Nothing was started."
  2. Rejeita um segundo pedido de preparação enquanto o primeiro pedido do jogador estiver pendente, e rejeita jogadores que já estejam numa partida, incluindo uma partida de outro plugin Nightbreak ("You are already in a match!"). O pedido pendente reserva o jogador no sistema partilhado de partidas, pelo que as partidas de outros plugins o recusam até o pedido terminar. Desligar-se, ou passarem 10 minutos sem um mundo pronto, cancela o pedido e elimina a cópia parcial.
  3. Reserva o próximo sufixo numérico livre: <worldName>_0, <worldName>_1, etc. A reserva é sincronizada e inclui nomes ainda não escritos no disco, evitando colisões entre entradas simultâneas.
  4. Copia o mundo fora da thread principal. O carregamento do mundo e a inicialização da sessão continuam na thread principal.
  5. Carrega a cópia como mundo vazio temporário através de TemporaryWorldManager do MagmaCore. A migração Paper 26.1+ fica em quarentena e chunks ausentes surgem vazios, sem gerar terreno novo.
  6. O EternalTD valida a grelha (consulte Validação do início da sessão) e InstanceProtector aplica as proteções do EternalTD. Depois, o sistema de partidas admite o jogador: guarda o seu registo de recuperação, teleporta-o para o ponto de spawn do novo mundo no tick seguinte e entrega os itens da partida. Se outro plugin recusar esse teletransporte, o jogador sai da partida e esta termina. Espere por "Created your world! We're ready to go." antes de começar. A mensagem anterior "Joined level!" não confirma, por si só, uma sessão bem-sucedida.

Requisitos do mundo de origem​

A raiz é validada antes da criação da cópia; cada ficheiro e subpasta é verificado durante a cópia. Uma entrada inválida faz eliminar a cópia parcial. O mundo é rejeitado se:

  • não for uma pasta ou for uma ligação simbólica
  • não contiver level.dat na raiz
  • contiver uma ligação simbólica em qualquer ponto
  • contiver .eternaltd-instance, nome reservado descrito abaixo

Uma cópia falhada desencadeia o restauro e uma mensagem de erro. Consulte a consola se a própria limpeza falhar; a mensagem não comprova que todos os ficheiros foram removidos.

Marcadores de propriedade das instâncias​

Cada cópia recebe .eternaltd-instance com os nomes do mundo de origem e da instância. O EternalTD verifica este marcador antes de eliminar uma instância abandonada e antes de eliminar um mundo copiado que falhou a validação. Uma cópia parcial é removida assim que a cópia falha, e o mundo de uma sessão criada é eliminado pelo sistema de partidas quando a sessão termina (ver abaixo).

Instâncias abandonadas após uma falha são limpas ao carregar os níveis, apenas se tiverem marcadores válidos e não estiverem carregadas.

Validação do início da sessão​

Uma sessão só fica jogável se passar todas as verificações seguintes:

  • levelLocations, startLocation e endLocation devem existir e não estar vazios; caso contrário, é apresentada a mensagem "This level doesn't have a complete play area, start, and end configuration!"
  • a área deve ser convertível numa grelha válida; caso contrário, é apresentada a mensagem "This level's play area is invalid!"
  • cada célula inicial deve ter um percurso terrestre e um aéreo até alguma célula final; caso contrário, é apresentada a mensagem "This level does not have complete land and air paths from every start to an end!"

Estas verificações ocorrem depois de o mundo copiado carregar e antes de o jogador ser admitido, pelo que uma falha deixa intacto o estado do próprio jogador. O EternalTD elimina então o mundo temporário e apresenta "Failed to start the level. Your player and the temporary world were rolled back." Se a própria admissão falhar depois de o registo de recuperação ter sido guardado, o registo é reposto de imediato.

Quando a sessão termina:

  • todas as torres restantes são desmontadas; erros ao remover uma torre são registados e essa torre é ignorada, sem impedir a remoção das restantes
  • o registo de recuperação do jogador é reposto e o jogador regressa ao local onde estava imediatamente antes de entrar no nível, ou ao spawn do mundo principal se esse mundo já não estiver carregado
  • as proteções do mundo são removidas. Quem ainda estiver no mundo copiado, como membros da equipa com eternaltd.match.bypass, é enviado para o spawn do hub definido em config.yml, ou para o spawn do mundo principal se não houver hub configurado
  • são libertadas as referências que mantêm os chunks da área carregados; consulte Validação dos percursos
  • a limpeza solicita retirar o mundo da memória e eliminá-lo nos dois formatos; se a retirada for recusada, não há eliminação. Com o plugin ativo, a eliminação é assíncrona e ocorre 5 segundos depois de o mundo ser retirado da memória. Um mundo novamente carregado antes da limpeza também é ignorado, e eliminações incompletas são registadas
  • o encerramento é idempotente: uma segunda chamada end(), como /etd quit seguido de /etd hub, não faz nada

Proteções das instâncias​

Enquanto o nível está ativo, o mundo copiado bloqueia:

  • explosões
  • fluxo de líquidos
  • élitros
  • alterações do voo
  • fogo amigo
  • aparecimento vanilla de mobs

O sistema de partidas também protege o mundo: o jogador da partida não se pode teletransportar para dentro, para fora ou dentro dele, exceto através dos movimentos do próprio EternalTD, e outros jogadores sem eternaltd.match.bypass não se podem teletransportar para lá. Consulte Regras da partida.

Procedimento de criação de mapas​

Use o mundo de origem como original editável e escreva a grelha no YAML do nível:

  1. Edite o mapa noutro mundo ou em modo de um jogador, guarde-o e coloque o mundo de origem em plugins/EternalTD/worlds/<worldName>/. Editar uma cópia de partida não atualiza esse original.
  2. Crie o YAML do nível, associe uma configuração de vagas carregada e preencha levelLocations, startLocation e endLocation. Use blocos centrais do chão para a grelha e posições iniciais/finais que correspondam a essas células.
  3. Para usar os auxiliares no mundo de edição, execute /etd reload para carregar a configuração e depois /etd selectfloor. O clique esquerdo define o canto 1 e o clique direito define o canto 2. Mantenha ambos no mesmo mundo e na mesma altura do chão; os comprimentos X/Z, incluindo as extremidades, devem ser múltiplos de 3.
  4. Em alternativa, /etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2> define os cantos no mundo atual e subtrai 1 a cada Y indicado. Forneça o Y do chão mais um.
  5. /etd showselection <level> mostra uma partícula END_ROD, visível apenas para si, por cima de cada célula do chão que a seleção registaria. Não guarda nada.
  6. Mantenha a seleção ativa ao executar /etd register <level> start ou end. Estes comandos acrescentam as coordenadas inteiras de bloco do jogador e guardam imediatamente, sem subtrair 1 a Y nem verificar se o ponto pertence à grelha. Inicialize ambas as listas antes de os usar. Também pode editá-las diretamente no YAML.
  7. Depois destes registos, execute /etd register <level>. O comando escreve levelLocations a partir da seleção, substituindo a lista existente, guarda o ficheiro do nível e limpa a seleção. Desativar o modo de seleção também a descarta.
  8. Guarde o mundo de origem editado, recarregue e entre pelo menu do NPC ou por /etd join <level>. Aguarde a mensagem final de mundo pronto e verifique o mapa em jogo.

O verificador de seleção calcula as dimensões assim, truncando o resultado para inteiro:

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

A ordem dos cantos não importa. São verificados os blocos centrais a partir do canto com menor X/Z mais um, de três em três blocos, à altura Y do canto 1, no mundo atual do jogador. Uma célula é ignorada se o bloco central do chão for atravessável ou o bloco acima não o for. A verificação não examina todos os blocos da célula 3×3.

Validação dos percursos​

Para cada início, o EternalTD guarda em cache o percurso A* de menor custo até um fim: um terrestre e outro aéreo. Com várias saídas, compara os percursos concluídos e conserva o mais curto, encaminhando os inimigos para a saída alcançável mais próxima.

A cache terrestre é reconstruída no início da sessão, ao colocar uma torre e ao vender uma torre. A cache aérea só é calculada uma vez, na primeira reconstrução, porque ignora completamente as torres.

Os inimigos aéreos seguem esse percurso separado, sem bloqueios de torres, a 4 blocos acima do caminho configurado.

Carregar a área também mantém carregados todos os chunks que contêm células da grelha, evitando interrupções em partidas longas. Estas referências são libertadas no fim, antes de eliminar o mundo.

A cache determina ainda se o nível é jogável: se faltar um percurso terrestre ou aéreo a qualquer início, a entrada é recusada. A mesma verificação ocorre ao construir, pelo que uma torre que bloqueasse um início é rejeitada sem gastar ouro.

A procura de inícios e fins usa X/Z para resolver uma célula, sem exigir um centro exato nem comparar Y. Uma entrada sem célula correspondente é ignorada com um aviso. O aviso sugere um centro incorreto, mas confirme também se a célula pretendida existe em levelLocations.

Todas as células iniciais e finais válidas são marcadas como não edificáveis, impedindo bloquear diretamente uma entrada ou saída. O indicador de colocação mostra-as a azul-claro.

NPCs e menus de níveis​

As configurações em plugins/EternalTD/npcs/ associam aldeões a níveis. O clique direito abre um menu de nove espaços, com entradas em 0, 2, 4, 6 e 8. Só são considerados os primeiros cinco IDs configurados; os ausentes deixam espaços vazios e os posteriores são ignorados com aviso. Um NPC sem qualquer nível referenciado carregado é ignorado por completo. Clique com o botão esquerdo num painel de vidro verde para pedir o nível correspondente.

CampoTipoPredefiniçãoObservações
isEnabledbooleanotrueNPCs desativados são ignorados
levelIDslista de textosobrigatórioNomes dos ficheiros de níveis oferecidos
locationtextonullPosição no formato worldName,x,y,z,pitch,yaw
nametextonull numa configuração personalizadaNome apresentado do NPC; as definições pré-fabricadas podem fornecer um
difficultytextonull numa configuração personalizadaRótulo acima do NPC; defina-o explicitamente
disguisetextonullDescritor LibsDisguises, por exemplo custom:etd_tutorial_npc
customDisguiseDatatextonullDados adicionais do comando LibsDisguises, normalmente o texto longo da skin de jogador

O aparecimento do NPC é agendado quatro segundos após a criação da sua configuração e exige que o mundo da posição esteja carregado. O aldeão é invulnerável, sem IA, persistente e marcado com a chave de NPC do EternalTD. Com LibsDisguises ativo e ambos os campos de disfarce definidos, o EternalTD tenta aplicar o disfarce; um descritor inválido pode deixar um aldeão normal.

Um suporte de armadura flutuante apresenta difficulty 2.3 blocos acima do NPC.

Comportamento do spawn​

DefaultConfig controla os jogadores no hub:

  • setupDone: indica se a orientação inicial foi concluída; predefinição false.
  • spawnLocations: predefinido como etd_spawn,0,65,0,0,0 e sempre escrito na configuração. Só é resolvido e usado com o mundo etd_spawn presente.
  • manageSpawn: predefinição true. Com a opção ativa e o destino carregado, o EternalTD solicita um teletransporte para o spawn um tick após a entrada.
  • playerGuide: texto do livro de instruções.
  • nightbreak.autoDownloadPluginUpdates: opção partilhada MagmaCore, predefinida como false, que ativa a transferência automática de atualizações do plugin e do conteúdo no arranque.
  • nightbreak.applyPluginUpdatesWithoutRestart: opção partilhada MagmaCore, predefinida como never (também aceita when-empty ou immediately), que controla quando uma atualização do plugin transferida é aplicada sem reiniciar. Consulte Configuração.

Ao terminar a inicialização, o EternalTD também solicita esse teletransporte para jogadores já ligados, exceto os que já estejam no mundo de destino. Este procedimento também exige manageSpawn e um destino carregado. As rotinas de entrada e de fim da inicialização não verificam o resultado do teletransporte, pelo que outro plugin pode impedir a chegada.