Update worldgen exporter features
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ __pycache__/
|
|||||||
|
|
||||||
# Project-specific output
|
# Project-specific output
|
||||||
worldgen-c/test_out/
|
worldgen-c/test_out/
|
||||||
|
worldgen-c/bin/worldgen-scan
|
||||||
mc.zip
|
mc.zip
|
||||||
lakeland-update-*/
|
lakeland-update-*/
|
||||||
untitled\ folder/
|
untitled\ folder/
|
||||||
|
|||||||
101
README.md
101
README.md
@@ -0,0 +1,101 @@
|
|||||||
|
MC Worldgen
|
||||||
|
===========
|
||||||
|
|
||||||
|
Procedural Minecraft Java region exporter. The C exporter is the maintained path:
|
||||||
|
it builds without Python dependencies and writes `.mca` region files that can be
|
||||||
|
copied into an existing world's `region/` folder.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- GCC or Clang
|
||||||
|
- `make`
|
||||||
|
- zlib development headers (`zlib1g-dev` on Debian/Ubuntu)
|
||||||
|
- pthreads, normally included with libc toolchains
|
||||||
|
|
||||||
|
Build
|
||||||
|
-----
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd worldgen-c
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Quick smoke test:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/worldgen --radius 0 --out /tmp/mcworldgen-smoke
|
||||||
|
```
|
||||||
|
|
||||||
|
That should create `/tmp/mcworldgen-smoke/r.0.0.mca`.
|
||||||
|
|
||||||
|
Diagnostic feature scan:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make scan
|
||||||
|
./bin/worldgen-scan --radius 2 --center-x -3200 --center-z 3200
|
||||||
|
```
|
||||||
|
|
||||||
|
The scanner prints counts for cabin blocks, gravel/path columns, redwood biome
|
||||||
|
columns, and titan-height tree trunks.
|
||||||
|
|
||||||
|
Generate Regions
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Small area around chunk 0,0:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/worldgen --radius 4 --trails --out output
|
||||||
|
```
|
||||||
|
|
||||||
|
Specific chunk rectangle:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/worldgen --min-x -8 --max-x 8 --min-z -8 --max-z 8 --trails --out output
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful options:
|
||||||
|
|
||||||
|
- `--radius N`: generate chunks from `center - N` through `center + N`.
|
||||||
|
- `--center-x X --center-z Z`: center chunk for radius mode.
|
||||||
|
- `--min-x --max-x --min-z --max-z`: explicit chunk bounds.
|
||||||
|
- `--threads N`: worker thread count.
|
||||||
|
- `--seed S`: deterministic world seed.
|
||||||
|
- `--sea-level L`: sea level, default `70`.
|
||||||
|
- `--snow-line H`: snow threshold, default `sea-level + 38`.
|
||||||
|
- `--trails`: enable trail and cabin prepass.
|
||||||
|
- `--format mca|bin`: write Minecraft region files or raw debug chunks.
|
||||||
|
- `--out DIR`: output directory.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Redwoods are enabled automatically. The biome classifier marks high-rainfall,
|
||||||
|
higher-relief areas as redwood forest, then places titan trees, dirt/mulch
|
||||||
|
floor cover, tall grass, and occasional fallen logs.
|
||||||
|
- Cabins are enabled automatically. With `--trails`, cabins only try to spawn on
|
||||||
|
chunks crossed by the trail network so their spur paths can connect to roads.
|
||||||
|
Without `--trails`, cabins can still spawn, but they are rarer and unconnected.
|
||||||
|
- Trails are optional because they need a prepass over the target area. Use
|
||||||
|
`--trails` when you want roads and better-connected cabins.
|
||||||
|
|
||||||
|
Minecraft Version
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The exporter writes Java Edition 1.17.1 chunk data (`DataVersion` 2730). That
|
||||||
|
version is chosen because the generator includes copper ore. Open the generated
|
||||||
|
regions in Minecraft Java 1.17.1 or a newer version that can upgrade old worlds.
|
||||||
|
|
||||||
|
The exporter writes region files only. To use them in game, create or choose a
|
||||||
|
Java world, close Minecraft, back up the save, and replace or add files under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<minecraft save>/region/
|
||||||
|
```
|
||||||
|
|
||||||
|
Python Exporter
|
||||||
|
---------------
|
||||||
|
|
||||||
|
`export_mca.py` is the older prototype exporter. It now shares the same 1.17.1
|
||||||
|
data version and maps generated coal correctly, but the C exporter is faster and
|
||||||
|
has the current terrain features.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ TAG_COMPOUND = 10
|
|||||||
TAG_INT_ARRAY = 11
|
TAG_INT_ARRAY = 11
|
||||||
TAG_LONG_ARRAY = 12
|
TAG_LONG_ARRAY = 12
|
||||||
|
|
||||||
DATA_VERSION = 2586 # Minecraft 1.15.2
|
DATA_VERSION = 2730 # Minecraft Java Edition 1.17.1; matches copper-era block IDs.
|
||||||
DEFAULT_RADIUS = 256 # ~512 block diameter (~0.5 km)
|
DEFAULT_RADIUS = 256 # ~512 block diameter (~0.5 km)
|
||||||
CHUNK_HEIGHT = 256
|
CHUNK_HEIGHT = 256
|
||||||
SECTION_COUNT = CHUNK_HEIGHT // 16
|
SECTION_COUNT = CHUNK_HEIGHT // 16
|
||||||
@@ -54,6 +54,7 @@ def build_state_mapping():
|
|||||||
set_state("oak_leaves", "minecraft:oak_leaves", {"distance": "1", "persistent": "false"})
|
set_state("oak_leaves", "minecraft:oak_leaves", {"distance": "1", "persistent": "false"})
|
||||||
set_state("birch_log", "minecraft:birch_log", {"axis": "y"})
|
set_state("birch_log", "minecraft:birch_log", {"axis": "y"})
|
||||||
set_state("birch_leaves", "minecraft:birch_leaves", {"distance": "1", "persistent": "false"})
|
set_state("birch_leaves", "minecraft:birch_leaves", {"distance": "1", "persistent": "false"})
|
||||||
|
set_state("coal", "minecraft:coal_ore")
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,22 @@ OBJ := $(SRC:.c=.o)
|
|||||||
|
|
||||||
BIN_DIR := bin
|
BIN_DIR := bin
|
||||||
TARGET := $(BIN_DIR)/worldgen
|
TARGET := $(BIN_DIR)/worldgen
|
||||||
|
SCAN_TARGET := $(BIN_DIR)/worldgen-scan
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
$(TARGET): $(OBJ) | $(BIN_DIR)
|
$(TARGET): $(OBJ) | $(BIN_DIR)
|
||||||
$(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(SCAN_TARGET): tools/worldgen_scan.o src/worldgen.o src/noise.o | $(BIN_DIR)
|
||||||
|
$(CC) $(CFLAGS) tools/worldgen_scan.o src/worldgen.o src/noise.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
$(BIN_DIR):
|
$(BIN_DIR):
|
||||||
mkdir -p $(BIN_DIR)
|
mkdir -p $(BIN_DIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(TARGET)
|
rm -f $(OBJ) tools/worldgen_scan.o $(TARGET) $(SCAN_TARGET)
|
||||||
|
|
||||||
.PHONY: all clean
|
scan: $(SCAN_TARGET)
|
||||||
|
|
||||||
|
.PHONY: all clean scan
|
||||||
|
|||||||
Binary file not shown.
@@ -80,5 +80,7 @@ void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_li
|
|||||||
void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out);
|
void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out);
|
||||||
void worldgen_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z);
|
void worldgen_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z);
|
||||||
void worldgen_free_trails(worldgen_ctx *ctx);
|
void worldgen_free_trails(worldgen_ctx *ctx);
|
||||||
|
int worldgen_debug_biome(worldgen_ctx *ctx, int x, int z);
|
||||||
|
double worldgen_debug_redwood_mask(worldgen_ctx *ctx, int x, int z);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ static const kv_pair PROPS_LADDER_S[] = {{"facing", "south"}, {"waterlogged", "f
|
|||||||
static const kv_pair PROPS_LADDER_W[] = {{"facing", "west"}, {"waterlogged", "false"}};
|
static const kv_pair PROPS_LADDER_W[] = {{"facing", "west"}, {"waterlogged", "false"}};
|
||||||
static const kv_pair PROPS_LADDER_E[] = {{"facing", "east"}, {"waterlogged", "false"}};
|
static const kv_pair PROPS_LADDER_E[] = {{"facing", "east"}, {"waterlogged", "false"}};
|
||||||
static const size_t BLOCK_STATE_CAP = 256;
|
static const size_t BLOCK_STATE_CAP = 256;
|
||||||
|
static const int32_t DATA_VERSION = 2730; /* Java Edition 1.17.1; includes copper ore. */
|
||||||
static const block_state BLOCK_STATE_TABLE[] = {
|
static const block_state BLOCK_STATE_TABLE[] = {
|
||||||
[BLOCK_BEDROCK] = {"minecraft:bedrock", NULL, 0},
|
[BLOCK_BEDROCK] = {"minecraft:bedrock", NULL, 0},
|
||||||
[BLOCK_STONE] = {"minecraft:stone", NULL, 0},
|
[BLOCK_STONE] = {"minecraft:stone", NULL, 0},
|
||||||
@@ -422,7 +423,7 @@ static void build_chunk_nbt(const chunk_data *chunk, buf *out) {
|
|||||||
pack_heightmap(chunk, heightmap, 36);
|
pack_heightmap(chunk, heightmap, 36);
|
||||||
|
|
||||||
nbt_start_compound(out, "");
|
nbt_start_compound(out, "");
|
||||||
nbt_write_int(out, "DataVersion", 2586);
|
nbt_write_int(out, "DataVersion", DATA_VERSION);
|
||||||
|
|
||||||
nbt_start_compound(out, "Level");
|
nbt_start_compound(out, "Level");
|
||||||
nbt_write_string_tag(out, "Status", "full");
|
nbt_write_string_tag(out, "Status", "full");
|
||||||
|
|||||||
@@ -158,14 +158,19 @@ static column_data get_column_data(worldgen_ctx *ctx, int x, int z);
|
|||||||
static int generate_coal(worldgen_ctx *ctx, int x, int y, int z, int column_height, biome_id biome);
|
static int generate_coal(worldgen_ctx *ctx, int x, int y, int z, int column_height, biome_id biome);
|
||||||
|
|
||||||
static uint32_t hash_coords(int x, int z, uint32_t seed) {
|
static uint32_t hash_coords(int x, int z, uint32_t seed) {
|
||||||
uint32_t h = (uint32_t)(x * 374761393 + z * 668265263) ^ seed;
|
uint32_t ux = (uint32_t)x;
|
||||||
|
uint32_t uz = (uint32_t)z;
|
||||||
|
uint32_t h = (ux * 374761393u + uz * 668265263u) ^ seed;
|
||||||
h = (h ^ (h >> 13)) * 1274126177u;
|
h = (h ^ (h >> 13)) * 1274126177u;
|
||||||
return h ^ (h >> 16);
|
return h ^ (h >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t hash_coords3(int x, int y, int z, uint32_t seed) {
|
static uint32_t hash_coords3(int x, int y, int z, uint32_t seed) {
|
||||||
uint32_t h = (uint32_t)(x * 374761393 + y * 668265263 + z * 362827313) ^ seed;
|
uint32_t ux = (uint32_t)x;
|
||||||
h ^= (uint32_t)y * 0x9E3779B9u;
|
uint32_t uy = (uint32_t)y;
|
||||||
|
uint32_t uz = (uint32_t)z;
|
||||||
|
uint32_t h = (ux * 374761393u + uy * 668265263u + uz * 362827313u) ^ seed;
|
||||||
|
h ^= uy * 0x9E3779B9u;
|
||||||
h = (h ^ (h >> 13)) * 1274126177u;
|
h = (h ^ (h >> 13)) * 1274126177u;
|
||||||
return h ^ (h >> 16);
|
return h ^ (h >> 16);
|
||||||
}
|
}
|
||||||
@@ -417,6 +422,18 @@ static void set_block_with_height(chunk_data *chunk, int local_x, int local_z, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_cabin_structure_block(uint16_t block) {
|
||||||
|
return block == BLOCK_SPRUCE_PLANKS || block == BLOCK_OAK_PLANKS ||
|
||||||
|
block == BLOCK_OAK_LOG_X || block == BLOCK_OAK_LOG_Z ||
|
||||||
|
block == BLOCK_SPRUCE_LOG_X || block == BLOCK_SPRUCE_LOG_Z ||
|
||||||
|
block == BLOCK_GLASS_PANE || block == BLOCK_SPRUCE_STAIRS_E || block == BLOCK_SPRUCE_STAIRS_W ||
|
||||||
|
block == BLOCK_LADDER_N || block == BLOCK_LADDER_S || block == BLOCK_LADDER_E || block == BLOCK_LADDER_W ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_N_LOWER || block == BLOCK_SPRUCE_DOOR_N_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_S_LOWER || block == BLOCK_SPRUCE_DOOR_S_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_E_LOWER || block == BLOCK_SPRUCE_DOOR_E_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_W_LOWER || block == BLOCK_SPRUCE_DOOR_W_UPPER;
|
||||||
|
}
|
||||||
|
|
||||||
static double worley_distance(int x, int z, double scale, uint32_t seed) {
|
static double worley_distance(int x, int z, double scale, uint32_t seed) {
|
||||||
double px = x * scale;
|
double px = x * scale;
|
||||||
double pz = z * scale;
|
double pz = z * scale;
|
||||||
@@ -527,16 +544,16 @@ static int local_relief(worldgen_ctx *ctx, int x, int z, int center_height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static biome_id classify_biome(worldgen_ctx *ctx, int x, int z, int column_height) {
|
static biome_id classify_biome(worldgen_ctx *ctx, int x, int z, int column_height) {
|
||||||
|
int slope = local_relief(ctx, x, z, column_height);
|
||||||
|
double redwood = redwood_biome_mask(ctx, x, z, column_height, slope);
|
||||||
|
if (redwood > 0.18) {
|
||||||
|
return BIOME_REDWOOD_FOREST;
|
||||||
|
}
|
||||||
double old_growth = old_growth_plains_mask(ctx, x, z);
|
double old_growth = old_growth_plains_mask(ctx, x, z);
|
||||||
if (old_growth > 0.6) {
|
if (old_growth > 0.6) {
|
||||||
return BIOME_OLD_GROWTH_PLAINS;
|
return BIOME_OLD_GROWTH_PLAINS;
|
||||||
}
|
}
|
||||||
double blend = region_blend(ctx, x, z);
|
double blend = region_blend(ctx, x, z);
|
||||||
int slope = local_relief(ctx, x, z, column_height);
|
|
||||||
double redwood = redwood_biome_mask(ctx, x, z, column_height, slope);
|
|
||||||
if (redwood > 0.62) {
|
|
||||||
return BIOME_REDWOOD_FOREST;
|
|
||||||
}
|
|
||||||
double slope_score = clamp01((double)slope / 10.0);
|
double slope_score = clamp01((double)slope / 10.0);
|
||||||
double relief = clamp01((double)(column_height - ctx->sea_level) / 45.0);
|
double relief = clamp01((double)(column_height - ctx->sea_level) / 45.0);
|
||||||
double jitter = simplex_noise2(&ctx->noise, (x - 17000) * 0.001, (z + 17000) * 0.001) * 0.1;
|
double jitter = simplex_noise2(&ctx->noise, (x - 17000) * 0.001, (z + 17000) * 0.001) * 0.1;
|
||||||
@@ -547,6 +564,15 @@ static biome_id classify_biome(worldgen_ctx *ctx, int x, int z, int column_heigh
|
|||||||
return BIOME_WEST_KY_COALFIELDS;
|
return BIOME_WEST_KY_COALFIELDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int worldgen_debug_biome(worldgen_ctx *ctx, int x, int z) {
|
||||||
|
return (int)classify_biome(ctx, x, z, column_height(ctx, x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
double worldgen_debug_redwood_mask(worldgen_ctx *ctx, int x, int z) {
|
||||||
|
int height = column_height(ctx, x, z);
|
||||||
|
return redwood_biome_mask(ctx, x, z, height, local_relief(ctx, x, z, height));
|
||||||
|
}
|
||||||
|
|
||||||
static void block_list_init(block_list *list) {
|
static void block_list_init(block_list *list) {
|
||||||
list->items = NULL;
|
list->items = NULL;
|
||||||
list->count = 0;
|
list->count = 0;
|
||||||
@@ -2049,6 +2075,14 @@ static void build_cabin_rect(worldgen_ctx *ctx, const cabin_blueprint *bp, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int wz = z0; wz <= z1; ++wz) {
|
||||||
|
for (int wx = x0; wx <= x1; ++wx) {
|
||||||
|
int lx = wx - chunk_origin_x;
|
||||||
|
int lz = wz - chunk_origin_z;
|
||||||
|
if (lx < 0 || lx >= CHUNK_SIZE || lz < 0 || lz >= CHUNK_SIZE) continue;
|
||||||
|
set_block_with_height(chunk, lx, lz, base_floor_y, foundation_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ladder_hole_x = INT_MIN;
|
int ladder_hole_x = INT_MIN;
|
||||||
int ladder_hole_z = INT_MIN;
|
int ladder_hole_z = INT_MIN;
|
||||||
@@ -2249,13 +2283,19 @@ static int try_place_cabin(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_da
|
|||||||
if (rect_overlaps_mask(occupancy, chunk_origin_x, chunk_origin_z, x0, x1, z0, z1, 3)) {
|
if (rect_overlaps_mask(occupancy, chunk_origin_x, chunk_origin_z, x0, x1, z0, z1, 3)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
for (int wz = z0 - 2; wz <= z1 + 2; ++wz) {
|
||||||
|
for (int wx = x0 - 2; wx <= x1 + 2; ++wx) {
|
||||||
|
if (wx < chunk_origin_x || wx >= chunk_origin_x + CHUNK_SIZE) continue;
|
||||||
|
if (wz < chunk_origin_z || wz >= chunk_origin_z + CHUNK_SIZE) continue;
|
||||||
|
if (column_has_manmade_surface(chunk, chunk_x, chunk_z, wx, wz)) return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int wz = z0; wz <= z1; ++wz) {
|
for (int wz = z0; wz <= z1; ++wz) {
|
||||||
for (int wx = x0; wx <= x1; ++wx) {
|
for (int wx = x0; wx <= x1; ++wx) {
|
||||||
int lx = wx - chunk_origin_x;
|
int lx = wx - chunk_origin_x;
|
||||||
int lz = wz - chunk_origin_z;
|
int lz = wz - chunk_origin_z;
|
||||||
column_data col = columns[lx][lz];
|
column_data col = columns[lx][lz];
|
||||||
if (col.has_water && col.height < col.water_surface) return 0;
|
if (col.has_water && col.height < col.water_surface) return 0;
|
||||||
if (column_has_manmade_surface(chunk, chunk_x, chunk_z, wx, wz)) return 0;
|
|
||||||
if (col.height < min_h) min_h = col.height;
|
if (col.height < min_h) min_h = col.height;
|
||||||
if (col.height > max_h) max_h = col.height;
|
if (col.height > max_h) max_h = col.height;
|
||||||
}
|
}
|
||||||
@@ -2438,8 +2478,8 @@ static void connect_cabin_to_trail(worldgen_ctx *ctx, int chunk_x, int chunk_z,
|
|||||||
int door_x, int door_z, int door_side, int path_width) {
|
int door_x, int door_z, int door_side, int path_width) {
|
||||||
int step_x = (door_side == 2) ? -1 : (door_side == 3) ? 1 : 0;
|
int step_x = (door_side == 2) ? -1 : (door_side == 3) ? 1 : 0;
|
||||||
int step_z = (door_side == 0) ? -1 : (door_side == 1) ? 1 : 0;
|
int step_z = (door_side == 0) ? -1 : (door_side == 1) ? 1 : 0;
|
||||||
int start_x = door_x + step_x;
|
int start_x = door_x + step_x * 3;
|
||||||
int start_z = door_z + step_z;
|
int start_z = door_z + step_z * 3;
|
||||||
int spur_len = 24;
|
int spur_len = 24;
|
||||||
if (path_width < 2) path_width = 2;
|
if (path_width < 2) path_width = 2;
|
||||||
carve_cabin_path(ctx, chunk_x, chunk_z, chunk, columns, start_x, start_z, step_x, step_z, spur_len, path_width);
|
carve_cabin_path(ctx, chunk_x, chunk_z, chunk, columns, start_x, start_z, step_x, step_z, spur_len, path_width);
|
||||||
@@ -2568,20 +2608,9 @@ static int column_has_manmade_surface(chunk_data *chunk, int chunk_x, int chunk_
|
|||||||
if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) return 0;
|
if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) return 0;
|
||||||
for (int y = CHUNK_HEIGHT - 2; y >= 1; --y) {
|
for (int y = CHUNK_HEIGHT - 2; y >= 1; --y) {
|
||||||
uint16_t block = chunk->blocks[y][local_x][local_z];
|
uint16_t block = chunk->blocks[y][local_x][local_z];
|
||||||
if (block == BLOCK_AIR) continue;
|
if (block == BLOCK_GRAVEL || is_cabin_structure_block(block)) {
|
||||||
if (block == BLOCK_WATER) return 0;
|
|
||||||
if (block == BLOCK_GRAVEL || block == BLOCK_SPRUCE_PLANKS || block == BLOCK_OAK_PLANKS ||
|
|
||||||
block == BLOCK_OAK_LOG_X || block == BLOCK_OAK_LOG_Z ||
|
|
||||||
block == BLOCK_SPRUCE_LOG_X || block == BLOCK_SPRUCE_LOG_Z ||
|
|
||||||
block == BLOCK_GLASS_PANE || block == BLOCK_SPRUCE_STAIRS_E || block == BLOCK_SPRUCE_STAIRS_W ||
|
|
||||||
block == BLOCK_LADDER_N || block == BLOCK_LADDER_S || block == BLOCK_LADDER_E || block == BLOCK_LADDER_W ||
|
|
||||||
block == BLOCK_SPRUCE_DOOR_N_LOWER || block == BLOCK_SPRUCE_DOOR_N_UPPER ||
|
|
||||||
block == BLOCK_SPRUCE_DOOR_S_LOWER || block == BLOCK_SPRUCE_DOOR_S_UPPER ||
|
|
||||||
block == BLOCK_SPRUCE_DOOR_E_LOWER || block == BLOCK_SPRUCE_DOOR_E_UPPER ||
|
|
||||||
block == BLOCK_SPRUCE_DOOR_W_LOWER || block == BLOCK_SPRUCE_DOOR_W_UPPER) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -3153,6 +3182,12 @@ static void place_trail_column(chunk_data *out, column_data columns[CHUNK_SIZE][
|
|||||||
int original_height = columns[local_x][local_z].height;
|
int original_height = columns[local_x][local_z].height;
|
||||||
if (original_height < 1) original_height = target_height;
|
if (original_height < 1) original_height = target_height;
|
||||||
|
|
||||||
|
for (int y = 1; y < CHUNK_HEIGHT; ++y) {
|
||||||
|
if (is_cabin_structure_block(out->blocks[y][local_x][local_z])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int y = target_height - 1; y >= 1; --y) {
|
for (int y = target_height - 1; y >= 1; --y) {
|
||||||
uint16_t block = out->blocks[y][local_x][local_z];
|
uint16_t block = out->blocks[y][local_x][local_z];
|
||||||
if (block == BLOCK_AIR || block == BLOCK_WATER) {
|
if (block == BLOCK_AIR || block == BLOCK_WATER) {
|
||||||
|
|||||||
146
worldgen-c/tools/worldgen_scan.c
Normal file
146
worldgen-c/tools/worldgen_scan.c
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#include "worldgen.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static int is_cabin_block(uint16_t block) {
|
||||||
|
return block == BLOCK_SPRUCE_PLANKS || block == BLOCK_OAK_PLANKS ||
|
||||||
|
block == BLOCK_OAK_LOG_X || block == BLOCK_OAK_LOG_Z ||
|
||||||
|
block == BLOCK_SPRUCE_LOG_X || block == BLOCK_SPRUCE_LOG_Z ||
|
||||||
|
block == BLOCK_GLASS_PANE || block == BLOCK_SPRUCE_STAIRS_E || block == BLOCK_SPRUCE_STAIRS_W ||
|
||||||
|
block == BLOCK_LADDER_N || block == BLOCK_LADDER_S || block == BLOCK_LADDER_E || block == BLOCK_LADDER_W ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_N_LOWER || block == BLOCK_SPRUCE_DOOR_N_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_S_LOWER || block == BLOCK_SPRUCE_DOOR_S_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_E_LOWER || block == BLOCK_SPRUCE_DOOR_E_UPPER ||
|
||||||
|
block == BLOCK_SPRUCE_DOOR_W_LOWER || block == BLOCK_SPRUCE_DOOR_W_UPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long parse_long(const char *s) {
|
||||||
|
char *end = NULL;
|
||||||
|
long v = strtol(s, &end, 10);
|
||||||
|
if (!end || *end != '\0') {
|
||||||
|
fprintf(stderr, "Invalid number: %s\n", s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(const char *prog) {
|
||||||
|
fprintf(stderr, "Usage: %s [--radius N] [--center-x X] [--center-z Z] [--seed S] [--sea-level L] [--snow-line H] [--trails]\n", prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int radius = 16;
|
||||||
|
int center_x = 0;
|
||||||
|
int center_z = 0;
|
||||||
|
int seed = 123456;
|
||||||
|
int sea_level = 70;
|
||||||
|
int snow_line = INT_MIN;
|
||||||
|
int trails = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
if (strcmp(argv[i], "--radius") == 0 && i + 1 < argc) {
|
||||||
|
radius = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--center-x") == 0 && i + 1 < argc) {
|
||||||
|
center_x = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--center-z") == 0 && i + 1 < argc) {
|
||||||
|
center_z = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--seed") == 0 && i + 1 < argc) {
|
||||||
|
seed = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--sea-level") == 0 && i + 1 < argc) {
|
||||||
|
sea_level = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--snow-line") == 0 && i + 1 < argc) {
|
||||||
|
snow_line = (int)parse_long(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--trails") == 0) {
|
||||||
|
trails = 1;
|
||||||
|
} else if (strcmp(argv[i], "--help") == 0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (snow_line == INT_MIN) snow_line = sea_level + 38;
|
||||||
|
|
||||||
|
worldgen_ctx ctx;
|
||||||
|
worldgen_init(&ctx, seed, sea_level, snow_line);
|
||||||
|
ctx.enable_trails = trails;
|
||||||
|
if (trails) {
|
||||||
|
worldgen_prepass(&ctx, (center_x - radius) * CHUNK_SIZE, (center_x + radius) * CHUNK_SIZE + CHUNK_SIZE - 1,
|
||||||
|
(center_z - radius) * CHUNK_SIZE, (center_z + radius) * CHUNK_SIZE + CHUNK_SIZE - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
long chunks = 0;
|
||||||
|
long cabin_blocks = 0;
|
||||||
|
long cabin_columns = 0;
|
||||||
|
long gravel_columns = 0;
|
||||||
|
long cabin_gravel_columns = 0;
|
||||||
|
long tall_log_columns = 0;
|
||||||
|
long max_oak_log_run = 0;
|
||||||
|
long biome_columns[4] = {0, 0, 0, 0};
|
||||||
|
double max_redwood_mask = 0.0;
|
||||||
|
|
||||||
|
for (int cx = center_x - radius; cx <= center_x + radius; ++cx) {
|
||||||
|
for (int cz = center_z - radius; cz <= center_z + radius; ++cz) {
|
||||||
|
chunk_data chunk;
|
||||||
|
worldgen_generate_chunk(&ctx, cx, cz, &chunk);
|
||||||
|
++chunks;
|
||||||
|
for (int lx = 0; lx < CHUNK_SIZE; ++lx) {
|
||||||
|
for (int lz = 0; lz < CHUNK_SIZE; ++lz) {
|
||||||
|
int wx = cx * CHUNK_SIZE + lx;
|
||||||
|
int wz = cz * CHUNK_SIZE + lz;
|
||||||
|
int biome = worldgen_debug_biome(&ctx, wx, wz);
|
||||||
|
if (biome >= 0 && biome < 4) ++biome_columns[biome];
|
||||||
|
double redwood_mask = worldgen_debug_redwood_mask(&ctx, wx, wz);
|
||||||
|
if (redwood_mask > max_redwood_mask) max_redwood_mask = redwood_mask;
|
||||||
|
int has_cabin = 0;
|
||||||
|
int has_gravel = 0;
|
||||||
|
int current_run = 0;
|
||||||
|
int best_run = 0;
|
||||||
|
for (int y = 1; y < CHUNK_HEIGHT; ++y) {
|
||||||
|
uint16_t block = chunk.blocks[y][lx][lz];
|
||||||
|
if (is_cabin_block(block)) {
|
||||||
|
has_cabin = 1;
|
||||||
|
++cabin_blocks;
|
||||||
|
}
|
||||||
|
if (block == BLOCK_GRAVEL) {
|
||||||
|
has_gravel = 1;
|
||||||
|
}
|
||||||
|
if (block == BLOCK_OAK_LOG) {
|
||||||
|
++current_run;
|
||||||
|
if (current_run > best_run) best_run = current_run;
|
||||||
|
} else {
|
||||||
|
current_run = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best_run > max_oak_log_run) max_oak_log_run = best_run;
|
||||||
|
if (best_run >= 40) ++tall_log_columns;
|
||||||
|
if (has_cabin) ++cabin_columns;
|
||||||
|
if (has_gravel) ++gravel_columns;
|
||||||
|
if (has_cabin && has_gravel) {
|
||||||
|
++cabin_gravel_columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("chunks=%ld\n", chunks);
|
||||||
|
printf("cabin_blocks=%ld\n", cabin_blocks);
|
||||||
|
printf("cabin_columns=%ld\n", cabin_columns);
|
||||||
|
printf("gravel_columns=%ld\n", gravel_columns);
|
||||||
|
printf("cabin_gravel_columns=%ld\n", cabin_gravel_columns);
|
||||||
|
printf("tall_oak_log_columns=%ld\n", tall_log_columns);
|
||||||
|
printf("max_oak_log_run=%ld\n", max_oak_log_run);
|
||||||
|
printf("biome_west_ky=%ld\n", biome_columns[0]);
|
||||||
|
printf("biome_east_ky=%ld\n", biome_columns[1]);
|
||||||
|
printf("biome_old_growth=%ld\n", biome_columns[2]);
|
||||||
|
printf("biome_redwood=%ld\n", biome_columns[3]);
|
||||||
|
printf("max_redwood_mask=%.6f\n", max_redwood_mask);
|
||||||
|
|
||||||
|
worldgen_free_trails(&ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user