Phase 9: reproducible builds, balance from the server, spark check, play feedback
Build: every tool pinned (JDK, packwiz, Gradle plugins, Python packages), exports normalised, tools/check_reproducible.py proves two clean builds are byte-identical. Server-side balance: a server can load config/cobblesurvival/datapack.zip in place of the jar's data, /reload applies it, tools/server_update.py packages it and refuses when a client change is needed. JEI drop pages are sent by the server. Code-held balance numbers moved to tuning/server.json. Performance: spark in the pack (server only), tools/perf_test.py with fake players whose chunks really are active, docs/performance.md. Adrian's feedback: default keys R, U, J, §; drop pages link to Cobblemon Info and show real chances; bosses only hurt in battles; battle XP halved; channels between the islands low level; temperature ×3 with insulation doubled; hazards start at once; no item dots on the map; a wild attack starts a battle; the starter only faints; quest book on join with a basics page and an HMs page; regional forms obtainable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0741c84077
commit
4083791944
133 files changed
+8349
-3272
No files matched your search
@@ -8,7 +8,7 @@ A free NeoForge 1.21.1 modpack on Cobblemon 1.8: one large island with soft-gate
|
||||
|
||||
- **Core:** Cobblemon 1.8.4, KubeJS, FTB Quests, WorldGen Editor, and the companion mod (`mod/`).
|
||||
- **World:** a Poké Ball-shaped island (world type "Cobblemon Survival Island") with a fixed biome list, no natural ore and no Nether. See [world/README.md](world/README.md).
|
||||
- **Quality of life:** JEI, Jade, Inventory Profiles Next, Mouse Tweaks, AppleSkin, Controlling, Xaero's Minimap and World Map; Default Options ships conflict-free default keybinds (`pack/config/defaultoptions/keybindings.txt`, check with `tools/check_keybinds.py`).
|
||||
- **Quality of life:** JEI, Jade, Inventory Profiles Next, Mouse Tweaks, AppleSkin, Controlling, Xaero's Minimap and World Map; Default Options ships default keybinds (R recipes, U uses, J map, § quest book; JEI's R shares with Cobblemon's send-out, one works in screens, the other in the world) (`pack/config/defaultoptions/keybindings.txt`, check with `tools/check_keybinds.py`).
|
||||
- **Rules:** no vanilla mobs (only Pokémon), Nether portals don't form.
|
||||
- **Performance:** Sodium, FerriteCore, ModernFix, ImmediatelyFast, Entity Culling.
|
||||
- **Shaders:** Iris with Complementary Reimagined, installed but off by default (Toggle Shaders key M, or Video Settings > Shader Packs).
|
||||
@@ -29,9 +29,9 @@ tools/ build/export, cross-linking, Prism sync, server smoke test, i
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java 21 to run Gradle and the server. `tools/` uses Prism's bundled Java 21 when present; Gradle downloads a JDK 21 for compiling if needed.
|
||||
- [packwiz](https://github.com/packwiz/packwiz): `go install github.com/packwiz/packwiz@latest`
|
||||
- Python 3.11+
|
||||
- Java 21 to run the test server (`tools/` uses Prism's bundled Java 21 when present). The build downloads its own pinned JDK.
|
||||
- [Go](https://go.dev/dl/) (the build compiles its pinned packwiz; no system packwiz needed)
|
||||
- Python 3.14 (the build makes its own venv with exact packages)
|
||||
|
||||
## Everyday commands
|
||||
|
||||
@@ -131,7 +131,39 @@ Writes the Sigils' recipes and data and their fights (Phase 9) from `world/summo
|
||||
```bash
|
||||
python3 tools/jei_sources.py
|
||||
```
|
||||
Writes the data behind the pack's own JEI pages (Phase 8) into the companion mod (`mod/src/main/resources/assets/cobblesurvival/jei/drop_sources.json`): every Pokémon's drops and zones, guardians, the End boss, the altar, ore veins, pasture finds. Run it after changing species drops, spawns, guardians, machines, ores or Cobblebase tables, then rebuild; the self-test `drop_sources_match_the_server` fails while it's stale. Needs the Cobblemon jar in `build/server/mods` (any test-server run).
|
||||
Writes the data behind the pack's own JEI pages (Phase 8) into the data pack (`datapacks/core/data/cobblesurvival/drop_sources/pages.json`), and the server sends it to players: every Pokémon's drops and zones, guardians, the End boss, the altar, ore veins, pasture finds. Run it after changing species drops, spawns, guardians, machines, ores or Cobblebase tables, then rebuild; the self-test `drop_sources_match_the_server` fails while it's stale. Needs the Cobblemon jar in `build/server/mods` (any test-server run).
|
||||
|
||||
## Balancing on a running server
|
||||
|
||||
Balance lives on the server, so playtesters keep the client they have. After changing the pack's data:
|
||||
|
||||
```bash
|
||||
python3 tools/server_update.py --install /path/to/server
|
||||
```
|
||||
|
||||
Or without `--install`, unzip `build/server-update-<ver>.zip` into the server's folder. Then `/reload` for the data pack and recipes, `/ftbquests reload` for the quest book; changed mod configs need a restart. `/cobblesurvival data` shows what is live. The JEI drop pages update for players by themselves.
|
||||
|
||||
This covers everything in `datapacks/core` (spawns, drops, machines, bosses, gates, hazards, recipes, `tuning/server.json`), `pack/config`, KubeJS server scripts and the quest book. It's refused when something players run or draw changed (mods, textures, lang, startup scripts, new items or blocks, client code), and names those files; hand out a new pack, then record it with `tools/server_update.py --mark-client-release`. Code that only the server runs can go along with `--with-jar`. Moving ores or reshaping the island needs a new world, not a new client.
|
||||
|
||||
`tools/check_server_update.py` proves on the test server that the data switches over and `/reload` picks up a change.
|
||||
|
||||
## Reproducible builds
|
||||
|
||||
The same commit builds the same bytes, wherever the repo is and whatever the machine has installed: `tools/build.py` compiles with a pinned JDK and runs a pinned packwiz (`tools/build-lock.toml`), with exact Python packages (`tools/requirements.txt`) and Gradle plugins, and every mod is pinned by hash. `build/SHA256SUMS` lists what it produced.
|
||||
|
||||
```bash
|
||||
python3 tools/check_reproducible.py --fresh-gradle
|
||||
```
|
||||
|
||||
Builds the working tree twice from clean copies in different folders, each with an empty Gradle home, and compares every output byte for byte. The island world template isn't covered: Minecraft's chunk generation is the same from one run to the next, the saved region files aren't.
|
||||
|
||||
## Performance
|
||||
|
||||
```bash
|
||||
python3 tools/perf_test.py
|
||||
```
|
||||
|
||||
Boots the island with fake players, full pastures and working machines at three sizes, profiles with spark (in the pack, server only) and writes [docs/performance.md](docs/performance.md). About 20 minutes.
|
||||
|
||||
## Adding a mod
|
||||
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": ["minecraft:jagged_peaks"]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": ["minecraft:windswept_hills"]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": ["minecraft:basalt_deltas"]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": ["minecraft:jagged_peaks"]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": ["minecraft:windswept_hills"]
|
||||
}
|
||||
@@ -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_galarian",
|
||||
"arena": 32,
|
||||
"patterns": [
|
||||
{
|
||||
"shape": "circle",
|
||||
"radius": 3.5,
|
||||
"aim": "player",
|
||||
"telegraph_ticks": 30,
|
||||
"damage": 13,
|
||||
"effect": "ice",
|
||||
"trigger": {
|
||||
"every_ticks": 112,
|
||||
"offset": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"shape": "line",
|
||||
"length": 14,
|
||||
"width": 2,
|
||||
"aim": "player",
|
||||
"telegraph_ticks": 30,
|
||||
"damage": 13,
|
||||
"effect": "ice",
|
||||
"trigger": {
|
||||
"every_ticks": 160,
|
||||
"offset": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
"shape": "cone",
|
||||
"length": 10,
|
||||
"angle": 70,
|
||||
"aim": "player",
|
||||
"telegraph_ticks": 30,
|
||||
"damage": 13,
|
||||
"effect": "ice",
|
||||
"trigger": {
|
||||
"every_ticks": 200,
|
||||
"offset": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"shape": "ring",
|
||||
"inner": 3,
|
||||
"outer": 9,
|
||||
"aim": "boss",
|
||||
"telegraph_ticks": 30,
|
||||
"damage": 13,
|
||||
"effect": "ice",
|
||||
"trigger": {
|
||||
"below_health": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"shape": "ring",
|
||||
"inner": 2,
|
||||
"outer": 12,
|
||||
"aim": "boss",
|
||||
"telegraph_ticks": 30,
|
||||
"damage": 13,
|
||||
"effect": "ice",
|
||||
"trigger": {
|
||||
"below_health": 0.25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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_galarian",
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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_galarian",
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+2209
-1675
File diff suppressed because it is too large.
Load diff
@@ -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:nether_star"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"id": "cobblesurvival:summon_sigil",
|
||||
"count": 1,
|
||||
"components": {
|
||||
"minecraft:custom_data": {
|
||||
"cobblesurvival_summon": "articuno_galarian"
|
||||
},
|
||||
"minecraft:item_name": "\"Galarian Articuno Sigil\"",
|
||||
"minecraft:lore": [
|
||||
"{\"text\": \"Use in Highland Meadows to summon a Lv 80 Galarian Articuno\", \"color\": \"gray\", \"italic\": false}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:nether_star"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"id": "cobblesurvival:summon_sigil",
|
||||
"count": 1,
|
||||
"components": {
|
||||
"minecraft:custom_data": {
|
||||
"cobblesurvival_summon": "moltres_galarian"
|
||||
},
|
||||
"minecraft:item_name": "\"Galarian Moltres Sigil\"",
|
||||
"minecraft:lore": [
|
||||
"{\"text\": \"Use in Deep Dark to summon a Lv 80 Galarian Moltres\", \"color\": \"gray\", \"italic\": false}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:nether_star"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"id": "cobblesurvival:summon_sigil",
|
||||
"count": 1,
|
||||
"components": {
|
||||
"minecraft:custom_data": {
|
||||
"cobblesurvival_summon": "zapdos_galarian"
|
||||
},
|
||||
"minecraft:item_name": "\"Galarian Zapdos Sigil\"",
|
||||
"minecraft:lore": [
|
||||
"{\"text\": \"Use in Verdant Jungle to summon a Lv 80 Galarian Zapdos\", \"color\": \"gray\", \"italic\": false}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -789,276 +789,6 @@
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid",
|
||||
"#cobblemon:is_freezing",
|
||||
"#cobblemon:is_taiga"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"isRaining": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-1-64",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth",
|
||||
"presets": [
|
||||
"natural",
|
||||
"urban"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 72.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid",
|
||||
"#cobblemon:is_freezing",
|
||||
"#cobblemon:is_taiga"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"isRaining": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_civilization"
|
||||
],
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-2-65",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 72.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid"
|
||||
],
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-3-66",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth",
|
||||
"presets": [
|
||||
"natural",
|
||||
"urban"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 72.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid"
|
||||
],
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_civilization"
|
||||
],
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-4-67",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 72.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid",
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"isRaining": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"minY": 62,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_illager_structure"
|
||||
],
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-alolan-5-68",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 432.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_arid",
|
||||
"#cobblemon:is_freezing"
|
||||
],
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"minY": 62,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_illager_structure"
|
||||
],
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-alolan-6-69",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 432.0,
|
||||
"weightMultipliers": [
|
||||
{
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 1.5
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"minY": 62,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_illager_structure"
|
||||
],
|
||||
"zone": "button_isle"
|
||||
},
|
||||
"id": "button_isle-meowth-alolan-7-70",
|
||||
"level": "4-10",
|
||||
"pokemon": "meowth alolan",
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 144.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -779,6 +779,21 @@
|
||||
"type": "pokemon",
|
||||
"weight": 36.0
|
||||
},
|
||||
{
|
||||
"id": "frostpeak-darumaka-94",
|
||||
"type": "pokemon",
|
||||
"pokemon": "darumaka galarian",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "40-55",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
@@ -2003,6 +2018,21 @@
|
||||
"type": "pokemon",
|
||||
"weight": 87.5
|
||||
},
|
||||
{
|
||||
"id": "frostpeak-sandshrew-92",
|
||||
"type": "pokemon",
|
||||
"pokemon": "sandshrew alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "40-55",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
@@ -2494,6 +2524,21 @@
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frostpeak-vulpix-93",
|
||||
"type": "pokemon",
|
||||
"pokemon": "vulpix alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "40-55",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frostpeak-wyrdeer-89",
|
||||
"type": "pokemon",
|
||||
@@ -2509,6 +2554,22 @@
|
||||
"condition": {
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frostpeak-zorua-95",
|
||||
"type": "pokemon",
|
||||
"pokemon": "zorua hisuian",
|
||||
"presets": [
|
||||
"natural",
|
||||
"wild"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "40-55",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -541,6 +541,21 @@
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "highlands-diglett-132",
|
||||
"type": "pokemon",
|
||||
"pokemon": "diglett alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "25-35",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "highlands"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "highlands-dodrio-131",
|
||||
"type": "pokemon",
|
||||
@@ -3463,6 +3478,21 @@
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "highlands-tauros-133",
|
||||
"type": "pokemon",
|
||||
"pokemon": "tauros paldean bull_breed=combat",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "25-35",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "highlands"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "ultra-rare",
|
||||
"condition": {
|
||||
|
||||
@@ -1004,6 +1004,21 @@
|
||||
"type": "pokemon",
|
||||
"weight": 18.0
|
||||
},
|
||||
{
|
||||
"id": "jungle-slowpoke-85",
|
||||
"type": "pokemon",
|
||||
"pokemon": "slowpoke galarian",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "18-30",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "jungle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "ultra-rare",
|
||||
"condition": {
|
||||
|
||||
@@ -30,262 +30,6 @@
|
||||
"multiplier": 5.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-arrokuda-1-74",
|
||||
"level": "25-38",
|
||||
"pokemon": "arrokuda",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 9.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#aether:is_aether",
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-arrokuda-2-75",
|
||||
"level": "25-38",
|
||||
"pokemon": "arrokuda",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 90.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-arrokuda-3-76",
|
||||
"level": "25-38",
|
||||
"pokemon": "arrokuda",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 9.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#aether:is_aether",
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-arrokuda-4-77",
|
||||
"level": "25-38",
|
||||
"pokemon": "arrokuda",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 90.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-barraskewda-1-78",
|
||||
"level": "26-38",
|
||||
"pokemon": "barraskewda",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 1.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#aether:is_aether",
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-barraskewda-2-79",
|
||||
"level": "26-38",
|
||||
"pokemon": "barraskewda",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 10.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-barraskewda-3-80",
|
||||
"level": "26-38",
|
||||
"pokemon": "barraskewda",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 1.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_freezing"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#aether:is_aether",
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_freshwater",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "mire"
|
||||
},
|
||||
"id": "mire-barraskewda-4-81",
|
||||
"level": "26-38",
|
||||
"pokemon": "barraskewda",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 10.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
@@ -961,7 +705,7 @@
|
||||
"weight": 4.0
|
||||
},
|
||||
{
|
||||
"id": "mire-hatenna-82",
|
||||
"id": "mire-hatenna-74",
|
||||
"type": "pokemon",
|
||||
"pokemon": "hatenna",
|
||||
"presets": [
|
||||
@@ -977,7 +721,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mire-hattrem-83",
|
||||
"id": "mire-hattrem-75",
|
||||
"type": "pokemon",
|
||||
"pokemon": "hattrem",
|
||||
"presets": [
|
||||
@@ -1729,6 +1473,21 @@
|
||||
"type": "pokemon",
|
||||
"weight": 60.0
|
||||
},
|
||||
{
|
||||
"id": "mire-stunfisk-79",
|
||||
"type": "pokemon",
|
||||
"pokemon": "stunfisk galarian",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "25-38",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "mire"
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
@@ -2148,7 +1907,22 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mire-toedscool-84",
|
||||
"id": "mire-tauros-78",
|
||||
"type": "pokemon",
|
||||
"pokemon": "tauros paldean bull_breed=aqua",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "25-38",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "mire"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mire-toedscool-76",
|
||||
"type": "pokemon",
|
||||
"pokemon": "toedscool",
|
||||
"presets": [
|
||||
@@ -2164,7 +1938,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mire-toedscruel-85",
|
||||
"id": "mire-toedscruel-77",
|
||||
"type": "pokemon",
|
||||
"pokemon": "toedscruel",
|
||||
"presets": [
|
||||
|
||||
@@ -4,25 +4,6 @@
|
||||
"neededInstalledMods": [],
|
||||
"neededUninstalledMods": [],
|
||||
"spawns": [
|
||||
{
|
||||
"anticondition": {
|
||||
"minLureLevel": 1
|
||||
},
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-alomomola-2-26",
|
||||
"level": "5-20",
|
||||
"pokemon": "alomomola",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 60.0
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
@@ -58,7 +39,7 @@
|
||||
"isPokeSnack": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-carvanha-1-73",
|
||||
"id": "shallows-carvanha-1-72",
|
||||
"level": "6-20",
|
||||
"pokemon": "carvanha",
|
||||
"presets": [
|
||||
@@ -80,7 +61,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-carvanha-2-74",
|
||||
"id": "shallows-carvanha-2-73",
|
||||
"level": "6-20",
|
||||
"pokemon": "carvanha",
|
||||
"presets": [
|
||||
@@ -99,7 +80,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-carvanha-6-75",
|
||||
"id": "shallows-carvanha-6-74",
|
||||
"level": "6-20",
|
||||
"pokemon": "carvanha",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -132,7 +113,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-carvanha-8-76",
|
||||
"id": "shallows-carvanha-8-75",
|
||||
"level": "6-20",
|
||||
"pokemon": "carvanha",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -154,6 +135,50 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_deep_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"isPokeSnack": true,
|
||||
"maxY": 48,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-chinchou-1-30",
|
||||
"level": "8-20",
|
||||
"pokemon": "chinchou",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 54.0
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_deep_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"maxY": 48,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-chinchou-2-31",
|
||||
"level": "8-20",
|
||||
"pokemon": "chinchou",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 54.0
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
@@ -163,7 +188,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-chinchou-4-27",
|
||||
"id": "shallows-chinchou-4-32",
|
||||
"level": "8-20",
|
||||
"pokemon": "chinchou",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -188,7 +213,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-chinchou-5-28",
|
||||
"id": "shallows-chinchou-5-33",
|
||||
"level": "8-20",
|
||||
"pokemon": "chinchou",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -212,7 +237,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-1-37",
|
||||
"id": "shallows-clobbopus-1-44",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"presets": [
|
||||
@@ -237,7 +262,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-2-38",
|
||||
"id": "shallows-clobbopus-2-45",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"presets": [
|
||||
@@ -264,7 +289,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-6-39",
|
||||
"id": "shallows-clobbopus-6-46",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -288,7 +313,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-7-40",
|
||||
"id": "shallows-clobbopus-7-47",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -320,7 +345,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-8-41",
|
||||
"id": "shallows-clobbopus-8-48",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -346,7 +371,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-clobbopus-9-42",
|
||||
"id": "shallows-clobbopus-9-49",
|
||||
"level": "6-20",
|
||||
"pokemon": "clobbopus",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -397,7 +422,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-cramorant-1-77",
|
||||
"id": "shallows-cramorant-1-76",
|
||||
"level": "5-20",
|
||||
"pokemon": "cramorant",
|
||||
"spawnablePositionType": "grounded",
|
||||
@@ -428,7 +453,7 @@
|
||||
"isPokeSnack": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-1-29",
|
||||
"id": "shallows-finizen-1-34",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"presets": [
|
||||
@@ -450,7 +475,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-2-30",
|
||||
"id": "shallows-finizen-2-35",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"presets": [
|
||||
@@ -472,7 +497,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-3-31",
|
||||
"id": "shallows-finizen-3-36",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -489,7 +514,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-4-32",
|
||||
"id": "shallows-finizen-4-37",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -516,7 +541,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-5-33",
|
||||
"id": "shallows-finizen-5-38",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -535,7 +560,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finizen-6-34",
|
||||
"id": "shallows-finizen-6-39",
|
||||
"level": "7-20",
|
||||
"pokemon": "finizen",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -548,6 +573,50 @@
|
||||
"multiplier": 3.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_deep_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"isPokeSnack": true,
|
||||
"maxY": 48,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finneon-1-40",
|
||||
"level": "8-20",
|
||||
"pokemon": "finneon",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 54.0
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_deep_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"maxY": 48,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finneon-2-41",
|
||||
"level": "8-20",
|
||||
"pokemon": "finneon",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 54.0
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
@@ -557,7 +626,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finneon-4-35",
|
||||
"id": "shallows-finneon-4-42",
|
||||
"level": "8-20",
|
||||
"pokemon": "finneon",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -575,208 +644,13 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-finneon-5-36",
|
||||
"id": "shallows-finneon-5-43",
|
||||
"level": "8-20",
|
||||
"pokemon": "finneon",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 90.0
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-1-43",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"presets": [
|
||||
"natural",
|
||||
"wild"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 16.8
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_beach"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minLureLevel": 1,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-10-50",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 18.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"minLureLevel": 3
|
||||
},
|
||||
"multiplier": 3.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"isPokeSnack": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-2-44",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"presets": [
|
||||
"wild"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 16.8
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"isPokeSnack": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-4-45",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 50.4
|
||||
},
|
||||
{
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-5-46",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 50.4
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_beach"
|
||||
],
|
||||
"minLureLevel": 1
|
||||
},
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-7-47",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 50.4
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_beach"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"minLureLevel": 1,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-8-48",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 18.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"minLureLevel": 3
|
||||
},
|
||||
"multiplier": 3.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_beach"
|
||||
],
|
||||
"minLureLevel": 1
|
||||
},
|
||||
"bucket": "uncommon",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_coast",
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-inkay-9-49",
|
||||
"level": "5-20",
|
||||
"pokemon": "inkay",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 50.4
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
@@ -787,7 +661,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-krabby-1-51",
|
||||
"id": "shallows-krabby-1-50",
|
||||
"level": "8-20",
|
||||
"pokemon": "krabby",
|
||||
"presets": [
|
||||
@@ -813,7 +687,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-krabby-2-52",
|
||||
"id": "shallows-krabby-2-51",
|
||||
"level": "8-20",
|
||||
"pokemon": "krabby",
|
||||
"presets": [
|
||||
@@ -839,7 +713,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-krabby-3-53",
|
||||
"id": "shallows-krabby-3-52",
|
||||
"level": "8-20",
|
||||
"pokemon": "krabby",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -865,7 +739,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-krabby-4-54",
|
||||
"id": "shallows-krabby-4-53",
|
||||
"level": "8-20",
|
||||
"pokemon": "krabby",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1122,7 +996,7 @@
|
||||
],
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pincurchin-1-55",
|
||||
"id": "shallows-pincurchin-1-54",
|
||||
"level": "19-20",
|
||||
"pokemon": "pincurchin",
|
||||
"presets": [
|
||||
@@ -1151,7 +1025,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pincurchin-2-56",
|
||||
"id": "shallows-pincurchin-2-55",
|
||||
"level": "19-20",
|
||||
"pokemon": "pincurchin",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1169,7 +1043,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pincurchin-3-57",
|
||||
"id": "shallows-pincurchin-3-56",
|
||||
"level": "19-20",
|
||||
"pokemon": "pincurchin",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1197,7 +1071,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pincurchin-4-58",
|
||||
"id": "shallows-pincurchin-4-57",
|
||||
"level": "19-20",
|
||||
"pokemon": "pincurchin",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1217,7 +1091,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pincurchin-5-59",
|
||||
"id": "shallows-pincurchin-5-58",
|
||||
"level": "19-20",
|
||||
"pokemon": "pincurchin",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1240,7 +1114,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-1-60",
|
||||
"id": "shallows-pyukumuku-1-59",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"presets": [
|
||||
@@ -1266,7 +1140,7 @@
|
||||
"minY": 48,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-2-61",
|
||||
"id": "shallows-pyukumuku-2-60",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"presets": [
|
||||
@@ -1285,7 +1159,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-3-62",
|
||||
"id": "shallows-pyukumuku-3-61",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"presets": [
|
||||
@@ -1311,7 +1185,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-4-63",
|
||||
"id": "shallows-pyukumuku-4-62",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1342,7 +1216,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-5-64",
|
||||
"id": "shallows-pyukumuku-5-63",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1367,7 +1241,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-6-65",
|
||||
"id": "shallows-pyukumuku-6-64",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1400,7 +1274,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-pyukumuku-7-66",
|
||||
"id": "shallows-pyukumuku-7-65",
|
||||
"level": "16-20",
|
||||
"pokemon": "pyukumuku",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1438,6 +1312,75 @@
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_cold_ocean",
|
||||
"#cobblemon:is_frozen_ocean"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_ocean_monument"
|
||||
],
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-10-22",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 1000.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_cold_ocean",
|
||||
"#cobblemon:is_frozen_ocean"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"maxY": 62,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_ocean_monument"
|
||||
],
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-2-14",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 480.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"minLureLevel": 1,
|
||||
@@ -1454,7 +1397,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-3-14",
|
||||
"id": "shallows-qwilfish-3-15",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1477,7 +1420,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-4-15",
|
||||
"id": "shallows-qwilfish-4-16",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1491,6 +1434,38 @@
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_cold_ocean",
|
||||
"#cobblemon:is_frozen_ocean"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": true,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_ocean_monument"
|
||||
],
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-5-17",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 1000.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"maxLureLevel": 1,
|
||||
"minLureLevel": 0
|
||||
},
|
||||
"multiplier": 0.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"structures": [
|
||||
@@ -1508,7 +1483,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-6-16",
|
||||
"id": "shallows-qwilfish-6-18",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"presets": [
|
||||
@@ -1524,6 +1499,58 @@
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_cold_ocean",
|
||||
"#cobblemon:is_frozen_ocean"
|
||||
]
|
||||
},
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_ocean"
|
||||
],
|
||||
"canSeeSky": false,
|
||||
"maxSkyLight": 15,
|
||||
"maxY": 62,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_ocean_monument"
|
||||
],
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-7-19",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"type": "pokemon",
|
||||
"weight": 480.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shallows-qwilfish-77",
|
||||
"type": "pokemon",
|
||||
"pokemon": "qwilfish hisuian",
|
||||
"presets": [
|
||||
"water"
|
||||
],
|
||||
"spawnablePositionType": "submerged",
|
||||
"bucket": "rare",
|
||||
"level": "19-20",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "shallows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"minLureLevel": 1,
|
||||
@@ -1542,7 +1569,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-8-17",
|
||||
"id": "shallows-qwilfish-8-20",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1567,7 +1594,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-qwilfish-9-18",
|
||||
"id": "shallows-qwilfish-9-21",
|
||||
"level": "19-20",
|
||||
"pokemon": "qwilfish",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1633,7 +1660,7 @@
|
||||
"minX": 0,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-shellos-east-1-19",
|
||||
"id": "shallows-shellos-east-1-23",
|
||||
"level": "8-20",
|
||||
"pokemon": "shellos sea=east",
|
||||
"presets": [
|
||||
@@ -1660,7 +1687,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-shellos-west-4-20",
|
||||
"id": "shallows-shellos-west-4-24",
|
||||
"level": "8-20",
|
||||
"pokemon": "shellos sea=west",
|
||||
"presets": [
|
||||
@@ -1681,7 +1708,7 @@
|
||||
"maxX": 0,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-shellos-west-5-21",
|
||||
"id": "shallows-shellos-west-5-25",
|
||||
"level": "8-20",
|
||||
"pokemon": "shellos sea=west",
|
||||
"presets": [
|
||||
@@ -1702,7 +1729,7 @@
|
||||
"maxX": 0,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-shellos-west-6-22",
|
||||
"id": "shallows-shellos-west-6-26",
|
||||
"level": "8-20",
|
||||
"pokemon": "shellos sea=west",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1729,7 +1756,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-shellos-west-8-23",
|
||||
"id": "shallows-shellos-west-8-27",
|
||||
"level": "8-20",
|
||||
"pokemon": "shellos sea=west",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1758,7 +1785,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wiglett-1-24",
|
||||
"id": "shallows-wiglett-1-28",
|
||||
"level": "5-20",
|
||||
"pokemon": "wiglett",
|
||||
"presets": [
|
||||
@@ -1784,7 +1811,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wiglett-2-25",
|
||||
"id": "shallows-wiglett-2-29",
|
||||
"level": "5-20",
|
||||
"pokemon": "wiglett",
|
||||
"presets": [
|
||||
@@ -1816,7 +1843,7 @@
|
||||
"timeRange": "day",
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wingull-1-67",
|
||||
"id": "shallows-wingull-1-66",
|
||||
"level": "5-20",
|
||||
"pokemon": "wingull",
|
||||
"presets": [
|
||||
@@ -1841,7 +1868,7 @@
|
||||
"timeRange": "day",
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wingull-2-68",
|
||||
"id": "shallows-wingull-2-67",
|
||||
"level": "5-20",
|
||||
"pokemon": "wingull",
|
||||
"presets": [
|
||||
@@ -1861,7 +1888,7 @@
|
||||
"isPokeSnack": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wishiwashi-1-69",
|
||||
"id": "shallows-wishiwashi-1-68",
|
||||
"level": "5-20",
|
||||
"pokemon": "wishiwashi",
|
||||
"presets": [
|
||||
@@ -1883,7 +1910,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wishiwashi-2-70",
|
||||
"id": "shallows-wishiwashi-2-69",
|
||||
"level": "5-20",
|
||||
"pokemon": "wishiwashi",
|
||||
"presets": [
|
||||
@@ -1903,7 +1930,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wishiwashi-3-71",
|
||||
"id": "shallows-wishiwashi-3-70",
|
||||
"level": "5-20",
|
||||
"pokemon": "wishiwashi",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1922,7 +1949,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "shallows"
|
||||
},
|
||||
"id": "shallows-wishiwashi-4-72",
|
||||
"id": "shallows-wishiwashi-4-71",
|
||||
"level": "5-20",
|
||||
"pokemon": "wishiwashi",
|
||||
"spawnablePositionType": "fishing",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -780,6 +780,21 @@
|
||||
"zone": "sunscorch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "sunscorch-growlithe-83",
|
||||
"type": "pokemon",
|
||||
"pokemon": "growlithe hisuian",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "30-45",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "sunscorch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
@@ -2020,6 +2035,21 @@
|
||||
"multiplier": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "sunscorch-tauros-82",
|
||||
"type": "pokemon",
|
||||
"pokemon": "tauros paldean bull_breed=blaze",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "30-45",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "sunscorch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "ultra-rare",
|
||||
"condition": {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-appletun-1-102",
|
||||
"id": "westmarch-appletun-1-107",
|
||||
"level": "21-25",
|
||||
"pokemon": "appletun",
|
||||
"presets": [
|
||||
@@ -51,7 +51,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-appletun-2-103",
|
||||
"id": "westmarch-appletun-2-108",
|
||||
"level": "21-25",
|
||||
"pokemon": "appletun",
|
||||
"presets": [
|
||||
@@ -80,7 +80,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-applin-1-98",
|
||||
"id": "westmarch-applin-1-103",
|
||||
"level": "15-25",
|
||||
"pokemon": "applin",
|
||||
"presets": [
|
||||
@@ -108,7 +108,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-applin-2-99",
|
||||
"id": "westmarch-applin-2-104",
|
||||
"level": "15-25",
|
||||
"pokemon": "applin",
|
||||
"presets": [
|
||||
@@ -350,6 +350,62 @@
|
||||
"type": "pokemon",
|
||||
"weight": 27.0
|
||||
},
|
||||
{
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-chansey-1-46",
|
||||
"level": "17-25",
|
||||
"pokemon": "chansey",
|
||||
"presets": [
|
||||
"natural",
|
||||
"urban"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 62.6,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_civilization"
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-chansey-2-47",
|
||||
"level": "17-25",
|
||||
"pokemon": "chansey",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 62.6,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
@@ -482,7 +538,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "westmarch-dedenne-142",
|
||||
"id": "westmarch-dedenne-147",
|
||||
"type": "pokemon",
|
||||
"pokemon": "dedenne",
|
||||
"presets": [
|
||||
@@ -715,7 +771,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dipplin-1-104",
|
||||
"id": "westmarch-dipplin-1-109",
|
||||
"level": "24-25",
|
||||
"pokemon": "dipplin",
|
||||
"presets": [
|
||||
@@ -743,7 +799,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dipplin-2-105",
|
||||
"id": "westmarch-dipplin-2-110",
|
||||
"level": "24-25",
|
||||
"pokemon": "dipplin",
|
||||
"presets": [
|
||||
@@ -771,7 +827,7 @@
|
||||
"minY": -31,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-1-120",
|
||||
"id": "westmarch-dratini-1-125",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"presets": [
|
||||
@@ -805,7 +861,7 @@
|
||||
"minY": -31,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-2-121",
|
||||
"id": "westmarch-dratini-2-126",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"presets": [
|
||||
@@ -840,7 +896,7 @@
|
||||
"minY": -31,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-3-122",
|
||||
"id": "westmarch-dratini-3-127",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -864,7 +920,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-4-123",
|
||||
"id": "westmarch-dratini-4-128",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"presets": [
|
||||
@@ -897,7 +953,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-5-124",
|
||||
"id": "westmarch-dratini-5-129",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"presets": [
|
||||
@@ -931,7 +987,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-dratini-6-125",
|
||||
"id": "westmarch-dratini-6-130",
|
||||
"level": "15-25",
|
||||
"pokemon": "dratini",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -939,7 +995,7 @@
|
||||
"weight": 16.8
|
||||
},
|
||||
{
|
||||
"id": "westmarch-emolga-143",
|
||||
"id": "westmarch-emolga-148",
|
||||
"type": "pokemon",
|
||||
"pokemon": "emolga",
|
||||
"presets": [
|
||||
@@ -968,7 +1024,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-blue-4-111",
|
||||
"id": "westmarch-flabebe-blue-4-116",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=blue",
|
||||
"presets": [
|
||||
@@ -998,7 +1054,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-orange-3-110",
|
||||
"id": "westmarch-flabebe-orange-3-115",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=orange",
|
||||
"presets": [
|
||||
@@ -1028,7 +1084,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-pink-6-113",
|
||||
"id": "westmarch-flabebe-pink-6-118",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=pink",
|
||||
"presets": [
|
||||
@@ -1058,7 +1114,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-red-1-108",
|
||||
"id": "westmarch-flabebe-red-1-113",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=red",
|
||||
"presets": [
|
||||
@@ -1088,7 +1144,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-white-5-112",
|
||||
"id": "westmarch-flabebe-white-5-117",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=white",
|
||||
"presets": [
|
||||
@@ -1118,7 +1174,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flabebe-yellow-2-109",
|
||||
"id": "westmarch-flabebe-yellow-2-114",
|
||||
"level": "15-25",
|
||||
"pokemon": "flabebe flower=yellow",
|
||||
"presets": [
|
||||
@@ -1153,7 +1209,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flapple-1-100",
|
||||
"id": "westmarch-flapple-1-105",
|
||||
"level": "21-25",
|
||||
"pokemon": "flapple",
|
||||
"presets": [
|
||||
@@ -1181,7 +1237,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-flapple-2-101",
|
||||
"id": "westmarch-flapple-2-106",
|
||||
"level": "21-25",
|
||||
"pokemon": "flapple",
|
||||
"presets": [
|
||||
@@ -1205,7 +1261,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-blue-4-117",
|
||||
"id": "westmarch-floette-blue-4-122",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=blue",
|
||||
"presets": [
|
||||
@@ -1235,7 +1291,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-orange-3-116",
|
||||
"id": "westmarch-floette-orange-3-121",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=orange",
|
||||
"presets": [
|
||||
@@ -1265,7 +1321,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-pink-6-119",
|
||||
"id": "westmarch-floette-pink-6-124",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=pink",
|
||||
"presets": [
|
||||
@@ -1295,7 +1351,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-red-1-114",
|
||||
"id": "westmarch-floette-red-1-119",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=red",
|
||||
"presets": [
|
||||
@@ -1325,7 +1381,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-white-5-118",
|
||||
"id": "westmarch-floette-white-5-123",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=white",
|
||||
"presets": [
|
||||
@@ -1355,7 +1411,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-floette-yellow-2-115",
|
||||
"id": "westmarch-floette-yellow-2-120",
|
||||
"level": "19-25",
|
||||
"pokemon": "floette flower=yellow",
|
||||
"presets": [
|
||||
@@ -1383,7 +1439,7 @@
|
||||
"minY": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gabite-1-130",
|
||||
"id": "westmarch-gabite-1-135",
|
||||
"level": "24-25",
|
||||
"pokemon": "gabite",
|
||||
"presets": [
|
||||
@@ -1404,7 +1460,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gabite-2-131",
|
||||
"id": "westmarch-gabite-2-136",
|
||||
"level": "24-25",
|
||||
"pokemon": "gabite",
|
||||
"presets": [
|
||||
@@ -1437,7 +1493,7 @@
|
||||
"minY": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gabite-3-132",
|
||||
"id": "westmarch-gabite-3-137",
|
||||
"level": "24-25",
|
||||
"pokemon": "gabite",
|
||||
"presets": [
|
||||
@@ -1469,7 +1525,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gabite-4-133",
|
||||
"id": "westmarch-gabite-4-138",
|
||||
"level": "24-25",
|
||||
"pokemon": "gabite",
|
||||
"presets": [
|
||||
@@ -1479,6 +1535,21 @@
|
||||
"type": "pokemon",
|
||||
"weight": 18.0
|
||||
},
|
||||
{
|
||||
"id": "westmarch-geodude-151",
|
||||
"type": "pokemon",
|
||||
"pokemon": "geodude alolan",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"bucket": "rare",
|
||||
"level": "15-25",
|
||||
"weight": 10,
|
||||
"condition": {
|
||||
"zone": "westmarch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
@@ -1491,7 +1562,7 @@
|
||||
"minY": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gible-1-126",
|
||||
"id": "westmarch-gible-1-131",
|
||||
"level": "15-25",
|
||||
"pokemon": "gible",
|
||||
"presets": [
|
||||
@@ -1512,7 +1583,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gible-2-127",
|
||||
"id": "westmarch-gible-2-132",
|
||||
"level": "15-25",
|
||||
"pokemon": "gible",
|
||||
"presets": [
|
||||
@@ -1545,7 +1616,7 @@
|
||||
"minY": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gible-3-128",
|
||||
"id": "westmarch-gible-3-133",
|
||||
"level": "15-25",
|
||||
"pokemon": "gible",
|
||||
"presets": [
|
||||
@@ -1577,7 +1648,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gible-4-129",
|
||||
"id": "westmarch-gible-4-134",
|
||||
"level": "15-25",
|
||||
"pokemon": "gible",
|
||||
"presets": [
|
||||
@@ -1607,7 +1678,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-6-134",
|
||||
"id": "westmarch-goomy-6-139",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1633,7 +1704,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-12-137",
|
||||
"id": "westmarch-goomy-hisui-12-142",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1660,7 +1731,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-13-138",
|
||||
"id": "westmarch-goomy-hisui-13-143",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1686,7 +1757,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-14-139",
|
||||
"id": "westmarch-goomy-hisui-14-144",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1712,7 +1783,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-15-140",
|
||||
"id": "westmarch-goomy-hisui-15-145",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1738,7 +1809,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-16-141",
|
||||
"id": "westmarch-goomy-hisui-16-146",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1772,7 +1843,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-8-135",
|
||||
"id": "westmarch-goomy-hisui-8-140",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"presets": [
|
||||
@@ -1800,7 +1871,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-goomy-hisui-9-136",
|
||||
"id": "westmarch-goomy-hisui-9-141",
|
||||
"level": "15-25",
|
||||
"pokemon": "goomy region_bias=hisui",
|
||||
"presets": [
|
||||
@@ -1843,7 +1914,7 @@
|
||||
"isPokeSnack": true,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gyarados-1-91",
|
||||
"id": "westmarch-gyarados-1-96",
|
||||
"level": "20-25",
|
||||
"pokemon": "gyarados",
|
||||
"presets": [
|
||||
@@ -1866,7 +1937,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gyarados-2-92",
|
||||
"id": "westmarch-gyarados-2-97",
|
||||
"level": "20-25",
|
||||
"pokemon": "gyarados",
|
||||
"presets": [
|
||||
@@ -1888,7 +1959,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gyarados-3-93",
|
||||
"id": "westmarch-gyarados-3-98",
|
||||
"level": "20-25",
|
||||
"pokemon": "gyarados",
|
||||
"presets": [
|
||||
@@ -1908,7 +1979,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gyarados-4-94",
|
||||
"id": "westmarch-gyarados-4-99",
|
||||
"level": "20-25",
|
||||
"pokemon": "gyarados",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -1927,13 +1998,71 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-gyarados-5-95",
|
||||
"id": "westmarch-gyarados-5-100",
|
||||
"level": "20-25",
|
||||
"pokemon": "gyarados",
|
||||
"spawnablePositionType": "fishing",
|
||||
"type": "pokemon",
|
||||
"weight": 10.0
|
||||
},
|
||||
{
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-happiny-1-48",
|
||||
"level": "15-25",
|
||||
"pokemon": "happiny",
|
||||
"presets": [
|
||||
"natural",
|
||||
"urban"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 34.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"isPokeSnack": true,
|
||||
"maxSkyLight": 15,
|
||||
"minSkyLight": 8,
|
||||
"structures": [
|
||||
"#cobblemon:spawns/is_civilization"
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-happiny-2-49",
|
||||
"level": "15-25",
|
||||
"pokemon": "happiny",
|
||||
"presets": [
|
||||
"natural"
|
||||
],
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 34.0,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"timeRange": "night"
|
||||
},
|
||||
"multiplier": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
@@ -2016,7 +2145,7 @@
|
||||
"isPokeSnack": true,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-1-45",
|
||||
"id": "westmarch-magikarp-1-50",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp",
|
||||
"presets": [
|
||||
@@ -2039,7 +2168,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-2-46",
|
||||
"id": "westmarch-magikarp-2-51",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp",
|
||||
"presets": [
|
||||
@@ -2061,7 +2190,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-3-47",
|
||||
"id": "westmarch-magikarp-3-52",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp",
|
||||
"presets": [
|
||||
@@ -2081,7 +2210,7 @@
|
||||
"canSeeSky": true,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-4-48",
|
||||
"id": "westmarch-magikarp-4-53",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2100,7 +2229,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-5-49",
|
||||
"id": "westmarch-magikarp-5-54",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2117,7 +2246,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-apricot-stripes-15-71",
|
||||
"id": "westmarch-magikarp-apricot-stripes-15-76",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=apricot-stripes",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2155,7 +2284,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-apricot-tiger-15-69",
|
||||
"id": "westmarch-magikarp-apricot-tiger-15-74",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=apricot-tiger",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2193,7 +2322,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-apricot-zebra-15-70",
|
||||
"id": "westmarch-magikarp-apricot-zebra-15-75",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=apricot-zebra",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2232,7 +2361,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-forehead-20-81",
|
||||
"id": "westmarch-magikarp-black-forehead-20-86",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2275,7 +2404,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-forehead-21-83",
|
||||
"id": "westmarch-magikarp-black-forehead-21-88",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2295,7 +2424,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-forehead-22-85",
|
||||
"id": "westmarch-magikarp-black-forehead-22-90",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2332,7 +2461,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-mask-20-82",
|
||||
"id": "westmarch-magikarp-black-mask-20-87",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2375,7 +2504,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-mask-21-84",
|
||||
"id": "westmarch-magikarp-black-mask-21-89",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2395,7 +2524,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-black-mask-22-86",
|
||||
"id": "westmarch-magikarp-black-mask-22-91",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=black-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2431,7 +2560,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-blue-raindrops-23-88",
|
||||
"id": "westmarch-magikarp-blue-raindrops-23-93",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=blue-raindrops",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2468,7 +2597,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-blue-saucy-23-87",
|
||||
"id": "westmarch-magikarp-blue-saucy-23-92",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=blue-saucy",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2505,7 +2634,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-brown-stripes-16-74",
|
||||
"id": "westmarch-magikarp-brown-stripes-16-79",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=brown-stripes",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2543,7 +2672,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-brown-tiger-16-72",
|
||||
"id": "westmarch-magikarp-brown-tiger-16-77",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=brown-tiger",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2581,7 +2710,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-brown-zebra-16-73",
|
||||
"id": "westmarch-magikarp-brown-zebra-16-78",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=brown-zebra",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2619,7 +2748,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-calico-orange-gold-10-56",
|
||||
"id": "westmarch-magikarp-calico-orange-gold-10-61",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=calico-orange-gold",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2654,7 +2783,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-calico-orange-white-9-53",
|
||||
"id": "westmarch-magikarp-calico-orange-white-9-58",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=calico-orange-white",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2691,7 +2820,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-calico-orange-white-black-9-54",
|
||||
"id": "westmarch-magikarp-calico-orange-white-black-9-59",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=calico-orange-white-black",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2728,7 +2857,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-calico-white-orange-9-55",
|
||||
"id": "westmarch-magikarp-calico-white-orange-9-60",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=calico-white-orange",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2765,7 +2894,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-gray-bubbles-13-63",
|
||||
"id": "westmarch-magikarp-gray-bubbles-13-68",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=gray-bubbles",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2803,7 +2932,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-gray-diamonds-13-64",
|
||||
"id": "westmarch-magikarp-gray-diamonds-13-69",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=gray-diamonds",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2841,7 +2970,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-gray-patches-13-65",
|
||||
"id": "westmarch-magikarp-gray-patches-13-70",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=gray-patches",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2879,7 +3008,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-dapples-11-59",
|
||||
"id": "westmarch-magikarp-orange-dapples-11-64",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-dapples",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2919,7 +3048,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-forehead-17-75",
|
||||
"id": "westmarch-magikarp-orange-forehead-17-80",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2954,7 +3083,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-forehead-18-77",
|
||||
"id": "westmarch-magikarp-orange-forehead-18-82",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -2974,7 +3103,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-forehead-19-79",
|
||||
"id": "westmarch-magikarp-orange-forehead-19-84",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-forehead",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3003,7 +3132,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-mask-17-76",
|
||||
"id": "westmarch-magikarp-orange-mask-17-81",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3038,7 +3167,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-mask-18-78",
|
||||
"id": "westmarch-magikarp-orange-mask-18-83",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3058,7 +3187,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-mask-19-80",
|
||||
"id": "westmarch-magikarp-orange-mask-19-85",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-mask",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3086,7 +3215,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-orca-11-58",
|
||||
"id": "westmarch-magikarp-orange-orca-11-63",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-orca",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3125,7 +3254,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-orange-two-tone-11-57",
|
||||
"id": "westmarch-magikarp-orange-two-tone-11-62",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=orange-two-tone",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3164,7 +3293,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-pink-dapples-12-62",
|
||||
"id": "westmarch-magikarp-pink-dapples-12-67",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=pink-dapples",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3202,7 +3331,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-pink-orca-12-61",
|
||||
"id": "westmarch-magikarp-pink-orca-12-66",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=pink-orca",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3240,7 +3369,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-pink-two-tone-12-60",
|
||||
"id": "westmarch-magikarp-pink-two-tone-12-65",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=pink-two-tone",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3278,7 +3407,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-purple-bubbles-14-66",
|
||||
"id": "westmarch-magikarp-purple-bubbles-14-71",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=purple-bubbles",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3316,7 +3445,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-purple-diamonds-14-67",
|
||||
"id": "westmarch-magikarp-purple-diamonds-14-72",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=purple-diamonds",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3354,7 +3483,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-purple-patches-14-68",
|
||||
"id": "westmarch-magikarp-purple-patches-14-73",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=purple-patches",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3393,7 +3522,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-skelly-6-50",
|
||||
"id": "westmarch-magikarp-skelly-6-55",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=skelly",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3425,7 +3554,7 @@
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-skelly-7-51",
|
||||
"id": "westmarch-magikarp-skelly-7-56",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=skelly",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3445,7 +3574,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-skelly-8-52",
|
||||
"id": "westmarch-magikarp-skelly-8-57",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=skelly",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3470,7 +3599,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-violet-raindrops-24-90",
|
||||
"id": "westmarch-magikarp-violet-raindrops-24-95",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=violet-raindrops",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3506,7 +3635,7 @@
|
||||
"minLureLevel": 1,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-magikarp-violet-saucy-24-89",
|
||||
"id": "westmarch-magikarp-violet-saucy-24-94",
|
||||
"level": "15-25",
|
||||
"pokemon": "magikarp magikarp_jump=violet-saucy",
|
||||
"spawnablePositionType": "fishing",
|
||||
@@ -3670,7 +3799,7 @@
|
||||
"minSkyLight": 8,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-munchlax-1-96",
|
||||
"id": "westmarch-munchlax-1-101",
|
||||
"level": "24-25",
|
||||
"pokemon": "munchlax",
|
||||
"presets": [
|
||||
@@ -3694,7 +3823,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-munchlax-2-97",
|
||||
"id": "westmarch-munchlax-2-102",
|
||||
"level": "24-25",
|
||||
"pokemon": "munchlax",
|
||||
"presets": [
|
||||
@@ -3759,6 +3888,35 @@
|
||||
"multiplier": 1.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"anticondition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_deep_dark"
|
||||
]
|
||||
},
|
||||
"bucket": "rare",
|
||||
"condition": {
|
||||
"biomes": [
|
||||
"#cobblemon:is_overworld"
|
||||
],
|
||||
"maxSkyLight": 7,
|
||||
"maxY": 0,
|
||||
"minSkyLight": 0,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-noibat-2-45",
|
||||
"level": "15-25",
|
||||
"pokemon": "noibat",
|
||||
"spawnablePositionType": "grounded",
|
||||
"type": "pokemon",
|
||||
"weight": 37.5,
|
||||
"weightMultiplier": {
|
||||
"condition": {
|
||||
"isPokeSnack": true
|
||||
},
|
||||
"multiplier": 2.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": "common",
|
||||
"condition": {
|
||||
@@ -3969,7 +4127,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "westmarch-pineco-144",
|
||||
"id": "westmarch-pineco-149",
|
||||
"type": "pokemon",
|
||||
"pokemon": "pineco",
|
||||
"presets": [
|
||||
@@ -3985,7 +4143,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "westmarch-rotom-145",
|
||||
"id": "westmarch-rotom-150",
|
||||
"type": "pokemon",
|
||||
"pokemon": "rotom",
|
||||
"presets": [
|
||||
@@ -4347,7 +4505,7 @@
|
||||
"minY": 48,
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-teddiursa-1-106",
|
||||
"id": "westmarch-teddiursa-1-111",
|
||||
"level": "15-25",
|
||||
"pokemon": "teddiursa",
|
||||
"presets": [
|
||||
@@ -4378,7 +4536,7 @@
|
||||
],
|
||||
"zone": "westmarch"
|
||||
},
|
||||
"id": "westmarch-teddiursa-2-107",
|
||||
"id": "westmarch-teddiursa-2-112",
|
||||
"level": "15-25",
|
||||
"pokemon": "teddiursa",
|
||||
"presets": [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "articuno",
|
||||
"pokemon": "articuno",
|
||||
"level": 70,
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "articuno_galarian",
|
||||
"pokemon": "articuno galarian",
|
||||
"level": 80,
|
||||
"zone": "highlands"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "eternatus",
|
||||
"pokemon": "eternatus",
|
||||
"level": 90,
|
||||
"zone": "the_end"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "hooh",
|
||||
"pokemon": "hooh",
|
||||
"level": 80,
|
||||
"zone": "highlands"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "ironleaves",
|
||||
"pokemon": "ironleaves",
|
||||
"level": 70,
|
||||
"zone": "jungle"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "latias",
|
||||
"pokemon": "latias",
|
||||
"level": 70,
|
||||
"zone": "highlands"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "latios",
|
||||
"pokemon": "latios",
|
||||
"level": 70,
|
||||
"zone": "highlands"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "lugia",
|
||||
"pokemon": "lugia",
|
||||
"level": 80,
|
||||
"zone": "deep_sea"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "mew",
|
||||
"pokemon": "mew",
|
||||
"level": 80,
|
||||
"zone": "jungle"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "mewtwo",
|
||||
"pokemon": "mewtwo",
|
||||
"level": 90,
|
||||
"zone": "deep_dark"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "moltres",
|
||||
"pokemon": "moltres",
|
||||
"level": 70,
|
||||
"zone": "basalt"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "moltres_galarian",
|
||||
"pokemon": "moltres galarian",
|
||||
"level": 80,
|
||||
"zone": "deep_dark"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "poipole",
|
||||
"pokemon": "poipole",
|
||||
"level": 70,
|
||||
"zone": "the_end"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "rayquaza",
|
||||
"pokemon": "rayquaza",
|
||||
"level": 90,
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "regice",
|
||||
"pokemon": "regice",
|
||||
"level": 70,
|
||||
"zone": "frostpeak"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "regidrago",
|
||||
"pokemon": "regidrago",
|
||||
"level": 70,
|
||||
"zone": "sunscorch"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "regieleki",
|
||||
"pokemon": "regieleki",
|
||||
"level": 70,
|
||||
"zone": "westmarch"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "regigigas",
|
||||
"pokemon": "regigigas",
|
||||
"level": 80,
|
||||
"zone": "deep_dark"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "regirock",
|
||||
"pokemon": "regirock",
|
||||
"level": 70,
|
||||
"zone": "dripstone"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "registeel",
|
||||
"pokemon": "registeel",
|
||||
"level": 70,
|
||||
"zone": "deep_dark"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "walkingwake",
|
||||
"pokemon": "walkingwake",
|
||||
"level": 70,
|
||||
"zone": "mire"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "xerneas",
|
||||
"pokemon": "xerneas",
|
||||
"level": 80,
|
||||
"zone": "jungle"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "zapdos",
|
||||
"pokemon": "zapdos",
|
||||
"level": 70,
|
||||
"zone": "westmarch"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "zapdos_galarian",
|
||||
"pokemon": "zapdos galarian",
|
||||
"level": 80,
|
||||
"zone": "jungle"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"_comment": "Generated by tools/summons.py from world/summons.json; do not edit by hand.",
|
||||
"species": "zarude",
|
||||
"pokemon": "zarude",
|
||||
"level": 80,
|
||||
"zone": "jungle"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"_comment": "Balance numbers the companion mod's server code reads (dev.cobblesurvival.Tuning). /reload applies them; see docs/balance.md.",
|
||||
"boss_evs": 170,
|
||||
"guardian_wake_range": 96.0,
|
||||
"guardian_leash": 24.0,
|
||||
"end_boss_wake_range": 160.0,
|
||||
"end_boss_leash": 24.0,
|
||||
"summon_spacing": 96.0,
|
||||
"wanderer_range": 64.0,
|
||||
"attack_battle_grace_ticks": 100
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"_comment":"Generated by tools/zone_layout.py from world/zones.json; do not edit by hand.","seed":"isle-26","radius":1700,"step":24,"size":142,"zones":[{"id":"button_isle","name":"Button Isle","levels":[2,10]},{"id":"frostpeak","name":"Frostpeak Range","levels":[40,55]},{"id":"sunscorch","name":"Sunscorch","levels":[30,45]},{"id":"mire","name":"Mangrove Mire","levels":[25,38]},{"id":"jungle","name":"Verdant Jungle","levels":[18,30]},{"id":"highlands","name":"Highland Meadows","levels":[25,35]},{"id":"westmarch","name":"Westmarch","levels":[15,25]},{"id":"southwold","name":"Southwold","levels":[8,18]},{"id":"shallows","name":"Shallows","levels":[5,20]},{"id":"deep_sea","name":"Deep Sea","levels":[30,50]}],"caves":[{"id":"lush_caves","name":"Lush Caves","levels":[12,28],"biome":"minecraft:lush_caves"},{"id":"dripstone","name":"Dripstone Depths","levels":[22,38],"biome":"minecraft:dripstone_caves"},{"id":"deep_dark","name":"Deep Dark","levels":[45,60],"biome":"minecraft:deep_dark"},{"id":"basalt","name":"Basalt Furnace","levels":[55,70],"biome":"minecraft:basalt_deltas"}],"dimensions":[{"id":"the_end","name":"The End","levels":[50,70],"dimension":"minecraft:the_end"}],"level_pressure":{"grace_levels":5,"per_level":0.1,"minimum":0.1},"wanderers":{"grace_levels":10},"samples":{"button_isle":[-116,null,-92],"frostpeak":[628,null,-572],"sunscorch":[1372,null,-236],"mire":[652,null,1012],"jungle":[844,null,196],"highlands":[100,null,-1076],"westmarch":[-428,null,-1100],"southwold":[-116,null,1108],"shallows":[1468,null,4],"deep_sea":[-1316,null,4],"lush_caves":[-1028,0,292],"dripstone":[940,40,-212],"deep_dark":[484,-52,-1124],"basalt":[1252,0,508]},"biome_samples":{"frostpeak":[580,null,-524],"sunscorch":[-44,null,-452],"mire":[652,null,1012],"jungle":[844,null,196]},"grid_rle":[[9,912],[8,22],[9,115],[8,32],[9,106],[8,40],[9,99],[8,18],[5,13],[8,15],[9,96],[8,12],[6,1],[5,22],[1,2],[8,12],[9,92],[8,9],[6,5],[5,27],[1,1],[8,10],[9,90],[8,8],[6,6],[5,29],[1,1],[8,10],[9,85],[8,7],[6,10],[5,21],[1,3],[5,7],[1,1],[5,1],[8,10],[9,80],[8,6],[6,1],[8,1],[6,11],[5,20],[1,8],[5,2],[1,2],[5,2],[8,11],[9,76],[8,5],[6,2],[8,1],[6,13],[5,19],[1,11],[5,5],[8,1],[5,2],[8,8],[9,73],[8,5],[6,2],[8,1],[6,15],[5,18],[1,11],[5,12],[8,7],[9,60],[8,2],[9,7],[8,5],[6,1],[8,2],[6,17],[5,18],[1,9],[5,15],[8,8],[9,57],[8,3],[9,5],[8,5],[6,2],[2,4],[6,16],[5,18],[1,6],[5,20],[8,7],[9,55],[8,12],[6,5],[2,2],[6,17],[5,46],[8,7],[9,52],[8,12],[6,6],[2,1],[6,19],[5,47],[8,6],[9,49],[8,12],[6,8],[2,1],[6,19],[5,25],[1,5],[5,18],[8,6],[9,47],[8,11],[6,1],[8,1],[6,27],[5,26],[1,6],[5,19],[8,5],[9,45],[8,10],[2,1],[8,2],[2,1],[6,27],[5,24],[1,8],[5,20],[8,6],[9,42],[8,9],[2,7],[6,26],[5,24],[1,9],[5,20],[8,6],[9,40],[8,9],[2,8],[6,26],[5,24],[1,11],[5,19],[8,6],[9,39],[8,8],[2,9],[6,26],[5,23],[1,5],[5,1],[1,5],[5,21],[8,5],[9,40],[8,6],[2,10],[6,26],[5,23],[1,12],[5,21],[8,5],[9,39],[8,5],[2,10],[6,27],[5,23],[1,13],[5,21],[8,5],[9,37],[8,5],[2,12],[6,26],[5,26],[1,10],[5,22],[8,5],[9,35],[8,5],[2,11],[6,28],[5,27],[1,7],[5,25],[8,5],[9,34],[8,5],[2,10],[6,29],[5,28],[1,5],[5,27],[8,5],[9,32],[8,5],[2,1],[6,2],[2,4],[6,33],[5,29],[1,3],[5,29],[8,4],[9,31],[8,5],[6,41],[5,62],[8,4],[9,30],[8,4],[6,42],[5,20],[1,1],[5,42],[8,4],[9,28],[8,5],[6,42],[5,16],[1,3],[5,1],[1,5],[5,38],[8,4],[9,27],[8,5],[6,43],[5,12],[1,18],[5,11],[1,1],[5,22],[8,4],[9,26],[8,5],[6,43],[5,12],[1,17],[5,3],[1,4],[5,2],[1,1],[5,2],[1,1],[5,22],[8,4],[9,25],[8,5],[6,44],[5,12],[1,18],[5,1],[1,5],[5,1],[1,3],[5,1],[1,1],[5,23],[8,4],[9,24],[8,4],[6,45],[5,12],[1,11],[5,1],[1,6],[5,1],[1,3],[5,2],[1,4],[5,25],[8,4],[9,23],[8,5],[6,44],[5,5],[1,1],[5,7],[1,13],[5,1],[1,2],[5,7],[1,2],[5,1],[1,2],[5,26],[8,4],[9,22],[8,4],[6,45],[5,4],[1,4],[5,5],[1,9],[5,2],[1,2],[5,9],[1,8],[5,24],[8,4],[9,21],[8,4],[2,3],[6,44],[5,1],[1,8],[5,3],[1,7],[5,4],[1,1],[5,9],[1,9],[5,24],[8,5],[9,20],[8,4],[2,4],[6,43],[5,1],[1,8],[5,4],[1,5],[5,3],[1,3],[5,9],[1,9],[5,25],[8,4],[9,19],[8,4],[2,5],[6,43],[1,9],[5,4],[1,10],[5,10],[1,9],[5,25],[8,5],[9,1],[8,1],[9,16],[8,4],[6,48],[1,10],[5,4],[1,3],[5,2],[1,3],[5,10],[1,10],[5,26],[8,6],[9,16],[8,4],[6,48],[1,10],[5,3],[2,1],[5,1],[2,2],[5,15],[1,9],[5,27],[8,7],[9,14],[8,4],[6,49],[1,9],[2,7],[5,16],[1,9],[5,4],[1,3],[5,21],[8,6],[9,14],[8,4],[6,48],[1,9],[2,8],[5,16],[1,9],[5,3],[1,4],[5,21],[8,6],[9,14],[8,3],[6,49],[1,7],[2,10],[5,16],[1,9],[5,3],[1,5],[5,20],[2,1],[8,6],[9,12],[8,4],[6,50],[1,5],[2,11],[5,16],[1,9],[5,3],[1,5],[5,19],[2,2],[8,6],[9,12],[8,4],[6,50],[5,2],[1,2],[2,13],[5,15],[1,10],[5,3],[1,4],[5,18],[2,3],[8,6],[9,12],[8,3],[6,51],[5,3],[2,12],[1,4],[5,14],[1,10],[5,23],[2,4],[8,7],[9,10],[8,4],[6,51],[5,3],[2,11],[1,5],[5,15],[1,9],[5,23],[2,5],[8,6],[9,10],[8,3],[6,52],[5,3],[2,11],[1,1],[5,2],[1,3],[5,15],[1,10],[5,20],[2,6],[8,6],[9,10],[8,3],[6,52],[5,3],[2,3],[8,3],[9,3],[8,2],[1,1],[5,3],[1,1],[5,16],[1,12],[5,17],[2,7],[8,6],[9,12],[8,1],[4,2],[6,50],[5,3],[2,1],[8,6],[9,2],[8,5],[5,1],[1,1],[5,16],[1,3],[5,5],[1,5]Line truncated
|
||||
{"_comment":"Generated by tools/zone_layout.py from world/zones.json; do not edit by hand.","seed":"isle-26","radius":1700,"step":24,"size":142,"zones":[{"id":"button_isle","name":"Button Isle","levels":[2,10]},{"id":"frostpeak","name":"Frostpeak Range","levels":[40,55]},{"id":"sunscorch","name":"Sunscorch","levels":[30,45]},{"id":"mire","name":"Mangrove Mire","levels":[25,38]},{"id":"jungle","name":"Verdant Jungle","levels":[18,30]},{"id":"highlands","name":"Highland Meadows","levels":[25,35]},{"id":"westmarch","name":"Westmarch","levels":[15,25]},{"id":"southwold","name":"Southwold","levels":[8,18]},{"id":"deep_sea","name":"Deep Sea","levels":[30,50]},{"id":"shallows","name":"Shallows","levels":[5,20]}],"caves":[{"id":"lush_caves","name":"Lush Caves","levels":[12,28],"biome":"minecraft:lush_caves"},{"id":"dripstone","name":"Dripstone Depths","levels":[22,38],"biome":"minecraft:dripstone_caves"},{"id":"deep_dark","name":"Deep Dark","levels":[45,60],"biome":"minecraft:deep_dark"},{"id":"basalt","name":"Basalt Furnace","levels":[55,70],"biome":"minecraft:basalt_deltas"}],"dimensions":[{"id":"the_end","name":"The End","levels":[50,70],"dimension":"minecraft:the_end"}],"level_pressure":{"grace_levels":5,"per_level":0.1,"minimum":0.1},"wanderers":{"grace_levels":10},"samples":{"button_isle":[-116,null,-92],"frostpeak":[628,null,-572],"sunscorch":[1372,null,-236],"mire":[652,null,1012],"jungle":[844,null,196],"highlands":[100,null,-1076],"westmarch":[-428,null,-1100],"southwold":[-116,null,1108],"deep_sea":[1540,null,-20],"shallows":[-500,null,4],"lush_caves":[-1028,0,292],"dripstone":[940,40,-212],"deep_dark":[484,-52,-1124],"basalt":[1252,0,508]},"biome_samples":{"frostpeak":[580,null,-524],"sunscorch":[-44,null,-452],"mire":[652,null,1012],"jungle":[844,null,196]},"grid_rle":[[8,1199],[9,16],[8,120],[9,9],[5,13],[9,6],[8,110],[9,7],[6,1],[5,22],[1,2],[9,4],[8,102],[9,7],[6,5],[5,27],[1,1],[9,4],[8,96],[9,8],[6,6],[5,29],[1,1],[9,4],[8,91],[9,7],[6,10],[5,21],[1,3],[5,7],[1,1],[5,1],[9,4],[8,86],[9,6],[6,1],[9,1],[6,11],[5,20],[1,8],[5,2],[1,2],[5,2],[9,5],[8,82],[9,5],[6,2],[9,1],[6,13],[5,19],[1,11],[5,5],[9,1],[5,2],[9,3],[8,78],[9,5],[6,2],[9,1],[6,15],[5,18],[1,11],[5,12],[9,2],[8,74],[9,5],[6,1],[9,2],[6,17],[5,18],[1,9],[5,15],[9,3],[8,70],[9,5],[6,2],[2,4],[6,16],[5,18],[1,6],[5,20],[9,3],[8,67],[9,4],[6,5],[2,2],[6,17],[5,46],[9,2],[8,64],[9,5],[6,6],[2,1],[6,19],[5,47],[9,2],[8,61],[9,4],[6,8],[2,1],[6,19],[5,25],[1,5],[5,18],[9,2],[8,58],[9,4],[6,1],[9,1],[6,27],[5,26],[1,6],[5,19],[9,2],[8,55],[9,3],[2,1],[9,2],[2,1],[6,27],[5,24],[1,8],[5,20],[9,2],[8,53],[9,2],[2,7],[6,26],[5,24],[1,9],[5,20],[9,2],[8,51],[9,2],[2,8],[6,26],[5,24],[1,11],[5,19],[9,2],[8,49],[9,2],[2,9],[6,26],[5,23],[1,5],[5,1],[1,5],[5,21],[9,2],[8,47],[9,2],[2,10],[6,26],[5,23],[1,12],[5,21],[9,2],[8,45],[9,2],[2,10],[6,27],[5,23],[1,13],[5,21],[9,2],[8,43],[9,2],[2,12],[6,26],[5,26],[1,10],[5,22],[9,2],[8,41],[9,2],[2,11],[6,28],[5,27],[1,7],[5,25],[9,2],[8,40],[9,2],[2,10],[6,29],[5,28],[1,5],[5,27],[9,2],[8,38],[9,2],[2,1],[6,2],[2,4],[6,33],[5,29],[1,3],[5,29],[9,1],[8,37],[9,2],[6,41],[5,62],[9,1],[8,35],[9,2],[6,42],[5,20],[1,1],[5,42],[9,1],[8,34],[9,2],[6,42],[5,16],[1,3],[5,1],[1,5],[5,38],[9,1],[8,33],[9,2],[6,43],[5,12],[1,18],[5,11],[1,1],[5,22],[9,1],[8,32],[9,2],[6,43],[5,12],[1,17],[5,3],[1,4],[5,2],[1,1],[5,2],[1,1],[5,22],[9,2],[8,30],[9,2],[6,44],[5,12],[1,18],[5,1],[1,5],[5,1],[1,3],[5,1],[1,1],[5,23],[9,1],[8,29],[9,2],[6,45],[5,12],[1,11],[5,1],[1,6],[5,1],[1,3],[5,2],[1,4],[5,25],[9,2],[8,28],[9,2],[6,44],[5,5],[1,1],[5,7],[1,13],[5,1],[1,2],[5,7],[1,2],[5,1],[1,2],[5,26],[9,1],[8,27],[9,2],[6,45],[5,4],[1,4],[5,5],[1,9],[5,2],[1,2],[5,9],[1,8],[5,24],[9,2],[8,26],[9,1],[2,3],[6,44],[5,1],[1,8],[5,3],[1,7],[5,4],[1,1],[5,9],[1,9],[5,24],[9,2],[8,25],[9,2],[2,4],[6,43],[5,1],[1,8],[5,4],[1,5],[5,3],[1,3],[5,9],[1,9],[5,25],[9,2],[8,24],[9,1],[2,5],[6,43],[1,9],[5,4],[1,10],[5,10],[1,9],[5,25],[9,2],[8,24],[9,1],[6,48],[1,10],[5,4],[1,3],[5,2],[1,3],[5,10],[1,10],[5,26],[9,1],[8,23],[9,2],[6,48],[1,10],[5,3],[2,1],[5,1],[2,2],[5,15],[1,9],[5,27],[9,2],[8,22],[9,1],[6,49],[1,9],[2,7],[5,16],[1,9],[5,4],[1,3],[5,21],[9,1],[8,21],[9,2],[6,48],[1,9],[2,8],[5,16],[1,9],[5,3],[1,4],[5,21],[9,1],[8,21],[9,1],[6,49],[1,7],[2,10],[5,16],[1,9],[5,3],[1,5],[5,20],[2,1],[9,1],[8,20],[9,1],[6,50],[1,5],[2,11],[5,16],[1,9],[5,3],[1,5],[5,19],[2,2],[9,1],[8,20],[9,1],[6,50],[5,2],[1,2],[2,13],[5,15],[1,10],[5,3],[1,4],[5,18],[2,3],[9,1],[8,19],[9,1],[6,51],[5,3],[2,12],[1,4],[5,14],[1,10],[5,23],[2,4],[9,2],[8,18],[9,1],[6,51],[5,3],[2,11],[1,5],[5,15],[1,9],[5,23],[2,5],[9,1],[8,18],[6,52],[5,3],[2,11],[1,1],[5,2],[1,3],[5,15],[1,10],[5,20],[2,6],[9,1],[8,17],[9,1],[6,52],[5,3],[2,3],[9,8],[1,1],[5,3],[1,1],[5,16],[1,12],[5,17],[2,7],[9,2],[8,16],[9,1],[4,2],[6,50],[5,3],[2,1],[9,13],[5,1],[1,1],[5,16],[1,3],[5,5],[1,5],[5,15],[2,8],[5,1],[9,1],[8,16],[9,1],[4,5],[6,47],[5,1],[9,18],[5,6],[4,1],[5,9],[1,2],[5,7],[1,2],[5Line truncated
|
||||
@@ -62,6 +62,7 @@ JEI declares Minecraft `[1.21, 1.21.1)` in every 1.21.1 build, which a strict re
|
||||
| Cobblemon Info for REI / JEI / EMI | 8 | 2.32.14+neoforge (MR `bxd0xXZI`) | MR | ✅ Declares Cobblemon `[1.7.1,)`. **In the pack** (2026-11-03, Adrian), on both sides: it reads the server's live spawn pool (the pack's zone pools); a client on a server without it falls back to Cobblemon's own spawn files. Shows spawns, drops, evolutions; doesn't know the pack's zone rule, so it lists a spawn's biomes, not its zone. Self-test `jei_addon_shows_island_spawns` |
|
||||
| Quests & Teams Fixes (flexible-mode soft-lock fix) | 8 | — | MR | ❌ Only 1.20.1 builds exist. Test the soft-lock on FTB Quests 2101.1.37 ourselves |
|
||||
| Iris Shaders | 9 | 1.8.14-beta.1+1.21.1-neoforge (MR `KduFYu4t`) | MR + CF | n/a (no Cobblemon dependency). **In the pack** (2026-11-09), client only. A beta, and the only NeoForge 1.21.1 build for Sodium 0.8; built against the pack's Sodium 0.8.13 |
|
||||
| spark | 9 | 1.10.124 (MR `v5qtqRQi`, CF file `6225208`) | MR + CF | n/a (no Cobblemon dependency). **In the pack** (2026-11-09), server only: `tools/perf_test.py` and profiling during playtests |
|
||||
| Complementary Reimagined (shader pack) | 9 | r5.9.3 (MR `Bqen1mJX`) | MR + CF | n/a. **In the pack** (2026-11-09), off by default; client only |
|
||||
| spark | 9 | 1.10.124 (MR `v5qtqRQi`, CF `6225208`) | MR + CF | n/a |
|
||||
|
||||
|
||||
+13
-2
@@ -1,6 +1,6 @@
|
||||
# Balance values
|
||||
|
||||
Every tunable lives in a config file or in this table. **Placeholder** means guessed and not yet approved by Adrian; never treat a placeholder as decided.
|
||||
Every tunable lives in a config file or in this table. **Placeholder** means guessed and not yet approved by Adrian; never treat a placeholder as decided. Anything applied in `datapacks/core`, `config/` or a KubeJS server script can be changed on a running server without players updating: see "Balancing on a running server" in the README.
|
||||
|
||||
| Value | Current | Status | Where it is applied | Notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@@ -20,7 +20,7 @@ Every tunable lives in a config file or in this table. **Placeholder** means gue
|
||||
| Lines pinned to a zone | Durant, Torkoal, Heatmor → Basalt Furnace; Ditto → Deep Dark, rare bucket in slime chunks and ultra-rare elsewhere (Placeholder; Adrian, 2026-10-31: rare, in an endgame area) | Decided (Adrian, 2026-10-30) | `world/zones.json` → `species` | Basalt's own lines were all too big for its caves. Ditto breeds with anything, so it's an endgame find; Deep Dark target 21 lines, Button Isle 19, so nothing else moved |
|
||||
| Comfortable temperature range | 0.25–1.51 (Cold Sweat units; Cold Sweat: 0.4–1.51) | Decided (Adrian, 2026-10-30) | `config/coldsweat/main.toml` | Keeps the lowlands' nights livable without gear |
|
||||
| Windswept hills temperature | 45–72 °F (Cold Sweat: 24–58 °F) | Decided (Adrian, 2026-10-30) | `config/coldsweat/world.toml` | They lie in the starter lowlands too |
|
||||
| Insulating armour | fur set (cold 10/14/12/10), hide set (heat 10/14/12/10), as Cold Sweat; leather armour none (Cold Sweat: 5/7/6/5 both) | Decided (Adrian, 2026-10-30) | `config/coldsweat/item.toml` | Frostpeak at noon: about 1 min bare, 14 min in fur; Sunscorch at noon: about 2 min bare, 25 min in hide |
|
||||
| Insulating armour | fur set (cold 20/28/24/20), hide set (heat 20/28/24/20), twice Cold Sweat's, to keep the protected time when body temperature went ×3 (2026-11-09); leather armour none (Cold Sweat: 5/7/6/5 both) | Decided (Adrian, 2026-10-30) | `config/coldsweat/item.toml` | Frostpeak at noon: about 1 min bare, 14 min in fur; Sunscorch at noon: about 2 min bare, 25 min in hide |
|
||||
| Sewn-in insulation | goat fur 2 cold, hoglin hide 2 heat, wool 0.5 cold, leather 0.5/0.5 (Cold Sweat: wool 1.5, leather 1/1, rabbit hide 1.5 heat) | Decided (Adrian, 2026-10-30) | `config/coldsweat/item.toml` | |
|
||||
| Goat Fur / Hoglin Hide recipes | 8 wool + 1 Heat Rock → 8 Goat Fur; 8 leather + 1 Icy Rock → 8 Hoglin Hide (a set takes 24) | Decided (Adrian, 2026-10-30) | `kubejs/server_scripts/insulation.js` | Heat and Icy Rocks come from the guardians before Frostpeak and Sunscorch (archaeology finds too) |
|
||||
| Guardian respawn | 5 hours of game time after being beaten | Decided (Adrian, 2026-10-29) | `world/zones.json` → `guardian_respawn_hours` | Fixed spots |
|
||||
@@ -82,3 +82,14 @@ Every tunable lives in a config file or in this table. **Placeholder** means gue
|
||||
| Dev client heap (Prism) | 6 GB | Tooling | `tools/prism_instance.py --memory` | Not a gameplay value |
|
||||
|
||||
Sections to fill as phases land: hazard damage (Phase 3), field-move cooldowns and caps (Phase 5), machine output (Phase 6), boss attack damage and telegraph times (Phase 7).
|
||||
| Boss training | 170 EVs each in HP, Defense, Sp. Def | Placeholder | `tuning/server.json` → `boss_evs` | |
|
||||
| Guardian and End boss wake range, leash | guardians 96 / 24 blocks, End boss 160 / 24 | Placeholder | `tuning/server.json` | |
|
||||
| Summon spacing | one summoned legend per 96 blocks | Placeholder | `tuning/server.json` → `summon_spacing` | |
|
||||
| Wanderer range | 64 blocks | Placeholder | `tuning/server.json` → `wanderer_range` | |
|
||||
| Battle XP | ×1 (Cobblemon: ×2), half of before (Adrian, 2026-11-09) | Placeholder | `config/cobblemon/main.json` → `experienceMultiplier` | |
|
||||
| Body temperature speed | ×3 (Cold Sweat: ×1), so heat and cold bite sooner (Adrian, 2026-11-09) | Placeholder | `config/coldsweat/main.toml` → `Rate Multiplier` | |
|
||||
| Attacked by a wild Pokémon | starts a battle instead of hurting; a player with no Pokémon able to fight is hurt (Adrian, 2026-11-09) | Decided | `catching/AttackBattles.kt`; after fleeing 5 s of grace: `tuning/server.json` → `attack_battle_grace_ticks` | |
|
||||
| Bosses' damage | only from battles; players, fire, falls and drowning do nothing (Adrian, 2026-11-09) | Decided | `boss/BossToughness.kt` | |
|
||||
| Sea levels | channels between the islands are Shallows (Lv 5–20), everything beyond 1,500 blocks from the centre is Deep Sea (Lv 30–50) (Adrian, 2026-11-09) | Decided | `world/zones.json` → `deep_sea.rule.beyond` | Run `tools/zone_layout.py`, `zone_spawns.py`, `zone_bosses.py`, `zone_ores.py` after changing it |
|
||||
| Starter | only faints: never removed, can't be released or sacrificed, a Revival Charm on it isn't used up (Adrian, 2026-11-09) | Decided | `permadeath/Permadeath.kt`, `mixin/HardcorePartyMixin.java` | Cobblemon's own record of each player's starter |
|
||||
| Regional forms | a rare spawn (`homeless`) unless a line or an evolution brings them; Galarian Zapdos, Articuno, Moltres from Sigils, tier 2 (Adrian, 2026-11-09) | Placeholder | `world/zones.json` → `homeless.zones`, `world/summons.json` | Five regional evolutions got an island biome: `data/cobblemon/tags/worldgen/biome/evolution/regional/` |
|
||||
@@ -12,8 +12,27 @@
|
||||
- **Shaders:** Iris and Complementary Reimagined are installed, off by default.
|
||||
- **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`.
|
||||
|
||||
- **Reproducible build:** `tools/check_reproducible.py` compares two clean builds byte for byte.
|
||||
- **Balance on the server:** `tools/server_update.py` updates a server without touching clients. Automated: `tools/check_server_update.py`, `drop_sources_reach_the_client`.
|
||||
- **Performance:** `docs/performance.md`, from `tools/perf_test.py`.
|
||||
|
||||
- **Adrian's list of 2026-11-09:** shortcuts, drop pages link to Cobblemon Info, real drop chances, bosses only hurt in battles, half the battle XP, low channels, quicker hazards, no item dots on the map, attacks start battles. Automated: `drop_chances_match_cobblemon`, `starter_only_faints`, `regional_forms_are_obtainable`, `new_players_get_the_quest_book`, `bosses_only_take_battle_damage`, `wild_attack_starts_a_battle`, `wild_attack_hurts_without_a_party`, `mire_poisons_without_waders`.
|
||||
|
||||
## In game (Prism instance)
|
||||
|
||||
- [ ] Let your starter faint: it stays, fainted, and a Healing Machine wakes it. Try to release it and sacrifice it: both are refused. Any other Pokémon is still lost.
|
||||
- [ ] A new player spawns with the quest book. Its first page walks through the basics; the second lists every HM.
|
||||
- [ ] Are the regional forms' zones and rarity right? They're in `docs/zone-species.md`.
|
||||
- [ ] R on an item shows recipes, U its uses, J the map, § the quest book (left of 1 on a Swiss keyboard).
|
||||
- [ ] On JEI's Island Drops, click the Pokémon in the corner: Cobblemon Info's pages for it open.
|
||||
- [ ] Hit a guardian with a sword, set it on fire: nothing happens.
|
||||
- [ ] Anger a wild Pokémon: its attack starts a battle, you lose no hearts. With no Pokémon able to fight it hurts you.
|
||||
- [ ] Walk into Sunscorch or Frostpeak: temperature moves about three times as fast. Into the Mire: poison starts within half a second.
|
||||
- [ ] Dropped items show no dot on the minimap.
|
||||
- [ ] The channels between the islands have Lv 5–20 Pokémon, the open sea around the island Lv 30–50.
|
||||
|
||||
- [ ] Join a dedicated server with this pack: JEI's Island Drops, Boss Rewards and the rest are filled in. Change a drop, run `tools/server_update.py --install`, `/reload`: the page changes without relogging. (The client side of this can't be tested without a game window.)
|
||||
|
||||
- [ ] 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).
|
||||
|
||||
+16
-4
@@ -81,8 +81,8 @@ A zone decides **which Pokémon spawn** and **at what levels**: Cobblemon's own
|
||||
| Highland Meadows | north-east around the peaks (20 %) | plains, forest, cherry grove | 25–35 | none | leather (Ponyta) for heat insulation, Hard Stone (Larvitar) | Rapidash, Lv 40 |
|
||||
| Sunscorch | the desert and badlands pockets: north of the ring channel, the north-east and north-west coasts (3.4 %) | desert, badlands | 30–45 | **heat** (Cold Sweat): heat insulation, Fire Resistance potions | armadillo scutes (Sandshrew), Soft Sand, magma cream (Numel) | Hippowdon, Lv 50 |
|
||||
| Frostpeak Range | the jagged peaks plus the windswept hills and cherry groves right next to them (6 %) | jagged peaks | 40–55 | **cold** (Cold Sweat): wool insulation; warming food and potions | Never-Melt Ice (Snover), Razor Claw (Sneasel) | Abomasnow, Lv 60 |
|
||||
| Shallows | coast and beaches | ocean, lukewarm and warm ocean, beach | 5–20 | none | fish, kelp | none |
|
||||
| Deep Sea | ring and band channels, outer sea | deep ocean | 30–50 | none | Heart Scale-type rarities | Lapras or Wailord, Lv 55 |
|
||||
| Shallows | coast, beaches and the channels between the islands | ocean within 1,500 blocks, beach | 5–20 | none | fish, kelp | none |
|
||||
| Deep Sea | the sea surrounding the island (Adrian, 2026-11-09: the ring and band channels moved to the Shallows) | ocean beyond 1,500 blocks | 30–50 | none | Heart Scale-type rarities | Lapras or Wailord, Lv 55 |
|
||||
| Lush Caves | under the whole island | lush caves | 12–28 | none | glow berries, mushrooms (Paras) | none |
|
||||
| Dripstone Depths | under the whole island | dripstone caves | 22–38 | none | Dragon Fang (Bagon, Gible) | none |
|
||||
| Deep Dark | under the cold and temperate land, deep (since 2026-10-30) | deep dark | 45–60 | darkness (vanilla sculk; no Warden) | | Sableye or Spiritomb, Lv 62 |
|
||||
@@ -306,7 +306,7 @@ Adrian's choices: Cobblemon tasks come from **Cobblemon Quests Reloaded** (a mai
|
||||
|
||||
**The quest book** is written in `docs/quests.yaml` and generated by `tools/quests.py` (run by `tools/build.py`) into `pack/config/ftbquests/quests/` in FTB Quests 2101's format (`data.snbt`, `chapter_groups.snbt`, `chapters/*.snbt`, every title and text in `lang/en_us.snbt`). Chapter, quest and task ids are derived from their names in the YAML, so they stay the same across regenerations and players keep their progress. The whole book is in flexible progression with lock icons off: dependencies show as lines but never block. No rewards.
|
||||
|
||||
- **Chapters** (18, 74 quests on 2026-11-03): Getting Started (welcome, first catch, Poké Balls, a full team, the Revival Charm and permadeath, healing, level 10, drops and JEI, evolution, on to Southwold); one per zone and the End; Pokémon Machines (how to build one, then each of the five); Base and Jobs (Pasture Block, PC). A zone chapter (`zone: <id>`) starts with the template quests `zone_chapter`, filled from the zone's data: reach it (levels, hazard), catch three of its Pokémon (any species of its spawn pool), mine its ores (one item task per vein), beat its guardian (species, level, drops, the boss bar). Chapters add their own quests (Mire Waders for the Mire; Eyes of Ender and Eternatus for the End).
|
||||
- **Chapters** (20 with the HMs page of 2026-11-09, listing all 39 field moves): Getting Started (welcome, keys, the starter, battling, first catch, Poké Balls, a full team, the Revival Charm and permadeath, healing, level 10, drops and JEI, evolution, on to Southwold); one per zone and the End; Pokémon Machines (how to build one, then each of the five); Base and Jobs (Pasture Block, PC). A zone chapter (`zone: <id>`) starts with the template quests `zone_chapter`, filled from the zone's data: reach it (levels, hazard), catch three of its Pokémon (any species of its spawn pool), mine its ores (one item task per vein), beat its guardian (species, level, drops, the boss bar). Chapters add their own quests (Mire Waders for the Mire; Eyes of Ender and Eternatus for the End).
|
||||
- **Tasks:** Cobblemon actions through Quests Reloaded (catch, defeat, evolve, level up), items, and the pack's own events: hidden advancements `cobblesurvival:events/zone/<zone>`, `guardian/<zone>`, `machine/<core>` and `end_boss` (no display, no toast; written by `tools/quests.py`), granted by the companion mod (`QuestEvents`: a player's zone once a second, a guardian's battle winners, a machine's owner when a Pokémon goes in, the End boss's winners). They unlock nothing.
|
||||
- **Node positions:** Adrian moves nodes in the in-game editor; `tools/quests.py --pull <folder>` records every node that isn't where the generator would put it in the `layout` block at the end of `docs/quests.yaml`. `tools/prism_instance.py` does that from the instance before each sync, and rebuilds if something moved, so a sync never undoes his layout (docs/quests.yaml then needs committing). Titles, texts, tasks and dependencies edited in game are overwritten.
|
||||
- **Self-tests:** `quest_book_loads` (FTB Quests on the server has the book in flexible mode, every task type is known, every item and advancement exists), `quest_events_mark_zones`, and the guardian and furnace tests check their events.
|
||||
@@ -320,7 +320,7 @@ Adrian's choices: Cobblemon tasks come from **Cobblemon Quests Reloaded** (a mai
|
||||
|
||||
The pages sit next to Cobblemon Info's own, and Adrian likes having both, so they share its look (2026-11-09): the same dark panel with a thin border, a header with icon, white title, warm tag on the right and a divider, the same text colours and spacing. Long text wraps. JEI gives all pages of a category the height of the tallest.
|
||||
|
||||
JEI runs on the client, which on a dedicated server doesn't have the server's data, so the pages are built from `assets/cobblesurvival/jei/drop_sources.json` inside the mod, written by `tools/jei_sources.py` from the pack's own files (Cobblemon's species drops with the pack's species additions, the zone spawn pools, guardians, the End boss, the machines, `world/zones.json` ores, Cobblebase's zone tables). Which ore a vein gives now lives in `world/zones.json` → `ores` → `item` (the quest book reads it too). Self-tests: `drop_sources_match_the_server` compares the file with the live server (every listed Pokémon's drops and zones, spawns with own drops, guardians, the End boss, altar tiers) and fails when it's stale; `jei_pages_build` builds every page with the same code JEI calls and checks every item exists.
|
||||
JEI runs on the client, which on a dedicated server doesn't have the server's data, so the server sends the pages' data to each player on joining and after `/reload`: `data/cobblesurvival/drop_sources/pages.json`, written by `tools/jei_sources.py` from the pack's own files (Cobblemon's species drops with the pack's species additions, the zone spawn pools, guardians, the End boss, the machines, `world/zones.json` ores, Cobblebase's zone tables). Which ore a vein gives now lives in `world/zones.json` → `ores` → `item` (the quest book reads it too). Self-tests: `drop_sources_match_the_server` compares the file with the live server (every listed Pokémon's drops and zones, spawns with own drops, guardians, the End boss, altar tiers) and fails when it's stale; `jei_pages_build` builds every page with the same code JEI calls and checks every item exists.
|
||||
|
||||
## Phase 9: balancing and release (Adrian, 2026-11-09)
|
||||
|
||||
@@ -341,6 +341,18 @@ Self-tests: `every_implemented_pokemon_is_obtainable` (every implemented species
|
||||
|
||||
**The respawn block** (Regenerating Ore Veins' regenerator, the placeholder where a mined vein block grows back) no longer stops the player's aim, so the ore behind it can be mined; it never had collision. While sneaking the player can still target it, which is how its info opens. A mixin returns an empty outline shape (`RegeneratorBlockMixin.java`, the mod's only one, targeting the class by name). Its model and glass come from `pack/kubejs/assets/regenerating_ore_veins/`, much fainter than the stained glass it had. Self-test `regenerator_lets_the_aim_through` casts a real ray at it.
|
||||
|
||||
### Drop chances on the JEI pages (Adrian, 2026-11-09)
|
||||
|
||||
The pages first showed each table's raw percentage, which isn't what a player gets. Cobblemon's `DropTable.getDrops` makes `amount` picks; in each it walks the table in order and takes the first entry that passes its percentage, so later entries are rarer than written. A count of 0 gives nothing. `tools/jei_sources.py` now works out the real chance of getting at least one, and the self-test `drop_chances_match_cobblemon` rolls Cobblemon's own code 4,000 times for every Pokémon and compares. The Pokémon in a page's corner is Cobblemon Info's own JEI ingredient, so clicking it opens that mod's pages.
|
||||
|
||||
### Balancing without a client update (Adrian, 2026-11-09)
|
||||
|
||||
Playtesters shouldn't have to reinstall for a number change. The server is the authority for everything that isn't drawn: Cobblemon, recipes, quests and Cold Sweat send clients what they need, and the pack's data pack now does too. A server can hold `config/cobblesurvival/datapack.zip`, which the companion mod loads in place of the copy in its jar, under the same pack id, so a world keeps it enabled. One file, so nothing left over from an older version; `/reload` applies it.
|
||||
|
||||
The JEI pages were the one thing clients took from the jar. Their data moved into the data pack; the server gzips it and sends it on joining and on `/reload` (`DropSources`). JEI can start before or after that packet, so the plugin both builds from what has arrived and swaps pages in the running JEI (`hideRecipes` and `addRecipes`). Numbers that only lived in Kotlin moved to `data/cobblesurvival/tuning/server.json` (`Tuning`).
|
||||
|
||||
`tools/server_update.py` packages this. It hashes what clients run or draw against `tools/client-baseline.json`, the last pack handed out, and refuses when they differ, rather than letting players find out from a crash or a missing texture.
|
||||
|
||||
## Open decisions for Adrian (proceeding with the default, flagged)
|
||||
|
||||
| Decision | Default used for now |
|
||||
|
||||
@@ -46,6 +46,7 @@ Read each license before adding a mod, and add a row here in the same change.
|
||||
| Cobbreeding | Ludichat, Fuzuki | "MIT, no AI, no reupload" ([text](https://fuzuki.dev/~fuzuki/mit-no-ai-no-reupload.txt)): MIT terms, but the mod may not be used to train or improve machine-learning models, and may not be reuploaded unchanged to CurseForge or Modrinth except bundled in a modpack | [Modrinth](https://modrinth.com/mod/cobbreeding), [source](https://gitlab.com/ludichat/Cobbreeding) | Phase 5: breeding (both). Referenced only, never copied. Not on CurseForge by the authors' choice |
|
||||
| Cloth Config API | shedaniel | LGPL-3.0-only | [Modrinth](https://modrinth.com/mod/cloth-config) | Cobbreeding dependency (client) |
|
||||
| Iris Shaders | IrisShaders (coderbot, IMS and contributors) | LGPL-3.0-only | [Modrinth](https://modrinth.com/mod/iris) | Phase 9: runs shader packs (client) |
|
||||
| spark | lucko and contributors | GPL-3.0-only | [Modrinth](https://modrinth.com/mod/spark) | Phase 9: server profiler for playtests (server only) |
|
||||
| Complementary Shaders - Reimagined | EminGT (Complementary Development) | [Complementary License Agreement](https://github.com/ComplementaryDevelopment/ComplementaryReimagined/blob/main/License.txt) | [Modrinth](https://modrinth.com/shader/complementary-reimagined) | Phase 9: the included shader pack, off by default (client). Allowed in modpacks unmodified and only through Modrinth's or CurseForge's own systems, never as an uploaded file |
|
||||
| Cobblemon Quests Reloaded | WinterWolfSV (original); community fork | CC-BY-NC-4.0 (jar metadata; the CurseForge page says "custom", the fork keeps the original's non-commercial license) | [CurseForge](https://www.curseforge.com/minecraft/mc-mods/cobblemon-quest-reloaded) | Phase 8: Cobblemon task types for FTB Quests (both). Not on Modrinth. Non-commercial: fine for this free pack |
|
||||
| Cobblemon Info for REI / JEI / EMI | Akkiruk | MIT | [Modrinth](https://modrinth.com/project/qekxLrOP) | Phase 8: spawns, drops and evolutions in JEI (both) |
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Performance
|
||||
|
||||
Generated by `tools/perf_test.py`: the island world template, a 6G heap, 120 s to settle, then 180 s measured tick by tick with spark profiling the server thread.
|
||||
Players are the self-tests' fake clients: the server ticks them, loads their chunks and spawns Pokémon around them. One walks a long ring round the island when there are several. They send and encode no packets and fight no battles, so read this as the cost of the world, not of the network.
|
||||
|
||||
A tick has 50 ms; over that, the server falls behind.
|
||||
|
||||
| Scenario | Players | Pastures × Pokémon each | Machines each | Mean tick | p95 | p99 | Worst | Ticks over 50 ms | Pokémon pastured / wild | Working machines | Loaded chunks |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| solo | 1 | 0 × 0 | 0 | 4.6 ms | 6.4 ms | 9.8 ms | 216 ms | 1 of 3900 | 0 / 18 | 0 of 0 | 2731 |
|
||||
| group | 5 | 4 × 8 | 5 | 19.2 ms | 25.7 ms | 33.3 ms | 269 ms | 2 of 3900 | 113 / 175 | 16 of 25 | 8704 |
|
||||
| stress | 8 | 6 × 16 | 10 | 38.3 ms | 50.4 ms | 54.9 ms | 322 ms | 215 of 3900 | 558 / 246 | 46 of 80 | 10763 |
|
||||
|
||||
## Where the server thread's busy time goes: solo
|
||||
|
||||
| Part | Share |
|
||||
| --- | --- |
|
||||
| Minecraft | 46.2 % |
|
||||
| Other mods | 39.0 % |
|
||||
| JVM and Kotlin | 11.4 % |
|
||||
| Cobblemon | 3.1 % |
|
||||
| Companion mod | 0.3 % |
|
||||
|
||||
Companion mod, heaviest methods including what they call:
|
||||
|
||||
- `dev.cobblesurvival.selftest.PerfLoad$$Lambda.0x000000080138d638.accept`: 2.02 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.register$onTickDone`: 2.00 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.onTickDone`: 2.00 %
|
||||
- `dev.cobblesurvival.progress.HmsGate$$Lambda.0x0000000801380c60.accept`: 0.12 %
|
||||
- `dev.cobblesurvival.progress.HmsGate.register$onServerTick`: 0.10 %
|
||||
- `dev.cobblesurvival.progress.HmsGate.onServerTick`: 0.10 %
|
||||
- `dev.cobblesurvival.progress.HmsGate.check`: 0.07 %
|
||||
- `dev.cobblesurvival.progress.QuestEvents$$Lambda.0x000000080137b338.accept`: 0.07 %
|
||||
|
||||
## Where the server thread's busy time goes: group
|
||||
|
||||
| Part | Share |
|
||||
| --- | --- |
|
||||
| Minecraft | 61.4 % |
|
||||
| Other mods | 20.6 % |
|
||||
| JVM and Kotlin | 13.2 % |
|
||||
| Cobblemon | 4.5 % |
|
||||
| Companion mod | 0.2 % |
|
||||
|
||||
Companion mod, heaviest methods including what they call:
|
||||
|
||||
- `dev.cobblesurvival.selftest.PerfLoad$$Lambda.0x0000000801394200.accept`: 1.34 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.register$onTickDone`: 1.32 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.onTickDone`: 1.32 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.walk`: 0.24 %
|
||||
- `dev.cobblesurvival.machine.MachineBlock$$Lambda.0x00000008022acee8.tick`: 0.13 %
|
||||
- `dev.cobblesurvival.machine.MachineBlock.getTicker$lambda$1`: 0.13 %
|
||||
- `dev.cobblesurvival.machine.MachineBlockEntity.serverTick`: 0.13 %
|
||||
- `dev.cobblesurvival.progress.CobblebaseGate$Gate.invoke`: 0.08 %
|
||||
|
||||
## Where the server thread's busy time goes: stress
|
||||
|
||||
| Part | Share |
|
||||
| --- | --- |
|
||||
| Minecraft | 58.4 % |
|
||||
| Other mods | 18.9 % |
|
||||
| JVM and Kotlin | 17.0 % |
|
||||
| Cobblemon | 5.5 % |
|
||||
| Companion mod | 0.2 % |
|
||||
|
||||
Companion mod, heaviest methods including what they call:
|
||||
|
||||
- `dev.cobblesurvival.selftest.PerfLoad$$Lambda.0x0000000801386618.accept`: 1.02 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.register$onTickDone`: 1.02 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.onTickDone`: 1.02 %
|
||||
- `dev.cobblesurvival.machine.MachineBlock$$Lambda.0x00000008022c2988.tick`: 0.31 %
|
||||
- `dev.cobblesurvival.machine.MachineBlock.getTicker$lambda$1`: 0.30 %
|
||||
- `dev.cobblesurvival.machine.MachineBlockEntity.serverTick`: 0.30 %
|
||||
- `dev.cobblesurvival.selftest.PerfLoad.walk`: 0.27 %
|
||||
- `dev.cobblesurvival.machine.MachineWork.tick`: 0.15 %
|
||||
+222
-7
@@ -83,16 +83,50 @@ chapters:
|
||||
This island has no zombies, no creepers and no cows: only Pokémon. Their drops are your food, string, leather and much more.
|
||||
The island is split into zones, each with its own Pokémon, levels, ores and a guardian. You start on Button Isle; the further out you go, the stronger everything gets.
|
||||
|
||||
This book suggests what to do next. Nothing in it is locked and it gives no rewards: do things in any order you like.
|
||||
This book suggests what to do next. Nothing in it is locked and it gives no rewards: do things in any order you like. This first page walks through how the island works; the next one lists every field move your Pokémon can lend you.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: first_partner
|
||||
title: A Partner
|
||||
- id: keys
|
||||
title: Keys Worth Knowing
|
||||
icon: minecraft:tripwire_hook
|
||||
after: [welcome]
|
||||
text: |
|
||||
§ (left of 1) opens this book. J opens the map.
|
||||
R sends out the Pokémon selected on the left of your screen, or calls it back. Up and down arrows pick another. N opens its summary.
|
||||
Over an item in your inventory or JEI, R shows how to get it and U what it's used for.
|
||||
V shows more detail on what you're looking at. M switches shaders on and off. Everything can be changed in Options > Controls.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Got it
|
||||
- id: starter
|
||||
title: Your First Partner
|
||||
icon: cobblemon:poke_ball
|
||||
after: [welcome]
|
||||
text: |
|
||||
Weaken a wild Pokémon in battle, then throw a Poké Ball at it.
|
||||
When you first arrive you choose a starter. Look after it: it is the only Pokémon on the island that can never be lost.
|
||||
When it faints it stays with you, fainted, until you heal it. It can't be released or sacrificed.
|
||||
Every other Pokémon is gone for good when it faints.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: battling
|
||||
title: Battling
|
||||
icon: minecraft:iron_sword
|
||||
after: [starter]
|
||||
text: |
|
||||
Look at a wild Pokémon and press R: your Pokémon goes out and the battle starts.
|
||||
Wild Pokémon can also come to you. One that attacks you starts a battle instead of hurting you, unless none of your Pokémon can fight: then you take the hit yourself.
|
||||
Pokémon stronger than your team hunt you; weaker ones keep their distance. In a battle you can run, and you get a few seconds' head start.
|
||||
tasks:
|
||||
- cobblemon: defeat
|
||||
amount: 1
|
||||
- id: first_partner
|
||||
title: Your First Catch
|
||||
icon: cobblemon:poke_ball
|
||||
after: [battling]
|
||||
text: |
|
||||
Weaken a wild Pokémon in battle, then throw a Poké Ball at it. The weaker it is, the easier it is to catch, and a Pokémon far above your team's level is much harder.
|
||||
tasks:
|
||||
- cobblemon: catch
|
||||
amount: 1
|
||||
@@ -105,6 +139,15 @@ chapters:
|
||||
tasks:
|
||||
- item: cobblemon:poke_ball
|
||||
count: 16
|
||||
- id: pc_pasture
|
||||
title: PC and Pastures
|
||||
icon: cobblemon:pasture
|
||||
after: [first_partner]
|
||||
text: |
|
||||
A PC holds the Pokémon that don't fit in your party. A Pasture Block lets them out to roam around your base.
|
||||
Pastured Pokémon also work: open the pasture and give them a skill, such as mining or gathering, and they bring in items. What they can find depends on the zone the pasture stands in, and JEI's Pasture Finds pages list it.
|
||||
tasks:
|
||||
- item: cobblemon:pasture
|
||||
- id: full_team
|
||||
title: A Full Team
|
||||
icon: cobblemon:great_ball
|
||||
@@ -119,8 +162,8 @@ chapters:
|
||||
icon: cobblesurvival:revival_charm
|
||||
after: [first_partner]
|
||||
text: |
|
||||
A Pokémon that faints is gone for good once the battle ends. Revives only help within that battle.
|
||||
A Pokémon holding a Revival Charm is saved once instead: the charm is used up and the Pokémon is healed. Give one to your most valuable partner.
|
||||
Apart from your starter, a Pokémon that faints is gone for good once the battle ends. Revives only help within that battle.
|
||||
A Pokémon holding a Revival Charm is saved once instead: the charm is used up and the Pokémon is healed. Give one to your most valuable partner. Your starter doesn't need one.
|
||||
tasks:
|
||||
- item: cobblesurvival:revival_charm
|
||||
- id: healing
|
||||
@@ -128,7 +171,7 @@ chapters:
|
||||
icon: cobblemon:healing_machine
|
||||
after: [first_partner]
|
||||
text: |
|
||||
Sleeping doesn't heal your Pokémon. A Healing Machine does, but it is expensive and recharges slowly; potions and berries help in between.
|
||||
Sleeping doesn't heal your Pokémon. A Healing Machine does, and it also wakes a fainted starter, but it is expensive and recharges slowly; potions and berries help in between.
|
||||
tasks:
|
||||
- item: cobblemon:healing_machine
|
||||
- id: level_10
|
||||
@@ -158,6 +201,46 @@ chapters:
|
||||
tasks:
|
||||
- cobblemon: evolve
|
||||
amount: 1
|
||||
- id: zones
|
||||
title: Zones and Guardians
|
||||
icon: minecraft:filled_map
|
||||
after: [level_10]
|
||||
text: |
|
||||
Each zone has its own Pokémon and level range; the book has a page for each. The channels between the islands are gentle, the open sea around them is not.
|
||||
Most zones have a guardian: a giant Pokémon with a health bar at the top of your screen when you are near. Only a battle can hurt it, and it can lose at most a quarter of its health to one attack. Beat it for its rewards; it comes back hours later.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: hazards
|
||||
title: Heat, Cold and Poison
|
||||
icon: minecraft:leather_chestplate
|
||||
after: [zones]
|
||||
text: |
|
||||
Some zones hurt to be in. Sunscorch is hot and Frostpeak is cold: your body temperature climbs or falls quickly there, so wear hide against heat and fur against cold.
|
||||
The Mangrove Mire poisons you unless you wear Mire Waders. Look these up in JEI with R.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: ores
|
||||
title: Ore Comes from Veins
|
||||
icon: minecraft:raw_iron
|
||||
after: [zones]
|
||||
text: |
|
||||
There is no ore in ordinary stone. Each zone has its own nodes and underground veins, which grow back after you mine them. JEI's Ore Veins pages show what grows where.
|
||||
A Pokémon that knows Rock Smash can mine a whole vein with one punch: see the HMs page of this book.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: machines
|
||||
title: Pokémon at Work
|
||||
icon: cobblesurvival:heat_core
|
||||
after: [zones]
|
||||
text: |
|
||||
Machines are small buildings with a Pokémon inside: a Large Furnace, a Generator, a Drop Synthesizer, a Sacrifice Altar and a Rebirth Chamber. Craft a core, use it, and ghost blocks show how to build around it.
|
||||
JEI lists each core's recipe, and each machine's own page says what it makes.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: beyond
|
||||
title: Beyond Button Isle
|
||||
icon: minecraft:compass
|
||||
@@ -168,6 +251,127 @@ chapters:
|
||||
- event: zone/southwold
|
||||
title: Reach Southwold
|
||||
|
||||
- id: hms
|
||||
title: HMs
|
||||
subtitle: Every field move your Pokémon can lend you
|
||||
icon: minecraft:iron_pickaxe
|
||||
quests:
|
||||
- id: how
|
||||
title: How HMs Work
|
||||
icon: minecraft:iron_pickaxe
|
||||
size: 1.5
|
||||
text: |
|
||||
Any Pokémon of yours that has learned one of these moves can use it for you outside battle. There is nothing to craft or learn yourself.
|
||||
Send the Pokémon out, sneak and right-click it, and pick Field moves. It lists what that Pokémon can do; hover over one to read about it. The top left of your screen shows what is active.
|
||||
|
||||
Partner HMs go to your empty hand: one per Pokémon, fired with a click.
|
||||
Auras and weather work on you while the Pokémon is out and within 24 blocks.
|
||||
Mount toggles help the Pokémon itself while you ride it.
|
||||
|
||||
Working makes a Pokémon hungry. Right-click it with any berry to feed it. If it works on an empty stomach it loses friendship, and at none it refuses.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: gathering
|
||||
title: Gathering and Building
|
||||
icon: minecraft:iron_axe
|
||||
after: [how]
|
||||
text: |
|
||||
Cut: fells a whole tree, or clears the plants in front of you.
|
||||
Rock Smash: breaks a stone block, or mines up to 32 connected ore in one punch, and veins still grow back.
|
||||
Dig: takes dirt, sand, gravel, clay and snow in one hit.
|
||||
Leafage: grows nearby crops, saplings and berry trees at once, and covers bare ground in flowers. Sneak to till soil instead.
|
||||
Stockpile: drinks water or lava, then spits it back out where you aim.
|
||||
Strength: pushes the block you aim at one block, in any direction, along with anything inside it.
|
||||
Tail Whip: sweeps suspicious sand or gravel clean in one go.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: handy
|
||||
title: Handy Tricks
|
||||
icon: minecraft:ender_pearl
|
||||
after: [how]
|
||||
text: |
|
||||
Teleport: blinks you up to 30 blocks to where you look (30 seconds to recharge).
|
||||
Charge: turns a block into a lasting redstone power source. Use it again to switch it off.
|
||||
Camouflage: makes you look like the creature you aim at for 15 minutes.
|
||||
Celebrate: fireworks over your head, for no reason at all.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: changing
|
||||
title: Changing a Pokémon
|
||||
icon: minecraft:nether_star
|
||||
after: [how]
|
||||
text: |
|
||||
Growth: makes the Pokémon you aim at one size bigger for good; aim elsewhere and you grow for 5 minutes.
|
||||
Minimize: the same, but smaller.
|
||||
Outrage: turns an XL Pokémon of yours into an Alpha.
|
||||
Calm Mind: settles an Alpha of yours back into an ordinary Pokémon.
|
||||
Decorate: one roll at making the Pokémon you aim at shiny for good, once an hour.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: exploring
|
||||
title: Exploring Auras
|
||||
icon: minecraft:spyglass
|
||||
after: [how]
|
||||
text: |
|
||||
Flash: lets you see in the dark. Needs the Dripstone gate.
|
||||
Dive: breathe, walk and mine at full speed underwater. Needs the Mire gate.
|
||||
Rock Climb: walls work like ladders, on foot or riding. Needs the Highlands gate.
|
||||
Magnet Rise: slow flight for building. Double-tap jump to lift off. Needs the Sunscorch gate.
|
||||
Lava Plume: the same as Dive, for lava, with fire immunity. Needs the Deep Dark gate.
|
||||
Jump: jump higher and land safely. Comes from Splash.
|
||||
Shadow Sneak: walk silently, unseen by pressure plates and sculk, at a reduced speed.
|
||||
Snowscape: snow under your feet, and still water freezes as you walk across it.
|
||||
|
||||
A gate means the Pokémon must have reached the top level of that zone, or hold one of its materials: its ore, evolution stone or guardian's drop.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: helping
|
||||
title: Helping Auras
|
||||
icon: minecraft:honey_bottle
|
||||
after: [how]
|
||||
text: |
|
||||
Absorb: loose items drift toward you.
|
||||
Lucky Chant: more from what you break and fish. Needs the Frostpeak gate.
|
||||
Sweet Scent: draws a wild Pokémon of the area about once a minute, like a Poké Snack. What the Pokémon holds works as seasoning.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: weather
|
||||
title: Weather
|
||||
icon: minecraft:snowball
|
||||
after: [how]
|
||||
text: |
|
||||
Only one weather runs at a time: the last one switched on wins.
|
||||
Rain Dance: real rain around you, anywhere; it puts out fires and counts for rain evolutions and spawns.
|
||||
Thunder: a thunderstorm around you.
|
||||
Sunny Day: keeps the sky clear.
|
||||
Blizzard: real snow falls and settles around you.
|
||||
Sandstorm: soul sand and honey don't slow you, and sand, gravel and clay break four times as fast.
|
||||
Mist: anything hunting you loses you once you're 8 blocks away. Needs the Mire gate.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
- id: mounts
|
||||
title: Mount Toggles
|
||||
icon: minecraft:saddle
|
||||
after: [how]
|
||||
text: |
|
||||
Switch these on from the Field moves of the Pokémon you ride. They stay on for that Pokémon.
|
||||
Surf: much faster through water.
|
||||
Tailwind: faster through the air.
|
||||
Agility: faster on land.
|
||||
Swift: reaches top speed twice as quickly.
|
||||
U-turn: turns far more sharply.
|
||||
Endure: double the stamina.
|
||||
tasks:
|
||||
- checkmark: true
|
||||
title: Read this
|
||||
|
||||
- id: button_isle
|
||||
zone: button_isle
|
||||
title: Button Isle
|
||||
@@ -359,3 +563,14 @@ chapters:
|
||||
Your PC holds every Pokémon not in your party. There is no remote access: you have to stand at a PC block.
|
||||
tasks:
|
||||
- item: cobblemon:pc
|
||||
|
||||
layout:
|
||||
getting_started/beyond: [6.0, 0.75]
|
||||
getting_started/drops: [4.0, 3.0]
|
||||
getting_started/evolve: [6.0, -0.75]
|
||||
getting_started/first_partner: [2.0, -0.75]
|
||||
getting_started/full_team: [4.0, -3.0]
|
||||
getting_started/healing: [4.0, 0.0]
|
||||
getting_started/level_10: [4.0, 1.5]
|
||||
getting_started/permadeath: [4.0, -1.5]
|
||||
getting_started/poke_balls: [2.0, 0.75]
|
||||
@@ -193,6 +193,8 @@ Goal: a quest book that points players onward, laid out like a research tree who
|
||||
- **Every implemented Pokémon is obtainable** (Adrian, 2026-11-09). Every species Cobblemon has a model for must be reachable somewhere in the world, even if rarely or through crafting: a wild spawn, evolving a wild line, a fossil, or a crafted or summoned encounter. Legendaries and the like count too; they can be very rare or expensive. Checked on 2026-11-09: 95 of 905 implemented species are not reachable by a wild spawn or by evolving a wild line (legendaries, fossils, and lines the zone assignment left out, such as Aron, Litwick and Piplup). Report what is missing with a tool or self-test so new Cobblemon versions can't reopen the gap.
|
||||
- **A shader pack included** (Adrian, 2026-11-09). Ship [Iris](https://modrinth.com/mod/iris) (client only) and [Complementary Reimagined](https://modrinth.com/shader/complementary-reimagined), installed but off by default; players switch it on with a key or in Video Settings. Iris 1.8.14-beta.1+1.21.1-neoforge (MR `KduFYu4t`) is built against Sodium 0.8.13, which the pack already pins; it is the only NeoForge 1.21.1 build for Sodium 0.8, and a beta. Complementary r5.9.3 (MR `Bqen1mJX`) has the most downloads of the common shader packs and keeps a vanilla-like look, which suits Cobblemon's models. Its license allows modpacks if it is left unmodified, credited where the pack is described when it's on by default (it isn't; it's credited anyway), and added through Modrinth or CurseForge's own systems, never as an uploaded file: the private pack has to download it from Modrinth. Check that Cobblemon's models, the machine and boss particles and the see-through blocks render properly with it on, and measure the frame-rate cost.
|
||||
- **Performance.** Profile with spark on a server with realistic pen and machine counts; cap entity density through pasture config if needed.
|
||||
- **Reproducible builds** (Adrian, 2026-11-09). The same commit gives the same bytes: pinned JDK, packwiz, Gradle plugins and Python packages; `tools/check_reproducible.py` proves it.
|
||||
- **Balancing on the server** (Adrian, 2026-11-09). Balance changes reach playtesters without a client update: `tools/server_update.py`, a server-held data pack, JEI pages sent by the server.
|
||||
- **Multiplayer test** with Adrian's group.
|
||||
- **Release.** Final `docs/licenses.md`, credits and non-affiliation disclaimer; publish client and server packs on Modrinth and CurseForge as dependency-based packs.
|
||||
|
||||
|
||||
+20
-20
@@ -2,45 +2,45 @@
|
||||
|
||||
Generated by `tools/zone_spawns.py` from Cobblemon's spawn data and `world/zones.json`; do not edit by hand. Each evolution line spawns in one zone of the island; the stages listed are the ones whose levels reach the zone's band. Pin a line to another zone in `world/zones.json` → `species`. The End's lines spawn there as well as in their island zone.
|
||||
|
||||
## Button Isle (Lv 2–10, 25 lines)
|
||||
## Button Isle (Lv 2–10, 24 lines)
|
||||
|
||||
Bidoof, Bonsly, Caterpie (Butterfree, Caterpie, Metapod), Combee, Gimmighoul, Hoothoot, Kricketot (Kricketot, Kricketune), Ledyba, Meditite, Meowth, Morpeko, Nincada, Pachirisu, Rattata, Sableye, Scatterbug (Scatterbug, Spewpa), Skitty, Smeargle, Spinarak, Sunkern, Tarountula, Weedle (Beedrill, Kakuna, Weedle), Wooper, Wurmple (Beautifly, Cascoon, Dustox, Silcoon, Wurmple), Wynaut
|
||||
Bidoof, Bonsly, Caterpie (Butterfree, Caterpie, Metapod), Combee, Gimmighoul, Hoothoot, Kricketot (Kricketot, Kricketune), Ledyba, Meditite, Morpeko, Nincada, Pachirisu, Rattata, Sableye, Scatterbug (Scatterbug, Spewpa), Skitty, Smeargle, Spinarak, Sunkern, Tarountula, Weedle (Beedrill, Kakuna, Weedle), Wooper, Wurmple (Beautifly, Cascoon, Dustox, Silcoon, Wurmple), Wynaut
|
||||
|
||||
## Frostpeak Range (Lv 40–55, 49 lines)
|
||||
## Frostpeak Range (Lv 40–55, 53 lines)
|
||||
|
||||
Axew (Fraxure, Haxorus), Basculin (Basculegion, Basculin), Bergmite (Avalugg), Budew (Roselia, Roserade), Cetoddle (Cetitan), Cherubi (Cherrim), Chespin (Chesnaught, Quilladin), Chikorita (Bayleef, Meganium), Chimchar (Infernape, Monferno), Cryogonal, Cubchoo (Beartic), Cufant (Copperajah), Delibird, Duraludon (Archaludon, Duraludon), Eiscue, Elekid (Electabuzz, Electivire), Gossifleur (Eldegoss), Larvitar (Pupitar, Tyranitar), Machop (Machamp, Machoke), Makuhita (Hariyama), Mankey (Annihilape, Primeape), Mienfoo (Mienshao), Minior, Rhyhorn (Rhydon, Rhyperior), Riolu (Lucario), Rockruff (Lycanroc), Rowlet (Dartrix, Decidueye), Rufflet (Braviary), Salandit, Sawk, Skiddo (Gogoat), Smoochum (Jynx), Sneasel (Sneasel, Sneasler), Snom (Frosmoth), Snorunt (Froslass, Glalie), Snover (Abomasnow), Spinda, Spoink (Grumpig), Sprigatito (Floragato, Meowscarada), Spritzee (Aromatisse), Stantler (Stantler, Wyrdeer), Starly (Staraptor), Swinub (Mamoswine, Piloswine), Throh, Togedemaru, Togepi (Togekiss, Togetic), Tyrogue (Hitmonchan, Hitmonlee, Hitmontop), Vanillite (Vanillish, Vanilluxe), Wooloo (Dubwool)
|
||||
Alolan Sandshrew (Sandshrew), Alolan Vulpix (Vulpix), Axew (Fraxure, Haxorus), Basculin (Basculegion, Basculin), Bergmite (Avalugg), Budew (Roselia, Roserade), Cetoddle (Cetitan), Cherubi (Cherrim), Chespin (Chesnaught, Quilladin), Chikorita (Bayleef, Meganium), Chimchar (Infernape, Monferno), Cryogonal, Cubchoo (Beartic), Cufant (Copperajah), Delibird, Duraludon (Archaludon, Duraludon), Eiscue, Elekid (Electabuzz, Electivire), Galarian Darumaka (Darumaka), Gossifleur (Eldegoss), Hisuian Zorua (Zorua), Larvitar (Pupitar, Tyranitar), Machop (Machamp, Machoke), Makuhita (Hariyama), Mankey (Annihilape, Primeape), Mienfoo (Mienshao), Minior, Rhyhorn (Rhydon, Rhyperior), Riolu (Lucario), Rockruff (Lycanroc), Rowlet (Dartrix, Decidueye), Rufflet (Braviary), Salandit, Sawk, Skiddo (Gogoat), Smoochum (Jynx), Sneasel (Sneasel, Sneasler), Snom (Frosmoth), Snorunt (Froslass, Glalie), Snover (Abomasnow), Spinda, Spoink (Grumpig), Sprigatito (Floragato, Meowscarada), Spritzee (Aromatisse), Stantler (Stantler, Wyrdeer), Starly (Staraptor), Swinub (Mamoswine, Piloswine), Throh, Togedemaru, Togepi (Togekiss, Togetic), Tyrogue (Hitmonchan, Hitmonlee, Hitmontop), Vanillite (Vanillish, Vanilluxe), Wooloo (Dubwool)
|
||||
|
||||
## Sunscorch (Lv 30–45, 36 lines)
|
||||
## Sunscorch (Lv 30–45, 38 lines)
|
||||
|
||||
Bramblin (Brambleghast), Cacnea (Cacnea, Cacturne), Capsakid (Capsakid, Scovillain), Cubone (Cubone, Marowak), Drampa, Dwebble (Crustle, Dwebble), Ekans (Arbok), Falinks, Fennekin (Braixen, Delphox, Fennekin), Flittle (Espathra), Fuecoco (Crocalor, Fuecoco, Skeledirge), Girafarig (Farigiraf, Girafarig), Helioptile (Heliolisk), Hippopotas (Hippopotas, Hippowdon), Kangaskhan, Klawf, Larvesta, Litleo (Litleo, Pyroar), Maractus, Nacli (Garganacl, Naclstack), Numel (Camerupt, Numel), Pawniard, Phanpy (Donphan, Phanpy), Poltchageist (Poltchageist, Sinistcha), Rellor (Rabsca), Sandile (Krokorok, Krookodile), Sandshrew (Sandshrew, Sandslash), Scraggy (Scrafty, Scraggy), Shinx (Luxio, Luxray), Silicobra (Sandaconda, Silicobra), Skarmory, Skorupi (Drapion, Skorupi), Spearow (Fearow), Totodile (Croconaw, Feraligatr, Totodile), Trapinch (Flygon, Vibrava), Tympole (Palpitoad, Seismitoad)
|
||||
Bramblin (Brambleghast), Cacnea (Cacnea, Cacturne), Capsakid (Capsakid, Scovillain), Cubone (Cubone, Marowak), Drampa, Dwebble (Crustle, Dwebble), Ekans (Arbok), Falinks, Fennekin (Braixen, Delphox, Fennekin), Flittle (Espathra), Fuecoco (Crocalor, Fuecoco, Skeledirge), Girafarig (Farigiraf, Girafarig), Helioptile (Heliolisk), Hippopotas (Hippopotas, Hippowdon), Hisuian Growlithe (Growlithe), Kangaskhan, Klawf, Larvesta, Litleo (Litleo, Pyroar), Maractus, Nacli (Garganacl, Naclstack), Numel (Camerupt, Numel), Paldean Tauros (Tauros), Pawniard, Phanpy (Donphan, Phanpy), Poltchageist (Poltchageist, Sinistcha), Rellor (Rabsca), Sandile (Krokorok, Krookodile), Sandshrew (Sandshrew, Sandslash), Scraggy (Scrafty, Scraggy), Shinx (Luxio, Luxray), Silicobra (Sandaconda, Silicobra), Skarmory, Skorupi (Drapion, Skorupi), Spearow (Fearow), Totodile (Croconaw, Feraligatr, Totodile), Trapinch (Flygon, Vibrava), Tympole (Palpitoad, Seismitoad)
|
||||
|
||||
## Mangrove Mire (Lv 25–38, 20 lines)
|
||||
## Mangrove Mire (Lv 25–38, 21 lines)
|
||||
|
||||
Arrokuda (Arrokuda, Barraskewda), Croagunk (Croagunk, Toxicroak), Dondozo, Ducklett (Ducklett, Swanna), Flamigo, Froakie (Froakie, Frogadier, Greninja), Hatenna (Hatenna, Hattrem), Illumise, Karrablast (Escavalier, Karrablast), Misdreavus, Mudkip (Marshtomp, Mudkip, Swampert), Shelmet (Accelgor, Shelmet), Slowpoke, Sobble (Drizzile, Inteleon, Sobble), Surskit (Masquerain, Surskit), Tadbulb (Bellibolt, Tadbulb), Tatsugiri, Toedscool (Toedscool, Toedscruel), Volbeat, Yanma (Yanma, Yanmega)
|
||||
Croagunk (Croagunk, Toxicroak), Dondozo, Ducklett (Ducklett, Swanna), Flamigo, Froakie (Froakie, Frogadier, Greninja), Galarian Stunfisk (Stunfisk), Hatenna (Hatenna, Hattrem), Illumise, Karrablast (Escavalier, Karrablast), Misdreavus, Mudkip (Marshtomp, Mudkip, Swampert), Paldean Tauros (Tauros), Shelmet (Accelgor, Shelmet), Slowpoke, Sobble (Drizzile, Inteleon, Sobble), Surskit (Masquerain, Surskit), Tadbulb (Bellibolt, Tadbulb), Tatsugiri, Toedscool (Toedscool, Toedscruel), Volbeat, Yanma (Yanma, Yanmega)
|
||||
|
||||
## Verdant Jungle (Lv 18–30, 29 lines)
|
||||
## Verdant Jungle (Lv 18–30, 30 lines)
|
||||
|
||||
Aipom, Bellsprout (Bellsprout, Weepinbell), Bounsweet (Bounsweet, Steenee, Tsareena), Bulbasaur (Bulbasaur, Ivysaur), Chatot, Darumaka, Exeggcute (Exeggcute, Exeggutor), Fomantis, Grookey (Grookey, Thwackey), Jangmo-o, Kecleon, Komala, Litten (Litten, Torracat), Natu (Natu, Xatu), Pancham, Panpour (Panpour, Simipour), Pansage (Pansage, Simisage), Pansear (Pansear, Simisear), Pikipek (Pikipek, Toucannon, Trumbeak), Shroodle (Grafaiai, Shroodle), Slakoth (Slakoth, Vigoroth), Snivy (Servine, Snivy), Stufful (Bewear, Stufful), Stunfisk, Tangela (Tangela, Tangrowth), Treecko (Grovyle, Treecko), Tropius, Unown, Woobat (Swoobat, Woobat)
|
||||
Aipom, Bellsprout (Bellsprout, Weepinbell), Bounsweet (Bounsweet, Steenee, Tsareena), Bulbasaur (Bulbasaur, Ivysaur), Chatot, Darumaka, Exeggcute (Exeggcute, Exeggutor), Fomantis, Galarian Slowpoke (Slowpoke), Grookey (Grookey, Thwackey), Jangmo-o, Kecleon, Komala, Litten (Litten, Torracat), Natu (Natu, Xatu), Pancham, Panpour (Panpour, Simipour), Pansage (Pansage, Simisage), Pansear (Pansear, Simisear), Pikipek (Pikipek, Toucannon, Trumbeak), Shroodle (Grafaiai, Shroodle), Slakoth (Slakoth, Vigoroth), Snivy (Servine, Snivy), Stufful (Bewear, Stufful), Stunfisk, Tangela (Tangela, Tangrowth), Treecko (Grovyle, Treecko), Tropius, Unown, Woobat (Swoobat, Woobat)
|
||||
|
||||
## Highland Meadows (Lv 25–35, 41 lines)
|
||||
## Highland Meadows (Lv 25–35, 43 lines)
|
||||
|
||||
Abra (Abra, Alakazam, Kadabra), Blitzle (Blitzle, Zebstrika), Bouffalant, Buizel (Buizel, Floatzel), Chewtle (Chewtle, Drednaw), Cottonee (Cottonee, Whimsicott), Cyclizar, Cyndaquil (Cyndaquil, Quilava), Doduo (Dodrio, Doduo), Electrike (Electrike, Manectric), Fletchling (Fletchinder, Fletchling, Talonflame), Golett, Hoppip (Hoppip, Jumpluff, Skiploom), Igglybuff (Jigglypuff, Wigglytuff), Lechonk (Lechonk, Oinkologne), Lickitung (Lickilicky, Lickitung), Mareep (Ampharos, Flaaffy, Mareep), Miltank, Mudbray (Mudbray, Mudsdale), Nymble (Lokix), Oddish (Bellossom, Gloom, Oddish, Vileplume), Pidgey (Pidgeotto, Pidgey), Poliwag (Politoed, Poliwag, Poliwhirl, Poliwrath), Ponyta, Poochyena (Mightyena), Psyduck (Golduck, Psyduck), Quaxly (Quaxly, Quaxwell), Scorbunny (Cinderace, Raboot, Scorbunny), Seviper, Sinistea (Polteageist, Sinistea), Spiritomb, Squirtle (Squirtle, Wartortle), Stonjourner, Tauros, Tepig (Pignite, Tepig), Tinkatink (Tinkatink, Tinkatuff), Torchic (Combusken, Torchic), Turtwig (Grotle, Torterra, Turtwig), Vulpix (Ninetales, Vulpix), Zangoose, Zorua (Zoroark, Zorua)
|
||||
Abra (Abra, Alakazam, Kadabra), Alolan Diglett (Diglett), Blitzle (Blitzle, Zebstrika), Bouffalant, Buizel (Buizel, Floatzel), Chewtle (Chewtle, Drednaw), Cottonee (Cottonee, Whimsicott), Cyclizar, Cyndaquil (Cyndaquil, Quilava), Doduo (Dodrio, Doduo), Electrike (Electrike, Manectric), Fletchling (Fletchinder, Fletchling, Talonflame), Golett, Hoppip (Hoppip, Jumpluff, Skiploom), Igglybuff (Jigglypuff, Wigglytuff), Lechonk (Lechonk, Oinkologne), Lickitung (Lickilicky, Lickitung), Mareep (Ampharos, Flaaffy, Mareep), Miltank, Mudbray (Mudbray, Mudsdale), Nymble (Lokix), Oddish (Bellossom, Gloom, Oddish, Vileplume), Paldean Tauros (Tauros), Pidgey (Pidgeotto, Pidgey), Poliwag (Politoed, Poliwag, Poliwhirl, Poliwrath), Ponyta, Poochyena (Mightyena), Psyduck (Golduck, Psyduck), Quaxly (Quaxly, Quaxwell), Scorbunny (Cinderace, Raboot, Scorbunny), Seviper, Sinistea (Polteageist, Sinistea), Spiritomb, Squirtle (Squirtle, Wartortle), Stonjourner, Tauros, Tepig (Pignite, Tepig), Tinkatink (Tinkatink, Tinkatuff), Torchic (Combusken, Torchic), Turtwig (Grotle, Torterra, Turtwig), Vulpix (Ninetales, Vulpix), Zangoose, Zorua (Zoroark, Zorua)
|
||||
|
||||
## Westmarch (Lv 15–25, 27 lines)
|
||||
## Westmarch (Lv 15–25, 30 lines)
|
||||
|
||||
Applin (Appletun, Applin, Dipplin, Flapple), Buneary, Corphish, Dedenne, Deerling, Dewpider (Araquanid, Dewpider), Dratini, Emolga, Flabébé (Flabébé, Floette), Gible (Gabite, Gible), Goomy, Magikarp (Gyarados, Magikarp), Munchlax, Nickit (Nickit, Thievul), Petilil (Lilligant, Petilil), Phantump, Pichu (Pichu, Pikachu), Pineco, Rotom, Scyther, Seedot (Nuzleaf, Seedot), Sewaddle (Sewaddle, Swadloon), Shroomish (Breloom, Shroomish), Skwovet (Greedent, Skwovet), Stunky, Tandemaus (Maushold, Tandemaus), Teddiursa
|
||||
Alolan Geodude (Geodude), Applin (Appletun, Applin, Dipplin, Flapple), Buneary, Corphish, Dedenne, Deerling, Dewpider (Araquanid, Dewpider), Dratini, Emolga, Flabébé (Flabébé, Floette), Gible (Gabite, Gible), Goomy, Happiny (Chansey, Happiny), Magikarp (Gyarados, Magikarp), Munchlax, Nickit (Nickit, Thievul), Noibat, Petilil (Lilligant, Petilil), Phantump, Pichu (Pichu, Pikachu), Pineco, Rotom, Scyther, Seedot (Nuzleaf, Seedot), Sewaddle (Sewaddle, Swadloon), Shroomish (Breloom, Shroomish), Skwovet (Greedent, Skwovet), Stunky, Tandemaus (Maushold, Tandemaus), Teddiursa
|
||||
|
||||
## Southwold (Lv 8–18, 53 lines)
|
||||
## Southwold (Lv 8–18, 54 lines)
|
||||
|
||||
Audino, Azurill (Azumarill, Azurill, Marill), Bunnelby, Burmy, Chingling, Corsola, Drifloon, Dunsparce, Eevee, Farfetch'd, Furfrou, Gastly, Glameow, Goldeen, Grimer, Grubbin, Happiny (Chansey, Happiny), Hawlucha, Heracross, Koffing, Lillipup (Herdier, Lillipup), Lotad (Lombre, Lotad), Magnemite, Minccino, Minun, Nidoran♀ (Nidoran♀, Nidorina), Nidoran♂ (Nidoran♂, Nidorino), Noibat, Patrat, Pidove, Pinsir, Plusle, Porygon, Pumpkaboo, Relicanth, Sentret (Furret, Sentret), Shuppet, Smoliv, Squawkabilly, Swablu, Taillow, Tentacool, Timburr, Toxel, Tynamo, Venonat, Voltorb, Whismur, Wimpod, Yamask, Yamper, Yungoos, Zigzagoon
|
||||
Audino, Azurill (Azumarill, Azurill, Marill), Bunnelby, Burmy, Chingling, Corsola, Drifloon, Dunsparce, Eevee, Farfetch'd, Furfrou, Galarian Meowth (Meowth), Galarian Zigzagoon (Zigzagoon), Gastly, Glameow, Goldeen, Grimer, Grubbin, Hawlucha, Heracross, Koffing, Lillipup (Herdier, Lillipup), Lotad (Lombre, Lotad), Magnemite, Meowth, Minccino, Minun, Nidoran♀ (Nidoran♀, Nidorina), Nidoran♂ (Nidoran♂, Nidorino), Patrat, Pidove, Pinsir, Plusle, Porygon, Pumpkaboo, Relicanth, Sentret (Furret, Sentret), Shuppet, Smoliv, Squawkabilly, Swablu, Taillow, Tentacool, Timburr, Toxel, Tynamo, Venonat, Voltorb, Whismur, Wimpod, Yamask, Yamper, Yungoos, Zigzagoon
|
||||
|
||||
## Shallows (Lv 5–20, 21 lines)
|
||||
## Deep Sea (Lv 30–50, 24 lines)
|
||||
|
||||
Alomomola, Bruxish, Carvanha, Chinchou, Clobbopus, Crabrawler, Cramorant, Finizen, Finneon, Inkay, Krabby, Luvdisc, Mareanie, Pincurchin, Pyukumuku, Qwilfish, Sandygast, Shellos, Wiglett, Wingull, Wishiwashi
|
||||
Alomomola, Arrokuda (Barraskewda), Binacle (Barbaracle, Binacle), Clamperl (Clamperl, Gorebyss, Huntail), Clauncher (Clauncher, Clawitzer), Dhelmise, Dreepy (Drakloak), Frillish (Frillish, Jellicent), Horsea (Horsea, Kingdra, Seadra), Inkay (Malamar), Lapras, Mantyke (Mantine, Mantyke), Oshawott (Dewott, Oshawott, Samurott), Piplup (Empoleon, Piplup, Prinplup), Popplio (Brionne, Popplio, Primarina), Remoraid (Octillery, Remoraid), Seel (Dewgong, Seel), Shellder (Cloyster, Shellder), Skrelp (Dragalge, Skrelp), Spheal (Sealeo, Walrein), Staryu (Starmie, Staryu), Veluza, Wailmer (Wailmer, Wailord), Wattrel (Kilowattrel)
|
||||
|
||||
## Deep Sea (Lv 30–50, 21 lines)
|
||||
## Shallows (Lv 5–20, 20 lines)
|
||||
|
||||
Binacle (Barbaracle, Binacle), Clamperl (Clamperl, Gorebyss, Huntail), Clauncher (Clauncher, Clawitzer), Dhelmise, Dreepy (Drakloak), Frillish (Frillish, Jellicent), Horsea (Horsea, Kingdra, Seadra), Lapras, Mantyke (Mantine, Mantyke), Oshawott (Dewott, Oshawott, Samurott), Piplup (Empoleon, Piplup, Prinplup), Popplio (Brionne, Popplio, Primarina), Remoraid (Octillery, Remoraid), Seel (Dewgong, Seel), Shellder (Cloyster, Shellder), Skrelp (Dragalge, Skrelp), Spheal (Sealeo, Walrein), Staryu (Starmie, Staryu), Veluza, Wailmer (Wailmer, Wailord), Wattrel (Kilowattrel)
|
||||
Bruxish, Carvanha, Chinchou, Clobbopus, Crabrawler, Cramorant, Finizen, Finneon, Hisuian Qwilfish (Qwilfish), Krabby, Luvdisc, Mareanie, Pincurchin, Pyukumuku, Qwilfish, Sandygast, Shellos, Wiglett, Wingull, Wishiwashi
|
||||
|
||||
## Lush Caves (Lv 12–28, 22 lines)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("dev.architectury.loom") version ("1.11-SNAPSHOT")
|
||||
id("architectury-plugin") version ("3.4-SNAPSHOT")
|
||||
id("dev.architectury.loom") version ("1.11.458")
|
||||
id("architectury-plugin") version ("3.4.164")
|
||||
// Must read the stdlib that Kotlin for Forge ships (5.12.0 -> Kotlin 2.4.0).
|
||||
kotlin("jvm") version ("2.4.0")
|
||||
}
|
||||
@@ -62,6 +62,12 @@ kotlin {
|
||||
}
|
||||
|
||||
tasks {
|
||||
// Same sources, same bytes (tools/check_reproducible.py).
|
||||
withType<AbstractArchiveTask>().configureEach {
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.cobblesurvival.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon;
|
||||
import dev.cobblesurvival.permadeath.Permadeath;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* A player's starter only faints, it is never removed (Adrian, 2026-11-09). CobbleHardcoreMon has no exception of its
|
||||
* own: this leaves its Revival Charm alone and skips its removal. Named by string, so the companion mod needs it only at run time.
|
||||
*/
|
||||
@Pseudo
|
||||
@Mixin(targets = "dev.matthiesen.cobblehardcoremon.common.handlers.PlayerPokeParty", remap = false)
|
||||
abstract class HardcorePartyMixin {
|
||||
@Inject(method = "popPokemonTotem", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblesurvival$keepCharm(Pokemon pokemon, CallbackInfoReturnable<Boolean> result) {
|
||||
if (Permadeath.INSTANCE.isStarter(pokemon)) result.setReturnValue(false);
|
||||
}
|
||||
|
||||
@Inject(method = "alertPlayerAndRemovedPokemon(Lcom/cobblemon/mod/common/pokemon/Pokemon;)V", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblesurvival$keepStarter(Pokemon pokemon, CallbackInfo info) {
|
||||
if (Permadeath.INSTANCE.isStarter(pokemon)) info.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "alertPlayerAndRemovedPokemon(Lcom/cobblemon/mod/common/pokemon/Pokemon;ZLjava/lang/String;)V", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblesurvival$keepStarter(Pokemon pokemon, boolean soulLink, String partner, CallbackInfo info) {
|
||||
if (Permadeath.INSTANCE.isStarter(pokemon)) info.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
package dev.cobblesurvival
|
||||
|
||||
import dev.cobblesurvival.catching.AttackBattles
|
||||
import dev.cobblesurvival.jei.DropSources
|
||||
import net.neoforged.fml.loading.FMLPaths
|
||||
import net.minecraft.server.packs.PackSelectionConfig
|
||||
import net.minecraft.server.packs.PackLocationInfo
|
||||
import net.minecraft.server.packs.FilePackResources
|
||||
import java.util.Optional
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Files
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.mojang.logging.LogUtils
|
||||
@@ -17,6 +26,7 @@ import dev.cobblesurvival.machine.MachineShowcase
|
||||
import dev.cobblesurvival.machine.Machines
|
||||
import dev.cobblesurvival.party.ShoulderPassives
|
||||
import dev.cobblesurvival.progress.Gates
|
||||
import dev.cobblesurvival.selftest.PerfLoad
|
||||
import dev.cobblesurvival.selftest.SelfTest
|
||||
import dev.cobblesurvival.world.OnlyPokemon
|
||||
import dev.cobblesurvival.world.SharedLoot
|
||||
@@ -54,6 +64,7 @@ import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
|
||||
class CobbleSurvival {
|
||||
companion object {
|
||||
const val MOD_ID = "cobblesurvival"
|
||||
private val serverData: Path by lazy { FMLPaths.CONFIGDIR.get().resolve("cobblesurvival/datapack.zip") }
|
||||
val LOGGER: Logger = LogUtils.getLogger()
|
||||
|
||||
val version: String
|
||||
@@ -61,6 +72,11 @@ class CobbleSurvival {
|
||||
.map { it.modInfo.version.toString() }
|
||||
.orElse("unknown")
|
||||
|
||||
/** Which balance is live: `/cobblesurvival data`, read over RCON by tools/check_server_update.py. */
|
||||
fun dataLine() = "data ${if (Files.isRegularFile(serverData)) "server" else "jar"}; " +
|
||||
"pages ${Integer.toHexString(DropSources.packed.contentHashCode())}; " +
|
||||
"boss_evs ${Tuning.values.boss_evs}; pokemon_pages ${DropSources.data.pokemon.size}"
|
||||
|
||||
/** One line the server smoke test reads over RCON to confirm the mod is live. */
|
||||
fun statusLine() =
|
||||
"$MOD_ID $version ok; cobblemon ${Cobblemon.VERSION}; species ${PokemonSpecies.count()}"
|
||||
@@ -69,6 +85,7 @@ class CobbleSurvival {
|
||||
init {
|
||||
LOGGER.info("Cobblemon Survival companion {} loaded (Cobblemon {})", version, Cobblemon.VERSION)
|
||||
MOD_BUS.addListener(::onAddPackFinders)
|
||||
DropSources.register(MOD_BUS)
|
||||
InZoneCondition.register(MOD_BUS)
|
||||
Machines.register(MOD_BUS)
|
||||
Summons.register(MOD_BUS)
|
||||
@@ -78,6 +95,7 @@ class CobbleSurvival {
|
||||
NeoForge.EVENT_BUS.addListener(OnlyPokemon::onChunkLoad)
|
||||
NeoForge.EVENT_BUS.addListener(OnlyPokemon::onEntityJoin)
|
||||
NeoForge.EVENT_BUS.addListener(OnlyPokemon::onServerTick)
|
||||
Tuning.register()
|
||||
Permadeath.register()
|
||||
ZoneCondition.register()
|
||||
NeoForge.EVENT_BUS.addListener(Zones::onAddReloadListeners)
|
||||
@@ -92,12 +110,14 @@ class CobbleSurvival {
|
||||
OreNodes.register()
|
||||
BaseLoot.register()
|
||||
Catching.register()
|
||||
AttackBattles.register()
|
||||
Wanderers.register()
|
||||
SharedLoot.register()
|
||||
SpecialPokemon.register()
|
||||
ZoneHazards.register()
|
||||
LevelPressure.register()
|
||||
SelfTest.register()
|
||||
PerfLoad.register()
|
||||
}
|
||||
|
||||
/** The island world type has no Nether, so a lit obsidian frame must not turn into a dead portal. */
|
||||
@@ -108,16 +128,27 @@ class CobbleSurvival {
|
||||
}
|
||||
}
|
||||
|
||||
/** Ships datapacks/core from the repo as a built-in data pack that is always on, in every world. */
|
||||
/**
|
||||
* Ships datapacks/core from the repo as a built-in data pack that is always on, in every world. A server with
|
||||
* `config/cobblesurvival/datapack.zip` (tools/server_update.py; Adrian, 2026-11-09) loads that instead, under the
|
||||
* same pack id: balance changes need no new jar for the server or anybody's client, and `/reload` applies them.
|
||||
*/
|
||||
private fun onAddPackFinders(event: AddPackFindersEvent) {
|
||||
event.addPackFinders(
|
||||
ResourceLocation.fromNamespaceAndPath(MOD_ID, "datapacks/core"),
|
||||
val title = Component.literal("Cobblemon Survival core data")
|
||||
val core = ResourceLocation.fromNamespaceAndPath(MOD_ID, "datapacks/core")
|
||||
if (event.packType != PackType.SERVER_DATA || !Files.isRegularFile(serverData)) {
|
||||
event.addPackFinders(core, PackType.SERVER_DATA, title, PackSource.BUILT_IN, true, Pack.Position.TOP)
|
||||
return
|
||||
}
|
||||
LOGGER.info("Loading the core data pack from {} instead of the mod jar", serverData)
|
||||
event.addRepositorySource { packs ->
|
||||
Pack.readMetaAndCreate(
|
||||
PackLocationInfo("mod/$core", title, PackSource.BUILT_IN, Optional.empty()),
|
||||
FilePackResources.FileResourcesSupplier(serverData),
|
||||
PackType.SERVER_DATA,
|
||||
Component.literal("Cobblemon Survival core data"),
|
||||
PackSource.BUILT_IN,
|
||||
true,
|
||||
Pack.Position.TOP,
|
||||
)
|
||||
PackSelectionConfig(true, Pack.Position.TOP, false),
|
||||
)?.let { packs.accept(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRegisterCommands(event: RegisterCommandsEvent) {
|
||||
@@ -127,6 +158,11 @@ class CobbleSurvival {
|
||||
context.source.sendSuccess({ Component.literal(statusLine()) }, false)
|
||||
1
|
||||
})
|
||||
.then(Commands.literal("data").executes { context ->
|
||||
context.source.sendSuccess({ Component.literal(dataLine()) }, false)
|
||||
1
|
||||
})
|
||||
.then(PerfLoad.command())
|
||||
.then(BiomePreview.command())
|
||||
.then(OreVeins.command())
|
||||
.then(MachineShowcase.command())
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.cobblesurvival
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener
|
||||
import net.minecraft.util.profiling.ProfilerFiller
|
||||
import net.neoforged.neoforge.common.NeoForge
|
||||
import net.neoforged.neoforge.event.AddReloadListenerEvent
|
||||
|
||||
/**
|
||||
* The balance numbers the server's code reads that have no data file of their own: `data/cobblesurvival/tuning/server.json`.
|
||||
* In the data pack so a server can change them without a new jar for anybody (tools/server_update.py); `/reload` applies them.
|
||||
* A field the file leaves out keeps its default here.
|
||||
*/
|
||||
object Tuning : SimpleJsonResourceReloadListener(Gson(), "tuning") {
|
||||
class Values(
|
||||
val boss_evs: Int = 170,
|
||||
val guardian_wake_range: Double = 96.0,
|
||||
val guardian_leash: Double = 24.0,
|
||||
val end_boss_wake_range: Double = 160.0,
|
||||
val end_boss_leash: Double = 24.0,
|
||||
val summon_spacing: Double = 96.0,
|
||||
val wanderer_range: Double = 64.0,
|
||||
val attack_battle_grace_ticks: Int = 100,
|
||||
)
|
||||
|
||||
private val FILE = ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "server")
|
||||
|
||||
@Volatile
|
||||
var values = Values()
|
||||
private set
|
||||
|
||||
fun register() {
|
||||
NeoForge.EVENT_BUS.addListener<AddReloadListenerEvent> { it.addListener(this) }
|
||||
}
|
||||
|
||||
override fun apply(files: Map<ResourceLocation, JsonElement>, manager: ResourceManager, profiler: ProfilerFiller) {
|
||||
values = files[FILE]?.let { Gson().fromJson(it, Values::class.java) } ?: Values()
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,12 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import dev.cobblesurvival.Tuning
|
||||
import dev.cobblesurvival.zone.Guardians
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.tags.DamageTypeTags
|
||||
import net.neoforged.neoforge.common.NeoForge
|
||||
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
||||
|
||||
/**
|
||||
* Bosses are big and tough, not strong (Adrian, 2026-11-02: Eternatus was too small and too easy; guardians should be
|
||||
@@ -25,10 +29,19 @@ import net.minecraft.network.chat.Component
|
||||
*/
|
||||
object BossToughness : HeldItemManager {
|
||||
const val WARD = "guardiansward"
|
||||
const val EVS = 170
|
||||
val EVS get() = Tuning.values.boss_evs
|
||||
private val DEFENSIVE = listOf(Stats.HP, Stats.DEFENCE, Stats.SPECIAL_DEFENCE)
|
||||
|
||||
fun register() = HeldItemProvider.register(this, Priority.HIGHEST)
|
||||
fun register() {
|
||||
HeldItemProvider.register(this, Priority.HIGHEST)
|
||||
NeoForge.EVENT_BUS.addListener(::onDamage)
|
||||
}
|
||||
|
||||
/** Only a battle may hurt a boss (Adrian, 2026-11-09): no players, Pokémon outside battle, fire, falls or drowning. `/kill` still works. */
|
||||
private fun onDamage(event: LivingIncomingDamageEvent) {
|
||||
val entity = event.entity as? PokemonEntity ?: return
|
||||
if (isBoss(entity.pokemon) && !event.source.`is`(DamageTypeTags.BYPASSES_INVULNERABILITY)) event.isCanceled = true
|
||||
}
|
||||
|
||||
fun isBoss(pokemon: Pokemon): Boolean {
|
||||
val data = pokemon.persistentData
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.boss
|
||||
|
||||
import dev.cobblesurvival.Tuning
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
|
||||
@@ -38,8 +39,8 @@ object EndBoss {
|
||||
/** Its entry in [GuardianState]. */
|
||||
private const val STATE = "the_end"
|
||||
private const val CHECK_TICKS = 40
|
||||
private const val NEAR = 160.0
|
||||
private const val LEASH = 24.0
|
||||
private val NEAR get() = Tuning.values.end_boss_wake_range
|
||||
private val LEASH get() = Tuning.values.end_boss_leash
|
||||
private const val TICKS_PER_HOUR = 72000.0
|
||||
/** Where it stands: south of the exit portal, on the island's top. */
|
||||
private val SPOT_XZ = BlockPos(0, 0, 24)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package dev.cobblesurvival.catching
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.ErroredBattleStart
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import dev.cobblesurvival.Tuning
|
||||
import dev.cobblesurvival.boss.BossToughness
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.neoforged.neoforge.common.NeoForge
|
||||
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* A wild Pokémon that attacks a player starts a battle instead of hurting them (Adrian, 2026-11-09). A player with no
|
||||
* Pokémon able to fight still takes the hit, so the wilds stay dangerous to them. After fleeing, a moment's grace
|
||||
* (`tuning/server.json` → `attack_battle_grace_ticks`) to get away, or the same Pokémon would pull them straight back.
|
||||
* A boss in a battle hits through: those are its area attacks.
|
||||
*/
|
||||
object AttackBattles {
|
||||
/** Game time until which a hit on this player does nothing: a battle is being set up, or they have just fled. */
|
||||
private val graceUntil = HashMap<UUID, Long>()
|
||||
|
||||
fun register() {
|
||||
NeoForge.EVENT_BUS.addListener(::onDamage)
|
||||
CobblemonEvents.BATTLE_FLED.subscribe { event ->
|
||||
event.battle.players.forEach { graceUntil[it.uuid] = it.serverLevel().gameTime + Tuning.values.attack_battle_grace_ticks }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDamage(event: LivingIncomingDamageEvent) {
|
||||
val player = event.entity as? ServerPlayer ?: return
|
||||
val attacker = event.source.entity as? PokemonEntity ?: return
|
||||
if (attacker.pokemon.isPlayerOwned() || (attacker.isBattling && BossToughness.isBoss(attacker.pokemon))) return
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
event.isCanceled = true
|
||||
return
|
||||
}
|
||||
val now = player.serverLevel().gameTime
|
||||
if (now < (graceUntil[player.uuid] ?: 0L)) {
|
||||
event.isCanceled = true
|
||||
return
|
||||
}
|
||||
if (BattleBuilder.pve(player, attacker) !is ErroredBattleStart) {
|
||||
graceUntil[player.uuid] = now + Tuning.values.attack_battle_grace_ticks
|
||||
event.isCanceled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,40 @@
|
||||
package dev.cobblesurvival.jei
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonElement
|
||||
import dev.cobblesurvival.CobbleSurvival
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener
|
||||
import net.minecraft.util.profiling.ProfilerFiller
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import net.neoforged.neoforge.common.NeoForge
|
||||
import net.neoforged.neoforge.event.AddReloadListenerEvent
|
||||
import net.neoforged.neoforge.event.OnDatapackSyncEvent
|
||||
import net.neoforged.neoforge.network.PacketDistributor
|
||||
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* Where items come from on the island (roadmap Phase 8), for the pack's JEI pages ([DropSourcesJei]):
|
||||
* `assets/cobblesurvival/jei/drop_sources.json`, written by tools/jei_sources.py from the pack's data. It ships inside
|
||||
* the mod because JEI runs on the client, which on a dedicated server has none of the server's data; the self-test
|
||||
* `drop_sources_match_the_server` checks it against the live server instead. Fields as tools/jei_sources.py writes them.
|
||||
* `data/cobblesurvival/drop_sources/pages.json`, written by tools/jei_sources.py from the pack's data. JEI runs on the
|
||||
* client, which on a dedicated server has none of the server's data, so the server sends this file to each player who
|
||||
* joins and again after `/reload`: the pages show the server's balance whatever version a player installed (Adrian,
|
||||
* 2026-11-09). The self-test `drop_sources_match_the_server` checks it against the live server. Fields as
|
||||
* tools/jei_sources.py writes them.
|
||||
*/
|
||||
object DropSources {
|
||||
object DropSources : SimpleJsonResourceReloadListener(Gson(), "drop_sources") {
|
||||
class Drop(val item: String, val count: String?, val chance: Double?)
|
||||
class Reward(val item: String, val count: Int)
|
||||
class Spawned(val zone: String, val levels: String)
|
||||
@@ -45,11 +67,65 @@ object DropSources {
|
||||
val pasture_finds: List<Finds>,
|
||||
)
|
||||
|
||||
const val PATH = "/assets/cobblesurvival/jei/drop_sources.json"
|
||||
/** The file's text, gzipped, as sent to clients. */
|
||||
class Payload(val packed: ByteArray) : CustomPacketPayload {
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
val data: Data by lazy {
|
||||
val stream = DropSources::class.java.getResourceAsStream(PATH) ?: error("$PATH is missing from the mod")
|
||||
stream.reader(Charsets.UTF_8).use { Gson().fromJson(it, Data::class.java) }
|
||||
val TYPE = CustomPacketPayload.Type<Payload>(ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "drop_sources"))
|
||||
private val CODEC: StreamCodec<ByteBuf, Payload> = ByteBufCodecs.BYTE_ARRAY.map(::Payload, Payload::packed)
|
||||
private val FILE = ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, "pages")
|
||||
private val NOTHING = Data(emptyMap(), emptyList(), emptyList(), emptyList(), emptyList(),
|
||||
EndBoss("", 0, 0, 0, 0.0, emptyList()), Altar(emptyList(), emptyList()), emptyList(), emptyList(), emptyList())
|
||||
|
||||
/** What the pages show: on a server, its data pack; on a client, what the server last sent. */
|
||||
@Volatile
|
||||
var data: Data = NOTHING
|
||||
private set
|
||||
|
||||
/** The current data as sent to clients. */
|
||||
var packed = ByteArray(0)
|
||||
private set
|
||||
|
||||
/** Set by the JEI plugin while it runs; called on the client thread after the server sent data. */
|
||||
var onReceived: (() -> Unit)? = null
|
||||
|
||||
fun register(modBus: IEventBus) {
|
||||
modBus.addListener<RegisterPayloadHandlersEvent> {
|
||||
// Optional: a client from before this change can still join, it only misses the pages.
|
||||
it.registrar("1").optional().playToClient(TYPE, CODEC, ::receive)
|
||||
}
|
||||
NeoForge.EVENT_BUS.addListener<AddReloadListenerEvent> { it.addListener(this) }
|
||||
NeoForge.EVENT_BUS.addListener(::sync)
|
||||
}
|
||||
|
||||
override fun apply(files: Map<ResourceLocation, JsonElement>, manager: ResourceManager, profiler: ProfilerFiller) {
|
||||
val file = files[FILE] ?: return
|
||||
packed = pack(file.toString())
|
||||
data = parse(packed)
|
||||
}
|
||||
|
||||
/** On joining and after `/reload`. */
|
||||
private fun sync(event: OnDatapackSyncEvent) {
|
||||
val payload = Payload(packed)
|
||||
event.relevantPlayers.filter { it.connection.hasChannel(TYPE) }.forEach { PacketDistributor.sendToPlayer(it, payload) }
|
||||
}
|
||||
|
||||
private fun receive(payload: Payload, context: IPayloadContext) {
|
||||
// On a singleplayer client the integrated server has already put the same bytes here.
|
||||
if (!payload.packed.contentEquals(packed)) {
|
||||
packed = payload.packed
|
||||
data = parse(packed)
|
||||
}
|
||||
onReceived?.invoke()
|
||||
}
|
||||
|
||||
fun pack(text: String): ByteArray = ByteArrayOutputStream().also { out ->
|
||||
GZIPOutputStream(out).use { it.write(text.toByteArray(Charsets.UTF_8)) }
|
||||
}.toByteArray()
|
||||
|
||||
fun parse(packed: ByteArray): Data = GZIPInputStream(ByteArrayInputStream(packed)).reader(Charsets.UTF_8).use {
|
||||
Gson().fromJson(it, Data::class.java)
|
||||
}
|
||||
|
||||
fun zoneName(zone: String?): String = zone?.let { data.zones[it]?.name ?: it } ?: "anywhere"
|
||||
|
||||
@@ -7,9 +7,11 @@ import dev.cobblesurvival.CobbleSurvival
|
||||
import dev.cobblesurvival.summon.Summons
|
||||
import mezz.jei.api.IModPlugin
|
||||
import mezz.jei.api.JeiPlugin
|
||||
import mezz.jei.api.constants.VanillaTypes
|
||||
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.IIngredientType
|
||||
import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter
|
||||
import mezz.jei.api.ingredients.subtypes.UidContext
|
||||
import mezz.jei.api.recipe.IFocusGroup
|
||||
@@ -21,6 +23,7 @@ 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 mezz.jei.api.runtime.IJeiRuntime
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.Font
|
||||
@@ -46,16 +49,20 @@ import net.minecraft.world.item.component.ItemLore
|
||||
@JeiPlugin
|
||||
class DropSourcesJei : IModPlugin {
|
||||
/** One page: an icon and title with a short [tag], rows of text, and the items it gives. */
|
||||
class Page(val icon: ItemStack, val title: Component, val tag: String, val rows: List<Row>, val outputs: List<Output>)
|
||||
class Page(val icon: ItemStack, val title: Component, val tag: String, val rows: List<Row>, val outputs: List<Output>, val species: String? = null)
|
||||
/** A line of text, wrapped if long, with optional [right]-aligned text on its first line. */
|
||||
class Row(val left: Component, val right: Component? = null)
|
||||
class Output(val stack: ItemStack, val notes: List<Component>)
|
||||
|
||||
private class Kind(val path: String, val title: String, val icon: String, val catalysts: List<String>, val pages: () -> List<Page>) {
|
||||
val type: RecipeType<Page> = RecipeType.create(CobbleSurvival.MOD_ID, path, Page::class.java)
|
||||
val built by lazy(pages)
|
||||
var built: List<Page> = emptyList()
|
||||
}
|
||||
|
||||
private var runtime: IJeiRuntime? = null
|
||||
/** The server data [Kind.built] was made from. */
|
||||
private var builtFrom: DropSources.Data? = null
|
||||
|
||||
private val kinds = listOf(
|
||||
Kind("pokemon_drops", "Island Drops", "cobblemon:poke_ball", listOf("cobblesurvival:synthesis_core"), ::pokemonPages),
|
||||
Kind("boss_rewards", "Boss Rewards", "minecraft:netherite_sword", emptyList(), ::bossPages),
|
||||
@@ -82,8 +89,10 @@ class DropSourcesJei : IModPlugin {
|
||||
|
||||
override fun registerCategories(registration: IRecipeCategoryRegistration) {
|
||||
val gui = registration.jeiHelpers.guiHelper
|
||||
builtFrom = DropSources.data
|
||||
kinds.forEach { it.built = it.pages() }
|
||||
registration.addRecipeCategories(*kinds.map { kind ->
|
||||
PageCategory(kind.type, Component.literal(kind.title), gui.createDrawableItemStack(DropSources.stack(kind.icon)), kind.built)
|
||||
PageCategory(kind.type, Component.literal(kind.title), gui.createDrawableItemStack(DropSources.stack(kind.icon))) { kind.built }
|
||||
}.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -95,15 +104,65 @@ class DropSourcesJei : IModPlugin {
|
||||
kinds.forEach { kind -> kind.catalysts.forEach { registration.addRecipeCatalyst(DropSources.stack(it), kind.type) } }
|
||||
}
|
||||
|
||||
class PageCategory(private val type: RecipeType<Page>, private val title: Component, private val icon: IDrawable, pages: List<Page>) : IRecipeCategory<Page> {
|
||||
override fun onRuntimeAvailable(jeiRuntime: IJeiRuntime) {
|
||||
runtime = jeiRuntime
|
||||
DropSources.onReceived = ::refresh
|
||||
refresh()
|
||||
}
|
||||
|
||||
override fun onRuntimeUnavailable() {
|
||||
runtime = null
|
||||
DropSources.onReceived = null
|
||||
}
|
||||
|
||||
/** The server's data can arrive after JEI has started, and changes with `/reload`: swaps the pages in the running JEI. */
|
||||
private fun refresh() {
|
||||
val jei = runtime ?: return
|
||||
if (builtFrom === DropSources.data) return
|
||||
builtFrom = DropSources.data
|
||||
val listed = jei.ingredientManager.getAllIngredients(VanillaTypes.ITEM_STACK).mapNotNull { Summons.speciesOf(it) }.toSet()
|
||||
val newSigils = sigils().filter { Summons.speciesOf(it) !in listed }
|
||||
if (newSigils.isNotEmpty()) jei.ingredientManager.addIngredientsAtRuntime(VanillaTypes.ITEM_STACK, newSigils)
|
||||
for (kind in kinds) {
|
||||
jei.recipeManager.hideRecipes(kind.type, kind.built)
|
||||
kind.built = kind.pages()
|
||||
jei.recipeManager.addRecipes(kind.type, kind.built)
|
||||
}
|
||||
}
|
||||
|
||||
/** Cobblemon Info's JEI ingredient for a Pokémon, by reflection: the add-on is a separate download, not a dependency. */
|
||||
private object CobbledexLink {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val type: IIngredientType<Any>? by lazy {
|
||||
runCatching { Class.forName("com.cobbledex.jei.PokemonIngredientType").getField("INSTANCE").get(null) as IIngredientType<Any> }.getOrNull()
|
||||
}
|
||||
private val constructor by lazy {
|
||||
runCatching { Class.forName("com.cobbledex.jei.PokemonIngredient").getConstructor(String::class.java, Set::class.java) }.getOrNull()
|
||||
}
|
||||
|
||||
fun of(species: String): Any? = constructor?.takeIf { type != null }?.newInstance(species.substringAfter(':'), emptySet<String>())
|
||||
}
|
||||
|
||||
class PageCategory(private val type: RecipeType<Page>, private val title: Component, private val icon: IDrawable, private val pages: () -> List<Page>) : IRecipeCategory<Page> {
|
||||
private var measured: List<Page>? = null
|
||||
private var tallest = 0
|
||||
|
||||
/** JEI gives every page of a category the same size: that of its tallest. */
|
||||
private val height = pages.maxOf { slotsTop(it) + (it.outputs.size + PER_ROW - 1) / PER_ROW * SLOT } + PADDING - 2
|
||||
private val pageHeight: Int
|
||||
get() {
|
||||
val now = pages()
|
||||
if (now !== measured) {
|
||||
measured = now
|
||||
tallest = (now.maxOfOrNull { slotsTop(it) + (it.outputs.size + PER_ROW - 1) / PER_ROW * SLOT } ?: BODY_TOP) + PADDING - 2
|
||||
}
|
||||
return tallest
|
||||
}
|
||||
|
||||
override fun getRecipeType() = type
|
||||
override fun getTitle() = title
|
||||
override fun getIcon() = icon
|
||||
override fun getWidth() = WIDTH
|
||||
override fun getHeight() = height
|
||||
override fun getHeight() = pageHeight
|
||||
|
||||
private val font: Font get() = Minecraft.getInstance().font
|
||||
|
||||
@@ -117,7 +176,10 @@ class DropSourcesJei : IModPlugin {
|
||||
}
|
||||
|
||||
override fun setRecipe(builder: IRecipeLayoutBuilder, page: Page, focuses: IFocusGroup) {
|
||||
builder.addSlot(RecipeIngredientRole.RENDER_ONLY, PADDING, ICON_TOP).addItemStack(page.icon)
|
||||
val linked = page.species?.let(CobbledexLink::of)
|
||||
// As Cobblemon Info's own ingredient the Pokémon opens that mod's pages when clicked (Adrian, 2026-11-09).
|
||||
if (linked != null) builder.addSlot(RecipeIngredientRole.INPUT, PADDING, ICON_TOP).addIngredient(CobbledexLink.type!!, linked)
|
||||
else builder.addSlot(RecipeIngredientRole.RENDER_ONLY, PADDING, ICON_TOP).addItemStack(page.icon)
|
||||
val top = slotsTop(page)
|
||||
page.outputs.forEachIndexed { i, out ->
|
||||
builder.addSlot(RecipeIngredientRole.OUTPUT, PADDING + 1 + (i % PER_ROW) * SLOT, top + 1 + (i / PER_ROW) * SLOT)
|
||||
@@ -128,11 +190,11 @@ class DropSourcesJei : IModPlugin {
|
||||
|
||||
override fun draw(page: Page, view: IRecipeSlotsView, graphics: GuiGraphics, mouseX: Double, mouseY: Double) {
|
||||
// An opaque panel of our own, so the text reads the same whatever JEI's theme is.
|
||||
graphics.fill(0, 0, WIDTH, height, SURFACE)
|
||||
graphics.fill(0, 0, WIDTH, pageHeight, SURFACE)
|
||||
graphics.fill(0, 0, WIDTH, 1, BORDER)
|
||||
graphics.fill(0, height - 1, WIDTH, height, BORDER)
|
||||
graphics.fill(0, 0, 1, height, BORDER)
|
||||
graphics.fill(WIDTH - 1, 0, WIDTH, height, BORDER)
|
||||
graphics.fill(0, pageHeight - 1, WIDTH, pageHeight, BORDER)
|
||||
graphics.fill(0, 0, 1, pageHeight, BORDER)
|
||||
graphics.fill(WIDTH - 1, 0, WIDTH, pageHeight, BORDER)
|
||||
|
||||
val right = WIDTH - PADDING
|
||||
val tagWidth = font.width(page.tag)
|
||||
@@ -220,26 +282,27 @@ class DropSourcesJei : IModPlugin {
|
||||
} 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.filter { DropSources.exists(it.item) }.map(::dropOutput))
|
||||
Page(picture(p.species), speciesName(p.species), "Drops", rows, p.drops.filter { DropSources.exists(it.item) }.map(::dropOutput), p.species)
|
||||
}
|
||||
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.filter { DropSources.exists(it.item) }.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), s.species)
|
||||
}
|
||||
return wild + special
|
||||
}
|
||||
|
||||
fun bossPages(): List<Page> {
|
||||
val data = DropSources.data
|
||||
if (data.guardians.isEmpty()) return emptyList()
|
||||
val guardians = data.guardians.map { g ->
|
||||
val hours = g.respawn_hours.let { if (it % 1.0 == 0.0) it.toInt() else it }
|
||||
Page(picture(g.species), speciesName(g.species), "Guardian", listOf(
|
||||
Row(text(DropSources.zoneName(g.zone)), text("Lv ${g.level}")), Row(muted("Back $hours hours after a win")),
|
||||
), rewardOutputs(g.drops, gray("for everyone in the winning battle")))
|
||||
), rewardOutputs(g.drops, gray("for everyone in the winning battle")), g.species)
|
||||
}
|
||||
val end = data.end_boss
|
||||
val boss = Page(picture(end.species), speciesName(end.species), "End boss", listOf(
|
||||
Row(text("The End"), text("Lv ${end.level}")), Row(muted("${end.xp_first_win} XP the first time, ${end.xp} after")),
|
||||
), rewardOutputs(end.first_win, gray("first win only")))
|
||||
), rewardOutputs(end.first_win, gray("first win only")), end.species)
|
||||
return guardians + boss
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.UUID
|
||||
*/
|
||||
object MachineShowcase {
|
||||
val OWNER: UUID = UUID.nameUUIDFromBytes("cobblesurvival:showcase".toByteArray())
|
||||
private const val SPACING = 10
|
||||
const val SPACING = 10
|
||||
private const val ROW = 12
|
||||
|
||||
private class Entry(val core: String, val casing: Block, val species: String, val level: Int, val sign: List<String>)
|
||||
@@ -60,6 +60,14 @@ object MachineShowcase {
|
||||
return level.getBlockEntity(core) as? MachineBlockEntity ?: error("no machine at $core")
|
||||
}
|
||||
|
||||
/** One working machine, the [index]th kind of the showcase, at ground level [at]: used by the load test. */
|
||||
fun working(level: ServerLevel, at: BlockPos, index: Int) {
|
||||
val entry = ENTRIES[index % ENTRIES.size]
|
||||
val core = at.above()
|
||||
build(level, core, entry.core, entry.casing, glass = true).fill(level, PokemonSpecies.getByName(entry.species)!!.create(entry.level), OWNER, "Showcase")
|
||||
extras(level, entry, core)
|
||||
}
|
||||
|
||||
private fun placeCore(level: ServerLevel, core: BlockPos, name: String) =
|
||||
level.setBlockAndUpdate(core, BuiltInRegistries.BLOCK.get(id(name)).defaultBlockState().setValue(MachineBlock.FACING, Direction.SOUTH))
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.machine
|
||||
|
||||
import dev.cobblesurvival.permadeath.Permadeath
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
@@ -177,6 +178,11 @@ object MachineWork {
|
||||
}
|
||||
|
||||
fun altarUse(core: MachineBlockEntity, level: ServerLevel, def: MachineDefs.Def, player: ServerPlayer, pokemon: Pokemon) {
|
||||
if (Permadeath.isStarter(pokemon)) {
|
||||
MachineBlockEntity.tell(player, Component.translatableWithFallback("message.cobblesurvival.machine.starter_sacrifice",
|
||||
"%s is your first partner: it can't be sacrificed.", pokemon.getDisplayName()).withStyle(ChatFormatting.YELLOW))
|
||||
return
|
||||
}
|
||||
val rules = def.altar!!
|
||||
val rewards = altarRewards(rules, pokemon)
|
||||
if (core.confirmBy == player.uuid && level.gameTime <= core.confirmUntil) {
|
||||
|
||||
@@ -34,7 +34,8 @@ import java.util.concurrent.ConcurrentLinkedQueue
|
||||
* - once a second, every online player's party and PC are swept for fainted Pokémon (one whose trainer was offline
|
||||
* or in a battle when it fainted).
|
||||
* Settling: a Pokémon holding an item from `#cobblesurvival:revival_charms` is healed and the charm is used up;
|
||||
* any other is removed from its party or PC.
|
||||
* any other is removed from its party or PC. The one exception is the player's starter (Adrian, 2026-11-09): it only
|
||||
* faints, and can't be released or sacrificed either ([isStarter]; CobbleHardcoreMon's side is mixin/HardcorePartyMixin).
|
||||
*/
|
||||
object Permadeath {
|
||||
val REVIVAL_CHARMS: TagKey<Item> =
|
||||
@@ -50,6 +51,18 @@ object Permadeath {
|
||||
// CobbleHardcoreMon settles the battle at NORMAL priority; it has to see the revived Pokémon fainted.
|
||||
CobblemonEvents.BATTLE_ENDED.subscribe(Priority.HIGHEST, ::onBattleEnded)
|
||||
NeoForge.EVENT_BUS.addListener(::onServerTick)
|
||||
CobblemonEvents.POKEMON_RELEASED_EVENT_PRE.subscribe(Priority.NORMAL) { event ->
|
||||
if (!isStarter(event.pokemon)) return@subscribe
|
||||
event.cancel()
|
||||
event.player.sendSystemMessage(Component.translatableWithFallback("message.cobblesurvival.permadeath.starter_release",
|
||||
"%s is your first partner: it can't be released.", event.pokemon.getDisplayName(false)).withStyle(ChatFormatting.YELLOW))
|
||||
}
|
||||
}
|
||||
|
||||
/** The Pokémon this player chose as a starter, which Cobblemon remembers for each player. */
|
||||
fun isStarter(pokemon: Pokemon): Boolean {
|
||||
val owner = pokemon.getOwnerUUID() ?: return false
|
||||
return Cobblemon.playerDataManager.getGenericData(owner).starterUUID == pokemon.uuid
|
||||
}
|
||||
|
||||
private fun onBattleFainted(event: BattleFaintedEvent) {
|
||||
@@ -85,7 +98,7 @@ object Permadeath {
|
||||
|
||||
private fun settle(player: ServerPlayer, pokemon: Pokemon) {
|
||||
val store = pokemon.storeCoordinates.get()?.store
|
||||
if (!pokemon.isFainted() || (store !is PlayerPartyStore && store !is PCStore)) return
|
||||
if (!pokemon.isFainted() || isStarter(pokemon) || (store !is PlayerPartyStore && store !is PCStore)) return
|
||||
// Faints during a battle are settled when it ends (above, and by CobbleHardcoreMon).
|
||||
if (player.isInBattle()) return
|
||||
try {
|
||||
|
||||
@@ -33,6 +33,7 @@ object BossTests {
|
||||
fun all() = listOf(
|
||||
SelfTestCase("bosses_cover_every_guardian", 20, ::coverage),
|
||||
SelfTestCase("boss_attack_shapes", 20, ::shapes),
|
||||
SelfTestCase("bosses_only_take_battle_damage", 40, ::onlyBattleDamage),
|
||||
SelfTestCase("boss_attacks_land_and_stop_with_the_battle", 400, ::landAndStop),
|
||||
SelfTestCase("boss_arena_leaving_loses", 400, ::arena),
|
||||
SelfTestCase("end_boss_replaces_the_dragon", 200, ::endBoss),
|
||||
@@ -109,6 +110,19 @@ object BossTests {
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
private fun onlyBattleDamage(s: Scenario) {
|
||||
val player = s.player()
|
||||
val boss = s.wild(s.pokemon("snorlax", 40), s.at(5.5, 1.0, 5.5))
|
||||
boss.pokemon.persistentData.putBoolean(Guardians.TAG, true)
|
||||
val health = boss.health
|
||||
val sources = boss.damageSources()
|
||||
s.check(!boss.hurt(sources.playerAttack(player), 8f)) { "a player's hit hurt a guardian" }
|
||||
s.check(!boss.hurt(sources.inFire(), 8f)) { "fire hurt a guardian" }
|
||||
s.check(!boss.hurt(sources.drown(), 8f)) { "drowning hurt a guardian" }
|
||||
s.check(boss.health == health) { "the guardian lost health" }
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
private fun shapes(s: Scenario) {
|
||||
val o = Vec3(0.0, 64.0, 0.0)
|
||||
val circle = Area.Circle(o, 3.0)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.selftest
|
||||
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
|
||||
@@ -19,9 +20,14 @@ import kotlin.math.abs
|
||||
* scan switched on only there (other tests put weak trainers next to wild Pokémon).
|
||||
*/
|
||||
object CatchingTests {
|
||||
/** A player who has just joined can't be hurt for this long. */
|
||||
private const val SPAWN_PROTECTION_TICKS = 70
|
||||
|
||||
fun all() = listOf(
|
||||
SelfTestCase("catch_rate_falls_with_level_gap", 20, ::catchRate, batch = "catching"),
|
||||
SelfTestCase("failed_catch_enrages", 400, ::failedCatch, batch = "catching"),
|
||||
SelfTestCase("wild_attack_starts_a_battle", 300, ::attackBattle, batch = "attack_battle"),
|
||||
SelfTestCase("wild_attack_hurts_without_a_party", 120, ::attackHurts, batch = "attack_battle"),
|
||||
SelfTestCase("strong_wild_pokemon_hunt_weak_trainers", 400, { s -> hostility(s, strongest = 5, hostile = true) },
|
||||
batch = "catching_weak"),
|
||||
SelfTestCase("wild_pokemon_ignore_stronger_trainers", 120, { s -> hostility(s, strongest = 50, hostile = false) },
|
||||
@@ -44,6 +50,37 @@ object CatchingTests {
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
private fun attackBattle(s: Scenario) {
|
||||
val player = s.player()
|
||||
s.give(player, s.pokemon("charizard", 50, "ember"))
|
||||
val wild = s.wild(s.pokemon("rattata", 5, "tackle"), s.at(5.5, 1.0, 5.5))
|
||||
val health = player.health
|
||||
s.helper.startSequence()
|
||||
.thenWaitUntil { s.check(wild.pokemon.entity === wild) { "the wild Pokémon has not ticked yet" } }
|
||||
.thenIdle(SPAWN_PROTECTION_TICKS)
|
||||
.thenExecute {
|
||||
s.check(!player.hurt(wild.damageSources().mobAttack(wild), 6f)) { "the hit went through" }
|
||||
// A second hit while the Pokémon is still being sent out doesn't hurt either.
|
||||
s.check(!player.hurt(wild.damageSources().mobAttack(wild), 6f)) { "the second hit went through" }
|
||||
s.check(player.health == health) { "the player lost health" }
|
||||
}
|
||||
.thenWaitUntil { s.check(BattleRegistry.getBattleByParticipatingPlayer(player) != null) { "no battle started" } }
|
||||
.thenSucceed()
|
||||
}
|
||||
|
||||
private fun attackHurts(s: Scenario) {
|
||||
val player = s.player()
|
||||
val wild = s.wild(s.pokemon("rattata", 5, "tackle"), s.at(5.5, 1.0, 5.5))
|
||||
val health = player.health
|
||||
s.helper.startSequence()
|
||||
.thenIdle(SPAWN_PROTECTION_TICKS)
|
||||
.thenExecute {
|
||||
s.check(player.hurt(wild.damageSources().mobAttack(wild), 6f)) { "the hit was cancelled for a player with no Pokémon" }
|
||||
s.check(player.health < health) { "the player lost no health" }
|
||||
}
|
||||
.thenSucceed()
|
||||
}
|
||||
|
||||
private fun failedCatch(s: Scenario) {
|
||||
val player = s.player()
|
||||
s.give(player, s.pokemon("pidgeot", 60)) // stronger than the wild one: only the failed catch angers it
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package dev.cobblesurvival.selftest
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.block.PastureBlock
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||
import dev.cobblesurvival.machine.MachineBlock
|
||||
import dev.cobblesurvival.machine.MachineBlockEntity
|
||||
import dev.cobblesurvival.machine.MachineShowcase
|
||||
import io.netty.channel.embedded.EmbeddedChannel
|
||||
import net.minecraft.commands.CommandSourceStack
|
||||
import net.minecraft.commands.Commands
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.neoforged.neoforge.common.NeoForge
|
||||
import net.neoforged.neoforge.event.tick.ServerTickEvent
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* A realistic load for tools/perf_test.py (roadmap Phase 9): `/cobblesurvival perf start <players> <pastures> <pokémon> <machines>`
|
||||
* puts that many players on the island, each with a base of full pastures and working machines. They are the self-tests'
|
||||
* fake clients, so the server ticks them as real players: Cobblemon spawns around them and their chunks load, while the
|
||||
* first walks a long way, as an explorer would. Only there with -Dcobblesurvival.selftest=true.
|
||||
*/
|
||||
object PerfLoad {
|
||||
private class Bot(val player: ServerPlayer, val channel: EmbeddedChannel, val home: BlockPos, val explorer: Boolean)
|
||||
|
||||
private const val PASTURE_LEVEL = 30
|
||||
private const val PASTURE_SPACING = 14
|
||||
private const val MACHINE_ROW = 24
|
||||
private const val BOT_DISTANCE = 420.0
|
||||
private const val EXPLORER_RING = 900.0
|
||||
private const val EXPLORER_BLOCKS_PER_SECOND = 5.0
|
||||
private const val WALK_RADIUS = 18.0
|
||||
private const val SPIKE_NANOS = 50_000_000L
|
||||
private const val BUILD_DELAY_TICKS = 200
|
||||
private val SPECIES = listOf("pidgey", "rattata", "geodude", "oddish", "ponyta", "magnemite", "eevee", "snorlax",
|
||||
"machop", "slowpoke", "psyduck", "growlithe", "abra", "gastly", "onix", "scyther")
|
||||
|
||||
private val bots = ArrayList<Bot>()
|
||||
private var tethered = 0
|
||||
private val machineCores = ArrayList<BlockPos>()
|
||||
/** The bases, built once the players' chunks are active: a Pokémon added to a chunk about to unload is lost with it. */
|
||||
private var pendingBases: (() -> Unit)? = null
|
||||
private var buildAtTick = 0
|
||||
private var tickStarted = 0L
|
||||
private val samples = ArrayList<Long>()
|
||||
|
||||
fun register() {
|
||||
if (!SelfTest.enabled) return
|
||||
NeoForge.EVENT_BUS.addListener<ServerTickEvent.Pre> { tickStarted = System.nanoTime() }
|
||||
NeoForge.EVENT_BUS.addListener(::onTickDone)
|
||||
}
|
||||
|
||||
fun command(): LiteralArgumentBuilder<CommandSourceStack> =
|
||||
Commands.literal("perf")
|
||||
.requires { it.hasPermission(4) }
|
||||
.then(Commands.literal("start").then(Commands.argument("players", IntegerArgumentType.integer(1)).then(
|
||||
Commands.argument("pastures", IntegerArgumentType.integer(0)).then(Commands.argument("pokemon", IntegerArgumentType.integer(0)).then(
|
||||
Commands.argument("machines", IntegerArgumentType.integer(0)).executes { context ->
|
||||
val reply = start(context.source.server, IntegerArgumentType.getInteger(context, "players"),
|
||||
IntegerArgumentType.getInteger(context, "pastures"), IntegerArgumentType.getInteger(context, "pokemon"),
|
||||
IntegerArgumentType.getInteger(context, "machines"))
|
||||
context.source.sendSuccess({ Component.literal(reply) }, false)
|
||||
1
|
||||
})))))
|
||||
.then(Commands.literal("measure").executes { context ->
|
||||
samples.clear()
|
||||
context.source.sendSuccess({ Component.literal("measuring") }, false)
|
||||
1
|
||||
})
|
||||
.then(Commands.literal("report").executes { context ->
|
||||
context.source.sendSuccess({ Component.literal(report(context.source.server)) }, false)
|
||||
1
|
||||
})
|
||||
|
||||
private fun start(server: MinecraftServer, players: Int, pastures: Int, pokemon: Int, machineCount: Int): String {
|
||||
val level = server.overworld()
|
||||
repeat(players) { i ->
|
||||
val home = land(level, i)
|
||||
val (player, channel) = FakeClient.join(server, level, "perf_$i")
|
||||
player.abilities.invulnerable = true
|
||||
player.teleportTo(level, home.x + 0.5, home.y + 1.0, home.z + 0.5, 0f, 0f)
|
||||
level.chunkSource.move(player) // a client's movement packets would do this: it's what activates the chunks around a player
|
||||
bots += Bot(player, channel, home, i == 0 && players > 1)
|
||||
}
|
||||
pendingBases = {
|
||||
for (bot in bots) {
|
||||
repeat(pastures) { p -> pasture(level, bot.player, surface(level, bot.home.x + p * PASTURE_SPACING, bot.home.z - PASTURE_SPACING), pokemon, p) }
|
||||
repeat(machineCount) { m ->
|
||||
val ground = surface(level, bot.home.x + m * MachineShowcase.SPACING, bot.home.z + MACHINE_ROW)
|
||||
MachineShowcase.working(level, ground, m)
|
||||
machineCores += ground.above()
|
||||
}
|
||||
}
|
||||
}
|
||||
buildAtTick = server.tickCount + BUILD_DELAY_TICKS
|
||||
return "perf started: ${bots.size} players, bases follow in ${BUILD_DELAY_TICKS / 20} s"
|
||||
}
|
||||
|
||||
private fun pasture(level: ServerLevel, player: ServerPlayer, at: BlockPos, count: Int, index: Int) {
|
||||
val pos = at.above()
|
||||
val block = CobblemonBlocks.PASTURE.defaultBlockState()
|
||||
level.setBlockAndUpdate(pos, block.setValue(PastureBlock.PART, PastureBlock.PasturePart.BOTTOM))
|
||||
level.setBlockAndUpdate(pos.above(), block.setValue(PastureBlock.PART, PastureBlock.PasturePart.TOP))
|
||||
val entity = level.getBlockEntity(pos) as? PokemonPastureBlockEntity ?: return
|
||||
val pc = Cobblemon.storage.getPC(player)
|
||||
repeat(count) { i ->
|
||||
val pokemon = PokemonSpecies.getByName(SPECIES[(index * 5 + i) % SPECIES.size])!!.create(PASTURE_LEVEL)
|
||||
pc.add(pokemon)
|
||||
if (entity.tether(player, pokemon, Direction.NORTH)) tethered++
|
||||
}
|
||||
}
|
||||
|
||||
private fun surface(level: ServerLevel, x: Int, z: Int): BlockPos {
|
||||
level.getChunk(x shr 4, z shr 4)
|
||||
return BlockPos(x, level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z), z)
|
||||
}
|
||||
|
||||
/** Dry land for the [index]th player: the first of the points around spawn that isn't water. */
|
||||
private fun land(level: ServerLevel, index: Int): BlockPos {
|
||||
val spawn = level.sharedSpawnPos
|
||||
for (attempt in 0 until 16) {
|
||||
val angle = 2 * PI * (index + attempt / 16.0) / 8
|
||||
val candidate = surface(level, spawn.x + (cos(angle) * BOT_DISTANCE).toInt(), spawn.z + (sin(angle) * BOT_DISTANCE).toInt())
|
||||
if (level.getFluidState(candidate.below()).isEmpty && level.getFluidState(candidate).isEmpty) return candidate
|
||||
}
|
||||
return surface(level, spawn.x, spawn.z)
|
||||
}
|
||||
|
||||
private fun onTickDone(event: ServerTickEvent.Post) {
|
||||
samples += System.nanoTime() - tickStarted
|
||||
val server = event.server
|
||||
if (pendingBases != null && server.tickCount >= buildAtTick) {
|
||||
pendingBases?.invoke()
|
||||
pendingBases = null
|
||||
}
|
||||
bots.removeIf { it.player.hasDisconnected() }
|
||||
for ((i, bot) in bots.withIndex()) {
|
||||
bot.player.doTick()
|
||||
bot.player.connection.chunkSender.onChunkBatchReceivedByClient(25f)
|
||||
bot.channel.releaseOutbound()
|
||||
if (server.tickCount % 20 == 0) walk(server, bot, i)
|
||||
}
|
||||
}
|
||||
|
||||
private fun walk(server: MinecraftServer, bot: Bot, index: Int) {
|
||||
val level = bot.player.serverLevel()
|
||||
val seconds = server.tickCount / 20.0
|
||||
val spawn = level.sharedSpawnPos
|
||||
val (x, z) = if (bot.explorer) {
|
||||
val angle = seconds * EXPLORER_BLOCKS_PER_SECOND / EXPLORER_RING
|
||||
spawn.x + cos(angle) * EXPLORER_RING to spawn.z + sin(angle) * EXPLORER_RING
|
||||
} else {
|
||||
val angle = seconds * 0.2 + index
|
||||
bot.home.x + cos(angle) * WALK_RADIUS to bot.home.z + sin(angle) * WALK_RADIUS
|
||||
}
|
||||
val y = surface(level, x.toInt(), z.toInt()).y + 1.0
|
||||
bot.player.teleportTo(level, x, y, z, 0f, 0f)
|
||||
level.chunkSource.move(bot.player)
|
||||
}
|
||||
|
||||
private fun report(server: MinecraftServer): String {
|
||||
val sorted = samples.sorted()
|
||||
fun at(share: Double) = if (sorted.isEmpty()) 0.0 else sorted[((sorted.size - 1) * share).toInt()] / 1e6
|
||||
val pokemon = server.allLevels.flatMap { level -> level.allEntities.filterIsInstance<PokemonEntity>() }
|
||||
val pastured = pokemon.count { it.tethering != null }
|
||||
val overworld = server.overworld()
|
||||
val working = machineCores.count { core -> (overworld.getBlockEntity(core) is MachineBlockEntity) && overworld.getBlockState(core).getValue(MachineBlock.ACTIVE) }
|
||||
val entityTicking = bots.count { overworld.isPositionEntityTicking(it.player.blockPosition()) }
|
||||
val entities = server.allLevels.sumOf { it.allEntities.count() }
|
||||
val chunks = server.allLevels.sumOf { it.chunkSource.loadedChunksCount }
|
||||
return "perf ticks=${sorted.size} mean_ms=%.2f p50_ms=%.2f p95_ms=%.2f p99_ms=%.2f max_ms=%.2f spikes_over_50ms=${sorted.count { it > SPIKE_NANOS }} "
|
||||
.format(sorted.average().takeIf { !it.isNaN() }?.div(1e6) ?: 0.0, at(.5), at(.95), at(.99), at(1.0)) +
|
||||
"bots=${bots.size} tethered=$tethered pastured=$pastured wild=${pokemon.size - pastured} machines=${machineCores.size} working=$working entities=$entities chunks=$chunks entity_ticking=$entityTicking"
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ object PermadeathTests {
|
||||
|
||||
fun all() = listOf(
|
||||
SelfTestCase("no_healing_or_auto_revive", 40, ::noHealingOrAutoRevive),
|
||||
SelfTestCase("starter_only_faints", 120, ::starterOnlyFaints),
|
||||
SelfTestCase("faint_outside_battle", 5, ::faintOutsideBattle),
|
||||
SelfTestCase("fall", 200, ::fall),
|
||||
SelfTestCase("lava", 300, ::lava),
|
||||
@@ -39,6 +40,23 @@ object PermadeathTests {
|
||||
)
|
||||
|
||||
/** Cobblemon heals the party while the player sleeps and wakes fainted Pokémon after a while; both are off. */
|
||||
/** Adrian, 2026-11-09: the starter is the one Pokémon that is never removed, and a charm it holds isn't used up. */
|
||||
private fun starterOnlyFaints(s: Scenario) {
|
||||
val player = s.player()
|
||||
val starter = s.give(player, s.pokemon("bulbasaur", 5))
|
||||
val other = s.give(player, s.pokemon("rattata", 5))
|
||||
starter.swapHeldItem(ItemStack(s.charm()))
|
||||
Cobblemon.playerDataManager.getGenericData(player).starterUUID = starter.uuid
|
||||
starter.currentHealth = 0
|
||||
other.currentHealth = 0
|
||||
s.helper.startSequence().thenExecuteAfter(80) {
|
||||
s.assertGone(player, other)
|
||||
s.check(Cobblemon.storage.getParty(player).any { it.uuid == starter.uuid }) { "the starter was removed" }
|
||||
s.check(starter.isFainted()) { "the starter was healed" }
|
||||
s.check(starter.heldItem().`is`(s.charm())) { "the starter's charm was used up" }
|
||||
}.thenSucceed()
|
||||
}
|
||||
|
||||
private fun noHealingOrAutoRevive(s: Scenario) {
|
||||
val config = Cobblemon.config
|
||||
s.check(config.healPercent == 0.0) { "healPercent is ${config.healPercent}, so sleeping heals the party" }
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package dev.cobblesurvival.selftest
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.pokemon.requirements.BiomeRequirement
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.fossil.Fossils
|
||||
@@ -40,6 +44,10 @@ object QuestTests {
|
||||
SelfTestCase("quest_events_mark_zones", 200, ::zoneEvent),
|
||||
SelfTestCase("drop_sources_match_the_server", 20, ::dropSources),
|
||||
SelfTestCase("jei_pages_build", 20, ::jeiPages),
|
||||
SelfTestCase("drop_sources_reach_the_client", 20, ::dropSourcesPacked),
|
||||
SelfTestCase("drop_chances_match_cobblemon", 20, ::dropChances),
|
||||
SelfTestCase("regional_forms_are_obtainable", 20, ::regionalForms, islandOnly = true),
|
||||
SelfTestCase("new_players_get_the_quest_book", 20, ::questBook),
|
||||
SelfTestCase("every_implemented_pokemon_is_obtainable", 20, ::obtainable),
|
||||
SelfTestCase("sigil_summons_a_catchable_legend", 200, ::summon),
|
||||
)
|
||||
@@ -113,6 +121,79 @@ object QuestTests {
|
||||
* 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).
|
||||
*/
|
||||
private val REGIONAL = setOf("alolan", "galarian", "hisuian", "paldean")
|
||||
|
||||
/**
|
||||
* Adrian, 2026-11-09: the regional forms JEI lists can be had too: from a wild spawn, a Sigil, or by evolving
|
||||
* something obtainable where a biome the evolution asks for exists on the island, or by breeding back down a line. Battle-only forms (Zen Mode) don't count. Other species count with any form; Shedinja comes with Nincada's evolution, which no requirement names.
|
||||
*/
|
||||
private fun regionalForms(s: Scenario) {
|
||||
val biomes = s.level.chunkSource.generator.biomeSource.possibleBiomes()
|
||||
fun possible(evolution: Evolution) = evolution.requirements.filterIsInstance<BiomeRequirement>().all { r ->
|
||||
biomes.any { b -> r.biomeCondition?.fits(b) != false && r.biomeAnticondition?.fits(b) != true }
|
||||
}
|
||||
val reached = HashSet<FormData>()
|
||||
for (d in CobblemonSpawnPools.WORLD_SPAWN_POOL.details.filterIsInstance<PokemonSpawnDetail>()) {
|
||||
val species = d.pokemon.species?.let { PokemonSpecies.getByName(it.substringAfter(':')) } ?: continue
|
||||
reached += if (d.pokemon.originalString.lowercase().split(' ').any { it in REGIONAL }) d.pokemon.create().form else species.standardForm
|
||||
}
|
||||
reached += Fossils.all().mapNotNull { it.result.species }.mapNotNull { PokemonSpecies.getByName(it.substringAfter(':'))?.standardForm }
|
||||
reached += Summons.defs.values.map { PokemonProperties.parse(it.pokemon).create().form }
|
||||
val queue = ArrayDeque(reached)
|
||||
while (queue.isNotEmpty()) {
|
||||
val current = queue.removeFirst()
|
||||
// Breeding (Cobbreeding) brings the start of a line from any of its stages.
|
||||
current.preEvolution?.form?.let { if (reached.add(it)) queue += it }
|
||||
for (evolution in current.evolutions.filter(::possible)) {
|
||||
val form = evolution.result.create().form
|
||||
if (reached.add(form)) queue += form
|
||||
}
|
||||
}
|
||||
val missing = PokemonSpecies.implemented.flatMap { species ->
|
||||
species.forms.filter { f -> f.aspects.any { it in REGIONAL } && "zen-mode" !in f.aspects && f !in reached }.map { "${it.name} ${species.name}" } +
|
||||
listOfNotNull(species.name.takeIf { species.forms.none(reached::contains) && species.standardForm !in reached && it != "Shedinja" })
|
||||
}
|
||||
s.check(missing.isEmpty()) { "${missing.size} forms can't be obtained: ${missing.take(15)}" }
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
private fun questBook(s: Scenario) {
|
||||
val player = s.player()
|
||||
val book = BuiltInRegistries.ITEM.get(ResourceLocation.parse("ftbquests:book"))
|
||||
s.check(player.inventory.items.any { it.`is`(book) }) { "a new player has no quest book" }
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
/** The chance on each JEI page is what Cobblemon's own roll gives (Adrian, 2026-11-09): rolled here, for every Pokémon. */
|
||||
private fun dropChances(s: Scenario) {
|
||||
val rolls = 4000
|
||||
val problems = mutableListOf<String>()
|
||||
for (p in DropSources.data.pokemon) {
|
||||
val species = PokemonSpecies.getByIdentifier(ResourceLocation.parse(p.species)) ?: continue
|
||||
val got = HashMap<String, Int>()
|
||||
repeat(rolls) {
|
||||
species.drops.getDrops().filterIsInstance<ItemDropEntry>().filter { (it.quantityRange?.random() ?: it.quantity) > 0 }
|
||||
.map { it.item.toString() }.toSet().forEach { got.merge(it, 1, Int::plus) }
|
||||
}
|
||||
for (d in p.drops) {
|
||||
val seen = 100.0 * (got[d.item] ?: 0) / rolls
|
||||
if (kotlin.math.abs(seen - (d.chance ?: 100.0)) > 4.0) problems += "${p.species}: ${d.item} page ${d.chance ?: 100.0}, rolled ${"%.1f".format(seen)}"
|
||||
}
|
||||
}
|
||||
s.check(problems.isEmpty()) { "${problems.size} wrong chances: ${problems.take(5)}" }
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
/** What the server sends players on joining unpacks to what the server has (Adrian, 2026-11-09: balance without a client update). */
|
||||
private fun dropSourcesPacked(s: Scenario) {
|
||||
val sent = DropSources.parse(DropSources.packed)
|
||||
val data = DropSources.data
|
||||
s.check(data.pokemon.isNotEmpty()) { "the server has no drop sources" }
|
||||
s.check(sent.pokemon.size == data.pokemon.size && sent.guardians.size == data.guardians.size && sent.summons.size == data.summons.size) { "the packed drop sources differ" }
|
||||
s.check(DropSources.packed.size < 1_000_000) { "the drop sources are ${DropSources.packed.size} bytes packed, too long for one packet" }
|
||||
s.helper.succeed()
|
||||
}
|
||||
|
||||
private fun jeiPages(s: Scenario) {
|
||||
val data = DropSources.data
|
||||
val pages = mapOf(
|
||||
@@ -129,14 +210,11 @@ object QuestTests {
|
||||
}
|
||||
|
||||
/** A drop as "item chance% ×count", the same way from the JEI data and from Cobblemon's live tables. */
|
||||
private fun dropKey(item: String, chance: Double?, count: String?) = "$item ${"%.2f".format(chance ?: 100.0)}% ×${count ?: "1"}"
|
||||
|
||||
private fun liveDrops(table: DropTable?): Set<String> = table?.entries.orEmpty().filterIsInstance<ItemDropEntry>().map { e ->
|
||||
dropKey(e.item.toString(), e.percentage.toDouble().takeIf { it < 100.0 }, e.quantityRange?.let { if (it.first == it.last) "${it.first}" else "${it.first}-${it.last}" } ?: e.quantity.toString())
|
||||
}.toSet()
|
||||
private fun liveDrops(table: DropTable?): Set<String> = table?.entries.orEmpty().filterIsInstance<ItemDropEntry>().map { it.item.toString() }.toSet()
|
||||
|
||||
/**
|
||||
* The JEI pages' data (assets/cobblesurvival/jei/drop_sources.json, tools/jei_sources.py) still says what the
|
||||
* The JEI pages' data (data/cobblesurvival/drop_sources/pages.json, tools/jei_sources.py) still says what the
|
||||
* server does: every Pokémon's drops and zones, the spawns with drops of their own, the guardians, the End boss
|
||||
* and the Sacrifice Altar. When this fails, run tools/jei_sources.py and rebuild.
|
||||
*/
|
||||
@@ -147,7 +225,7 @@ object QuestTests {
|
||||
val zonesOf = details.groupBy({ "cobblemon:${it.pokemon.species?.lowercase()}" }, { it.id.substringBefore('-') }).mapValues { it.value.toSet() }
|
||||
for (p in data.pokemon) {
|
||||
val species = PokemonSpecies.getByIdentifier(ResourceLocation.parse(p.species)) ?: continue.also { problems += "no species ${p.species}" }
|
||||
val shown = p.drops.map { dropKey(it.item, it.chance, it.count) }.toSet()
|
||||
val shown = p.drops.map { it.item }.toSet()
|
||||
if (shown != liveDrops(species.drops)) problems += "${p.species} drops ${liveDrops(species.drops)}, the JEI data says $shown"
|
||||
if (p.zones.map { it.zone }.toSet() != zonesOf[p.species].orEmpty()) problems += "${p.species} spawns in ${zonesOf[p.species]}, the JEI data says ${p.zones.map { it.zone }}"
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import net.minecraft.gametest.framework.GameTestSequence
|
||||
import net.minecraft.network.Connection
|
||||
import net.minecraft.network.protocol.PacketFlow
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.network.CommonListenerCookie
|
||||
@@ -67,14 +68,7 @@ class Scenario(val case: SelfTestCase, private val index: Int) : GameTestListene
|
||||
*/
|
||||
fun player(): ServerPlayer {
|
||||
val server = level.server
|
||||
val cookie = CommonListenerCookie.createInitial(GameProfile(UUID.randomUUID(), "selftest_${index}_${players++}"), false)
|
||||
val player = ServerPlayer(server, level, cookie.gameProfile(), cookie.clientInformation())
|
||||
val connection = Connection(PacketFlow.SERVERBOUND)
|
||||
EmbeddedChannel(connection)
|
||||
ChannelAttributes.setPayloadSetup(connection, NetworkPayloadSetup.empty())
|
||||
ChannelAttributes.setConnectionType(connection, ConnectionType.NEOFORGE)
|
||||
connection.channel().attr(ChannelAttributes.ADHOC_CHANNELS).set(EveryChannel())
|
||||
server.playerList.placeNewPlayer(connection, player, cookie)
|
||||
val (player, _) = FakeClient.join(server, level, "selftest_${index}_${players++}")
|
||||
val pos = at(3.5, 0.0, 7.5)
|
||||
player.teleportTo(level, pos.x, pos.y, pos.z, 0f, 0f)
|
||||
cleanups += { server.playerList.remove(player) }
|
||||
@@ -220,8 +214,23 @@ class Scenario(val case: SelfTestCase, private val index: Int) : GameTestListene
|
||||
if (passed) CobbleSurvival.LOGGER.info("selftest PASS {}", case.name)
|
||||
else CobbleSurvival.LOGGER.error("selftest FAIL {}: {}", case.name, failure)
|
||||
}
|
||||
}
|
||||
|
||||
/** A client as the server sees one: its connection goes nowhere and accepts every mod's network channel, since NeoForge refuses to send mod packets on an unnegotiated one. */
|
||||
internal object FakeClient {
|
||||
private class EveryChannel : HashSet<ResourceLocation>() {
|
||||
override fun contains(element: ResourceLocation) = true
|
||||
}
|
||||
|
||||
fun join(server: MinecraftServer, level: ServerLevel, name: String): Pair<ServerPlayer, EmbeddedChannel> {
|
||||
val cookie = CommonListenerCookie.createInitial(GameProfile(UUID.randomUUID(), name), false)
|
||||
val player = ServerPlayer(server, level, cookie.gameProfile(), cookie.clientInformation())
|
||||
val connection = Connection(PacketFlow.SERVERBOUND)
|
||||
val channel = EmbeddedChannel(connection)
|
||||
ChannelAttributes.setPayloadSetup(connection, NetworkPayloadSetup.empty())
|
||||
ChannelAttributes.setConnectionType(connection, ConnectionType.NEOFORGE)
|
||||
connection.channel().attr(ChannelAttributes.ADHOC_CHANNELS).set(EveryChannel())
|
||||
server.playerList.placeNewPlayer(connection, player, cookie)
|
||||
return player to channel
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.summon
|
||||
|
||||
import dev.cobblesurvival.Tuning
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
@@ -54,11 +55,11 @@ object Summons : SimpleJsonResourceReloadListener(Gson(), "summons") {
|
||||
/** 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 val NEARBY get() = Tuning.values.summon_spacing
|
||||
private const val FLOOR_ABOVE = 4
|
||||
private const val FLOOR_BELOW = 8
|
||||
|
||||
class Def(val species: String, val level: Int, val zone: String)
|
||||
class Def(val species: String, val pokemon: String, val level: Int, val zone: String)
|
||||
|
||||
var defs: Map<String, Def> = emptyMap()
|
||||
private set
|
||||
@@ -74,7 +75,7 @@ object Summons : SimpleJsonResourceReloadListener(Gson(), "summons") {
|
||||
|
||||
override fun apply(files: Map<ResourceLocation, JsonElement>, 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)
|
||||
it.get("species").asString to Def(it.get("species").asString, it.get("pokemon")?.asString ?: it.get("species").asString, it.get("level").asInt, it.get("zone").asString)
|
||||
}
|
||||
CobbleSurvival.LOGGER.info("Loaded {} summons", defs.size)
|
||||
}
|
||||
@@ -118,7 +119,7 @@ object Summons : SimpleJsonResourceReloadListener(Gson(), "summons") {
|
||||
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()
|
||||
val pokemon = PokemonProperties.parse("${def.pokemon} 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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.zone
|
||||
|
||||
import dev.cobblesurvival.Tuning
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
|
||||
@@ -48,8 +49,8 @@ object Guardians : SimpleJsonResourceReloadListener(Gson(), "guardians") {
|
||||
/** Key in the guardian Pokémon's persistent data; its value is the zone id. */
|
||||
const val TAG = "cobblesurvival_guardian"
|
||||
private const val CHECK_TICKS = 40
|
||||
private const val NEAR = 96.0
|
||||
private const val LEASH = 24.0
|
||||
private val NEAR get() = Tuning.values.guardian_wake_range
|
||||
private val LEASH get() = Tuning.values.guardian_leash
|
||||
private const val TICKS_PER_HOUR = 72_000L
|
||||
/** How far (blocks, each way) a cave guardian's spot may lie from the zone's sample point. */
|
||||
private const val CAVE_SEARCH = 24
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.cobblesurvival.zone
|
||||
|
||||
import dev.cobblesurvival.Tuning
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import dev.cobblesurvival.world.SpecialPokemon
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -18,7 +19,7 @@ import java.util.UUID
|
||||
*/
|
||||
object Wanderers {
|
||||
private const val EVERY_TICKS = 100
|
||||
private const val RANGE = 64.0
|
||||
private val RANGE get() = Tuning.values.wanderer_range
|
||||
|
||||
/** The self-tests switch this off, except in their own batch: other tests put strong wild Pokémon on the platform. */
|
||||
var paused = false
|
||||
|
||||
@@ -29,6 +29,8 @@ import java.util.UUID
|
||||
* apples still work against it. Definitions: `data/cobblesurvival/zone_hazards/<name>.json`.
|
||||
*/
|
||||
object ZoneHazards : SimpleJsonResourceReloadListener(Gson(), "zone_hazards") {
|
||||
private const val CHECK_TICKS = 10
|
||||
|
||||
class Hazard(
|
||||
val zone: String,
|
||||
val effect: Holder<MobEffect>,
|
||||
@@ -72,7 +74,7 @@ object ZoneHazards : SimpleJsonResourceReloadListener(Gson(), "zone_hazards") {
|
||||
|
||||
private fun onServerTick(event: ServerTickEvent.Post) {
|
||||
if (hazards.isEmpty()) return
|
||||
val tick = event.server.tickCount
|
||||
if (event.server.tickCount % CHECK_TICKS != 0) return
|
||||
for (player in event.server.playerList.players) {
|
||||
if (player.isCreative || player.isSpectator) continue
|
||||
val zone = Zones.at(player.serverLevel(), player.blockPosition())?.id
|
||||
@@ -81,7 +83,10 @@ object ZoneHazards : SimpleJsonResourceReloadListener(Gson(), "zone_hazards") {
|
||||
continue
|
||||
}
|
||||
for (hazard in hazards) {
|
||||
if (hazard.zone != zone || tick % hazard.everyTicks != 0 || isProtected(player, hazard)) continue
|
||||
if (hazard.zone != zone || isProtected(player, hazard)) continue
|
||||
// At once on entering the zone (Adrian, 2026-11-09), then renewed every every_ticks.
|
||||
val current = player.getEffect(hazard.effect)
|
||||
if (current != null && current.duration > hazard.durationTicks - hazard.everyTicks) continue
|
||||
player.addEffect(MobEffectInstance(hazard.effect, hazard.durationTicks, hazard.amplifier))
|
||||
if (hazard.warning != null && warned[player.uuid] != zone) {
|
||||
warned[player.uuid] = zone
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"minVersion": "0.8",
|
||||
"package": "dev.cobblesurvival.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": ["RegeneratorBlockMixin"],
|
||||
"mixins": ["HardcorePartyMixin", "RegeneratorBlockMixin"],
|
||||
"injectors": {"defaultRequire": 1}
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
"awardExperienceOnBattleLoss": false,
|
||||
"luckyEggMultiplier": 1.5,
|
||||
"allowSpectating": true,
|
||||
"experienceMultiplier": 2.0,
|
||||
"experienceMultiplier": 1.0,
|
||||
"predationChance": 0.05,
|
||||
"pokemonPerChunk": 1.0,
|
||||
"pokeSnackPokemonPerChunk": 2.0,
|
||||
|
||||
@@ -125,14 +125,14 @@
|
||||
# i.e. if an item has 5 insulation slots and gives one slot of cold insulation by default, it will only have 4 free slots left
|
||||
# ⌄
|
||||
"Insulating Armor" = [
|
||||
["cold_sweat:hoglin_helmet", 0, 10],
|
||||
["cold_sweat:hoglin_chestplate", 0, 14],
|
||||
["cold_sweat:hoglin_leggings", 0, 12],
|
||||
["cold_sweat:hoglin_boots", 0, 10],
|
||||
["cold_sweat:goat_fur_helmet", 10, 0],
|
||||
["cold_sweat:goat_fur_chestplate", 14, 0],
|
||||
["cold_sweat:goat_fur_leggings", 12, 0],
|
||||
["cold_sweat:goat_fur_boots", 10, 0],
|
||||
["cold_sweat:hoglin_helmet", 0, 20],
|
||||
["cold_sweat:hoglin_chestplate", 0, 28],
|
||||
["cold_sweat:hoglin_leggings", 0, 24],
|
||||
["cold_sweat:hoglin_boots", 0, 20],
|
||||
["cold_sweat:goat_fur_helmet", 20, 0],
|
||||
["cold_sweat:goat_fur_chestplate", 28, 0],
|
||||
["cold_sweat:goat_fur_leggings", 24, 0],
|
||||
["cold_sweat:goat_fur_boots", 20, 0],
|
||||
["cold_sweat:chameleon_helmet", 10, 0.0085, "adaptive"],
|
||||
["cold_sweat:chameleon_chestplate", 14, 0.0085, "adaptive"],
|
||||
["cold_sweat:chameleon_leggings", 12, 0.0085, "adaptive"],
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# Rate at which the player's body temperature changes (default: 1.0 (100%))
|
||||
# Default: 1.0
|
||||
# Range: 0.0 ~ Infinity
|
||||
"Rate Multiplier" = 1.0
|
||||
"Rate Multiplier" = 3.0
|
||||
#─────────────────────────────────────────────────────────────────────────
|
||||
# Damage dealt to the player when they are too hot or too cold
|
||||
# Default: 2.0
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
key_key.cobblemon.summary:key.keyboard.n
|
||||
key_key.cobblemon.hideparty:key.keyboard.y
|
||||
key_gui.xaero_waypoints_key:key.keyboard.j
|
||||
key_gui.xaero_open_map:key.keyboard.j
|
||||
key_gui.xaero_waypoints_key:key.keyboard.unknown
|
||||
key_gui.xaero_minimap_settings:key.keyboard.unknown
|
||||
key_key.jade.show_details:key.keyboard.v
|
||||
key_key.jei.showRecipe:key.keyboard.g
|
||||
key_key.jei.showRecipe:key.keyboard.r
|
||||
key_key.jei.showUses:key.keyboard.u
|
||||
key_key.jei.bookmark:key.keyboard.k
|
||||
key_key.jei.focusSearch:key.keyboard.h:CONTROL
|
||||
key_key.jei.pauseRecipeCycling:key.keyboard.right.control
|
||||
key_key.jei.nextSearch:key.keyboard.unknown
|
||||
key_key.jei.previousSearch:key.keyboard.unknown
|
||||
key_key.kubejs.kubedex:key.keyboard.unknown
|
||||
key_key.ftbquests.quests:key.keyboard.grave.accent
|
||||
key_key.ftbquests.gui.recenter:key.keyboard.home
|
||||
key_key.ftbquests.gui.next_chapter:key.keyboard.period
|
||||
key_key.ftbquests.gui.prev_chapter:key.keyboard.comma
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "minecraft:magma_block"
|
||||
}
|
||||
id: "1AB2113B8D294817"
|
||||
order_index: 14
|
||||
order_index: 15
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:pasture"
|
||||
}
|
||||
id: "5B1F192C5A22F0B9"
|
||||
order_index: 18
|
||||
order_index: 19
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:poke_ball"
|
||||
}
|
||||
id: "5B1E1FC28AA13DF0"
|
||||
order_index: 1
|
||||
order_index: 2
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:beautifly,cobblemon:beedrill,cobblemon:bidoof,cobblemon:bonsly,cobblemon:butterfree,cobblemon:cascoon,cobblemon:caterpie,cobblemon:combee,cobblemon:dustox,cobblemon:gimmighoul,cobblemon:hoothoot,cobblemon:kakuna,cobblemon:kricketot,cobblemon:kricketune,cobblemon:ledyba,cobblemon:meditite,cobblemon:meowth,cobblemon:metapod,cobblemon:morpeko,cobblemon:nincada,cobblemon:pachirisu,cobblemon:rattata,cobblemon:sableye,cobblemon:scatterbug,cobblemon:silcoon,cobblemon:skitty,cobblemon:smeargle,cobblemon:spewpa,cobblemon:spinarak,cobblemon:sunkern,cobblemon:tarountula,cobblemon:weedle,cobblemon:wooper,cobblemon:wurmple,cobblemon:wynaut"
|
||||
pokemon: "cobblemon:beautifly,cobblemon:beedrill,cobblemon:bidoof,cobblemon:bonsly,cobblemon:butterfree,cobblemon:cascoon,cobblemon:caterpie,cobblemon:combee,cobblemon:dustox,cobblemon:gimmighoul,cobblemon:hoothoot,cobblemon:kakuna,cobblemon:kricketot,cobblemon:kricketune,cobblemon:ledyba,cobblemon:meditite,cobblemon:metapod,cobblemon:morpeko,cobblemon:nincada,cobblemon:pachirisu,cobblemon:rattata,cobblemon:sableye,cobblemon:scatterbug,cobblemon:silcoon,cobblemon:skitty,cobblemon:smeargle,cobblemon:spewpa,cobblemon:spinarak,cobblemon:sunkern,cobblemon:tarountula,cobblemon:weedle,cobblemon:wooper,cobblemon:wurmple,cobblemon:wynaut"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "minecraft:sculk"
|
||||
}
|
||||
id: "3D9DA7F91C127569"
|
||||
order_index: 13
|
||||
order_index: 14
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:mystic_water"
|
||||
}
|
||||
id: "720F052C0D14A813"
|
||||
order_index: 11
|
||||
order_index: 12
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:barbaracle,cobblemon:binacle,cobblemon:brionne,cobblemon:clamperl,cobblemon:clauncher,cobblemon:clawitzer,cobblemon:cloyster,cobblemon:dewgong,cobblemon:dewott,cobblemon:dhelmise,cobblemon:dragalge,cobblemon:drakloak,cobblemon:empoleon,cobblemon:frillish,cobblemon:gorebyss,cobblemon:horsea,cobblemon:huntail,cobblemon:jellicent,cobblemon:kilowattrel,cobblemon:kingdra,cobblemon:lapras,cobblemon:mantine,cobblemon:mantyke,cobblemon:octillery,cobblemon:oshawott,cobblemon:piplup,cobblemon:popplio,cobblemon:primarina,cobblemon:prinplup,cobblemon:remoraid,cobblemon:samurott,cobblemon:seadra,cobblemon:sealeo,cobblemon:seel,cobblemon:shellder,cobblemon:skrelp,cobblemon:starmie,cobblemon:staryu,cobblemon:veluza,cobblemon:wailmer,cobblemon:wailord,cobblemon:walrein"
|
||||
pokemon: "cobblemon:alomomola,cobblemon:barbaracle,cobblemon:barraskewda,cobblemon:binacle,cobblemon:brionne,cobblemon:clamperl,cobblemon:clauncher,cobblemon:clawitzer,cobblemon:cloyster,cobblemon:dewgong,cobblemon:dewott,cobblemon:dhelmise,cobblemon:dragalge,cobblemon:drakloak,cobblemon:empoleon,cobblemon:frillish,cobblemon:gorebyss,cobblemon:horsea,cobblemon:huntail,cobblemon:jellicent,cobblemon:kilowattrel,cobblemon:kingdra,cobblemon:lapras,cobblemon:malamar,cobblemon:mantine,cobblemon:mantyke,cobblemon:octillery,cobblemon:oshawott,cobblemon:piplup,cobblemon:popplio,cobblemon:primarina,cobblemon:prinplup,cobblemon:remoraid,cobblemon:samurott,cobblemon:seadra,cobblemon:sealeo,cobblemon:seel,cobblemon:shellder,cobblemon:skrelp,cobblemon:starmie,cobblemon:staryu,cobblemon:veluza,cobblemon:wailmer,cobblemon:wailord,cobblemon:walrein"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "minecraft:pointed_dripstone"
|
||||
}
|
||||
id: "1CC462D3533A24CB"
|
||||
order_index: 7
|
||||
order_index: 8
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:ice_stone"
|
||||
}
|
||||
id: "5FFC982FA1DF927B"
|
||||
order_index: 12
|
||||
order_index: 13
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:abomasnow,cobblemon:annihilape,cobblemon:archaludon,cobblemon:aromatisse,cobblemon:avalugg,cobblemon:basculegion,cobblemon:basculin,cobblemon:bayleef,cobblemon:beartic,cobblemon:braviary,cobblemon:cetitan,cobblemon:cherrim,cobblemon:chesnaught,cobblemon:copperajah,cobblemon:cryogonal,cobblemon:dartrix,cobblemon:decidueye,cobblemon:delibird,cobblemon:dubwool,cobblemon:duraludon,cobblemon:eiscue,cobblemon:eldegoss,cobblemon:electabuzz,cobblemon:electivire,cobblemon:floragato,cobblemon:fraxure,cobblemon:froslass,cobblemon:frosmoth,cobblemon:glalie,cobblemon:gogoat,cobblemon:grumpig,cobblemon:hariyama,cobblemon:haxorus,cobblemon:hitmonchan,cobblemon:hitmonlee,cobblemon:hitmontop,cobblemon:infernape,cobblemon:jynx,cobblemon:lucario,cobblemon:lycanroc,cobblemon:machamp,cobblemon:machoke,cobblemon:mamoswine,cobblemon:meganium,cobblemon:meowscarada,cobblemon:mienshao,cobblemon:minior,cobblemon:monferno,cobblemon:piloswine,cobblemon:primeape,cobblemon:pupitar,cobblemon:quilladin,cobblemon:rhydon,cobblemon:rhyperior,cobblemon:roselia,cobblemon:roserade,cobblemon:salandit,cobblemon:sawk,cobblemon:sneasel,cobblemon:sneasler,cobblemon:spinda,cobblemon:stantler,cobblemon:staraptor,cobblemon:throh,cobblemon:togedemaru,cobblemon:togekiss,cobblemon:togetic,cobblemon:tyranitar,cobblemon:vanillish,cobblemon:vanilluxe,cobblemon:wyrdeer"
|
||||
pokemon: "cobblemon:abomasnow,cobblemon:annihilape,cobblemon:archaludon,cobblemon:aromatisse,cobblemon:avalugg,cobblemon:basculegion,cobblemon:basculin,cobblemon:bayleef,cobblemon:beartic,cobblemon:braviary,cobblemon:cetitan,cobblemon:cherrim,cobblemon:chesnaught,cobblemon:copperajah,cobblemon:cryogonal,cobblemon:dartrix,cobblemon:darumaka,cobblemon:decidueye,cobblemon:delibird,cobblemon:dubwool,cobblemon:duraludon,cobblemon:eiscue,cobblemon:eldegoss,cobblemon:electabuzz,cobblemon:electivire,cobblemon:floragato,cobblemon:fraxure,cobblemon:froslass,cobblemon:frosmoth,cobblemon:glalie,cobblemon:gogoat,cobblemon:grumpig,cobblemon:hariyama,cobblemon:haxorus,cobblemon:hitmonchan,cobblemon:hitmonlee,cobblemon:hitmontop,cobblemon:infernape,cobblemon:jynx,cobblemon:lucario,cobblemon:lycanroc,cobblemon:machamp,cobblemon:machoke,cobblemon:mamoswine,cobblemon:meganium,cobblemon:meowscarada,cobblemon:mienshao,cobblemon:minior,cobblemon:monferno,cobblemon:piloswine,cobblemon:primeape,cobblemon:pupitar,cobblemon:quilladin,cobblemon:rhydon,cobblemon:rhyperior,cobblemon:roselia,cobblemon:roserade,cobblemon:salandit,cobblemon:sandshrew,cobblemon:sawk,cobblemon:sneasel,cobblemon:sneasler,cobblemon:spinda,cobblemon:stantler,cobblemon:staraptor,cobblemon:throh,cobblemon:togedemaru,cobblemon:togekiss,cobblemon:togetic,cobblemon:tyranitar,cobblemon:vanillish,cobblemon:vanilluxe,cobblemon:vulpix,cobblemon:wyrdeer,cobblemon:zorua"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
|
||||
@@ -27,6 +27,68 @@
|
||||
}
|
||||
{
|
||||
dependencies: ["692C224D099307BA"]
|
||||
icon: {
|
||||
id: "minecraft:tripwire_hook"
|
||||
}
|
||||
id: "3E981E279A76CEC0"
|
||||
tasks: [
|
||||
{
|
||||
id: "79A62C2E20551202"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: -1.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["692C224D099307BA"]
|
||||
icon: {
|
||||
id: "cobblemon:poke_ball"
|
||||
}
|
||||
id: "20A91DB2D922F503"
|
||||
tasks: [
|
||||
{
|
||||
id: "180A6D1BC999F7D4"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["20A91DB2D922F503"]
|
||||
icon: {
|
||||
id: "minecraft:iron_sword"
|
||||
}
|
||||
id: "47A58B466F15469C"
|
||||
tasks: [
|
||||
{
|
||||
action: "defeat"
|
||||
amount: 1L
|
||||
biome: ""
|
||||
dex_progress: ""
|
||||
dimension: ""
|
||||
form: ""
|
||||
gender: ""
|
||||
id: "023CD014C314A448"
|
||||
max_level: 0
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: ""
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
time_max: 0L
|
||||
time_min: 0L
|
||||
type: "cobblemon_tasks:cobblemon_task"
|
||||
}
|
||||
]
|
||||
x: 4.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["47A58B466F15469C"]
|
||||
icon: {
|
||||
id: "cobblemon:poke_ball"
|
||||
}
|
||||
@@ -77,6 +139,25 @@
|
||||
x: 2.0d
|
||||
y: 0.75d
|
||||
}
|
||||
{
|
||||
dependencies: ["662D0BC3B032EA6A"]
|
||||
icon: {
|
||||
id: "cobblemon:pasture"
|
||||
}
|
||||
id: "7426375BD8FE75B4"
|
||||
tasks: [
|
||||
{
|
||||
id: "0EF1735FF82BF64A"
|
||||
item: {
|
||||
count: 1
|
||||
id: "cobblemon:pasture"
|
||||
}
|
||||
type: "item"
|
||||
}
|
||||
]
|
||||
x: 8.0d
|
||||
y: -3.75d
|
||||
}
|
||||
{
|
||||
dependencies: ["662D0BC3B032EA6A"]
|
||||
icon: {
|
||||
@@ -243,6 +324,66 @@
|
||||
x: 6.0d
|
||||
y: -0.75d
|
||||
}
|
||||
{
|
||||
dependencies: ["734F4C633D8946E3"]
|
||||
icon: {
|
||||
id: "minecraft:filled_map"
|
||||
}
|
||||
id: "60FEC6A879706B6D"
|
||||
tasks: [
|
||||
{
|
||||
id: "7AF22D9FB20998A9"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 10.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["60FEC6A879706B6D"]
|
||||
icon: {
|
||||
id: "minecraft:leather_chestplate"
|
||||
}
|
||||
id: "2CD942BD947AD220"
|
||||
tasks: [
|
||||
{
|
||||
id: "72EC56D3618A6550"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 12.0d
|
||||
y: -1.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["60FEC6A879706B6D"]
|
||||
icon: {
|
||||
id: "minecraft:raw_iron"
|
||||
}
|
||||
id: "2E30721826656889"
|
||||
tasks: [
|
||||
{
|
||||
id: "330E52B7A4CDB749"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 12.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["60FEC6A879706B6D"]
|
||||
icon: {
|
||||
id: "cobblesurvival:heat_core"
|
||||
}
|
||||
id: "64D6151FAA5792B2"
|
||||
tasks: [
|
||||
{
|
||||
id: "66630E973B974C93"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 12.0d
|
||||
y: 1.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["734F4C633D8946E3", "4E95F2B6B144DACB"]
|
||||
icon: {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:sun_stone"
|
||||
}
|
||||
id: "26CC78ED0BC7D785"
|
||||
order_index: 8
|
||||
order_index: 9
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:abra,cobblemon:alakazam,cobblemon:ampharos,cobblemon:bellossom,cobblemon:blitzle,cobblemon:bouffalant,cobblemon:buizel,cobblemon:chewtle,cobblemon:cinderace,cobblemon:combusken,cobblemon:cottonee,cobblemon:cyclizar,cobblemon:cyndaquil,cobblemon:dodrio,cobblemon:doduo,cobblemon:drednaw,cobblemon:electrike,cobblemon:flaaffy,cobblemon:fletchinder,cobblemon:fletchling,cobblemon:floatzel,cobblemon:gloom,cobblemon:golduck,cobblemon:golett,cobblemon:grotle,cobblemon:hoppip,cobblemon:jigglypuff,cobblemon:jumpluff,cobblemon:kadabra,cobblemon:lechonk,cobblemon:lickilicky,cobblemon:lickitung,cobblemon:lokix,cobblemon:manectric,cobblemon:mareep,cobblemon:mightyena,cobblemon:miltank,cobblemon:mudbray,cobblemon:mudsdale,cobblemon:ninetales,cobblemon:oddish,cobblemon:oinkologne,cobblemon:pidgeotto,cobblemon:pidgey,cobblemon:pignite,cobblemon:politoed,cobblemon:poliwag,cobblemon:poliwhirl,cobblemon:poliwrath,cobblemon:polteageist,cobblemon:ponyta,cobblemon:psyduck,cobblemon:quaxly,cobblemon:quaxwell,cobblemon:quilava,cobblemon:raboot,cobblemon:scorbunny,cobblemon:seviper,cobblemon:sinistea,cobblemon:skiploom,cobblemon:spiritomb,cobblemon:squirtle,cobblemon:stonjourner,cobblemon:talonflame,cobblemon:tauros,cobblemon:tepig,cobblemon:tinkatink,cobblemon:tinkatuff,cobblemon:torchic,cobblemon:torterra,cobblemon:turtwig,cobblemon:vileplume,cobblemon:vulpix,cobblemon:wartortle,cobblemon:whimsicott,cobblemon:wigglytuff,cobblemon:zangoose,cobblemon:zebstrika,cobblemon:zoroark,cobblemon:zorua"
|
||||
pokemon: "cobblemon:abra,cobblemon:alakazam,cobblemon:ampharos,cobblemon:bellossom,cobblemon:blitzle,cobblemon:bouffalant,cobblemon:buizel,cobblemon:chewtle,cobblemon:cinderace,cobblemon:combusken,cobblemon:cottonee,cobblemon:cyclizar,cobblemon:cyndaquil,cobblemon:diglett,cobblemon:dodrio,cobblemon:doduo,cobblemon:drednaw,cobblemon:electrike,cobblemon:flaaffy,cobblemon:fletchinder,cobblemon:fletchling,cobblemon:floatzel,cobblemon:gloom,cobblemon:golduck,cobblemon:golett,cobblemon:grotle,cobblemon:hoppip,cobblemon:jigglypuff,cobblemon:jumpluff,cobblemon:kadabra,cobblemon:lechonk,cobblemon:lickilicky,cobblemon:lickitung,cobblemon:lokix,cobblemon:manectric,cobblemon:mareep,cobblemon:mightyena,cobblemon:miltank,cobblemon:mudbray,cobblemon:mudsdale,cobblemon:ninetales,cobblemon:oddish,cobblemon:oinkologne,cobblemon:pidgeotto,cobblemon:pidgey,cobblemon:pignite,cobblemon:politoed,cobblemon:poliwag,cobblemon:poliwhirl,cobblemon:poliwrath,cobblemon:polteageist,cobblemon:ponyta,cobblemon:psyduck,cobblemon:quaxly,cobblemon:quaxwell,cobblemon:quilava,cobblemon:raboot,cobblemon:scorbunny,cobblemon:seviper,cobblemon:sinistea,cobblemon:skiploom,cobblemon:spiritomb,cobblemon:squirtle,cobblemon:stonjourner,cobblemon:talonflame,cobblemon:tauros,cobblemon:tepig,cobblemon:tinkatink,cobblemon:tinkatuff,cobblemon:torchic,cobblemon:torterra,cobblemon:turtwig,cobblemon:vileplume,cobblemon:vulpix,cobblemon:wartortle,cobblemon:whimsicott,cobblemon:wigglytuff,cobblemon:zangoose,cobblemon:zebstrika,cobblemon:zoroark,cobblemon:zorua"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
default_hide_dependency_lines: false
|
||||
default_quest_shape: ""
|
||||
filename: "hms"
|
||||
group: ""
|
||||
icon: {
|
||||
id: "minecraft:iron_pickaxe"
|
||||
}
|
||||
id: "15B6062D067ECE6C"
|
||||
order_index: 1
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
icon: {
|
||||
id: "minecraft:iron_pickaxe"
|
||||
}
|
||||
id: "5662DCFF3C494207"
|
||||
size: 1.5d
|
||||
tasks: [
|
||||
{
|
||||
id: "1E3E3883C5B3D807"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 0.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:iron_axe"
|
||||
}
|
||||
id: "136DBA5479235C7C"
|
||||
tasks: [
|
||||
{
|
||||
id: "27978949A669D5C5"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: -4.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:ender_pearl"
|
||||
}
|
||||
id: "2CE94666160FA049"
|
||||
tasks: [
|
||||
{
|
||||
id: "68A914F71CB2AC6B"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: -3.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:nether_star"
|
||||
}
|
||||
id: "14426F35CDAB120B"
|
||||
tasks: [
|
||||
{
|
||||
id: "44F75B6501B37EE0"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: -1.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:spyglass"
|
||||
}
|
||||
id: "14A663D7085B96EF"
|
||||
tasks: [
|
||||
{
|
||||
id: "12874838083C1D42"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 0.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:honey_bottle"
|
||||
}
|
||||
id: "22D0F5E8ADD14B3C"
|
||||
tasks: [
|
||||
{
|
||||
id: "54E90B9B1AF4C46C"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 1.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:snowball"
|
||||
}
|
||||
id: "0364A4D8EA904F6E"
|
||||
tasks: [
|
||||
{
|
||||
id: "4F3BC1526B0D54EB"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 3.0d
|
||||
}
|
||||
{
|
||||
dependencies: ["5662DCFF3C494207"]
|
||||
icon: {
|
||||
id: "minecraft:saddle"
|
||||
}
|
||||
id: "0C98431E89D63AA8"
|
||||
tasks: [
|
||||
{
|
||||
id: "37E7DC548E1504B2"
|
||||
type: "checkmark"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 4.5d
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblemon:leaf_stone"
|
||||
}
|
||||
id: "0F763138AA0551CF"
|
||||
order_index: 6
|
||||
order_index: 7
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:aipom,cobblemon:bellsprout,cobblemon:bewear,cobblemon:bounsweet,cobblemon:bulbasaur,cobblemon:chatot,cobblemon:darumaka,cobblemon:exeggcute,cobblemon:exeggutor,cobblemon:fomantis,cobblemon:grafaiai,cobblemon:grookey,cobblemon:grovyle,cobblemon:ivysaur,cobblemon:jangmoo,cobblemon:kecleon,cobblemon:komala,cobblemon:litten,cobblemon:natu,cobblemon:pancham,cobblemon:panpour,cobblemon:pansage,cobblemon:pansear,cobblemon:pikipek,cobblemon:servine,cobblemon:shroodle,cobblemon:simipour,cobblemon:simisage,cobblemon:simisear,cobblemon:slakoth,cobblemon:snivy,cobblemon:steenee,cobblemon:stufful,cobblemon:stunfisk,cobblemon:swoobat,cobblemon:tangela,cobblemon:tangrowth,cobblemon:thwackey,cobblemon:torracat,cobblemon:toucannon,cobblemon:treecko,cobblemon:tropius,cobblemon:trumbeak,cobblemon:tsareena,cobblemon:unown,cobblemon:vigoroth,cobblemon:weepinbell,cobblemon:woobat,cobblemon:xatu"
|
||||
pokemon: "cobblemon:aipom,cobblemon:bellsprout,cobblemon:bewear,cobblemon:bounsweet,cobblemon:bulbasaur,cobblemon:chatot,cobblemon:darumaka,cobblemon:exeggcute,cobblemon:exeggutor,cobblemon:fomantis,cobblemon:grafaiai,cobblemon:grookey,cobblemon:grovyle,cobblemon:ivysaur,cobblemon:jangmoo,cobblemon:kecleon,cobblemon:komala,cobblemon:litten,cobblemon:natu,cobblemon:pancham,cobblemon:panpour,cobblemon:pansage,cobblemon:pansear,cobblemon:pikipek,cobblemon:servine,cobblemon:shroodle,cobblemon:simipour,cobblemon:simisage,cobblemon:simisear,cobblemon:slakoth,cobblemon:slowpoke,cobblemon:snivy,cobblemon:steenee,cobblemon:stufful,cobblemon:stunfisk,cobblemon:swoobat,cobblemon:tangela,cobblemon:tangrowth,cobblemon:thwackey,cobblemon:torracat,cobblemon:toucannon,cobblemon:treecko,cobblemon:tropius,cobblemon:trumbeak,cobblemon:tsareena,cobblemon:unown,cobblemon:vigoroth,cobblemon:weepinbell,cobblemon:woobat,cobblemon:xatu"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblesurvival:summon_sigil"
|
||||
}
|
||||
id: "0217ED8D82F11BE4"
|
||||
order_index: 16
|
||||
order_index: 17
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
@@ -729,5 +729,101 @@
|
||||
x: 6.5d
|
||||
y: 4.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["73A150C1A7221BD0"]
|
||||
icon: {
|
||||
id: "cobblesurvival:summon_sigil"
|
||||
}
|
||||
id: "027E3B8E614FA133"
|
||||
tasks: [
|
||||
{
|
||||
action: "catch"
|
||||
amount: 1L
|
||||
biome: ""
|
||||
dex_progress: ""
|
||||
dimension: ""
|
||||
form: ""
|
||||
gender: ""
|
||||
id: "2424A0A7C316243A"
|
||||
max_level: 0
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:zapdos"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
time_max: 0L
|
||||
time_min: 0L
|
||||
type: "cobblemon_tasks:cobblemon_task"
|
||||
}
|
||||
]
|
||||
x: 8.0d
|
||||
y: 4.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["73A150C1A7221BD0"]
|
||||
icon: {
|
||||
id: "cobblesurvival:summon_sigil"
|
||||
}
|
||||
id: "3B2B92B85F91AABA"
|
||||
tasks: [
|
||||
{
|
||||
action: "catch"
|
||||
amount: 1L
|
||||
biome: ""
|
||||
dex_progress: ""
|
||||
dimension: ""
|
||||
form: ""
|
||||
gender: ""
|
||||
id: "3A1DA041487A4346"
|
||||
max_level: 0
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:articuno"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
time_max: 0L
|
||||
time_min: 0L
|
||||
type: "cobblemon_tasks:cobblemon_task"
|
||||
}
|
||||
]
|
||||
x: 9.5d
|
||||
y: 4.5d
|
||||
}
|
||||
{
|
||||
dependencies: ["73A150C1A7221BD0"]
|
||||
icon: {
|
||||
id: "cobblesurvival:summon_sigil"
|
||||
}
|
||||
id: "1DADE103D473A8DB"
|
||||
tasks: [
|
||||
{
|
||||
action: "catch"
|
||||
amount: 1L
|
||||
biome: ""
|
||||
dex_progress: ""
|
||||
dimension: ""
|
||||
form: ""
|
||||
gender: ""
|
||||
id: "2FCDD8BF421D688F"
|
||||
max_level: 0
|
||||
min_level: 0
|
||||
natures: ""
|
||||
poke_ball_used: ""
|
||||
pokemon: "cobblemon:moltres"
|
||||
pokemon_type: ""
|
||||
region: ""
|
||||
shiny: false
|
||||
time_max: 0L
|
||||
time_min: 0L
|
||||
type: "cobblemon_tasks:cobblemon_task"
|
||||
}
|
||||
]
|
||||
x: 2.0d
|
||||
y: 6.0d
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "minecraft:glow_berries"
|
||||
}
|
||||
id: "5FD07FF070099857"
|
||||
order_index: 4
|
||||
order_index: 5
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
id: "cobblesurvival:heat_core"
|
||||
}
|
||||
id: "5170190C30A58C4C"
|
||||
order_index: 17
|
||||
order_index: 18
|
||||
quest_links: [ ]
|
||||
quests: [
|
||||
{
|
||||
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user