Phase 9 begins: bigger, sparser veins, a mine-through respawn block, left-out lines

Phase 8 approved (2026-11-09). Adrian's Phase 9 answers: left-out Pokemon rare
in fitting zones; legendaries from crafted summons; no public release.

- Ore veins more sporadic but bigger: nodes about 40 % rarer, veins about 30 %
  further apart, each one block larger in radius. 1.5-3x the ore overall; a
  new island is needed to see it. World check on the regenerated island passes.
- The respawn block (Regenerating Ore Veins' regenerator): the aim passes
  through it unless sneaking (mixin, the pack's first), so ore behind it can be
  mined; much fainter glass through KubeJS assets. Self-test
  regenerator_lets_the_aim_through casts a real ray.
- 31 lines (67 species) with no matching island biome, or no spawn data
  (Rotom), get rare homes in fitting zones (world/zones.json -> homeless).
  Existing spawns and guardians don't change. JEI data and quests regenerated.
- surface_nodes_lie_in_the_ground checks the node's centre column: a wider
  node's edge can lie under a tree or a slope.
- Roadmap: every implemented Pokemon obtainable; Iris and Complementary
  Reimagined as the default shader.

Self-tests and the island check pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-09 12:45:44 +01:00
1 parent 0be2c2e5c6
commit e206664b3d
45 files changed
+2723 -242

No files matched your search

+50 -1
View File
@@ -37,6 +37,8 @@ datapacks/core/data/cobblesurvival/spawn_pool_world/<zone>.json:
its stages fitted to the band as in 2. The entries go into "bucket" (Cobblemon rolls the bucket first, and its
End entries are all uncommon or rare), the heaviest of each line weighing "line_weight", Cobblemon's rare ones
"rare_factor" of that.
8. world/zones.json → "homeless" gives the lines that have no matching biome on the island a zone: rare spawns
anywhere in it, at its levels (Adrian, 2026-11-09: every implemented Pokémon must be obtainable).
Also writes docs/zone-species.md, the list per zone for review.
@@ -370,6 +372,53 @@ def main():
report[did].append((species.get(root, {}).get("name", root), names))
dimension_roots.add(root)
# 5. Lines with no home on the island (Adrian, 2026-11-09: every implemented Pokémon must be obtainable). They only
# spawn in biomes the island lacks, or have no spawn data at all (Rotom), so the field guide can't place them:
# world/zones.json → "homeless" names a zone for each, and they spawn there, anywhere in the zone, in the
# "bucket" at the zone's levels. Everything tied to the missing biomes is left out of the condition.
homeless = plan.get("homeless", {})
raw_lines = collections.defaultdict(list)
for entry in entries.values():
if "pokemon" in entry:
member = f"cobblemon:{entry['pokemon'].split()[0].lower()}"
a, _, b = str(entry["level"]).partition("-")
raw_lines[line_root(member, species)].append((entry, member, (int(a), int(b or a))))
homeless_roots = set()
for zid, roots in homeless.get("zones", {}).items():
lo, hi = zones[zid]["levels"]
kinds = {"submerged", "surface"} if "water" in zones[zid].get("rule", {}) else {"grounded"}
for name in roots:
root = f"cobblemon:{name}"
items = [i for i in raw_lines[root] if i[0].get("spawnablePositionType") in kinds]
if not items:
# No usable spawn data at all: one plain entry across the band.
items = [({"type": "pokemon", "pokemon": name, "presets": ["natural"], "spawnablePositionType": "grounded"}, root, (lo, hi))]
fitting = [(e, m, max(lv[0], lo), hi) for e, m, lv in items if lv[0] <= hi and lv[1] >= lo]
if not fitting:
_, m, lv = min(items, key=lambda u: min(abs(u[2][0] - hi), abs(u[2][1] - lo)))
fitting = [(e, um, lo, hi) for e, um, ulv in items if um == m and ulv == lv]
seen = set()
for entry, member, a, b in fitting:
key = (member, entry.get("spawnablePositionType"))
if key in seen:
continue
seen.add(key)
condition = {"zone": zid}
if "fluid" in entry.get("condition", {}):
condition["fluid"] = entry["condition"]["fluid"]
presets = entry.get("presets", [])
if zid in cave_zones:
condition["biomes"] = [cave_biome[zid]]
presets = [x for x in presets if x != "natural"]
pools[zid].append({
"id": f"{zid}-{member.split(':')[1]}-{len(pools[zid])}", "type": "pokemon", "pokemon": entry["pokemon"],
"presets": presets, "spawnablePositionType": entry["spawnablePositionType"], "bucket": homeless["bucket"],
"level": f"{a}-{b}", "weight": homeless["weight"], "condition": condition,
})
names = sorted({species[m]["name"] for _, m, _, _ in fitting if m in species})
report[zid].append((species.get(root, {}).get("name", root), names))
homeless_roots.add(root)
low_flyers = [(zid, s["pokemon"]) for zid, spawns in pools.items() for s in spawns
if zones[zid]["levels"][0] < HIGH_ZONE_LEVEL and f"cobblemon:{s['pokemon'].split()[0].lower()}" in strong_flyers]
if low_flyers:
@@ -430,7 +479,7 @@ def main():
f"| {zone['levels'][1] + GUARDIAN_LEVELS_ABOVE_BAND} | {drop_text} |")
REPORT.write_text("\n".join(lines_out) + "\n")
unplaced = [r for r in lines if r not in home and r not in dimension_roots]
unplaced = [r for r in lines if r not in home and r not in dimension_roots and r not in homeless_roots]
grounded = sorted(species.get(r, {}).get("name", r) for r in unplaced if r in flying_lines)
if grounded:
print(f"strong flyers kept out of the wild (no high zone fits their habitat): {', '.join(grounded)}")