diff --git a/biomenoise.c b/biomenoise.c index 0f91668..2814fb3 100644 --- a/biomenoise.c +++ b/biomenoise.c @@ -51,6 +51,62 @@ void initSurfaceNoiseBeta(SurfaceNoiseBeta *snb, uint64_t seed) octaveInitBeta(&snb->octcontB, &s, snb->oct+50, 16, 200.0, 0.5, 1.0, 2.0); } +double sampleSurfaceNoiseBetween(const SurfaceNoise *sn, int x, int y, int z, + double noiseMin, double noiseMax) +{ + double persist, amp; + double dx, dy, dz, sy; + int i; + + double xzScale = 684.412 * sn->xzScale; + double yScale = 684.412 * sn->yScale; + double vmin = 0; + double vmax = 0; + + persist = 1.0 / 32768.0; + amp = 64.0; + + for (i = 15; i >= 0; i--) + { + dx = x * xzScale * persist; + dz = z * xzScale * persist; + sy = yScale * persist; + 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; + + amp *= 0.5; + persist *= 2.0; + } + + double xzStep = xzScale / sn->xzFactor; + double yStep = yScale / sn->yFactor; + double vmain = 0.5; + + persist = 1.0 / 128.0; + amp = 0.05 * 128.0; + + for (i = 7; i >= 0; i--) + { + dx = x * xzStep * persist; + dz = z * xzStep * persist; + sy = yStep * persist; + dy = y * sy; + + vmain += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, dy) * amp; + if (vmain - amp > 1) return vmax; + if (vmain + amp < 0) return vmin; + + amp *= 0.5; + persist *= 2.0; + } + + return clampedLerp(vmain, vmin, vmax); +} + double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z) { double xzScale = 684.412 * sn->xzScale; @@ -62,6 +118,7 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z) double maxNoise = 0; double mainNoise = 0; double persist = 1.0; + double contrib = 1.0; double dx, dy, dz, sy, ty; int i; @@ -73,8 +130,8 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z) sy = yScale * persist; ty = y * sy; - minNoise += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, ty) / persist; - maxNoise += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, ty) / persist; + minNoise += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, ty) * contrib; + maxNoise += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, ty) * contrib; if (i < 8) { @@ -83,15 +140,15 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z) dz = maintainPrecision(z * xzStep * persist); sy = yStep * persist; ty = y * sy; - mainNoise += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, ty) / persist; + mainNoise += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, ty) * contrib; } - persist /= 2.0; + persist *= 0.5; + contrib *= 2.0; } return clampedLerp(0.5 + 0.05*mainNoise, minNoise/512.0, maxNoise/512.0); } - //============================================================================== // Nether (1.16+) and End (1.9+) Biome Generation //============================================================================== @@ -489,8 +546,12 @@ 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) + const EndNoise *en, int x, int z, int colymin, int colymax) { double depth = getEndHeightNoise(en, x, z, 0) - 8.0f; int y; @@ -505,6 +566,55 @@ void sampleNoiseColumnEnd(double column[], const SurfaceNoise *sn, } } +void sampleNoiseColumnEndFull(double column[END_NOISE_COL_SIZE], + const SurfaceNoise *sn, const EndNoise *en, int x, int z) +{ + // 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 + }; + + // depth is between [-108, +72] + // noise is between [-128, +128] + // for a sold block we need the upper drop as: + // (72 + 128) * u - 3000 * (1-u) > 0 => upper_drop = u < 15/16 + // which occurs at y = 18 for the highest relevant noise cell + // for the lower drop we need: + // (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++) + { + if (lower_drop[y] * (103.0 + depth) < 30) + { + column[y - END_NOISE_COL_YMIN] = -30; + continue; + } + 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; + } +} + /* Given bordering noise columns and a fractional position between those, * determine the surface block height (i.e. where the interpolated noise > 0). * Note that the noise columns should be of size: ncolxz[ colymax-colymin+1 ] @@ -568,6 +678,59 @@ 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) +{ + if (scale != 1 && scale != 2 && scale != 4 && scale != 8) + return 1; + + enum { YSIZ = END_NOISE_COL_SIZE }; + + double cellmid = scale > 1 ? scale / 16.0 : 0; + int cellsiz = 8 / scale; + int cx = floordiv(x, cellsiz); + int cz = floordiv(z, cellsiz); + int cw = floordiv(x + w - 1, cellsiz) - cx + 2; + int i, j; + + double *buf = malloc(sizeof(double) * YSIZ * cw * 2); + double *ncol[2]; + ncol[0] = buf; + ncol[1] = buf + YSIZ * cw; + + for (i = 0; i < cw; i++) + sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cz+0); + + for (j = 0; j < h; j++) + { + int cj = floordiv(z + j, cellsiz); + int dj = z + j - cj * cellsiz; + if (j == 0 || dj == 0) + { + double *tmp = ncol[0]; + ncol[0] = ncol[1]; + ncol[1] = tmp; + for (i = 0; i < cw; i++) + sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cj+1); + } + + for (i = 0; i < w; i++) + { + int ci = floordiv(x + i, cellsiz); + 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); + } + } + + free(buf); + return 0; +} + int genEndScaled(const EndNoise *en, int *out, Range r, int mc, uint64_t sha) { if (mc < MC_1_0) diff --git a/biomenoise.h b/biomenoise.h index a000324..4fd38d3 100644 --- a/biomenoise.h +++ b/biomenoise.h @@ -223,6 +223,9 @@ 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); + /** * The scaled End generation supports scales 1, 4, 16, and 64. * The End biomes are usually 2D, but in 1.15+ there is 3D voronoi noise, which diff --git a/biomes.h b/biomes.h index 84ade04..55a12ed 100644 --- a/biomes.h +++ b/biomes.h @@ -32,7 +32,8 @@ enum MCVersion MC_1_19_2, MC_1_19, // 1.19.3 - 1.19.4 MC_1_20, - MC_NEWEST = MC_1_20, + MC_1_21, + MC_NEWEST = MC_1_21, }; enum Dimension diff --git a/biometree.c b/biometree.c index 71676b4..2d354d6 100644 --- a/biometree.c +++ b/biometree.c @@ -19,5 +19,8 @@ BiomeTree g_btree[MC_NEWEST - MC_1_18 + 1] = // MC_1_20 { btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order, sizeof(btree20_nodes) / sizeof(uint64_t) }, + // MC_1_21 + { btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order, + sizeof(btree20_nodes) / sizeof(uint64_t) }, }; diff --git a/finders.c b/finders.c index 598fc95..97957c7 100644 --- a/finders.c +++ b/finders.c @@ -83,7 +83,8 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf) s_ruined_portal_n = { 34222645, 40, 25, Ruined_Portal, STRUCT_NETHER,0}, s_ruined_portal_n_117 = { 34222645, 25, 15, Ruined_Portal_N, STRUCT_NETHER,0}, s_ancient_city = { 20083232, 24, 16, Ancient_City, 0,0}, - s_trail_ruin = { 83469867, 34, 26, Trail_Ruin, 0,0}, + s_trail_ruins = { 83469867, 34, 26, Trail_Ruins, 0,0}, + s_trial_chambers = { 94251327, 34, 22, Trial_Chambers, 0,0}, s_treasure = { 10387320, 1, 1, Treasure, STRUCT_CHUNK,0}, s_mineshaft = { 0, 1, 1, Mineshaft, STRUCT_CHUNK,0}, s_desert_well_115 = { 30010, 1, 1, Desert_Well, STRUCT_CHUNK, 1.f/1000}, @@ -100,7 +101,9 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf) s_end_gateway_115 = { 30000, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 700}, 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_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 = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 1.f/14} ; switch (structureType) @@ -170,6 +173,10 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf) // 1.11 and 1.12 generate gateways using a random source that passed // 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; + else *sconf = s_end_island; + return mc >= MC_1_13; // we only support decorator features for 1.13+ case Desert_Well: if (mc <= MC_1_15) *sconf = s_desert_well_115; else if (mc <= MC_1_17) *sconf = s_desert_well_117; @@ -180,9 +187,12 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf) case Geode: *sconf = mc <= MC_1_17 ? s_geode_117 : s_geode; return mc >= MC_1_17; - case Trail_Ruin: - *sconf = s_trail_ruin; + case Trail_Ruins: + *sconf = s_trail_ruins; return mc >= MC_1_20; + case Trial_Chambers: + *sconf = s_trial_chambers; + return mc >= MC_1_21; default: memset(sconf, 0, sizeof(StructureConfig)); return 0; @@ -224,7 +234,8 @@ int getStructurePos(int structureType, int mc, uint64_t seed, int regX, int regZ case Ruined_Portal: case Ruined_Portal_N: case Ancient_City: - case Trail_Ruin: + case Trail_Ruins: + case Trial_Chambers: *pos = getFeaturePos(sconf, seed, regX, regZ); return 1; @@ -278,6 +289,7 @@ int getStructurePos(int structureType, int mc, uint64_t seed, int regX, int regZ } case End_Gateway: + case End_Island: case Desert_Well: case Geode: // decorator features @@ -950,7 +962,7 @@ int isViableFeatureBiome(int mc, int structureType, int biomeID) if (mc <= MC_1_18) return 0; return biomeID == deep_dark; - case Trail_Ruin: + case Trail_Ruins: if (mc <= MC_1_19) return 0; else { switch (biomeID) { @@ -966,6 +978,10 @@ int isViableFeatureBiome(int mc, int structureType, int biomeID) } } + case Trial_Chambers: + if (mc <= MC_1_20) return 0; + return biomeID != deep_dark && isOverworld(mc, biomeID); + case Treasure: if (mc <= MC_1_12) return 0; return biomeID == beach || biomeID == snowy_beach; @@ -1257,7 +1273,7 @@ int isViableStructurePos(int structureType, Generator *g, int x, int z, uint32_t switch (structureType) { - case Trail_Ruin: + case Trail_Ruins: if (g->mc <= MC_1_19) goto L_not_viable; goto L_feature; case Ocean_Ruin: @@ -1301,8 +1317,8 @@ L_feature: { if (g->mc <= MC_1_17) g->entry = &g->ls.layers[L_RIVER_MIX_4]; - sampleX = x >> 2; - sampleZ = z >> 2; + sampleX = x * 4; + sampleZ = z * 4; } id = getBiomeAt(g, 0, sampleX, 319>>2, sampleZ); if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id)) @@ -1501,14 +1517,18 @@ L_feature: goto L_viable; case Ancient_City: - if (g->mc <= MC_1_18) - goto L_not_viable; + if (g->mc <= MC_1_18) goto L_not_viable; + goto L_jigsaw; + + case Trial_Chambers: + if (g->mc <= MC_1_20) goto L_not_viable; +L_jigsaw: { StructureVariant sv; - getVariant(&sv, Ancient_City, g->mc, g->seed, x, z, -1); - sampleX = (chunkX*32 + 2*sv.x + sv.sx) / 2 >> 2; - sampleZ = (chunkZ*32 + 2*sv.z + sv.sz) / 2 >> 2; - sampleY = -27 >> 2; + getVariant(&sv, structureType, g->mc, g->seed, x, z, -1); + sampleX = (chunkX*32 + 2*sv.x + sv.sx - 1) / 2 >> 2; + sampleZ = (chunkZ*32 + 2*sv.z + sv.sz - 1) / 2 >> 2; + sampleY = sv.y >> 2; id = getBiomeAt(g, 4, sampleX, sampleY, sampleZ); } if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id)) @@ -1686,7 +1706,7 @@ int isViableEndCityTerrain(const Generator *g, const SurfaceNoise *sn, if (h01 < h00) h00 = h01; if (h10 < h00) h00 = h10; if (h11 < h00) h00 = h11; - return h00 >= 60; + return h00 >= 60 ? h00 : 0; } @@ -1865,6 +1885,7 @@ int getVariant(StructureVariant *r, int structType, int mc, uint64_t seed, case 2: r->x = x+sx; r->z = z+sz; break; // 2:cw180 case 3: r->x = x-sz; r->z = z+sx; break; // 3:cw270=ccw90 } + r->y = -27; r->sy = sy; return 1; @@ -2040,6 +2061,21 @@ int getVariant(StructureVariant *r, int structType, int mc, uint64_t seed, } return 1; + case Trial_Chambers: + r->y = nextInt(&rng, 1+20) + -40; // Y-level + r->rotation = nextInt(&rng, 4); + r->start = nextInt(&rng, 2); // corridor/end_[12] + r->sx = 19; r->sy = 20; r->sz = 19; + //r->y += -1; // groundLevelData + switch (r->rotation) + { // 0:0, 1:cw90, 2:cw180, 3:cw270=ccw90 + case 0: break; + case 1: r->x = 1-r->sz; r->z = 0; break; + case 2: r->x = 1-r->sx; r->z = 1-r->sz; break; + case 3: r->x = 0; r->z = 1-r->sx; break; + } + return 1; + default: return 0; } @@ -3996,7 +4032,10 @@ int floodFillGen(struct locate_info_t *info, int i, int j, Pos *p) while (--qn >= 0) { if (info->stop && *info->stop) + { + free(queue); return 0; + } int d = queue[qn].d; i = queue[qn].i; j = queue[qn].j; @@ -4337,7 +4376,7 @@ struct _gp_args int mc; uint32_t flags; }; -// TODO: This function requires testing across versions + static void _genPotential(struct _gp_args *a, int layer, int id) { int mc = a->mc; diff --git a/finders.h b/finders.h index cdd20b2..a44f897 100644 --- a/finders.h +++ b/finders.h @@ -36,7 +36,9 @@ enum StructureType Bastion, End_City, End_Gateway, - Trail_Ruin, + End_Island, + Trail_Ruins, + Trial_Chambers, FEATURE_NUM }; diff --git a/generator.c b/generator.c index 5917a03..1049fc3 100644 --- a/generator.c +++ b/generator.c @@ -608,8 +608,15 @@ int genArea(const Layer *layer, int *out, int areaX, int areaZ, int areaWidth, i int mapApproxHeight(float *y, int *ids, const Generator *g, const SurfaceNoise *sn, int x, int z, int w, int h) { - if (g->dim != DIM_OVERWORLD) - return 1; + if (g->dim == DIM_NETHER) + return 127; + + if (g->dim == DIM_END) + { + if (g->mc <= MC_1_8) + return 1; + return mapSurfaceHeightEnd(&g->en, sn, y, x, z, w, h, 4); + } if (g->mc >= MC_1_18) { diff --git a/util.c b/util.c index d1bf0c3..16c5128 100644 --- a/util.c +++ b/util.c @@ -73,12 +73,14 @@ 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; default: return NULL; } } int str2mc(const char *s) { + if (!strcmp(s, "1.21")) return MC_1_21; if (!strcmp(s, "1.20")) return MC_1_20; if (!strcmp(s, "1.19")) return MC_1_19; if (!strcmp(s, "1.19.2")) return MC_1_19_2; @@ -265,7 +267,8 @@ const char* struct2str(int stype) case Ruined_Portal_N: return "ruined_portal_nether"; case Geode: return "amethyst_geode"; case Ancient_City: return "ancient_city"; - case Trail_Ruin: return "trail_ruins"; + case Trail_Ruins: return "trail_ruins"; + case Trial_Chambers: return "trial_chambers"; case Fortress: return "fortress"; case Bastion: return "bastion_remnant"; case End_City: return "end_city";