Phase 9: safe start island, better quest book, every useful item obtainable

- Button Isle is peaceful: wild Pokémon only fight a player who hit them first
  (tuning/server.json peaceful_zones); its apricorns and coal make Poké Balls
  (4 apricorns + coal or charcoal), and the world report checks it has enough.
- Quest book: no boilerplate; each zone page says what is particular to it, and
  every zone that needs gear or a field move to be safe has its own quest. New
  page on the bigger mods (Cold Sweat, Fight or Flight, Cobblebase, breeding,
  veins, shared loot, maps and info).
- The Mire's poison stops 10 blocks below the surface.
- Player battles never cost a Pokémon (CobbleHardcoreMon config, mixins, and the
  companion mod's own permadeath).
- tools/item_audit.py: every item with a use has a source on the island; new
  sources for the gaps it found (species additions, three recipes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-10 15:04:09 +01:00
1 parent 6ace26e012
commit d29e177f9d
65 files changed
+1920 -364

No files matched your search

+8 -1
View File
@@ -194,11 +194,18 @@ def title_case(item):
def zone_fields(zone, chapter, book):
"""What the zone template fills in. A zone chapter says what is particular about it (Adrian, 2026-11-10: no
boilerplate): `about`, the `locals` named as examples (they must spawn there), and a `guardian_note`."""
g = zone["guardian_data"]
ores = zone["ore_items"]
if not chapter.get("about"):
sys.exit(f"zone chapter {chapter['id']} has no 'about'")
for name in re.split(r", | and ", chapter.get("locals", "")):
if name and f"cobblemon:{re.sub(r'[^a-z0-9]', '', name.lower())}" not in zone["species"]:
sys.exit(f"{chapter['id']}: {name} is named as a local but doesn't spawn in {zone['name']}")
return {
"zone": zone["name"], "zone_id": zone["id"], "low": zone["levels"][0], "high": zone["levels"][1],
"hazard_text": chapter.get("hazard_text", ""),
"about": chapter["about"].strip(), "locals": chapter.get("locals", ""), "guardian_note": chapter.get("guardian_note", ""),
"guardian": g["pokemon"].split()[0].title() if g else "",
"guardian_level": re.search(r"level=(\d+)", g["pokemon"])[1] if g else "",
"drops": ", ".join(f"{d['count']} {title_case(d['item'])}" for d in g["drops"]) if g else "",