Files
claudeandClaude Opus 5.5 6361636da7 WIP: ore nodes, catching rules, bee nest fix, smaller Deep Dark
Done and checked: surface and cave-floor ore nodes (companion mod
ore/OreNodes.kt), bigger and rarer underground veins; template with nodes
passed the world check and the island quick check (node self-tests).

Built, not yet tested: catching rules (catch rate falls with the level
gap to the trainer's strongest Pokemon, failed catch enrages, strong wild
Pokemon hunt weaker trainers; catching/, Fight or Flight config), bee
nests emptied when their bee is turned away.

In progress: the Deep Dark no longer fills the deepslate layer (split by
temperature: cold = Deep Dark, hot = basalt deltas, warm = ordinary caves;
world/biomes.json), zone layout regenerated; cave zone types added to
world/zones.json but tools/zone_spawns.py doesn't use them yet; template
not rebuilt for the new caves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:44:17 +01:00

113 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Write the island world type from world/biomes.json.
Vanilla's overworld biome parameters come from Minecraft's own data generator
(run on the vanilla server jar that the NeoForge installer put in
build/server; no server is started). Every entry is sent to its target biome
from world/biomes.json ("remap"), or cut into pieces along one climate
parameter ("split": each piece of the parameter's range goes to its own
biome; the parts no piece covers are dropped, so the biome around them takes
over), the extra entries are appended ("add"), and the result becomes the
inline biome list of WorldGen Editor's delegate in
datapacks/core/data/cobblesurvival/worldgen/world_preset/island.json.
The world type has no Nether dimension.
Usage: tools/biome_layout.py (run tools/smoke_test.py once first so build/server exists)
"""
import json
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from mcserver import CACHE, PACK, ROOT, SERVER # noqa: E402
from packwiz_sync import java # noqa: E402
PRESET = ROOT / "datapacks/core/data/cobblesurvival/worldgen/world_preset/island.json"
def vanilla_parameters():
version = PACK["versions"]["minecraft"]
report = CACHE / f"datagen-{version}" / "reports/biome_parameters/minecraft/overworld.json"
if not report.exists():
jar = SERVER / "libraries/net/minecraft/server" / version / f"server-{version}.jar"
if not jar.exists():
sys.exit(f"{jar} is missing; run tools/smoke_test.py once so the NeoForge installer fetches it")
work = CACHE / f"datagen-{version}"
work.mkdir(parents=True, exist_ok=True)
subprocess.run([java(), "-DbundlerMainClass=net.minecraft.data.Main", "-jar", str(jar),
"--reports", "--output", str(work)], cwd=work, check=True, stdout=subprocess.DEVNULL)
return json.loads(report.read_text())["biomes"]
def split(entry, rule):
"""The pieces of a vanilla entry along rule["parameter"], each sent to its own biome."""
name = rule["parameter"]
value = entry["parameters"][name]
low, high = (value, value) if isinstance(value, (int, float)) else value
pieces = []
for piece in rule["pieces"]:
a, b = max(low, piece["range"][0]), min(high, piece["range"][1])
if a < b or (a == b and low == high):
pieces.append({"biome": piece["to"], "parameters": {**entry["parameters"], name: [a, b]}})
return pieces
def main():
spec = json.loads((ROOT / "world/biomes.json").read_text())
targets = set(spec["allowed"]) | set(spec["water"])
entries, unmapped = [], set()
splits = {s["biome"]: s for s in spec.get("split", [])}
for entry in vanilla_parameters():
if entry["biome"] in splits:
entries += split(entry, splits[entry["biome"]])
continue
target = spec["remap"].get(entry["biome"])
if target is None:
unmapped.add(entry["biome"])
continue
entries.append({"biome": target, "parameters": entry["parameters"]})
for extra in spec["add"]:
entries.append({"biome": extra["biome"], "parameters": extra["parameters"]})
bad_targets = {e["biome"] for e in entries} - targets
if unmapped or bad_targets:
sys.exit(f"vanilla biomes without a target: {sorted(unmapped)}; targets outside the list: {sorted(bad_targets)}")
preset = {
"dimensions": {
"minecraft:overworld": {
"type": "minecraft:overworld",
"generator": {
"type": "minecraft:noise",
"biome_source": {
"type": "worldgen_editor:island_biome_source",
"preset": "cobblesurvival_island",
"delegate": {"type": "minecraft:multi_noise", "biomes": "@BIOMES@"},
},
"settings": "worldgen_editor:island_overworld",
},
},
"minecraft:the_end": {
"type": "minecraft:the_end",
"generator": {"type": "minecraft:noise", "biome_source": {"type": "minecraft:the_end"},
"settings": "minecraft:end"},
},
}
}
# One biome entry per line keeps the 7,600-entry file diffable.
body = ",\n".join(" " + json.dumps(e, separators=(",", ":")) for e in entries)
text = json.dumps(preset, indent=2).replace('"@BIOMES@"', "[\n" + body + "\n ]")
json.loads(text)
PRESET.write_text(text + "\n")
counts = {}
for e in entries:
counts[e["biome"]] = counts.get(e["biome"], 0) + 1
print(f"wrote {PRESET.relative_to(ROOT)}: {len(entries)} entries")
for biome, n in sorted(counts.items(), key=lambda kv: -kv[1]):
print(f" {n:5} {biome}")
if __name__ == "__main__":
main()