EternalTD Waves & Enemies
A wave file under plugins/EternalTD/waves/ defines the entire enemy lineup for a level, including normal waves, sudden-death waves, and optional pre-wave or wave messages. Each level config references one wave file through wavesConfigFile:.
Top-Level Fields
| Field | Type | Default | Notes |
|---|---|---|---|
isEnabled | bool | true | Disabling here also disables the level that depends on this file |
baseMobAmount | int | 20 | Default amount used when an entry doesn't specify amount= |
waveEntities | string list | required | Wave-by-wave enemy entries |
suddenDeathWaveEntities | string list | optional | Optional cyclic wave pool that runs after the configured waves are exhausted |
prewaveMessages | list of maps | optional | Messages shown before a specific wave starts |
waveMessages | list of maps | optional | Messages shown when a wave begins |
If the wave file does not define any sudden-death entries, the level ends as soon as the configured waves are exhausted.
Wave Entity Entry Format
Each line in waveEntities (or suddenDeathWaveEntities) is a colon-separated entry:
- wave=1:entityType=ZOMBIE:healthMultiplier=1.0:amount=10
- wave=2:entityType=SKELETON:fast:treasure
- wave=5:entityType=WITHER_SKELETON:boss
- wave=8:entityType=PHANTOM:air:horde
| Key | Type | Notes |
|---|---|---|
wave | int | Required. Wave number this entry belongs to |
entityType | enum | Required. Bukkit EntityType enum value |
healthMultiplier | double | Optional, default 1.0. Multiplies the entity's max health |
amount | int | Optional. Overrides baseMobAmount for this entry |
name | string | Optional. Custom name string. Parsed and stored, but the plugin does not currently apply it to the spawned mob (nameplates are the health/armor bars instead) |
resist | flag | Marks the entity as slow-immune |
fast | flag | Increases movement speed |
treasure | flag | Doubles the gold bounty on kill |
boss | flag | Spawns as a single boss with scaled health and a boss bar |
horde | flag | Doubles amount and halves health |
air | flag | Entity spawns 4 blocks above the path and ignores ground walls |
Internal Quantity Math
amountis incremented bywave / 10and then truncated back to a whole number, so an entry withamount=20yields 21 mobs at wave 15 and 22 at wave 20.bossentries overrideamounttomax(1, floor(wave / 10)).hordeentries double the finalamount.- On top of the per-entry amount, the wave runtime adds duplicates when the wave is built. Note that the list it indexes into is the fully expanded mob list — the config parser has already emitted one object per mob, so a single
amount=20line contributes 20 consecutive indices, not one. For each even indexi(0, 2, 4, ...) that is both a valid index in that expanded list and smaller than the wave number, one extra clone of the mob at indexiis added. Boss entries are never cloned this way. So the number of bonus mobs is roughlyceil(min(waveNumber, listSize) / 2): 1 extra mob at wave 1, 2 at waves 3-4, 5 at waves 9-10, 8 at waves 15-16, and so on until the wave number outgrows the list.
Spawn cadence inside a wave is min(200 / totalEntities, 20) ticks, and the runtime waits twice that before releasing each mob. horde waves halve the gap, releasing their mobs at double the normal rate. Every mob picks one of the level's start tiles at random when it spawns.
Message Block Format
Both prewaveMessages and waveMessages accept a list of maps:
prewaveMessages:
- wave: 1
message:
- "&aWelcome! Build a couple of basic towers along the path."
- wave: 5
message:
- "&6Heads up: a boss is coming next wave."
prewaveMessages runs at the start of the construction phase for that wave. waveMessages runs the moment the defensive phase begins.
Wave Lifecycle
A session moves through these states:
STARTING— before the player runs/etd startfor the first time.CONSTRUCTION_PHASE— 10-second build window between defensive phases.DEFENSIVE_PHASE— enemies are spawning and walking the path.ENDING— player quit, lost, or the level ran out of waves.
The construction phase always lasts 10 seconds unless the tutorial holds it (see Tutorial Level).
Wave end behavior:
- Completing a wave grants
waveNumber * 10gold. - If you lost no lives during the wave, you receive a "Perfect" bonus worth half the wave bonus (
waveNumber * 10 * 0.5). The announced number and the gold actually granted now match. - Each wave can only pay out once — a wave that somehow reports completion twice is ignored the second time, and no payout happens at all once the session is ending.
- The next wave's modifier list is announced after each wave end.
When a Level Ends
The session advances only if the next wave number exists as a key in waveEntities, so wave numbers must run contiguously from 1. When the next number is missing:
- if the file defines
suddenDeathWaveEntities, the run rolls into sudden death; - otherwise the level ends immediately, without counting the wave that never ran. The persisted high score and the completion title use the last wave the player actually played.
Sudden Death
If suddenDeathWaveEntities is defined, the level keeps running past the configured wave list.
The sudden-death pool cycles by configured wave number, not by a running counter: each time a sudden-death wave is needed, EternalTD picks the lowest wave= number in the pool that is greater than the current one, and wraps back to the lowest configured number when there is nothing higher. The pool therefore does not need to be numbered 1, 2, 3, ... — gaps and arbitrary starting numbers cycle correctly.
The displayed wave number keeps counting up from the regular wave list; only the pool entry that gets spawned cycles.
Elemental Attunement on Wave Entities
Each wave has its own elemental attunement that rotates per wave:
- Wave 1 → Fire
- Wave 2 → Air
- Wave 3 → Water
- Wave 4 → Earth
- (then repeats)
When a wave entity spawns, there is a waveNumber / 100 chance that the entity inherits the wave's elemental attunement, displayed as a colored tag floating above the mob.
Elemental damage rules are handled by the tower's DamageTower class:
- Same attunement attacker vs same attunement target — 0.5× damage (resist).
- Opposite single elements (Fire vs Water, Air vs Earth) — 2× damage (crit).
- Fusion attunements (Magma, Blast, Storm, Sludge) follow paired interactions; see source for the full matrix.
- An unattuned tower deals 1× damage to everyone.
Fusion attunements form when a Basic Tower is touched by support towers with combining elemental attunements:
| Combination | Resulting fusion |
|---|---|
| Fire + Earth | Magma |
| Fire + Air | Blast |
| Water + Air | Storm |
| Water + Earth | Sludge |
The conflict rules are narrower than they look:
- Earth + Air on the same tower always reverts it to Unattuned, whatever else is attached.
- Fire + Water reverts to Unattuned only when three or more distinct attunements are in play. A tower touched by exactly one Fire support and one Water support keeps one of the two arbitrarily, because the code falls through to "pick the first attunement in the set" and that set is unordered.
- Unattuned support towers (Spyglass, Gold Mine) never affect the result; they are dropped from the set as soon as any attuned tower is attached.
Health, Armor, and Bounty Scaling
WaveEntity applies these steps at spawn, in this exact order:
- Base health starts at
0.5and grows with the wave:health = 0.5 + ((wave - 1)^1.2) / 2. - Boss entities multiply that by
baseMobAmount × 0.5. - Horde entities halve it.
- Bounty is computed here, as
ceil(health)— beforehealthMultiplierand before the air penalty.treasureentries then double it. maxHealth = health × healthMultiplier.- Air entities halve
maxHealthagain. - Armor only appears from wave 10 onward, set to
maxHealth / 80.
The ordering in step 4 matters: a healthMultiplier of 3.0 triples an enemy's health without changing its gold payout, and air enemies pay the same bounty as their ground equivalents despite having half the health. Gold Mine support towers add their bonus to the bounty at the moment of the killing blow.
Movement speed is:
speed = 0.3 × (1 - slow)
× 1.5 × (1 - slow) if fast
× 0.5 if air
+ wave / 1000
The trailing wave / 1000 term means enemies get very slightly faster every wave regardless of their flags, and it is applied after the air and slow multipliers rather than being scaled by them.
Splitterlings
From wave 20 onward, non-air enemies spawn additional small mobs ("splitterlings") on death.
- Count:
floor(wave / 20)per kill. - If the parent was a
CAVE_SPIDER, the splitterlings areSILVERFISH. Otherwise they areCAVE_SPIDER. - Splitterlings spawn at
wave / 2of the parent's wave, so a deep enough run can still chain a splitterling into more splitterlings on its own death. - Splitterlings give 0 gold on death and air enemies never spawn splitterlings.
Tutorial Level Special Case
The bundled tutorial map runs through a hardcoded tutorial wave script:
- The construction phase does not auto-end until you have placed the exact required towers on the highlighted squares.
- Hardcoded towers are indicated by colored concrete patches under the targeted grid square and, if FMM is installed, a downward red arrow model floating above it.
- The tutorial injects extra gold on early waves so the scripted tower lineup is always affordable:
+100on waves 1, 2 and 3, and+200on wave 4. Later waves get nothing extra. - Each required tower square is coloured by the tower it wants: green concrete for Basic Tower, brown for Barricade, white for Sugar, cyan for Spyglass, yellow for Beam Generator, red for Explosion Factory, blue for Ice Box, gray for Armor Crusher, orange for Gold Mine, purple for Obsidian Quarry, black for anything else.
Player Lives and Gold
- Starting lives: 20.
- Starting gold: 150.
- Boss mobs cost 5 lives if they reach the end. All other mobs cost 1 life.
- Cheating (
/etd cheat) sets gold to10,000,000and lives to1,000,000, and marks the session as cheated for highscore purposes. Toggling cheat mode back off does not restore the original values, and the cheated flag is permanent for that session. /etd setwave <n>also sets the cheated flag, and rebases the wave-reward guard so the wave you jump to still pays out when it completes.
Highscore Tracking
Each level config persists highscoreWave and highscorePlayerName. At the end of a non-cheating session, the plugin saves the new wave number into the level file and broadcasts a server-wide message if it beat the previous score.