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:
1 parent
6ace26e012
commit
d29e177f9d
65 files changed
+1920
-364
No files matched your search
@@ -14,6 +14,8 @@
|
||||
the check, and so (except with --sample) do an ore tier without nodes, a
|
||||
surface tier without underground veins, and a chunk that never got its
|
||||
veins and nodes.
|
||||
* Start island: Button Isle has enough apricorns and coal ore for Poké Balls
|
||||
(four apricorns and coal or charcoal make one; Adrian, 2026-11-10).
|
||||
* Biome set: every biome stored in any chunk section (surface and caves)
|
||||
must be listed in world/biomes.json ("allowed" or "water").
|
||||
* End access: at least one stronghold must start inside the world border.
|
||||
@@ -42,6 +44,7 @@ import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
@@ -331,6 +334,38 @@ def island_mask(data, cell=16, min_share=0.01):
|
||||
return out
|
||||
|
||||
|
||||
# Adrian, 2026-11-10: the start island alone makes Poké Balls (four apricorns and coal or charcoal, the pack's
|
||||
# poke_ball_from_coal recipe), with some exploring. Coal grows back in its veins; apricorns grow back on their trees.
|
||||
START_MINIMUM = {"apricorns": 100, "coal ore": 500}
|
||||
|
||||
|
||||
def check_start_isle(world):
|
||||
"""Apricorns and coal ore on Button Isle (the zone within world/zones.json's radius of 0,0), from its stored chunks."""
|
||||
plan = json.loads((ROOT / "world/zones.json").read_text())
|
||||
radius = next(z for z in plan["zones"] if z["id"] == "button_isle")["rule"]["within"]
|
||||
found = defaultdict(int)
|
||||
span = range(-((radius + 511) // 512), (radius + 511) // 512)
|
||||
for rx in span:
|
||||
for rz in span:
|
||||
path = world / "region" / f"r.{rx}.{rz}.mca"
|
||||
if not path.exists():
|
||||
continue
|
||||
for cx, cz, chunk in anvil.region_chunks(path):
|
||||
x0, z0 = cx * 16, cz * 16
|
||||
if math.hypot(x0 + 8, z0 + 8) > radius + 12:
|
||||
continue
|
||||
inside = np.hypot(np.arange(16)[None, :] + x0 + .5, np.arange(16)[:, None] + z0 + .5) <= radius
|
||||
for section in chunk.get("sections", []):
|
||||
palette, indices = anvil.section_blocks(section)
|
||||
wanted = [i for i, name in enumerate(palette) if name.endswith("_apricorn") or name.endswith("coal_ore")]
|
||||
cube = indices.reshape(16, 16, 16)
|
||||
for i in wanted:
|
||||
kind = "apricorns" if palette[i].endswith("_apricorn") else "coal ore"
|
||||
found[kind] += int(((cube == i) & inside[None, :, :]).sum())
|
||||
problems = [f"Button Isle has {found[k]} {k}, fewer than {n}" for k, n in START_MINIMUM.items() if found[k] < n]
|
||||
return dict(found), problems
|
||||
|
||||
|
||||
def check_border(data, level, margin):
|
||||
zs, xs = np.nonzero(island_mask(data))
|
||||
ox, oz = data["origin"]
|
||||
@@ -479,10 +514,12 @@ def main():
|
||||
listed = set(biomes["allowed"]) | set(biomes["water"])
|
||||
unlisted = {b: n for b, n in data["sections_per_biome"].items() if b not in listed}
|
||||
vein_counts, vein_problems = check_veins(args.world, data["ore_at"], data["full_chunks"], args.sample)
|
||||
start_isle, start_problems = check_start_isle(args.world)
|
||||
report = {
|
||||
"chunks_scanned": data["chunks"],
|
||||
"ore_blocks": {k: v for k, v in data["ores"].items() if ORE.search(k) or k in VEIN_BLOCKS},
|
||||
"vein_blocks_per_tier_and_zone": vein_counts,
|
||||
"start_isle_ball_materials": start_isle,
|
||||
"mob_source_blocks": {k: v for k, v in data["ores"].items() if MOB_BLOCK.search(k) or "shrieker" in k},
|
||||
"unlisted_biomes": unlisted,
|
||||
"gamerules_not_as_recipe": wrong_rules,
|
||||
@@ -502,6 +539,7 @@ def main():
|
||||
|
||||
failures = ([f"{n} {name}" for name, n in data["ores"].items() if MOB_BLOCK.search(name) or "shrieker" in name]
|
||||
+ vein_problems
|
||||
+ ([] if args.sample else start_problems)
|
||||
+ border["problems"]
|
||||
+ [f"unlisted biome {b} in {n} chunk sections" for b, n in unlisted.items()]
|
||||
+ [f"gamerule {r} is {v}, recipe says {recipe['gamerules'][r]}" for r, v in wrong_rules.items()]
|
||||
|
||||
Reference in new issue
Block a user