|
|
|
@@ -125,14 +125,22 @@ static inline int clamp_int(int v, int min_v, int max_v) {
|
|
|
|
return v;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline double smoothstep01(double v) {
|
|
|
|
|
|
|
|
v = clamp01(v);
|
|
|
|
|
|
|
|
return v * v * (3.0 - 2.0 * v);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ground_slope(worldgen_ctx *ctx, int x, int z);
|
|
|
|
static int ground_slope(worldgen_ctx *ctx, int x, int z);
|
|
|
|
|
|
|
|
static int is_dry_beach_sand_patch(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z);
|
|
|
|
static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z);
|
|
|
|
static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z);
|
|
|
|
static void generate_chunk_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_redwood_floor(worldgen_ctx *ctx, int chunk_x, int chunk_z,
|
|
|
|
static void generate_chunk_redwood_floor(worldgen_ctx *ctx, int chunk_x, int chunk_z,
|
|
|
|
column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *chunk);
|
|
|
|
column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *chunk);
|
|
|
|
static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
|
|
|
|
static void generate_chunk_aquatic_life(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
|
|
|
|
|
|
|
|
static void generate_chunk_border_wall(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out);
|
|
|
|
static void ensure_trail_prepass(worldgen_ctx *ctx, int chunk_x, int chunk_z);
|
|
|
|
static void ensure_trail_prepass(worldgen_ctx *ctx, int chunk_x, int chunk_z);
|
|
|
|
static void append_trail_segment(worldgen_ctx *ctx, int ax, int az, int bx, int bz, int *points, int count);
|
|
|
|
static void append_trail_segment(worldgen_ctx *ctx, int ax, int az, int bx, int bz, int *points, int count);
|
|
|
|
static int *smooth_trail_polyline(worldgen_ctx *ctx, int *points, int count, int *out_count);
|
|
|
|
static int *smooth_trail_polyline(worldgen_ctx *ctx, int *points, int count, int *out_count);
|
|
|
|
@@ -175,6 +183,13 @@ static uint32_t hash_coords3(int x, int y, int z, uint32_t seed) {
|
|
|
|
return h ^ (h >> 16);
|
|
|
|
return h ^ (h >> 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int floor_div_int(int value, int divisor) {
|
|
|
|
|
|
|
|
int q = value / divisor;
|
|
|
|
|
|
|
|
int r = value % divisor;
|
|
|
|
|
|
|
|
if (r != 0 && ((r < 0) != (divisor < 0))) q--;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static double land_value(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
static double land_value(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
column_data col = get_column_data(ctx, x, z);
|
|
|
|
column_data col = get_column_data(ctx, x, z);
|
|
|
|
double value = 0.0;
|
|
|
|
double value = 0.0;
|
|
|
|
@@ -519,6 +534,15 @@ static double redwood_tree_presence(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
return clamp01(presence);
|
|
|
|
return clamp01(presence);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static double buildable_flatland_mask(worldgen_ctx *ctx, int x, int z, double mountain_factor) {
|
|
|
|
|
|
|
|
double cell = worley_distance(x + 71000, z - 71000, 0.00082, 0xB17D4B11u);
|
|
|
|
|
|
|
|
double broad_patch = smoothstep01((0.66 - cell) / 0.28);
|
|
|
|
|
|
|
|
double gate = simplex_noise2(&ctx->noise, (x - 91000) * 0.00022, (z + 91000) * 0.00022) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
double campus_patch = broad_patch * smoothstep01((gate - 0.32) / 0.36);
|
|
|
|
|
|
|
|
double mountain_guard = 1.0 - smoothstep01((mountain_factor - 0.42) / 0.34) * 0.82;
|
|
|
|
|
|
|
|
return clamp01(campus_patch * mountain_guard);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static double region_blend(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
static double region_blend(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
(void)ctx;
|
|
|
|
(void)ctx;
|
|
|
|
@@ -605,7 +629,7 @@ static void block_list_push(block_list *list, int x, int y, int z, uint16_t id)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Core worldgen functions
|
|
|
|
// Core worldgen functions
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static int column_height(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
static int raw_column_height(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
double region = region_blend(ctx, x, z);
|
|
|
|
double region = region_blend(ctx, x, z);
|
|
|
|
double old_growth = old_growth_plains_mask(ctx, x, z);
|
|
|
|
double old_growth = old_growth_plains_mask(ctx, x, z);
|
|
|
|
double warp1_x = x + simplex_noise2(&ctx->noise, x * 0.001, z * 0.001) * 50.0;
|
|
|
|
double warp1_x = x + simplex_noise2(&ctx->noise, x * 0.001, z * 0.001) * 50.0;
|
|
|
|
@@ -634,6 +658,7 @@ static int column_height(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
double mix_base = region * 0.35 + base_mountain * 0.55;
|
|
|
|
double mix_base = region * 0.35 + base_mountain * 0.55;
|
|
|
|
double mix = clamp01(mix_base * 0.65 + 0.15);
|
|
|
|
double mix = clamp01(mix_base * 0.65 + 0.15);
|
|
|
|
double height = lowland * (1.0 - mix) + highland * mix;
|
|
|
|
double height = lowland * (1.0 - mix) + highland * mix;
|
|
|
|
|
|
|
|
double build_flats = buildable_flatland_mask(ctx, x, z, mountain_factor);
|
|
|
|
|
|
|
|
|
|
|
|
double flat_noise = simplex_noise2(&ctx->noise, warp2_x * 0.0015, warp2_z * 0.0015) * 0.5 + 0.5;
|
|
|
|
double flat_noise = simplex_noise2(&ctx->noise, warp2_x * 0.0015, warp2_z * 0.0015) * 0.5 + 0.5;
|
|
|
|
double flat_bias = (mix < 0.45 ? (0.45 - mix) * 1.6 : 0.0) * flat_noise;
|
|
|
|
double flat_bias = (mix < 0.45 ? (0.45 - mix) * 1.6 : 0.0) * flat_noise;
|
|
|
|
@@ -648,20 +673,57 @@ static int column_height(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
double region_bias = 0.7 + 0.3 * clamp01(region);
|
|
|
|
double region_bias = 0.7 + 0.3 * clamp01(region);
|
|
|
|
double plains_detail_quiet = clamp01(1.0 - mix * 1.2);
|
|
|
|
double plains_detail_quiet = clamp01(1.0 - mix * 1.2);
|
|
|
|
double adjusted_weight = detail_weight * region_bias * (1.0 - plains_detail_quiet * 0.8);
|
|
|
|
double adjusted_weight = detail_weight * region_bias * (1.0 - plains_detail_quiet * 0.8);
|
|
|
|
|
|
|
|
adjusted_weight *= 1.0 - build_flats * 0.92;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.02, warp2_z * 0.02) * 5.0 * adjusted_weight;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.02, warp2_z * 0.02) * 5.0 * adjusted_weight;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.05, warp2_z * 0.05) * 2.0 * adjusted_weight;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.05, warp2_z * 0.05) * 2.0 * adjusted_weight;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.1, warp2_z * 0.1) * 1.0 * adjusted_weight;
|
|
|
|
height += simplex_noise2(&ctx->noise, warp2_x * 0.1, warp2_z * 0.1) * 1.0 * adjusted_weight;
|
|
|
|
height += simplex_noise3(&ctx->noise, warp2_x * 0.015, warp2_z * 0.015, region * 7.0) * 1.5;
|
|
|
|
height += simplex_noise3(&ctx->noise, warp2_x * 0.015, warp2_z * 0.015, region * 7.0) * 1.5 * (1.0 - build_flats * 0.85);
|
|
|
|
|
|
|
|
if (build_flats > 0.0) {
|
|
|
|
|
|
|
|
double campus_target = ctx->sea_level + 4;
|
|
|
|
|
|
|
|
campus_target += simplex_noise2(&ctx->noise, (x + 51000) * 0.00055, (z - 51000) * 0.00055) * 2.0;
|
|
|
|
|
|
|
|
campus_target += simplex_noise2(&ctx->noise, (x - 62000) * 0.0014, (z + 62000) * 0.0014) * 0.8;
|
|
|
|
|
|
|
|
double campus_blend = build_flats * 0.88;
|
|
|
|
|
|
|
|
height = height * (1.0 - campus_blend) + campus_target * campus_blend;
|
|
|
|
|
|
|
|
}
|
|
|
|
double flats = clamp01((old_growth - 0.25) / 0.75);
|
|
|
|
double flats = clamp01((old_growth - 0.25) / 0.75);
|
|
|
|
if (flats > 0.0) {
|
|
|
|
if (flats > 0.0) {
|
|
|
|
double plains_target = ctx->sea_level - 2 + simplex_noise2(&ctx->noise, (x - 22000) * 0.002, (z + 22000) * 0.002) * 5.0;
|
|
|
|
double plains_target = ctx->sea_level - 2 + simplex_noise2(&ctx->noise, (x - 22000) * 0.002, (z + 22000) * 0.002) * 5.0;
|
|
|
|
plains_target += simplex_noise2(&ctx->noise, (x + 33000) * 0.01, (z - 33000) * 0.01) * 1.5;
|
|
|
|
plains_target += simplex_noise2(&ctx->noise, (x + 33000) * 0.01, (z - 33000) * 0.01) * 1.5;
|
|
|
|
height = height * (1.0 - flats) + plains_target * flats;
|
|
|
|
double plains_blend = flats * (1.0 - build_flats * 0.55);
|
|
|
|
|
|
|
|
height = height * (1.0 - plains_blend) + plains_target * plains_blend;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double above_water = height - (double)ctx->sea_level;
|
|
|
|
|
|
|
|
if (above_water > -1.5 && above_water < 24.0) {
|
|
|
|
|
|
|
|
double island_mask = smoothstep01((above_water + 1.5) / 6.0) * (1.0 - smoothstep01((above_water - 12.0) / 12.0));
|
|
|
|
|
|
|
|
island_mask *= 1.0 - build_flats * 0.7;
|
|
|
|
|
|
|
|
double rolling = simplex_noise2(&ctx->noise, (warp2_x + 76000) * 0.0045, (warp2_z - 76000) * 0.0045) * 3.2;
|
|
|
|
|
|
|
|
rolling += simplex_noise2(&ctx->noise, (warp2_x - 83000) * 0.013, (warp2_z + 83000) * 0.013) * 1.4;
|
|
|
|
|
|
|
|
double mound = simplex_noise2(&ctx->noise, (warp2_x + 91000) * 0.021, (warp2_z - 91000) * 0.021) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
rolling += mound * mound * 2.0;
|
|
|
|
|
|
|
|
double coastal_mask = (1.0 - smoothstep01((above_water - 1.0) / 5.0)) * smoothstep01((above_water + 1.0) / 3.0);
|
|
|
|
|
|
|
|
double dune = fabs(simplex_noise2(&ctx->noise, (warp2_x - 47000) * 0.028, (warp2_z + 47000) * 0.028)) * 2.2;
|
|
|
|
|
|
|
|
height += (rolling * island_mask) + (dune * coastal_mask * (1.0 - build_flats * 0.85));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (int)height;
|
|
|
|
return (int)height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int column_height(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
|
|
|
|
uint32_t h = hash_coords(x, z, (uint32_t)ctx->world_seed ^ 0xA53C9E21u);
|
|
|
|
|
|
|
|
size_t idx = (size_t)(h & (WORLDGEN_HEIGHT_CACHE_SIZE - 1));
|
|
|
|
|
|
|
|
worldgen_height_cache_entry *entry = &ctx->height_cache[idx];
|
|
|
|
|
|
|
|
if (entry->valid && entry->x == x && entry->z == z) {
|
|
|
|
|
|
|
|
return entry->value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int value = raw_column_height(ctx, x, z);
|
|
|
|
|
|
|
|
entry->x = x;
|
|
|
|
|
|
|
|
entry->z = z;
|
|
|
|
|
|
|
|
entry->value = value;
|
|
|
|
|
|
|
|
entry->valid = 1;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static column_data get_column_data(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
static column_data get_column_data(worldgen_ctx *ctx, int x, int z) {
|
|
|
|
column_data data;
|
|
|
|
column_data data;
|
|
|
|
data.height = column_height(ctx, x, z);
|
|
|
|
data.height = column_height(ctx, x, z);
|
|
|
|
@@ -889,11 +951,51 @@ static int generate_coal(worldgen_ctx *ctx, int x, int y, int z, int column_heig
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Terrain block generation
|
|
|
|
// Terrain block generation
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static int is_dry_beach_sand_patch(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z) {
|
|
|
|
|
|
|
|
if (data->has_water && data->water_surface >= data->height) return 0;
|
|
|
|
|
|
|
|
if (ground_slope(ctx, world_x, world_z) > 2) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int nearest_water_dist2 = 9999;
|
|
|
|
|
|
|
|
int shore_level = ctx->sea_level;
|
|
|
|
|
|
|
|
for (int dx = -7; dx <= 7; ++dx) {
|
|
|
|
|
|
|
|
for (int dz = -7; dz <= 7; ++dz) {
|
|
|
|
|
|
|
|
int dist2 = dx * dx + dz * dz;
|
|
|
|
|
|
|
|
if (dist2 == 0 || dist2 > 49) continue;
|
|
|
|
|
|
|
|
column_data neighbor = get_column_data(ctx, world_x + dx, world_z + dz);
|
|
|
|
|
|
|
|
if (!neighbor.has_water || neighbor.water_surface < neighbor.height) continue;
|
|
|
|
|
|
|
|
if (data->height < neighbor.water_surface - 1 || data->height > neighbor.water_surface + 4) continue;
|
|
|
|
|
|
|
|
if (dist2 < nearest_water_dist2) {
|
|
|
|
|
|
|
|
nearest_water_dist2 = dist2;
|
|
|
|
|
|
|
|
shore_level = neighbor.water_surface;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nearest_water_dist2 == 9999) return 0;
|
|
|
|
|
|
|
|
if (data->height > shore_level + 4) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double broad = simplex_noise2(&ctx->noise, (world_x + 72000) * 0.045, (world_z - 72000) * 0.045) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
double ragged = simplex_noise2(&ctx->noise, (world_x - 81000) * 0.16, (world_z + 81000) * 0.16) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
double patch = broad * 0.7 + ragged * 0.3;
|
|
|
|
|
|
|
|
double near_bonus = clamp01((18.0 - (double)nearest_water_dist2) / 18.0) * 0.18;
|
|
|
|
|
|
|
|
double height_penalty = clamp01((double)(data->height - shore_level) / 4.0) * 0.16;
|
|
|
|
|
|
|
|
return patch + near_bonus - height_penalty > 0.56;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z) {
|
|
|
|
static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z) {
|
|
|
|
if (data->has_water && data->water_surface >= data->height) {
|
|
|
|
if (data->has_water && data->water_surface >= data->height) {
|
|
|
|
int slope = ground_slope(ctx, world_x, world_z);
|
|
|
|
int slope = ground_slope(ctx, world_x, world_z);
|
|
|
|
if (slope <= 1) return BLOCK_SAND;
|
|
|
|
double shelf = simplex_noise2(&ctx->noise, (world_x + 45000) * 0.025, (world_z - 45000) * 0.025) * 0.5 + 0.5;
|
|
|
|
if (slope <= 3) return BLOCK_GRAVEL;
|
|
|
|
double basin = simplex_noise2(&ctx->noise, (world_x - 23000) * 0.007, (world_z + 23000) * 0.007) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
int depth = data->water_surface - data->height;
|
|
|
|
|
|
|
|
if (slope <= 1) {
|
|
|
|
|
|
|
|
if (depth >= 3 && basin > 0.74) return BLOCK_CLAY;
|
|
|
|
|
|
|
|
if (shelf > 0.76) return BLOCK_GRAVEL;
|
|
|
|
|
|
|
|
return BLOCK_SAND;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slope <= 3) {
|
|
|
|
|
|
|
|
if (depth >= 4 && basin > 0.82) return BLOCK_CLAY;
|
|
|
|
|
|
|
|
return (shelf > 0.44) ? BLOCK_GRAVEL : BLOCK_SAND;
|
|
|
|
|
|
|
|
}
|
|
|
|
return BLOCK_STONE;
|
|
|
|
return BLOCK_STONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int snow_line = ctx->snow_line;
|
|
|
|
int snow_line = ctx->snow_line;
|
|
|
|
@@ -906,6 +1008,9 @@ static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data,
|
|
|
|
double noise = simplex_noise2(&ctx->noise, world_x * 0.02, world_z * 0.02) * 0.5 + 0.5;
|
|
|
|
double noise = simplex_noise2(&ctx->noise, world_x * 0.02, world_z * 0.02) * 0.5 + 0.5;
|
|
|
|
if (noise < t) return BLOCK_SNOW;
|
|
|
|
if (noise < t) return BLOCK_SNOW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_dry_beach_sand_patch(ctx, data, world_x, world_z)) {
|
|
|
|
|
|
|
|
return BLOCK_SAND;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (data->biome == BIOME_REDWOOD_FOREST) {
|
|
|
|
if (data->biome == BIOME_REDWOOD_FOREST) {
|
|
|
|
int slope = ground_slope(ctx, world_x, world_z);
|
|
|
|
int slope = ground_slope(ctx, world_x, world_z);
|
|
|
|
if (slope >= 5) return BLOCK_STONE;
|
|
|
|
if (slope >= 5) return BLOCK_STONE;
|
|
|
|
@@ -967,14 +1072,19 @@ static int can_place_tree(worldgen_ctx *ctx, int x, int y, int z, int trunk_heig
|
|
|
|
if (ground_block != BLOCK_GRASS && ground_block != BLOCK_SNOW && ground_block != BLOCK_DIRT) return 0;
|
|
|
|
if (ground_block != BLOCK_GRASS && ground_block != BLOCK_SNOW && ground_block != BLOCK_DIRT) return 0;
|
|
|
|
int top = y + trunk_height + crown_extra;
|
|
|
|
int top = y + trunk_height + crown_extra;
|
|
|
|
if (top >= CHUNK_HEIGHT) return 0;
|
|
|
|
if (top >= CHUNK_HEIGHT) return 0;
|
|
|
|
for (int cy = y; cy <= top; ++cy) {
|
|
|
|
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dz = -radius; dz <= radius; ++dz) {
|
|
|
|
for (int dz = -radius; dz <= radius; ++dz) {
|
|
|
|
if (cy < y && (dx != 0 || dz != 0)) continue;
|
|
|
|
column_data data = get_column_data(ctx, x + dx, z + dz);
|
|
|
|
int block = sample_block(ctx, x + dx, cy, z + dz);
|
|
|
|
int natural_top = data.height;
|
|
|
|
if (block != BLOCK_AIR && block != BLOCK_SNOW) {
|
|
|
|
if (data.has_water && data.water_surface > natural_top) {
|
|
|
|
return 0;
|
|
|
|
natural_top = data.water_surface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (natural_top >= y && natural_top <= top) {
|
|
|
|
|
|
|
|
if (natural_top == y && natural_top == data.height &&
|
|
|
|
|
|
|
|
select_surface_block(ctx, &data, x + dx, z + dz) == BLOCK_SNOW) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1005,6 +1115,7 @@ static void place_leaf_circle(int cx, int cy, int cz, int radius, int leaf_block
|
|
|
|
int r2 = radius * radius;
|
|
|
|
int r2 = radius * radius;
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dz = -radius; dz <= radius; ++dz) {
|
|
|
|
for (int dz = -radius; dz <= radius; ++dz) {
|
|
|
|
|
|
|
|
if (dx == 0 && dz == 0) continue;
|
|
|
|
if (dx * dx + dz * dz > r2) continue;
|
|
|
|
if (dx * dx + dz * dz > r2) continue;
|
|
|
|
if (hole_prob > 0.0 && rng_next_f64(rng) < hole_prob) continue;
|
|
|
|
if (hole_prob > 0.0 && rng_next_f64(rng) < hole_prob) continue;
|
|
|
|
block_list_push(out, cx + dx, cy, cz + dz, (uint16_t)leaf_block);
|
|
|
|
block_list_push(out, cx + dx, cy, cz + dz, (uint16_t)leaf_block);
|
|
|
|
@@ -1039,6 +1150,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) {
|
|
|
|
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);
|
|
|
|
int block = sample_block(ctx, x, y, z);
|
|
|
|
if (block == BLOCK_SNOW) {
|
|
|
|
if (block == BLOCK_SNOW) {
|
|
|
|
@@ -1441,53 +1633,117 @@ 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) {
|
|
|
|
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, 3, 8);
|
|
|
|
int core_height = height + rng_range_inclusive(rng, 2, 7);
|
|
|
|
if (core_height < 32) core_height = 32;
|
|
|
|
if (core_height < 30) core_height = 30;
|
|
|
|
if (core_height > 46) core_height = 46;
|
|
|
|
if (core_height > 44) core_height = 44;
|
|
|
|
if (y + core_height + 9 >= CHUNK_HEIGHT) {
|
|
|
|
if (y + core_height + 10 >= CHUNK_HEIGHT) {
|
|
|
|
core_height = CHUNK_HEIGHT - y - 9;
|
|
|
|
core_height = CHUNK_HEIGHT - y - 10;
|
|
|
|
if (core_height < 26) core_height = 26;
|
|
|
|
if (core_height < 24) core_height = 24;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
if (rng_next_f64(rng) < 0.10) {
|
|
|
|
|
|
|
|
int snag_height = core_height - rng_range_inclusive(rng, 4, 10);
|
|
|
|
|
|
|
|
if (snag_height < 22) snag_height = 22;
|
|
|
|
|
|
|
|
int taper_start = snag_height - 5;
|
|
|
|
|
|
|
|
for (int dy = 0; dy < snag_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 > 10 && (dy % 3) == 0) continue;
|
|
|
|
|
|
|
|
block_list_push(out, x + dx, y + dy, z + dz, (uint16_t)arch->log_block);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dy > 8 && dy < snag_height - 4 && (dy % 7) == 0) {
|
|
|
|
|
|
|
|
int dir = (dy / 7 + rng_range_inclusive(rng, 0, 3)) & 7;
|
|
|
|
|
|
|
|
int branch_log = redwood_axis_log(arch->log_block, dirs8[dir][0], dirs8[dir][1]);
|
|
|
|
|
|
|
|
int len = 2 + rng_range_inclusive(rng, 0, 2);
|
|
|
|
|
|
|
|
for (int step = 2; step <= len + 1; ++step) {
|
|
|
|
|
|
|
|
block_list_push(out, x + dirs8[dir][0] * step, y + dy, z + dirs8[dir][1] * step, (uint16_t)branch_log);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
|
|
|
|
int root_len = 3 + rng_range_inclusive(rng, 0, 2);
|
|
|
|
|
|
|
|
place_redwood_root(ctx, x, y, z, dirs8[i][0], dirs8[i][1], root_len, arch->log_block, rng, out);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
for (int dy = 0; dy < core_height; ++dy) {
|
|
|
|
int radius = (dy >= taper_start) ? 0 : 1;
|
|
|
|
int radius = (dy >= taper_start) ? 0 : 1;
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dx = -radius; dx <= radius; ++dx) {
|
|
|
|
for (int dz = -radius; dz <= radius; ++dz) {
|
|
|
|
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);
|
|
|
|
block_list_push(out, x + dx, y + dy, z + dz, (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);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int spire = 3 + rng_range_inclusive(rng, 0, 2);
|
|
|
|
|
|
|
|
place_log_column(x, y + core_height, z, spire, arch->log_block, out);
|
|
|
|
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) {
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
int radius = 4 - ring;
|
|
|
|
int root_len = 3 + rng_range_inclusive(rng, 0, (i < 4) ? 2 : 1);
|
|
|
|
if (radius < 2) radius = 2;
|
|
|
|
place_redwood_root(ctx, x, y, z, dirs8[i][0], dirs8[i][1], root_len, arch->log_block, rng, out);
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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_leaf_blob(x, canopy_base - 2, z, 4, 3, arch->leaf_block, rng, out);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
typedef enum {
|
|
|
|
@@ -1536,7 +1792,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_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_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_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};
|
|
|
|
static const int TREE_VARIANTS_OAK[] = {TREE_OAK_ROUND, TREE_OAK_SPRAWL, TREE_OAK_COLUMNAR, TREE_OAK_ANCIENT};
|
|
|
|
@@ -1700,7 +1956,7 @@ static void generate_chunk_redwood_floor(worldgen_ctx *ctx, int chunk_x, int chu
|
|
|
|
double litter = simplex_noise2(&ctx->noise, (wx + 8000) * 0.03, (wz - 8000) * 0.03) * 0.5 + 0.5;
|
|
|
|
double litter = simplex_noise2(&ctx->noise, (wx + 8000) * 0.03, (wz - 8000) * 0.03) * 0.5 + 0.5;
|
|
|
|
double fern_chance = clamp01(0.25 + rain * 0.5 + (1.0 - litter) * 0.25);
|
|
|
|
double fern_chance = clamp01(0.25 + rain * 0.5 + (1.0 - litter) * 0.25);
|
|
|
|
if (chunk->blocks[cd.height + 1][dx][dz] == BLOCK_AIR && rng_next_f64(&rng) < fern_chance) {
|
|
|
|
if (chunk->blocks[cd.height + 1][dx][dz] == BLOCK_AIR && rng_next_f64(&rng) < fern_chance) {
|
|
|
|
chunk->blocks[cd.height + 1][dx][dz] = BLOCK_TALL_GRASS;
|
|
|
|
chunk->blocks[cd.height + 1][dx][dz] = (rain > 0.55 || rng_next_f64(&rng) < 0.45) ? BLOCK_FERN : BLOCK_TALL_GRASS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rng_next_f64(&rng) < 0.0035) {
|
|
|
|
if (rng_next_f64(&rng) < 0.0035) {
|
|
|
|
try_place_redwood_fallen_log(ctx, chunk_x, chunk_z, chunk, columns, dx, dz, &rng);
|
|
|
|
try_place_redwood_fallen_log(ctx, chunk_x, chunk_z, chunk, columns, dx, dz, &rng);
|
|
|
|
@@ -1731,7 +1987,7 @@ static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, co
|
|
|
|
uint32_t h = hash_coords(world_x, world_z, (uint32_t)(ctx->world_seed ^ 0x5F3759DFu));
|
|
|
|
uint32_t h = hash_coords(world_x, world_z, (uint32_t)(ctx->world_seed ^ 0x5F3759DFu));
|
|
|
|
double roll = (double)(h & 0xFFFF) / 65535.0;
|
|
|
|
double roll = (double)(h & 0xFFFF) / 65535.0;
|
|
|
|
if (roll > chance) continue;
|
|
|
|
if (roll > chance) continue;
|
|
|
|
out->blocks[cd.height + 1][dx][dz] = BLOCK_TALL_GRASS;
|
|
|
|
out->blocks[cd.height + 1][dx][dz] = (humidity > 0.72 && rain > 0.45) ? BLOCK_FERN : BLOCK_TALL_GRASS;
|
|
|
|
if (roll < chance * 0.4) {
|
|
|
|
if (roll < chance * 0.4) {
|
|
|
|
const int offsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
|
|
|
const int offsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
@@ -1742,7 +1998,7 @@ static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, co
|
|
|
|
if (nd.height <= 0 || nd.height >= CHUNK_HEIGHT - 1) continue;
|
|
|
|
if (nd.height <= 0 || nd.height >= CHUNK_HEIGHT - 1) continue;
|
|
|
|
if (out->blocks[nd.height][nx][nz] != BLOCK_GRASS) continue;
|
|
|
|
if (out->blocks[nd.height][nx][nz] != BLOCK_GRASS) continue;
|
|
|
|
if (out->blocks[nd.height + 1][nx][nz] != BLOCK_AIR) continue;
|
|
|
|
if (out->blocks[nd.height + 1][nx][nz] != BLOCK_AIR) continue;
|
|
|
|
out->blocks[nd.height + 1][nx][nz] = BLOCK_TALL_GRASS;
|
|
|
|
out->blocks[nd.height + 1][nx][nz] = ((h >> (i + 3)) & 1u) ? BLOCK_TALL_GRASS : BLOCK_FERN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1772,7 +2028,17 @@ static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z,
|
|
|
|
uint32_t h = hash_coords(world_x + 30000, world_z - 30000, (uint32_t)(ctx->world_seed ^ 0xA511E9B5u));
|
|
|
|
uint32_t h = hash_coords(world_x + 30000, world_z - 30000, (uint32_t)(ctx->world_seed ^ 0xA511E9B5u));
|
|
|
|
double roll = (double)(h & 0xFFFF) / 65535.0;
|
|
|
|
double roll = (double)(h & 0xFFFF) / 65535.0;
|
|
|
|
if (roll > chance) continue;
|
|
|
|
if (roll > chance) continue;
|
|
|
|
out->blocks[cd.height + 1][dx][dz] = BLOCK_WILDFLOWER;
|
|
|
|
uint16_t flower = BLOCK_WILDFLOWER;
|
|
|
|
|
|
|
|
uint32_t flower_hash = hash_coords(world_x - 17000, world_z + 17000, (uint32_t)(ctx->world_seed ^ 0xB10F00Du));
|
|
|
|
|
|
|
|
switch ((flower_hash >> 8) % 6) {
|
|
|
|
|
|
|
|
case 0: flower = BLOCK_DANDELION; break;
|
|
|
|
|
|
|
|
case 1: flower = BLOCK_AZURE_BLUET; break;
|
|
|
|
|
|
|
|
case 2: flower = BLOCK_OXEYE_DAISY; break;
|
|
|
|
|
|
|
|
case 3: flower = BLOCK_CORNFLOWER; break;
|
|
|
|
|
|
|
|
case 4: flower = BLOCK_LILY_OF_THE_VALLEY; break;
|
|
|
|
|
|
|
|
default: flower = BLOCK_WILDFLOWER; break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
out->blocks[cd.height + 1][dx][dz] = flower;
|
|
|
|
static const int offsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
|
|
|
static const int offsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
int nx = dx + offsets[i][0];
|
|
|
|
int nx = dx + offsets[i][0];
|
|
|
|
@@ -1784,7 +2050,40 @@ static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z,
|
|
|
|
if (out->blocks[nd.height + 1][nx][nz] != BLOCK_AIR) continue;
|
|
|
|
if (out->blocks[nd.height + 1][nx][nz] != BLOCK_AIR) continue;
|
|
|
|
uint32_t nh = hash_coords(world_x + offsets[i][0] + 60000, world_z + offsets[i][1] - 60000, (uint32_t)(ctx->world_seed ^ 0xF00DBAAu));
|
|
|
|
uint32_t nh = hash_coords(world_x + offsets[i][0] + 60000, world_z + offsets[i][1] - 60000, (uint32_t)(ctx->world_seed ^ 0xF00DBAAu));
|
|
|
|
if ((nh & 0xFFFF) > 24000) continue;
|
|
|
|
if ((nh & 0xFFFF) > 24000) continue;
|
|
|
|
out->blocks[nd.height + 1][nx][nz] = BLOCK_WILDFLOWER;
|
|
|
|
uint16_t patch_flower = flower;
|
|
|
|
|
|
|
|
if ((nh & 7u) == 0u) patch_flower = BLOCK_DANDELION;
|
|
|
|
|
|
|
|
out->blocks[nd.height + 1][nx][nz] = patch_flower;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void generate_chunk_aquatic_life(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out) {
|
|
|
|
|
|
|
|
for (int dx = 0; dx < CHUNK_SIZE; ++dx) {
|
|
|
|
|
|
|
|
for (int dz = 0; dz < CHUNK_SIZE; ++dz) {
|
|
|
|
|
|
|
|
column_data cd = columns[dx][dz];
|
|
|
|
|
|
|
|
if (!cd.has_water || cd.water_surface <= cd.height) continue;
|
|
|
|
|
|
|
|
if (cd.height <= 0 || cd.height >= CHUNK_HEIGHT - 2) continue;
|
|
|
|
|
|
|
|
int depth = cd.water_surface - cd.height;
|
|
|
|
|
|
|
|
if (depth < 1 || depth > 9) continue;
|
|
|
|
|
|
|
|
int world_x = chunk_x * CHUNK_SIZE + dx;
|
|
|
|
|
|
|
|
int world_z = chunk_z * CHUNK_SIZE + dz;
|
|
|
|
|
|
|
|
uint16_t floor = out->blocks[cd.height][dx][dz];
|
|
|
|
|
|
|
|
if (floor != BLOCK_SAND && floor != BLOCK_GRAVEL && floor != BLOCK_CLAY && floor != BLOCK_DIRT) continue;
|
|
|
|
|
|
|
|
if (out->blocks[cd.height + 1][dx][dz] != BLOCK_WATER) continue;
|
|
|
|
|
|
|
|
double bed = simplex_noise2(&ctx->noise, (world_x + 70000) * 0.035, (world_z - 70000) * 0.035) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
double patch = simplex_noise2(&ctx->noise, (world_x - 15000) * 0.011, (world_z + 15000) * 0.011) * 0.5 + 0.5;
|
|
|
|
|
|
|
|
double chance = 0.08 + patch * 0.24;
|
|
|
|
|
|
|
|
if (floor == BLOCK_CLAY) chance += 0.10;
|
|
|
|
|
|
|
|
if (depth <= 3) chance += 0.12;
|
|
|
|
|
|
|
|
if (ground_slope(ctx, world_x, world_z) > 2) chance *= 0.35;
|
|
|
|
|
|
|
|
uint32_t h = hash_coords(world_x, world_z, (uint32_t)(ctx->world_seed ^ 0x5EA6A55u));
|
|
|
|
|
|
|
|
double roll = (double)(h & 0xFFFF) / 65535.0;
|
|
|
|
|
|
|
|
if (roll > clamp01(chance)) continue;
|
|
|
|
|
|
|
|
out->blocks[cd.height + 1][dx][dz] = BLOCK_SEAGRASS;
|
|
|
|
|
|
|
|
if (depth >= 3 && bed > 0.78 && cd.height + 2 < CHUNK_HEIGHT &&
|
|
|
|
|
|
|
|
out->blocks[cd.height + 2][dx][dz] == BLOCK_WATER) {
|
|
|
|
|
|
|
|
out->blocks[cd.height + 2][dx][dz] = BLOCK_SEAGRASS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -3391,6 +3690,269 @@ static void generate_chunk_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int border_wall_anchor_base(worldgen_ctx *ctx, int side, int anchor_along, int wall_center) {
|
|
|
|
|
|
|
|
int center_x = anchor_along;
|
|
|
|
|
|
|
|
int center_z = anchor_along;
|
|
|
|
|
|
|
|
switch (side) {
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
|
|
center_x = ctx->wall_min_x + wall_center;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
center_x = ctx->wall_max_x - wall_center;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
center_z = ctx->wall_min_z + wall_center;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
center_z = ctx->wall_max_z - wall_center;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int smoothed_base = 0;
|
|
|
|
|
|
|
|
int weight_sum = 0;
|
|
|
|
|
|
|
|
int highest_sample_base = 0;
|
|
|
|
|
|
|
|
const int sample_offsets[5] = {-32, -16, 0, 16, 32};
|
|
|
|
|
|
|
|
const int sample_weights[5] = {1, 2, 4, 2, 1};
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
|
|
|
|
int sx = center_x;
|
|
|
|
|
|
|
|
int sz = center_z;
|
|
|
|
|
|
|
|
if (side < 2) {
|
|
|
|
|
|
|
|
sz += sample_offsets[i];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
sx += sample_offsets[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sx < ctx->wall_min_x) sx = ctx->wall_min_x;
|
|
|
|
|
|
|
|
if (sx > ctx->wall_max_x) sx = ctx->wall_max_x;
|
|
|
|
|
|
|
|
if (sz < ctx->wall_min_z) sz = ctx->wall_min_z;
|
|
|
|
|
|
|
|
if (sz > ctx->wall_max_z) sz = ctx->wall_max_z;
|
|
|
|
|
|
|
|
column_data sample = get_column_data(ctx, sx, sz);
|
|
|
|
|
|
|
|
int sample_base = sample.height;
|
|
|
|
|
|
|
|
if (sample.has_water && sample.water_surface > sample_base) sample_base = sample.water_surface;
|
|
|
|
|
|
|
|
if (i == 0 || sample_base > highest_sample_base) highest_sample_base = sample_base;
|
|
|
|
|
|
|
|
smoothed_base += sample_base * sample_weights[i];
|
|
|
|
|
|
|
|
weight_sum += sample_weights[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
smoothed_base /= weight_sum;
|
|
|
|
|
|
|
|
if (highest_sample_base > smoothed_base + 8) {
|
|
|
|
|
|
|
|
smoothed_base += (highest_sample_base - smoothed_base - 8 + 1) / 2;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smoothed_base < 1) smoothed_base = 1;
|
|
|
|
|
|
|
|
if (smoothed_base > CHUNK_HEIGHT - 2) smoothed_base = CHUNK_HEIGHT - 2;
|
|
|
|
|
|
|
|
return smoothed_base;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int border_wall_ramp_base(worldgen_ctx *ctx, int side, int along, int wall_center, int tower_spacing) {
|
|
|
|
|
|
|
|
int anchor = floor_div_int(along + tower_spacing / 2, tower_spacing) * tower_spacing;
|
|
|
|
|
|
|
|
int current_base = border_wall_anchor_base(ctx, side, anchor, wall_center);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int next_anchor = anchor + tower_spacing;
|
|
|
|
|
|
|
|
int next_boundary = anchor + tower_spacing / 2;
|
|
|
|
|
|
|
|
int next_base = border_wall_anchor_base(ctx, side, next_anchor, wall_center);
|
|
|
|
|
|
|
|
int next_delta = next_base - current_base;
|
|
|
|
|
|
|
|
if (next_delta != 0) {
|
|
|
|
|
|
|
|
int next_span = clamp_int(abs(next_delta), 6, tower_spacing - 8);
|
|
|
|
|
|
|
|
int next_start = next_boundary - next_span / 2;
|
|
|
|
|
|
|
|
int next_end = next_start + next_span;
|
|
|
|
|
|
|
|
if (along >= next_start && along <= next_end) {
|
|
|
|
|
|
|
|
int progress = along - next_start;
|
|
|
|
|
|
|
|
return current_base + (next_delta * progress + (next_delta > 0 ? next_span / 2 : -next_span / 2)) / next_span;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int prev_anchor = anchor - tower_spacing;
|
|
|
|
|
|
|
|
int prev_boundary = anchor - tower_spacing / 2;
|
|
|
|
|
|
|
|
int prev_base = border_wall_anchor_base(ctx, side, prev_anchor, wall_center);
|
|
|
|
|
|
|
|
int prev_delta = current_base - prev_base;
|
|
|
|
|
|
|
|
if (prev_delta != 0) {
|
|
|
|
|
|
|
|
int prev_span = clamp_int(abs(prev_delta), 6, tower_spacing - 8);
|
|
|
|
|
|
|
|
int prev_start = prev_boundary - prev_span / 2;
|
|
|
|
|
|
|
|
int prev_end = prev_start + prev_span;
|
|
|
|
|
|
|
|
if (along >= prev_start && along <= prev_end) {
|
|
|
|
|
|
|
|
int progress = along - prev_start;
|
|
|
|
|
|
|
|
return prev_base + (prev_delta * progress + (prev_delta > 0 ? prev_span / 2 : -prev_span / 2)) / prev_span;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return current_base;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t border_wall_ramp_stair_block(int side, int slope) {
|
|
|
|
|
|
|
|
if (side < 2) {
|
|
|
|
|
|
|
|
return (slope > 0) ? BLOCK_STONE_BRICK_STAIRS_S : BLOCK_STONE_BRICK_STAIRS_N;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (slope > 0) ? BLOCK_STONE_BRICK_STAIRS_E : BLOCK_STONE_BRICK_STAIRS_W;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void generate_chunk_border_wall(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out) {
|
|
|
|
|
|
|
|
if (!ctx->enable_wall) return;
|
|
|
|
|
|
|
|
if (ctx->wall_min_x > ctx->wall_max_x || ctx->wall_min_z > ctx->wall_max_z) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int exterior_apron = 5;
|
|
|
|
|
|
|
|
const int wall_depth = 7;
|
|
|
|
|
|
|
|
const int wall_start = exterior_apron;
|
|
|
|
|
|
|
|
const int wall_end = wall_start + wall_depth - 1;
|
|
|
|
|
|
|
|
const int wall_center = wall_start + wall_depth / 2;
|
|
|
|
|
|
|
|
const int wall_height = 18;
|
|
|
|
|
|
|
|
const int tower_outer = 1;
|
|
|
|
|
|
|
|
const int tower_inner = wall_end + 5;
|
|
|
|
|
|
|
|
const int tower_depth = 9;
|
|
|
|
|
|
|
|
const int tower_height = 28;
|
|
|
|
|
|
|
|
const int tower_spacing = 32;
|
|
|
|
|
|
|
|
const int tower_half_width = 4;
|
|
|
|
|
|
|
|
const int ladder_spacing = 96;
|
|
|
|
|
|
|
|
for (int dx = 0; dx < CHUNK_SIZE; ++dx) {
|
|
|
|
|
|
|
|
for (int dz = 0; dz < CHUNK_SIZE; ++dz) {
|
|
|
|
|
|
|
|
int world_x = chunk_x * CHUNK_SIZE + dx;
|
|
|
|
|
|
|
|
int world_z = chunk_z * CHUNK_SIZE + dz;
|
|
|
|
|
|
|
|
if (world_x < ctx->wall_min_x || world_x > ctx->wall_max_x) continue;
|
|
|
|
|
|
|
|
if (world_z < ctx->wall_min_z || world_z > ctx->wall_max_z) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int dist_min_x = world_x - ctx->wall_min_x;
|
|
|
|
|
|
|
|
int dist_max_x = ctx->wall_max_x - world_x;
|
|
|
|
|
|
|
|
int dist_min_z = world_z - ctx->wall_min_z;
|
|
|
|
|
|
|
|
int dist_max_z = ctx->wall_max_z - world_z;
|
|
|
|
|
|
|
|
int side_dist = dist_min_x;
|
|
|
|
|
|
|
|
int along = world_z;
|
|
|
|
|
|
|
|
int side = 0;
|
|
|
|
|
|
|
|
if (dist_max_x < side_dist) {
|
|
|
|
|
|
|
|
side_dist = dist_max_x;
|
|
|
|
|
|
|
|
along = world_z;
|
|
|
|
|
|
|
|
side = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dist_min_z < side_dist) {
|
|
|
|
|
|
|
|
side_dist = dist_min_z;
|
|
|
|
|
|
|
|
along = world_x;
|
|
|
|
|
|
|
|
side = 2;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dist_max_z < side_dist) {
|
|
|
|
|
|
|
|
side_dist = dist_max_z;
|
|
|
|
|
|
|
|
along = world_x;
|
|
|
|
|
|
|
|
side = 3;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int phase = along % tower_spacing;
|
|
|
|
|
|
|
|
if (phase < 0) phase += tower_spacing;
|
|
|
|
|
|
|
|
int tower_offset = phase;
|
|
|
|
|
|
|
|
if (tower_offset > tower_spacing / 2) tower_offset = tower_spacing - tower_offset;
|
|
|
|
|
|
|
|
int ladder_phase = along % ladder_spacing;
|
|
|
|
|
|
|
|
if (ladder_phase < 0) ladder_phase += ladder_spacing;
|
|
|
|
|
|
|
|
int ladder_offset = ladder_phase - ladder_spacing / 2;
|
|
|
|
|
|
|
|
if (ladder_offset < 0) ladder_offset = -ladder_offset;
|
|
|
|
|
|
|
|
int near_corner = (dist_min_x < tower_depth || dist_max_x < tower_depth) &&
|
|
|
|
|
|
|
|
(dist_min_z < tower_depth || dist_max_z < tower_depth);
|
|
|
|
|
|
|
|
int in_tower = side_dist >= tower_outer && side_dist <= tower_inner &&
|
|
|
|
|
|
|
|
(tower_offset <= tower_half_width || near_corner);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
column_data data = get_column_data(ctx, world_x, world_z);
|
|
|
|
|
|
|
|
int visible_base = data.height;
|
|
|
|
|
|
|
|
if (data.has_water && data.water_surface > visible_base) {
|
|
|
|
|
|
|
|
visible_base = data.water_surface;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (visible_base >= CHUNK_HEIGHT - 1) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int dry_ladder_site = !(data.has_water && data.height < data.water_surface);
|
|
|
|
|
|
|
|
int ladder_module = !near_corner && dry_ladder_site && ladder_offset <= 1;
|
|
|
|
|
|
|
|
int ladder_face = ladder_module && ladder_offset == 0 && side_dist == wall_end + 1;
|
|
|
|
|
|
|
|
int ladder_trim = ladder_module && side_dist == wall_end + 1 && ladder_offset == 1;
|
|
|
|
|
|
|
|
int in_wall = (side_dist >= wall_start && side_dist <= wall_end) || in_tower || ladder_face || ladder_trim;
|
|
|
|
|
|
|
|
int in_exterior_apron = side_dist < wall_start && !in_wall;
|
|
|
|
|
|
|
|
if (!in_wall && !in_exterior_apron) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (in_exterior_apron) {
|
|
|
|
|
|
|
|
for (int y = data.height + 1; y < CHUNK_HEIGHT; ++y) {
|
|
|
|
|
|
|
|
out->blocks[y][dx][dz] = BLOCK_AIR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
out->heightmap[dx][dz] = (uint16_t)data.height;
|
|
|
|
|
|
|
|
uint32_t rubble_hash = hash_coords(world_x, world_z, (uint32_t)(ctx->world_seed ^ 0x4A7C9B2Du));
|
|
|
|
|
|
|
|
if (side_dist >= wall_start - 3 && (rubble_hash % 5) != 0) {
|
|
|
|
|
|
|
|
uint16_t rubble = (rubble_hash % 3 == 0) ? BLOCK_GRAVEL : ((rubble_hash % 3 == 1) ? BLOCK_STONE : BLOCK_STONE_BRICKS);
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, visible_base + 1, rubble);
|
|
|
|
|
|
|
|
if (side_dist == wall_start - 1 && rubble_hash % 7 == 0) {
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, visible_base + 2, BLOCK_STONE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int smoothed_base = border_wall_ramp_base(ctx, side, along, wall_center, tower_spacing);
|
|
|
|
|
|
|
|
int prev_base = border_wall_ramp_base(ctx, side, along - 1, wall_center, tower_spacing);
|
|
|
|
|
|
|
|
int next_base = border_wall_ramp_base(ctx, side, along + 1, wall_center, tower_spacing);
|
|
|
|
|
|
|
|
int stair_slope = 0;
|
|
|
|
|
|
|
|
if (next_base > smoothed_base) {
|
|
|
|
|
|
|
|
stair_slope = 1;
|
|
|
|
|
|
|
|
} else if (prev_base > smoothed_base) {
|
|
|
|
|
|
|
|
stair_slope = -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int height = in_tower ? tower_height : wall_height;
|
|
|
|
|
|
|
|
int top = smoothed_base + height;
|
|
|
|
|
|
|
|
if (top >= CHUNK_HEIGHT) top = CHUNK_HEIGHT - 1;
|
|
|
|
|
|
|
|
uint16_t ladder_block = BLOCK_LADDER_E;
|
|
|
|
|
|
|
|
if (side == 1) ladder_block = BLOCK_LADDER_W;
|
|
|
|
|
|
|
|
if (side == 2) ladder_block = BLOCK_LADDER_S;
|
|
|
|
|
|
|
|
if (side == 3) ladder_block = BLOCK_LADDER_N;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = data.height + 1; y < CHUNK_HEIGHT; ++y) {
|
|
|
|
|
|
|
|
out->blocks[y][dx][dz] = BLOCK_AIR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
out->heightmap[dx][dz] = (uint16_t)data.height;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int start = data.height - 4;
|
|
|
|
|
|
|
|
if (smoothed_base - 4 < start) start = smoothed_base - 4;
|
|
|
|
|
|
|
|
if (start < 1) start = 1;
|
|
|
|
|
|
|
|
for (int y = start; y <= top; ++y) {
|
|
|
|
|
|
|
|
int rel_y = y - smoothed_base;
|
|
|
|
|
|
|
|
uint16_t block = BLOCK_SMOOTH_STONE;
|
|
|
|
|
|
|
|
int in_center_corridor = side_dist >= wall_center - 1 && side_dist <= wall_center + 1 &&
|
|
|
|
|
|
|
|
rel_y >= 2 && rel_y <= 4;
|
|
|
|
|
|
|
|
int in_walltop_tower_passage = in_tower &&
|
|
|
|
|
|
|
|
side_dist >= wall_center - 1 && side_dist <= wall_center + 1 &&
|
|
|
|
|
|
|
|
rel_y >= wall_height + 1 && rel_y <= wall_height + 3;
|
|
|
|
|
|
|
|
if (in_center_corridor || in_walltop_tower_passage) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rel_y < 0) {
|
|
|
|
|
|
|
|
block = (rel_y == -1 || rel_y == -4) ? BLOCK_STONE_BRICKS : BLOCK_STONE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rel_y <= 2 || rel_y % 6 == 0 || rel_y == height - 1) {
|
|
|
|
|
|
|
|
block = BLOCK_STONE_BRICKS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (side_dist >= wall_center - 1 && side_dist <= wall_center + 1 && rel_y == height) {
|
|
|
|
|
|
|
|
block = BLOCK_STONE_BRICKS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ladder_face && rel_y >= 2 && rel_y <= height) {
|
|
|
|
|
|
|
|
if (rel_y == height) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
block = ladder_block;
|
|
|
|
|
|
|
|
} else if (ladder_trim && rel_y >= 2 && rel_y <= height - 1 && rel_y % 3 != 1) {
|
|
|
|
|
|
|
|
block = (rel_y % 6 == 0) ? BLOCK_BLACKSTONE : BLOCK_STONE_BRICKS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_tower && side_dist == tower_outer && rel_y >= 9 && rel_y <= 17 &&
|
|
|
|
|
|
|
|
(tower_offset == 0 || (rel_y == 13 && tower_offset <= 3))) {
|
|
|
|
|
|
|
|
block = BLOCK_BLACKSTONE;
|
|
|
|
|
|
|
|
} else if (in_tower && side_dist == tower_outer + 1 && tower_offset == 0 && rel_y >= 4 && rel_y <= 7) {
|
|
|
|
|
|
|
|
block = BLOCK_IRON_BARS;
|
|
|
|
|
|
|
|
} else if (!in_tower && side_dist == wall_start && rel_y >= 7 && rel_y <= 10 && phase % 16 == 8) {
|
|
|
|
|
|
|
|
block = BLOCK_IRON_BARS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, y, block);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top + 1 < CHUNK_HEIGHT) {
|
|
|
|
|
|
|
|
int rail_opening = ladder_module && side_dist == wall_end && ladder_offset <= 1;
|
|
|
|
|
|
|
|
if (side_dist >= wall_center - 1 && side_dist <= wall_center + 1 && stair_slope != 0) {
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, top + 1, border_wall_ramp_stair_block(side, stair_slope));
|
|
|
|
|
|
|
|
} else if (!in_tower && !rail_opening && (side_dist == wall_start || side_dist == wall_end)) {
|
|
|
|
|
|
|
|
uint16_t rail_block = (side < 2) ? BLOCK_IRON_BARS_NS : BLOCK_IRON_BARS_EW;
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, top + 1, rail_block);
|
|
|
|
|
|
|
|
} else if (in_tower && phase % 4 < 2) {
|
|
|
|
|
|
|
|
set_block_with_height(out, dx, dz, top + 1, BLOCK_STONE_BRICKS);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Public API
|
|
|
|
// Public API
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@@ -3399,12 +3961,16 @@ void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_li
|
|
|
|
ctx->sea_level = sea_level;
|
|
|
|
ctx->sea_level = sea_level;
|
|
|
|
ctx->snow_line = snow_line;
|
|
|
|
ctx->snow_line = snow_line;
|
|
|
|
ctx->enable_trails = 0;
|
|
|
|
ctx->enable_trails = 0;
|
|
|
|
|
|
|
|
ctx->enable_wall = 0;
|
|
|
|
|
|
|
|
ctx->wall_min_x = ctx->wall_max_x = 0;
|
|
|
|
|
|
|
|
ctx->wall_min_z = ctx->wall_max_z = 0;
|
|
|
|
ctx->trail_segments = NULL;
|
|
|
|
ctx->trail_segments = NULL;
|
|
|
|
ctx->trail_segment_count = 0;
|
|
|
|
ctx->trail_segment_count = 0;
|
|
|
|
ctx->trail_segment_cap = 0;
|
|
|
|
ctx->trail_segment_cap = 0;
|
|
|
|
ctx->prepass_done = 0;
|
|
|
|
ctx->prepass_done = 0;
|
|
|
|
ctx->prepass_min_x = ctx->prepass_max_x = 0;
|
|
|
|
ctx->prepass_min_x = ctx->prepass_max_x = 0;
|
|
|
|
ctx->prepass_min_z = ctx->prepass_max_z = 0;
|
|
|
|
ctx->prepass_min_z = ctx->prepass_max_z = 0;
|
|
|
|
|
|
|
|
memset(ctx->height_cache, 0, sizeof(ctx->height_cache));
|
|
|
|
simplex_init(&ctx->noise, (uint32_t)world_seed);
|
|
|
|
simplex_init(&ctx->noise, (uint32_t)world_seed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3412,6 +3978,13 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
out->chunk_x = chunk_x;
|
|
|
|
out->chunk_x = chunk_x;
|
|
|
|
out->chunk_z = chunk_z;
|
|
|
|
out->chunk_z = chunk_z;
|
|
|
|
|
|
|
|
for (int y = 0; y < CHUNK_HEIGHT; ++y) {
|
|
|
|
|
|
|
|
for (int dx = 0; dx < CHUNK_SIZE; ++dx) {
|
|
|
|
|
|
|
|
for (int dz = 0; dz < CHUNK_SIZE; ++dz) {
|
|
|
|
|
|
|
|
out->blocks[y][dx][dz] = BLOCK_AIR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ensure_trail_prepass(ctx, chunk_x, chunk_z);
|
|
|
|
ensure_trail_prepass(ctx, chunk_x, chunk_z);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3432,26 +4005,37 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
|
|
|
|
column_data cd = columns[dx][dz];
|
|
|
|
column_data cd = columns[dx][dz];
|
|
|
|
int world_x = chunk_x * CHUNK_SIZE + dx;
|
|
|
|
int world_x = chunk_x * CHUNK_SIZE + dx;
|
|
|
|
int world_z = chunk_z * CHUNK_SIZE + dz;
|
|
|
|
int world_z = chunk_z * CHUNK_SIZE + dz;
|
|
|
|
for (int y = 0; y < CHUNK_HEIGHT; ++y) {
|
|
|
|
out->blocks[0][dx][dz] = BLOCK_BEDROCK;
|
|
|
|
int id;
|
|
|
|
int stone_top = cd.height - 3;
|
|
|
|
if (y == 0) {
|
|
|
|
if (stone_top > CHUNK_HEIGHT) stone_top = CHUNK_HEIGHT;
|
|
|
|
id = BLOCK_BEDROCK;
|
|
|
|
for (int y = 1; y < stone_top; ++y) {
|
|
|
|
} else if (y < cd.height - 3) {
|
|
|
|
|
|
|
|
if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) {
|
|
|
|
if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) {
|
|
|
|
id = BLOCK_COAL;
|
|
|
|
out->blocks[y][dx][dz] = BLOCK_COAL;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
id = (int)generate_normal_ores(ctx, world_x, y, world_z, &cd);
|
|
|
|
out->blocks[y][dx][dz] = generate_normal_ores(ctx, world_x, y, world_z, &cd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (y < cd.height) {
|
|
|
|
|
|
|
|
id = BLOCK_DIRT;
|
|
|
|
|
|
|
|
} else if (y == cd.height) {
|
|
|
|
|
|
|
|
id = select_surface_block(ctx, &cd, world_x, world_z);
|
|
|
|
|
|
|
|
} else if (cd.has_water && y <= cd.water_surface && y > cd.height) {
|
|
|
|
|
|
|
|
id = BLOCK_WATER;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
id = BLOCK_AIR;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out->blocks[y][dx][dz] = (uint16_t)id;
|
|
|
|
int dirt_start = stone_top > 1 ? stone_top : 1;
|
|
|
|
|
|
|
|
for (int y = dirt_start; y < cd.height && y < CHUNK_HEIGHT; ++y) {
|
|
|
|
|
|
|
|
out->blocks[y][dx][dz] = BLOCK_DIRT;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cd.height >= 0 && cd.height < CHUNK_HEIGHT) {
|
|
|
|
|
|
|
|
uint16_t surface = select_surface_block(ctx, &cd, world_x, world_z);
|
|
|
|
|
|
|
|
if (surface == BLOCK_SAND) {
|
|
|
|
|
|
|
|
int sand_bottom = cd.height - 2;
|
|
|
|
|
|
|
|
if (sand_bottom < dirt_start) sand_bottom = dirt_start;
|
|
|
|
|
|
|
|
for (int y = sand_bottom; y < cd.height; ++y) {
|
|
|
|
|
|
|
|
if (y > 0 && y < CHUNK_HEIGHT) out->blocks[y][dx][dz] = BLOCK_SAND;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
out->blocks[cd.height][dx][dz] = surface;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cd.has_water) {
|
|
|
|
|
|
|
|
int water_top = cd.water_surface;
|
|
|
|
|
|
|
|
if (water_top >= CHUNK_HEIGHT) water_top = CHUNK_HEIGHT - 1;
|
|
|
|
|
|
|
|
for (int y = cd.height + 1; y <= water_top; ++y) {
|
|
|
|
|
|
|
|
if (y > 0) out->blocks[y][dx][dz] = BLOCK_WATER;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -3463,6 +4047,7 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
|
|
|
|
generate_chunk_redwood_floor(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
generate_chunk_redwood_floor(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
generate_chunk_grass(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
generate_chunk_grass(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
generate_chunk_flowers(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
generate_chunk_flowers(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
|
|
|
|
generate_chunk_aquatic_life(ctx, chunk_x, chunk_z, columns, out);
|
|
|
|
|
|
|
|
|
|
|
|
// Tree overlay
|
|
|
|
// Tree overlay
|
|
|
|
block_list trees;
|
|
|
|
block_list trees;
|
|
|
|
@@ -3477,4 +4062,6 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
block_list_free(&trees);
|
|
|
|
block_list_free(&trees);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generate_chunk_border_wall(ctx, chunk_x, chunk_z, out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|