Files
cobblemon-survival-pack/tools/cobblebase_loot.py
T
claudeandClaude Opus 5.5 11a583db01 Phase 5: Cobblebase jobs take back gated work, dig only zone ores
From Adrian's Mining test (2026-10-31):

- A gated job assigned to a pastured Pokemon that doesn't pass its gate
  is taken back at once (BaseManager assignment cleared, Cobblebase's
  assignment sync sent, owner told what the Pokemon needs), so it no
  longer sticks or shows under Buffs -> Active.
- Mining, Ore Finder and Evolution-item Finder only give the ores and
  evolution stones of the pasture's zone: new loot condition
  cobblesurvival:in_zone, the pack's versions of the 12 Cobblebase
  tables (tools/cobblebase_loot.py), Cobblebase's built-in copies
  removed at server start (ore/BaseLoot.kt). Mining adds quartz and the
  evolution stones, drops the type gems.
- Mining's loot reaching the chest needs an Item Gatherer in the same
  pasture (Cobblebase's design); documented, not changed.
- Self-tests gated_jobs_are_taken_back, base_jobs_dig_their_zones_ores;
  docs, balance table, licenses, Phase 5 checklist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:56:00 +01:00

129 lines
5.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""Write the pack's versions of Cobblebase's mining and finder loot tables: a pasture only yields its zone's ores.
Adrian (2026-10-31): Cobblebase's Mining skill should only dig up the ores of the zone its pasture is in. Its tables,
and those of the Ore Finder and the Evolution-item Finder, which hand out the same ores and evolution stones, ignore
the island's zones. This copies them from the Cobblebase jar in build/server/mods and puts the loot condition
`cobblesurvival:in_zone` on every ore and evolution stone, naming the zone of its tier in world/zones.json. The mining
tables also lose the type gems (crafting shortcuts to gate materials such as the Heat Rock; ruins archaeology still
has them) and gain the zone ores Cobblebase leaves out (quartz, the evolution stones). The companion mod takes
Cobblebase's built-in copies of these tables out at server start (ore/BaseLoot.kt), so these files are what is rolled.
Output: datapacks/core/data/cobblebase/loot_table/<table>.json (derived from Cobblebase, MPL-2.0).
Usage: tools/cobblebase_loot.py
"""
import json
import sys
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
OUT = ROOT / "datapacks/core/data/cobblebase/loot_table"
CONDITION = "cobblesurvival:in_zone"
# What an ore tier of world/zones.json drops; evolution-stone tiers drop their stone.
TIER_ITEMS = {
"coal": ["minecraft:coal"],
"copper": ["minecraft:raw_copper"],
"iron": ["minecraft:raw_iron"],
"gold": ["minecraft:raw_gold"],
"redstone": ["minecraft:redstone"],
"lapis": ["minecraft:lapis_lazuli"],
"emerald": ["minecraft:emerald"],
"amethyst": ["minecraft:amethyst_shard"],
"diamond": ["minecraft:diamond"],
"quartz": ["minecraft:quartz"],
"netherite": ["minecraft:ancient_debris", "minecraft:netherite_scrap"],
}
# Table families: which items get the zone condition, which are dropped, and where missing zone items are added
# (rarity suffix and weight, like Cobblebase's neighbouring entries).
FAMILIES = {
"mining_": {"zoned": ("ore", "stone"), "drop_gems": True, "add": {"ore": ("uncommon", 8), "stone": ("rare", 3)}},
"finder_ore_": {"zoned": ("ore",), "drop_gems": False, "add": {"ore": ("uncommon", 8)}},
"finder_evo_": {"zoned": ("stone",), "drop_gems": False, "add": {"stone": ("common", 10)}},
}
def cobblebase_jar():
jars = sorted((ROOT / "build/server/mods").glob("cobblebase-*.jar"))
if not jars:
sys.exit("no Cobblebase jar in build/server/mods: run tools/build.py and tools/selftest.py (or the quick check) first")
return jars[-1]
def zone_items():
"""item id -> (kind, zone) for every ore tier of world/zones.json."""
ores = json.loads((ROOT / "world/zones.json").read_text())["ores"]
items = {}
for tier, ore in ores.items():
if tier.endswith("_stone"):
drops, kind = [f"cobblemon:{tier}"], "stone"
elif tier in TIER_ITEMS:
drops, kind = TIER_ITEMS[tier], "ore"
else:
sys.exit(f"ore tier {tier} has no drop in TIER_ITEMS")
for item in drops:
items[item] = (kind, ore["zone"])
return items
def rewrite(table, family, items):
spec = FAMILIES[family]
seen = set()
for pool in table["pools"]:
kept = []
for entry in pool["entries"]:
name = entry.get("name", "")
if spec["drop_gems"] and name.endswith("_gem"):
continue
kind, zone = items.get(name, (None, None))
if kind in spec["zoned"]:
entry = dict(entry, conditions=[{"condition": CONDITION, "zones": [zone]}])
seen.add(name)
kept.append(entry)
pool["entries"] = kept
return seen
def main():
items = zone_items()
jar = zipfile.ZipFile(cobblebase_jar())
prefix = "data/cobblebase/loot_table/"
names = sorted(n[len(prefix):-5] for n in jar.namelist() if n.startswith(prefix) and n.endswith(".json"))
OUT.mkdir(parents=True, exist_ok=True)
written = []
for family, spec in FAMILIES.items():
tables = {name: json.loads(jar.read(f"{prefix}{name}.json")) for name in names if name.startswith(family)}
if not tables:
sys.exit(f"Cobblebase has no {family}* loot tables any more")
seen = set()
for table in tables.values():
seen |= rewrite(table, family, items)
for item, (kind, zone) in sorted(items.items()):
if kind not in spec["zoned"] or item in seen:
continue
rarity, weight = spec["add"][kind]
target = tables.get(f"{family}{rarity}")
if target is None:
sys.exit(f"no table {family}{rarity} to add {item} to")
target["pools"][0]["entries"].append({
"type": "minecraft:item", "name": item, "weight": weight,
"conditions": [{"condition": CONDITION, "zones": [zone]}],
})
for name, table in tables.items():
(OUT / f"{name}.json").write_text(json.dumps(table, indent=2, ensure_ascii=False) + "\n")
written.append(name)
print(f"wrote {len(written)} tables to {OUT.relative_to(ROOT)}: {', '.join(written)}")
by_zone = {}
for item, (kind, zone) in items.items():
by_zone.setdefault(zone, []).append(item.split(":")[1])
for zone, zoned in sorted(by_zone.items()):
print(f" {zone}: {', '.join(sorted(zoned))}")
if __name__ == "__main__":
main()