Added Pale Garden biome and Small End Islands

* added pale_garden biome with 1.21.3 (version TBA)
* added small end islands and better end surface generation (#117)
* added linked end gateway finder (#117)
* fixed inaccurate End generation at large distances from 0,0
* fixed incorrect biome in outer end rings
* fixed incorrect biome area size (#122)
This commit is contained in:
Cubitect 2024-10-07 13:20:00 +02:00
parent 0af31b4e7e
commit e49c8c561b
15 changed files with 3447 additions and 567 deletions

View File

@ -74,9 +74,11 @@ double sampleSurfaceNoiseBetween(const SurfaceNoise *sn, int x, int y, int z,
dy = y * sy;
vmin += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, dy) * amp;
if (vmin - amp > noiseMax) return noiseMax;
vmax += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, dy) * amp;
if (vmax + amp < noiseMin) return noiseMin;
if (vmin - amp > noiseMax && vmax - amp > noiseMax)
return noiseMax;
if (vmin + amp < noiseMin && vmax + amp < noiseMin)
return noiseMin;
amp *= 0.5;
persist *= 2.0;
@ -436,7 +438,10 @@ int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h)
uint16_t v = 0;
if (rsq > 4096 && sampleSimplex2D(&en->perlin, rx, rz) < -0.9f)
{
v = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9;
//v = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9;
v = (unsigned int)(
fabsf((float)rx) * 3439.0f + fabsf((float)rz) * 147.0f
) % 13 + 9;
v *= v;
}
hmap[(int64_t)j*hw+i] = v;
@ -457,12 +462,12 @@ int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h)
{
hx = 2*hx + 1;
hz = 2*hz + 1;
if (en->mc >= MC_1_14)
{
if (en->mc > MC_1_13)
{ // add outer end rings
rsq = hx * hx + hz * hz;
if ((int)rsq < 0)
{
out[j*w+i] = small_end_islands;
out[j*w+i] = end_barrens;
continue;
}
}
@ -529,7 +534,10 @@ float getEndHeightNoise(const EndNoise *en, int x, int z, int range)
uint16_t v = 0;
if (rsq > 4096 && sampleSimplex2D(&en->perlin, rx, rz) < -0.9f)
{
v = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9;
//v = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9;
v = (unsigned int)(
fabsf((float)rx) * 3439.0f + fabsf((float)rz) * 147.0f
) % 13 + 9;
rx = (oddx - i * 2);
rz = (oddz - j * 2);
rsq = rx*rx + rz*rz;
@ -546,28 +554,9 @@ float getEndHeightNoise(const EndNoise *en, int x, int z, int range)
return ret;
}
#define END_NOISE_COL_YMIN 2
#define END_NOISE_COL_YMAX 18
#define END_NOISE_COL_SIZE (END_NOISE_COL_YMAX - END_NOISE_COL_YMIN + 1)
void sampleNoiseColumnEnd(double column[], const SurfaceNoise *sn,
const EndNoise *en, int x, int z, int colymin, int colymax)
{
double depth = getEndHeightNoise(en, x, z, 0) - 8.0f;
int y;
for (y = colymin; y <= colymax; y++)
{
double noise = sampleSurfaceNoise(sn, x, y, z);
noise += depth; // falloff for the End is just the depth
// clamp top and bottom slides from End settings
noise = clampedLerp((32 + 46 - y) / 64.0, -3000, noise);
noise = clampedLerp((y - 1) / 7.0, -30, noise);
column[y - colymin] = noise;
}
}
void sampleNoiseColumnEndFull(double column[END_NOISE_COL_SIZE],
const SurfaceNoise *sn, const EndNoise *en, int x, int z)
void sampleNoiseColumnEnd(double column[],
const SurfaceNoise *sn, const EndNoise *en, int x, int z,
int colymin, int colymax)
{
// clamped (32 + 46 - y) / 64.0
static const double upper_drop[] = {
@ -583,9 +572,21 @@ void sampleNoiseColumnEndFull(double column[END_NOISE_COL_SIZE],
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 8-15
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 16-23
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 24-31
1.0 // 32
1.0, // 32
};
int y;
if (en->mc > MC_1_13)
{ // add outer end rings
uint64_t rsq = (uint64_t) x * x + (uint64_t) z * z;
if ((int)rsq < 0)
{
for (y = colymin; y <= colymax; y++)
column[y - colymin] = nan("");
return;
}
}
// depth is between [-108, +72]
// noise is between [-128, +128]
// for a sold block we need the upper drop as:
@ -595,23 +596,18 @@ void sampleNoiseColumnEndFull(double column[END_NOISE_COL_SIZE],
// (72 + 128) * l - 30 * (1-l) > 0 => lower_drop = l > 3/23
// which occurs at y = 3 for the lowest relevant noise cell
// in terms of the depth this becomes:
// l > 30 / (103 + depth)
double depth = getEndHeightNoise(en, x, z, 0) - 8.0f;
int y;
for (y = END_NOISE_COL_YMIN; y <= END_NOISE_COL_YMAX; y++)
for (y = colymin; y <= colymax; y++)
{
if (lower_drop[y] * (103.0 + depth) < 30)
{
column[y - END_NOISE_COL_YMIN] = -30;
if (lower_drop[y] == 0.0) {
column[y - colymin] = -30;
continue;
}
double noise = sampleSurfaceNoiseBetween(sn, x, y, z, -128, 128);
double noise = sampleSurfaceNoiseBetween(sn, x, y, z, -128, +128);
double clamped = noise + depth;
clamped = lerp(upper_drop[y], -3000, clamped);
clamped = lerp(lower_drop[y], -30, clamped);
column[y - END_NOISE_COL_YMIN] = clamped;
column[y - colymin] = clamped;
}
}
@ -650,7 +646,7 @@ int getSurfaceHeight(
return 0;
}
int getSurfaceHeightEnd(int mc, uint64_t seed, int x, int z)
int getEndSurfaceHeight(int mc, uint64_t seed, int x, int z)
{
EndNoise en;
setEndSeed(&en, mc, seed);
@ -678,14 +674,17 @@ int getSurfaceHeightEnd(int mc, uint64_t seed, int x, int z)
return getSurfaceHeight(ncol00, ncol01, ncol10, ncol11, y0, y1, 4, dx, dz);
}
int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int x, int z, int w, int h, int scale)
int mapEndSurfaceHeight(float *y, const EndNoise *en, const SurfaceNoise *sn,
int x, int z, int w, int h, int scale, int ymin)
{
if (scale != 1 && scale != 2 && scale != 4 && scale != 8)
return 1;
enum { YSIZ = END_NOISE_COL_SIZE };
int y0 = ymin >> 2;
if (y0 < 2) y0 = 2;
if (y0 > 17) y0 = 17;
int y1 = 18;
int yn = y1 - y0 + 1;
double cellmid = scale > 1 ? scale / 16.0 : 0;
int cellsiz = 8 / scale;
int cx = floordiv(x, cellsiz);
@ -693,13 +692,13 @@ int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int cw = floordiv(x + w - 1, cellsiz) - cx + 2;
int i, j;
double *buf = malloc(sizeof(double) * YSIZ * cw * 2);
double *buf = malloc(sizeof(double) * yn * cw * 2);
double *ncol[2];
ncol[0] = buf;
ncol[1] = buf + YSIZ * cw;
ncol[1] = buf + yn * cw;
for (i = 0; i < cw; i++)
sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cz+0);
sampleNoiseColumnEnd(ncol[1]+i*yn, sn, en, cx+i, cz+0, y0, y1);
for (j = 0; j < h; j++)
{
@ -711,7 +710,7 @@ int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
ncol[0] = ncol[1];
ncol[1] = tmp;
for (i = 0; i < cw; i++)
sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cj+1);
sampleNoiseColumnEnd(ncol[1]+i*yn, sn, en, cx+i, cj+1, y0, y1);
}
for (i = 0; i < w; i++)
@ -720,10 +719,10 @@ int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int di = x + i - ci * cellsiz;
double dx = di / (double) cellsiz + cellmid;
double dz = dj / (double) cellsiz + cellmid;
double *ncol0 = ncol[0] + (ci - cx) * YSIZ;
double *ncol1 = ncol[1] + (ci - cx) * YSIZ;
y[j*w+i] = getSurfaceHeight(ncol0, ncol1, ncol0+YSIZ, ncol1+YSIZ,
END_NOISE_COL_YMIN, END_NOISE_COL_YMAX, 4, dx, dz);
double *ncol0 = ncol[0] + (ci - cx) * yn;
double *ncol1 = ncol[1] + (ci - cx) * yn;
y[j*w+i] = getSurfaceHeight(ncol0, ncol1, ncol0+yn, ncol1+yn,
y0, y1, 4, dx, dz);
}
}
@ -802,9 +801,9 @@ int genEndScaled(const EndNoise *en, int *out, Range r, int mc, uint64_t sha)
out[j*r.sx+i] = the_end;
continue;
}
else if (mc >= MC_1_14 && (int)(rsq) < 0)
else if (mc > MC_1_13 && (int)(rsq) < 0)
{
out[j*r.sx+i] = small_end_islands;
out[j*r.sx+i] = end_barrens;
continue;
}
float h = getEndHeightNoise(en, hx, hz, 4);

View File

@ -175,6 +175,8 @@ extern BiomeTree g_btree[MC_NEWEST - MC_1_18 + 1];
void initSurfaceNoise(SurfaceNoise *sn, int dim, uint64_t seed);
void initSurfaceNoiseBeta(SurfaceNoiseBeta *snb, uint64_t seed);
double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z);
double sampleSurfaceNoiseBetween(const SurfaceNoise *sn, int x, int y, int z,
double noiseMin, double noiseMax);
//==============================================================================
@ -222,9 +224,9 @@ int genNetherScaled(const NetherNoise *nn, int *out, Range r, int mc, uint64_t s
void setEndSeed(EndNoise *en, int mc, uint64_t seed);
int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h);
int mapEnd(const EndNoise *en, int *out, int x, int z, int w, int h);
int getSurfaceHeightEnd(int mc, uint64_t seed, int x, int z);
int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int x, int z, int w, int h, int scale);
int getEndSurfaceHeight(int mc, uint64_t seed, int x, int z);
int mapEndSurfaceHeight(float *y, const EndNoise *en, const SurfaceNoise *sn,
int x, int z, int w, int h, int scale, int ymin);
/**
* The scaled End generation supports scales 1, 4, 16, and 64.
@ -307,6 +309,8 @@ int genBiomeNoiseBetaScaled(const BiomeNoiseBeta *bnb, const SurfaceNoiseBeta *s
int *out, Range r);
int getBiomeDepthAndScale(int id, double *depth, double *scale, int *grass);
// Gets the range in the parent/source layer which may be accessed by voronoi.
Range getVoronoiSrcRange(Range r);

455
biomes.c Normal file
View File

@ -0,0 +1,455 @@
#include "biomes.h"
#include <inttypes.h>
int biomeExists(int mc, int id)
{
if (mc >= MC_1_18)
{
if (id >= soul_sand_valley && id <= basalt_deltas)
return 1;
if (id >= small_end_islands && id <= end_barrens)
return 1;
if (id == pale_garden)
return mc >= MC_1_21_3;
if (id == cherry_grove)
return mc >= MC_1_20;
if (id == deep_dark || id == mangrove_swamp)
return mc >= MC_1_19_2;
switch (id)
{
case ocean:
case plains:
case desert:
case mountains: // windswept_hills
case forest:
case taiga:
case swamp:
case river:
case nether_wastes:
case the_end:
case frozen_ocean:
case frozen_river:
case snowy_tundra: // snowy_plains
case mushroom_fields:
case beach:
case jungle:
case jungle_edge: // sparse_jungle
case deep_ocean:
case stone_shore: // stony_shore
case snowy_beach:
case birch_forest:
case dark_forest:
case snowy_taiga:
case giant_tree_taiga: // old_growth_pine_taiga
case wooded_mountains: // windswept_forest
case savanna:
case savanna_plateau:
case badlands:
case wooded_badlands_plateau: // wooded_badlands
case warm_ocean:
case lukewarm_ocean:
case cold_ocean:
case deep_warm_ocean:
case deep_lukewarm_ocean:
case deep_cold_ocean:
case deep_frozen_ocean:
case sunflower_plains:
case gravelly_mountains: // windswept_gravelly_hills
case flower_forest:
case ice_spikes:
case tall_birch_forest: // old_growth_birch_forest
case giant_spruce_taiga: // old_growth_spruce_taiga
case shattered_savanna: // windswept_savanna
case eroded_badlands:
case bamboo_jungle:
case dripstone_caves:
case lush_caves:
case meadow:
case grove:
case snowy_slopes:
case stony_peaks:
case jagged_peaks:
case frozen_peaks:
return 1;
default:
return 0;
}
}
if (mc <= MC_B1_7)
{
switch(id)
{
case plains:
case desert:
case forest:
case taiga:
case swamp:
case snowy_tundra:
case savanna:
case seasonal_forest:
case rainforest:
case shrubland:
// we treat areas below the sea level as oceans
case ocean:
case frozen_ocean:
return 1;
default:
return 0;
}
}
if (mc <= MC_B1_8)
{
switch (id)
{
case frozen_ocean:
case frozen_river:
case snowy_tundra:
case mushroom_fields:
case mushroom_field_shore:
case the_end:
return 0;
}
}
if (mc <= MC_1_0)
{
switch (id)
{
case snowy_mountains:
case beach:
case desert_hills:
case wooded_hills:
case taiga_hills:
case mountain_edge:
return 0;
}
}
if (id >= ocean && id <= mountain_edge) return 1;
if (id >= jungle && id <= jungle_hills) return mc >= MC_1_2;
if (id >= jungle_edge && id <= badlands_plateau) return mc >= MC_1_7;
if (id >= small_end_islands && id <= end_barrens) return mc >= MC_1_9;
if (id >= warm_ocean && id <= deep_frozen_ocean) return mc >= MC_1_13;
switch (id)
{
case the_void:
return mc >= MC_1_9;
case sunflower_plains:
case desert_lakes:
case gravelly_mountains:
case flower_forest:
case taiga_mountains:
case swamp_hills:
case ice_spikes:
case modified_jungle:
case modified_jungle_edge:
case tall_birch_forest:
case tall_birch_hills:
case dark_forest_hills:
case snowy_taiga_mountains:
case giant_spruce_taiga:
case giant_spruce_taiga_hills:
case modified_gravelly_mountains:
case shattered_savanna:
case shattered_savanna_plateau:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
return mc >= MC_1_7;
case bamboo_jungle:
case bamboo_jungle_hills:
return mc >= MC_1_14;
case soul_sand_valley:
case crimson_forest:
case warped_forest:
case basalt_deltas:
return mc >= MC_1_16_1;
case dripstone_caves:
case lush_caves:
return mc >= MC_1_17;
default:
return 0;
}
}
int isOverworld(int mc, int id)
{
if (!biomeExists(mc, id))
return 0;
if (id >= small_end_islands && id <= end_barrens) return 0;
if (id >= soul_sand_valley && id <= basalt_deltas) return 0;
switch (id)
{
case nether_wastes:
case the_end:
return 0;
case frozen_ocean:
return mc <= MC_1_6 || mc >= MC_1_13;
case mountain_edge:
return mc <= MC_1_6;
case deep_warm_ocean:
case the_void:
return 0;
case tall_birch_forest:
return mc <= MC_1_8 || mc >= MC_1_11;
case dripstone_caves:
case lush_caves:
return mc >= MC_1_18;
}
return 1;
}
int getDimension(int id)
{
if (id >= small_end_islands && id <= end_barrens) return DIM_END;
if (id >= soul_sand_valley && id <= basalt_deltas) return DIM_NETHER;
if (id == the_end) return DIM_END;
if (id == nether_wastes) return DIM_NETHER;
return DIM_OVERWORLD;
}
int getMutated(int mc, int id)
{
switch (id)
{
case plains: return sunflower_plains;
case desert: return desert_lakes;
case mountains: return gravelly_mountains;
case forest: return flower_forest;
case taiga: return taiga_mountains;
case swamp: return swamp_hills;
case snowy_tundra: return ice_spikes;
case jungle: return modified_jungle;
case jungle_edge: return modified_jungle_edge;
// emulate MC-98995
case birch_forest:
return (mc >= MC_1_9 && mc <= MC_1_10) ? tall_birch_hills : tall_birch_forest;
case birch_forest_hills:
return (mc >= MC_1_9 && mc <= MC_1_10) ? none : tall_birch_hills;
case dark_forest: return dark_forest_hills;
case snowy_taiga: return snowy_taiga_mountains;
case giant_tree_taiga: return giant_spruce_taiga;
case giant_tree_taiga_hills: return giant_spruce_taiga_hills;
case wooded_mountains: return modified_gravelly_mountains;
case savanna: return shattered_savanna;
case savanna_plateau: return shattered_savanna_plateau;
case badlands: return eroded_badlands;
case wooded_badlands_plateau: return modified_wooded_badlands_plateau;
case badlands_plateau: return modified_badlands_plateau;
default:
return none;
}
}
int getCategory(int mc, int id)
{
switch (id)
{
case beach:
case snowy_beach:
return beach;
case desert:
case desert_hills:
case desert_lakes:
return desert;
case mountains:
case mountain_edge:
case wooded_mountains:
case gravelly_mountains:
case modified_gravelly_mountains:
return mountains;
case forest:
case wooded_hills:
case birch_forest:
case birch_forest_hills:
case dark_forest:
case flower_forest:
case tall_birch_forest:
case tall_birch_hills:
case dark_forest_hills:
return forest;
case snowy_tundra:
case snowy_mountains:
case ice_spikes:
return snowy_tundra;
case jungle:
case jungle_hills:
case jungle_edge:
case modified_jungle:
case modified_jungle_edge:
case bamboo_jungle:
case bamboo_jungle_hills:
return jungle;
case badlands:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
return mesa;
case wooded_badlands_plateau:
case badlands_plateau:
return mc <= MC_1_15 ? mesa : badlands_plateau;
case mushroom_fields:
case mushroom_field_shore:
return mushroom_fields;
case stone_shore:
return stone_shore;
case ocean:
case frozen_ocean:
case deep_ocean:
case warm_ocean:
case lukewarm_ocean:
case cold_ocean:
case deep_warm_ocean:
case deep_lukewarm_ocean:
case deep_cold_ocean:
case deep_frozen_ocean:
return ocean;
case plains:
case sunflower_plains:
return plains;
case river:
case frozen_river:
return river;
case savanna:
case savanna_plateau:
case shattered_savanna:
case shattered_savanna_plateau:
return savanna;
case swamp:
case swamp_hills:
return swamp;
case taiga:
case taiga_hills:
case snowy_taiga:
case snowy_taiga_hills:
case giant_tree_taiga:
case giant_tree_taiga_hills:
case taiga_mountains:
case snowy_taiga_mountains:
case giant_spruce_taiga:
case giant_spruce_taiga_hills:
return taiga;
case nether_wastes:
case soul_sand_valley:
case crimson_forest:
case warped_forest:
case basalt_deltas:
return nether_wastes;
default:
return none;
}
}
int areSimilar(int mc, int id1, int id2)
{
if (id1 == id2) return 1;
if (mc <= MC_1_15)
{
if (id1 == wooded_badlands_plateau || id1 == badlands_plateau)
return id2 == wooded_badlands_plateau || id2 == badlands_plateau;
}
return getCategory(mc, id1) == getCategory(mc, id2);
}
int isMesa(int id)
{
switch (id)
{
case badlands:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
case wooded_badlands_plateau:
case badlands_plateau:
return 1;
default:
return 0;
}
}
int isShallowOcean(int id)
{
const uint64_t shallow_bits =
(1ULL << ocean) |
(1ULL << frozen_ocean) |
(1ULL << warm_ocean) |
(1ULL << lukewarm_ocean) |
(1ULL << cold_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & shallow_bits);
}
int isDeepOcean(int id)
{
const uint64_t deep_bits =
(1ULL << deep_ocean) |
(1ULL << deep_warm_ocean) |
(1ULL << deep_lukewarm_ocean) |
(1ULL << deep_cold_ocean) |
(1ULL << deep_frozen_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & deep_bits);
}
int isOceanic(int id)
{
const uint64_t ocean_bits =
(1ULL << ocean) |
(1ULL << frozen_ocean) |
(1ULL << warm_ocean) |
(1ULL << lukewarm_ocean) |
(1ULL << cold_ocean) |
(1ULL << deep_ocean) |
(1ULL << deep_warm_ocean) |
(1ULL << deep_lukewarm_ocean) |
(1ULL << deep_cold_ocean) |
(1ULL << deep_frozen_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & ocean_bits);
}
int isSnowy(int id)
{
switch (id)
{
case frozen_ocean:
case frozen_river:
case snowy_tundra:
case snowy_mountains:
case snowy_beach:
case snowy_taiga:
case snowy_taiga_hills:
case ice_spikes:
case snowy_taiga_mountains:
return 1;
default:
return 0;
}
}

View File

@ -30,10 +30,12 @@ enum MCVersion
MC_1_17_1, MC_1_17 = MC_1_17_1,
MC_1_18_2, MC_1_18 = MC_1_18_2,
MC_1_19_2,
MC_1_19, // 1.19.3 - 1.19.4
MC_1_20,
MC_1_21,
MC_NEWEST = MC_1_21,
MC_1_19_4, MC_1_19 = MC_1_19_4,
MC_1_20_6, MC_1_20 = MC_1_20_6,
MC_1_21_2,
MC_1_21_3, // (Winter Drop version TBA)
MC_1_21 = MC_1_21_2,
MC_NEWEST = MC_1_21_3,
};
enum Dimension
@ -168,8 +170,35 @@ enum BiomeID
mangrove_swamp = 184,
// 1.20
cherry_grove = 185,
// 1.21.3
pale_garden = 186,
};
#ifdef __cplusplus
extern "C"
{
#endif
//==============================================================================
// BiomeID Helper Functions
//==============================================================================
int biomeExists(int mc, int id);
int isOverworld(int mc, int id); // false for biomes that don't generate
int getDimension(int id);
int getMutated(int mc, int id);
int getCategory(int mc, int id);
int areSimilar(int mc, int id1, int id2);
int isMesa(int id);
int isShallowOcean(int id);
int isDeepOcean(int id);
int isOceanic(int id);
int isSnowy(int id);
#ifdef __cplusplus
}
#endif
#endif /* BIOMES_H_ */

View File

@ -4,23 +4,27 @@
#include "tables/btree192.h"
#include "tables/btree19.h"
#include "tables/btree20.h"
#include "tables/btree213.h"
BiomeTree g_btree[MC_NEWEST - MC_1_18 + 1] =
BiomeTree g_btree[MC_NEWEST - MC_1_18 + 1] =
{
// MC_1_18 (== MC_1_18_2)
// MC_1_18_2
{ btree18_steps, &btree18_param[0][0], btree18_nodes, btree18_order,
sizeof(btree18_nodes) / sizeof(uint64_t) },
// MC_1_19_2
{ btree192_steps, &btree192_param[0][0], btree192_nodes, btree192_order,
sizeof(btree192_nodes) / sizeof(uint64_t) },
// MC_1_19
// MC_1_19_4
{ btree19_steps, &btree19_param[0][0], btree19_nodes, btree19_order,
sizeof(btree19_nodes) / sizeof(uint64_t) },
// MC_1_20
// MC_1_20_6
{ btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order,
sizeof(btree20_nodes) / sizeof(uint64_t) },
// MC_1_21
// MC_1_21_2
{ btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order,
sizeof(btree20_nodes) / sizeof(uint64_t) },
// MC_1_21_3
{ btree213_steps, &btree213_param[0][0], btree213_nodes, btree213_order,
sizeof(btree213_nodes) / sizeof(uint64_t) },
};

421
finders.c
View File

@ -1,4 +1,5 @@
#include "finders.h"
#include "biomes.h"
#include <stdio.h>
#include <string.h>
@ -102,7 +103,7 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
s_end_gateway_116 = { 40013, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 700},
s_end_gateway_117 = { 40013, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 1.f/700},
s_end_gateway = { 40000, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 1.f/700},
s_end_island_115 = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 14},
s_end_island_116 = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 14},
s_end_island = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 1.f/14}
;
@ -174,7 +175,7 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
// the block filling, making them much more difficult to predict
return mc >= MC_1_13;
case End_Island:
if (mc <= MC_1_15) *sconf = s_end_island_115;
if (mc <= MC_1_16) *sconf = s_end_island_116;
else *sconf = s_end_island;
return mc >= MC_1_13; // we only support decorator features for 1.13+
case Desert_Well:
@ -385,7 +386,249 @@ int getMineshafts(int mc, uint64_t seed, int cx0, int cz0, int cx1, int cz1,
return n;
}
int getEndIslands(EndIsland islands[2], int mc, uint64_t seed, int chunkX, int chunkZ)
{
StructureConfig sconf;
if (!getStructureConfig(End_Island, mc, &sconf))
return 0;
int x = chunkX * 16;
int z = chunkZ * 16;
uint64_t rng = getPopulationSeed(mc, seed, x, z);
Xoroshiro xr;
float r;
if (mc <= MC_1_16)
{
setSeed(&rng, rng + sconf.salt);
if (nextInt(&rng, (int)sconf.rarity) != 0)
return 0;
islands[0].x = nextInt(&rng, 16) + x;
islands[0].y = nextInt(&rng, 16) + 55;
islands[0].z = nextInt(&rng, 16) + z;
if (nextInt(&rng, 4) != 0)
{
islands[0].r = nextInt(&rng, 3) + 4;
return 1;
}
islands[1].x = nextInt(&rng, 16) + x;
islands[1].y = nextInt(&rng, 16) + 55;
islands[1].z = nextInt(&rng, 16) + z;
islands[0].r = nextInt(&rng, 3) + 4;
for (r = islands[0].r; r > 0.5; r -= nextInt(&rng, 2) + 0.5);
islands[1].r = nextInt(&rng, 3) + 4;
return 2;
}
else if (mc <= MC_1_17)
{
setSeed(&rng, rng + sconf.salt);
if (nextFloat(&rng) >= sconf.rarity)
return 0;
int second = nextInt(&rng, 4) == 0;
islands[0].x = nextInt(&rng, 16) + x;
islands[0].z = nextInt(&rng, 16) + z;
islands[0].y = nextInt(&rng, 16) + 55;
islands[0].r = nextInt(&rng, 3) + 4;
for (r = islands[0].r; r > 0.5; r -= nextInt(&rng, 2) + 0.5);
if (!second)
return 1;
islands[1].x = nextInt(&rng, 16) + x;
islands[1].z = nextInt(&rng, 16) + z;
islands[1].y = nextInt(&rng, 16) + 55;
islands[1].r = nextInt(&rng, 3) + 4;
return 2;
}
else
{
xSetSeed(&xr, rng + sconf.salt);
if (xNextFloat(&xr) >= sconf.rarity)
return 0;
int second = (xNextIntJ(&xr, 4) == 3);
islands[0].x = xNextIntJ(&xr, 16) + x;
islands[0].z = xNextIntJ(&xr, 16) + z;
islands[0].y = xNextIntJ(&xr, 16) + 55;
islands[0].r = xNextIntJ(&xr, 3) + 4;
if (!second)
return 1;
for (r = islands[0].r; r > 0.5; r -= xNextIntJ(&xr, 2) + 0.5);
islands[1].x = xNextIntJ(&xr, 16) + x;
islands[1].z = xNextIntJ(&xr, 16) + z;
islands[1].y = xNextIntJ(&xr, 16) + 55;
islands[1].r = xNextIntJ(&xr, 3) + 4;
return 2;
}
}
static void applyEndIslandHeight(float *y, const EndIsland *island,
int x, int z, int w, int h, int scale)
{
int r = island->r;
int r2 = (r + 1) * (r + 1);
int x0 = floordiv(island->x - r, scale);
int z0 = floordiv(island->z - r, scale);
int x1 = floordiv(island->x + r, scale);
int z1 = floordiv(island->z + r, scale);
int ds = 0;
int i, j;
for (j = z0; j <= z1; j++)
{
if (j < z || j >= z+h)
continue;
int dz = j * scale - island->z + ds;
for (i = x0; i <= x1; i++)
{
if (i < x || i >= x+w)
continue;
int dx = i * scale - island->x + ds;
if (dx*dx + dz*dz > r2)
continue;
int idx = (j - z) * w + (i - x);
if (y[idx] < island->y)
y[idx] = island->y;
}
}
}
int mapEndIslandHeight(float *y, const EndNoise *en, uint64_t seed,
int x, int z, int w, int h, int scale)
{
int rmax = (6 + scale - 1) / scale;
int cx = floordiv(x - rmax, 16 / scale);
int cz = floordiv(z - rmax, 16 / scale);
int cw = floordiv(x + w + rmax, 16 / scale) - cx + 1;
int ch = floordiv(z + h + rmax, 16 / scale) - cz + 1;
int ci, cj;
int *ids = (int*) malloc(sizeof(int) * cw * ch);
mapEndBiome(en, ids, cx, cz, cw, ch);
for (cj = 0; cj < ch; cj++)
{
for (ci = 0; ci < cw; ci++)
{
if (ids[cj*cw + ci] != small_end_islands)
continue;
EndIsland islands[2];
int n = getEndIslands(islands, en->mc, seed, cx+ci, cz+cj);
while (n --> 0)
applyEndIslandHeight(y, islands+n, x, z, w, h, scale);
}
}
free(ids);
return 0;
}
float getEndHeightNoise(const EndNoise *en, int x, int z, int range);
int isEndChunkEmpty(const EndNoise *en, const SurfaceNoise *sn, uint64_t seed,
int chunkX, int chunkZ)
{
int i, j, k;
int x = chunkX * 2;
int z = chunkZ * 2;
double depth[3][3];
float y[256];
// check if small end islands intersect this chunk
for (j = -1; j <= +1; j++)
{
for (i = -1; i <= +1; i++)
{
EndIsland is[2];
int n = getEndIslands(is, en->mc, seed, chunkX+i, chunkZ+j);
while (n --> 0)
{
if (is[n].x + is[n].r <= chunkX*16) continue;
if (is[n].z + is[n].r <= chunkZ*16) continue;
if (is[n].x - is[n].r > chunkX*16 + 15) continue;
if (is[n].z - is[n].r > chunkZ*16 + 15) continue;
int id;
mapEndBiome(en, &id, is[n].x >> 4, is[n].z >> 4, 1, 1);
if (id == small_end_islands)
return 0;
}
}
}
// clamped (32 + 46 - y) / 64.0
static const double upper_drop[] = {
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 0-7
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 63./64, // 8-15
62./64, 61./64, 60./64, 59./64, 58./64, 57./64, 56./64, 55./64, // 16-23
54./64, 53./64, 52./64, 51./64, 50./64, 49./64, 48./64, 47./64, // 24-31
46./64 // 32
};
// clamped (y - 1) / 7.0
static const double lower_drop[] = {
0.0, 0.0, 1./7, 2./7, 3./7, 4./7, 5./7, 6./7, // 0-7
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 8-15
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 16-23
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 24-31
1.0, // 32
};
// inverse of clamping func: ( 30 * (1-l) / l + 3000 * (1-u) ) / u
static const double inverse_drop[] = {
1e9, 1e9, 180.0, 75.0, 40.0, 22.5, 12.0, 5.0, // 0-7
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // 8-14
1000./21, 3000./31, 9000./61, 200.0, // 15-18
};
const double eps = 0.001;
// get the inner depth values and see if they imply blocks in the chunk
for (i = 0; i < 2; i++)
{
for (j = 0; j < 2; j++)
{
depth[i][j] = getEndHeightNoise(en, x+i, z+j, 0) - 8.0f;
for (k = 8; k <= 14; k++)
{
double u = upper_drop[k];
double l = lower_drop[k];
double noise = depth[i][j];
double pivot = inverse_drop[k] - noise;
noise += sampleSurfaceNoiseBetween(sn, x+i, k, z+j, pivot-eps, pivot+eps);
noise = lerp(u, -3000, noise);
noise = lerp(l, -30, noise);
if (noise > 0)
return 0;
}
}
}
// fill in the depth values at the boundaries to neighbouring chunks
for (i = 0; i < 3; i++)
depth[i][2] = getEndHeightNoise(en, x+i, z+2, 0) - 8.0f;
for (j = 0; j < 2; j++)
depth[2][j] = getEndHeightNoise(en, x+2, z+j, 0) - 8.0f;
// see if none of the noise values can generate blocks
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
for (k = 2; k < 18; k++)
{
double u = upper_drop[k];
double l = lower_drop[k];
double noise = depth[i][j];
double pivot = inverse_drop[k] - noise;
noise += sampleSurfaceNoiseBetween(sn, x+i, k, z+j, pivot-eps, pivot+eps);
noise = lerp(u, -3000, noise);
noise = lerp(l, -30, noise);
if (noise > 0)
goto L_check_full;
}
}
}
return 1;
L_check_full:
mapEndSurfaceHeight(y, en, sn, chunkX*16, chunkZ*16, 16, 16, 1, 0);
for (k = 0; k < 256; k++)
if (y[k] != 0) return 0;
return 1;
}
//==============================================================================
// Checking Biomes & Biome Helper Functions
@ -1633,7 +1876,7 @@ int isViableEndCityTerrain(const Generator *g, const SurfaceNoise *sn,
blockZ = chunkZ * 16 + 7;
int cellx = (blockX >> 3);
int cellz = (blockZ >> 3);
// TODO: make sure upper bound is ok
enum { y0 = 15, y1 = 18, yn = y1-y0+1 };
double ncol[3][3][yn];
@ -2618,27 +2861,160 @@ uint64_t getHouseList(int *out, uint64_t seed, int chunkX, int chunkZ)
}
void getFixedEndGateways(Pos pos[20][2], uint64_t seed)
void getFixedEndGateways(int mc, uint64_t seed, Pos src[20])
{
(void) seed;
(void) mc;
static const Pos fixed[20] = {
{ 96, 0}, { 91, 29}, { 77, 56}, { 56, 77}, { 29, 91},
{ -1, 96}, {-30, 91}, {-57, 77}, {-78, 56}, {-92, 29},
{-96, -1}, {-92,-30}, {-78,-57}, {-57,-78}, {-30,-92},
{ 0,-96}, { 29,-92}, { 56,-78}, { 77,-57}, { 91,-30},
};
int i;
for (i = 0; i < 20; i++)
uint8_t order[] = {
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
};
uint64_t rng = 0;
setSeed(&rng, seed);
for (int i = 0; i < 20; i++)
{
Pos p = pos[i][0] = fixed[i];
// TODO: estimated positions on outer island
float r = sqrtf(p.x * p.x + p.z * p.z);
r = 1024.0f / r;
pos[i][1].x = (int) (p.x * r);
pos[i][1].z = (int) (p.z * r);
uint8_t j = 19 - nextInt(&rng, 20-i);
uint8_t tmp = order[i];
order[i] = order[j];
order[j] = tmp;
}
for (int i = 0; i < 20; i++)
src[i] = fixed[ order[i] ];
}
Pos getLinkedGatewayChunk(const EndNoise *en, const SurfaceNoise *sn, uint64_t seed,
Pos src, Pos *dst)
{
double invr = 1.0 / sqrt(src.x * src.x + src.z * src.z);
double dx = src.x * invr;
double dz = src.z * invr;
double px = dx * 1024;
double pz = dz * 1024;
dx *= 16;
dz *= 16;
int i;
Pos c;
c.x = (int) floor(px) >> 4;
c.z = (int) floor(pz) >> 4;
if (isEndChunkEmpty(en, sn, seed, c.x, c.z))
{ // look forward for the first non-empty chunk
for (i = 0; i < 15; i++)
{
int qx = (int) floor(px += dx) >> 4;
int qz = (int) floor(pz += dz) >> 4;
if (qx == c.x && qz == c.z)
continue;
c.x = qx;
c.z = qz;
if (!isEndChunkEmpty(en, sn, seed, c.x, c.z))
break;
}
}
else
{ // look backward for the last non-empty chunk
for (i = 0; i < 15; i++)
{
int qx = (int) floor(px -= dx) >> 4;
int qz = (int) floor(pz -= dz) >> 4;
if (isEndChunkEmpty(en, sn, seed, qx, qz))
break;
c.x = qx;
c.z = qz;
}
}
if (dst)
{
dst->x = (int) floor(px);
dst->z = (int) floor(pz);
}
return c;
}
Pos getLinkedGatewayPos(const EndNoise *en, const SurfaceNoise *sn, uint64_t seed, Pos src)
{
float y[33*33]; // buffer for [16][16] and [33][33]
int ymin = 0;
int i, j;
Pos dst;
Pos c = getLinkedGatewayChunk(en, sn, seed, src, &dst);
if (en->mc > MC_1_16)
{
// The original java implementation has a bug where the result
// variable for the in-chunk block search is assigned a reference
// to the mutable iterator, which ends up as the last iteration
// position and discards the found location.
dst.x = c.x * 16 + 15;
dst.z = c.z * 16 + 15;
}
else
{
mapEndSurfaceHeight(y, en, sn, c.x*16, c.z*16, 16, 16, 1, 30);
mapEndIslandHeight(y, en, seed, c.x*16, c.z*16, 16, 16, 1);
uint64_t d = 0;
for (j = 0; j < 16; j++)
{
for (i = 0; i < 16; i++)
{
int v = (int) y[j*16 + i];
if (v < 30) continue;
uint64_t dx = 16*c.x + i;
uint64_t dz = 16*c.z + j;
uint64_t dr = dx*dx + dz*dz + v*v;
if (dr > d)
{
d = dr;
dst.x = dx;
dst.z = dz;
}
}
}
// use the previous result to retrieve the minimum y-level we know,
// we can skip generation of surfaces that are lower than this
for (i = 0; i < 16*16; i++)
if (y[i] > ymin)
ymin = (int) floor(y[i]);
}
Pos sp = { dst.x-16, dst.z-16 };
// checking end islands is much cheaper than surface height generation, so
// we can also skip surface generation lower than the highest island around
memset(y, 0, sizeof(float)*33*33);
mapEndIslandHeight(y, en, seed, sp.x, sp.z, 33, 33, 1);
for (i = 0; i < 33*33; i++)
if (y[i] > ymin)
ymin = (int) floor(y[i]);
mapEndSurfaceHeight(y, en, sn, sp.x, sp.z, 33, 33, 1, ymin);
mapEndIslandHeight(y, en, seed, sp.x, sp.z, 33, 33, 1);
float v = -1;
for (i = 0; i < 33; i++)
{
for (j = 0; j < 33; j++)
{
if (y[j*33 + i] <= v)
continue;
v = y[j*33 + i];
dst.x = sp.x + i;
dst.z = sp.z + j;
}
}
return dst;
}
//==============================================================================
@ -4040,10 +4416,12 @@ int floodFillGen(struct locate_info_t *info, int i, int j, Pos *p)
i = queue[qn].i;
j = queue[qn].j;
int k = j * info->r.sx + i;
int id = info->ids[k];
if (id == INT_MAX)
continue;
info->ids[k] = INT_MAX;
int x = info->r.x + i;
int z = info->r.z + j;
int id = info->ids[k];
info->ids[k] = INT_MAX;
if (info->g->mc >= MC_1_18)
id = getBiomeAt(info->g, info->r.scale, x, info->r.y, z);
if (id == info->match)
@ -5079,6 +5457,10 @@ static const int g_biome_para_range_20_diff[][13] = {
{cherry_grove , -4500, 2000, IMIN,-1000, 300, IMAX, -7799, 500, IMIN, IMAX, 2666, IMAX},
};
static const int g_biome_para_range_213_diff[][13] = {
{pale_garden , -1500, 2000, 3000, IMAX, 300, IMAX, -7799, 500, IMIN, IMAX, 2666, IMAX},
};
/**
* Gets the min/max parameter values within which a biome change can occur.
@ -5117,6 +5499,15 @@ const int *getBiomeParaLimits(int mc, int id)
if (mc <= MC_1_17)
return NULL;
int i, n;
if (mc > MC_1_21_2)
{
n = sizeof(g_biome_para_range_213_diff) / sizeof(g_biome_para_range_213_diff[0]);
for (i = 0; i < n; i++)
{
if (g_biome_para_range_213_diff[i][0] == id)
return &g_biome_para_range_213_diff[i][1];
}
}
if (mc > MC_1_19)
{
n = sizeof(g_biome_para_range_20_diff) / sizeof(g_biome_para_range_20_diff[0]);

View File

@ -108,6 +108,11 @@ STRUCT(Piece)
Piece *next;
};
STRUCT(EndIsland)
{
int x, y, z;
int r;
};
enum
{
@ -257,6 +262,23 @@ int isSlimeChunk(uint64_t seed, int chunkX, int chunkZ)
return nextInt(&rnd, 10) == 0;
}
/* Finds the position and size of the small end islands in a given chunk.
* Returns the number of end islands found.
*/
int getEndIslands(EndIsland islands[2], int mc, uint64_t seed, int chunkX, int chunkZ);
/* Finds the small end islands in the given area and updates the existing
* height map, y, accordingly. Note that values in the y-map can only increase
* using this.
*/
int mapEndIslandHeight(float *y, const EndNoise *en, uint64_t seed,
int x, int z, int w, int h, int scale);
/* Checks if the given chunk contains no blocks. This included a check for
* small end islands.
*/
int isEndChunkEmpty(const EndNoise *en, const SurfaceNoise *sn, uint64_t seed,
int chunkX, int chunkZ);
//==============================================================================
// Finding Strongholds and Spawn
@ -447,10 +469,18 @@ enum
PIECE_COUNT,
};
/* Find the inner ring positions where End Gateways generate upon defeating the
* Dragon, as well as an estimate of where they link up to (WIP).
/* Find the 20 fixed inner positions where End Gateways generate upon defeating
* the Dragon. The positions are written to 'src' in generation order.
*/
void getFixedEndGateways(Pos pos[20][2], uint64_t seed);
void getFixedEndGateways(int mc, uint64_t seed, Pos src[20]);
/* Get the outer linked Gateway destination for an inner source Gateway.
* (mc > MC_1_12)
*/
Pos getLinkedGatewayChunk(const EndNoise *en, const SurfaceNoise *sn,
uint64_t seed, Pos src, Pos *dst);
Pos getLinkedGatewayPos(const EndNoise *en, const SurfaceNoise *sn,
uint64_t seed, Pos src);
/* Find the number of each type of house that generate in a village

View File

@ -154,7 +154,7 @@ size_t getMinCacheSize(const Generator *g, int scale, int sx, int sy, int sz)
const Layer *entry = getLayerForScale(g, scale);
if (!entry) {
printf("getMinCacheSize(): failed to determine scaled entry\n");
exit(1);
return 0;
}
size_t len2d = getMinLayerCacheSize(entry, sx, sz);
len += len2d - sx*sz;
@ -173,6 +173,8 @@ size_t getMinCacheSize(const Generator *g, int scale, int sx, int sy, int sz)
int *allocCache(const Generator *g, Range r)
{
size_t len = getMinCacheSize(g, r.scale, r.sx, r.sy, r.sz);
if (len == 0)
return NULL;
return (int*) calloc(len, sizeof(int));
}
@ -615,7 +617,7 @@ int mapApproxHeight(float *y, int *ids, const Generator *g, const SurfaceNoise *
{
if (g->mc <= MC_1_8)
return 1;
return mapSurfaceHeightEnd(&g->en, sn, y, x, z, w, h, 4);
return mapEndSurfaceHeight(y, &g->en, sn, x, z, w, h, 4, 0);
}
if (g->mc >= MC_1_18)

452
layers.c
View File

@ -6,458 +6,6 @@
#include <float.h>
//==============================================================================
// Essentials
//==============================================================================
int biomeExists(int mc, int id)
{
if (mc >= MC_1_18)
{
if (id >= soul_sand_valley && id <= basalt_deltas)
return 1;
if (id >= small_end_islands && id <= end_barrens)
return 1;
if (id == cherry_grove)
return mc >= MC_1_20;
if (id == deep_dark || id == mangrove_swamp)
return mc >= MC_1_19_2;
switch (id)
{
case ocean:
case plains:
case desert:
case mountains: // windswept_hills
case forest:
case taiga:
case swamp:
case river:
case nether_wastes:
case the_end:
case frozen_ocean:
case frozen_river:
case snowy_tundra: // snowy_plains
case mushroom_fields:
case beach:
case jungle:
case jungle_edge: // sparse_jungle
case deep_ocean:
case stone_shore: // stony_shore
case snowy_beach:
case birch_forest:
case dark_forest:
case snowy_taiga:
case giant_tree_taiga: // old_growth_pine_taiga
case wooded_mountains: // windswept_forest
case savanna:
case savanna_plateau:
case badlands:
case wooded_badlands_plateau: // wooded_badlands
case warm_ocean:
case lukewarm_ocean:
case cold_ocean:
case deep_warm_ocean:
case deep_lukewarm_ocean:
case deep_cold_ocean:
case deep_frozen_ocean:
case sunflower_plains:
case gravelly_mountains: // windswept_gravelly_hills
case flower_forest:
case ice_spikes:
case tall_birch_forest: // old_growth_birch_forest
case giant_spruce_taiga: // old_growth_spruce_taiga
case shattered_savanna: // windswept_savanna
case eroded_badlands:
case bamboo_jungle:
case dripstone_caves:
case lush_caves:
case meadow:
case grove:
case snowy_slopes:
case stony_peaks:
case jagged_peaks:
case frozen_peaks:
return 1;
default:
return 0;
}
}
if (mc <= MC_B1_7)
{
switch(id)
{
case plains:
case desert:
case forest:
case taiga:
case swamp:
case snowy_tundra:
case savanna:
case seasonal_forest:
case rainforest:
case shrubland:
// we treat areas below the sea level as oceans
case ocean:
case frozen_ocean:
return 1;
default:
return 0;
}
}
if (mc <= MC_B1_8)
{
switch (id)
{
case frozen_ocean:
case frozen_river:
case snowy_tundra:
case mushroom_fields:
case mushroom_field_shore:
case the_end:
return 0;
}
}
if (mc <= MC_1_0)
{
switch (id)
{
case snowy_mountains:
case beach:
case desert_hills:
case wooded_hills:
case taiga_hills:
case mountain_edge:
return 0;
}
}
if (id >= ocean && id <= mountain_edge) return 1;
if (id >= jungle && id <= jungle_hills) return mc >= MC_1_2;
if (id >= jungle_edge && id <= badlands_plateau) return mc >= MC_1_7;
if (id >= small_end_islands && id <= end_barrens) return mc >= MC_1_9;
if (id >= warm_ocean && id <= deep_frozen_ocean) return mc >= MC_1_13;
switch (id)
{
case the_void:
return mc >= MC_1_9;
case sunflower_plains:
case desert_lakes:
case gravelly_mountains:
case flower_forest:
case taiga_mountains:
case swamp_hills:
case ice_spikes:
case modified_jungle:
case modified_jungle_edge:
case tall_birch_forest:
case tall_birch_hills:
case dark_forest_hills:
case snowy_taiga_mountains:
case giant_spruce_taiga:
case giant_spruce_taiga_hills:
case modified_gravelly_mountains:
case shattered_savanna:
case shattered_savanna_plateau:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
return mc >= MC_1_7;
case bamboo_jungle:
case bamboo_jungle_hills:
return mc >= MC_1_14;
case soul_sand_valley:
case crimson_forest:
case warped_forest:
case basalt_deltas:
return mc >= MC_1_16_1;
case dripstone_caves:
case lush_caves:
return mc >= MC_1_17;
default:
return 0;
}
}
int isOverworld(int mc, int id)
{
if (!biomeExists(mc, id))
return 0;
if (id >= small_end_islands && id <= end_barrens) return 0;
if (id >= soul_sand_valley && id <= basalt_deltas) return 0;
switch (id)
{
case nether_wastes:
case the_end:
return 0;
case frozen_ocean:
return mc <= MC_1_6 || mc >= MC_1_13;
case mountain_edge:
return mc <= MC_1_6;
case deep_warm_ocean:
case the_void:
return 0;
case tall_birch_forest:
return mc <= MC_1_8 || mc >= MC_1_11;
case dripstone_caves:
case lush_caves:
return mc >= MC_1_18;
}
return 1;
}
int getDimension(int id)
{
if (id >= small_end_islands && id <= end_barrens) return DIM_END;
if (id >= soul_sand_valley && id <= basalt_deltas) return DIM_NETHER;
if (id == the_end) return DIM_END;
if (id == nether_wastes) return DIM_NETHER;
return DIM_OVERWORLD;
}
int getMutated(int mc, int id)
{
switch (id)
{
case plains: return sunflower_plains;
case desert: return desert_lakes;
case mountains: return gravelly_mountains;
case forest: return flower_forest;
case taiga: return taiga_mountains;
case swamp: return swamp_hills;
case snowy_tundra: return ice_spikes;
case jungle: return modified_jungle;
case jungle_edge: return modified_jungle_edge;
// emulate MC-98995
case birch_forest:
return (mc >= MC_1_9 && mc <= MC_1_10) ? tall_birch_hills : tall_birch_forest;
case birch_forest_hills:
return (mc >= MC_1_9 && mc <= MC_1_10) ? none : tall_birch_hills;
case dark_forest: return dark_forest_hills;
case snowy_taiga: return snowy_taiga_mountains;
case giant_tree_taiga: return giant_spruce_taiga;
case giant_tree_taiga_hills: return giant_spruce_taiga_hills;
case wooded_mountains: return modified_gravelly_mountains;
case savanna: return shattered_savanna;
case savanna_plateau: return shattered_savanna_plateau;
case badlands: return eroded_badlands;
case wooded_badlands_plateau: return modified_wooded_badlands_plateau;
case badlands_plateau: return modified_badlands_plateau;
default:
return none;
}
}
int getCategory(int mc, int id)
{
switch (id)
{
case beach:
case snowy_beach:
return beach;
case desert:
case desert_hills:
case desert_lakes:
return desert;
case mountains:
case mountain_edge:
case wooded_mountains:
case gravelly_mountains:
case modified_gravelly_mountains:
return mountains;
case forest:
case wooded_hills:
case birch_forest:
case birch_forest_hills:
case dark_forest:
case flower_forest:
case tall_birch_forest:
case tall_birch_hills:
case dark_forest_hills:
return forest;
case snowy_tundra:
case snowy_mountains:
case ice_spikes:
return snowy_tundra;
case jungle:
case jungle_hills:
case jungle_edge:
case modified_jungle:
case modified_jungle_edge:
case bamboo_jungle:
case bamboo_jungle_hills:
return jungle;
case badlands:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
return mesa;
case wooded_badlands_plateau:
case badlands_plateau:
return mc <= MC_1_15 ? mesa : badlands_plateau;
case mushroom_fields:
case mushroom_field_shore:
return mushroom_fields;
case stone_shore:
return stone_shore;
case ocean:
case frozen_ocean:
case deep_ocean:
case warm_ocean:
case lukewarm_ocean:
case cold_ocean:
case deep_warm_ocean:
case deep_lukewarm_ocean:
case deep_cold_ocean:
case deep_frozen_ocean:
return ocean;
case plains:
case sunflower_plains:
return plains;
case river:
case frozen_river:
return river;
case savanna:
case savanna_plateau:
case shattered_savanna:
case shattered_savanna_plateau:
return savanna;
case swamp:
case swamp_hills:
return swamp;
case taiga:
case taiga_hills:
case snowy_taiga:
case snowy_taiga_hills:
case giant_tree_taiga:
case giant_tree_taiga_hills:
case taiga_mountains:
case snowy_taiga_mountains:
case giant_spruce_taiga:
case giant_spruce_taiga_hills:
return taiga;
case nether_wastes:
case soul_sand_valley:
case crimson_forest:
case warped_forest:
case basalt_deltas:
return nether_wastes;
default:
return none;
}
}
int areSimilar(int mc, int id1, int id2)
{
if (id1 == id2) return 1;
if (mc <= MC_1_15)
{
if (id1 == wooded_badlands_plateau || id1 == badlands_plateau)
return id2 == wooded_badlands_plateau || id2 == badlands_plateau;
}
return getCategory(mc, id1) == getCategory(mc, id2);
}
int isMesa(int id)
{
switch (id)
{
case badlands:
case eroded_badlands:
case modified_wooded_badlands_plateau:
case modified_badlands_plateau:
case wooded_badlands_plateau:
case badlands_plateau:
return 1;
default:
return 0;
}
}
int isShallowOcean(int id)
{
const uint64_t shallow_bits =
(1ULL << ocean) |
(1ULL << frozen_ocean) |
(1ULL << warm_ocean) |
(1ULL << lukewarm_ocean) |
(1ULL << cold_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & shallow_bits);
}
int isDeepOcean(int id)
{
const uint64_t deep_bits =
(1ULL << deep_ocean) |
(1ULL << deep_warm_ocean) |
(1ULL << deep_lukewarm_ocean) |
(1ULL << deep_cold_ocean) |
(1ULL << deep_frozen_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & deep_bits);
}
int isOceanic(int id)
{
const uint64_t ocean_bits =
(1ULL << ocean) |
(1ULL << frozen_ocean) |
(1ULL << warm_ocean) |
(1ULL << lukewarm_ocean) |
(1ULL << cold_ocean) |
(1ULL << deep_ocean) |
(1ULL << deep_warm_ocean) |
(1ULL << deep_lukewarm_ocean) |
(1ULL << deep_cold_ocean) |
(1ULL << deep_frozen_ocean);
return (uint32_t) id < 64 && ((1ULL << id) & ocean_bits);
}
int isSnowy(int id)
{
switch (id)
{
case frozen_ocean:
case frozen_river:
case snowy_tundra:
case snowy_mountains:
case snowy_beach:
case snowy_taiga:
case snowy_taiga_hills:
case ice_spikes:
case snowy_taiga_mountains:
return 1;
default:
return 0;
}
}
//==============================================================================
// Essentials
//==============================================================================

View File

@ -127,23 +127,6 @@ extern "C"
{
#endif
//==============================================================================
// BiomeID Helpers
//==============================================================================
int biomeExists(int mc, int id);
int isOverworld(int mc, int id);
int getDimension(int id);
int getMutated(int mc, int id);
int getCategory(int mc, int id);
int areSimilar(int mc, int id1, int id2);
int isMesa(int id);
int isShallowOcean(int id);
int isDeepOcean(int id);
int isOceanic(int id);
int isSnowy(int id);
int getBiomeDepthAndScale(int id, double *depth, double *scale, int *grass);
//==============================================================================
// Essentials
//==============================================================================

View File

@ -30,7 +30,7 @@ release: CFLAGS += -fPIC
endif
libcubiomes: noise.o layers.o biometree.o biomenoise.o generator.o finders.o util.o quadbase.o
libcubiomes: noise.o biomes.o layers.o biometree.o biomenoise.o generator.o finders.o util.o quadbase.o
$(AR) $(ARFLAGS) libcubiomes.a $^
finders.o: finders.c finders.h
@ -48,6 +48,9 @@ biometree.o: biometree.c
layers.o: layers.c layers.h
$(CC) -c $(CFLAGS) $<
biomes.o: biomes.c biomes.h
$(CC) -c $(CFLAGS) $<
noise.o: noise.c noise.h
$(CC) -c $(CFLAGS) $<

View File

@ -45,7 +45,7 @@ static const int32_t btree20_param[][2] =
static const uint64_t btree20_nodes[] =
{
// Binary encoded biome parameter search tree for 1.20 (20w07a).
// Binary encoded biome parameter search tree for 1.20 (23w07a).
//
// +-------------- If the top byte equals 0xFF, the node is a leaf and the
// | second byte is the biome id, otherwise the two bytes

2342
tables/btree213.h Normal file

File diff suppressed because it is too large Load Diff

76
tests.c
View File

@ -133,6 +133,8 @@ uint32_t testAreas(int mc, int dim, int scale)
Generator g;
setupGenerator(&g, mc, 0);
SurfaceNoise sn;
double t = -now();
uint32_t hash = 0;
uint64_t s;
@ -149,6 +151,14 @@ uint32_t testAreas(int mc, int dim, int scale)
Range r = {scale, x, z, w, h, y, 1};
int *ids = allocCache(&g, r);
genBiomes(&g, ids, r);
float *surf = malloc(4 * w * h);
initSurfaceNoise(&sn, dim, s);
mapApproxHeight(surf, 0, &g, &sn, x, z, w, h);
for (int i = 0; i < w*h; i++)
ids[i] = (int) surf[i];
free(surf);
int i = 0;
hash = 0;
for (i = 0; i < w*h; i++)
@ -322,7 +332,7 @@ void findBiomeParaBounds()
}
Generator g;
setupGenerator(&g, MC_1_20, 0);
setupGenerator(&g, MC_1_21_3, 0);
int64_t s;
int r = 1000;
for (s = 0; s < 20000; s++)
@ -337,14 +347,16 @@ void findBiomeParaBounds()
getParaRange(&g.bn.climate[NP_EROSION], &tmin, &tmax, x-r, z-r, r, r, &g, _f2);
getParaRange(&g.bn.climate[NP_CONTINENTALNESS], &tmin, &tmax, x-r, z-r, r, r, &g, _f2);
getParaRange(&g.bn.climate[NP_WEIRDNESS], &tmin, &tmax, x-r, z-r, r, r, &g, _f2);
if (s % 1000 == 999)
printf(".\n");
}
for (i = 0; i < 256; i++)
{
if (!isOverworld(MC_1_20, i))
if (!isOverworld(MC_1_21_3, i))
continue;
printf("{%-24s", biome2str(MC_1_20, i));
printf("{%-24s", biome2str(MC_1_21_3, i));
for (j = 0; j < 6; j++)
{
printf(", %6ld,%6ld", bbounds[i][j][0], bbounds[i][j][1]);
@ -458,20 +470,70 @@ void findStructures(int structureType, int mc, int dim, uint64_t seed,
if (!isViableStructureTerrain(structureType, &g, pos.x, pos.z))
continue;
}
printf("%d, %d\n", pos.x, pos.z);
int id = getBiomeAt(&g, 4, pos.x>>2, 320>>2, pos.z>>2);
StructureVariant sv;
getVariant(&sv, structureType, mc, seed, pos.x, pos.z, id);
int x = pos.x + sv.x;
int z = pos.z + sv.z;
printf("%d, %d : [%d %d %d] - [%d %d %d]\n", pos.x, pos.z,
x, sv.y, z, x+sv.sx, sv.y+sv.sy, z+sv.sz
);
}
}
}
int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
{
return getStructureConfig(stype, mc, sconf);
}
int main()
{
//testAreas(mc, 0, 1);
/*
int mc = MC_1_21;
uint64_t seed = 2;
double t0 = 0, t1 = 0;
t0 -= now();
EndNoise en;
setEndSeed(&en, mc, seed);
SurfaceNoise sn;
initSurfaceNoise(&sn, DIM_END, seed);
Pos src[20];
getFixedEndGateways(mc, seed, src);
t0 += now();
t1 -= now();
for (int i = 0; i < 20; i++)
{
Pos dst = getLinkedGatewayPos(&en, &sn, seed, src[i]);
printf("%d %d -> %d %d\n", src[i].x, src[i].z, dst.x, dst.z);
}
t1 += now();
printf("Time: %g -> %g sec\n", t0, t1);
*/
//findStructures(Trial_Chambers, MC_1_21, 0, 1, 3056, 3440, 3056, 3440);
//endHeight(MC_1_21, 1, 80>>1, 1216>>1, 32, 32, 2);
//endHeight(MC_1_15, 1, 370704, 96, 32, 32, 1);
//testAreas(MC_1_21, 1, 1);
//testAreas(mc, 0, 4);
//testAreas(mc, 0, 16);
//testAreas(mc, 0, 256);
//testCanBiomesGenerate();
testGeneration();
//findBiomeParaBounds();
//testGeneration();
findBiomeParaBounds();
return 0;
}

32
util.c
View File

@ -73,7 +73,9 @@ const char* mc2str(int mc)
case MC_1_19_2: return "1.19.2"; break;
case MC_1_19: return "1.19"; break;
case MC_1_20: return "1.20"; break;
case MC_1_21: return "1.21"; break;
case MC_1_21_2: return "1.21.2"; break;
case MC_1_21_3: return "1.21.3"; break;
//case MC_1_21: return "1.21"; break;
default: return NULL;
}
}
@ -81,29 +83,52 @@ const char* mc2str(int mc)
int str2mc(const char *s)
{
if (!strcmp(s, "1.21")) return MC_1_21;
if (!strcmp(s, "1.21.3")) return MC_1_21_3;
if (!strcmp(s, "1.21.2")) return MC_1_21_2;
if (!strcmp(s, "1.20")) return MC_1_20;
if (!strcmp(s, "1.20.6")) return MC_1_20_6;
if (!strcmp(s, "1.19")) return MC_1_19;
if (!strcmp(s, "1.19.4")) return MC_1_19_4;
if (!strcmp(s, "1.19.2")) return MC_1_19_2;
if (!strcmp(s, "1.18")) return MC_1_18;
if (!strcmp(s, "1.18.2")) return MC_1_18_2;
if (!strcmp(s, "1.17")) return MC_1_17;
if (!strcmp(s, "1.16.1")) return MC_1_16_1;
if (!strcmp(s, "1.17.1")) return MC_1_17_1;
if (!strcmp(s, "1.16")) return MC_1_16;
if (!strcmp(s, "1.16.5")) return MC_1_16_5;
if (!strcmp(s, "1.16.1")) return MC_1_16_1;
if (!strcmp(s, "1.15")) return MC_1_15;
if (!strcmp(s, "1.15.2")) return MC_1_15_2;
if (!strcmp(s, "1.14")) return MC_1_14;
if (!strcmp(s, "1.14.4")) return MC_1_14_4;
if (!strcmp(s, "1.13")) return MC_1_13;
if (!strcmp(s, "1.13.2")) return MC_1_13_2;
if (!strcmp(s, "1.12")) return MC_1_12;
if (!strcmp(s, "1.12.2")) return MC_1_12_2;
if (!strcmp(s, "1.11")) return MC_1_11;
if (!strcmp(s, "1.11.2")) return MC_1_11_2;
if (!strcmp(s, "1.10")) return MC_1_10;
if (!strcmp(s, "1.10.2")) return MC_1_10_2;
if (!strcmp(s, "1.9")) return MC_1_9;
if (!strcmp(s, "1.9.4")) return MC_1_9_4;
if (!strcmp(s, "1.8")) return MC_1_8;
if (!strcmp(s, "1.8.9")) return MC_1_8_9;
if (!strcmp(s, "1.7")) return MC_1_7;
if (!strcmp(s, "1.7.10")) return MC_1_7_10;
if (!strcmp(s, "1.6")) return MC_1_6;
if (!strcmp(s, "1.6.4")) return MC_1_6_4;
if (!strcmp(s, "1.5")) return MC_1_5;
if (!strcmp(s, "1.5.2")) return MC_1_5_2;
if (!strcmp(s, "1.4")) return MC_1_4;
if (!strcmp(s, "1.4.7")) return MC_1_4_7;
if (!strcmp(s, "1.3")) return MC_1_3;
if (!strcmp(s, "1.3.2")) return MC_1_3_2;
if (!strcmp(s, "1.2")) return MC_1_2;
if (!strcmp(s, "1.2.5")) return MC_1_2_5;
if (!strcmp(s, "1.1")) return MC_1_1;
if (!strcmp(s, "1.1.0")) return MC_1_1_0;
if (!strcmp(s, "1.0")) return MC_1_0;
if (!strcmp(s, "1.0.0")) return MC_1_0_0;
if (!strcmp(s, "Beta 1.8")) return MC_B1_8;
if (!strcmp(s, "Beta 1.7")) return MC_B1_7;
return -1;
@ -242,6 +267,8 @@ const char *biome2str(int mc, int id)
case mangrove_swamp: return "mangrove_swamp";
// 1.20
case cherry_grove: return "cherry_grove";
// 1.21.3 (Winter Drop)
case pale_garden: return "pale_garden";
}
return NULL;
}
@ -387,6 +414,7 @@ void initBiomeColors(unsigned char colors[256][3])
setColor(colors, deep_dark, 0x031f29); // -
setColor(colors, mangrove_swamp, 0x2ccc8e); // -
setColor(colors, cherry_grove, 0xff91c8); // -
setColor(colors, pale_garden, 0x726c68); // -
}
void initBiomeTypeColors(unsigned char colors[256][3])