diff --git a/worldgen-c/src/worldgen.c b/worldgen-c/src/worldgen.c index e1b62de..010b3a3 100644 --- a/worldgen-c/src/worldgen.c +++ b/worldgen-c/src/worldgen.c @@ -1039,6 +1039,87 @@ static void place_branch_span(int x, int y, int z, int dx, int dz, int length, i } } +static int redwood_axis_log(int log_block, int dx, int dz) { + if (log_block == BLOCK_OAK_LOG) { + return (abs(dz) > abs(dx)) ? BLOCK_OAK_LOG_Z : BLOCK_OAK_LOG_X; + } + return log_block; +} + +static void place_redwood_leaf_disc(int cx, int cy, int cz, int radius, int trunk_x, int trunk_z, + int trunk_radius, int leaf_block, rng_state *rng, double hole_prob, + double edge_hole, block_list *out) { + if (radius < 1) return; + int r2 = radius * radius; + for (int dx = -radius; dx <= radius; ++dx) { + for (int dz = -radius; dz <= radius; ++dz) { + int d2 = dx * dx + dz * dz; + if (d2 > r2) continue; + int wx = cx + dx; + int wz = cz + dz; + if (trunk_radius >= 0 && abs(wx - trunk_x) <= trunk_radius && abs(wz - trunk_z) <= trunk_radius) continue; + double edge = radius > 0 ? sqrt((double)d2) / (double)radius : 0.0; + double skip = hole_prob + edge * edge_hole; + if (skip > 0.0 && rng_next_f64(rng) < skip) continue; + block_list_push(out, wx, cy, wz, (uint16_t)leaf_block); + } + } +} + +static void place_redwood_leaf_cluster(int cx, int cy, int cz, int trunk_x, int trunk_z, + int radius, int leaf_block, rng_state *rng, block_list *out) { + if (radius < 2) radius = 2; + place_redwood_leaf_disc(cx, cy, cz, radius, trunk_x, trunk_z, 1, leaf_block, rng, 0.04, 0.12, out); + place_redwood_leaf_disc(cx, cy - 1, cz, radius - 1, trunk_x, trunk_z, 1, leaf_block, rng, 0.08, 0.16, out); + place_redwood_leaf_disc(cx, cy + 1, cz, radius - 1, trunk_x, trunk_z, 1, leaf_block, rng, 0.10, 0.18, out); + if (radius > 2 && rng_next_f64(rng) < 0.55) { + place_redwood_leaf_disc(cx, cy - 2, cz, 1, trunk_x, trunk_z, 1, leaf_block, rng, 0.0, 0.0, out); + } +} + +static void place_redwood_branch(int x, int y, int z, int dx, int dz, int length, + int log_block, int leaf_block, rng_state *rng, block_list *out) { + if (length < 2) length = 2; + int branch_log = redwood_axis_log(log_block, dx, dz); + for (int step = 0; step < length; ++step) { + int reach = 2 + step; + int px = x + dx * reach; + int pz = z + dz * reach; + int py = y + ((step >= length - 2 && length > 3) ? 1 : 0); + if (step >= length - 3) { + place_redwood_leaf_cluster(px, py, pz, x, z, step == length - 1 ? 3 : 2, leaf_block, rng, out); + } else if ((step & 1) == 1) { + place_redwood_leaf_disc(px, py - 1, pz, 2, x, z, 1, leaf_block, rng, 0.18, 0.20, out); + } + } + for (int step = 0; step < length; ++step) { + int reach = 2 + step; + int px = x + dx * reach; + int pz = z + dz * reach; + int py = y + ((step >= length - 2 && length > 3) ? 1 : 0); + block_list_push(out, px, py, pz, (uint16_t)branch_log); + } +} + +static void place_redwood_root(worldgen_ctx *ctx, int x, int y, int z, int dx, int dz, + int length, int log_block, rng_state *rng, block_list *out) { + int root_log = redwood_axis_log(log_block, dx, dz); + for (int step = 2; step <= length; ++step) { + int px = x + dx * step; + int pz = z + dz * step; + int ground = column_height(ctx, px, pz); + if (ground <= 0 || ground >= CHUNK_HEIGHT - 2) continue; + if (abs(ground - y) > 3) continue; + int py = ground + 1; + block_list_push(out, px, py, pz, (uint16_t)root_log); + int stack = 4 - step + rng_range_inclusive(rng, 0, 1); + if (stack < 0) stack = 0; + for (int s = 1; s <= stack; ++s) { + block_list_push(out, px, py + s, pz, (uint16_t)log_block); + } + } +} + static void clear_snow_block(worldgen_ctx *ctx, int x, int y, int z, block_list *out) { int block = sample_block(ctx, x, y, z); if (block == BLOCK_SNOW) { @@ -1441,53 +1522,90 @@ static void build_maple_ancient(worldgen_ctx *ctx, int x, int y, int z, int heig } static void build_redwood_titan(worldgen_ctx *ctx, int x, int y, int z, int height, rng_state *rng, block_list *out, const tree_archetype *arch) { - (void)ctx; - int core_height = height + rng_range_inclusive(rng, 2, 7); - if (core_height < 30) core_height = 30; - if (core_height > 44) core_height = 44; - if (y + core_height + 10 >= CHUNK_HEIGHT) { - core_height = CHUNK_HEIGHT - y - 10; - if (core_height < 24) core_height = 24; + int core_height = height + rng_range_inclusive(rng, 3, 8); + if (core_height < 32) core_height = 32; + if (core_height > 46) core_height = 46; + if (y + core_height + 9 >= CHUNK_HEIGHT) { + core_height = CHUNK_HEIGHT - y - 9; + if (core_height < 26) core_height = 26; } - int taper_start = core_height - 6; + + const int dirs8[8][2] = { + {1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {1, -1}, {-1, 1}, {-1, -1} + }; + int spire = 3 + rng_range_inclusive(rng, 0, 2); + int crown_start = y + core_height / 2 + rng_range_inclusive(rng, 0, 2); + + for (int cy = crown_start; cy <= y + core_height + 2; cy += 2) { + int below_top = y + core_height + 2 - cy; + int radius = 2 + below_top / 9; + if (radius > 4) radius = 4; + if (cy > y + core_height - 4) radius = 2; + place_redwood_leaf_disc(x, cy, z, radius, x, z, 1, arch->leaf_block, rng, 0.08, 0.16, out); + if (radius > 2) { + place_redwood_leaf_disc(x, cy - 1, z, radius - 1, x, z, 1, arch->leaf_block, rng, 0.12, 0.20, out); + } + } + + int whorls = 7 + rng_range_inclusive(rng, 0, 2); + for (int w = 0; w < whorls; ++w) { + int wy = crown_start + w * 3 + rng_range_inclusive(rng, -1, 1); + if (wy > y + core_height - 2) break; + int branch_count = 3 + rng_range_inclusive(rng, 0, 2); + if (w >= whorls - 2) branch_count += 1; + int base_len = 6 - w / 2 + rng_range_inclusive(rng, 0, 1); + if (base_len < 3) base_len = 3; + if (base_len > 6) base_len = 6; + int start_dir = rng_range_inclusive(rng, 0, 7); + for (int b = 0; b < branch_count; ++b) { + int idx = (start_dir + b * 2 + rng_range_inclusive(rng, 0, 1)) & 7; + int branch_len = base_len - rng_range_inclusive(rng, 0, 1); + place_redwood_branch(x, wy, z, dirs8[idx][0], dirs8[idx][1], branch_len, + arch->log_block, arch->leaf_block, rng, out); + } + } + + for (int t = 0; t < 4; ++t) { + int py = y + core_height - 2 + t * 2; + int radius = (t < 2) ? 2 : 1; + place_redwood_leaf_disc(x, py, z, radius, x, z, 0, arch->leaf_block, rng, 0.04, 0.10, out); + } + + int dead_nubs = 5 + rng_range_inclusive(rng, 0, 3); + for (int n = 0; n < dead_nubs; ++n) { + int idx = rng_range_inclusive(rng, 0, 7); + int nub_y = y + core_height / 4 + rng_range_inclusive(rng, 0, core_height / 3); + int nub_len = 2 + rng_range_inclusive(rng, 0, 1); + int branch_log = redwood_axis_log(arch->log_block, dirs8[idx][0], dirs8[idx][1]); + for (int step = 2; step <= nub_len + 1; ++step) { + block_list_push(out, x + dirs8[idx][0] * step, nub_y, z + dirs8[idx][1] * step, (uint16_t)branch_log); + } + } + + int taper_start = core_height - 7; for (int dy = 0; dy < core_height; ++dy) { int radius = (dy >= taper_start) ? 0 : 1; for (int dx = -radius; dx <= radius; ++dx) { for (int dz = -radius; dz <= radius; ++dz) { + if (radius == 1 && abs(dx) == 1 && abs(dz) == 1 && dy > 12 && dy < taper_start - 2) { + int corner_seed = (dy + (dx > 0 ? 2 : 0) + (dz > 0 ? 5 : 0)) % 7; + if (corner_seed == 0 || corner_seed == 3) continue; + } block_list_push(out, x + dx, y + dy, z + dz, (uint16_t)arch->log_block); } } - } - int spire = 3 + rng_range_inclusive(rng, 0, 2); - place_log_column(x, y + core_height, z, spire, arch->log_block, out); - int canopy_base = y + core_height - 6; - for (int ring = 0; ring < 4; ++ring) { - int radius = 4 - ring; - if (radius < 2) radius = 2; - place_leaf_circle(x, canopy_base + ring, z, radius, arch->leaf_block, rng, 0.1, out); - place_leaf_circle(x, canopy_base + ring + 1, z, radius - 1, arch->leaf_block, rng, 0.18, out); - } - place_leaf_circle(x, canopy_base + 5, z, 2, arch->leaf_block, rng, 0.0, out); - place_leaf_circle(x, canopy_base + 6, z, 1, arch->leaf_block, rng, 0.0, out); - const int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; - for (int i = 0; i < 4; ++i) { - int buttress_height = 3 + rng_range_inclusive(rng, 0, 2); - for (int step = 0; step < 2; ++step) { - int px = x + dirs[i][0] * (2 + step); - int pz = z + dirs[i][1] * (2 + step); - int stack = buttress_height - step; - if (stack < 1) stack = 1; - for (int s = 0; s < stack; ++s) { - block_list_push(out, px, y + s, pz, (uint16_t)arch->log_block); - } + if (dy < taper_start - 4 && dy > 2 && (dy % 5) == 0) { + int rib = (dy / 5 + rng_range_inclusive(rng, 0, 3)) & 3; + block_list_push(out, x + dirs8[rib][0] * 2, y + dy, z + dirs8[rib][1] * 2, (uint16_t)arch->log_block); } } - for (int i = 0; i < 4; ++i) { - int branch_y = canopy_base - 3 - rng_range_inclusive(rng, 0, 3); - int branch_len = 3 + rng_range_inclusive(rng, 0, 2); - place_branch_span(x, branch_y, z, dirs[i][0], dirs[i][1], branch_len, 2, arch->log_block, arch->leaf_block, rng, out); + place_log_column(x, y + core_height, z, spire, arch->log_block, out); + + for (int i = 0; i < 8; ++i) { + int root_len = 3 + rng_range_inclusive(rng, 0, (i < 4) ? 2 : 1); + place_redwood_root(ctx, x, y, z, dirs8[i][0], dirs8[i][1], root_len, arch->log_block, rng, out); } - place_leaf_blob(x, canopy_base - 2, z, 4, 3, arch->leaf_block, rng, out); } typedef enum { @@ -1536,7 +1654,7 @@ static void build_redwood_titan(worldgen_ctx *ctx, int x, int y, int z, int heig [TREE_OAK_ANCIENT] = {"oak_ancient", TREE_SPECIES_OAK, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 12, 16, 6, 7, build_oak_ancient}, [TREE_SPRUCE_ANCIENT] = {"spruce_ancient", TREE_SPECIES_SPRUCE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 12, 16, 5, 7, build_spruce_ancient}, [TREE_MAPLE_ANCIENT] = {"maple_ancient", TREE_SPECIES_MAPLE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 11, 15, 6, 6, build_maple_ancient}, - [TREE_REDWOOD_TITAN] = {"redwood_titan", TREE_SPECIES_PINE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 28, 38, 4, 10, build_redwood_titan}, + [TREE_REDWOOD_TITAN] = {"redwood_titan", TREE_SPECIES_PINE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 28, 38, 5, 11, build_redwood_titan}, }; static const int TREE_VARIANTS_OAK[] = {TREE_OAK_ROUND, TREE_OAK_SPRAWL, TREE_OAK_COLUMNAR, TREE_OAK_ANCIENT};