# Design notes The roadmap (design pillars, phases, open decisions) is the source of truth for *what* to build. This file records *how* it was built and every decision taken on the way. ## Decisions taken in Phase 0 | Decision | Why | | --- | --- | | Loader NeoForge 21.1.265 (latest 21.1 build on 2026-10-28) | The roadmap's default. Not yet confirmed by Adrian | | Cobblemon 1.8.4 instead of 1.8.3 | 1.8.4 came out 2026-10-27 (bug-fix release); every 1.8-aware addon accepts it | | Companion mod builds with Cobblemon's official MDK (Architectury Loom), not the plain NeoForge MDK | The roadmap says to take Cobblemon's coordinates from its official docs or addon template. That template is Loom-based and the coordinates (`com.cobblemon:neoforge` on `artefacts.cobblemon.com`) come from it | | Kotlin 2.4.0 instead of the MDK's 2.2.20 | Kotlin for Forge 5.12.0 ships stdlib 2.4.0, which the older compiler can't read | | `datapacks/core` ships *inside* the companion mod as an always-on built-in data pack (top priority) | Works in every world and on every server without copying files around. Its `pack.mcmeta` filter can later blank Cobblemon's base spawns (Phase 3). The data stays plain JSON in `datapacks/core` | | Every mod `.pw.toml` carries both a Modrinth URL and CurseForge IDs where both exist (`tools/crosslink.py`) | packwiz only records one source. Without the second one, an export to the other platform copies the jar into `overrides/`, which bundles a third-party jar | | Mods hosted on one platform only are left out of the other platform's export | Same rule: no bundling. See the platform split in `docs/addon-status.md` | | Dev instance in Prism is synced with packwiz-installer from `build/stage/full` | Gets every mod from both platforms, and re-syncing removes mods that left the pack. Prism is a Flatpak that can't read the repo folder, so the sync runs on the host | | Companion mod id `cobblesurvival`, package `dev.cobblesurvival` | Placeholder names, easy to rename while the mod is empty | | No publishing for now (Adrian, 2026-10-28) | The target is the Prism instance and a private server, which both get every mod. The platform split below only matters again for a public release (Phase 9). Mods may be chosen regardless of which platform hosts them | ## Decisions taken in Phase 1 | Decision | Why | | --- | --- | | World type `cobblesurvival:island` (our data pack) points WorldGen Editor at our own preset `cobblesurvival_island` | Our preset is never confused with WorldGen Editor's bundled examples. Servers set `level-type=cobblesurvival:island`; the type is also listed in the singleplayer world-type button | | Biome set = Adrian's list (2026-10-28): plains, forest, cherry grove, mangrove swamp, jungle, desert, badlands, "mountain edge", jagged peaks; caves: lush caves, dripstone caves, deep dark, basalt deltas | Set in `world/biomes.json`. Ocean, deep ocean, lukewarm/warm ocean, river and beach stay, because an island needs a sea and a coast | | The biome list is enforced by remapping vanilla's overworld biome table (from Minecraft's data generator) onto the list, written into the world type by `tools/biome_layout.py` | Keeps vanilla's terrain–biome fit (peaks on peaks, swamps in lowlands) while only listed biomes can appear. Pure data. WorldGen Editor's own climate/exclusion options were rejected: they replace cave biomes with surface fallbacks such as snowy plains, and leak unlisted biomes | | "Mountain edge" → `windswept_hills` (with snowy slopes and stony shore folded in) | Mountain Edge was removed in 1.18; windswept hills is its successor. **Flagged** in case another biome was meant | | Basalt deltas is an overworld cave biome under the hottest, driest land (where desert and badlands are), at cave depth like lush and dripstone caves | Adrian's request. It keeps its own features (basalt columns, magma, lava deltas) and particles; its nether ores are removed with the rest | | The Deep Dark no longer fills the deepslate layer (Adrian, 2026-10-30: "too much"; part of it should become basalt deltas). Vanilla puts it at depth 1.1 wherever erosion is low, at any temperature; `world/biomes.json` → `split` cuts it by the temperature of the land above: up to −0.1 Deep Dark, from 0.4 basalt deltas, in between ordinary caves of the land above | Biome preview at y −52, share of land: Deep Dark 30 % → 14 %, basalt deltas 0 → 7 %, ordinary caves 9 %; at y −30 Deep Dark 18 % → 7 %. The deep basalt is part of the Basalt Furnace (Lv 55–70), so the deepest caves reach Lv 70 under hot land. The gaps between the pieces must be wide: Minecraft picks the nearest biome entry, and the land biome above is only 0.1 away in depth, so a narrow gap goes to the neighbouring piece | | No Nether: the world type `cobblesurvival:island` defines only the overworld and the End | Adrian's request. Portals can still be lit but lead nowhere. The smoke test checks that `minecraft:the_nether` is an unknown dimension | | Island shaped like a Poké Ball (Adrian, 2026-10-28): a round island cut into top and bottom halves by a channel across the middle, with a ring channel around a round centre island where the spawn is. Written by `tools/pokeball_preset.py` (ball r 1,500, band 160, button r 280, ring 120 — placeholders) | WorldGen Editor makes the round island; two flat-ended ocean entries cut the band and 36 overlapping ocean circles cut the ring. Entries have no climate or exclusions; biomes come from the remapped table | | The seed is chosen by `tools/seed_search.py`, which scores many seeds with the op-only dev command `/cobblesurvival biomepreview` (samples the generator without generating chunks) | With vanilla climate noise, which biomes land on a 1,500-block island depends on the seed. The search wants every listed biome at a useful share, few large patches, a gentle start area and all four cave biomes | | Natural ore removed with data only, no extra mod: a NeoForge `remove_features` biome modifier for vanilla ores, zero-placement overrides for Cobblemon's evolution-stone ores, `ore_veins_enabled: false` in WorldGen Editor's noise settings | Pillar 7 (data before code). Ore Generation Configuration was not needed. Cobblemon adds its ores from code as direct holders, which the biome modifier can't match, hence the overrides (found by the ore scan) | | Structures and fossils place no ore either: `tools/clean_structures.py` appends a rule processor that turns ore into stone (raw ore blocks into tuff/terracotta, placeholders) to every processor list that places an ore-containing template or outputs ore | Found by the ore scan of the first template: Cobblemon habitats/ruins have ore and raw ore blocks built into their templates, vanilla fossils have coal/diamond ore. Overriding the processor lists keeps the structures and only swaps those blocks | | Nether fortresses and Nether ruined portals are kept out of the overworld: their biome tags list the Nether biomes except basalt deltas | Basalt deltas is now an overworld cave biome, and these structures go wherever a Nether biome is. The first template had fortress pieces underground | | Standard QOL mods (Adrian, 2026-10-28): JEI, Jade, Inventory Profiles Next, Mouse Tweaks, AppleSkin, Controlling, Xaero's Minimap + World Map; performance: Sodium, FerriteCore, ModernFix, ImmediatelyFast, Entity Culling | JEI as the NEI successor, Jade as the block/mod probe, Inventory Profiles Next as the Inventory Tweaks successor. **Flagged:** a minimap and world map make exploring the island easier; drop them if exploration should stay harder. Xaero's radar can show mobs; restrict it in its config if that feels like cheating | | The world template is generated by `tools/make_world.py` from `world/world.json` and kept out of git | The world is several hundred MB. Seed + preset + mod versions reproduce it. Prism and the test server get it from `build/world-template` | | Two test speeds (Adrian, 2026-10-28: 10 minutes was too long): `tools/quick_check.py` (one boot, smoke checks, `/locate` for removed structures, a sampled world around spawn and sample structures; about 75 s) for everyday changes, and `tools/make_world.py` (whole island, about 6 min) only when the island changes | The template build pregenerates a circle that just covers the island instead of the whole square, and sets the border in the same server session | | Chunky is used only while building the template; it is not in the pack | It is a build tool. Pinned by URL and SHA-1 in `world/world.json` | | **Seed `isle-26` approved** (Adrian, 2026-10-28) | Poké Ball, radius 1,500 | | No vanilla mobs, only Pokémon (Adrian, 2026-10-28): the companion mod (`world/OnlyPokemon.kt`) never lets a vanilla mob join a world unless it is loaded from a save (since Phase 3 any mob that isn't Cobblemon's, e.g. Cold Sweat's chameleons); exceptions go in the entity type tag `cobblesurvival:allowed_vanilla_mobs` (only the Ender Dragon for now). On top, every biome's vanilla spawn list is emptied (`no_vanilla_spawns.json`), infested stone doesn't generate (`no_infested_stone.json`) and the template has `doMobSpawning false` | One rule covers mobs placed by structures during world generation (villagers, guardians, drowned, shulkers, ...), natural spawns, spawners and commands. Cobblemon spawns through its own spawner and isn't a vanilla mob, so Pokémon are unaffected. The data-pack and gamerule layers save the work of creating mobs only to block them. **To undo:** remove the listener, the two modifier files and the gamerule entry. A side effect: player-built golems and the Wither can't be summoned either | | All structures stay, only their ore and vanilla mobs go (Adrian, 2026-10-28) | Villages, ocean ruins, monuments, mineshafts, strongholds, trial chambers, ancient cities, Gimmighoul towers etc. generate as designed (igloos, witch huts and mansions can't, because their biomes are remapped away). Exception: ruined portals stay off, because Nether portals shouldn't generate at all | | Mob sources in structures go when a chunk has just been generated (`world/OnlyPokemon.kt`): blocks in the tag `cobblesurvival:removed_on_generation` (spawner, trial spawner) become air, sculk shriekers can't summon the Warden, infested blocks become normal stone/bricks | Stronghold, mineshaft and dungeon spawners and stronghold infested bricks are placed by game code, so data can't reach them. Chunks loaded from disk are never touched. The Deep Dark's own sculk patches can summon the Warden too, not only ancient cities. Trial chamber vaults stay but can't be opened, since trial keys come from trial spawners. `tools/world_report.py` fails a world that still has a spawner, trial spawner, infested block, summoning shrieker or vanilla mob | | Nether portals don't form: the companion mod cancels portal creation while the server has no Nether dimension; ruined portals don't generate (their biome tags are empty) | Adrian: portals formed but led nowhere. The check is on the dimension, so if the Nether ever comes back, portals work again. The smoke test lights an obsidian frame and expects no portal | | Default keybinds without conflicts, shipped with Default Options (`pack/config/defaultoptions/keybindings.txt`) | NeoForge's Controls screen flags any two keybinds on the same key, even a GUI-only one next to an in-game one. Changes: Cobblemon summary M→N and hide party O→Y; Xaero waypoints U→J, minimap settings unbound; Jade details Shift→V; JEI recipe R→G, bookmark A→K, search Ctrl+F→Ctrl+H, pause cycling Shift→Right Ctrl, search history unbound; KubeJS Kubedex unbound; FTB Quests: recenter Home, chapters `.`/`,`, search Ctrl+I, panning up/down and player prefs unbound, **all quest-editor shortcuts unbound** (pack authors rebind them in Controls → FTB Quests). JEI's mouse-click actions still share mouse buttons with attack/use by design. Check with `tools/check_keybinds.py` after adding a mod | | Square vanilla world border around the land's bounding box, 96 blocks of sea margin (placeholder) | The vanilla border can only be a square. On the island's shorter axis the gap to the coast is larger than 96 blocks. The border is measured from the large land masses only; icebergs, sea stacks and wrecks out at sea don't count | ## Decisions taken in Phase 2 | Decision | Why | | --- | --- | | Permadeath: CobbleHardcoreMon removes Pokémon that fainted in a battle when the battle ends; holding a Revival Charm (`cobblesurvival:revival_charm`, a KubeJS item) saves it once instead | Roadmap pillar 2. The charm replaces the Totem of Undying (`defaultconfigs/cobblehardcoremon/server.toml`). Health Link off; Soul Link is opt-in per player and not used | | The companion mod closes CobbleHardcoreMon's gaps (`permadeath/Permadeath.kt`): a Pokémon that faints outside a battle is settled at the end of the same tick; one that fainted in a battle and was revived before it ended is fainted again just before CobbleHardcoreMon looks; every second each online player's party and PC are swept for fainted Pokémon. A charm is any item in `#cobblesurvival:revival_charms` | Found while writing the permadeath tests: CobbleHardcoreMon only looks once a second outside battles (time to heal a Pokémon or put it in the PC), waits 60 s for a Pokémon whose charm already saved it once, never looks at the PC, and skips a Pokémon that isn't fainted when the battle ends. With the companion mod's part switched off, `charm_saves_once`, `fainted_in_pc` and `revived_during_battle` fail (2026-10-29). Out-of-battle poison can knock a Pokémon out too (5 % HP now and then, no floor). Both mods use the same chat messages | | A Revive used during a battle doesn't save a Pokémon that fainted in it (Adrian, 2026-10-29): it is gone when the battle ends | Roadmap: a fainted Pokémon is deleted. Revives, Max Revives and Revival Blessing only help within that battle; Revives are mainly Revival Charm material. Adrian may later disable revive items in battles completely | | No remote PC access: Cobblemon's `/pc` needs cheats (operator level 2) and is left as an admin tool; a PC opened at a block closes once the player is more than 10 blocks from it. No remote-PC mod is installed | Roadmap Phase 2. Checked by the self-test `no_remote_pc`. Players on a server never have level 2; single-player worlds only with cheats on | | Free movement in battle stays: nothing in the pack stops breaking, placing or walking during a battle | Phase 7 boss fights need it. The self-test `free_movement_in_battle` checks the server side; Cobblemon's own key for leaving the battle screen is a manual check. A wild battle still ends if the player gets too far from the wild Pokémon (Cobblemon's flee distance) | | In-game self-tests (`/cobblesurvival selftest`, `tools/selftest.py`, also run by `tools/quick_check.py`) on Minecraft's GameTest framework | NeoForge only runs GameTests in a development environment, so the companion mod starts and ticks them itself on the real pack server. Fake players get a connection that accepts every mod channel. The command only exists with `-Dcobblesurvival.selftest=true`. Tests run in barrier-walled boxes 15×12×15 on a platform at y 200 above spawn; a wild Pokémon must have ticked once before a battle with it starts, or its side never picks a move | | Cobblemon's faint timer is effectively infinite (`defaultFaintTimer` 1,000,000,000 s) | Otherwise a fainted Pokémon wakes up on its own after 5 minutes, and staying in a battle that long would let it escape permadeath | | No healing while sleeping: Cobblemon `healPercent` 0 | Cobblemon only uses `healPercent`/`healTimer` while the player sleeps | | Healing Machines are expensive (roadmap): a dearer recipe (gold, iron, a Max Revive and a diamond) and a four times slower recharge; values in `docs/balance.md` | With loot chests handing out ingots (Adrian, 2026-10-29), Cobblemon's copper-and-iron recipe would be cheap early on. The recipe is replaced by KubeJS under the id `cobblesurvival:healing_machine` | | Fight or Flight Reborn and TP-Pastures installed with default settings | Tuning comes with the zones (Phase 3) and pastures (Phase 4) | | Chest loot keeps its ore and ingots; every loot container refills with a new roll 1 hour after it was first opened (Adrian, 2026-10-29). Tiering loot by structure/biome comes later (lower priority) | Done with Lootr (`config/lootr-common.toml`: `refresh_all`, `refresh_value` 72,000 ticks of game time). Loot is shared by all players (Adrian, 2026-10-30): every player joins the scoreboard team `islanders` (`world/SharedLoot.kt`) and Lootr's `team_loot` is on, so a container has one inventory for everybody. Cobblemon's gilded chests (Gimmighoul's hiding spots) aren't Lootr containers: their loot is rolled once, shared, and doesn't come back. This changes the roadmap's Phase 2 item "remove loot tables that hand out ore or ingots": chests are a second, renewable ore source next to the veins (pillar 3) | | Lootr converts a vanilla loot chest once the 5×5 chunks around it are loaded, i.e. when a player is near, not during pregeneration | So the template keeps vanilla loot chests until then. `tools/quick_check.py` force-loads the area around a sampled village and checks that its chests became Lootr chests | | Matthiesen Core's usage statistics are off by default | A pack shouldn't send data for its players without asking; players can switch it on in `config/matthiesen_core/metrics.properties` | ## Phase 3 zone plan (approved by Adrian, 2026-10-29) Source of truth: `world/zones.json`; `tools/zone_layout.py` lays it onto the island and draws `build/zone-map.png`. The model is decided; levels, drops and guardians below are still placeholders (`docs/balance.md`). A zone decides **which Pokémon spawn** and **at what levels**: Cobblemon's own spawn lists are switched off and each zone gets its own list, drawn from Cobblemon's spawn data for the zone's biomes. **Model.** Zones are regions that follow the island's biome clusters. A biome with a hazard (jagged peaks, desert/badlands, mangrove swamp) or its own character (jungle) is its own zone wherever it lies, with small specks dropped. The forest and plains that fill the rest (65 % of the land) are split into regions by position. Rivers belong to the zone they run through; only the coast and the open sea are sea zones. Caves are zones by cave biome, everywhere under the island. Cold Sweat's heat and cold follow the biome anyway, so hazards and zones line up. Cobblemon spawn files can't express these shapes, so the companion mod will add a spawn condition `"zone": ""` (Cobblemon's appendage API) that looks up a grid generated from this plan. | Zone | Where | Biomes | Levels | Hazard (gate) | Signature drops (examples) | Alpha guardian (example) | | --- | --- | --- | --- | --- | --- | --- | | Button Isle | the start island inside the ring channel (3.6 % of land) | forest, plains, a little cherry grove and desert | 2–10 | none | feathers, berries (Pidgey, Sentret, Bidoof) | Pidgeotto, Lv 14 | | Southwold | south half, west of the jungle and swamp (30 %) | forest, plains, windswept hills | 8–18 | none | **wool** (Wooloo, Mareep) for cold insulation later; bones, honey | Dubwool, Lv 22 | | Westmarch | north-west (18 %) | forest, windswept hills | 15–25 | none | raw copper (Cufant), charcoal (Vulpix) | Ursaring, Lv 30 | | Verdant Jungle | east, both sides of the band (9 %) | jungle | 18–30 | none (warm and humid, below Cold Sweat's damage) | vines, Big Root (Tangela), food (Tropius) | Tangrowth, Lv 34 | | Mangrove Mire | south-east (10 %) | mangrove swamp | 25–38 | **poison** while in the swamp (companion mod area effect); Pecha berries, milk and potions help | Black Sludge (Croagunk, Grimer), Pecha berries | Toxicroak, Lv 42 | | Highland Meadows | north-east around the peaks (20 %) | plains, forest, cherry grove | 25–35 | none | leather (Ponyta) for heat insulation, Hard Stone (Larvitar) | Rapidash, Lv 40 | | Sunscorch | the desert and badlands pockets: north of the ring channel, the north-east and north-west coasts (3.4 %) | desert, badlands | 30–45 | **heat** (Cold Sweat): heat insulation, Fire Resistance potions | armadillo scutes (Sandshrew), Soft Sand, magma cream (Numel) | Hippowdon, Lv 50 | | Frostpeak Range | the jagged peaks plus the windswept hills and cherry groves right next to them (6 %) | jagged peaks | 40–55 | **cold** (Cold Sweat): wool insulation; warming food and potions | Never-Melt Ice (Snover), Razor Claw (Sneasel) | Abomasnow, Lv 60 | | Shallows | coast and beaches | ocean, lukewarm and warm ocean, beach | 5–20 | none | fish, kelp | none | | Deep Sea | ring and band channels, outer sea | deep ocean | 30–50 | none | Heart Scale-type rarities | Lapras or Wailord, Lv 55 | | Lush Caves | under the whole island | lush caves | 12–28 | none | glow berries, mushrooms (Paras) | none | | Dripstone Depths | under the whole island | dripstone caves | 22–38 | none | Dragon Fang (Bagon, Gible) | none | | Deep Dark | under the cold and temperate land, deep (since 2026-10-30) | deep dark | 45–60 | darkness (vanilla sculk; no Warden) | | Sableye or Spiritomb, Lv 62 | | Basalt Furnace | under the hottest land, at cave depth and (since 2026-10-30) down to the bottom | basalt deltas | 55–70 | heat (Cold Sweat) | **raw iron** (Durant), coal (Torkoal), blaze powder (Heatmor) | Heatran-like boss later (Phase 7) | Order of play the bands suggest: Button Isle → Southwold → Westmarch or Jungle → Mire or Highlands → Sunscorch (heat) → Frostpeak (cold) → the deep caves. **What helps with a zone drops from the boss fight of the zone before it** (Adrian, 2026-10-29). Wild Pokémon keep their normal drops (the "signature drops" column); the items a zone's hazard asks for come from the previous zone's Alpha guardian, so beating it is the way forward. Never a hard unlock: potions and golden apples still get a player through. Placeholder chain: | Boss of | Helps with | Drops (placeholder) | | --- | --- | --- | | Button Isle | Southwold | Poké Balls, Potions | | Southwold | Westmarch, Verdant Jungle | Great Balls, Exp. Candy | | Westmarch, Verdant Jungle | Mangrove Mire (poison) | the material for wading gear that keeps the swamp's poison off, Pecha berries | | Mangrove Mire, Highland Meadows | Sunscorch (heat) | Cold Sweat heat-insulation material | | Sunscorch | Frostpeak Range (cold) | Cold Sweat cold-insulation material (the recipe also takes wool) | | Frostpeak Range | Deep Dark, Basalt Furnace | Night Vision potions, more heat insulation | **Decided with the plan** (Adrian, 2026-10-29): the Sunscorch pocket just north of the start and the hot desert spots on Button Isle stay as an early taste of the heat; caves follow the cave biome everywhere, so the Deep Dark under the start island is Lv 45–60 too; the straight Westmarch/Highland Meadows border (x = −250) stays. **How zones are built** (Phase 3, step 2): - `tools/zone_layout.py` writes the zone grid to `datapacks/core/data/cobblesurvival/zones/island.json` (24-block cells; caves by cave biome). The companion mod loads it (`zone/Zones.kt`) and adds the spawn-condition field `"zone"`/`"zones"` to every Cobblemon spawn condition. `/cobblesurvival zone` (operators) names the zone you stand in. - Cobblemon's own spawn files are switched off by a filter in `datapacks/core/pack.mcmeta`. `tools/zone_spawns.py` writes one spawn pool per zone (`datapacks/core/data/cobblesurvival/spawn_pool_world/.json`) from Cobblemon's spawn data: every evolution line that can spawn on the island gets one home zone (none is lost; legendaries, mythicals, Ultra Beasts and Paradox Pokémon stay out), and its levels are clamped into the zone's band, which picks the evolution stages; every stage that reaches the band spawns up to its top (Adrian, 2026-10-31; Cobblemon's own ranges stop where the next stage takes over, which left Dusclops at Lv 45–46 in the Deep Dark and the zone topping out at Lv 54). Wild Pokémon never evolve; a caught one past its evolution level evolves on its next level-up. Placement weighs how much of the zone is the line's habitat, how much of the line's range the zone covers, and the line's strength (base-stat total of its strongest form) against the zone's band. Lines that Cobblemon lets spawn nearly everywhere first fill the cave zones, which take those whose first stage's primary type fits the cave (`world/zones.json` → caves → `types`; Adrian, 2026-10-30: the deep caves' tables were too small), then are shared out over the land zones by strength. The result per zone is in `docs/zone-species.md`; `world/zones.json` → `species` pins a line by hand. - Cave zones: every entry spawns in the cave's biome, and sky-light minimums, "can see sky", time of day, excluded biomes and the "natural" ground preset are dropped; otherwise the Deep Dark (fed by Cobblemon's surface "spooky" spawns, many of them night-only) would barely spawn anything, and lush caves' moss floors would barely count. - Strays (Adrian, 2026-10-30: a Lv 61 from the Basalt Furnace turned up in the Lush Caves): every 5 seconds a wild Pokémon near a player that stands in a zone whose band it tops by more than 10 levels despawns, unless it is in a battle, angry at someone or a guardian (`zone/Wanderers.kt`, `world/zones.json` → `wanderers`). Tested: `strays_despawn`. - Basalt Furnace got Durant, Torkoal and Heatmor pinned to it: its own lines were all big (Charizard, Turtonator, Centiskorch, …) and Cobblemon only spawns a Pokémon where its body fits, so cramped basalt caves stayed empty. - Tested: `spawn_pools_are_zoned` (no Cobblemon default spawn is left; every entry has a zone and spawns within its band) and `spawn_survey` (a player at a point deep inside each zone is offered only that zone's spawns, and at least one), both in `tools/quick_check.py`. **The Mire's poison** (Phase 3, step 5): while a player is in the Mangrove Mire without Mire Waders on, the companion mod gives them Poison every 5 seconds (`zone/ZoneHazards.kt`, data in `datapacks/core/data/cobblesurvival/zone_hazards/mire.json`, protection tag `#cobblesurvival:mire_protection`); the first time they walk in unprotected they get a warning. Vanilla Poison never kills, but it pins a player at half a heart, so a fall or a Pokémon's hit becomes deadly; milk, potions and golden apples still help. Mire Waders (leather boots, a KubeJS item) drop from the guardians of Westmarch and the Verdant Jungle, the zones before the Mire. Tested: `mire_poisons_without_waders`. **Soft level pressure** (Phase 3, step 6; no hard level caps): a Pokémon more than 5 levels above the band of the zone its trainer is in gets 10 % less experience per extra level, down to 10 % (`zone/LevelPressure.kt`, values in `world/zones.json` → `level_pressure`). It counts battle experience and anything else except Exp. Candy and commands. The roadmap also offered a disobedience chance; experience alone is gentler and Cobblemon has nothing to build disobedience on. Tested: `level_pressure_cuts_xp`. **Flyers** (Phase 3, step 7): strong flyers only spawn in high-level zones. A species you can ride through the air with a top flying speed of 60 or a top stamina of 85 (Cobblemon's ride stats) never spawns in a zone whose band starts below Lv 30; its weaker pre-evolutions still do, and a line that starts as a strong flyer is only placed in such zones (`tools/zone_spawns.py`, which refuses to write pools that break this). So Drampa lives in Sunscorch, Togekiss, Staraptor and Braviary in Frostpeak, Charizard in the Basalt Furnace; Salamence never spawns (Bagon and Shelgon stay in the Dripstone caves), and Aerodactyl only comes from fossils. Ride stats stay Cobblemon's for now: they can be overridden per species with a `species_additions` file replacing `riding`, best tuned after play-testing how far a flyer takes you on a 3 km island. Nothing blocks mounting. **Alpha guardians** (Phase 3, step 4; Adrian, 2026-10-29: fixed spots, back 5 hours after being beaten): - One per zone except the Shallows, Lush Caves and Dripstone; the Basalt Furnace gets its boss in Phase 7. Each stands at its zone's sample point (deep inside the zone; a cave floor for the Deep Dark) and only appears while a player is within 96 blocks; it wanders no further than 24 blocks. - The guardian is the strongest Pokémon that spawns walking (or, at sea, swimming) in its zone, five levels above the band, half again as big, and uncatchable: Pachirisu (Button Isle), Sigilyph (Southwold), Carbink (Westmarch), Tangrowth (Jungle), Arcanine (Mire), Cinderace (Highlands), Delphox (Sunscorch), Tyranitar (Frostpeak), Kingdra (Deep Sea), Dusknoir (Deep Dark). `world/zones.json` → `guardian.species` overrides the pick. - Beating it in a battle gives every player in that battle its drops (the chain above: Icy Rocks before Sunscorch, Heat Rocks before Frostpeak, ...). It returns after 5 hours of game time, which only passes while the server runs. Cobblemon's Habitat Blocks were the roadmap's idea, but they can't wait a set time after a defeat or carry custom drops, so the companion mod runs guardians (`zone/Guardians.kt`; state in the world save; `/cobblesurvival guardians` for operators). Definitions: `datapacks/core/data/cobblesurvival/guardians/.json`, written by `tools/zone_spawns.py`. - Tested: `guardian_appears_and_returns` (appears big and uncatchable at its level, beaten in a real battle, drops arrive, stays away, returns after its time). **Hazards** (Phase 3, step 5), with Cold Sweat 2.4.4: - Cold and heat follow the biome: Frostpeak's jagged peaks freeze an unprotected player in about a minute even at noon, Sunscorch's desert and badlands overheat one in about two minutes at noon; deserts cool off at night. Cold Sweat's insulation doesn't move the comfortable range, it slows how fast the body drifts out of it. - Only Cold Sweat's fur set (cold) and hide set (heat) really insulate: about 14 minutes in Frostpeak at noon and 25 in Sunscorch, longer with extra fur or hide sewn in. Their materials need boss drops (Adrian's rule): Goat Fur from wool and a Heat Rock (Sunscorch's guardian), Hoglin Hide from leather and an Icy Rock (the Mire's and the Highlands' guardians), `kubejs/server_scripts/insulation.js`. Leather armour no longer insulates by itself; sewn-in leather and wool only a little. Fire Resistance and Cold Sweat's own potions and warm/cold food still work. - So the lowlands stay livable at night, the coldest comfortable temperature is lower (0.25 instead of 0.4) and windswept hills are milder (45–72 °F instead of 24–58 °F). New players keep Cold Sweat's 5-minute grace period. - Cold Sweat's own animals (chameleons) can't join, like vanilla mobs: the only-Pokémon rule now lets in Cobblemon's entities only. - Tested on the island itself (`cold_needs_fur`, `heat_needs_hide`, `lowlands_are_mild` in `tools/quick_check.py`): players stand at real spots in each zone, their body drift is turned into minutes until it hurts, and a frozen player without the grace period takes damage. ## Phase 4: resources **Adrian's answers (2026-10-30):** Cobblebase's skills look fun and stay in, gated behind progression; power comes from an existing mod, also gated (Cobblemon Poke Power); evolution stones as ores are fine for now; the balance values are fine for now and stay configurable, to tweak later. **Ore nodes and veins.** Ore only comes from [Regenerating Ore Veins](https://www.curseforge.com/minecraft/mc-mods/regenerating-ore-veins) (plus chest loot): a mined ore block turns into a regenerator and grows back after its time, which counts in real time, unlike the guardians' game time. Each ore tier lives in exactly one zone, higher tiers in higher zones (table in `docs/balance.md`). Adrian (2026-10-30): ore shows up mainly as **small nodes on the surface**, the **larger veins underground are harder to find**, and the cave zones' ores are **nodes on the cave floor, not in the walls**. `tools/zone_ores.py` writes the vein mod's config (`pack/config/regenerating_ore_veins/`) and the node table from `world/zones.json` → `ores`: - **Surface nodes** (every surface tier): 1–5 ore blocks lying in natural ground (grass, dirt, sand, stone, snow, ...; tag `cobblesurvival:ore_node_ground`), at most one block standing out, never in water or under leaves, a given chance per chunk of the tier's zone. Evolution stones are single blocks and rarer. - **Cave nodes** (Lush Caves, Dripstone Depths, Deep Dark, Basalt Furnace): a lump on a cave floor in the tier's cave biome, at the depths where that biome lies (the Deep Dark mostly below y −24, the others between 0 and 40). Cave tiers have no veins inside the rock. - The companion mod places the nodes (`ore/OreNodes.kt`, table `datapacks/core/data/cobblesurvival/ore_nodes/island.json`) in every new overworld chunk, through the vein mod's own placement, so they are veins like any other and grow back. - **Underground veins** (surface tiers): larger (radius 2–4) and fewer than the nodes, generated by the vein mod inside the rock under their zone. A surface zone is a set of boxes cut from the zone grid; the mod checks every vein block against them, so no vein block lies outside its zone; cave biomes are zones of their own, which the veins keep out of (the mod checks the biome at a vein's centre, so an edge can reach a little over a cave border). Tiers with an ordinary and a deepslate ore get two veins: the ordinary ore from y 0 up, the deepslate one below. - **Vein Locator**: attuned in the crafting grid with an ore *block* (needs Silk Touch), it finds the nearest vein or node of exactly that block and throws a signal toward it like an Eye of Ender; 75 % chance to break per use. Attuned to the deepslate ore it only finds the deep half of the veins. - **Cobblemon's evolution stones are tiers too** (approved for now, 2026-10-30): their natural ores have been off since Phase 1, so stones had no source. Each stone lives in one zone that fits it (Leaf Stone in the jungle, Water Stone in the Mire, Ice and Dawn Stone on Frostpeak, ...). - Players can't break regenerators (`allow_breaking` false), so nobody removes a vein for good, and explosions don't hurt veins. The Vein Locator is crafted from a compass and a copper ingot instead of netherite. - Both mods fill a new chunk only while it is loaded, a few chunks a tick, and the vein mod forgets the rest when the server stops. Pregeneration is faster than that, so `tools/make_world.py` loads the island again row by row until every chunk has its veins and nodes (`/cobblesurvival veins unfilled` counts what's left), and the world report fails on any chunk without them. On a live server new chunks fill as players load them. - Checked: the world report (ore only in tracked veins and nodes inside their tier's zone, every tier with nodes and every surface tier with veins, every chunk filled) and the self-tests `surface_nodes_lie_in_the_ground`, `cave_nodes_lie_on_the_cave_floor`, `vein_regrows_after_unload` and `vein_regrows_after_restart` (a second server start in `tools/selftest.py` and `tools/quick_check.py`). **Unique drops.** Only Pokémon live on the island, so a mob's drop needs another source. `tools/drop_audit.py` checks every item a Pokémon or a vanilla mob drops that a recipe or brewing consumes, and writes `docs/pokemon-drops.md` (item, wild Pokémon with zone and chance, other sources, consumers). Gaps it found, filled through Cobblemon species additions (`datapacks/core/data/cobblesurvival/species_additions/`, placeholders): Blaze Rod (Carkol in Westmarch, Torkoal in the Basalt Furnace; needed for brewing stands, blaze powder and Eyes of Ender), Ghast Tear (Drifloon, Dusclops), Breeze Rod (Whimsicott), Shulker Shell (Shuckle, Cloyster), Nether Star (Charizard). The tide armour trim and the wither skeleton skull stay without a source (cosmetic here). A zone spawn with its own drops overrides the species' drops, so the audit also fails if an addition targets such a spawn (Magmortar's and Centiskorch's did). Self-test: `drop_additions_apply`. **Base jobs and power.** [Cobblebase](https://modrinth.com/project/cobblebase) (Pokémon in a pasture work: harvesting, producing, finding, buffs, ...) and [Cobblemon Poke Power](https://modrinth.com/project/lIn7oRFm) (Electric-type Pokémon in a Power Block make Forge Energy; nothing in the pack uses the energy yet, Phase 6 machines). Phase 4 first gated them with flags: a skill or the Power Block opened once its owner had beaten a zone's Alpha guardian. **Replaced by soft gates on 2026-10-31** (Adrian: guardians must not set flags; see "Soft gates" under Phase 5). The companion mod still puts every Cobblebase skill executor behind the gate, by reflection, when the server starts (`progress/CobblebaseGate.kt`); a skill missing from the table stays off, so an update can't add ungated skills. Breeder and Egg Hatcher work since Cobbreeding came in (Phase 5, "Breeding"). **Catching high-level Pokémon** (Adrian, 2026-10-30). Measured against the trainer's strongest party Pokémon (`catching/Catching.kt`, values in `datapacks/core/data/cobblesurvival/catching/rules.json`): - The catch rate falls with the level gap: full at the trainer's level, then linearly down to 2 % of the normal rate at 20 levels above and beyond ("20+ levels higher should be very improbable"). Applied to the rate Cobblemon's catch formula uses (its catch-rate event), so balls, status and HP still count. - A failed catch angers the Pokémon: it attacks the thrower instead of fleeing. - A wild Pokémon above the strongest Pokémon in a nearby player's party (16 blocks) is hostile to that player. - The fighting is [Fight or Flight Reborn](https://modrinth.com/mod/cobblemon-fight-or-flight-reborn)'s: an angry Pokémon gets the aspect `cobblesurvival_enraged`, which the pack's `config/fightorflight.json5` lists in `always_aggro_aspects` (so it fights rather than flees), and its target is set through Fight or Flight's own "caught by" mark, by reflection. While an angry Pokémon has a target, thrown balls don't catch it (Fight or Flight's `aggressive_pokemon_catchable` false, its default): it has to be battled. Pokémon below Lv 5 never fight back (its `minimum_attack_level`). - Self-tests: `catch_rate_falls_with_level_gap`, `failed_catch_enrages`, `strong_wild_pokemon_hunt_weak_trainers`, `wild_pokemon_ignore_stronger_trainers`. **Bee nests** (found by Adrian in the Phase 4 world): a nest whose bee the island turns away kept the bee and tried to let it out every tick, with its sound each time. The companion mod now empties such a nest (`world/OnlyPokemon.kt`); honey still comes from Combee and shearing nests that have honey. The field guide learned two things on the way: vanilla mobs count as absent (the companion mod lets only Cobblemon's entities join a world), and species additions replace the drops they set. The veins come in as sources from `world/vein-sources.json`. ## Notes for Phase 5 - **Shoulder Pokémon** (Adrian, 2026-10-30): Cobblemon lets a player carry a small Pokémon on their shoulder (96 species are `shoulderMountable`: Pidgey, Pikachu, Eevee, Zubat, Squirtle, ...). It has a shoulder-effect system with one type, `potion_effect` (effect, amplifier, particles, icon), but no species uses it in 1.8. Party passives can start there, as data: species additions giving shoulder Pokémon an effect while carried (e.g. Pidgey slow falling, Zubat night vision, a Fire type warmth against Frostpeak's cold), maybe gated behind progression like Cobblebase's skills. To check first: that species additions can set `shoulderEffects`, and how the effect behaves when the Pokémon faints or goes back in its ball. ## Phase 5: existing mods checked first (2026-10-31) The roadmap lists four mods as prior art; on Cobblemon 1.8 / NeoForge 1.21.1 today: - [Cobblemon HMs](https://modrinth.com/project/cobblemon-hms) 4.1.0 (NeoForge, Cobblemon ≥ 1.8.0, Kotlin for Forge): send out a Pokémon that knows the move, pick it on Cobblemon's interaction wheel, and your fist uses it. 39 abilities: Cut, Rock Smash, Dig, Strength, Teleport, Leafage, Growth, Flash, Dive, Rock Climb, Magnet Rise, Sweet Scent, weather moves (Rain Dance, Sunny Day, Thunder, Sandstorm, Snowscape, Blizzard), riding boosts (Surf, Tailwind, Agility, ...), Camouflage. Each ability has an on/off switch, cooldown, power, a minimum Pokémon level and a fullness/friendship cost (`config/cobblemon_hms.json`). Tried with the pack: loads, all self-tests pass. License "shiero's Mods License": free, also in a free modpack; anything that earns money (paid pack, paid server perks) needs an agreement with the author. Missing from the roadmap's first set: ignite (Ember/Flamethrower), freeze water (Ice Beam), extinguish and fill cauldrons (Water Gun). - [Cobblemon Environment Interactions](https://modrinth.com/project/cobblemon-environment-interactions) 3.1.0 (NeoForge, Apache-2.0, Cobblemon ≥ 1.7.3): type-based mining and utilities while riding or commanding one companion; not move-based. - Cobblemon Field Moves (Fabric, Cobblemon 1.6), Cobblemon Map Kit (Fabric), SceneBlocks (special blocks, custom license), CobbleStrike (real-time attacks): no fit. ## Phase 5: field moves from Cobblemon HMs (Adrian, 2026-10-31) Adrian: use Cobblemon HMs. Mining ore is the one hard requirement; the other field moves are nice to have, and Flamethrower in the roadmap was only an example. Furnaces are not lit by a field move: a Pokémon goes *inside* a Phase 6 multiblock instead. So the companion mod doesn't build the roadmap's own `field_moves` system for now; HMs replaces it. - **Mining** is HMs' Rock Smash: with a Pokémon that knows it sent out, the player's fist breaks the ore it hits and the ore connected to it, up to 32 blocks (power 32). HMs breaks each block as the player with a netherite pickaxe, through the normal break event, so Regenerating Ore Veins turns mined vein blocks into regenerators and the vein grows back. Automated: `rock_smash_mines_veins` (fires HMs' own Rock Smash handler at a vein). - **Trainer Ditto.** HMs sends a visiting Trainer Ditto that sells TMs; underneath it is a wild Cobblemon Ditto. The island's wild-Pokémon rules (hunting weaker trainers, strays despawning) leave it alone, like guardians (`world/SpecialPokemon.kt`). - **Config:** HMs' defaults for now (`config/cobblemon_hms.json` is written by the mod on first start; the pack ships none). Every ability can be switched off or tuned there. Adrian's answers (2026-10-31): HMs' abilities stay open; the ones that get around zone hazards get soft gates; party passives come from shoulder Pokémon. **Soft gates** (Adrian, 2026-10-31: gate behind ores/materials or a high-level Pokémon of a hard zone; materials may come from guardians, but guardians set no flags, and a gifted Pokémon or material does the same as an earned one). This is the roadmap's first rule ("soft gates only"), which the Phase 4 flags broke. One rule for everything gated (`progress/Gates.kt`, data `datapacks/core/data/cobblesurvival/progression_gates/base.json`): - A gate names a zone. The Pokémon doing the work passes it when its level reaches the top of that zone's band, or when it holds one of the zone's materials: its ores (raw or smelted), its evolution stone, or its guardian's drop (Heat Rock for Sunscorch, Mystic Water for the Deep Sea). A material of a zone with a higher gate counts too, so a Pokémon holding a diamond passes the Westmarch gate. Cobblemon lets a Pokémon hold any item, and Cobblebase doesn't use the held item, so holding the material costs nothing else. - **Cobblebase skills** keep their Phase 4 stages, now as zone gates on the pastured Pokémon. When one idles, its owner (if within 64 blocks) is told once what it needs. - **The Power Block** has no gate of its own: its recipe already takes four redstone blocks, the Verdant Jungle's ore. - **HMs auras that get around hazards** (`progress/HmsGate.kt`): Flash (Dripstone gate, for the Deep Dark), Dive and Mist (Mire; the Deep Sea, and slipping away from Pokémon that hunt you), Rock Climb (Highland Meadows; Frostpeak's cliffs), Magnet Rise (Sunscorch; flight), Lucky Chant (Frostpeak; more ore), Lava Plume (Deep Dark; the Basalt Furnace). HMs keeps a switch per player, Pokémon and aura; every 5 ticks the companion mod switches off a gated aura whose Pokémon doesn't pass and says what it needs. Dive, Mist and Rock Climb were added to Adrian's four (Flash, Lava Plume, Magnet Rise, Lucky Chant) because they get around hazards the same way. - The guardian record (`Progress`) and `/cobblesurvival progress` are gone; a guardian win only gives its drops. **Shoulder passives** (`party/ShoulderPassives.kt`, data `datapacks/core/data/cobblesurvival/shoulder_passives/base.json`): while a Pokémon rides a player's shoulder, its type gives an effect: Flying slow falling, Electric speed, Rock/Ground/Steel haste; behind soft gates, Fire Cold Sweat's warmth (Highland Meadows gate, for Frostpeak), Ice its frigidness (Jungle gate, for Sunscorch), Water water breathing (Mire gate) and Ghost/Dark night vision (Dripstone gate). 93 species can ride a shoulder in Cobblemon 1.8, and every passive's types have at least one. Done in the companion mod rather than with Cobblemon's own `shoulderEffects`, which can match neither types nor gates. Self-tests: `gates_cover_every_cobblebase_skill`, `cobblebase_skills_need_level_or_material`, `power_block_gated_by_its_recipe`, `hazard_auras_need_level_or_material`, `shoulder_pokemon_give_passives`. **Cobblebase Mining, from Adrian's play-test (2026-10-31):** - *Loot on the ground.* Cobblebase's Mining never fills a chest: it drops what it digs at the Pokémon's feet, tagged with its pasture. A second Pokémon in the same pasture working as **Item Gatherer** (open from the start) picks up items tagged with its pasture and puts them into the best container within 12 blocks of it. Untouched, the loot despawns after 5 minutes like any dropped item. Left as Cobblebase designed it; direct delivery would be a change in the companion mod. - *Gated jobs don't stick.* Cobblebase lets the player assign one job per pastured Pokémon in its Skills tab, and the Buffs tab's "Active" list shows the assigned jobs. A job the Pokémon doesn't pass the gate for is now taken back the moment Cobblebase ticks it: the assignment is cleared, every client gets Cobblebase's assignment sync (so it leaves the Active list), and the owner is told what the Pokémon needs (`progress/CobblebaseGate.kt`). The job's button still shows in the Skills tab. Hiding it per Pokémon would need the gate tables sent to clients and a client mixin into Cobblebase's `SkillsPanel` (the screen already knows each pastured Pokémon's level and held item); Adrian (2026-10-31): leave it as is. - *Ores from the pasture's zone only* (Adrian: only the items of the zone the pasture is in). Mining, the Ore Finder and the Evolution-item Finder roll Cobblebase's tables `mining_*`, `finder_ore_*` and `finder_evo_*`. Cobblebase rolls its own built-in copy of a table (without loot conditions) and only falls back to the data pack's table when it has none, so the companion mod removes its copies of those twelve tables at server start (`ore/BaseLoot.kt`) and the pack ships its own versions (`datapacks/core/data/cobblebase/loot_table/`, written by `tools/cobblebase_loot.py` from Cobblebase's): every ore and evolution stone carries the loot condition `cobblesurvival:in_zone` (`zone/InZoneCondition.kt`) naming the zone of its tier in `world/zones.json`, checked at the Pokémon's position. Mining also gets the zone ores Cobblebase leaves out (quartz, the evolution stones) and loses the type gems, which are crafting shortcuts to gate materials (a Fire Gem makes a Heat Rock); ruins archaeology still has the gems. Cobblestone, gravel, flint, tumblestones and fossils stay everywhere. A table an operator edits in Cobblebase's own loot editor still wins. Self-tests: `gated_jobs_are_taken_back`, `base_jobs_dig_their_zones_ores`. **Breeding** (Adrian, 2026-10-31: add Cobbreeding without a gate; only Ditto should be rare, in an endgame area or gated behind something else). Cobblebase's hatchery only takes [Cobbreeding](https://modrinth.com/mod/cobbreeding)'s eggs, so its Breeder and Egg Hatcher did nothing before. - Cobbreeding 2.4.1: two compatible Pokémon (or one and a Ditto) in a Pasture Block, breeding switched on with the pasture's button, lay an egg in its nest now and then (every 8,000–14,000 ticks); an egg hatches in a player's inventory (about 10 minutes for 20 egg cycles). Defaults kept: two Dittos make no random eggs, Masuda shiny boost ×4, hoppers may pull eggs out of a pasture. Open from the start, no gate; Cobblebase's Breeder and Egg Hatcher stay behind the Deep Dark gate. Cloth Config comes along for its client config screen. - **Ditto** moved from Button Isle to the Deep Dark (Lv 45–60), with both of Cobblemon's entries: rare bucket in slime chunks, ultra-rare elsewhere. `world/zones.json` → `species` takes the long form `{"zone": "deep_dark", "all_entries": true}` for it, because when no stage fits a band `tools/zone_spawns.py` keeps one entry, which was the slime-chunk one. The Deep Dark's line target went up by one and Button Isle's down by one, so no other line moved; the Southwold and Frostpeak guardians are pinned (Pinsir, Tyranitar), since they tie on base stats with Hawlucha and Archaludon and the tool's pick between ties changed from run to run. - HMs' visiting **Trainer Ditto** is made uncatchable (`world/SpecialPokemon.kt`); it still trades TMs. - Cobbreeding lists a mixin into Cobblemon's client-only pasture screen among its common ones, so a dedicated server logs one harmless "invalid dist" error at start; allowlisted in `tools/smoke_allowlist.txt`. `tools/check_deps.py` now skips dependencies meant for the other side (Cobbreeding needs Cloth Config on clients only). Self-tests: `cobbreeding_eggs_fit_the_hatchery` (Cobblebase's bridge takes Cobbreeding's eggs), `trainer_ditto_cant_be_caught`. ## Notes for Phase 6 (researched 2026-10-31) Adrian asked for existing mods for the multiblocks, or at least their logic. Phase 6's machines need a pattern-checked structure with a controller, a Pokémon stored inside (keeping its data, visible), safe return when the structure breaks, and state that survives unload and restart. Power is already Poke Power's (Phase 4), so the first machine is the Large Furnace. Nothing on NeoForge 1.21.1 puts a Pokémon into a multiblock; the structure part has several options: | Option | What it is | License, size | Fit | | --- | --- | --- | --- | | Vanilla `BlockPattern` / `BlockPatternBuilder` | Minecraft's own pattern matcher (iron golems, withers, end portals): layered aisles, block predicates, finds the structure in any rotation | built in | Enough for checking a fixed shape around a controller; no previews or editor. **Recommended** for the companion mod | | [Patchouli](https://modrinth.com/mod/patchouli) multiblock API | Pattern definition and validation with rotation, plus in-world "visualize" previews from a guide book | CC BY-NC-SA 3.0, very widely installed | Good add-on to the above if players should see how to build a machine (e.g. a field-guide page) | | [Multiblocked2](https://modrinth.com/mod/multiblocked2) | Full multiblock framework: in-game 3D editor, controller, recipes, GUIs, machine effects, KubeJS; a Java API for custom traits and recipe capabilities (where a "Pokémon slot" would plug in) | LGPL-3.0; needs LDLib2 and KilaGraph (MIT); the companion mod would compile against a snapshot Maven | Most powerful, if many machines with recipes and GUIs are wanted; heavy, and the Pokémon part stays custom code | | [Custom Machinery](https://modrinth.com/mod/custom-machinery) | Data-driven machines for pack makers; recipes can require a structure around the machine and entities nearby (check, hurt, kill) | LGPL-3.0, KubeJS optional | Poor fit: no stored Pokémon, only nearby entities | | [Modular Machinery Reborn](https://modrinth.com/mod/modular-machinery-reborn) | JSON multiblocks with item/fluid/energy hatches | LGPL-3.0; needs ExperienceLib and Athena (GPL) | Poor fit: hatches and recipes, nothing for Pokémon | | Tiny Multiblock Lib, CTMLib, ZeroCore | bed/door-style big blocks; connected textures; an old All Rights Reserved API | | Not useful | For the Pokémon inside, the best prior art is already in the pack: Poke Power's Power Block (MIT, [source](https://github.com/Matthiesen-dev/poke-power)) stores Pokémon with their owner, returns them on break, saves and syncs them, and renders the model in the block. Study it (and Cobblemon's Pasture Block, as the roadmap says); don't depend on it. ## Phase 6: Pokémon machines (Adrian, 2026-10-31) Adrian's choices: the Large Furnace has a Fire Pokémon heat real furnaces, and hoppers must work in and out; an Electric generator making Forge Energy is built too (alongside Poke Power's Power Block); machine work costs the Pokémon nothing. Then three more (Adrian, 2026-10-31): a drop synthesizer that turns a lot of energy into what the Pokémon inside would drop, faster with more energy, with a full drop roll and tiers that add looting; a sacrifice altar that trades a high-level Pokémon for hard-to-get items, rare candies only above the highest natural level, lower tiers too; and a rebirth chamber that turns a Lv 100 Pokémon into a shiny Lv 1 after a long time. **Shape.** Every machine is a 3×3×3 shell with the machine's core block in the middle of one wall and air in the centre, where the Pokémon stands. The block under the core is free (a hopper can go there), and the middle of each side wall and the top centre are window spots that may be glass instead of casing, to see the Pokémon. The shell is data (`datapacks/core/data/cobblesurvival/machines/.json`: layers bottom to top, a key per character listing the blocks allowed, the core and the chamber marked), checked in all four horizontal rotations around the core every second. Casing is a block tag per machine, so the casing is the materials gate: | Machine | Core | Casing | Pokémon | | --- | --- | --- | --- | | Large Furnace | Heat Core | `#cobblesurvival:heat_casing`: bricks, mud bricks, deepslate bricks | any Fire type | | Generator | Dynamo Core | `#cobblesurvival:dynamo_casing`: copper blocks (any weathering, waxed or not, cut or not) | any Electric type | | Drop Synthesizer | Synthesis Core | `#cobblesurvival:synthesis_casing`: iron, gold, diamond or netherite blocks; the lowest metal present sets the tier | any Pokémon | | Sacrifice Altar | Altar Core | `#cobblesurvival:altar_casing`: obsidian, crying obsidian, blackstone, polished blackstone bricks | Lv 20 or more | | Rebirth Chamber | Rebirth Core | `#cobblesurvival:rebirth_casing`: amethyst, quartz and calcite blocks | Lv 100 | **A Pokémon goes in** when its owner sends it out near the core and uses the core with an empty hand: the nearest of their sent-out Pokémon that fits walks in (it leaves the party and its data is kept in the core). Using the core again takes it out, back to the party or else the PC. Sneak-use shows the machine's state. Breaking the core or any part of the shell sends the Pokémon back to its owner, online or not. The core is the only place the Pokémon exists while inside, so nothing can be duplicated by breaking and inserting at once. **What's shown** is a real Cobblemon Pokémon entity, a copy, standing in the chamber: scaled to fit, no AI, invulnerable, silent, uncatchable, and it can't be battled or used. It's never kept in the world save: one loaded from disk is dropped, and the core puts a fresh one there. The cores are see-through (Adrian, 2026-11-02): a solid frame around tinted glass, rendered translucent, so the Pokémon shows through the core as well as through glass windows. When a sacrifice is made or a rebirth ends, the shown Pokémon plays Cobblemon's faint animation and its cry pitched down (Cobblemon has no separate death sounds; the games pitch the cry down when a Pokémon faints), and vanishes two seconds later; a reborn Pokémon then appears in its new form. **Large Furnace.** Furnaces, blast furnaces and smokers (`#cobblesurvival:machine_furnaces`) standing against the outside of the shell burn without fuel while they have something to cook, and cook faster. They stay ordinary vanilla blocks, so their screens, recipes and hoppers work as always (input from above, output from below). How many are heated and how fast depend on the Pokémon's level and Special Attack (numbers in the data file and `docs/balance.md`). **Drop Synthesizer.** Takes Forge Energy into the Synthesis Core (from a Dynamo Core or cables) and, for every so much energy, rolls the Pokémon's own drop table once, as if it had been defeated. The shell's tier adds looting: iron none, gold I, diamond II, netherite III (more of each drop, rare drops likelier). It takes energy up to a cap per tick, so more energy makes it faster. What it makes waits in the core (nine stacks; it stops when full) for a hopper under the core, or pipes from other mods on any side; breaking the core drops it. **Sacrifice Altar.** Sneak-use the Altar Core with the Pokémon inside to see what it would give, sneak-use again within five seconds to sacrifice it: the Pokémon is gone for good and the rewards go to the owner. The highest level tier reached decides the reward: Exp. Candies from Lv 20, Ability Capsules from Lv 65, rare candies only from Lv 80 (above the highest natural level: Basalt Furnace Lv 70, guardians Lv 75), an Ability Patch from Lv 90, a nether star at Lv 100; a Lv 100 Dragon type also gives a dragon egg. **Rebirth Chamber.** A Lv 100 Pokémon put in starts a long rebirth (placeholder: two hours of server time, also while the chunk is unloaded). Then it is the first stage of its line at Lv 1 and shiny, keeps its nickname, original trainer, held item and every move it learned, gets three of its IVs set to 31 (no IV ever goes down), and keeps its ability; an Ability Patch comes with it, so taking the hidden ability is the player's choice. Taking it out before the end cancels the rebirth. **Finding out how to build one** (Adrian, 2026-11-02: there must be a way in game). Placing a core, using it while its shell is incomplete, or sneak-using it while empty shows every missing or wrong spot of the shell as a small glowing ghost block for ten seconds (vanilla block displays, so clients need nothing; never saved), in the orientation the core faces, with a chat line counting what's missing. The cores' tooltips say what each machine does and what its shell is made of. The machine test world (`tools/make_machine_world.py`) has every machine built. **Cores** are crafted (soft gates by material): Heat Core from bricks, iron and a blast furnace; Dynamo Core from copper blocks, redstone and a thunder stone; Synthesis Core from iron and redstone blocks, hoppers and a crafter; Altar Core from obsidian, gold blocks and an echo shard; Rebirth Core from amethyst blocks, diamonds and a shiny stone. Their textures are drawn by `tools/machine_assets.py`. Self-tests (`MachineTests`): in and out, wrong Pokémon refused, broken shell and broken core (to the PC when the party is full), chunk unload, server restart, and each machine's work (a furnace fed and emptied by hoppers without fuel, a Poke Power cable charged by a Dynamo Core, Magikarp's salmon through a hopper under a gold Synthesis Core, a confirmed sacrifice with a Lv 100 Dragonite's rewards, a rebirth from Charizard to a shiny Lv 1 Charmander with its moves and ability). **A working machine shows it** (Adrian, 2026-11-02: particles around the Pokémon and an animated core, like the fire in a furnace; the Pokémon should now and then look as if it takes a hit). While its machine works, the core's block state is `active`: the core gives off light, its front switches to an animated face (the furnace's flame flickers; the other emblems pulse with sparks rising; `tools/machine_assets.py`), and particles show (`MachineEffects`, client side): flames around a Fire Pokémon and at the furnace core's front with smoke and the furnace crackle; electric sparks in the generator; enchanting glyphs flowing into the Pokémon in the synthesizer; souls and soul fire at the altar; end rods and glyphs in the rebirth chamber. It stays active two seconds after the work stops, so it doesn't flicker. About every 15 seconds of work the Pokémon inside flinches: it flashes red and Cobblemon's hit sound plays, but nothing is hurt (`MachineDisplay.flinch`). Working means: a heated furnace has something to cook, the generator isn't full, the synthesizer gets energy, a rebirth runs, the altar holds a Pokémon. Self-test: `heat_core_cooks_with_hoppers` also checks that the core turns active while cooking and back afterwards, and that a flinch leaves the Pokémon unharmed. **Generator.** The Dynamo Core stores Forge Energy and pushes it to whatever touches the core and takes energy (cables, machines from other mods); its output per tick depends on the Pokémon's level and Special Attack, in line with Poke Power's 50 FE per level. ## Phase 7: boss battles with area attacks (Adrian, 2026-11-02) Adrian's choices: every zone guardian gets area attacks (more and harder in harder zones); the Basalt Furnace gets a boss and the End a Pokémon that replaces the Ender Dragon; dying or leaving the arena loses the fight; area attacks hit only the player (the roadmap's default). **Guardians reviewed** (Adrian, 2026-11-02: the Deep Dark should have a legendary, and each guardian should fit its zone; Tyranitar in the snow looked off). Adrian's first picks, Groudon (Basalt), Giratina (End) and Meloetta (Deep Dark), have no model in Cobblemon 1.8.4 (only 17 legendaries and 2 mythicals are implemented), so implemented ones stand in until they come; changing one is a word in `world/zones.json`: | Zone | Was | Now | Why | | --- | --- | --- | --- | | Highland Meadows | Cinderace | **Mudsdale** | a draft horse of highland pastures, not a starter | | Sunscorch | Delphox | **Flygon** | the desert spirit of sandstorms | | Frostpeak Range | Tyranitar | **Abomasnow** | lives on snowy mountains; spawns there | | Deep Dark | Crobat | **Regigigas** | a legendary; the ancient colossus sleeping in ruins, like the Ancient Cities | | Basalt Furnace | none | **Moltres** (Lv 75) | the flame bird of the volcano; stands in for Groudon | | The End | Ender Dragon | **Eternatus** | the cosmic final boss; stands in for Giratina | The others fit and stay: Morpeko (Button Isle), Pinsir (Southwold), Scyther (Westmarch), Tangrowth (Jungle), Swampert (Mire), Kingdra (Deep Sea). **Bosses are data**, `datapacks/core/data/cobblesurvival/bosses/.json`, written by `tools/zone_bosses.py` from a tier table per zone band: which fight it is (a zone's guardian, or the End boss), the arena radius, and the attack patterns. A pattern has a shape (circle, ring, line, cone) and its size, where it's aimed (at the player, or around the boss), a telegraph time, damage, an optional effect (fire: set alight; ice: slowed and freezing) and a trigger (every so many seconds, every so many turns, or once when the boss's HP falls below a share). **Runtime** (companion mod). A battle between players and a boss Pokémon becomes a boss fight. While it lasts, due patterns telegraph on the ground where they'll land (made more visible after Adrian's first fight, 2026-11-02: large particles redrawn every 2 ticks over the whole area, a bright outline with rising sparks, and a low bell when an attack begins), then hit the battling players standing inside; everything stops when the battle ends. The arena is a circle around where the boss stood when the fight began: a player who leaves it, or dies, loses the fight (the battle ends, the boss stays, nothing drops). Cobblemon's own flee check only applies when a battle starts, which is why the arena is enforced here. **Moltres** becomes the Basalt Furnace's guardian like the others (Lv 75, band top + 5), with placeholder drops (3 Exp. Candy XL, 2 netherite scrap). Self-tests (`BossTests`): every guardian has a fight and a Cobblemon model; each attack shape hits a player standing in it and misses one ten blocks away; attacks stop when the battle ends; leaving the arena ends the battle and the boss heals; the dragon can't join the End, Eternatus appears, and beating it opens the exit portal with the egg. **Eternatus** replaces the Ender Dragon: the dragon no longer spawns; Eternatus (uncatchable, Lv 80, 15 blocks tall) 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. **Bosses are big and tough, not strong** (Adrian, 2026-11-02: Eternatus was too small and too easy; guardians should be way bigger and tougher; only defensive stats go up, never Attack or Speed; no single attack may take more than a quarter of a boss's HP). Every boss (guardian, End boss, practice copy) is scaled to stand about 8 blocks tall (Eternatus 15; `tools/zone_spawns.py`, `tools/zone_bosses.py`) and gets perfect IVs and 170 EVs each in HP, Defense and Sp. Def, nothing offensive (`BossToughness`). In battle it brings the **Guardian's Ward**, a held item that exists only inside Cobblemon's battle engine (Showdown): Cobblemon loads it from `datapacks/core/data/cobblesurvival/held_items/guardiansward.js`, and the companion mod hands it to every boss ahead of whatever it holds, so it can't be stolen, knocked off or suppressed. It starts a lasting condition that doubles Defense and Sp. Def while the boss defends (not for Body Press) and caps one attack, all hits of a multi-hit move together, at a quarter of the boss's max HP. Guardians and an Eternatus already standing in a world grow and get their training on the next check outside battle; a cave guardian moves to a spot it fits in. Area attacks that start at the boss (rings, lines, cones) reach half its width further, so a big boss's ring starts at its edge. Self-tests: `practice_bosses_spawn` (size, training, full health) and `boss_takes_a_quarter_per_attack` (a real battle: a Lv 100 Garchomp's Bullet Seed, every hit enough to knock out the Lv 15 boss, takes exactly a quarter per use). **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 | | --- | --- | | Roadmap's open decisions table (loader, island size, safety net, boss AoE, etc.) | Roadmap defaults, unchanged | | **Platform split** (deferred until a public release; not publishing for now) | No platform hosts the full mod list (FTB Quests is CurseForge-only; CobbleHardcoreMon is Modrinth-only). Both mods stay; the exports keep listing what they leave out. Options for release: (a) publish one platform's pack, or both with the gaps listed; (b) ask the authors to cross-post (CobbleHardcoreMon, Matthiesen Core, Regenerating Ore Veins and Ore Generation Configuration are MIT); (c) a quest mod on both platforms; (d) permadeath in the companion mod | | **No CI** (Adrian, 2026-10-30): no Actions runners on the Gitea server, which is too weak; a push that started the old `.github/workflows/ci.yml` crashed it. The workflow is removed (still in the git history) | The roadmap's "CI smoke test" runs locally instead: `tools/smoke_test.py`, `tools/selftest.py`, `tools/quick_check.py`. Adrian accepted the Minecraft EULA for the local test server on 2026-10-28 | | License for the companion mod and pack content | MIT for the companion mod (placeholder); nothing chosen for the rest | | Pack name and mod id | "Cobblemon Survival Pack" / `cobblesurvival` (working titles) | | Basalt deltas keeps the vanilla Nether biome's mobs and features: ghasts, magma cubes and striders can spawn in those caves, glowstone generates, and Cobblemon's Nether-themed habitats appear there (found by the field guide) | Settled by Phase 2: no vanilla mob can join (`world/OnlyPokemon.kt`), so no ghasts, magma cubes or striders; glowstone and Cobblemon's Nether-themed habitats stay | | Items that need the Nether have no source: Choice Band, Choice Specs (Nether Quartz, Blaze Rod), Master Ball (Netherite) | Phase 4 adds sources (veins, drops or recipe changes); listed by the field guide | | End portals stay openable; the Ender Dragon will be replaced by a Pokémon boss fight (Adrian, 2026-10-28; Phase 7) | Eyes of ender are obtainable: ender pearls drop from Abra, Kadabra and Alakazam (and trial chamber loot), blaze powder from Magmar, Magby, Houndour and others. The dragon is on the allowed list until its replacement exists; End city shulkers are blocked like every vanilla mob | | End access depends on the seed: a stronghold must start inside the border | `isle-26` has two; `tools/world_report.py` now fails a world without one |