Feature Difficulty Scaling - bh679/dungeon-train-mc GitHub Wiki
Difficulty scaling β mob gear & villager trades by carriage tier
Since: v0.227.0 (PR #279) Β· latest: v0.416.0 (PR #688)
v0.227.0 β PR #279 β carriage-index-based mob progression: hostile mobs spawned inside train carriages receive tiered armor / weapons / effects based on how far the run has travelled. Original 5-rung data-driven tiers.json ladder.
v0.230.0 β PR #283, PR #284, PR #285 β rolled equipment gets Adventure Item Names display names + Adventure Item Stats modifiers; the fixed 5-rung ladder is replaced by the procedural tier curve (ProceduralTiers β smooth material bands from level 1β50 with infinite post-cap effect scaling); progression pauses when no player is boarded; buffs are restricted to hostile (Enemy) mobs β passive carriage animals keep their vanilla/variant gear.
v0.255.0 β PR #317 β mob difficulty resets on player death (the tier is computed from max(travelledCarriageIndex) across online players, and each player's counter zeroes on death).
v0.271.0 β PR #336 β PlayerMobs scale on the same tier (difficulty-scaled equipment, no potion effects).
v0.290.0 β PR #358 β villager trade level capped to the mob weapon stage (see Villager trade cap).
v0.293.0 β PR #364 β gentle first level: a one-level progression delay, plus a first-tier-band "easy mobs" swap (carriage hostiles β small slimes / magma cubes) and a starter-loot downgrade (see Gentle first level).
v0.296.0 β PR #370 β variant spawn-egg mobs spawn equipped: carriage block-variant spawn-egg mobs now run vanilla Mob#finalizeSpawn, so they arrive with their natural default gear β a pillager crossbow (with the ~6% raid-captain ominous banner), a skeleton / bogged bow β even at tier 0; and the difficulty pass now keeps and enchants an existing ranged weapon instead of swapping it for a melee one (see Mob gear scaling).
v0.298.0 β PR #373 β piglins respect the dimension: the first-band easy-mobs swap no longer turns piglins into out-of-place magma cubes on Overworld trains. Zombified piglins are never substituted (Nether-native), and piglins / piglin brutes only become magma cubes in the Nether β split out into the new first_band_nether_only_mobs and first_band_no_substitute_mobs tags (see Gentle first level).
v0.307.0 β PR #405 β three-stage gentle onboarding: the single easy-mobs band becomes a configurable ramp β the first stretch spawns no hostile mobs at all, the next stretch the existing slimes, then normal. Stage lengths are explicit carriage counts (defaults 10 + 15), decoupled from carriagesPerTier (see Gentle first level).
v0.416.0 β PR #688 β gear keeps scaling past netherite: past the level-50 material cap, hostile gear no longer plateaus β each rolled piece gains a per-tier Adventure Item Stats primary-stat bonus (attack damage on weapons, armor on armor), so weapons and armor keep getting stronger the deeper you ride even though the material stays netherite. Reuses the same stat scaling PlayerMobs already receive; tiers β€ 50 are unchanged. Config difficultyScaleHostileGearPastCap (default on) β see Post-cap gear scaling.
v0.738.0 β PR #1203 β the onboarding ramp is halved: the two gentle stages drop from 10 + 15 carriages to 5 + 5, so authored hostiles begin at carriage 10 instead of 25 and the starter-loot window narrows to match (it is derived from the two stage lengths). The difficulty-level curve itself is unchanged β gear still switches on at level 1 (β carriage 40). Because both keys are held to their defaults by the config-integrity check, the change ships with a config migration (v3 β v4) that rewrites only files still holding the old values, plus a version-dated grace so an install that hasn't migrated yet isn't reported as deviating (see Gentle first level).
The train gets harder the further you ride. Every hostile mob that spawns in a carriage rolls armor, a weapon, enchantments and potion effects scaled to a difficulty tier derived from how far the furthest-progressed boarded player has travelled this life. As of v0.290.0 the same tier also caps the trade level of carriage villagers, so merchant strength tracks the mobs you're fighting. As of v0.293.0 the opening band is gentled β the curve is delayed a level and the first tier band swaps hostiles for small slimes/magma cubes and rich chests for starter loot. All default-on and tunable from the one [difficulty] config.
Progression at a glance
Generated from the live curves by tools/build-difficulty-graphs.py in the wiki repo (re-run it whenever the curves are retuned β it mirrors the ProceduralTiers / DifficultyProgression formulas). Everything below keys off the difficulty level (max(0, βcarriages Γ· carriagesPerTierβ β progressionLevelDelay)), so at the defaults level L begins around carriage 20Β·(L + 1).

The whole gentle onboarding lives inside level 0 (carriages 0β9); authored hostiles only begin rolling gear at level 1 (β carriage 40), and gear material tops out at netherite around level 50. Past that, difficulty keeps climbing two ways: the gear keeps gaining raw stats (a per-tier attack/armor bonus past the material cap, v0.416.0 β see Post-cap gear scaling) and the potion-buff ladder layers on β nine infinite-duration buffs from level 15 to level 90:

Early game (levels 0β3)
The first ~100 carriages in detail: level 0 is fully vanilla, gear switches on at level 1, and only leather armour + wood weapons appear (chainmail / stone don't enter until level 5). Equip rates ramp gently β armour 0 β 8 β 16 β 24 % per slot, weapons 0 β 10 β 20 β 30 % β with no potion effects and villagers capped at Apprentice:

Difficulty tier
The tier is a pure function of the leader's progress this life:
// DifficultyProgression
rawTier = abs( max( travelledCarriageIndex over all online players ) ) / max(1, carriagesPerTier)
tier = max(0, rawTier - progressionLevelDelay) // effective/displayed tier (delay default 1)
travelledCarriageIndexis the signed net carriages a player has traversed while boarded (forward+, backwardβ), stored per-player inPlayerRunStateand reset to 0 on death. The tier uses themaxacross online players, so a wipe drops difficulty back to the next live leader (or tier 0 when solo). Walking the train backward lowers the tier.carriagesPerTier(default 20) sets how many carriages a tier is worth β tierTβ20Β·Tcarriages travelled by default. The same value drives the boarding-progress HUD tier and is centralised inDifficultyProgression.tierForTravelled(one formula shared by the mob applier, the HUD packet, and the villager cap).progressionLevelDelay(default 1, since v0.293.0) subtracts from the raw tier βtier = max(0, rawTier β delay)β so the whole curve (mob gear, effects, villager cap, HUDDiff-Level) arrivesdelaylevels later. The raw tier is still used by the first-band features, so the easy-mob / starter-loot opening keeps its length regardless of the delay.0= original curve.- Tier 0 = vanilla baseline β
ProceduralTiers.tierForreturnsnullfor tier β€ 0, so freshly-boarded mobs get no gear, effects, or enchants.
Mob gear scaling
Eligible mobs (Enemy hostiles, carrying the carriage-contents tag, not loaded from disk) roll their tier once at spawn via DifficultyApplier.apply and are stamped with a sticky dungeontrain_difficulty_applied tag so chunk reloads don't re-roll. Pre-existing NBT equipment is respected β only empty slots are filled. Rolled gear is AIN-named and AIS-statted, and drops at ~5%.
Default & ranged equipment (v0.296.0). Variant spawn-egg mobs run vanilla finalizeSpawn at spawn (CarriageContentsPlacer.spawnVariantMob), so they carry their natural default weapon β a pillager's crossbow, a skeleton's / bogged's bow β even at tier 0, before any difficulty roll. Because the tier pass fills only empty slots, a mob already holding a ranged weapon keeps it: the roll enchants the existing bow/crossbow (Power, Quick Charge, β¦) rather than overwriting it with the tier's melee weapon, so ranged mobs stay ranged as the difficulty climbs. Mobs authored with explicit NBT (anvil-renamed eggs) keep their baked equipment as before.
ProceduralTiers builds the tier from overlapping trapezoidal material curves so transitions are gradual rather than stepped. The dominant material by tier:

| Tier | Armor (most likely) | Weapon (most likely) | Armor slot chance | Weapon chance | Enchant chance (max lvl) | New effects |
|---|---|---|---|---|---|---|
| 0 | β none (vanilla) | β | 0 | 0 | 0 | β |
| 1β7 | Leather | Wood | up to ~56% | up to ~70% | up to ~19% | β |
| 8β17 | Leather β Chainmail | Stone | ~64β100% | 100% | ~21β45% | Speed @15 |
| 18β31 | Chainmail β Iron | Iron | 100% | 100% | ~48β83% | + Strength @25 |
| 32β49 | Iron β Diamond | Diamond | 100% | 100% | ~85β100% | + Resistance @35, Fire Res @40 |
| 50+ | Diamond / Netherite | Diamond / Netherite | 100% | 100% | 100% (lvl β€50) | + Regen @50, Jump @60, Haste @70, Absorption @80, Slow Fall @90 |
- Per-slot chances ramp linearly: armor slot
min(1, tier/12.5), weaponmin(1, tier/10), enchantmin(1, tier/37.5)with table-levelmin(50, tier). Golden gear sprinkles in at a constant low weight at every tier as a flavour "rare drop". - Effects layer in at the tiers above and gain
+1amplifier every 25 tiers, so progression stays meaningful well past netherite gear. - Armor is skipped for blob/boss shapes (slimes, magma cubes, ghasts, phantoms, dragon, wither) β effects still apply.
Post-cap gear scaling (v0.416.0)
v0.416.0 (PR #688) fixes a difficulty plateau: because gear material caps at netherite and table-level enchants cap at level 50, a hostile's gear used to stop improving once the run passed difficulty level 50 (β 1,500 carriages at carriagesPerTier=30, β 1,020 at the default 20) β past that only potion effects scaled. Now each rolled equipment piece additionally gets a flat per-tier primary-stat bonus β attack damage on weapons, armor on armor β via Adventure Item Stats, the same mechanism PlayerMobs already use.
- The bonus is scaled off the post-cap tier
max(0, tier β 50)(ItemStatLevelScaling.pastCapTier), so it is 0 at or below level 50 β tiers β€ 50 are byte-identical to before β and climbs from level 51 up at+0.10per tier (+0.15for axes). At level 100 that's+5.0attack / armor (+7.5axes); the netherite material is unchanged, the numbers on it are higher. - Applies only to gear the difficulty pass rolls into empty slots (and rolled melee weapons); a kept default bow/crossbow or author-baked NBT gear is not stat-boosted β the same "empty slots only" contract as the rest of the applier.
- PlayerMobs continue to scale off the full tier (strong from tier 1); regular hostiles use the post-cap tier. Controlled by
DifficultyApplier.StatScaling(NONE/FULL/PAST_MATERIAL_CAP). - Toggle with
difficultyScaleHostileGearPastCap(defaulttrue); setfalseto restore the pre-v0.416.0 behaviour where hostile gear stops improving at netherite.
Experience rewards (v0.796.0)
v0.796.0 (PR #1300) closes the other half of the plateau: gear and buffs kept climbing, but the experience a monster dropped never did, so the reward curve flattened exactly where the difficulty curve steepened β a fully-kitted netherite zombie with deep enchants and four infinite buffs paid the same 5 XP as a naked one on carriage one.
A monster's drop is now scored at death from what it actually carries and multiplied by 1 + Ξ£ weighted points:
| Component | Weight | Source |
|---|---|---|
| Gear stat points | 0.03 each |
Each equipped item's attack damage, or its armor if it has none (bows/crossbows count 4) |
| Enchant levels | 0.05 each |
Every enchantment level summed across all six equipment slots |
| Effect points | 0.20 each |
amplifier + 1 per active beneficial effect |
- The score is read off the live mob at death, not a recorded tier. That keeps it honest about the roll (an unlucky bare mob at tier 40 stays cheap), survives a respawn or progress reset, and needs nothing stored in entity NBT.
ItemPowerScore.baseStatreads theATTRIBUTE_MODIFIERScomponent first β which is where AIS writes the post-cap stat bonus β so gear scaled past netherite pays out more without any extra plumbing.- Beneficial effects only. Harmful and neutral effects score nothing, so a mob the player has debuffed with Weakness never earns a bonus.
- Absurd attribute amounts are discarded via
BakedItemStats.isAbsurd; a corrupt baked-template item can't become an XP bomb. - Applies to any
Mobanywhere β train, Free Play, editor β so a naturally-armoured vanilla mob is rewarded on the same terms. A vanilla full-iron zombie goes5 β 7XP; a kitted tier-60 one5 β 27. - Capped at
mobExperienceMaxMultiplier(defaultΓ10, so a zombie tops out at 50 XP), which bounds the throughput of a late-train mob farm.
Unlike the gear pass this is not gated on difficultyEnabled β it has its own switch, mobExperienceScalingEnabled.
Villager trade cap (v0.290.0)
Carriage villagers with a profession have their trade offers re-rolled at spawn by VillagerTrainSpawnEvents on a weighted [1, 5] curve (Novice common, Master rare). As of v0.290.0 that roll is capped to the dominant mob weapon material at the current tier β so a Master villager (the strongest enchanted-gear / mending trades) can't appear until the mobs around them have advanced to diamond:
| Mob weapon stage | Villager max level | Difficulty tier | Carriages (default cpt=20) |
|---|---|---|---|
| none (vanilla) | 1 (Novice) | 0 | 0β19 |
| Wood | 2 (Apprentice) | 1β7 | 20β159 |
| Stone | 3 (Journeyman) | 8β17 | 160β359 |
| Iron | 4 (Expert) | 18β31 | 360β639 |
| Diamond (+ Netherite) | 5 (Master) | 32+ | 640+ |
// VillagerTrainSpawnEvents
newLevel = min( pickLevel(), // same weighted [1,5] roll
min(5, ProceduralTiers.dominantWeaponStage(tier) + 1) );
- The roll itself is unchanged β the cap is a ceiling, not a re-scale, so at low tiers most villagers pile up at the cap (e.g. tier 8β17 β mostly Journeyman).
- The cap is derived from the same weapon curve the mobs use (
ProceduralTiers.dominantWeaponStage), so it stays paired automatically if the curve is retuned. - Capping a villager to level N also trims their offers to the first N trade tiers β low-tier merchants sell weaker goods, not just fewer.
- Honours
difficultyEnabled: with difficulty off, villagers fall back to the full random[1, 5]range. - Sticky like the mob path β a villager tagged
dungeontrain_trades_rerolledkeeps its level across reloads; the cap only applies to newly-spawned villagers.
Gentle first level (v0.293.0)
v0.293.0 (PR #364) softened the opening band of every run, and v0.307.0 (PR #405) expanded the mob swap into a three-stage ramp β no mobs β slimes β normal β so new players and fresh respawns get an even gentler landing. All default-on and tunable in the [difficulty] config:
- One-level progression delay (
progressionLevelDelay, default1) β shifts the whole difficulty curve back one tier (tier = max(0, rawTier β delay)); see Difficulty tier. Mob gear, effects, the villager cap and the HUDDiff-Levelall arrive a level later.0restores the original curve. - No-hostiles stage (
firstLevelNoHostiles, defaulttrue; lengthfirstLevelNoHostilesCarriages, default 5) β for the first ~5 carriages of leader progress, every hostile (Enemy) authored into a carriage is suppressed entirely (it simply doesn't spawn), so the opening stretch is combat-free. Slimes count as hostiles here too, so even an authored "slimes" carriage is empty in this stage. - Easy-mobs (slimes) stage (
firstLevelEasyMobs, defaulttrue; lengthfirstLevelEasyMobsCarriages, default 5) β for the next ~5 carriages, each authored hostile is replaced at spawn with a small slime (size 1β2), or a small magma cube for nether / pillager-raider mobs. The substitute is tagged + persisted exactly like a normal carriage mob, so kill-ahead, despawn and diagnostics treat it identically. Classification is data-driven via three entity-type tags (tunable without recompiling):first_band_magma_mobs(#minecraft:raiders+ the nether roster β magma cube),first_band_nether_only_mobs(piglin,piglin_bruteβ magma cube only in the Nether; spawn as authored elsewhere, since a magma cube is a Nether creature and an Overworld piglin would just zombify), andfirst_band_no_substitute_mobs(zombified_piglinβ never substituted, in any dimension β it's Nether-native and shouldn't be downgraded). Precedence: no-substitute β nether-only β magma vs slime. - Starter loot (
firstLevelStarterLoot, defaulttrue) β across the whole opening window (both stages,firstLevelNoHostilesCarriages + firstLevelEasyMobsCarriagescarriages), carriage chests linked to the richloot/loot_irongoldloot prefabs instead roll thestarterprefab (starter-tier gear, not late-game loot). All other loot prefabs (wood, stone, mining, villager, pot, β¦) are unchanged. Swapped at the variant-placement seam inCarriageContentsPlacer; editor previews keep the authored loot.
After both combat stages (β carriage 10 by default) authored hostiles spawn normally β at tier 0 (vanilla gear) with the default one-level delay β and the difficulty curve climbs from there. The two stage lengths are explicit carriage counts (of leader progress), decoupled from carriagesPerTier; set either to 0 to drop that stage. Passive / neutral non-Enemy mobs (villagers, animals, PlayerMobs) are untouched throughout, and editor previews keep the authored mobs.
All of the above key off the raw (un-delayed) tier, so the gentle opening stays the same length regardless of progressionLevelDelay, and β like the rest of difficulty β they are leader-anchored (the furthest-progressed boarded player decides the current stage) and decided at spawn / placement time. They run independently of difficultyEnabled (each has its own toggle). The combat-stage decision is centralised in DifficultyProgression.onboardingStage (pure, unit-tested) and applied at the single hostile-spawn seam CarriageContentsPlacer.tryHandleOnboardingHostile β which suppresses, slime-substitutes, or spawns as authored per the current stage.
Nether ghast escalation per lap (v0.483.0)
v0.483.0 (PR #802) makes the Nether band's ghast density climb every time the train laps back around to the Nether. This is a separate axis from the carriage tier documented above: it scales spawn density, not gear, and keys off the world-gen cycle rather than travelledCarriageIndex.
Nether mobs aren't in any Overworld biome's spawn list, so the natural spawner never proposes them β NetherMobSpawner actively spawns the roster around players inside the band core (netherRamp β₯ 0.5), 4 attempts per player every 40 ticks. Before v0.483.0 ghasts were a flat 1-in-3 of those attempts in soul sand valley / basalt deltas and 1-in-6 elsewhere, with no ceiling of their own β they swarmed and crowded the ground rosters out of the shared 10-mob cap.
Two levers now escalate together:
| Nether pass | Soul sand valley / basalt deltas | Other Nether biomes | Ghast cap |
|---|---|---|---|
| 0 (first Nether) | 1-in-4 | 1-in-8 | 3 |
| 1 | 1-in-3 | 1-in-7 | 5 |
| 2 | 1-in-2 | 1-in-6 | 8 |
| 3+ | 1-in-2 | ramps to 1-in-2 by pass 6 | 15 (see limitations) |
- Odds (
ghastDenomFor) step one denser per completed lap from a per-biome base, floored at 1-in-2. - Ghast cap (
ghastCapFor) is a lookup table holding its last entry for every lap beyond it. It escalates alongside the odds deliberately: with a fixed cap the odds barely matter, since the cap fills within seconds and becomes the real ceiling β raising the odds alone would change respawn latency rather than felt difficulty.
The lap comes from NetherBand.netherPassIndex, the 0-based world-gen cycle repeat β which doubles as the Nether pass index because the Nether band is the first special band of every period, and already drives the Nether Return Again advancement. Being positional (derived from world-X rather than travel history) it needs no saved state, is reproducible across reloads, and can't be farmed by re-entering the same band.
A capped-out ghast roll falls through to the ground-mob path rather than wasting the attempt, so the band's overall mob density is unchanged at every lap β only the ghast share moves. The ramp math lives in two pure, param-in helpers covered by NetherGhastRampTest.
Unlike everything else on this page there is no config toggle β the curve is compile-time constants in NetherMobSpawner.
Configuration
The knobs live in the [difficulty] table of the server config (config/dungeontrain-server.toml, also editable from Esc β Mods β Dungeon Train β Config while in a world β title-screen edits no-op because the server config isn't loaded there):
| Key | Default | Range | Effect |
|---|---|---|---|
difficultyEnabled |
true |
bool | Master switch for mob gear/effects and the villager trade cap. |
carriagesPerTier |
20 |
[1, 1000] |
Carriages per tier step. Set to 1 for fast-paced / testing progression (tier == carriages travelled). |
progressionLevelDelay |
1 |
[0, 100] |
Delay the whole difficulty curve by this many levels (tier = max(0, rawTier β this)). 0 = original curve. |
difficultyScaleHostileGearPastCap |
true |
bool | Keep scaling hostile gear past the netherite / level-50 material cap via a per-tier attack/armor bonus (see Post-cap gear scaling). false = gear stops improving at netherite (pre-v0.416.0 behaviour). |
mobExperienceScalingEnabled |
true |
bool | Scale a monster's experience drop with its gear and beneficial effects (see Experience rewards). false = vanilla drops. Independent of difficultyEnabled. |
mobExperiencePerStatPoint |
0.03 |
[0, 5] |
Multiplier added per point of the mob's gear stat total. 0 disables the gear component. |
mobExperiencePerEnchantLevel |
0.05 |
[0, 5] |
Multiplier added per enchantment level across all equipment. 0 disables the enchant component. |
mobExperiencePerEffectPoint |
0.20 |
[0, 5] |
Multiplier added per level of each beneficial active effect. 0 disables the effect component. |
mobExperienceMaxMultiplier |
10.0 |
[1, 100] |
Hard ceiling on the experience multiplier. 1.0 caps every mob at its vanilla drop. |
firstLevelNoHostiles |
true |
bool | Stage 1. Suppress authored carriage hostiles entirely (they don't spawn) for the opening carriages. |
firstLevelNoHostilesCarriages |
5 |
[0, 1000] |
Length (carriages of leader progress) of the no-hostiles stage. 0 disables it. |
firstLevelEasyMobs |
true |
bool | Stage 2. Replace authored carriage hostiles with small slimes (small magma cubes for nether / raider mobs per first_band_magma_mobs; piglins / piglin brutes only in the Nether; zombified piglins never). |
firstLevelEasyMobsCarriages |
5 |
[0, 1000] |
Length (carriages of leader progress) of the slimes stage, following the no-hostiles stage. 0 disables it. |
firstLevelStarterLoot |
true |
bool | Across the whole opening window (both stages), swap rich loot / loot_irongold carriage chests for the starter loot prefab. |
difficultyAffectsBabyMobs (default false) also lives here β when true, baby mobs receive gear/effects too. The firstLevel* swaps and progressionLevelDelay are evaluated independently of difficultyEnabled.
PlayerMobs
PlayerMobGroupSpawner routes PlayerMobs through the same tier with applyEffects=false, so they get difficulty-scaled (and AIN-named / AIS-statted) equipment but no potion buffs. Their gear stats scale off the full tier (StatScaling.FULL) from tier 1 β unlike regular hostiles, whose stat bonus only kicks in past the level-50 material cap (PAST_MATERIAL_CAP; see Post-cap gear scaling).
Files
DifficultyProgression.javaβ leader-progress β tier math (maxTravelledCarriageIndex,rawTier/effectiveTier,tierForTravelled,currentTier) + onboarding gates (onboardingStage/onboardingStageFor,inOnboardingWindow,downgradeLootId/effectiveLootPrefabId)ProceduralTiers.javaβ procedural material/effect curves;tierFor,dominantWeaponStage,MATERIAL_CAP_LEVEL(netherite full-weight level, the post-cap-scaling anchor)ItemStatLevelScaling.javaβ per-tier AIS primary-stat bonus (bonusFor,pastCapTier,primaryStatBonus); the post-cap gear-scaling policyDifficultyTier.javaβ immutable per-tier armor/weapon/effect/enchant specDifficultyApplier.javaβ rolls + applies a tier to a mob (dungeontrain_difficulty_appliedsticky tag);StatScalingenum (NONE/FULL/PAST_MATERIAL_CAP) picks the stat-bonus frameItemPowerScore.javaβ the single definition of "how powerful is this item": headline combat stat + total enchant levels, shared by experience scaling and the echo encounter storyMobExperienceScaling.javaβ gear/effect score β experience multiplier (scaledXp,beneficialEffectPoints)MobExperienceEvents.javaβLivingExperienceDropEventhookMobDifficultyEvents.javaβ hostile-only spawn hookCarriageContentsPlacer.javaβ onboarding hostile gate (tryHandleOnboardingHostile: suppress / slime-substitute / spawn-as-authored by stage) + loot-prefab swap at the variant-placement seamfirst_band_magma_mobs.jsonβ entity-type tag: first-band hostiles that become magma cubes (nether roster +#minecraft:raiders)first_band_nether_only_mobs.jsonβpiglin/piglin_brute: magma cube only in the Nether, else spawn as authoredfirst_band_no_substitute_mobs.jsonβzombified_piglin: never substituted, in any dimensionVillagerTrainSpawnEvents.javaβ trade re-roll + weapon-stage level capPlayerRunState.javaβtravelledCarriageIndexper-life counterNetherMobSpawner.javaβ Nether band roster + the per-lap ghast ramp (ghastDenomFor/ghastCapFor)NetherBand.javaβnetherPassIndex, the positional lap counter the ghast ramp keys offDungeonTrainConfig.javaβdifficultyEnabled,carriagesPerTier,progressionLevelDelay,difficultyScaleHostileGearPastCap,firstLevelNoHostiles,firstLevelNoHostilesCarriages,firstLevelEasyMobs,firstLevelEasyMobsCarriages,firstLevelStarterLoot,mobExperienceScalingEnabled,mobExperiencePerStatPoint,mobExperiencePerEnchantLevel,mobExperiencePerEffectPoint,mobExperienceMaxMultipliertools/build-difficulty-graphs.py(wiki repo) β regenerates the progression graphs above (matplotlib; mirrors theProceduralTiers/DifficultyProgressionformulas)
Known limitations
- Hostile-only effects. Only
Enemymobs get the full gear+effects pass; passive carriage animals are untouched (villagers are handled solely by the trade cap). - Leader-anchored tier. Difficulty follows the single furthest-progressed boarded player, not a per-mob or per-region value β in multiplayer everyone fights the leader's tier.
- Experience is scored, gear is not. The experience multiplier reads the mob's live equipment, so it reflects a mob geared at any tier β but it can't distinguish gear the difficulty pass rolled from gear an author baked into a carriage variant, or from a player-thrown item the mob picked up. A mob handed a netherite sword drops more XP for it.
- Sticky rolls. Tier is locked at spawn; a mob/villager that spawned at tier 2 stays tier-2 even if the run later climbs. Only newly-spawned entities reflect a higher tier.
- Villager floor of 1. The cap never drops a villager below Novice, so profession villagers always trade something even at tier 0.
- Ghast escalation plateaus at pass 3.
NetherMobSpawner.NEARBY_CAP(10) bounds all band mobs near a player and is checked before any ghast logic, so ghast-cap entries above 10 never bind β passes 3 and 4 behave identically, and the table's 15s are the intended ceiling should that cap ever rise. At pass 2 (cap 8) ghasts may already occupy 8 of the 10 slots, leaving the ground rosters thin.