Cobblemon Survival Pack: phases 0-2 and Phase 3 zones and hazards
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>
This commit is contained in:
commit
398301f0be
232 files changed
+72665
No files matched your search
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the field guide: where every Pokémon lives on the island, how to get every mod item,
|
||||
and what each item is for, from the pack's own data.
|
||||
|
||||
Reads vanilla data (build/server/libraries), NeoForge, every jar listed in pack/mods (copied to
|
||||
build/fieldguide-cache/jars from build/stage/full, build/server or the Prism instance), then
|
||||
datapacks/core on top, plus world/biomes.json and world/world.json for the island. The recipes the
|
||||
pack's KubeJS scripts add, change or remove come from `/kubejs export` on the test server
|
||||
(kubejs_export.py); when that export is older than the pack, this boots the test server once to
|
||||
refresh it (about a minute) unless --no-server is given.
|
||||
|
||||
Writes build/field-guide/:
|
||||
fieldguide.json the single source every renderer reads
|
||||
index.html, pokemon.html, items.html, data.js, assets/, icons/ the HTML guide
|
||||
ingame/ftbquests/ FTB Quests "Field Guide" chapters (not installed)
|
||||
ingame/kubejs/ Pokédex "Found in" lang override (not installed)
|
||||
|
||||
Usage: tools/fieldguide/build.py [--out DIR] [--no-icons] [--no-server] [--from-json FILE] [--extra FILE ...]
|
||||
--no-server never start Minecraft: use the last KubeJS recipe export even if the pack changed since
|
||||
--from-json skip extraction and render an existing fieldguide.json (e.g. one a runtime exporter wrote)
|
||||
--extra add sources from a JSON file (format in docs/field-guide.md), e.g. ore veins or trades
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
sys.dont_write_bytecode = True # keep tools/fieldguide free of __pycache__
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from fieldguide import datasource, export_ftbquests, export_lang, guide, kubejs_export, render_html # noqa: E402
|
||||
from fieldguide.icons import Icons # noqa: E402
|
||||
from fieldguide.model import Model # noqa: E402
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
||||
ap.add_argument("--out", type=Path, default=ROOT / "build/field-guide")
|
||||
ap.add_argument("--no-icons", action="store_true", help="skip rendering item icons")
|
||||
ap.add_argument("--no-server", action="store_true", help="use the last KubeJS recipe export as it is")
|
||||
ap.add_argument("--from-json", type=Path, help="render this fieldguide.json instead of extracting")
|
||||
ap.add_argument("--extra", type=Path, action="append", default=[], help="extra sources JSON (repeatable)")
|
||||
args = ap.parse_args()
|
||||
out = args.out
|
||||
started = time.time()
|
||||
|
||||
if args.from_json:
|
||||
data = json.loads(args.from_json.read_text())
|
||||
lang = {}
|
||||
else:
|
||||
print("reading the pack's data")
|
||||
stack = datasource.build_stack(log=_log_once())
|
||||
model = Model(stack, log=lambda msg: print(" " + msg), extras=args.extra, kubejs=_kubejs(args.no_server))
|
||||
icons = {}
|
||||
if not args.no_icons:
|
||||
wanted = set(model.mod_items) | _vanilla_mentions(model)
|
||||
ic = Icons(stack, out)
|
||||
icons = ic.write_all(wanted)
|
||||
print(f" icons: {len(icons)} rendered, {len(ic.missing)} without a usable model")
|
||||
data = guide.build(model, icons)
|
||||
guide.write(data, out / "fieldguide.json")
|
||||
lang = model.lang
|
||||
render_html.write(data, out)
|
||||
chapters = export_ftbquests.write(data, out / "ingame")
|
||||
if lang:
|
||||
n, missing = export_lang.write(data, lang, out / "ingame")
|
||||
print(f" Pokédex override: {n} keys" + (f", {len(missing)} keys not in the jar's lang" if missing else ""))
|
||||
(out / "ingame/README.md").write_text(INGAME_README)
|
||||
_summary(data, chapters, out, time.time() - started)
|
||||
|
||||
|
||||
def _kubejs(no_server):
|
||||
"""The pack's KubeJS recipe changes; refreshed from the test server when the pack changed since."""
|
||||
data = kubejs_export.load()
|
||||
if data and not data["stale"]:
|
||||
print(f" KubeJS recipes: export from {data['exported']}")
|
||||
return data
|
||||
why = "no KubeJS recipe export yet" if not data else "pack/kubejs, pack/mods or datapacks/core changed since the last KubeJS recipe export"
|
||||
if no_server:
|
||||
print(f" warning: {why}; --no-server, so the guide " +
|
||||
("uses the old export" if data else "shows no pack recipes"))
|
||||
return data
|
||||
print(f" {why}: booting the test server for /kubejs export")
|
||||
try:
|
||||
return kubejs_export.export(log=lambda msg: print(" " + msg))
|
||||
except (SystemExit, Exception) as e: # e.g. no EULA, a server already running, mods failing to load
|
||||
print(f" warning: KubeJS recipe export failed ({type(e).__name__}: {e}); the guide " +
|
||||
("uses the old export" if data else "shows no pack recipes"))
|
||||
return data
|
||||
|
||||
|
||||
def _vanilla_mentions(model):
|
||||
"""Vanilla items that show up in recipes or sources of mod items (they get icons too)."""
|
||||
out = set()
|
||||
mods = set(model.mod_items)
|
||||
for r in model.recipes.values():
|
||||
if r["result"] in mods or any(i in mods for s in r["slots"] for i in s["items"]):
|
||||
out.add(r["result"])
|
||||
for s in r["slots"]:
|
||||
out.update(s["items"][:8])
|
||||
for d in model.species.values():
|
||||
for e in (d.get("drops") or {}).get("entries", []):
|
||||
out.add(e["item"] if ":" in e["item"] else "minecraft:" + e["item"])
|
||||
return {i for i in out if i.startswith("minecraft:")}
|
||||
|
||||
|
||||
def _log_once():
|
||||
seen = []
|
||||
|
||||
def log(msg):
|
||||
if msg.startswith("warning:") and "not found in" in msg:
|
||||
seen.append(msg.split()[1])
|
||||
if len(seen) == 1:
|
||||
print(" some pack/mods jars are not downloaded yet (run tools/build.py); continuing without them")
|
||||
return
|
||||
print(" " + msg)
|
||||
return log
|
||||
|
||||
|
||||
def _summary(data, chapters, out, secs):
|
||||
sp = data["species"]
|
||||
items = data["items"]
|
||||
wild = sum(1 for s in sp.values() if s["wild"] == "available")
|
||||
other = sum(1 for s in sp.values() if s["wild"] != "available" and s["status"] == "available")
|
||||
cond = sum(1 for s in sp.values() if s["status"] == "conditional")
|
||||
none = [s for s in sp.values() if s["status"] == "unavailable"]
|
||||
nosrc = [i for i, v in items.items() if v["status"] == "unavailable" and not v["technical"]]
|
||||
print(f"\nPokémon: {wild} wild on the island, {other} more by evolution/fossil/starter, {cond} only in the End, "
|
||||
f"{len(none)} not obtainable ({sum(1 for s in none if not s['implemented'])} of them not implemented)")
|
||||
for loc in data["locations"]:
|
||||
n = len({m["species"] for i in loc["spawns"] for m in data["spawns"][i]["members"]})
|
||||
print(f" {loc['name']:<24} {loc['kind']:<8} {n:>4} species")
|
||||
print(f"Items: {len(items)} mod items, {len(nosrc)} with no source on the island")
|
||||
print(f"FTB Quests chapters: " + ", ".join(f"{k} ({v} quests)" for k, v in chapters.items()))
|
||||
print(f"Wrote {out} in {secs:.1f}s; open {out / 'index.html'}")
|
||||
|
||||
|
||||
INGAME_README = """# In-game exports (generated, not installed)
|
||||
|
||||
- `ftbquests/`: FTB Quests "Field Guide" chapters. See `ftbquests/README.md`.
|
||||
- `kubejs/`: Pokédex description override that appends "Found in: …". See `kubejs/README.md`.
|
||||
|
||||
Both are generated by `python3 tools/fieldguide/build.py` from `../fieldguide.json`.
|
||||
Installing them is a separate decision; nothing here is copied into pack/.
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user