From f7ea371862447e22c63c259ec2cd42d37fe41a02 Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 9 Nov 2026 13:03:50 +0100 Subject: [PATCH] Phase 9: crafted summons for the legendaries Adrian, 2026-11-09: legendaries, mythicals, Ultra Beasts and Paradox Pokemon come from crafted summons. 22 Sigils (Naganadel evolves from Poipole), one item told apart by custom data, from world/summons.json through tools/summons.py: late-game recipes in three tiers (Lv 70, 80, 90), each used in a fitting zone. A summoned legend fights as a boss (area attacks by level and type, arena 32, the Guardian's Ward) but can be caught; on capture it loses every boss mark. One at a time within 96 blocks. Quest book: a Legends chapter, one quest per Sigil (97 quests). JEI lists each Sigil as its own item, and Island Drops says where a legend is summoned; drops Cobblemon names but doesn't have (Ho-Oh's Sacred Ash) get no slot. Self-tests every_implemented_pokemon_is_obtainable (spawn, fossil or Sigil for every implemented species) and sigil_summons_a_catchable_legend. Self-tests and the island check pass. Co-Authored-By: Claude Opus 5.5 --- README.md | 7 + .../bosses/summon_articuno.json | 69 ++ .../bosses/summon_eternatus.json | 69 ++ .../cobblesurvival/bosses/summon_hooh.json | 69 ++ .../bosses/summon_ironleaves.json | 69 ++ .../cobblesurvival/bosses/summon_latias.json | 69 ++ .../cobblesurvival/bosses/summon_latios.json | 69 ++ .../cobblesurvival/bosses/summon_lugia.json | 69 ++ .../cobblesurvival/bosses/summon_mew.json | 69 ++ .../cobblesurvival/bosses/summon_mewtwo.json | 69 ++ .../cobblesurvival/bosses/summon_moltres.json | 69 ++ .../cobblesurvival/bosses/summon_poipole.json | 69 ++ .../bosses/summon_rayquaza.json | 69 ++ .../cobblesurvival/bosses/summon_regice.json | 69 ++ .../bosses/summon_regidrago.json | 69 ++ .../bosses/summon_regieleki.json | 69 ++ .../bosses/summon_regigigas.json | 69 ++ .../bosses/summon_regirock.json | 69 ++ .../bosses/summon_registeel.json | 69 ++ .../bosses/summon_walkingwake.json | 69 ++ .../cobblesurvival/bosses/summon_xerneas.json | 69 ++ .../cobblesurvival/bosses/summon_zapdos.json | 69 ++ .../cobblesurvival/bosses/summon_zarude.json | 69 ++ .../recipe/summon_articuno.json | 34 + .../recipe/summon_eternatus.json | 34 + .../cobblesurvival/recipe/summon_hooh.json | 34 + .../recipe/summon_ironleaves.json | 34 + .../cobblesurvival/recipe/summon_latias.json | 34 + .../cobblesurvival/recipe/summon_latios.json | 34 + .../cobblesurvival/recipe/summon_lugia.json | 34 + .../cobblesurvival/recipe/summon_mew.json | 34 + .../cobblesurvival/recipe/summon_mewtwo.json | 34 + .../cobblesurvival/recipe/summon_moltres.json | 34 + .../cobblesurvival/recipe/summon_poipole.json | 34 + .../recipe/summon_rayquaza.json | 34 + .../cobblesurvival/recipe/summon_regice.json | 34 + .../recipe/summon_regidrago.json | 34 + .../recipe/summon_regieleki.json | 34 + .../recipe/summon_regigigas.json | 34 + .../recipe/summon_regirock.json | 34 + .../recipe/summon_registeel.json | 34 + .../recipe/summon_walkingwake.json | 34 + .../cobblesurvival/recipe/summon_xerneas.json | 34 + .../cobblesurvival/recipe/summon_zapdos.json | 34 + .../cobblesurvival/recipe/summon_zarude.json | 34 + .../data/cobblesurvival/summons/articuno.json | 6 + .../cobblesurvival/summons/eternatus.json | 6 + .../data/cobblesurvival/summons/hooh.json | 6 + .../cobblesurvival/summons/ironleaves.json | 6 + .../data/cobblesurvival/summons/latias.json | 6 + .../data/cobblesurvival/summons/latios.json | 6 + .../data/cobblesurvival/summons/lugia.json | 6 + .../core/data/cobblesurvival/summons/mew.json | 6 + .../data/cobblesurvival/summons/mewtwo.json | 6 + .../data/cobblesurvival/summons/moltres.json | 6 + .../data/cobblesurvival/summons/poipole.json | 6 + .../data/cobblesurvival/summons/rayquaza.json | 6 + .../data/cobblesurvival/summons/regice.json | 6 + .../cobblesurvival/summons/regidrago.json | 6 + .../cobblesurvival/summons/regieleki.json | 6 + .../cobblesurvival/summons/regigigas.json | 6 + .../data/cobblesurvival/summons/regirock.json | 6 + .../cobblesurvival/summons/registeel.json | 6 + .../cobblesurvival/summons/walkingwake.json | 6 + .../data/cobblesurvival/summons/xerneas.json | 6 + .../data/cobblesurvival/summons/zapdos.json | 6 + .../data/cobblesurvival/summons/zarude.json | 6 + docs/balance.md | 1 + docs/checklists/phase-9.md | 3 + docs/design.md | 6 +- docs/quests.yaml | 21 +- .../dev/cobblesurvival/CobbleSurvival.kt | 2 + .../dev/cobblesurvival/jei/DropSources.kt | 8 +- .../dev/cobblesurvival/jei/DropSourcesJei.kt | 39 +- .../dev/cobblesurvival/selftest/QuestTests.kt | 75 ++ .../dev/cobblesurvival/summon/Summons.kt | 145 ++++ .../cobblesurvival/world/SpecialPokemon.kt | 5 +- .../cobblesurvival/jei/drop_sources.json | 358 +++++++++ .../assets/cobblesurvival/lang/en_us.json | 3 +- .../models/item/summon_sigil.json | 6 + .../textures/item/summon_sigil.png | Bin 0 -> 265 bytes .../ftbquests/quests/chapters/base.snbt | 2 +- .../ftbquests/quests/chapters/legends.snbt | 733 ++++++++++++++++++ .../ftbquests/quests/chapters/machines.snbt | 2 +- pack/config/ftbquests/quests/lang/en_us.snbt | 96 +++ pack/index.toml | 10 +- pack/pack.toml | 2 +- tools/jei_sources.py | 14 +- tools/quests.py | 22 +- tools/summons.py | 104 +++ tools/zone_bosses.py | 8 + world/summons.json | 32 + 92 files changed, 4081 insertions(+), 21 deletions(-) create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_articuno.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_eternatus.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_hooh.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_ironleaves.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_latias.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_latios.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_lugia.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_mew.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_mewtwo.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_moltres.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_poipole.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_rayquaza.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_regice.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_regidrago.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_regieleki.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_regigigas.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_regirock.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_registeel.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_walkingwake.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_xerneas.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_zapdos.json create mode 100644 datapacks/core/data/cobblesurvival/bosses/summon_zarude.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_articuno.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_eternatus.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_hooh.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_ironleaves.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_latias.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_latios.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_lugia.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_mew.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_mewtwo.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_moltres.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_poipole.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_rayquaza.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_regice.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_regidrago.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_regieleki.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_regigigas.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_regirock.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_registeel.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_walkingwake.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_xerneas.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_zapdos.json create mode 100644 datapacks/core/data/cobblesurvival/recipe/summon_zarude.json create mode 100644 datapacks/core/data/cobblesurvival/summons/articuno.json create mode 100644 datapacks/core/data/cobblesurvival/summons/eternatus.json create mode 100644 datapacks/core/data/cobblesurvival/summons/hooh.json create mode 100644 datapacks/core/data/cobblesurvival/summons/ironleaves.json create mode 100644 datapacks/core/data/cobblesurvival/summons/latias.json create mode 100644 datapacks/core/data/cobblesurvival/summons/latios.json create mode 100644 datapacks/core/data/cobblesurvival/summons/lugia.json create mode 100644 datapacks/core/data/cobblesurvival/summons/mew.json create mode 100644 datapacks/core/data/cobblesurvival/summons/mewtwo.json create mode 100644 datapacks/core/data/cobblesurvival/summons/moltres.json create mode 100644 datapacks/core/data/cobblesurvival/summons/poipole.json create mode 100644 datapacks/core/data/cobblesurvival/summons/rayquaza.json create mode 100644 datapacks/core/data/cobblesurvival/summons/regice.json create mode 100644 datapacks/core/data/cobblesurvival/summons/regidrago.json create mode 100644 datapacks/core/data/cobblesurvival/summons/regieleki.json create mode 100644 datapacks/core/data/cobblesurvival/summons/regigigas.json create mode 100644 datapacks/core/data/cobblesurvival/summons/regirock.json create mode 100644 datapacks/core/data/cobblesurvival/summons/registeel.json create mode 100644 datapacks/core/data/cobblesurvival/summons/walkingwake.json create mode 100644 datapacks/core/data/cobblesurvival/summons/xerneas.json create mode 100644 datapacks/core/data/cobblesurvival/summons/zapdos.json create mode 100644 datapacks/core/data/cobblesurvival/summons/zarude.json create mode 100644 mod/src/main/kotlin/dev/cobblesurvival/summon/Summons.kt create mode 100644 mod/src/main/resources/assets/cobblesurvival/models/item/summon_sigil.json create mode 100644 mod/src/main/resources/assets/cobblesurvival/textures/item/summon_sigil.png create mode 100644 pack/config/ftbquests/quests/chapters/legends.snbt create mode 100644 tools/summons.py create mode 100644 world/summons.json diff --git a/README.md b/README.md index 3c10f0b..97419b7 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,13 @@ python3 tools/quests.py ``` Generates the quest book (Phase 8) from `docs/quests.yaml` into `pack/config/ftbquests/quests/`, and the hidden advancements quests check. `tools/build.py` runs it. `--pull ` first copies node positions moved in the in-game editor back into `docs/quests.yaml` (`tools/prism_instance.py` does it from the instance before each sync); `--check` fails if the generated files are out of date. +## Legend summons + +```bash +python3 tools/summons.py && python3 tools/zone_bosses.py +``` +Writes the Sigils' recipes and data and their fights (Phase 9) from `world/summons.json`. Then run `tools/jei_sources.py` and rebuild. + ## JEI drop sources ```bash diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_articuno.json b/datapacks/core/data/cobblesurvival/bosses/summon_articuno.json new file mode 100644 index 0000000..34bbed7 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_articuno.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "articuno", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_eternatus.json b/datapacks/core/data/cobblesurvival/bosses/summon_eternatus.json new file mode 100644 index 0000000..b94ec07 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_eternatus.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "eternatus", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "poison", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "poison", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "poison", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "poison", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "poison", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_hooh.json b/datapacks/core/data/cobblesurvival/bosses/summon_hooh.json new file mode 100644 index 0000000..30bdb5a --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_hooh.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "hooh", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "fire", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "fire", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "fire", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "fire", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "fire", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_ironleaves.json b/datapacks/core/data/cobblesurvival/bosses/summon_ironleaves.json new file mode 100644 index 0000000..afae2f9 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_ironleaves.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "ironleaves", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_latias.json b/datapacks/core/data/cobblesurvival/bosses/summon_latias.json new file mode 100644 index 0000000..4f1c57d --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_latias.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "latias", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_latios.json b/datapacks/core/data/cobblesurvival/bosses/summon_latios.json new file mode 100644 index 0000000..bf4cdf2 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_latios.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "latios", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_lugia.json b/datapacks/core/data/cobblesurvival/bosses/summon_lugia.json new file mode 100644 index 0000000..9ccf0cb --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_lugia.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "lugia", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_mew.json b/datapacks/core/data/cobblesurvival/bosses/summon_mew.json new file mode 100644 index 0000000..2b4e1d8 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_mew.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "mew", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_mewtwo.json b/datapacks/core/data/cobblesurvival/bosses/summon_mewtwo.json new file mode 100644 index 0000000..3b4140c --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_mewtwo.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "mewtwo", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_moltres.json b/datapacks/core/data/cobblesurvival/bosses/summon_moltres.json new file mode 100644 index 0000000..93af569 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_moltres.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "moltres", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "fire", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "fire", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "fire", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "fire", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "fire", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_poipole.json b/datapacks/core/data/cobblesurvival/bosses/summon_poipole.json new file mode 100644 index 0000000..a3ae294 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_poipole.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "poipole", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "poison", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "poison", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "poison", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "poison", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "poison", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_rayquaza.json b/datapacks/core/data/cobblesurvival/bosses/summon_rayquaza.json new file mode 100644 index 0000000..5b0d3f9 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_rayquaza.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "rayquaza", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 15, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_regice.json b/datapacks/core/data/cobblesurvival/bosses/summon_regice.json new file mode 100644 index 0000000..c5b753e --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_regice.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "regice", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "ice", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_regidrago.json b/datapacks/core/data/cobblesurvival/bosses/summon_regidrago.json new file mode 100644 index 0000000..c19e501 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_regidrago.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "regidrago", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_regieleki.json b/datapacks/core/data/cobblesurvival/bosses/summon_regieleki.json new file mode 100644 index 0000000..e6fe563 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_regieleki.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "regieleki", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_regigigas.json b/datapacks/core/data/cobblesurvival/bosses/summon_regigigas.json new file mode 100644 index 0000000..8a4fa12 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_regigigas.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "regigigas", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_regirock.json b/datapacks/core/data/cobblesurvival/bosses/summon_regirock.json new file mode 100644 index 0000000..9f6e1dc --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_regirock.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "regirock", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_registeel.json b/datapacks/core/data/cobblesurvival/bosses/summon_registeel.json new file mode 100644 index 0000000..e30dabb --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_registeel.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "registeel", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_walkingwake.json b/datapacks/core/data/cobblesurvival/bosses/summon_walkingwake.json new file mode 100644 index 0000000..cb12b85 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_walkingwake.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "walkingwake", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_xerneas.json b/datapacks/core/data/cobblesurvival/bosses/summon_xerneas.json new file mode 100644 index 0000000..e5c1bba --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_xerneas.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "xerneas", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_zapdos.json b/datapacks/core/data/cobblesurvival/bosses/summon_zapdos.json new file mode 100644 index 0000000..883ec03 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_zapdos.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "zapdos", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 12, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/bosses/summon_zarude.json b/datapacks/core/data/cobblesurvival/bosses/summon_zarude.json new file mode 100644 index 0000000..a5e2aa0 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/bosses/summon_zarude.json @@ -0,0 +1,69 @@ +{ + "_comment": "Generated by tools/zone_bosses.py; do not edit by hand. See docs/design.md (Phase 7) for the format.", + "summon": "zarude", + "arena": 32, + "patterns": [ + { + "shape": "circle", + "radius": 3.5, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 112, + "offset": 40 + } + }, + { + "shape": "line", + "length": 14, + "width": 2, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 160, + "offset": 100 + } + }, + { + "shape": "cone", + "length": 10, + "angle": 70, + "aim": "player", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "every_ticks": 200, + "offset": 160 + } + }, + { + "shape": "ring", + "inner": 3, + "outer": 9, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.5 + } + }, + { + "shape": "ring", + "inner": 2, + "outer": 12, + "aim": "boss", + "telegraph_ticks": 30, + "damage": 13, + "effect": "none", + "trigger": { + "below_health": 0.25 + } + } + ] +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_articuno.json b/datapacks/core/data/cobblesurvival/recipe/summon_articuno.json new file mode 100644 index 0000000..06acdb9 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_articuno.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:ice_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "articuno" + }, + "minecraft:item_name": "\"Articuno Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Frostpeak Range to summon a Lv 70 Articuno\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_eternatus.json b/datapacks/core/data/cobblesurvival/recipe/summon_eternatus.json new file mode 100644 index 0000000..e28af89 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_eternatus.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:ender_eye" + }, + "R": { + "item": "minecraft:netherite_ingot" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "eternatus" + }, + "minecraft:item_name": "\"Eternatus Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in The End to summon a Lv 90 Eternatus\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_hooh.json b/datapacks/core/data/cobblesurvival/recipe/summon_hooh.json new file mode 100644 index 0000000..91465dd --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_hooh.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:sun_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "hooh" + }, + "minecraft:item_name": "\"Ho-Oh Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Highland Meadows to summon a Lv 80 Ho-Oh\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_ironleaves.json b/datapacks/core/data/cobblesurvival/recipe/summon_ironleaves.json new file mode 100644 index 0000000..0c01645 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_ironleaves.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:leaf_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "ironleaves" + }, + "minecraft:item_name": "\"Iron Leaves Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Verdant Jungle to summon a Lv 70 Iron Leaves\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_latias.json b/datapacks/core/data/cobblesurvival/recipe/summon_latias.json new file mode 100644 index 0000000..1fd5155 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_latias.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:sun_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "latias" + }, + "minecraft:item_name": "\"Latias Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Highland Meadows to summon a Lv 70 Latias\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_latios.json b/datapacks/core/data/cobblesurvival/recipe/summon_latios.json new file mode 100644 index 0000000..9041358 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_latios.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:moon_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "latios" + }, + "minecraft:item_name": "\"Latios Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Highland Meadows to summon a Lv 70 Latios\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_lugia.json b/datapacks/core/data/cobblesurvival/recipe/summon_lugia.json new file mode 100644 index 0000000..fc1ce42 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_lugia.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:water_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "lugia" + }, + "minecraft:item_name": "\"Lugia Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Deep Sea to summon a Lv 80 Lugia\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_mew.json b/datapacks/core/data/cobblesurvival/recipe/summon_mew.json new file mode 100644 index 0000000..643a757 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_mew.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:shiny_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "mew" + }, + "minecraft:item_name": "\"Mew Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Verdant Jungle to summon a Lv 80 Mew\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_mewtwo.json b/datapacks/core/data/cobblesurvival/recipe/summon_mewtwo.json new file mode 100644 index 0000000..7f1c26a --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_mewtwo.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:dusk_stone" + }, + "R": { + "item": "minecraft:netherite_ingot" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "mewtwo" + }, + "minecraft:item_name": "\"Mewtwo Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Deep Dark to summon a Lv 90 Mewtwo\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_moltres.json b/datapacks/core/data/cobblesurvival/recipe/summon_moltres.json new file mode 100644 index 0000000..1bc0ecf --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_moltres.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:fire_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "moltres" + }, + "minecraft:item_name": "\"Moltres Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Basalt Furnace to summon a Lv 70 Moltres\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_poipole.json b/datapacks/core/data/cobblesurvival/recipe/summon_poipole.json new file mode 100644 index 0000000..bbb899e --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_poipole.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:ender_pearl" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "poipole" + }, + "minecraft:item_name": "\"Poipole Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in The End to summon a Lv 70 Poipole\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_rayquaza.json b/datapacks/core/data/cobblesurvival/recipe/summon_rayquaza.json new file mode 100644 index 0000000..6022378 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_rayquaza.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:emerald_block" + }, + "R": { + "item": "minecraft:netherite_ingot" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "rayquaza" + }, + "minecraft:item_name": "\"Rayquaza Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Frostpeak Range to summon a Lv 90 Rayquaza\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_regice.json b/datapacks/core/data/cobblesurvival/recipe/summon_regice.json new file mode 100644 index 0000000..4e5f16f --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_regice.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:icy_rock" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "regice" + }, + "minecraft:item_name": "\"Regice Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Frostpeak Range to summon a Lv 70 Regice\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_regidrago.json b/datapacks/core/data/cobblesurvival/recipe/summon_regidrago.json new file mode 100644 index 0000000..02c7f96 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_regidrago.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:gold_block" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "regidrago" + }, + "minecraft:item_name": "\"Regidrago Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Sunscorch to summon a Lv 70 Regidrago\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_regieleki.json b/datapacks/core/data/cobblesurvival/recipe/summon_regieleki.json new file mode 100644 index 0000000..c48fca4 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_regieleki.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:redstone_block" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "regieleki" + }, + "minecraft:item_name": "\"Regieleki Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Westmarch to summon a Lv 70 Regieleki\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_regigigas.json b/datapacks/core/data/cobblesurvival/recipe/summon_regigigas.json new file mode 100644 index 0000000..62a3a4a --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_regigigas.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:echo_shard" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "regigigas" + }, + "minecraft:item_name": "\"Regigigas Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Deep Dark to summon a Lv 80 Regigigas\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_regirock.json b/datapacks/core/data/cobblesurvival/recipe/summon_regirock.json new file mode 100644 index 0000000..d9646ff --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_regirock.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:amethyst_shard" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "regirock" + }, + "minecraft:item_name": "\"Regirock Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Dripstone Depths to summon a Lv 70 Regirock\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_registeel.json b/datapacks/core/data/cobblesurvival/recipe/summon_registeel.json new file mode 100644 index 0000000..aa1063b --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_registeel.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:iron_block" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "registeel" + }, + "minecraft:item_name": "\"Registeel Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Deep Dark to summon a Lv 70 Registeel\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_walkingwake.json b/datapacks/core/data/cobblesurvival/recipe/summon_walkingwake.json new file mode 100644 index 0000000..644650e --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_walkingwake.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:water_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "walkingwake" + }, + "minecraft:item_name": "\"Walking Wake Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Mangrove Mire to summon a Lv 70 Walking Wake\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_xerneas.json b/datapacks/core/data/cobblesurvival/recipe/summon_xerneas.json new file mode 100644 index 0000000..3fd810a --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_xerneas.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:leaf_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "xerneas" + }, + "minecraft:item_name": "\"Xerneas Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Verdant Jungle to summon a Lv 80 Xerneas\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_zapdos.json b/datapacks/core/data/cobblesurvival/recipe/summon_zapdos.json new file mode 100644 index 0000000..0d58bce --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_zapdos.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "cobblemon:thunder_stone" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:netherite_ingot" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "zapdos" + }, + "minecraft:item_name": "\"Zapdos Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Westmarch to summon a Lv 70 Zapdos\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/recipe/summon_zarude.json b/datapacks/core/data/cobblesurvival/recipe/summon_zarude.json new file mode 100644 index 0000000..2e05410 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/recipe/summon_zarude.json @@ -0,0 +1,34 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ + "MRM", + "RCR", + "MRM" + ], + "key": { + "M": { + "item": "minecraft:redstone_block" + }, + "R": { + "item": "minecraft:diamond" + }, + "C": { + "item": "minecraft:nether_star" + } + }, + "result": { + "id": "cobblesurvival:summon_sigil", + "count": 1, + "components": { + "minecraft:custom_data": { + "cobblesurvival_summon": "zarude" + }, + "minecraft:item_name": "\"Zarude Sigil\"", + "minecraft:lore": [ + "{\"text\": \"Use in Verdant Jungle to summon a Lv 80 Zarude\", \"color\": \"gray\", \"italic\": false}" + ] + } + } +} diff --git a/datapacks/core/data/cobblesurvival/summons/articuno.json b/datapacks/core/data/cobblesurvival/summons/articuno.json new file mode 100644 index 0000000..b942284 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/articuno.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "articuno", + "level": 70, + "zone": "frostpeak" +} diff --git a/datapacks/core/data/cobblesurvival/summons/eternatus.json b/datapacks/core/data/cobblesurvival/summons/eternatus.json new file mode 100644 index 0000000..65da57d --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/eternatus.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "eternatus", + "level": 90, + "zone": "the_end" +} diff --git a/datapacks/core/data/cobblesurvival/summons/hooh.json b/datapacks/core/data/cobblesurvival/summons/hooh.json new file mode 100644 index 0000000..3a4a95d --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/hooh.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "hooh", + "level": 80, + "zone": "highlands" +} diff --git a/datapacks/core/data/cobblesurvival/summons/ironleaves.json b/datapacks/core/data/cobblesurvival/summons/ironleaves.json new file mode 100644 index 0000000..d11dceb --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/ironleaves.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "ironleaves", + "level": 70, + "zone": "jungle" +} diff --git a/datapacks/core/data/cobblesurvival/summons/latias.json b/datapacks/core/data/cobblesurvival/summons/latias.json new file mode 100644 index 0000000..53a889d --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/latias.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "latias", + "level": 70, + "zone": "highlands" +} diff --git a/datapacks/core/data/cobblesurvival/summons/latios.json b/datapacks/core/data/cobblesurvival/summons/latios.json new file mode 100644 index 0000000..bf92c22 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/latios.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "latios", + "level": 70, + "zone": "highlands" +} diff --git a/datapacks/core/data/cobblesurvival/summons/lugia.json b/datapacks/core/data/cobblesurvival/summons/lugia.json new file mode 100644 index 0000000..38ad4f6 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/lugia.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "lugia", + "level": 80, + "zone": "deep_sea" +} diff --git a/datapacks/core/data/cobblesurvival/summons/mew.json b/datapacks/core/data/cobblesurvival/summons/mew.json new file mode 100644 index 0000000..5ab4a77 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/mew.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "mew", + "level": 80, + "zone": "jungle" +} diff --git a/datapacks/core/data/cobblesurvival/summons/mewtwo.json b/datapacks/core/data/cobblesurvival/summons/mewtwo.json new file mode 100644 index 0000000..215d568 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/mewtwo.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "mewtwo", + "level": 90, + "zone": "deep_dark" +} diff --git a/datapacks/core/data/cobblesurvival/summons/moltres.json b/datapacks/core/data/cobblesurvival/summons/moltres.json new file mode 100644 index 0000000..21f5de9 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/moltres.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "moltres", + "level": 70, + "zone": "basalt" +} diff --git a/datapacks/core/data/cobblesurvival/summons/poipole.json b/datapacks/core/data/cobblesurvival/summons/poipole.json new file mode 100644 index 0000000..759ed82 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/poipole.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "poipole", + "level": 70, + "zone": "the_end" +} diff --git a/datapacks/core/data/cobblesurvival/summons/rayquaza.json b/datapacks/core/data/cobblesurvival/summons/rayquaza.json new file mode 100644 index 0000000..48e807b --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/rayquaza.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "rayquaza", + "level": 90, + "zone": "frostpeak" +} diff --git a/datapacks/core/data/cobblesurvival/summons/regice.json b/datapacks/core/data/cobblesurvival/summons/regice.json new file mode 100644 index 0000000..03af569 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/regice.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "regice", + "level": 70, + "zone": "frostpeak" +} diff --git a/datapacks/core/data/cobblesurvival/summons/regidrago.json b/datapacks/core/data/cobblesurvival/summons/regidrago.json new file mode 100644 index 0000000..a27c7c3 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/regidrago.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "regidrago", + "level": 70, + "zone": "sunscorch" +} diff --git a/datapacks/core/data/cobblesurvival/summons/regieleki.json b/datapacks/core/data/cobblesurvival/summons/regieleki.json new file mode 100644 index 0000000..8465e66 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/regieleki.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "regieleki", + "level": 70, + "zone": "westmarch" +} diff --git a/datapacks/core/data/cobblesurvival/summons/regigigas.json b/datapacks/core/data/cobblesurvival/summons/regigigas.json new file mode 100644 index 0000000..b784ec3 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/regigigas.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "regigigas", + "level": 80, + "zone": "deep_dark" +} diff --git a/datapacks/core/data/cobblesurvival/summons/regirock.json b/datapacks/core/data/cobblesurvival/summons/regirock.json new file mode 100644 index 0000000..602af1b --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/regirock.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "regirock", + "level": 70, + "zone": "dripstone" +} diff --git a/datapacks/core/data/cobblesurvival/summons/registeel.json b/datapacks/core/data/cobblesurvival/summons/registeel.json new file mode 100644 index 0000000..091ddd4 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/registeel.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "registeel", + "level": 70, + "zone": "deep_dark" +} diff --git a/datapacks/core/data/cobblesurvival/summons/walkingwake.json b/datapacks/core/data/cobblesurvival/summons/walkingwake.json new file mode 100644 index 0000000..412249b --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/walkingwake.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "walkingwake", + "level": 70, + "zone": "mire" +} diff --git a/datapacks/core/data/cobblesurvival/summons/xerneas.json b/datapacks/core/data/cobblesurvival/summons/xerneas.json new file mode 100644 index 0000000..be848d6 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/xerneas.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "xerneas", + "level": 80, + "zone": "jungle" +} diff --git a/datapacks/core/data/cobblesurvival/summons/zapdos.json b/datapacks/core/data/cobblesurvival/summons/zapdos.json new file mode 100644 index 0000000..9673f99 --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/zapdos.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "zapdos", + "level": 70, + "zone": "westmarch" +} diff --git a/datapacks/core/data/cobblesurvival/summons/zarude.json b/datapacks/core/data/cobblesurvival/summons/zarude.json new file mode 100644 index 0000000..53f3a3f --- /dev/null +++ b/datapacks/core/data/cobblesurvival/summons/zarude.json @@ -0,0 +1,6 @@ +{ + "_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.", + "species": "zarude", + "level": 80, + "zone": "jungle" +} diff --git a/docs/balance.md b/docs/balance.md index 9be4fbe..2c5820f 100644 --- a/docs/balance.md +++ b/docs/balance.md @@ -54,6 +54,7 @@ Every tunable lives in a config file or in this table. **Placeholder** means gue | Regenerators breakable by players | no; explosions don't hurt veins | Fine for now (Adrian, 2026-10-30) | `tools/zone_ores.py` → `global.json` | Otherwise a player could remove a vein for good | | Respawn block (regenerator) | the player's aim passes through it unless sneaking, so ore behind it can be mined; no collision (the mod's own); glass about 9 % opaque with a 27 % edge (was stained glass, about 50 %) | Adrian, 2026-11-09 | `mod/src/main/java/.../RegeneratorBlockMixin.java`; `pack/kubejs/assets/regenerating_ore_veins/` | Sneak-use still shows its info | | Homeless lines | 31 lines with no matching biome on the island: rare bucket, weight 10, at the zone's levels, anywhere in the zone | Placeholder (Adrian, 2026-11-09: rare, in fitting zones) | `world/zones.json` → `homeless` → `tools/zone_spawns.py` | Which zone each lives in is listed there and in `docs/zone-species.md` | +| Legend summons | 22 Sigils; tier 1 Lv 70: 4 zone material, 4 diamonds, netherite ingot; tier 2 Lv 80: 4 material, 4 diamonds, nether star; tier 3 Lv 90 (Rayquaza, Mewtwo, Eternatus): 4 material, 4 netherite ingots, nether star; one at a time within 96 blocks; fight as a boss with the Guardian's Ward, arena 32 | Placeholder (Adrian, 2026-11-09: crafted summons) | `world/summons.json` → `tools/summons.py`, `tools/zone_bosses.py` (`SUMMON_ARENA`) | Nether stars come from the Sacrifice Altar at Lv 100 | | Vein Locator | compass + copper ingot; 75 % chance to break per use (the mod's default) | Fine for now (Adrian, 2026-10-30) | `tools/zone_ores.py` → `vein_locator.json` | The mod's default recipe needs netherite | | Drops for mobs the island lacks | Blaze Rod: Carkol 10 %, Torkoal 25 % ×1–2; Ghast Tear: Drifloon 5 %, Dusclops 10 %; Breeze Rod: Whimsicott 10 %; Shulker Shell: Shuckle 10 %, Cloyster 10 %; Nether Star: Charizard 5 % | Fine for now (Adrian, 2026-10-30) | `datapacks/core/data/cobblesurvival/species_additions/` | Every drop and what consumes it: `docs/pokemon-drops.md` (`tools/drop_audit.py`) | | Cobblebase skill gates | start: gatherer, harvester, irrigator, producer, furnace fuel, water fill, extinguisher, scout, guard; Button Isle: fishing, forager, botanist, collector, snow fill, brewing fuel, craftsman; Southwold: pharmacist, scholar, mentor (passive XP), lava fill; Westmarch: mining, excavator, architect, armorer, diving; Verdant Jungle: alchemist, trainer, smith, prospector, growth aura; Highland Meadows: speed, jump, haste, night vision, water breathing, saturation buffs; Mangrove Mire: resistance, strength buffs; Sunscorch: friend recruiter, aura boost; Frostpeak: healer, lucky charm; Deep Dark: breeder, egg hatcher | Placeholder | `datapacks/core/data/cobblesurvival/progression_gates/base.json` → `cobblebase_skills` | A zone means its soft gate, checked on the pastured Pokémon (see "Soft gate levels and materials") | diff --git a/docs/checklists/phase-9.md b/docs/checklists/phase-9.md index 425aa72..ec4b381 100644 --- a/docs/checklists/phase-9.md +++ b/docs/checklists/phase-9.md @@ -8,12 +8,15 @@ - **Ore veins:** rarer nodes and wider vein spacing, one block larger in radius. Needs a new island: the save "Cobblemon Survival Island (new veins)" has it. Your other saves keep their old veins. - **Respawn block:** much fainter glass; you aim through it unless sneaking. Automated: `regenerator_lets_the_aim_through`. +- **Legend summons:** 22 Sigils, crafted from late-game materials (search "Sigil" in JEI). Used in the zone written on one, it summons a catchable legend that fights like a boss. Automated: `every_implemented_pokemon_is_obtainable`, `sigil_summons_a_catchable_legend`. The Legends chapter in the quest book lists them. - **Left-out Pokémon:** 31 lines (67 species) spawn rarely in fitting zones (`docs/zone-species.md`). Automated: `spawn_pools_are_zoned`, `drop_sources_match_the_server`. ## In game (Prism instance) - [ ] In the new save, mine into an ore node or vein: the mined blocks turn into faint glass, and you can keep aiming and mining the ore behind them. Sneak and use one: its info still shows. - [ ] Do nodes and veins feel right: fewer, bigger? The island has 1.5–3× the old ore; tell me if that's too much. +- [ ] Give yourself a Sigil from JEI (creative) and use it outside its zone: it refuses and says where it works. Use it in its zone: the legend appears with a sound and particles. Fight it: area attacks, at most a quarter of its HP per attack. Weaken it and catch it; afterwards it's an ordinary Pokémon in your party (no quarter-HP limit in later battles). +- [ ] Is a Sigil the right cost, and the fight the right difficulty? - [ ] Spend time in Frostpeak or the Deep Sea: now and then a Beartic, Mamoswine, Empoleon or Walrein turns up. JEI's Island Drops page names the zone. ## Ask Adrian diff --git a/docs/design.md b/docs/design.md index e6c642e..9d8ce5d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -329,7 +329,11 @@ Adrian's choices: Pokémon with no biome on the island become rare spawns in fit **Every implemented Pokémon is obtainable** (roadmap). Of 905 implemented species, 95 were out of reach by wild spawn or by evolving a wild line (2026-11-09): - 5 fossils (Aerodactyl, Arctovish, Arctozolt, Dracovish, Dracozolt): already obtainable. The field guide lists their fossils as found on the island, and the fossil machines are craftable. - 67 species in 31 lines that spawn only in biomes the island lacks, or not at all (Rotom): each line gets a home in the closest-fitting zone (`world/zones.json` → `homeless`), as a rare spawn anywhere in that zone at its levels. Conditions tied to the missing biomes are dropped. Existing spawns and homes don't move. They show in `docs/zone-species.md` and in JEI. -- 23 legendaries, mythicals, Ultra Beasts and Paradox Pokémon, with no spawn data: crafted summons (to do). +- 23 legendaries, mythicals, Ultra Beasts and Paradox Pokémon, with no spawn data: crafted summons (below). Naganadel comes from evolving Poipole, so 22 Sigils. + +**Crafted summons** (Adrian, 2026-11-09). Each legend has a Sigil (`cobblesurvival:summon_sigil`, one item told apart by its custom data), crafted from four of a zone material, four of a ring item and a core: tier 1 (Lv 70) diamond and netherite ingot, tier 2 (Lv 80) diamond and nether star, tier 3 (Lv 90: Rayquaza, Mewtwo, Eternatus) netherite ingots and nether star. Used in the zone written on it, it summons a wild one six blocks ahead, one at a time within 96 blocks. It fights like a boss (`bosses/summon_.json`: area attacks by level and type, a 32-block arena, the Guardian's Ward, so at most a quarter of its HP per attack), except it can be caught: the pack's usual catching rules apply. On capture it loses every boss mark and is an ordinary Pokémon. If it's knocked out, the Sigil is spent; if the fight is lost it heals and stays. Which zone, tier and material: `world/summons.json` → `tools/summons.py` (recipes and data, with the name and "use in" text on the recipe's result so the client needs no server data). The quest book has a Legends chapter with a quest per Sigil; JEI lists each Sigil as its own item, and the Island Drops pages say where a legend is summoned. Moltres, Regigigas and Eternatus are guardians too: those stay uncatchable, the summoned ones are separate. + +Self-tests: `every_implemented_pokemon_is_obtainable` (every implemented species' line spawns, comes from a fossil or has a Sigil; a new Cobblemon version adding species fails it until they have a home), `sigil_summons_a_catchable_legend`. **Ore veins** (Adrian, 2026-11-09: more sporadic but bigger). Nodes are about 40 % rarer and veins about 30 % further apart, each one block larger in radius; the bigger size outweighs the spacing, so the island holds 1.5–3× the ore. Veins are laid down when chunks generate, so this needs a newly generated island. diff --git a/docs/quests.yaml b/docs/quests.yaml index 144280f..b0a42c5 100644 --- a/docs/quests.yaml +++ b/docs/quests.yaml @@ -15,7 +15,8 @@ # checkmark: true the player ticks it # A task may also have a title. A chapter with `zone: ` starts with the quests of `zone_chapter`, where # {zone}, {low}, {high}, {hazard_text}, {guardian}, {guardian_level}, {drops} and {ore_names} come from the -# zone's data; a template quest with `when:` is left out of zones that lack the thing named. +# zone's data; a template quest with `when:` is left out of zones that lack the thing named. A chapter with +# `summons: true` adds a quest per Sigil (world/summons.json), `columns: ` lays those out in a grid. book: title: Cobblemon Survival @@ -260,6 +261,24 @@ chapters: - event: end_boss title: Beat Eternatus + - id: legends + title: Legends + subtitle: What never spawns on its own + icon: cobblesurvival:summon_sigil + summons: true + columns: 6 + quests: + - id: about + title: Calling a Legend + icon: cobblesurvival:summon_sigil + size: 1.5 + text: | + Legendary Pokémon don't spawn on their own. Each has a Sigil you can craft from late-game materials: look "Sigil" up in JEI. Using one in the zone written on it summons the Pokémon. + It fights like a guardian, area attacks and all, but this one can be caught. If it's knocked out, the Sigil is lost. + tasks: + - checkmark: true + title: Read this + - id: machines title: Pokémon Machines subtitle: Five machines, each powered by a Pokémon inside it diff --git a/mod/src/main/kotlin/dev/cobblesurvival/CobbleSurvival.kt b/mod/src/main/kotlin/dev/cobblesurvival/CobbleSurvival.kt index 3a10077..314e7a3 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/CobbleSurvival.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/CobbleSurvival.kt @@ -22,6 +22,7 @@ import dev.cobblesurvival.world.OnlyPokemon import dev.cobblesurvival.world.SharedLoot import dev.cobblesurvival.world.SpecialPokemon import dev.cobblesurvival.progress.QuestEvents +import dev.cobblesurvival.summon.Summons import dev.cobblesurvival.zone.GuardianBars import dev.cobblesurvival.zone.Guardians import dev.cobblesurvival.zone.InZoneCondition @@ -70,6 +71,7 @@ class CobbleSurvival { MOD_BUS.addListener(::onAddPackFinders) InZoneCondition.register(MOD_BUS) Machines.register(MOD_BUS) + Summons.register(MOD_BUS) NeoForge.EVENT_BUS.addListener(::onRegisterCommands) NeoForge.EVENT_BUS.addListener(::onServerStarted) NeoForge.EVENT_BUS.addListener(::onPortalSpawn) diff --git a/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSources.kt b/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSources.kt index d47262b..e33e30b 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSources.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSources.kt @@ -17,7 +17,9 @@ object DropSources { class Reward(val item: String, val count: Int) class Spawned(val zone: String, val levels: String) class LineMember(val zone: String, val species: String) - class Pokemon(val species: String, val drops: List, val zones: List, val line: List?) + class SummonedAt(val zone: String, val level: Int) + class Summon(val species: String, val name: String, val zone: String, val level: Int) + class Pokemon(val species: String, val drops: List, val zones: List, val line: List?, val summon: SummonedAt?) class SpawnDrops(val species: String, val zone: String, val drops: List) class Guardian(val zone: String, val species: String, val level: Int, val drops: List, val respawn_hours: Double) class EndBoss(val species: String, val level: Int, val xp_first_win: Int, val xp: Int, val respawn_hours: Double, val first_win: List) @@ -33,6 +35,7 @@ object DropSources { class Data( val zones: Map, val pokemon: List, + val summons: List, val spawn_drops: List, val guardians: List, val end_boss: EndBoss, @@ -56,6 +59,9 @@ object DropSources { return ItemStack(found, count.coerceIn(1, 99)) } + /** Cobblemon's own tables can name an item it doesn't have (Ho-Oh's Sacred Ash in 1.8.4): it never drops, so it gets no slot. */ + fun exists(item: String): Boolean = BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(item)) + /** The most a drop gives: "0-2" → 2. */ fun most(count: String?): Int = count?.substringAfter('-')?.toIntOrNull() ?: 1 } diff --git a/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSourcesJei.kt b/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSourcesJei.kt index 3ce2c1e..6c239a7 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSourcesJei.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/jei/DropSourcesJei.kt @@ -4,25 +4,34 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties import com.cobblemon.mod.common.api.pokemon.PokemonSpecies import com.cobblemon.mod.common.item.PokemonItem import dev.cobblesurvival.CobbleSurvival +import dev.cobblesurvival.summon.Summons import mezz.jei.api.IModPlugin import mezz.jei.api.JeiPlugin import mezz.jei.api.gui.builder.IRecipeLayoutBuilder import mezz.jei.api.gui.drawable.IDrawable import mezz.jei.api.gui.ingredient.IRecipeSlotsView +import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter +import mezz.jei.api.ingredients.subtypes.UidContext import mezz.jei.api.recipe.IFocusGroup import mezz.jei.api.recipe.RecipeIngredientRole import mezz.jei.api.recipe.RecipeType import mezz.jei.api.recipe.category.IRecipeCategory import mezz.jei.api.registration.IRecipeCatalystRegistration import mezz.jei.api.registration.IRecipeCategoryRegistration +import mezz.jei.api.registration.IExtraIngredientRegistration import mezz.jei.api.registration.IRecipeRegistration +import mezz.jei.api.registration.ISubtypeRegistration import net.minecraft.ChatFormatting import net.minecraft.client.Minecraft import net.minecraft.client.gui.Font import net.minecraft.client.gui.GuiGraphics +import net.minecraft.core.component.DataComponents +import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.component.CustomData +import net.minecraft.world.item.component.ItemLore /** * The pack's own JEI pages (roadmap Phase 8; Adrian, 2026-11-02: every item that can drop gets a page naming where it @@ -58,6 +67,19 @@ class DropSourcesJei : IModPlugin { override fun getPluginUid(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "drop_sources") + /** Each Sigil counts as an item of its own, by the species in its custom data. */ + override fun registerItemSubtypes(registration: ISubtypeRegistration) { + registration.registerSubtypeInterpreter(Summons.SIGIL.get(), object : ISubtypeInterpreter { + override fun getSubtypeData(stack: ItemStack, context: UidContext): Any? = Summons.speciesOf(stack) + override fun getLegacyStringSubtypeInfo(stack: ItemStack, context: UidContext): String = Summons.speciesOf(stack).orEmpty() + }) + } + + /** The Sigils aren't in a creative tab (they need a species), so JEI wouldn't list them otherwise. */ + override fun registerExtraIngredients(registration: IExtraIngredientRegistration) { + registration.addExtraItemStacks(sigils()) + } + override fun registerCategories(registration: IRecipeCategoryRegistration) { val gui = registration.jeiHelpers.guiHelper registration.addRecipeCategories(*kinds.map { kind -> @@ -177,18 +199,31 @@ class DropSourcesJei : IModPlugin { private fun zoneRow(zone: String, levels: String) = Row(text(DropSources.zoneName(zone)), text("Lv ${levels.replace("-", "–")}")) + /** Each Sigil as its recipe makes it: the species, its name and where to use it. */ + fun sigils(): List = DropSources.data.summons.map { s -> + ItemStack(Summons.SIGIL.get()).apply { + val data = CompoundTag().apply { putString(Summons.KEY, s.species.substringAfter(':')) } + set(DataComponents.CUSTOM_DATA, CustomData.of(data)) + set(DataComponents.ITEM_NAME, Component.literal("${s.name} Sigil")) + set(DataComponents.LORE, ItemLore(listOf(Component.literal("Use in ${DropSources.zoneName(s.zone)} to summon a Lv ${s.level} ${s.name}") + .withStyle { it.withColor(ChatFormatting.GRAY).withItalic(false) }))) + } + } + fun pokemonPages(): List { val data = DropSources.data val wild = data.pokemon.map { p -> val rows = if (p.zones.isNotEmpty()) { p.zones.map { zoneRow(it.zone, it.levels) } + } else if (p.summon != null) { + listOf(Row(muted("Not wild. A Sigil summons it in:")), Row(text(DropSources.zoneName(p.summon.zone)), text("Lv ${p.summon.level}"))) } else { listOf(Row(muted("Not wild. Its line lives in:"))) + p.line.orEmpty().map { Row(text(DropSources.zoneName(it.zone)), speciesName(it.species)) } } - Page(picture(p.species), speciesName(p.species), "Drops", rows, p.drops.map(::dropOutput)) + Page(picture(p.species), speciesName(p.species), "Drops", rows, p.drops.filter { DropSources.exists(it.item) }.map(::dropOutput)) } val special = data.spawn_drops.map { s -> - Page(picture(s.species), speciesName(s.species), "Drops here", listOf(Row(text(DropSources.zoneName(s.zone)), text("instead"))), s.drops.map(::dropOutput)) + Page(picture(s.species), speciesName(s.species), "Drops here", listOf(Row(text(DropSources.zoneName(s.zone)), text("instead"))), s.drops.filter { DropSources.exists(it.item) }.map(::dropOutput)) } return wild + special } diff --git a/mod/src/main/kotlin/dev/cobblesurvival/selftest/QuestTests.kt b/mod/src/main/kotlin/dev/cobblesurvival/selftest/QuestTests.kt index b9bc485..7685f9d 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/selftest/QuestTests.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/selftest/QuestTests.kt @@ -2,14 +2,22 @@ package dev.cobblesurvival.selftest import com.cobblemon.mod.common.api.drop.DropTable import com.cobblemon.mod.common.api.drop.ItemDropEntry +import com.cobblemon.mod.common.api.fossil.Fossils import com.cobblemon.mod.common.api.pokemon.PokemonSpecies import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail +import com.cobblemon.mod.common.entity.pokemon.PokemonEntity +import com.cobblemon.mod.common.pokemon.Species +import dev.cobblesurvival.CobbleSurvival import dev.cobblesurvival.boss.BossDefs +import dev.cobblesurvival.boss.BossFights +import dev.cobblesurvival.boss.BossToughness import dev.cobblesurvival.jei.DropSources import dev.cobblesurvival.jei.DropSourcesJei import dev.cobblesurvival.machine.MachineDefs import dev.cobblesurvival.progress.QuestEvents +import dev.cobblesurvival.summon.Summons +import dev.cobblesurvival.world.SpecialPokemon import dev.cobblesurvival.zone.Guardians import dev.cobblesurvival.zone.Zones import java.util.function.Consumer @@ -32,8 +40,75 @@ object QuestTests { SelfTestCase("quest_events_mark_zones", 200, ::zoneEvent), SelfTestCase("drop_sources_match_the_server", 20, ::dropSources), SelfTestCase("jei_pages_build", 20, ::jeiPages), + SelfTestCase("every_implemented_pokemon_is_obtainable", 20, ::obtainable), + SelfTestCase("sigil_summons_a_catchable_legend", 200, ::summon), ) + private fun rootOf(species: Species): Species { + var current = species + val seen = HashSet() + while (seen.add(current)) current = current.preEvolution?.species ?: return current + return current + } + + /** + * Roadmap Phase 9 (Adrian, 2026-11-09): every species Cobblemon has a model for can be had without commands: its line + * spawns somewhere, comes from a fossil, or has a Sigil. A new Cobblemon version adding species fails this until + * they have a home. + */ + private fun obtainable(s: Scenario) { + val wild = CobblemonSpawnPools.WORLD_SPAWN_POOL.details.filterIsInstance().mapNotNull { it.pokemon.species } + val fossils = Fossils.all().mapNotNull { it.result.species } + val summons = Summons.defs.keys + val reachable = (wild + fossils + summons).mapNotNull { PokemonSpecies.getByName(it.substringAfter(':')) }.map(::rootOf).toSet() + val missing = PokemonSpecies.implemented.filter { rootOf(it) !in reachable } + s.check(missing.isEmpty()) { "${missing.size} implemented Pokémon can't be obtained: ${missing.take(12).joinToString { it.name }}" } + s.helper.succeed() + } + + /** A Sigil works only in its zone, summons a catchable legend that fights as a boss, and once caught it's ordinary. */ + private fun summon(s: Scenario) { + val level = s.level + val player = s.player() + val sigil = DropSourcesJei.sigils().first { Summons.speciesOf(it) == "zapdos" } + val recipe = level.server.recipeManager.byKey(ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "summon_zapdos")).orElse(null) + ?: throw GameTestAssertException("the Zapdos Sigil has no recipe") + val made = recipe.value.getResultItem(level.registryAccess()) + s.check(Summons.speciesOf(made) == "zapdos" && made.hoverName.string == "Zapdos Sigil") { "the recipe makes '${made.hoverName.string}' for ${Summons.speciesOf(made)}" } + val missingRecipes = Summons.defs.keys.filter { level.server.recipeManager.byKey(ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "summon_$it")).isEmpty } + s.check(missingRecipes.isEmpty()) { "no recipe for the Sigils of $missingRecipes" } + val elsewhere = Zones.samples["button_isle"] ?: throw GameTestAssertException("no sample point for Button Isle") + val home = Zones.samples["westmarch"] ?: throw GameTestAssertException("no sample point for Westmarch") + fun ground(x: Int, z: Int) = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z).toDouble() + fun summoned() = level.getEntitiesOfClass(PokemonEntity::class.java, player.boundingBox.inflate(32.0)) { it.pokemon.persistentData.contains(Summons.TAG) } + s.afterTest { summoned().forEach { it.discard() } } + s.helper.startSequence() + .thenExecute { + level.getChunk(elsewhere.x shr 4, elsewhere.z shr 4) + s.moveTo(player, elsewhere.x + 0.5, ground(elsewhere.x, elsewhere.z), elsewhere.z + 0.5) + } + .thenWaitUntil { s.check(level.isPositionEntityTicking(player.blockPosition())) { "Button Isle isn't loaded yet" } } + .thenExecute { + s.check(!Summons.summon(level, player, sigil) && summoned().isEmpty()) { "the Sigil worked outside its zone" } + level.getChunk(home.x shr 4, home.z shr 4) + s.moveTo(player, home.x + 0.5, ground(home.x, home.z), home.z + 0.5) + } + .thenWaitUntil { s.check(level.isPositionEntityTicking(player.blockPosition())) { "Westmarch isn't loaded yet" } } + .thenExecute { + s.check(Summons.summon(level, player, sigil)) { "the Sigil didn't work in its zone" } + val legend = summoned().singleOrNull() ?: throw GameTestAssertException("${summoned().size} legends appeared") + val pokemon = legend.pokemon + s.check(pokemon.species.name == "Zapdos" && pokemon.level == Summons.defs.getValue("zapdos").level) { "summoned ${pokemon.species.name} Lv ${pokemon.level}" } + s.check(!pokemon.isUncatchable() && BossFights.defOf(legend) != null && BossToughness.isBoss(pokemon)) { "the legend isn't a catchable boss" } + s.check(SpecialPokemon.isExempt(pokemon)) { "the wild-Pokémon rules would remove the legend" } + s.check(!Summons.summon(level, player, sigil)) { "a second legend came while the first was here" } + Summons.released(pokemon) + s.check(!BossToughness.isBoss(pokemon) && !SpecialPokemon.isExempt(pokemon)) { "a caught legend still counts as a boss" } + legend.discard() + } + .thenSucceed() + } + /** * Every JEI page builds (the same code JEI calls on the client), one per Pokémon, guardian, altar tier, vein zone * and pasture list, and every item on them exists (an unknown id would show as a barrier). diff --git a/mod/src/main/kotlin/dev/cobblesurvival/summon/Summons.kt b/mod/src/main/kotlin/dev/cobblesurvival/summon/Summons.kt new file mode 100644 index 0000000..3b5642e --- /dev/null +++ b/mod/src/main/kotlin/dev/cobblesurvival/summon/Summons.kt @@ -0,0 +1,145 @@ +package dev.cobblesurvival.summon + +import com.cobblemon.mod.common.api.Priority +import com.cobblemon.mod.common.api.events.CobblemonEvents +import com.cobblemon.mod.common.api.pokemon.PokemonProperties +import com.cobblemon.mod.common.entity.pokemon.PokemonEntity +import com.cobblemon.mod.common.pokemon.Pokemon +import com.google.gson.Gson +import com.google.gson.JsonElement +import dev.cobblesurvival.CobbleSurvival +import dev.cobblesurvival.boss.BossFights +import dev.cobblesurvival.zone.Zones +import net.minecraft.ChatFormatting +import net.minecraft.core.BlockPos +import net.minecraft.core.component.DataComponents +import net.minecraft.core.particles.ParticleTypes +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import net.minecraft.server.packs.resources.ResourceManager +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener +import net.minecraft.sounds.SoundEvents +import net.minecraft.sounds.SoundSource +import net.minecraft.util.Mth +import net.minecraft.util.profiling.ProfilerFiller +import net.minecraft.world.InteractionHand +import net.minecraft.world.InteractionResultHolder +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Rarity +import net.minecraft.world.level.Level +import net.minecraft.world.phys.Vec3 +import net.neoforged.bus.api.IEventBus +import net.neoforged.neoforge.common.NeoForge +import net.neoforged.neoforge.event.AddReloadListenerEvent +import net.neoforged.neoforge.registries.DeferredHolder +import net.neoforged.neoforge.registries.DeferredRegister +import java.util.function.Supplier + +/** + * Crafted summons (Adrian, 2026-11-09): legendaries, mythicals, Ultra Beasts and Paradox Pokémon have no spawn data, so + * each has a Sigil, used in a fitting zone, that summons a catchable wild one. It fights as a boss would (area attacks + * and the Guardian's Ward, [BossFights]) until it's caught, when it becomes an ordinary Pokémon ([released]). One item, + * told apart by its custom data; the recipe's result also carries its name and where to use it, so the client needs + * none of this. Definitions: `data/cobblesurvival/summons/.json`, written by tools/summons.py. + */ +object Summons : SimpleJsonResourceReloadListener(Gson(), "summons") { + /** Key in the sigil's custom data; its value is the species. */ + const val KEY = "cobblesurvival_summon" + /** Key in a summoned Pokémon's persistent data, for as long as it's wild. */ + const val TAG = "cobblesurvival_summoned" + /** How far ahead of the player it appears. */ + private const val DISTANCE = 6.0 + /** Only one at a time within this many blocks, so sigils can't be stacked into one place. */ + private const val NEARBY = 96.0 + private const val FLOOR_ABOVE = 4 + private const val FLOOR_BELOW = 8 + + class Def(val species: String, val level: Int, val zone: String) + + var defs: Map = emptyMap() + private set + + private val ITEMS = DeferredRegister.createItems(CobbleSurvival.MOD_ID) + val SIGIL: DeferredHolder = ITEMS.register("summon_sigil", Supplier { SummonSigil(Item.Properties().stacksTo(1).rarity(Rarity.EPIC)) }) + + fun register(bus: IEventBus) { + ITEMS.register(bus) + NeoForge.EVENT_BUS.addListener { it.addListener(this) } + CobblemonEvents.POKEMON_CAPTURED.subscribe(Priority.NORMAL) { released(it.pokemon) } + } + + override fun apply(files: Map, resources: ResourceManager, profiler: ProfilerFiller) { + defs = files.values.map { it.asJsonObject }.associate { + it.get("species").asString to Def(it.get("species").asString, it.get("level").asInt, it.get("zone").asString) + } + CobbleSurvival.LOGGER.info("Loaded {} summons", defs.size) + } + + /** Caught: from here on it's an ordinary Pokémon, without the boss rules. */ + fun released(pokemon: Pokemon) { + pokemon.persistentData.remove(TAG) + pokemon.persistentData.remove(BossFights.TAG) + } + + fun speciesOf(stack: ItemStack): String? = + stack.get(DataComponents.CUSTOM_DATA)?.copyTag()?.getString(KEY)?.takeIf { it.isNotEmpty() } + + private fun tell(player: ServerPlayer, message: Component) = player.displayClientMessage(message, true) + + /** Where it appears: on the floor a little ahead of the player, or level with them when there's none (at sea). */ + private fun spot(level: ServerLevel, player: ServerPlayer): Vec3 { + val look = player.lookAngle + val ahead = player.position().add(Vec3(look.x, 0.0, look.z).normalize().scale(DISTANCE)) + val x = Mth.floor(ahead.x) + val z = Mth.floor(ahead.z) + for (y in player.blockY + FLOOR_ABOVE downTo player.blockY - FLOOR_BELOW) { + val pos = BlockPos(x, y, z) + if (level.getBlockState(pos).isAir && level.getBlockState(pos.below()).isSolid) return Vec3.atBottomCenterOf(pos) + } + return ahead + } + + /** Summons what [stack] calls for, if this is the place; true when it appeared and the sigil is spent. */ + fun summon(level: ServerLevel, player: ServerPlayer, stack: ItemStack): Boolean { + val def = speciesOf(stack)?.let { defs[it] } ?: run { + tell(player, Component.translatableWithFallback("message.cobblesurvival.summon.blank", "This sigil is blank.").withStyle(ChatFormatting.RED)) + return false + } + if (Zones.at(level, player.blockPosition())?.id != def.zone) { + tell(player, Component.translatableWithFallback("message.cobblesurvival.summon.wrong_zone", "This sigil only answers in %s.", + Zones.byId[def.zone]?.name ?: def.zone).withStyle(ChatFormatting.RED)) + return false + } + if (level.getEntitiesOfClass(PokemonEntity::class.java, player.boundingBox.inflate(NEARBY)) { it.pokemon.persistentData.contains(TAG) }.isNotEmpty()) { + tell(player, Component.translatableWithFallback("message.cobblesurvival.summon.busy", "Another legend is already here.").withStyle(ChatFormatting.RED)) + return false + } + val pokemon = PokemonProperties.parse("${def.species} level=${def.level}").create() + pokemon.persistentData.putBoolean(TAG, true) + pokemon.persistentData.putString(BossFights.TAG, "${CobbleSurvival.MOD_ID}:summon_${def.species}") + val entity = PokemonEntity(level, pokemon) + val at = spot(level, player) + entity.moveTo(at.x, at.y, at.z, player.yRot + 180f, 0f) + entity.setPersistenceRequired() + if (!level.addFreshEntity(entity)) return false + level.playSound(null, at.x, at.y, at.z, SoundEvents.WITHER_SPAWN, SoundSource.HOSTILE, 0.8f, 1.1f) + level.sendParticles(ParticleTypes.END_ROD, at.x, at.y + 1.0, at.z, 60, 1.2, 1.5, 1.2, 0.08) + player.sendSystemMessage(Component.translatableWithFallback("message.cobblesurvival.summon.answered", + "%s answers the sigil. Battle it and catch it: if it's knocked out, the sigil is lost.", pokemon.getDisplayName()).withStyle(ChatFormatting.GOLD)) + CobbleSurvival.LOGGER.info("{} summoned {} at {}", player.gameProfile.name, def.species, BlockPos.containing(at).toShortString()) + return true + } +} + +/** A Sigil: use it in its zone (written on it) to summon its legendary. */ +class SummonSigil(properties: Properties) : Item(properties) { + override fun use(level: Level, player: Player, hand: InteractionHand): InteractionResultHolder { + val stack = player.getItemInHand(hand) + if (level is ServerLevel && player is ServerPlayer && Summons.summon(level, player, stack) && !player.isCreative) stack.shrink(1) + return InteractionResultHolder.sidedSuccess(stack, level.isClientSide) + } +} diff --git a/mod/src/main/kotlin/dev/cobblesurvival/world/SpecialPokemon.kt b/mod/src/main/kotlin/dev/cobblesurvival/world/SpecialPokemon.kt index 04d2ca4..efe35a4 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/world/SpecialPokemon.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/world/SpecialPokemon.kt @@ -3,6 +3,7 @@ package dev.cobblesurvival.world import com.cobblemon.mod.common.entity.pokemon.PokemonEntity import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty +import dev.cobblesurvival.boss.BossFights import dev.cobblesurvival.zone.Guardians import net.minecraft.server.TickTask import net.minecraft.server.level.ServerLevel @@ -11,7 +12,7 @@ import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent /** * Wild Pokémon that the island's rules for wild Pokémon (strays despawning, hunting weaker trainers) leave alone: - * guardians, anything uncatchable, and other mods' characters that are Pokémon underneath, such as Cobblemon HMs' + * guardians, summoned legendaries while they're wild, anything uncatchable, and other mods' characters that are Pokémon underneath, such as Cobblemon HMs' * visiting Trainer Ditto, which sells TMs. * * The Trainer Ditto is also made uncatchable (Adrian, 2026-10-31: Ditto, which breeds with anything, is a rare find in @@ -26,7 +27,7 @@ object SpecialPokemon { } fun isExempt(pokemon: Pokemon): Boolean = - pokemon.isUncatchable() || pokemon.persistentData.contains(Guardians.TAG) || isTrainerDitto(pokemon) + pokemon.isUncatchable() || pokemon.persistentData.contains(Guardians.TAG) || pokemon.persistentData.contains(BossFights.TAG) || isTrainerDitto(pokemon) fun isTrainerDitto(pokemon: Pokemon): Boolean = pokemon.persistentData.contains(HMS_TRAINER) diff --git a/mod/src/main/resources/assets/cobblesurvival/jei/drop_sources.json b/mod/src/main/resources/assets/cobblesurvival/jei/drop_sources.json index c79fcae..217764a 100644 --- a/mod/src/main/resources/assets/cobblesurvival/jei/drop_sources.json +++ b/mod/src/main/resources/assets/cobblesurvival/jei/drop_sources.json @@ -774,6 +774,28 @@ } ] }, + { + "species": "cobblemon:articuno", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + }, + { + "item": "minecraft:chicken", + "count": "1" + }, + { + "item": "cobblemon:never_melt_ice", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "frostpeak", + "level": 70 + } + }, { "species": "cobblemon:audino", "drops": [ @@ -7994,6 +8016,24 @@ } ] }, + { + "species": "cobblemon:hooh", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + }, + { + "item": "cobblemon:sacred_ash", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "highlands", + "level": 80 + } + }, { "species": "cobblemon:hoothoot", "drops": [ @@ -9231,6 +9271,34 @@ } ] }, + { + "species": "cobblemon:latias", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + } + ], + "zones": [], + "summon": { + "zone": "highlands", + "level": 70 + } + }, + { + "species": "cobblemon:latios", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + } + ], + "zones": [], + "summon": { + "zone": "highlands", + "level": 70 + } + }, { "species": "cobblemon:leafeon", "drops": [ @@ -9732,6 +9800,20 @@ } ] }, + { + "species": "cobblemon:lugia", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + } + ], + "zones": [], + "summon": { + "zone": "deep_sea", + "level": 80 + } + }, { "species": "cobblemon:lumineon", "drops": [ @@ -10792,6 +10874,20 @@ } ] }, + { + "species": "cobblemon:mewtwo", + "drops": [ + { + "item": "cobblemon:twisted_spoon", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "deep_dark", + "level": 90 + } + }, { "species": "cobblemon:mienfoo", "drops": [ @@ -11078,6 +11174,28 @@ } ] }, + { + "species": "cobblemon:moltres", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + }, + { + "item": "minecraft:cooked_chicken", + "count": "1" + }, + { + "item": "cobblemon:charcoal_stick", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "basalt", + "level": 70 + } + }, { "species": "cobblemon:monferno", "drops": [ @@ -11443,6 +11561,20 @@ } ] }, + { + "species": "cobblemon:naganadel", + "drops": [ + { + "item": "minecraft:echo_shard", + "count": "1-3" + } + ], + "zones": [], + "summon": { + "zone": "the_end", + "level": 70 + } + }, { "species": "cobblemon:natu", "drops": [ @@ -12807,6 +12939,20 @@ } ] }, + { + "species": "cobblemon:poipole", + "drops": [ + { + "item": "minecraft:echo_shard", + "count": "1-2" + } + ], + "zones": [], + "summon": { + "zone": "the_end", + "level": 70 + } + }, { "species": "cobblemon:politoed", "drops": [ @@ -13587,6 +13733,62 @@ } ] }, + { + "species": "cobblemon:rayquaza", + "drops": [ + { + "item": "minecraft:dragon_breath", + "count": "3-5" + } + ], + "zones": [], + "summon": { + "zone": "frostpeak", + "level": 90 + } + }, + { + "species": "cobblemon:regice", + "drops": [ + { + "item": "cobblemon:never_melt_ice", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "frostpeak", + "level": 70 + } + }, + { + "species": "cobblemon:regirock", + "drops": [ + { + "item": "cobblemon:hard_stone", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "dripstone", + "level": 70 + } + }, + { + "species": "cobblemon:registeel", + "drops": [ + { + "item": "cobblemon:metal_coat", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "deep_dark", + "level": 70 + } + }, { "species": "cobblemon:relicanth", "drops": [ @@ -19349,6 +19551,28 @@ } ] }, + { + "species": "cobblemon:zapdos", + "drops": [ + { + "item": "minecraft:feather", + "count": "2-4" + }, + { + "item": "minecraft:chicken", + "count": "1" + }, + { + "item": "cobblemon:magnet", + "count": "1" + } + ], + "zones": [], + "summon": { + "zone": "westmarch", + "level": 70 + } + }, { "species": "cobblemon:zebstrika", "drops": [ @@ -19473,6 +19697,140 @@ ] } ], + "summons": [ + { + "species": "cobblemon:zapdos", + "name": "Zapdos", + "zone": "westmarch", + "level": 70 + }, + { + "species": "cobblemon:regieleki", + "name": "Regieleki", + "zone": "westmarch", + "level": 70 + }, + { + "species": "cobblemon:ironleaves", + "name": "Iron Leaves", + "zone": "jungle", + "level": 70 + }, + { + "species": "cobblemon:regirock", + "name": "Regirock", + "zone": "dripstone", + "level": 70 + }, + { + "species": "cobblemon:latias", + "name": "Latias", + "zone": "highlands", + "level": 70 + }, + { + "species": "cobblemon:latios", + "name": "Latios", + "zone": "highlands", + "level": 70 + }, + { + "species": "cobblemon:walkingwake", + "name": "Walking Wake", + "zone": "mire", + "level": 70 + }, + { + "species": "cobblemon:regidrago", + "name": "Regidrago", + "zone": "sunscorch", + "level": 70 + }, + { + "species": "cobblemon:articuno", + "name": "Articuno", + "zone": "frostpeak", + "level": 70 + }, + { + "species": "cobblemon:regice", + "name": "Regice", + "zone": "frostpeak", + "level": 70 + }, + { + "species": "cobblemon:registeel", + "name": "Registeel", + "zone": "deep_dark", + "level": 70 + }, + { + "species": "cobblemon:moltres", + "name": "Moltres", + "zone": "basalt", + "level": 70 + }, + { + "species": "cobblemon:poipole", + "name": "Poipole", + "zone": "the_end", + "level": 70 + }, + { + "species": "cobblemon:xerneas", + "name": "Xerneas", + "zone": "jungle", + "level": 80 + }, + { + "species": "cobblemon:zarude", + "name": "Zarude", + "zone": "jungle", + "level": 80 + }, + { + "species": "cobblemon:mew", + "name": "Mew", + "zone": "jungle", + "level": 80 + }, + { + "species": "cobblemon:hooh", + "name": "Ho-Oh", + "zone": "highlands", + "level": 80 + }, + { + "species": "cobblemon:lugia", + "name": "Lugia", + "zone": "deep_sea", + "level": 80 + }, + { + "species": "cobblemon:regigigas", + "name": "Regigigas", + "zone": "deep_dark", + "level": 80 + }, + { + "species": "cobblemon:rayquaza", + "name": "Rayquaza", + "zone": "frostpeak", + "level": 90 + }, + { + "species": "cobblemon:mewtwo", + "name": "Mewtwo", + "zone": "deep_dark", + "level": 90 + }, + { + "species": "cobblemon:eternatus", + "name": "Eternatus", + "zone": "the_end", + "level": 90 + } + ], "spawn_drops": [ { "species": "cobblemon:beheeyem", diff --git a/mod/src/main/resources/assets/cobblesurvival/lang/en_us.json b/mod/src/main/resources/assets/cobblesurvival/lang/en_us.json index 875b15d..f06c576 100644 --- a/mod/src/main/resources/assets/cobblesurvival/lang/en_us.json +++ b/mod/src/main/resources/assets/cobblesurvival/lang/en_us.json @@ -35,5 +35,6 @@ "tooltip.cobblesurvival.altar_core": "Sacrifice Altar: trades a Pokémon of Lv 20 or more for items; sneak-use twice. It's gone for good.", "tooltip.cobblesurvival.altar_core.shell": "Shell: obsidian, crying obsidian, blackstone or polished blackstone bricks.", "tooltip.cobblesurvival.rebirth_core": "Rebirth Chamber: a Lv 100 Pokémon becomes a shiny Lv 1 of its first stage after a long time.", - "tooltip.cobblesurvival.rebirth_core.shell": "Shell: amethyst, quartz or calcite blocks." + "tooltip.cobblesurvival.rebirth_core.shell": "Shell: amethyst, quartz or calcite blocks.", + "item.cobblesurvival.summon_sigil": "Blank Sigil" } diff --git a/mod/src/main/resources/assets/cobblesurvival/models/item/summon_sigil.json b/mod/src/main/resources/assets/cobblesurvival/models/item/summon_sigil.json new file mode 100644 index 0000000..05549c0 --- /dev/null +++ b/mod/src/main/resources/assets/cobblesurvival/models/item/summon_sigil.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "cobblesurvival:item/summon_sigil" + } +} diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/item/summon_sigil.png b/mod/src/main/resources/assets/cobblesurvival/textures/item/summon_sigil.png new file mode 100644 index 0000000000000000000000000000000000000000..f37a223783dbbdfcc6d42a8d7948cb091bbea5a9 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7E_k{)hE&X18muUESV17h zD%6HiP*zyJP>U&UlK2t#E8kfwIRyhde$9HtHBZ3X*OPNXW?ID2H-)>)k28uiJw35Y zY5L>%{Kt^(E25UGmu`+qyUTds_+hT~A5|Bou`!rey=if~Y_u>< zOgd)IZ=;VYGgmMMHct@FN;`dIdzpSAYvLw%ok<}DEK(L3C-}ciIUuRx%#--;b$sKJ zri)v67 0): + pos[f"{chapter_id}/{qid}"] = [2.0 + (i % grid) * 1.5, (i // grid) * 1.5] + columns = {c: ids for c, ids in columns.items() if c == 0} for col, ids in columns.items(): for row, qid in enumerate(ids): pos[f"{chapter_id}/{qid}"] = [col * 2.0, (row - (len(ids) - 1) / 2) * 1.5] @@ -296,7 +310,7 @@ def build(book): for index, chapter in enumerate(book["chapters"]): cid = chapter["id"] quests = expanded[cid] - auto = auto_layout(quests, cid) + auto = auto_layout(quests, cid, chapter.get("columns")) chapter_id = object_id(f"chapter:{cid}") lang[f"chapter.{chapter_id}.title"] = chapter["title"] if chapter.get("subtitle"): @@ -369,7 +383,7 @@ def pull(book, folder): auto, by_id = {}, {} for c in book["chapters"]: quests = chapter_quests(c, book, zones) - auto |= auto_layout(quests, c["id"]) + auto |= auto_layout(quests, c["id"], c.get("columns")) by_id |= {object_id(f"quest:{c['id']}/{q['id']}"): f"{c['id']}/{q['id']}" for q in quests} layout = dict(book.get("layout") or {}) for path in sorted((folder / "chapters").glob("*.snbt")): diff --git a/tools/summons.py b/tools/summons.py new file mode 100644 index 0000000..6edbb29 --- /dev/null +++ b/tools/summons.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Write the crafted summons (Phase 9; Adrian, 2026-11-09) from world/summons.json. + +Legendaries, mythicals, Ultra Beasts and Paradox Pokémon have no spawn data, so each gets a Sigil (one item, +cobblesurvival:summon_sigil, told apart by its custom data), used in a fitting zone to summon a catchable wild one. This +writes, under datapacks/core/data/cobblesurvival/: + + summons/.json species, level, zone, read by the companion mod (summon/Summons.kt) + recipe/summon_.json material in the corners, ring on the sides, core in the middle; the result carries + the species, its name and where to use it, so the client needs no server data + +and the sigil's texture and item model in the companion mod. The fights are written by tools/zone_bosses.py, so run that +afterwards, and tools/jei_sources.py and tools/quests.py after it. + +Needs the Cobblemon jar in build/server/mods (any test-server run), for the names. + +Usage: tools/summons.py +""" +import json +import re +import zipfile +from pathlib import Path + +from PIL import Image + +ROOT = Path(__file__).resolve().parent.parent +DATA = ROOT / "datapacks/core/data/cobblesurvival" +ASSETS = ROOT / "mod/src/main/resources/assets/cobblesurvival" +SIGIL = "cobblesurvival:summon_sigil" + +# The sigil: a dark disc with a gold ring and a violet eye. +TEXTURE = [ + "................", + ".....######.....", + "...##gggggg##...", + "..#gg......gg#..", + "..#g..vvvv..g#..", + ".#g..vv++vv..g#.", + ".#g.vv++++vv.g#.", + ".#g.vv+WW+vv.g#.", + ".#g.vv+WW+vv.g#.", + ".#g.vv++++vv.g#.", + ".#g..vv++vv..g#.", + "..#g..vvvv..g#..", + "..#gg......gg#..", + "...##gggggg##...", + ".....######.....", + "................", +] +COLORS = {"#": (40, 30, 60, 255), "g": (230, 185, 70, 255), ".": (0, 0, 0, 0), "v": (130, 60, 200, 255), + "+": (190, 120, 245, 255), "W": (250, 235, 255, 255)} + + +def names(jar): + found = {} + with zipfile.ZipFile(jar) as z: + for name in z.namelist(): + m = re.fullmatch(r"data/cobblemon/species/[^/]+/([^/]+)\.json", name) + if m: + found[m[1]] = json.loads(z.read(name))["name"] + return found + + +def main(): + table = json.loads((ROOT / "world/summons.json").read_text()) + plan = json.loads((ROOT / "world/zones.json").read_text()) + zones = {z["id"]: z["name"] for z in plan["zones"] + plan["caves"] + plan.get("dimensions", [])} + species_names = names(next((ROOT / "build/server/mods").glob("Cobblemon-*.jar"))) + + summons, recipes = DATA / "summons", DATA / "recipe" + summons.mkdir(parents=True, exist_ok=True) + for old in list(summons.glob("*.json")) + list(recipes.glob("summon_*.json")): + old.unlink() + comment = "Generated by tools/summons.py from world/summons.json; do not edit by hand." + for species, s in table["summons"].items(): + tier = table["tiers"][str(s["tier"])] + name = species_names[species] + (summons / f"{species}.json").write_text(json.dumps( + {"_comment": comment, "species": species, "level": tier["level"], "zone": s["zone"]}, indent=1) + "\n") + lore = f"Use in {zones[s['zone']]} to summon a Lv {tier['level']} {name}" + (recipes / f"summon_{species}.json").write_text(json.dumps({ + "_comment": comment, "type": "minecraft:crafting_shaped", "category": "misc", + "pattern": ["MRM", "RCR", "MRM"], + "key": {"M": {"item": s["material"]}, "R": {"item": tier["ring"]}, "C": {"item": tier["core"]}}, + "result": {"id": SIGIL, "count": 1, "components": { + "minecraft:custom_data": {"cobblesurvival_summon": species}, + "minecraft:item_name": json.dumps(f"{name} Sigil"), + "minecraft:lore": [json.dumps({"text": lore, "color": "gray", "italic": False})], + }}, + }, indent=1) + "\n") + + image = Image.new("RGBA", (16, 16)) + for y, row in enumerate(TEXTURE): + for x, c in enumerate(row): + image.putpixel((x, y), COLORS[c]) + (ASSETS / "textures/item").mkdir(parents=True, exist_ok=True) + image.save(ASSETS / "textures/item/summon_sigil.png") + (ASSETS / "models/item/summon_sigil.json").write_text(json.dumps( + {"parent": "minecraft:item/generated", "textures": {"layer0": "cobblesurvival:item/summon_sigil"}}, indent=2) + "\n") + print(f"wrote {len(table['summons'])} summons") + + +if __name__ == "__main__": + main() diff --git a/tools/zone_bosses.py b/tools/zone_bosses.py index 5662b8e..bff8d21 100644 --- a/tools/zone_bosses.py +++ b/tools/zone_bosses.py @@ -26,6 +26,8 @@ END = {"species": "eternatus", "level": 80, "respawn_hours": 5, "xp_first_win": # Adrian, 2026-11-02: Eternatus was too small (0.2x, about a block). Scaled to stand this many blocks tall. END_HEIGHT = 15 END_ARENA = 40 +# A summoned legendary's fight (world/summons.json; Adrian, 2026-11-09): catchable, same rules as a boss. +SUMMON_ARENA = 32 # Up to this level: these patterns. TIERS = [ @@ -71,6 +73,7 @@ def species_data(jar): if name.startswith("data/cobblemon/species/") and name.endswith(".json"): d = json.loads(z.read(name)) types[d["name"].lower()] = {t for t in (d.get("primaryType"), d.get("secondaryType")) if t} + types[Path(name).stem] = types[d["name"].lower()] heights[d["name"].lower()] = (d.get("hitbox") or {}).get("height", 1) * d.get("baseScale", 1) return types, heights @@ -94,6 +97,11 @@ def main(): end = boss(END["level"], types.get(END["species"], set()), END_ARENA, end=end_def) (OUT / "end.json").write_text(json.dumps({"_comment": comment, **end}, indent=1) + "\n") print(f"end: {END['species']} Lv {END['level']}, {len(end['patterns'])} patterns") + table = json.loads((ROOT / "world/summons.json").read_text()) + for species, s in table["summons"].items(): + fight = boss(table["tiers"][str(s["tier"])]["level"], types.get(species, set()), SUMMON_ARENA, summon=species) + (OUT / f"summon_{species}.json").write_text(json.dumps({"_comment": comment, **fight}, indent=1) + "\n") + print(f"{len(table['summons'])} summoned legendaries") if __name__ == "__main__": diff --git a/world/summons.json b/world/summons.json new file mode 100644 index 0000000..129989f --- /dev/null +++ b/world/summons.json @@ -0,0 +1,32 @@ +{ + "_comment": "Crafted summons (Adrian, 2026-11-09): legendaries, mythicals, Ultra Beasts and Paradox Pokemon have no spawn data, so each gets a sigil, used in a fitting zone, that summons a catchable wild one for a hard fight. Levels and recipes are placeholders; see docs/balance.md. tools/summons.py writes the recipes and data; tools/zone_bosses.py their fights. Recipe: material in the corners, ring on the sides, core in the middle.", + "tiers": { + "1": {"level": 70, "core": "minecraft:netherite_ingot", "ring": "minecraft:diamond"}, + "2": {"level": 80, "core": "minecraft:nether_star", "ring": "minecraft:diamond"}, + "3": {"level": 90, "core": "minecraft:nether_star", "ring": "minecraft:netherite_ingot"} + }, + "summons": { + "zapdos": {"zone": "westmarch", "tier": 1, "material": "cobblemon:thunder_stone"}, + "regieleki": {"zone": "westmarch", "tier": 1, "material": "minecraft:redstone_block"}, + "ironleaves": {"zone": "jungle", "tier": 1, "material": "cobblemon:leaf_stone"}, + "regirock": {"zone": "dripstone", "tier": 1, "material": "minecraft:amethyst_shard"}, + "latias": {"zone": "highlands", "tier": 1, "material": "cobblemon:sun_stone"}, + "latios": {"zone": "highlands", "tier": 1, "material": "cobblemon:moon_stone"}, + "walkingwake": {"zone": "mire", "tier": 1, "material": "cobblemon:water_stone"}, + "regidrago": {"zone": "sunscorch", "tier": 1, "material": "minecraft:gold_block"}, + "articuno": {"zone": "frostpeak", "tier": 1, "material": "cobblemon:ice_stone"}, + "regice": {"zone": "frostpeak", "tier": 1, "material": "cobblemon:icy_rock"}, + "registeel": {"zone": "deep_dark", "tier": 1, "material": "minecraft:iron_block"}, + "moltres": {"zone": "basalt", "tier": 1, "material": "cobblemon:fire_stone"}, + "poipole": {"zone": "the_end", "tier": 1, "material": "minecraft:ender_pearl"}, + "xerneas": {"zone": "jungle", "tier": 2, "material": "cobblemon:leaf_stone"}, + "zarude": {"zone": "jungle", "tier": 2, "material": "minecraft:redstone_block"}, + "mew": {"zone": "jungle", "tier": 2, "material": "cobblemon:shiny_stone"}, + "hooh": {"zone": "highlands", "tier": 2, "material": "cobblemon:sun_stone"}, + "lugia": {"zone": "deep_sea", "tier": 2, "material": "cobblemon:water_stone"}, + "regigigas": {"zone": "deep_dark", "tier": 2, "material": "minecraft:echo_shard"}, + "rayquaza": {"zone": "frostpeak", "tier": 3, "material": "minecraft:emerald_block"}, + "mewtwo": {"zone": "deep_dark", "tier": 3, "material": "cobblemon:dusk_stone"}, + "eternatus": {"zone": "the_end", "tier": 3, "material": "minecraft:ender_eye"} + } +}