Niveaux et cartes d'EternalTD
Un « niveau » dans EternalTD est une configuration YAML dans plugins/EternalTD/levels/ associée à un dossier de monde modèle dans plugins/EternalTD/worlds/. Lorsqu'un joueur rejoint, EternalTD clone le monde modèle dans le conteneur de mondes du serveur et exécute la session dans cette copie clonée.
Champs de configuration de niveau
| Champ | Type | Défaut | Notes |
|---|---|---|---|
isEnabled | bool | true | Les niveaux désactivés sont ignorés lors du chargement |
levelName | string | null | Nom d'affichage présenté dans les messages, les tableaux de score et les menus NPC |
levelDescription | liste de chaînes | [] | Lignes affichées dans les menus NPC. Prend en charge les variables $highscoreWave et $highscorePlayer |
worldName | string | null | Nom du dossier modèle sous plugins/EternalTD/worlds/ |
startLocation | liste de chaînes | null | Liste des emplacements sérialisés où les créatures apparaissent |
endLocation | liste de chaînes | null | Liste des emplacements sérialisés vers lesquels les créatures se dirigent (les tuiles « rouges ») |
levelLocations | liste de chaînes | null | Chaque case de grille praticable dans le niveau ; dans la version actuelle, elles doivent provenir du paquet ou être écrites manuellement dans le YAML |
wavesConfigFile | string | requis | Nom de fichier de la configuration de vagues liée (waves/<name>.yml) |
highscoreWave | int | 0 | Meilleure vague atteinte sur ce niveau |
highscorePlayerName | string | "no one" | Nom d'affichage du joueur qui a établi le score |
environment | enum | NORMAL | Environnement de monde utilisé lors du chargement du monde cloné |
La taille de grille utilisée partout est de 3 blocs par case logique (constante GRID_SIZE dans le code).
waveCount n'existe plus. Les anciens fichiers de niveau qui portent encore cette clé sont simplement ignorés — le plugin ne la lit jamais et ne la réécrit jamais.
Format de chaîne d'emplacement
Les emplacements dans EternalTD sont sérialisés sous forme de chaînes séparées par des virgules de la forme :
worldName,x,y,z,yaw,pitch
Les paquets téléchargés fournissent normalement ces valeurs. Pour une carte personnalisée, les commandes actuelles de sélection et d'enregistrement n'enregistrent pas levelLocations : vous devez donc écrire manuellement les emplacements de grille générés dans le YAML de niveau.
Cycle de vie du monde
Lorsqu'un joueur rejoint un niveau :
- EternalTD vérifie que ce joueur n'a pas déjà une copie en cours. Un second
/etd joinpendant que la première copie est toujours en cours est refusé avec « Your level is already being prepared. » - Il recherche le dossier modèle par
worldNamedansplugins/EternalTD/worlds/, et rejette la connexion si le dossier du monde est absent, si le fichier de vagues lié n'a pas pu être chargé, ou si le nom du monde n'est pas un nom de dossier sûr. - Il réserve le prochain suffixe numérique libre (
<worldName>_0,<worldName>_1, ...). La réservation est synchronisée et mémorise les noms réservés mais pas encore présents sur le disque, de sorte que deux joueurs se connectant en même temps ne peuvent pas entrer en collision sur le même nom d'instance. - Le modèle est copié dans le conteneur de mondes du serveur hors du thread principal, afin que le serveur ne se bloque pas sur une grande carte.
- Le monde cloné est chargé en tant que monde vide temporaire via le
TemporaryWorldManagerde MagmaCore, afin que la migration Paper 26.1+ soit mise en quarantaine et que les chunks manquants reviennent sous forme de vide plutôt que de terrain fraîchement généré. - Le joueur est téléporté dans le nouveau monde ; un
InstanceProtectorinterne applique les règles de protection d'EternalTD.
Exigences relatives aux modèles
La copie est validée avant que quoi que ce soit ne soit écrit, et un modèle est rejeté si :
- ce n'est pas un répertoire, ou si c'est un lien symbolique
- il n'a pas de
level.datà sa racine - il contient un lien symbolique quelque part à l'intérieur
- il contient un fichier nommé
.eternaltd-instance(ce nom est réservé, voir ci-dessous)
Une copie échouée est annulée et le joueur est informé que le niveau n'a pas démarré.
Marqueurs de propriété d'instance
Chaque clone reçoit un fichier .eternaltd-instance écrit à sa racine, consignant le nom du modèle et le nom de l'instance. EternalTD ne supprimera un dossier de monde que si son marqueur correspond à l'instance qu'il pense supprimer. C'est le garde-fou qui empêche une collision de nom obsolète, un dossier créé à la main ou une session incohérente de détruire un monde qu'EternalTD n'a pas créé — si le marqueur est absent ou ne correspond pas, le dossier est laissé en place et un avertissement est journalisé à la place.
Les instances obsolètes laissées par un plantage sont nettoyées au chargement des niveaux, et même alors uniquement pour les dossiers portant un marqueur valide et qui ne sont pas actuellement chargés.
Validation au démarrage de la session
Une session ne devient jouable que si toutes les conditions suivantes sont réunies. Tout échec interrompt la partie, restaure le joueur (inventaire, mode de jeu, vol, tableau de score) et supprime le monde temporaire :
levelLocations,startLocationetendLocationsont tous présents et non vides — sinon « This level doesn't have a complete play area, start, and end configuration! »- la zone de jeu elle-même s'analyse en une grille valide — sinon « This level's play area is invalid! »
- chaque case de départ dispose à la fois d'un chemin terrestre et d'un chemin aérien vers une case d'arrivée — sinon « This level does not have complete land and air paths from every start to an end! »
Lorsque la session se termine :
- Tous les joueurs restants dans le monde cloné sont téléportés vers l'emplacement de spawn défini dans
config.yml, ou expulsés si aucun spawn n'est configuré. - Les règles de protection du monde sont retirées et toutes les tours restantes sont démantelées (une tour qui lève une erreur lors de son retrait est journalisée et ignorée plutôt que de bloquer le reste).
- Les tickets de chunk pris sur la zone de jeu sont libérés (voir Validation du chemin).
- Le monde cloné est déchargé et supprimé du disque (
TemporaryWorldManager.permanentlyDeleteWorld), à la fois dans la disposition héritée et dans la disposition migrée de Paper 26.1+. - La fin est idempotente — un second
end()(par exemple/etd quitimmédiatement suivi de/etd hub) est sans effet.
Règles de protection d'instance
Pendant qu'un niveau est actif, les règles suivantes sont appliquées au monde cloné :
- Explosions désactivées
- Écoulement des liquides désactivé
- Élytres désactivées
- Activation du vol empêchée
- Tir ami empêché
- Apparition vanille des créatures empêchée
Flux de création de carte
Le flux actuel de création de carte utilise l'outillage en jeu :
- Placez un dossier de monde modèle sous
plugins/EternalTD/worlds/<worldName>/. - Créez ou téléchargez un YAML de niveau correspondant dans
plugins/EternalTD/levels/. - Exécutez
/etd reloadet rejoignez manuellement le monde du niveau (ou ouvrez-le en solo pour le configurer). - Utilisez
/etd selectflooret cliquez-droit/cliquez-gauche sur deux coins pour marquer la zone de jeu, ou utilisez/etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2>pour les fournir directement. - Exécutez
/etd showselection <level>pour confirmer que la sélection est correcte. - Exécutez
/etd register <level>pour effacer la sélection. Notez que dans la version actuelle, niregisternishowselectionne persistent réellement la région de sol — l'utilitaire qui sauvegarderaitlevelLocations(LevelsConfigFields#addLevelLocations) est défini mais n'est jamais invoqué par une commande. Vous devez actuellement écrirelevelLocationsdans le YAML de niveau à la main s'il n'est pas déjà rempli par un paquet téléchargé. - Placez-vous sur une tuile de spawn de départ et exécutez
/etd register <level> start. Répétez pour chaque tuile de départ (cette commande persiste bien dansstartLocation). - Placez-vous sur une tuile d'arrivée et exécutez
/etd register <level> end. Répétez pour chaque tuile d'arrivée (cette commande persiste bien dansendLocation). - Rechargez à nouveau et testez le niveau en le rejoignant via le menu NPC ou
/etd join <level>.
Les commandes de sélection génèrent les cases de grille selon :
size = abs(corner1 - corner2 + 1) / 3
Une case est ignorée lorsque son bloc de sol est traversable, ou lorsque le bloc directement au-dessus du sol n'est pas traversable. Autrement dit, le sol doit être solide et l'espace au-dessus doit être dégagé pour que la case soit enregistrée comme jouable — sélectionnez le sol, pas l'air au-dessus.
Validation du chemin
EternalTD met en cache, pour chaque tuile de départ, le chemin A* le moins coûteux vers une tuile d'arrivée quelconque — un chemin terrestre et un chemin aérien. Avec plusieurs tuiles d'arrivée, il compare les chemins terminés par leur coût et conserve le plus court : une carte comportant plusieurs sorties achemine donc les ennemis vers la plus proche accessible plutôt que vers la première trouvée.
Le cache terrestre est reconstruit au démarrage de la session, à chaque placement de tour et à chaque vente de tour. Le cache aérien n'est calculé qu'une seule fois, lors de la première reconstruction : les chemins aériens ignorent totalement les tours, rien de ce qu'un joueur construit ne peut donc les invalider.
Les ennemis aériens utilisent ce chemin aérien distinct, qui ignore entièrement le blocage par les tours et suit à la place le décalage aérien (4 blocs au-dessus du chemin configuré).
Le chargement de la zone de jeu force également le chargement de chaque chunk contenant une case de grille, afin qu'une longue partie ne puisse pas se bloquer sur un chunk déchargé. Ces tickets de chunk sont libérés à la fin de la session, juste avant la suppression du monde d'instance.
Le cache détermine également si un niveau est jouable tout court : si une tuile de départ n'a pas son chemin terrestre ou son chemin aérien, la session est refusée au moment de la connexion plutôt que de lancer une partie que les ennemis ne peuvent pas terminer. La même vérification s'exécute lors du placement d'une tour : une tour qui murerait une tuile de départ est refusée et l'or n'est pas dépensé.
Un emplacement de départ ou d'arrivée qui ne se trouve pas au centre de sa case de grille 3x3 est abandonné avec un avertissement en console, ce qui est la cause habituelle d'un niveau signalant un ensemble de chemins incomplet.
Chaque tuile de départ et d'arrivée résolue est également marquée comme non constructible, afin que les joueurs ne puissent pas murer une apparition ou une sortie en construisant dessus. Ces tuiles apparaissent en bleu clair sous la surbrillance de placement.
NPC et menus de niveau
Les configurations de NPC dans plugins/EternalTD/npcs/ lient des NPC villageois à un ou plusieurs niveaux. Un clic droit sur le NPC ouvre un inventaire de 9 emplacements listant chaque niveau sous forme d'un panneau de verre teinté vert étiqueté avec le nom et la description du niveau.
| Champ | Type | Défaut | Notes |
|---|---|---|---|
isEnabled | bool | true | Les NPC désactivés sont ignorés |
levelIDs | liste de chaînes | requis | Noms de fichiers des niveaux que ce NPC propose |
location | string | null | Emplacement d'apparition au format standard worldName,x,y,z,yaw,pitch |
name | string | "Default Name" | Nom d'affichage du NPC |
difficulty | string | "Difficulty: Not Set" | Étiquette de difficulté affichée au-dessus du NPC |
disguise | string | null | Descripteur LibsDisguises (par exemple custom:etd_tutorial_npc) |
customDisguiseData | string | null | Données de commande LibsDisguises supplémentaires — généralement la longue chaîne de skin de joueur |
Le villageois apparaît invulnérable, avec l'IA désactivée, persistant, et marqué avec la clé d'espace de noms NPC d'EternalTD. Si LibsDisguises est installé et que disguise et customDisguiseData sont tous deux définis, le villageois est déguisé à l'apparition.
Un armor stand flottant portant l'étiquette de difficulty apparaît 2,3 blocs au-dessus du NPC.
Comportement de spawn
DefaultConfig contrôle la façon dont les joueurs sont gérés dans le monde hub :
setupDone— indicateur suivant si les conseils de configuration initiale ont été complétés (par défautfalse).spawnLocations— par défautetd_spawn,0,65,0,0,0et toujours écrit dans la configuration. Il n'est résolu et utilisé que lorsque le mondeetd_spawnexiste.manageSpawn— par défauttrue. Lorsqu'activé, les joueurs qui rejoignent sont téléportés à l'emplacement de spawn 1 tick après la connexion.playerGuide— le texte du livre guide en jeu.nightbreak.autoDownloadPluginUpdates— paramètre partagé de MagmaCore (par défautfalse). Lorsqu'activé, les mises à jour du plugin et du contenu sont téléchargées automatiquement au démarrage.
Lorsque manageSpawn est true et que le monde de spawn est chargé, chaque joueur qui rejoint le serveur est téléporté vers spawnLocations. Les joueurs déjà connectés pendant qu'EternalTD s'initialisait encore sont ramenés vers le même spawn une fois l'initialisation terminée, sauf s'ils sont déjà dans le monde hub.