Free NeoForge 1.21.1 modpack on Cobblemon 1.8.4 built with packwiz, plus the Kotlin companion mod (cobblesurvival) and its built-in data pack. - Phase 0: packwiz pack with Modrinth/CurseForge cross-links, build and export tooling, Prism instance sync, test server with smoke checks. - Phase 1: Poke Ball shaped island (WorldGen Editor preset, seed isle-26), Adrian's biome list, no natural ore, structures without ore or vanilla mobs, no Nether portals, world template builder and world report. - Phase 2: permadeath (CobbleHardcoreMon plus the companion mod's gap fixes), Revival Charm, expensive Healing Machine, Lootr refreshing loot, in-game self-tests on Minecraft's GameTest framework. - Phase 3 so far: 14 zones with their own spawn pools and level bands (tools/zone_layout.py, tools/zone_spawns.py, "zone" spawn condition), Cold Sweat heat and cold as gates with boss-drop insulation recipes, spawn survey and hazard self-tests in tools/quick_check.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
11 KiB
Markdown
100 lines
11 KiB
Markdown
# Field guide
|
||
|
||
`tools/fieldguide/` generates a field guide from the pack's own data: where every Pokémon lives on the island, how to get every item the mods add (and whether the island can provide it at all), and what each item is for. It also exports the same knowledge in two forms the game can show. Everything it writes goes to `build/field-guide/`; nothing is installed into `pack/`.
|
||
|
||
```bash
|
||
python3 tools/fieldguide/build.py # about 10 s (+ ~25 s when pack recipes changed); open build/field-guide/index.html
|
||
python3 tools/fieldguide/build.py --no-icons # skip icon rendering
|
||
python3 tools/fieldguide/build.py --no-server # never start Minecraft; use the last KubeJS recipe export as it is
|
||
python3 tools/fieldguide/kubejs_export.py # only refresh the KubeJS recipe export (below)
|
||
python3 tools/fieldguide/build.py --from-json build/field-guide/fieldguide.json # re-render only
|
||
python3 tools/fieldguide/build.py --extra my-sources.json # add sources (below)
|
||
```
|
||
|
||
It needs `tools/build.py` (for `build/stage/full/mods`) or a server under `build/server` to have run once, because it reads the downloaded jars and the vanilla data jar. The only time it starts Minecraft is to refresh the pack's KubeJS recipes (see [Pack recipes](#pack-recipes-kubejs)).
|
||
|
||
## Outputs
|
||
|
||
| Path under `build/field-guide/` | What |
|
||
| --- | --- |
|
||
| `fieldguide.json` | The single source. Every renderer reads only this file (schema in `tools/fieldguide/guide.py`). `meta.kubejs_export` says which KubeJS recipe export it used; `hidden_spawns` keeps the spawn entries a filter hides (below) |
|
||
| `index.html` | Overview: counts, locations, items with no source on the island, structures, unresolved data |
|
||
| `pokemon.html` | Pokémon by location (every island biome, caves and the End included), a page per species with the zones it lives in, and the species that are not wild here with the reason and the other ways to get them |
|
||
| `items.html` | Every mod item with filters; the detail view shows recipe grids, all other sources with chances, and every use |
|
||
| `icons/` | Item icons rendered from the jars' models. Third-party assets: build output only, never commit them |
|
||
| `ingame/ftbquests/` | Three FTB Quests chapters ("Field Guide: Wild Pokémon", "Other Pokémon", "Items") and their `lang/en_us.snbt`. Install note in its `README.md` |
|
||
| `ingame/kubejs/` | `assets/cobblemon/lang/en_us.json`: Cobblemon's Pokédex descriptions with "Found in: …" appended. Install note in its `README.md` |
|
||
|
||
The HTML works from disk (no server, no network) and follows the system's light or dark mode; the ◐ button switches by hand.
|
||
|
||
## How it reads the data
|
||
|
||
The data stack mirrors Minecraft's pack order: vanilla server data, NeoForge, every jar named in `pack/mods/*.pw.toml` (plus the companion mod), then `datapacks/core`, then `pack/kubejs` on top (KubeJS serves its `data/` as a data pack and its `assets/` as a resource pack; the guide reads textures and lang from there). A higher layer replaces a file, tags merge across layers, and a `pack.mcmeta` `filter` block hides the matching files of the layers below, so now that `datapacks/core` filters out Cobblemon's spawn pools and adds the zone pools, `spawns` and every page show only the zone pools. Cobblemon's own entries stay in `fieldguide.json` under `hidden_spawns`, in the same shape and resolved against the island the same way (status is what the entry would have if it were active), because `tools/zone_spawns.py` plans the zones from them; they count for nothing else. Jars are copied to `build/fieldguide-cache/jars/` first.
|
||
|
||
The island comes from `world/biomes.json` (`allowed` + `water`), the world preset named in `world/world.json` (which dimensions exist: overworld and the End, no Nether), structure and structure-set JSON (which structures can generate in the island's biomes), biome feature lists plus NeoForge biome modifiers (which blocks generate naturally, and which ores the pack switched off), and biome spawners plus structure spawn overrides (which vanilla mobs exist).
|
||
|
||
Spawns follow Cobblemon 1.8.4's rules: a spawn's conditions are alternatives, anticonditions veto, presets are merged into every condition (lists appended, single values kept), and a biome named in any anticondition never counts. A spawn that needs a structure is only listed in the biomes that structure generates in.
|
||
|
||
The companion mod's zone condition (`"zone": "<id>"`, or `"zones": [...]`, which wins; `mod/.../zone/Zones.kt`) is shown first in a spawn's conditions as the zone's name and level band from `world/zones.json`, e.g. "zone Southwold (Lv 8–18)". Each spawn entry in `fieldguide.json` carries its zone ids (`zones`), and each "Where to find it" row of a species names the zones (`species[*].locations[*].zones`; the FTB Quests text says "Badlands (Southwold)"). A zone id missing from `world/zones.json` is listed under unresolved data.
|
||
|
||
### Statuses
|
||
|
||
Every way to get something is a *source* that may need other things (a recipe needs its ingredients and station, a Pokémon drop needs a wild spawn, a mob drop needs the mob, archaeology needs a brush, a fossil needs the fossil and the Fossil Analyzer parts, an evolution needs the pre-evolution and its item). A fixpoint over all items, blocks, mobs, spawns and species then marks each one:
|
||
|
||
- **available**: obtainable on the island.
|
||
- **conditional**: only with something that may not exist: the End (needs a stronghold portal) or strongholds themselves (their rings start 1,280 blocks out and may lie outside the world border).
|
||
- **unavailable**: no source here. The guide says why: ore generation switched off, Nether only, a biome or structure the island lacks, or no source in the data at all.
|
||
|
||
## Pack recipes (KubeJS)
|
||
|
||
The pack adds, replaces and removes recipes in `pack/kubejs/server_scripts/` (Phase 2: the Revival Charm, and a Healing Machine that replaces Cobblemon's). Those scripts are code, so the guide does not parse them: it asks KubeJS what they did on the real server.
|
||
|
||
`tools/fieldguide/kubejs_export.py` prepares the test server in `build/server` like the other server tools (it syncs from `build/stage/full`), boots it on a throwaway void world, runs `/kubejs export debug` over RCON and stops it (about 25 s). KubeJS reloads the data and writes `build/server/local/kubejs/export/`; the exporter keeps three parts in `build/fieldguide-cache/kubejs-export/export.json`:
|
||
|
||
- every recipe that carries KubeJS's `_kubejs_changed_marker` (added or changed by a script; the final recipe JSON plus the script and line),
|
||
- every recipe id a script removed (`removed_recipes/`), whichever jar or data pack it came from,
|
||
- the server's item registry, so items a KubeJS startup script registers (`cobblesurvival:revival_charm`) are listed although no jar has a model for them. Their icon is the texture under `pack/kubejs/assets`, as KubeJS's default `item/generated` model would show it.
|
||
|
||
`build.py` applies that export after reading the data stack: removed recipes disappear (with every source and use they gave), added or changed ones replace any recipe with the same id and are labelled "(pack recipe)" in the guide and the FTB Quests chapter, with `script` in `fieldguide.json` pointing at the script line.
|
||
|
||
The export stores a hash of what can change the recipe set: `pack/kubejs/{server_scripts,startup_scripts,data}`, `pack/mods` and `datapacks/core`. When that no longer matches, `build.py` refreshes the export by itself before building. So after editing a recipe script:
|
||
|
||
1. Run `tools/build.py` (the server runs the staged copy of `pack/kubejs`; the exporter refuses to export a staged copy that differs from `pack/kubejs`).
|
||
2. Run `tools/fieldguide/build.py`.
|
||
|
||
If the export cannot run (no EULA in `build/server/eula.txt`, a test server is already running there, a mod fails to load) or `--no-server` is given, the guide uses the last export, says so on the console and lists it under "unresolved data". Without any export it shows only the jars' recipes and says that too.
|
||
|
||
## Adding sources: `--extra`
|
||
|
||
Sources the data cannot show yet (Phase 4 ore veins, trades, code-only mechanics) can be fed in without touching the code:
|
||
|
||
```json
|
||
{
|
||
"sources": [
|
||
{"item": "minecraft:raw_iron", "how": "Regenerating iron vein (forest zone)", "kind": "ore vein", "needs": []},
|
||
{"item": "cobblemon:tasty_tail", "how": "Shear a Slowpoke", "needs": [["species:cobblemon:slowpoke"], ["item:minecraft:shears"]]}
|
||
],
|
||
"remove_recipes": ["cobblemon:poke_ball"]
|
||
}
|
||
```
|
||
|
||
`needs` is a list of groups; each group is met by any one of its keys (`item:`, `block:`, `mob:`, `species:`). `remove_recipes` drops recipes by id. Recipes from data (`datapacks/core`, `pack/kubejs/data`) and from KubeJS scripts are read without this file. A later runtime exporter can instead write `fieldguide.json` itself and render it with `--from-json`.
|
||
|
||
## Known limits
|
||
|
||
- Mechanics that live only in code are not in the data: villager trades (only emeralds are entered by hand), Poké Puffs, Slowpoke tail shearing (Tasty Tail), Oricorio's flowers, Gimmighoul chests, budding Tumblestone. Those items show "no source in the data". Hand-entered vanilla mechanics are in `tools/fieldguide/manual_sources.json`.
|
||
- Code tables to re-check after a Cobblemon or Minecraft update: `COBBLEMON_CODED_FEATURES`, `CODE_FEATURE_BLOCKS`, `CODE_LOOT`, `CODE_STRUCTURE_BLOCKS` and `SPECIAL_MOBS` in `tools/fieldguide/world.py`, `STATIONS` and `TECHNICAL` in `model.py`. Each says where it comes from.
|
||
- Loot chances are estimates (expected rolls × weight share × random-chance conditions). Spawn chances are Cobblemon's bucket chance plus the weight; the real rate also depends on what else can spawn at that spot.
|
||
- A spawn that needs particular blocks nearby (concrete, pyramid blocks) is listed in every biome its tag allows, because players can build those blocks anywhere; the guide lists those spawns last and marks them.
|
||
- The guide names a spawn's zone but does not know where the zones lie (that is the zone map the companion mod reads at runtime), so a zone pool entry is listed in every island biome its biome condition allows, also where its zone has none of that biome, and its status ignores the zone.
|
||
- Mod item lists come from item models that have a name or appear in data, plus the server's item registry from the KubeJS export; a few creative-only items are marked as such.
|
||
- What a KubeJS startup script sets in code (`.displayName`, `.tooltip`) is not in the export: such items are named from `pack/kubejs/assets/*/lang` if present, else from their id ("Revival Charm"), and have no tooltip in the guide.
|
||
|
||
## In-game checks still needed
|
||
|
||
The exports follow the formats read from FTB Quests 2101.1.37's and Cobblemon 1.8.4's source, but nobody has loaded them in the game yet:
|
||
|
||
1. FTB Quests: the three chapters appear, titles and descriptions show (not raw keys), Pokémon icons render as models (`cobblemon:pokemon_model` with the `cobblemon:pokemon_item` component), the quest grid is readable, nothing is locked, and `&c`/`&6` colour codes in item descriptions render as colours.
|
||
2. Pokédex override: KubeJS serves `kubejs/assets` on the client, the appended "Found in: …" sentence shows on a Pokédex page, long texts wrap or scroll inside the description box, and other languages are unaffected.
|
||
3. Spot-check a few guide claims in a test world, e.g. Bulbasaur in jungle only, Nidoran absent (savanna is remapped to desert), Aron absent (it needs iron ore nearby), a Water Stone from a Gimmi Tower chest.
|