From 4f0bc039196949ad40d05eda17e51f7aadce2249 Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 2 Nov 2026 17:13:12 +0100 Subject: [PATCH] =?UTF-8?q?The=20End=20gets=20Pok=C3=A9mon:=20a=20dimensio?= =?UTF-8?q?n=20zone=20with=20Cobblemon's=20End=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adrian, 2026-11-02: the End looked barren. The End is now a zone of its own (Lv 50-70, world/zones.json -> dimensions; Zones.at returns it anywhere in the End). tools/zone_spawns.py copies every line Cobblemon spawns in End biomes into spawn_pool_world/the_end.json, on top of its island home: Beheeyem, Reuniclus, Metagross; Gothitelle and Sigilyph in End cities; Malamar and Unown with a Poke Snack. Cobblemon rolls the bucket before the Pokemon and has these all uncommon or rare, so they move to the common bucket with even line weights (Metagross a fifth). Self-test end_has_spawns: the main island is in the_end and offers only End spawns, some in the common bucket. Self-tests and the island check pass. Co-Authored-By: Claude Opus 5.5 --- .../spawn_pool_world/the_end.json | 695 ++++++++++++++++++ .../data/cobblesurvival/zones/island.json | 2 +- docs/balance.md | 1 + docs/checklists/phase-7.md | 2 + docs/design.md | 2 + docs/zone-species.md | 6 +- .../dev/cobblesurvival/selftest/ZoneTests.kt | 41 ++ .../kotlin/dev/cobblesurvival/zone/Zones.kt | 11 +- tools/zone_layout.py | 1 + tools/zone_spawns.py | 52 +- world/zones.json | 6 + 11 files changed, 810 insertions(+), 9 deletions(-) create mode 100644 datapacks/core/data/cobblesurvival/spawn_pool_world/the_end.json diff --git a/datapacks/core/data/cobblesurvival/spawn_pool_world/the_end.json b/datapacks/core/data/cobblesurvival/spawn_pool_world/the_end.json new file mode 100644 index 0000000..537ee02 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/spawn_pool_world/the_end.json @@ -0,0 +1,695 @@ +{ + "_comment": "Generated by tools/zone_spawns.py from Cobblemon's spawn data; do not edit by hand.", + "enabled": true, + "neededInstalledMods": [], + "neededUninstalledMods": [], + "spawns": [ + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "zone": "the_end" + }, + "drops": { + "amount": 6, + "entries": [ + { + "item": "minecraft:ender_pearl", + "quantityRange": "0-2" + }, + { + "item": "minecraft:chorus_fruit", + "quantityRange": "0-2" + }, + { + "item": "cobblemon:light_clay", + "percentage": 5.0 + }, + { + "item": "cobblemon:colbur_berry", + "percentage": 5.0 + } + ] + }, + "id": "the_end-beheeyem-1-1", + "level": "50-70", + "pokemon": "beheeyem", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0, + "weightMultiplier": { + "condition": { + "isPokeSnack": true + }, + "multiplier": 1.25 + } + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "structures": [ + "#cobblemon:spawns/is_end_structure" + ], + "zone": "the_end" + }, + "id": "the_end-gothitelle-2-2", + "level": "50-70", + "pokemon": "gothitelle", + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0, + "weightMultiplier": { + "condition": { + "isPokeSnack": true + }, + "multiplier": 2.0 + } + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-malamar-3-3", + "level": "50-70", + "pokemon": "malamar", + "presets": [ + "natural", + "wild" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "zone": "the_end" + }, + "id": "the_end-metagross-2-0", + "level": "50-70", + "pokemon": "metagross", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 2.0, + "weightMultiplier": { + "condition": { + "isPokeSnack": true + }, + "multiplier": 2.0 + } + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "zone": "the_end" + }, + "id": "the_end-reuniclus-2-5", + "level": "50-70", + "pokemon": "reuniclus", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0, + "weightMultiplier": { + "condition": { + "isPokeSnack": true + }, + "multiplier": 1.25 + } + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "structures": [ + "#cobblemon:spawns/is_end_structure" + ], + "zone": "the_end" + }, + "id": "the_end-sigilyph-7-4", + "level": "50-70", + "pokemon": "sigilyph", + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-!-2-32", + "level": "50-70", + "pokemon": "unown character=!", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 2.653 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-?-2-33", + "level": "50-70", + "pokemon": "unown character=?", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 2.653 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-a-1-6", + "level": "50-70", + "pokemon": "unown character=a", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-b-1-7", + "level": "50-70", + "pokemon": "unown character=b", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-c-1-8", + "level": "50-70", + "pokemon": "unown character=c", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-d-1-9", + "level": "50-70", + "pokemon": "unown character=d", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-e-1-10", + "level": "50-70", + "pokemon": "unown character=e", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-f-1-11", + "level": "50-70", + "pokemon": "unown character=f", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-g-1-12", + "level": "50-70", + "pokemon": "unown character=g", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-h-1-13", + "level": "50-70", + "pokemon": "unown character=h", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-i-1-14", + "level": "50-70", + "pokemon": "unown character=i", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-j-1-15", + "level": "50-70", + "pokemon": "unown character=j", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-k-1-16", + "level": "50-70", + "pokemon": "unown character=k", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-l-1-17", + "level": "50-70", + "pokemon": "unown character=l", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-m-1-18", + "level": "50-70", + "pokemon": "unown character=m", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-n-1-19", + "level": "50-70", + "pokemon": "unown character=n", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-o-1-20", + "level": "50-70", + "pokemon": "unown character=o", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-p-1-21", + "level": "50-70", + "pokemon": "unown character=p", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-q-1-22", + "level": "50-70", + "pokemon": "unown character=q", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-r-1-23", + "level": "50-70", + "pokemon": "unown character=r", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-s-1-24", + "level": "50-70", + "pokemon": "unown character=s", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-t-1-25", + "level": "50-70", + "pokemon": "unown character=t", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-u-1-26", + "level": "50-70", + "pokemon": "unown character=u", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-v-1-27", + "level": "50-70", + "pokemon": "unown character=v", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-w-1-28", + "level": "50-70", + "pokemon": "unown character=w", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-x-1-29", + "level": "50-70", + "pokemon": "unown character=x", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-y-1-30", + "level": "50-70", + "pokemon": "unown character=y", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + }, + { + "bucket": "common", + "condition": { + "biomes": [ + "#cobblemon:is_end" + ], + "isPokeSnack": true, + "zone": "the_end" + }, + "id": "the_end-unown-z-1-31", + "level": "50-70", + "pokemon": "unown character=z", + "presets": [ + "natural" + ], + "spawnablePositionType": "grounded", + "type": "pokemon", + "weight": 10.0 + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/zones/island.json b/datapacks/core/data/cobblesurvival/zones/island.json index c1d1cfe..69f3a20 100644 --- a/datapacks/core/data/cobblesurvival/zones/island.json +++ b/datapacks/core/data/cobblesurvival/zones/island.json @@ -1 +1 @@ -{"_comment":"Generated by tools/zone_layout.py from world/zones.json; do not edit by hand.","seed":"isle-26","radius":1700,"step":24,"size":142,"zones":[{"id":"button_isle","name":"Button Isle","levels":[2,10]},{"id":"frostpeak","name":"Frostpeak Range","levels":[40,55]},{"id":"sunscorch","name":"Sunscorch","levels":[30,45]},{"id":"mire","name":"Mangrove Mire","levels":[25,38]},{"id":"jungle","name":"Verdant Jungle","levels":[18,30]},{"id":"highlands","name":"Highland Meadows","levels":[25,35]},{"id":"westmarch","name":"Westmarch","levels":[15,25]},{"id":"southwold","name":"Southwold","levels":[8,18]},{"id":"shallows","name":"Shallows","levels":[5,20]},{"id":"deep_sea","name":"Deep Sea","levels":[30,50]}],"caves":[{"id":"lush_caves","name":"Lush Caves","levels":[12,28],"biome":"minecraft:lush_caves"},{"id":"dripstone","name":"Dripstone Depths","levels":[22,38],"biome":"minecraft:dripstone_caves"},{"id":"deep_dark","name":"Deep Dark","levels":[45,60],"biome":"minecraft:deep_dark"},{"id":"basalt","name":"Basalt Furnace","levels":[55,70],"biome":"minecraft:basalt_deltas"}],"level_pressure":{"grace_levels":5,"per_level":0.1,"minimum":0.1},"wanderers":{"grace_levels":10},"samples":{"button_isle":[-116,null,-92],"frostpeak":[628,null,-572],"sunscorch":[1372,null,-236],"mire":[652,null,1012],"jungle":[844,null,196],"highlands":[100,null,-1076],"westmarch":[-428,null,-1100],"southwold":[-116,null,1108],"shallows":[1468,null,4],"deep_sea":[-1316,null,4],"lush_caves":[-1028,0,292],"dripstone":[940,40,-212],"deep_dark":[484,-52,-1124],"basalt":[1252,0,508]},"biome_samples":{"frostpeak":[580,null,-524],"sunscorch":[-44,null,-452],"mire":[652,null,1012],"jungle":[844,null,196]},"grid_rle":[[9,912],[8,22],[9,115],[8,32],[9,106],[8,40],[9,99],[8,18],[5,13],[8,15],[9,96],[8,12],[6,1],[5,22],[1,2],[8,12],[9,92],[8,9],[6,5],[5,27],[1,1],[8,10],[9,90],[8,8],[6,6],[5,29],[1,1],[8,10],[9,85],[8,7],[6,10],[5,21],[1,3],[5,7],[1,1],[5,1],[8,10],[9,80],[8,6],[6,1],[8,1],[6,11],[5,20],[1,8],[5,2],[1,2],[5,2],[8,11],[9,76],[8,5],[6,2],[8,1],[6,13],[5,19],[1,11],[5,5],[8,1],[5,2],[8,8],[9,73],[8,5],[6,2],[8,1],[6,15],[5,18],[1,11],[5,12],[8,7],[9,60],[8,2],[9,7],[8,5],[6,1],[8,2],[6,17],[5,18],[1,9],[5,15],[8,8],[9,57],[8,3],[9,5],[8,5],[6,2],[2,4],[6,16],[5,18],[1,6],[5,20],[8,7],[9,55],[8,12],[6,5],[2,2],[6,17],[5,46],[8,7],[9,52],[8,12],[6,6],[2,1],[6,19],[5,47],[8,6],[9,49],[8,12],[6,8],[2,1],[6,19],[5,25],[1,5],[5,18],[8,6],[9,47],[8,11],[6,1],[8,1],[6,27],[5,26],[1,6],[5,19],[8,5],[9,45],[8,10],[2,1],[8,2],[2,1],[6,27],[5,24],[1,8],[5,20],[8,6],[9,42],[8,9],[2,7],[6,26],[5,24],[1,9],[5,20],[8,6],[9,40],[8,9],[2,8],[6,26],[5,24],[1,11],[5,19],[8,6],[9,39],[8,8],[2,9],[6,26],[5,23],[1,5],[5,1],[1,5],[5,21],[8,5],[9,40],[8,6],[2,10],[6,26],[5,23],[1,12],[5,21],[8,5],[9,39],[8,5],[2,10],[6,27],[5,23],[1,13],[5,21],[8,5],[9,37],[8,5],[2,12],[6,26],[5,26],[1,10],[5,22],[8,5],[9,35],[8,5],[2,11],[6,28],[5,27],[1,7],[5,25],[8,5],[9,34],[8,5],[2,10],[6,29],[5,28],[1,5],[5,27],[8,5],[9,32],[8,5],[2,1],[6,2],[2,4],[6,33],[5,29],[1,3],[5,29],[8,4],[9,31],[8,5],[6,41],[5,62],[8,4],[9,30],[8,4],[6,42],[5,20],[1,1],[5,42],[8,4],[9,28],[8,5],[6,42],[5,16],[1,3],[5,1],[1,5],[5,38],[8,4],[9,27],[8,5],[6,43],[5,12],[1,18],[5,11],[1,1],[5,22],[8,4],[9,26],[8,5],[6,43],[5,12],[1,17],[5,3],[1,4],[5,2],[1,1],[5,2],[1,1],[5,22],[8,4],[9,25],[8,5],[6,44],[5,12],[1,18],[5,1],[1,5],[5,1],[1,3],[5,1],[1,1],[5,23],[8,4],[9,24],[8,4],[6,45],[5,12],[1,11],[5,1],[1,6],[5,1],[1,3],[5,2],[1,4],[5,25],[8,4],[9,23],[8,5],[6,44],[5,5],[1,1],[5,7],[1,13],[5,1],[1,2],[5,7],[1,2],[5,1],[1,2],[5,26],[8,4],[9,22],[8,4],[6,45],[5,4],[1,4],[5,5],[1,9],[5,2],[1,2],[5,9],[1,8],[5,24],[8,4],[9,21],[8,4],[2,3],[6,44],[5,1],[1,8],[5,3],[1,7],[5,4],[1,1],[5,9],[1,9],[5,24],[8,5],[9,20],[8,4],[2,4],[6,43],[5,1],[1,8],[5,4],[1,5],[5,3],[1,3],[5,9],[1,9],[5,25],[8,4],[9,19],[8,4],[2,5],[6,43],[1,9],[5,4],[1,10],[5,10],[1,9],[5,25],[8,5],[9,1],[8,1],[9,16],[8,4],[6,48],[1,10],[5,4],[1,3],[5,2],[1,3],[5,10],[1,10],[5,26],[8,6],[9,16],[8,4],[6,48],[1,10],[5,3],[2,1],[5,1],[2,2],[5,15],[1,9],[5,27],[8,7],[9,14],[8,4],[6,49],[1,9],[2,7],[5,16],[1,9],[5,4],[1,3],[5,21],[8,6],[9,14],[8,4],[6,48],[1,9],[2,8],[5,16],[1,9],[5,3],[1,4],[5,21],[8,6],[9,14],[8,3],[6,49],[1,7],[2,10],[5,16],[1,9],[5,3],[1,5],[5,20],[2,1],[8,6],[9,12],[8,4],[6,50],[1,5],[2,11],[5,16],[1,9],[5,3],[1,5],[5,19],[2,2],[8,6],[9,12],[8,4],[6,50],[5,2],[1,2],[2,13],[5,15],[1,10],[5,3],[1,4],[5,18],[2,3],[8,6],[9,12],[8,3],[6,51],[5,3],[2,12],[1,4],[5,14],[1,10],[5,23],[2,4],[8,7],[9,10],[8,4],[6,51],[5,3],[2,11],[1,5],[5,15],[1,9],[5,23],[2,5],[8,6],[9,10],[8,3],[6,52],[5,3],[2,11],[1,1],[5,2],[1,3],[5,15],[1,10],[5,20],[2,6],[8,6],[9,10],[8,3],[6,52],[5,3],[2,3],[8,3],[9,3],[8,2],[1,1],[5,3],[1,1],[5,16],[1,12],[5,17],[2,7],[8,6],[9,12],[8,1],[4,2],[6,50],[5,3],[2,1],[8,6],[9,2],[8,5],[5,1],[1,1],[5,16],[1,3],[5,5],[1,5],[5,15],[2,8],[5,1],[8,1],[9,1],[8,3],[9,7],[8,3],[9,2],[8,1],[4,5],[6,47],[5,1],[8,1],[9,2],[8,6],[9,3],[8,6],[5,6],[4,1],[5,9],[1,2],[5,7],[1,2],[5,1],[1,1],[5,15],[2,9],[8,1],[9,2],[8,3],[9,6],[8,4],[9,1],[4,7],[6,46],[9,5],[8,5],[9,2],[8,8],[9,1],[5,3],[4,3],[5,8],[1,1],[5,8],[1,2],[5,2],[1,1],[5,13],[2,10],[8,1],[9,4],[8,1],[9,6],[8,5],[4,7],[6,45],[9,7],[8,4],[9,1],[0,1],[8,8],[9,2],[5,1],[4,5],[5,3],[4,3],[5,1],[1,1],[5,7],[1,3],[5,14],[2,12],[8,1],[9,11],[8,5],[4,8],[6,43],[9,7],[8,1],[0,9],[8,4],[9,3],[8,1],[4,6],[5,1],[4,4],[5,1],[1,1],[5,6],[1,2],[5,14],[2,1],[5,1],[2,11],[5,2],[8,1],[9,10],[8,5],[4,8],[6,42],[9,6],[8,1],[0,13],[9,4],[8,3],[4,9],[5,2],[1,2],[5,2],[1,2],[5,16],[2,14],[5,2],[8,1],[9,10],[8,5],[4,8],[6,41],[8,1],[9,5],[8,1],[0,15],[9,2],[8,4],[4,9],[5,2],[1,5],[5,2],[4,2],[5,10],[2,17],[5,2],[8,1],[9,9],[8,6],[4,8],[6,12],[3,2],[6,27],[9,5],[8,1],[0,17],[8,6],[4,8],[5,3],[1,3],[5,2],[4,5],[5,5],[2,19],[5,3],[8,1],[9,9],[8,5],[6,1],[4,7],[6,12],[3,3],[6,27],[9,4],[8,1],[0,19],[8,5],[4,9],[5,2],[1,3],[5,1],[4,6],[5,6],[2,18],[5,3],[8,1],[9,9],[8,5],[4,8],[6,9],[3,6],[6,26],[9,5],[0,20],[8,6],[4,11],[1,1],[5,2],[4,6],[5,7],[2,16],[5,5],[9,9],[8,5],[4,7],[6,9],[3,6],[6,27],[9,4],[8,1],[0,21],[8,5],[4,21],[5,6],[2,15],[5,6],[9,9],[8,5],[4,6],[6,10],[3,5],[6,27],[8,1],[9,4],[0,22],[8,5],[4,22],[5,6],[2,13],[5,7],[8,1],[9,8],[8,1],[9,3],[8,1],[4,1],[8,1],[9,42],[8,4],[9,5],[0,23],[9,1],[8,2],[9,2],[8,1],[9,1],[8,7],[9,22],[8,15],[5,1],[8,5],[9,4],[8,1],[9,3],[8,1],[9,53],[0,23],[9,1],[8,3],[9,1],[8,15],[9,19],[8,18],[9,4],[8,1],[9,3],[8,2],[9,51],[0,24],[9,1],[8,26],[9,15],[8,15],[9,6],[8,4],[9,51],[8,1],[0,23],[9,2],[8,30],[9,13],[8,12],[9,6],[8,4],[9,51],[8,1],[0,23],[8,1],[9,1],[8,29],[9,14],[8,12],[9,6],[8,4],[9,46],[8,1],[9,5],[0,23],[8,30],[9,16],[8,11],[9,6],[8,3],[7,6],[8,41],[7,1],[9,5],[0,23],[8,5],[4,3],[8,22],[9,16],[8,2],[4,4],[8,5],[9,6],[8,4],[7,47],[8,1],[9,4],[0,22],[8,5],[4,48],[8,5],[9,6],[8,4],[7,47],[8,1],[9,4],[8,1],[0,21],[9,1],[8,4],[4,34],[7,1],[4,9],[7,2],[4,2],[8,5],[9,6],[8,3],[7,49],[9,5],[8,1],[0,19],[9,2],[8,4],[4,42],[7,6],[8,5],[9,6],[8,4],[7,49],[9,5],[0,19],[9,5],[4,43],[7,6],[8,5],[9,7],[8,3],[7,49],[9,6],[0,17],[9,6],[4,42],[7,6],[8,6],[9,7],[8,3],[7,50],[9,6],[0,15],[9,6],[7,1],[4,14],[7,2],[4,27],[7,5],[8,6],[9,7],[8,3],[7,50],[9,7],[8,1],[0,11],[9,7],[7,2],[4,42],[7,6],[8,5],[9,8],[8,3],[7,51],[9,8],[8,1],[0,7],[9,8],[7,5],[4,41],[7,5],[8,5],[9,8],[8,4],[7,51],[9,22],[8,1],[7,6],[4,41],[7,4],[8,5],[9,8],[8,4],[7,52],[8,1],[9,19],[7,8],[4,42],[7,2],[8,6],[9,9],[8,3],[7,54],[9,17],[7,9],[4,44],[8,6],[9,9],[8,3],[7,56],[9,13],[7,11],[4,44],[8,5],[9,10],[8,4],[7,58],[9,4],[8,1],[9,1],[8,1],[7,15],[4,42],[8,6],[9,11],[8,3],[7,81],[4,38],[7,3],[8,3],[9,14],[8,3],[7,81],[4,36],[7,5],[8,1],[9,16],[8,4],[7,81],[4,1],[3,1],[4,32],[7,5],[8,1],[9,18],[8,3],[7,79],[3,3],[4,6],[3,1],[4,24],[7,7],[8,1],[9,18],[8,3],[7,79],[3,3],[4,6],[3,3],[4,21],[7,8],[8,1],[9,18],[8,4],[7,76],[3,5],[4,7],[3,3],[4,20],[7,7],[8,1],[9,20],[8,3],[7,72],[3,10],[4,6],[3,4],[4,19],[7,7],[8,1],[9,20],[8,4],[7,67],[3,15],[4,5],[3,5],[4,18],[7,7],[9,22],[8,3],[7,66],[3,17],[4,4],[3,7],[4,16],[7,6],[8,1],[9,22],[8,4],[7,64],[3,32],[4,13],[7,6],[9,24],[8,3],[7,63],[3,33],[4,13],[7,5],[8,1],[9,24],[8,4],[7,62],[3,35],[4,11],[7,5],[9,26],[8,3],[7,62],[3,38],[4,7],[7,5],[8,1],[9,26],[8,4],[7,58],[3,44],[4,4],[7,4],[8,1],[9,28],[8,3],[7,58],[3,44],[4,3],[7,5],[8,1],[9,28],[8,4],[7,56],[3,45],[4,2],[7,5],[8,1],[9,30],[8,3],[7,56],[3,45],[7,7],[8,1],[9,31],[8,3],[7,55],[3,7],[7,3],[3,34],[7,7],[8,1],[9,32],[8,4],[7,54],[3,6],[7,4],[3,34],[7,7],[9,34],[8,3],[7,54],[3,6],[7,3],[3,34],[7,7],[8,1],[9,35],[8,3],[7,53],[3,6],[7,2],[3,7],[7,3],[3,25],[7,6],[8,1],[9,36],[8,4],[7,52],[3,15],[7,3],[3,27],[7,3],[8,1],[9,38],[8,4],[7,52],[3,16],[7,1],[3,27],[7,2],[8,1],[9,40],[8,3],[7,52],[3,43],[7,3],[8,1],[9,40],[8,4],[7,52],[3,43],[7,1],[8,1],[9,42],[8,4],[7,51],[3,43],[8,1],[9,44],[8,4],[7,51],[3,41],[8,7],[9,40],[8,4],[7,50],[3,30],[7,1],[3,9],[8,7],[9,42],[8,4],[7,48],[3,26],[7,7],[3,7],[8,7],[9,44],[8,4],[7,48],[3,24],[7,9],[3,5],[8,7],[9,46],[8,4],[7,47],[3,24],[7,7],[4,1],[3,5],[8,8],[9,47],[8,4],[7,46],[3,21],[7,9],[4,3],[3,2],[8,9],[9,49],[8,4],[7,45],[3,17],[7,13],[4,3],[3,1],[8,9],[9,51],[8,5],[7,43],[3,15],[7,14],[4,3],[3,1],[8,9],[9,53],[8,5],[7,38],[3,2],[7,1],[3,15],[7,16],[4,2],[8,8],[9,56],[8,6],[7,36],[3,15],[7,19],[4,1],[8,8],[9,59],[8,5],[7,35],[3,12],[7,21],[8,9],[9,61],[8,6],[7,33],[3,10],[7,22],[8,9],[9,63],[8,7],[7,32],[3,9],[7,20],[8,10],[9,66],[8,6],[7,34],[3,3],[7,21],[8,10],[9,69],[8,7],[7,55],[8,10],[9,72],[8,7],[7,51],[8,11],[9,75],[8,7],[7,47],[8,7],[9,2],[8,2],[9,79],[8,8],[7,41],[8,8],[9,4],[8,1],[9,81],[8,9],[7,26],[2,4],[7,6],[8,9],[9,90],[8,10],[7,21],[2,7],[7,3],[8,9],[9,95],[8,10],[7,14],[8,4],[2,2],[8,1],[2,2],[8,12],[9,99],[8,13],[7,8],[8,19],[9,105],[8,34],[9,111],[8,28],[9,118],[8,19],[9,488]]} +{"_comment":"Generated by tools/zone_layout.py from world/zones.json; do not edit by hand.","seed":"isle-26","radius":1700,"step":24,"size":142,"zones":[{"id":"button_isle","name":"Button Isle","levels":[2,10]},{"id":"frostpeak","name":"Frostpeak Range","levels":[40,55]},{"id":"sunscorch","name":"Sunscorch","levels":[30,45]},{"id":"mire","name":"Mangrove Mire","levels":[25,38]},{"id":"jungle","name":"Verdant Jungle","levels":[18,30]},{"id":"highlands","name":"Highland Meadows","levels":[25,35]},{"id":"westmarch","name":"Westmarch","levels":[15,25]},{"id":"southwold","name":"Southwold","levels":[8,18]},{"id":"shallows","name":"Shallows","levels":[5,20]},{"id":"deep_sea","name":"Deep Sea","levels":[30,50]}],"caves":[{"id":"lush_caves","name":"Lush Caves","levels":[12,28],"biome":"minecraft:lush_caves"},{"id":"dripstone","name":"Dripstone Depths","levels":[22,38],"biome":"minecraft:dripstone_caves"},{"id":"deep_dark","name":"Deep Dark","levels":[45,60],"biome":"minecraft:deep_dark"},{"id":"basalt","name":"Basalt Furnace","levels":[55,70],"biome":"minecraft:basalt_deltas"}],"dimensions":[{"id":"the_end","name":"The End","levels":[50,70],"dimension":"minecraft:the_end"}],"level_pressure":{"grace_levels":5,"per_level":0.1,"minimum":0.1},"wanderers":{"grace_levels":10},"samples":{"button_isle":[-116,null,-92],"frostpeak":[628,null,-572],"sunscorch":[1372,null,-236],"mire":[652,null,1012],"jungle":[844,null,196],"highlands":[100,null,-1076],"westmarch":[-428,null,-1100],"southwold":[-116,null,1108],"shallows":[1468,null,4],"deep_sea":[-1316,null,4],"lush_caves":[-1028,0,292],"dripstone":[940,40,-212],"deep_dark":[484,-52,-1124],"basalt":[1252,0,508]},"biome_samples":{"frostpeak":[580,null,-524],"sunscorch":[-44,null,-452],"mire":[652,null,1012],"jungle":[844,null,196]},"grid_rle":[[9,912],[8,22],[9,115],[8,32],[9,106],[8,40],[9,99],[8,18],[5,13],[8,15],[9,96],[8,12],[6,1],[5,22],[1,2],[8,12],[9,92],[8,9],[6,5],[5,27],[1,1],[8,10],[9,90],[8,8],[6,6],[5,29],[1,1],[8,10],[9,85],[8,7],[6,10],[5,21],[1,3],[5,7],[1,1],[5,1],[8,10],[9,80],[8,6],[6,1],[8,1],[6,11],[5,20],[1,8],[5,2],[1,2],[5,2],[8,11],[9,76],[8,5],[6,2],[8,1],[6,13],[5,19],[1,11],[5,5],[8,1],[5,2],[8,8],[9,73],[8,5],[6,2],[8,1],[6,15],[5,18],[1,11],[5,12],[8,7],[9,60],[8,2],[9,7],[8,5],[6,1],[8,2],[6,17],[5,18],[1,9],[5,15],[8,8],[9,57],[8,3],[9,5],[8,5],[6,2],[2,4],[6,16],[5,18],[1,6],[5,20],[8,7],[9,55],[8,12],[6,5],[2,2],[6,17],[5,46],[8,7],[9,52],[8,12],[6,6],[2,1],[6,19],[5,47],[8,6],[9,49],[8,12],[6,8],[2,1],[6,19],[5,25],[1,5],[5,18],[8,6],[9,47],[8,11],[6,1],[8,1],[6,27],[5,26],[1,6],[5,19],[8,5],[9,45],[8,10],[2,1],[8,2],[2,1],[6,27],[5,24],[1,8],[5,20],[8,6],[9,42],[8,9],[2,7],[6,26],[5,24],[1,9],[5,20],[8,6],[9,40],[8,9],[2,8],[6,26],[5,24],[1,11],[5,19],[8,6],[9,39],[8,8],[2,9],[6,26],[5,23],[1,5],[5,1],[1,5],[5,21],[8,5],[9,40],[8,6],[2,10],[6,26],[5,23],[1,12],[5,21],[8,5],[9,39],[8,5],[2,10],[6,27],[5,23],[1,13],[5,21],[8,5],[9,37],[8,5],[2,12],[6,26],[5,26],[1,10],[5,22],[8,5],[9,35],[8,5],[2,11],[6,28],[5,27],[1,7],[5,25],[8,5],[9,34],[8,5],[2,10],[6,29],[5,28],[1,5],[5,27],[8,5],[9,32],[8,5],[2,1],[6,2],[2,4],[6,33],[5,29],[1,3],[5,29],[8,4],[9,31],[8,5],[6,41],[5,62],[8,4],[9,30],[8,4],[6,42],[5,20],[1,1],[5,42],[8,4],[9,28],[8,5],[6,42],[5,16],[1,3],[5,1],[1,5],[5,38],[8,4],[9,27],[8,5],[6,43],[5,12],[1,18],[5,11],[1,1],[5,22],[8,4],[9,26],[8,5],[6,43],[5,12],[1,17],[5,3],[1,4],[5,2],[1,1],[5,2],[1,1],[5,22],[8,4],[9,25],[8,5],[6,44],[5,12],[1,18],[5,1],[1,5],[5,1],[1,3],[5,1],[1,1],[5,23],[8,4],[9,24],[8,4],[6,45],[5,12],[1,11],[5,1],[1,6],[5,1],[1,3],[5,2],[1,4],[5,25],[8,4],[9,23],[8,5],[6,44],[5,5],[1,1],[5,7],[1,13],[5,1],[1,2],[5,7],[1,2],[5,1],[1,2],[5,26],[8,4],[9,22],[8,4],[6,45],[5,4],[1,4],[5,5],[1,9],[5,2],[1,2],[5,9],[1,8],[5,24],[8,4],[9,21],[8,4],[2,3],[6,44],[5,1],[1,8],[5,3],[1,7],[5,4],[1,1],[5,9],[1,9],[5,24],[8,5],[9,20],[8,4],[2,4],[6,43],[5,1],[1,8],[5,4],[1,5],[5,3],[1,3],[5,9],[1,9],[5,25],[8,4],[9,19],[8,4],[2,5],[6,43],[1,9],[5,4],[1,10],[5,10],[1,9],[5,25],[8,5],[9,1],[8,1],[9,16],[8,4],[6,48],[1,10],[5,4],[1,3],[5,2],[1,3],[5,10],[1,10],[5,26],[8,6],[9,16],[8,4],[6,48],[1,10],[5,3],[2,1],[5,1],[2,2],[5,15],[1,9],[5,27],[8,7],[9,14],[8,4],[6,49],[1,9],[2,7],[5,16],[1,9],[5,4],[1,3],[5,21],[8,6],[9,14],[8,4],[6,48],[1,9],[2,8],[5,16],[1,9],[5,3],[1,4],[5,21],[8,6],[9,14],[8,3],[6,49],[1,7],[2,10],[5,16],[1,9],[5,3],[1,5],[5,20],[2,1],[8,6],[9,12],[8,4],[6,50],[1,5],[2,11],[5,16],[1,9],[5,3],[1,5],[5,19],[2,2],[8,6],[9,12],[8,4],[6,50],[5,2],[1,2],[2,13],[5,15],[1,10],[5,3],[1,4],[5,18],[2,3],[8,6],[9,12],[8,3],[6,51],[5,3],[2,12],[1,4],[5,14],[1,10],[5,23],[2,4],[8,7],[9,10],[8,4],[6,51],[5,3],[2,11],[1,5],[5,15],[1,9],[5,23],[2,5],[8,6],[9,10],[8,3],[6,52],[5,3],[2,11],[1,1],[5,2],[1,3],[5,15],[1,10],[5,20],[2,6],[8,6],[9,10],[8,3],[6,52],[5,3],[2,3],[8,3],[9,3],[8,2],[1,1],[5,3],[1,1],[5,16],[1,12],[5,17],[2,7],[8,6],[9,12],[8,1],[4,2],[6,50],[5,3],[2,1],[8,6],[9,2],[8,5],[5,1],[1,1],[5,16],[1,3],[5,5],[1,5],[5,15],[2,8],[5,1],[8,1],[9,1],[8,3],[9,7],[8,3],[9,2],[8,1],[4,5],[6,47],[5,1],[8,1],[9,2],[8,6],[9,3],[8,6],[5,6],[4,1],[5,9],[1,2],[5,7],[1,2],[5,1],[1,1],[5,15],[2,9],[8,1],[9,2],[8,3],[9,6],[8,4],[9,1],[4,7],[6,46],[9,5],[8,5],[9,2],[8,8],[9,1],[5,3],[4,3],[5,8],[1,1],[5,8],[1,2],[5,2],[1,1],[5,13],[2,10],[8,1],[9,4],[8,1],[9,6],[8,5],[4,7],[6,45],[9,7],[8,4],[9,1],[0,1],[8,8],[9,2],[5,1],[4,5],[5,3],[4,3],[5,1],[1,1],[5,7],[1,3],[5,14],[2,12],[8,1],[9,11],[8,5],[4,8],[6,43],[9,7],[8,1],[0,9],[8,4],[9,3],[8,1],[4,6],[5,1],[4,4],[5,1],[1,1],[5,6],[1,2],[5,14],[2,1],[5,1],[2,11],[5,2],[8,1],[9,10],[8,5],[4,8],[6,42],[9,6],[8,1],[0,13],[9,4],[8,3],[4,9],[5,2],[1,2],[5,2],[1,2],[5,16],[2,14],[5,2],[8,1],[9,10],[8,5],[4,8],[6,41],[8,1],[9,5],[8,1],[0,15],[9,2],[8,4],[4,9],[5,2],[1,5],[5,2],[4,2],[5,10],[2,17],[5,2],[8,1],[9,9],[8,6],[4,8],[6,12],[3,2],[6,27],[9,5],[8,1],[0,17],[8,6],[4,8],[5,3],[1,3],[5,2],[4,5],[5,5],[2,19],[5,3],[8,1],[9,9],[8,5],[6,1],[4,7],[6,12],[3,3],[6,27],[9,4],[8,1],[0,19],[8,5],[4,9],[5,2],[1,3],[5,1],[4,6],[5,6],[2,18],[5,3],[8,1],[9,9],[8,5],[4,8],[6,9],[3,6],[6,26],[9,5],[0,20],[8,6],[4,11],[1,1],[5,2],[4,6],[5,7],[2,16],[5,5],[9,9],[8,5],[4,7],[6,9],[3,6],[6,27],[9,4],[8,1],[0,21],[8,5],[4,21],[5,6],[2,15],[5,6],[9,9],[8,5],[4,6],[6,10],[3,5],[6,27],[8,1],[9,4],[0,22],[8,5],[4,22],[5,6],[2,13],[5,7],[8,1],[9,8],[8,1],[9,3],[8,1],[4,1],[8,1],[9,42],[8,4],[9,5],[0,23],[9,1],[8,2],[9,2],[8,1],[9,1],[8,7],[9,22],[8,15],[5,1],[8,5],[9,4],[8,1],[9,3],[8,1],[9,53],[0,23],[9,1],[8,3],[9,1],[8,15],[9,19],[8,18],[9,4],[8,1],[9,3],[8,2],[9,51],[0,24],[9,1],[8,26],[9,15],[8,15],[9,6],[8,4],[9,51],[8,1],[0,23],[9,2],[8,30],[9,13],[8,12],[9,6],[8,4],[9,51],[8,1],[0,23],[8,1],[9,1],[8,29],[9,14],[8,12],[9,6],[8,4],[9,46],[8,1],[9,5],[0,23],[8,30],[9,16],[8,11],[9,6],[8,3],[7,6],[8,41],[7,1],[9,5],[0,23],[8,5],[4,3],[8,22],[9,16],[8,2],[4,4],[8,5],[9,6],[8,4],[7,47],[8,1],[9,4],[0,22],[8,5],[4,48],[8,5],[9,6],[8,4],[7,47],[8,1],[9,4],[8,1],[0,21],[9,1],[8,4],[4,34],[7,1],[4,9],[7,2],[4,2],[8,5],[9,6],[8,3],[7,49],[9,5],[8,1],[0,19],[9,2],[8,4],[4,42],[7,6],[8,5],[9,6],[8,4],[7,49],[9,5],[0,19],[9,5],[4,43],[7,6],[8,5],[9,7],[8,3],[7,49],[9,6],[0,17],[9,6],[4,42],[7,6],[8,6],[9,7],[8,3],[7,50],[9,6],[0,15],[9,6],[7,1],[4,14],[7,2],[4,27],[7,5],[8,6],[9,7],[8,3],[7,50],[9,7],[8,1],[0,11],[9,7],[7,2],[4,42],[7,6],[8,5],[9,8],[8,3],[7,51],[9,8],[8,1],[0,7],[9,8],[7,5],[4,41],[7,5],[8,5],[9,8],[8,4],[7,51],[9,22],[8,1],[7,6],[4,41],[7,4],[8,5],[9,8],[8,4],[7,52],[8,1],[9,19],[7,8],[4,42],[7,2],[8,6],[9,9],[8,3],[7,54],[9,17],[7,9],[4,44],[8,6],[9,9],[8,3],[7,56],[9,13],[7,11],[4,44],[8,5],[9,10],[8,4],[7,58],[9,4],[8,1],[9,1],[8,1],[7,15],[4,42],[8,6],[9,11],[8,3],[7,81],[4,38],[7,3],[8,3],[9,14],[8,3],[7,81],[4,36],[7,5],[8,1],[9,16],[8,4],[7,81],[4,1],[3,1],[4,32],[7,5],[8,1],[9,18],[8,3],[7,79],[3,3],[4,6],[3,1],[4,24],[7,7],[8,1],[9,18],[8,3],[7,79],[3,3],[4,6],[3,3],[4,21],[7,8],[8,1],[9,18],[8,4],[7,76],[3,5],[4,7],[3,3],[4,20],[7,7],[8,1],[9,20],[8,3],[7,72],[3,10],[4,6],[3,4],[4,19],[7,7],[8,1],[9,20],[8,4],[7,67],[3,15],[4,5],[3,5],[4,18],[7,7],[9,22],[8,3],[7,66],[3,17],[4,4],[3,7],[4,16],[7,6],[8,1],[9,22],[8,4],[7,64],[3,32],[4,13],[7,6],[9,24],[8,3],[7,63],[3,33],[4,13],[7,5],[8,1],[9,24],[8,4],[7,62],[3,35],[4,11],[7,5],[9,26],[8,3],[7,62],[3,38],[4,7],[7,5],[8,1],[9,26],[8,4],[7,58],[3,44],[4,4],[7,4],[8,1],[9,28],[8,3],[7,58],[3,44],[4,3],[7,5],[8,1],[9,28],[8,4],[7,56],[3,45],[4,2],[7,5],[8,1],[9,30],[8,3],[7,56],[3,45],[7,7],[8,1],[9,31],[8,3],[7,55],[3,7],[7,3],[3,34],[7,7],[8,1],[9,32],[8,4],[7,54],[3,6],[7,4],[3,34],[7,7],[9,34],[8,3],[7,54],[3,6],[7,3],[3,34],[7,7],[8,1],[9,35],[8,3],[7,53],[3,6],[7,2],[3,7],[7,3],[3,25],[7,6],[8,1],[9,36],[8,4],[7,52],[3,15],[7,3],[3,27],[7,3],[8,1],[9,38],[8,4],[7,52],[3,16],[7,1],[3,27],[7,2],[8,1],[9,40],[8,3],[7,52],[3,43],[7,3],[8,1],[9,40],[8,4],[7,52],[3,43],[7,1],[8,1],[9,42],[8,4],[7,51],[3,43],[8,1],[9,44],[8,4],[7,51],[3,41],[8,7],[9,40],[8,4],[7,50],[3,30],[7,1],[3,9],[8,7],[9,42],[8,4],[7,48],[3,26],[7,7],[3,7],[8,7],[9,44],[8,4],[7,48],[3,24],[7,9],[3,5],[8,7],[9,46],[8,4],[7,47],[3,24],[7,7],[4,1],[3,5],[8,8],[9,47],[8,4],[7,46],[3,21],[7,9],[4,3],[3,2],[8,9],[9,49],[8,4],[7,45],[3,17],[7,13],[4,3],[3,1],[8,9],[9,51],[8,5],[7,43],[3,15],[7,14],[4,3],[3,1],[8,9],[9,53],[8,5],[7,38],[3,2],[7,1],[3,15],[7,16],[4,2],[8,8],[9,56],[8,6],[7,36],[3,15],[7,19],[4,1],[8,8],[9,59],[8,5],[7,35],[3,12],[7,21],[8,9],[9,61],[8,6],[7,33],[3,10],[7,22],[8,9],[9,63],[8,7],[7,32],[3,9],[7,20],[8,10],[9,66],[8,6],[7,34],[3,3],[7,21],[8,10],[9,69],[8,7],[7,55],[8,10],[9,72],[8,7],[7,51],[8,11],[9,75],[8,7],[7,47],[8,7],[9,2],[8,2],[9,79],[8,8],[7,41],[8,8],[9,4],[8,1],[9,81],[8,9],[7,26],[2,4],[7,6],[8,9],[9,90],[8,10],[7,21],[2,7],[7,3],[8,9],[9,95],[8,10],[7,14],[8,4],[2,2],[8,1],[2,2],[8,12],[9,99],[8,13],[7,8],[8,19],[9,105],[8,34],[9,111],[8,28],[9,118],[8,19],[9,488]]} diff --git a/docs/balance.md b/docs/balance.md index 3fe724f..35be2b5 100644 --- a/docs/balance.md +++ b/docs/balance.md @@ -39,6 +39,7 @@ Every tunable lives in a config file or in this table. **Placeholder** means gue | Boss attacks (guardians) | by guardian level: up to Lv 20 a circle under the player; up to 35 + a line from the boss; up to 50 + a cone; up to 65 + a ring around the boss at half HP; above + a wide ring at a quarter HP. Telegraph 2.5 s at the lowest tier down to 1.5 s; damage level/6 health points (Morpeko 3, Moltres 12); circle every 7.4 s down to 5.6 s, line and cone less often; Fire types burn, Ice types freeze, Poison types poison | Placeholder | `tools/zone_bosses.py` → `datapacks/core/data/cobblesurvival/bosses/*.json` | Adrian, 2026-11-02: every guardian, harder in harder zones | | Boss arena | 24 blocks around where the boss stood; Basalt Furnace 32; the End 40; leaving it or dying loses the fight | Placeholder | same files, `arena` | | | End boss | Eternatus Lv 80, 0.2× size (its model is 20 m tall), back 5 hours after a win; 12,000 XP the first time, 500 after | Placeholder | `bosses/end.json` (`tools/zone_bosses.py` → `END`) | Stands in for Giratina, which Cobblemon 1.8.4 has no model for | +| End spawns | the End is a zone, Lv 50–70; Cobblemon's End lines spawn there (Beheeyem, Reuniclus, Metagross; Gothitelle and Sigilyph in End cities; Malamar and Unown with a Poké Snack), all in the common bucket, each line's heaviest entry weight 10, Metagross 2 | Placeholder | `world/zones.json` → `dimensions` (`levels`, `bucket`, `line_weight`, `rare_factor`), then `tools/zone_layout.py` and `tools/zone_spawns.py` | Adrian, 2026-11-02: the End was barren; Lv 50–70 his pick | | Level pressure | full experience up to 5 levels above the zone's band, then 10 % less per level, at least 10 % (Exp. Candy and commands exempt) | Decided (Adrian, 2026-10-30) | `world/zones.json` → `level_pressure` | E.g. on Button Isle (up to Lv 10): Lv 15 full, Lv 20 half, Lv 24+ a tenth | | Strong flyer | ridable through the air with top speed ≥ 60 or top stamina ≥ 85 (Cobblemon ride stats) | Decided (Adrian, 2026-10-30) | `tools/zone_spawns.py` (`STRONG_FLYER_*`) | 29 species | | High zone for strong flyers | band starts at Lv 30 or more (Sunscorch, Frostpeak, Deep Sea, Deep Dark, Basalt Furnace) | Decided (Adrian, 2026-10-30) | `tools/zone_spawns.py` (`HIGH_ZONE_LEVEL`) | | diff --git a/docs/checklists/phase-7.md b/docs/checklists/phase-7.md index 2bfd6a8..3af71e9 100644 --- a/docs/checklists/phase-7.md +++ b/docs/checklists/phase-7.md @@ -10,6 +10,7 @@ - **Boss fights:** a battle with a guardian (or the End boss) becomes a fight with area attacks: red or coloured particles on the ground show where an attack lands, about 1.5–2.5 seconds later it hits whoever stands inside. Circles, lines, cones and rings; harder zones get more of them. Fire, Ice and Poison bosses burn, freeze or poison. Automated: `boss_attack_shapes`, `boss_attacks_land_and_stop_with_the_battle`. - **Arena:** leaving it (24 blocks; Basalt 32; End 40) or dying ends the battle as lost; the boss heals and stays. Automated: `boss_arena_leaving_loses`. - **The End:** the Ender Dragon is gone; Eternatus appears on the main island, with the dragon's boss bar; beating it opens the exit portal and gateway, leaves the egg the first time and gives experience. Automated: `end_boss_replaces_the_dragon`. +- **End spawns:** the End is a zone (Lv 50–70) with Cobblemon's End lines: Beheeyem, Reuniclus, Metagross; Gothitelle and Sigilyph in End cities; Malamar and Unown with a Poké Snack. Automated: `end_has_spawns`. ## In game (Prism instance) @@ -21,6 +22,7 @@ On the island save (or a new one), in creative or survival: - [ ] Leave the End and come back without beating Eternatus: still only one (fixed 2026-11-02). - [ ] Go to the End: no dragon; Eternatus stands south of the portal, the boss bar shows its HP. Beat it: the exit portal opens, the egg appears. - [ ] Find Regigigas in a Deep Dark cave and Moltres in the Basalt Furnace. +- [ ] Walk around the End's islands for a minute: Beheeyem, Reuniclus and now and then Metagross appear, Lv 50–70 (`/cobblesurvival zone` shows "The End"). ## Ask Adrian diff --git a/docs/design.md b/docs/design.md index 4abe755..cd29879 100644 --- a/docs/design.md +++ b/docs/design.md @@ -290,6 +290,8 @@ Self-tests (`BossTests`): every guardian has a fight and a Cobblemon model; each **Eternatus** replaces the Ender Dragon: the dragon no longer spawns; Eternatus (uncatchable, Lv 80, scaled down to fit) appears on the main End island while a player is there, and beating it does what killing the dragon did (exit portal, gateways, the egg on the first win). Only one at a time: it waits until the entities around its spot have loaded before deciding it's missing, and a second copy never joins while the recorded one is alive (Adrian met a second Eternatus on his second visit, 2026-11-02); extra copies left in older worlds are removed. +**The End has Pokémon** (Adrian, 2026-11-02: it looked barren). The End is a zone of its own, "The End", Lv 50–70 (`world/zones.json` → `dimensions`; `Zones.at` returns it anywhere in the End). Every line Cobblemon spawns in End biomes spawns there too, on top of its home on the island: Beheeyem, Reuniclus and Metagross anywhere; Gothitelle and Sigilyph in End cities; Malamar and Unown with a Poké Snack. Gothita had no island home (it only spawns in the End), so it's new. Cobblemon has all of these uncommon or rare, and it rolls the bucket before the Pokémon, so in the End they move to the common bucket or the End would stay nearly empty; each line weighs the same, Metagross a fifth. Being a zone, the End also has level pressure (full experience up to Lv 75, like the Basalt Furnace) and the wild-Pokémon cleanup above Lv 80; Eternatus is exempt as an uncatchable boss. Self-test `end_has_spawns`. + ## Open decisions for Adrian (proceeding with the default, flagged) | Decision | Default used for now | diff --git a/docs/zone-species.md b/docs/zone-species.md index fe8086c..8fb2249 100644 --- a/docs/zone-species.md +++ b/docs/zone-species.md @@ -1,6 +1,6 @@ # Pokémon per zone -Generated by `tools/zone_spawns.py` from Cobblemon's spawn data and `world/zones.json`; do not edit by hand. Each evolution line spawns in one zone; the stages listed are the ones whose levels reach the zone's band. Pin a line to another zone in `world/zones.json` → `species`. +Generated by `tools/zone_spawns.py` from Cobblemon's spawn data and `world/zones.json`; do not edit by hand. Each evolution line spawns in one zone of the island; the stages listed are the ones whose levels reach the zone's band. Pin a line to another zone in `world/zones.json` → `species`. The End's lines spawn there as well as in their island zone. ## Button Isle (Lv 2–10, 25 lines) @@ -58,6 +58,10 @@ Bronzor (Bronzong), Carbink, Deino (Zweilous), Ditto, Duskull (Dusclops, Dusknoi Absol, Charmander (Charizard), Diglett (Dugtrio), Drilbur (Excadrill), Durant, Gligar (Gliscor), Growlithe (Arcanine), Heatmor, Houndour (Houndoom), Magby (Magmortar), Mawile, Purrloin (Liepard), Sizzlipede (Centiskorch), Slugma (Magcargo), Torkoal, Turtonator +## The End (Lv 50–70, 7 lines) + +Beldum (Metagross), Elgyem (Beheeyem), Gothita (Gothitelle), Inkay (Malamar), Sigilyph, Solosis (Reuniclus), Unown + ## Alpha guardians One per zone at its sample point, uncatchable, five levels above the band; it returns 5 hours after it is beaten. Its drops help with the next zone. diff --git a/mod/src/main/kotlin/dev/cobblesurvival/selftest/ZoneTests.kt b/mod/src/main/kotlin/dev/cobblesurvival/selftest/ZoneTests.kt index 122116a..3b06f4b 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/selftest/ZoneTests.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/selftest/ZoneTests.kt @@ -19,6 +19,8 @@ import net.minecraft.core.BlockPos import net.minecraft.gametest.framework.GameTestAssertException import net.minecraft.server.level.ServerPlayer import net.minecraft.util.Mth +import net.minecraft.world.level.ChunkPos +import net.minecraft.world.level.Level import net.minecraft.world.item.ItemStack import net.minecraft.world.level.levelgen.Heightmap import kotlin.math.roundToInt @@ -36,6 +38,7 @@ object ZoneTests { SelfTestCase("spawn_pools_are_zoned", 20, ::spawnPoolsAreZoned), SelfTestCase("spawn_survey", 2400, ::spawnSurvey, islandOnly = true), SelfTestCase("level_pressure_cuts_xp", 100, ::levelPressure), + SelfTestCase("end_has_spawns", 200, ::endSpawns), ) private fun zoneOf(detail: SpawnDetail): Zone? = Zones.byId[detail.id.substringBefore('-')] @@ -108,6 +111,44 @@ object ZoneTests { }.thenSucceed() } + /** + * The End is a zone of its own (Adrian, 2026-11-02: it was barren). On the main island Cobblemon offers End + * spawns only, and some in the common bucket, which it rolls most of the time. + */ + private fun endSpawns(s: Scenario) { + val end = s.level.server.getLevel(Level.END) ?: throw GameTestAssertException("no End") + val chunks = (0..2).flatMap { x -> (0..2).map { z -> ChunkPos(x, z) } } + chunks.forEach { end.setChunkForced(it.x, it.z, true) } + s.afterTest { chunks.forEach { end.setChunkForced(it.x, it.z, false) } } + s.helper.startSequence() + .thenWaitUntil { s.check(chunks.all { end.areEntitiesLoaded(it.toLong()) }) { "the End's main island isn't loaded yet" } } + .thenExecute { + val stand = ground(end, 20, 20) + s.check(Zones.at(end, stand)?.id == "the_end") { "the End's main island is in zone ${Zones.at(end, stand)?.id}, not the_end" } + val (offered, common) = offeredAt(s.player(), end, stand) + val strangers = offered.filter { zoneOf(it)?.id != "the_end" } + s.check(strangers.isEmpty()) { "the End offers other zones' spawns: ${strangers.take(5).map { it.id }}" } + s.check(common.isNotEmpty()) { "nothing in the common bucket can spawn on the End's main island (${offered.map { it.id }})" } + } + .thenSucceed() + } + + /** What Cobblemon could spawn around [pos] in [level] right now (the way `/checkspawn` does): all, and the common bucket's. */ + private fun offeredAt(player: ServerPlayer, level: net.minecraft.server.level.ServerLevel, pos: BlockPos): Pair, Set> { + val spawner = player.spawner + val diameter = Cobblemon.config.spawningZoneDiameter + val height = Cobblemon.config.spawningZoneHeight + val input = SpawningZoneInput( + SpawnCause(spawner, player), level, + pos.x - diameter / 2, pos.y - height / 2, pos.z - diameter / 2, diameter, height, diameter, + ) + val positions = Cobblemon.areaSpawnablePositionResolver.resolve( + spawner, SpawnablePositionCalculator.prioritizedAreaCalculators, Cobblemon.spawningZoneGenerator.generate(spawner, input), + ) + fun inBucket(bucket: String) = spawner.selector.getProbabilities(spawner, bucket, positions).filterValues { it > 0f }.keys + return CobblemonSpawnPools.WORLD_SPAWN_POOL.buckets.flatMap(::inBucket).toSet() to inBucket("common") + } + /** * The ground at [x] [z], below any tree: standing on a mangrove's roots or logs, the survey once found no natural * ground around it, so nothing could spawn (2026-11-02). diff --git a/mod/src/main/kotlin/dev/cobblesurvival/zone/Zones.kt b/mod/src/main/kotlin/dev/cobblesurvival/zone/Zones.kt index c4e8503..f1ccd45 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/zone/Zones.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/zone/Zones.kt @@ -37,6 +37,8 @@ object Zones : SimpleJsonResourceReloadListener(Gson(), "zones") { var byId: Map = emptyMap() private set private var caves: Map = emptyMap() + /** Zones that are a whole dimension (the End; Adrian, 2026-11-02: it was barren). */ + private var dimensions: Map = emptyMap() var samples: Map = emptyMap() private set var levelPressure = LevelPressureRules() @@ -65,7 +67,9 @@ object Zones : SimpleJsonResourceReloadListener(Gson(), "zones") { byIndex = json.getAsJsonArray("zones").map { zone(it.asJsonObject) } val caveZones = json.getAsJsonArray("caves").map { it.asJsonObject } caves = caveZones.associate { ResourceLocation.parse(it.get("biome").asString) to zone(it) } - byId = (byIndex + caves.values).associateBy { it.id } + dimensions = json.getAsJsonArray("dimensions")?.map { it.asJsonObject } + ?.associate { ResourceLocation.parse(it.get("dimension").asString) to zone(it) }.orEmpty() + byId = (byIndex + caves.values + dimensions.values).associateBy { it.id } levelPressure = json.getAsJsonObject("level_pressure")?.let { rules -> LevelPressureRules( graceLevels = rules.get("grace_levels")?.asInt ?: 5, @@ -87,7 +91,7 @@ object Zones : SimpleJsonResourceReloadListener(Gson(), "zones") { } check(cells.size == size * size) { "zone grid has ${cells.size} cells, expected ${size * size}" } grid = cells.toIntArray() - CobbleSurvival.LOGGER.info("Loaded {} zones ({} surface, {} cave)", byId.size, byIndex.size, caves.size) + CobbleSurvival.LOGGER.info("Loaded {} zones ({} surface, {} cave, {} dimension)", byId.size, byIndex.size, caves.size, dimensions.size) } private fun samplesOf(json: JsonObject, key: String): Map = @@ -101,8 +105,9 @@ object Zones : SimpleJsonResourceReloadListener(Gson(), "zones") { return Zone(json.get("id").asString, json.get("name").asString, levels[0].asInt, levels[1].asInt) } - /** The zone at [pos], or null outside the island's zones or outside the overworld. */ + /** The zone at [pos]: a whole other dimension's zone, or in the overworld the island's; null outside them. */ fun at(level: Level, pos: BlockPos, biome: Holder = level.getBiome(pos)): Zone? { + dimensions[level.dimension().location()]?.let { return it } if (level.dimension() != Level.OVERWORLD || size == 0) return null biome.unwrapKey().orElse(null)?.let { caves[it.location()] }?.let { return it } // Grid cells are centred on -radius + i * step. diff --git a/tools/zone_layout.py b/tools/zone_layout.py index de593d3..fd93cd8 100755 --- a/tools/zone_layout.py +++ b/tools/zone_layout.py @@ -312,6 +312,7 @@ def write_datapack(zone, plan, preview): "seed": preview["seed"], "radius": preview["radius"], "step": preview["step"], "size": preview["size"], "zones": [{k: z[k] for k in keep} for z in plan["zones"]], "caves": [{**{k: c[k] for k in keep}, "biome": c["biome"]} for c in plan["caves"]], + "dimensions": [{**{k: d[k] for k in keep}, "dimension": d["dimension"]} for d in plan.get("dimensions", [])], "level_pressure": plan.get("level_pressure", {}), "wanderers": plan.get("wanderers", {}), "samples": samples(zone, plan, preview), diff --git a/tools/zone_spawns.py b/tools/zone_spawns.py index 4a5ef6a..5034b80 100755 --- a/tools/zone_spawns.py +++ b/tools/zone_spawns.py @@ -32,6 +32,11 @@ datapacks/core/data/cobblesurvival/spawn_pool_world/.json: 6. Each zone with a "guardian" gets datapacks/core/data/cobblesurvival/guardians/.json: the strongest Pokémon spawning in the zone (or "species"), uncatchable, half again as big (or "scale"), five levels above the band, with the zone's boss drops and world/zones.json → guardian_respawn_hours. + 7. world/zones.json → "dimensions" are zones that are a whole other dimension (the End; Adrian, 2026-11-02: it was + barren). Every line Cobblemon spawns in their "biome_tags" spawns there too, on top of its island home, with + its stages fitted to the band as in 2. The entries go into "bucket" (Cobblemon rolls the bucket first, and its + End entries are all uncommon or rare), the heaviest of each line weighing "line_weight", Cobblemon's rare ones + "rare_factor" of that. Also writes docs/zone-species.md, the list per zone for review. @@ -306,6 +311,45 @@ def main(): names = sorted({species[m]["name"] for _, m, _, _ in fitting}) report[zid].append((species.get(root, {}).get("name", root), names)) + # 4. Dimension zones (Adrian, 2026-11-02: the End was barren): every line Cobblemon spawns in the dimension's + # biome tags spawns there too, at the dimension's band, on top of its home on the island. Read straight from + # Cobblemon's spawn files, since the field guide only resolves the island's biomes. Cobblemon rolls the bucket + # before the Pokémon, and its End entries are all uncommon or rare: they move to the dimension's bucket, the + # heaviest entry of each line weighing line_weight (Cobblemon's rare ones rare_factor of that). + dimension_roots = set() + for dim in plan.get("dimensions", []): + did = dim["id"] + zones[did] = dim + lo, hi = dim["levels"] + tags = set(dim["biome_tags"]) + found = collections.defaultdict(list) + for entry in entries.values(): + if "pokemon" not in entry or not tags & set(entry.get("condition", {}).get("biomes", [])): + continue + member = f"cobblemon:{entry['pokemon'].split()[0].lower()}" + if EXCLUDED_LABELS & set(species.get(member, {}).get("labels", [])): + continue + a, _, b = str(entry["level"]).partition("-") + found[line_root(member, species)].append((entry, member, (int(a), int(b or a)))) + for root, items in sorted(found.items()): + fitting = [(e, m, max(lv[0], lo), hi) for e, m, lv in items if lv[0] <= hi and lv[1] >= lo] + if not fitting: + _, m, lv = min(items, key=lambda u: min(abs(u[2][0] - hi), abs(u[2][1] - lo))) + fitting = [(e, um, lo, hi) for e, um, ulv in items if um == m and ulv == lv] + heaviest = max(e.get("weight", 1) for e, _, _, _ in fitting) + for entry, member, a, b in fitting: + spawn = copy.deepcopy(entry) + spawn["id"] = f"{did}-{entry.get('id') or member.split(':')[1]}-{len(pools[did])}" + spawn["level"] = f"{a}-{b}" + rare = entry.get("bucket") in ("rare", "ultra-rare") + spawn["weight"] = round(dim["line_weight"] * entry.get("weight", 1) / heaviest * (dim["rare_factor"] if rare else 1), 3) + spawn["bucket"] = dim["bucket"] + spawn.setdefault("condition", {})["zone"] = did + pools[did].append(spawn) + names = sorted({species[m]["name"] for _, m, _, _ in fitting if m in species}) + report[did].append((species.get(root, {}).get("name", root), names)) + dimension_roots.add(root) + low_flyers = [(zid, s["pokemon"]) for zid, spawns in pools.items() for s in spawns if zones[zid]["levels"][0] < HIGH_ZONE_LEVEL and f"cobblemon:{s['pokemon'].split()[0].lower()}" in strong_flyers] if low_flyers: @@ -346,9 +390,9 @@ def main(): lines_out = ["# Pokémon per zone", "", "Generated by `tools/zone_spawns.py` from Cobblemon's spawn data and `world/zones.json`; " - "do not edit by hand. Each evolution line spawns in one zone; the stages listed are the ones " - "whose levels reach the zone's band. Pin a line to another zone in `world/zones.json` → " - "`species`.", ""] + "do not edit by hand. Each evolution line spawns in one zone of the island; the stages listed are " + "the ones whose levels reach the zone's band. Pin a line to another zone in `world/zones.json` → " + "`species`. The End's lines spawn there as well as in their island zone.", ""] for zid, zone in zones.items(): rows = sorted(report.get(zid, [])) lines_out += [f"## {zone['name']} (Lv {zone['levels'][0]}–{zone['levels'][1]}, {len(rows)} lines)", ""] @@ -364,7 +408,7 @@ def main(): f"| {zone['levels'][1] + GUARDIAN_LEVELS_ABOVE_BAND} | {drop_text} |") REPORT.write_text("\n".join(lines_out) + "\n") - unplaced = [r for r in lines if r not in home] + unplaced = [r for r in lines if r not in home and r not in dimension_roots] grounded = sorted(species.get(r, {}).get("name", r) for r in unplaced if r in flying_lines) if grounded: print(f"strong flyers kept out of the wild (no high zone fits their habitat): {', '.join(grounded)}") diff --git a/world/zones.json b/world/zones.json index 1a9b862..8bab390 100644 --- a/world/zones.json +++ b/world/zones.json @@ -78,5 +78,11 @@ {"id": "dripstone", "types": ["rock", "ground", "steel", "fairy", "psychic", "dragon"], "lines": 20, "name": "Dripstone Depths", "biome": "minecraft:dripstone_caves", "levels": [22, 38], "hazard": "none"}, {"id": "deep_dark", "types": ["ghost", "dark", "steel", "poison", "psychic", "rock"], "guardian": {"species": "regigigas", "scale": 1.0, "drops": [["cobblemon:icy_rock", 3], ["cobblemon:exp_candy_l", 2]]}, "lines": 21, "name": "Deep Dark", "biome": "minecraft:deep_dark", "levels": [45, 60], "hazard": "darkness (vanilla sculk; no Warden)"}, {"id": "basalt", "types": ["fire", "rock", "ground", "steel", "dark"], "guardian": {"species": "moltres", "drops": [["cobblemon:exp_candy_xl", 3], ["minecraft:netherite_scrap", 2]]}, "lines": 15, "name": "Basalt Furnace", "biome": "minecraft:basalt_deltas", "levels": [55, 70], "hazard": "heat (Cold Sweat)"} + ], + "dimensions": [ + {"id": "the_end", "name": "The End", "dimension": "minecraft:the_end", "levels": [50, 70], "hazard": "none", + "biome_tags": ["#cobblemon:is_end", "#minecraft:is_end"], + "bucket": "common", "line_weight": 10, "rare_factor": 0.2, + "_comment": "Adrian, 2026-11-02: the End was barren. Every line Cobblemon spawns in End biomes spawns there too, at this band, on top of its island home (tools/zone_spawns.py). Cobblemon has them all uncommon or rare, and it rolls the bucket before the Pokemon, so they move to `bucket`; each line's heaviest entry weighs `line_weight`, Cobblemon's rare ones `rare_factor` of that."} ] }