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>
This commit is contained in:
1 parent
362d06aa4b
commit
6361636da7
29 files changed
+1733
-357
No files matched your search
+59
-40
@@ -1,25 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Write Regenerating Ore Veins' config from the zone plan, so every ore tier only has veins in its zone.
|
||||
"""Write Regenerating Ore Veins' config and the companion mod's ore node table from the zone plan.
|
||||
|
||||
world/zones.json → "ores" lists the tiers. This tool writes pack/config/regenerating_ore_veins/:
|
||||
world/zones.json → "ores" lists the tiers (Adrian, 2026-10-30: small ore nodes on the surface, larger veins
|
||||
underground that are harder to find, and the cave zones' ores as nodes on the cave floor). This tool writes
|
||||
pack/config/regenerating_ore_veins/:
|
||||
|
||||
* areas.json: each surface zone that has a tier, as boxes from the zone grid (build/zone-layout.json,
|
||||
24-block cells; rows of cells merged into rectangles), named <zone>_<n>, reaching from bedrock to
|
||||
the build limit. The mod's boxes are centre plus half-size, so a box covers its cells except their
|
||||
last row and column of blocks: no box reaches into a neighbouring zone.
|
||||
* veins.json: one vein per tier, or two when the tier has "stone" and "deepslate" blocks (an upper vein
|
||||
from y 0 up, "<tier>_deep" below). A surface tier's veins are limited to its zone's boxes and kept out
|
||||
of the cave biomes, which are zones of their own; a cave tier's veins to its cave biome. The mod checks
|
||||
the area for every block and the biome at a vein's centre. One vein only for the self-tests comes
|
||||
last ("selftest", a single coal block); its area lies far outside the world border, so it never
|
||||
generates, and the tests place it by command. Each tier with "showcase" also gets "<tier>_showcase", a full
|
||||
radius-2 vein in the same place-only area, which tools/make_world.py places by hand near the zone's sample point.
|
||||
* veins.json: a surface tier's underground veins ("veins"): one vein, or two when the tier has "stone" and
|
||||
"deepslate" blocks (an upper vein from y 0 up, "<tier>_deep" below), limited to its zone's boxes and kept
|
||||
out of the cave biomes, which are zones of their own. The mod checks the area for every block and the
|
||||
biome at a vein's centre. Then every tier's "<tier>_node": the ore node (the stone ore, or the tier's
|
||||
blocks), placed only by the companion mod, so its area lies far outside the world border, where the mod
|
||||
never generates it by itself. The same goes for "selftest" (a single coal block the self-tests place).
|
||||
* global.json: regenerators can't be broken by players (allow_breaking false), so nobody can remove a
|
||||
vein for good; explosions don't destroy veins.
|
||||
* vein_locator.json: the Vein Locator from a compass and a copper ingot (mid-game) instead of netherite.
|
||||
|
||||
It also writes world/vein-sources.json, the veins as item sources for the field guide (tools/fieldguide/build.py
|
||||
reads it by default): what each tier's blocks drop and in which zone.
|
||||
It also writes datapacks/core/data/cobblesurvival/ore_nodes/island.json, the node table the companion mod reads
|
||||
(ore/OreNodes.kt: which node, in which zone, how often per chunk, on the surface or on a cave floor), and
|
||||
world/vein-sources.json, the nodes and veins as item sources for the field guide (tools/fieldguide/build.py reads it
|
||||
by default): what each tier's blocks drop and in which zone.
|
||||
|
||||
Re-run after changing the zone plan or the grid (tools/zone_layout.py) and commit the result.
|
||||
|
||||
@@ -35,6 +38,7 @@ LAYOUT = ROOT / "build/zone-layout.json"
|
||||
BOTTOM, TOP = -64, 320
|
||||
SELFTEST_AREA = "selftest_nowhere"
|
||||
SOURCES = ROOT / "world/vein-sources.json"
|
||||
NODES = ROOT / "datapacks/core/data/cobblesurvival/ore_nodes/island.json"
|
||||
# What an ore block drops, where it isn't the block itself or "<name>_ore" → "<name>".
|
||||
DROPS = {"iron": "raw_iron", "copper": "raw_copper", "gold": "raw_gold", "lapis": "lapis_lazuli",
|
||||
"nether_quartz": "quartz", "amethyst_block": "amethyst_block", "budding_amethyst": "amethyst_shard"}
|
||||
@@ -83,7 +87,8 @@ def zone_areas(layout, zone_id):
|
||||
return areas
|
||||
|
||||
|
||||
def vein(vein_id, blocks, spec, defaults, y_range, where):
|
||||
def vein(vein_id, blocks, spec, shape, defaults, y_range, where):
|
||||
"""One entry of veins.json: spec gives the regrowth time, shape the size and spacing."""
|
||||
seconds = spec["regen_minutes"] * 60
|
||||
jitter = round(seconds * spec.get("jitter", defaults["jitter"]))
|
||||
return {
|
||||
@@ -93,13 +98,13 @@ def vein(vein_id, blocks, spec, defaults, y_range, where):
|
||||
"dimension_whitelist": ["minecraft:overworld"],
|
||||
**where,
|
||||
"shape": "sphere",
|
||||
"min_radius": spec["radius"][0],
|
||||
"max_radius": spec["radius"][1],
|
||||
"attempts": spec.get("attempts", defaults["attempts"]),
|
||||
"min_radius": shape["radius"][0],
|
||||
"max_radius": shape["radius"][1],
|
||||
"attempts": shape.get("attempts", defaults["attempts"]),
|
||||
"min_y": y_range[0],
|
||||
"max_y": y_range[1],
|
||||
"chunk_minimum_generation_separation": spec["spacing"],
|
||||
"fill_factor": spec.get("fill", defaults["fill"]),
|
||||
"chunk_minimum_generation_separation": shape.get("spacing", 64),
|
||||
"fill_factor": shape.get("fill", defaults["fill"]),
|
||||
"regeneration_interval_seconds": seconds,
|
||||
"regeneration_interval_jitter": {"range_min": -jitter, "range_max": jitter},
|
||||
}
|
||||
@@ -122,34 +127,41 @@ def main():
|
||||
caves = {c["id"]: c["biome"] for c in plan["caves"]}
|
||||
surface = {z["id"] for z in plan["zones"]}
|
||||
|
||||
areas, area_names, veins = [], {}, []
|
||||
areas, area_names, veins, nodes = [], {}, [], []
|
||||
place_only = {"area_whitelist": [SELFTEST_AREA]}
|
||||
for tier, spec in plan["ores"].items():
|
||||
zone = spec["zone"]
|
||||
if zone in caves:
|
||||
where = {"biome_whitelist": [caves[zone]]}
|
||||
elif zone in surface:
|
||||
if zone not in area_names:
|
||||
boxes = zone_areas(layout, zone)
|
||||
if not boxes:
|
||||
sys.exit(f"zone {zone} (ore tier {tier}) has no cells in the grid")
|
||||
areas += boxes
|
||||
area_names[zone] = [a["name"] for a in boxes]
|
||||
where = {"area_whitelist": area_names[zone], "biome_blacklist": sorted(caves.values())}
|
||||
else:
|
||||
if zone not in caves and zone not in surface:
|
||||
sys.exit(f"ore tier {tier}: unknown zone {zone}")
|
||||
low, high = spec["y"]
|
||||
if spec.get("showcase"):
|
||||
blocks = [(spec["stone"], 1)] if spec.get("stone") else [tuple(b) for b in spec["blocks"]]
|
||||
showcase = vein(f"{tier}_showcase", blocks, spec, defaults, (0, 0), {"area_whitelist": [SELFTEST_AREA]})
|
||||
veins.append({**showcase, "min_radius": 2, "max_radius": 2, "fill_factor": 0.8})
|
||||
# A node is the stone ore; deepslate where there is no stone ore or the node sits below y 0 (deep caves).
|
||||
deep = not spec.get("stone") or (zone in caves and spec["nodes"]["y"][1] < 0)
|
||||
node_blocks = [tuple(b) for b in spec["blocks"]] if "blocks" in spec else [(spec["deepslate" if deep else "stone"], 1)]
|
||||
node_shape = {**spec["nodes"], "fill": 1.0 if spec["nodes"]["radius"][1] == 0 else defaults["node_fill"]}
|
||||
veins.append(vein(f"{tier}_node", node_blocks, spec, node_shape, defaults, (0, 0), place_only))
|
||||
node = {"vein": f"{tier}_node", "zone": zone, "where": "cave" if zone in caves else "surface",
|
||||
"chance": spec["nodes"]["chance"], "radius": spec["nodes"]["radius"][1]}
|
||||
if zone in caves:
|
||||
node.update({"biome": caves[zone], "tries": spec["nodes"]["tries"], "y": spec["nodes"]["y"]})
|
||||
nodes.append(node)
|
||||
if "veins" not in spec:
|
||||
continue
|
||||
if zone not in area_names:
|
||||
boxes = zone_areas(layout, zone)
|
||||
if not boxes:
|
||||
sys.exit(f"zone {zone} (ore tier {tier}) has no cells in the grid")
|
||||
areas += boxes
|
||||
area_names[zone] = [a["name"] for a in boxes]
|
||||
where = {"area_whitelist": area_names[zone], "biome_blacklist": sorted(caves.values())}
|
||||
shape = spec["veins"]
|
||||
low, high = shape["y"]
|
||||
if "blocks" in spec:
|
||||
veins.append(vein(tier, spec["blocks"], spec, defaults, (low, high), where))
|
||||
veins.append(vein(tier, spec["blocks"], spec, shape, defaults, (low, high), where))
|
||||
continue
|
||||
if spec.get("stone") and high >= 0:
|
||||
veins.append(vein(tier, [(spec["stone"], 1)], spec, defaults, (max(low, 0), high), where))
|
||||
veins.append(vein(tier, [(spec["stone"], 1)], spec, shape, defaults, (max(low, 0), high), where))
|
||||
if spec.get("deepslate") and low < 0:
|
||||
deep_id = f"{tier}_deep" if spec.get("stone") and high >= 0 else tier
|
||||
veins.append(vein(deep_id, [(spec["deepslate"], 1)], spec, defaults, (low, min(high, -1)), where))
|
||||
veins.append(vein(deep_id, [(spec["deepslate"], 1)], spec, shape, defaults, (low, min(high, -1)), where))
|
||||
|
||||
areas.append({"dimension": "minecraft:overworld", "name": SELFTEST_AREA, "type": "box",
|
||||
"x": 29_000_000, "y": 0, "z": 29_000_000, "dimx": 0, "dimy": 0, "dimz": 0})
|
||||
@@ -177,12 +189,18 @@ def main():
|
||||
"durability": 2,
|
||||
"stack_size": 64,
|
||||
}, indent=2) + "\n")
|
||||
NODES.parent.mkdir(parents=True, exist_ok=True)
|
||||
NODES.write_text(json.dumps({
|
||||
"_comment": "Generated by tools/zone_ores.py from world/zones.json; read by the companion mod (ore/OreNodes.kt).",
|
||||
"nodes": nodes}, indent=1) + "\n")
|
||||
names = {z["id"]: z["name"] for z in plan["zones"] + plan["caves"]}
|
||||
sources = []
|
||||
for tier, spec in plan["ores"].items():
|
||||
blocks = [b for b, _ in spec.get("blocks", [])] + [spec[k] for k in ("stone", "deepslate") if spec.get(k)]
|
||||
where = (f"ore nodes on the cave floor in {names[spec['zone']]}" if spec["zone"] in caves
|
||||
else f"ore nodes on the ground and veins underground in {names[spec['zone']]}")
|
||||
for item in dict.fromkeys(drop_of(b) for b in blocks):
|
||||
sources.append({"item": item, "how": f"Regenerating {tier.replace('_', ' ')} vein in {names[spec['zone']]}",
|
||||
sources.append({"item": item, "how": f"Regenerating {tier.replace('_', ' ')} {where}",
|
||||
"kind": "ore vein", "needs": []})
|
||||
SOURCES.write_text(json.dumps({
|
||||
"_comment": "Generated by tools/zone_ores.py from world/zones.json; extra item sources for the field guide.",
|
||||
@@ -190,10 +208,11 @@ def main():
|
||||
per_zone = {}
|
||||
for tier, spec in plan["ores"].items():
|
||||
per_zone.setdefault(spec["zone"], []).append(tier)
|
||||
print(f"wrote {len(veins)} veins ({len(plan['ores'])} tiers) and {len(areas)} areas to {OUT.relative_to(ROOT)}")
|
||||
print(f"wrote {len(veins)} veins ({len(plan['ores'])} tiers) and {len(areas)} areas to {OUT.relative_to(ROOT)}, "
|
||||
f"{len(nodes)} node kinds to {NODES.relative_to(ROOT)}")
|
||||
for zone, tiers in per_zone.items():
|
||||
boxes = len(area_names.get(zone, []))
|
||||
print(f" {zone}: {', '.join(tiers)}" + (f" ({boxes} boxes)" if boxes else " (cave biome)"))
|
||||
print(f" {zone}: {', '.join(tiers)}" + (f" ({boxes} boxes)" if boxes else " (cave nodes)"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user