#!/usr/bin/env python3 """Every item in the pack that has a use can be had on the island (Adrian, 2026-11-10). Reads the field guide (build/field-guide/fieldguide.json; run tools/fieldguide/build.py first), which works out what the island can provide from the pack's data: worldgen, loot, recipes, Pokémon drops, and what Minecraft and the pack's own rules give in code (tools/fieldguide/manual_sources.json). Every item still without a source must be listed in NO_USE below, with the reason it needs none; anything else fails. Usage: tools/item_audit.py """ import fnmatch import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent GUIDE = ROOT / "build/field-guide/fieldguide.json" # Items that may stay without a source, and why (fnmatch patterns). NO_USE = { "cobblemon_hms:hm_*": "left over from Cobblemon HMs before 4.0: the items do nothing now (the HM Case has a source)", "lootr:*": "Lootr's own containers: structures place them, nobody uses them as items", "regenerating_ore_veins:regenerator_block": "the block a mined vein turns into; it can't be broken", "cold_sweat:chameleon_spawn_egg": "a creative-mode spawn egg", "minecraft:infested_*": "silverfish blocks; the island has no silverfish", "minecraft:crimson_*": "Nether wood for decoration; the island has no Nether", "cobblemon:*tumblestone_cluster": "tumblestone itself comes from the ruins; clusters are decoration", "cobblemon:*_budding_*tumblestone": "tumblestone itself comes from the ruins; buds are decoration", "cobblemon:damaged_monitor": "a decoration block in Cobblemon's ruins", "cobblemon:gimmighoul_chest": "the chest Gimmighoul hides in", "cobblemon:lectern": "Cobblemon's decorative lectern, with no recipe in Cobblemon", "cobblemon:bugwort": "a decoration plant with no recipe, seasoning or other use in Cobblemon", "cobblemon:poke_puff": "Cobblemon 1.8.4 has no way to make Poké Puffs yet", "cobblemon:shalour_sable": "Cobblemon 1.8.4 has no way to make it yet", "cobblemon:scroll_of_*": "they evolve Kubfu, which Cobblemon 1.8.4 doesn't have", "cobblemon:saccharine_log_slathered": "only gives planks and charcoal, which other logs give too", "cobblemon:nether_fire_stone_ore": "an ore the island's veins don't use; Fire Stones come from Sunscorch's veins", "cobblemon:terracotta_sun_stone_ore": "an ore the island's veins don't use; Sun Stones come from the Highlands' veins", "cobblemon:dusk_stone_ore": "an ore the island's veins don't use; Dusk Stones come from the Deep Dark's veins", "cold_sweat:slush_bucket": "slush is a cold fuel for the Hearth and Icebox; ice and snow do the same", } def main(): if not GUIDE.exists(): sys.exit(f"{GUIDE} is missing: run tools/fieldguide/build.py first") guide = json.loads(GUIDE.read_text()) entries = {**guide["vanilla"], **guide["items"]} missing = sorted(k for k, v in entries.items() if v.get("status") != "available" and not v.get("technical")) unexplained = [k for k in missing if not any(fnmatch.fnmatch(k, pattern) for pattern in NO_USE)] stale = [p for p in NO_USE if not any(fnmatch.fnmatch(k, p) for k in missing)] print(f"{len(entries)} items, {len(missing)} without a source on the island, {len(missing) - len(unexplained)} of them with no use") for pattern in stale: print(f" note: nothing matches NO_USE {pattern!r} any more; remove it") if unexplained: for k in unexplained: uses = ", ".join(u.get("text", "") for u in entries[k].get("uses", [])[:3]) or "no recipe uses" print(f" - {k}: {entries[k].get('reason', '')} (uses: {uses})") sys.exit("ITEM AUDIT FAILED: give these a source, or list them in NO_USE with the reason they need none") print("ITEM AUDIT PASSED") if __name__ == "__main__": main()