diff --git a/core/src/Dimension.cpp b/core/src/Dimension.cpp new file mode 100644 index 0000000..5f88a0e --- /dev/null +++ b/core/src/Dimension.cpp @@ -0,0 +1,152 @@ +#include "vf/Dimension.h" + +#include +#include +#include + +namespace vf { + +namespace { + +constexpr long long ticksPerDay = 24000; + +// A destination portal site is only worth taking if it sits within this many +// blocks of the scaled arrival height; otherwise a platform is cheaper than a +// long fall or a climb to the roof. +constexpr int safeSpotVerticalBand = 32; + +uint32_t deriveSeed(uint32_t baseSeed, uint32_t salt) { + uint64_t h = baseSeed; + h ^= salt + 0x9E3779B97F4A7C15ull + (h << 6) + (h >> 2); + h *= 1099511628211ull; + return static_cast(h ^ (h >> 32)); +} + +} // namespace + +DimensionConfig DimensionConfig::forDimension(DimensionId id) { + DimensionConfig cfg; + cfg.id = id; + switch (id) { + case DimensionId::Nether: + cfg.floorY = 0; + cfg.ceilingY = 128; + cfg.hasSky = false; + cfg.hasCeiling = true; + cfg.hasDayCycle = false; + cfg.waterEvaporates = true; + cfg.bedsExplode = true; + cfg.coordinateScale = 8.0f; + cfg.ambientLight = 0.10f; + cfg.seedSalt = 0x4e544852u; + break; + case DimensionId::End: + cfg.floorY = 0; + cfg.ceilingY = 128; + cfg.hasSky = false; + cfg.hasCeiling = false; + cfg.hasDayCycle = false; + cfg.waterEvaporates = false; + cfg.bedsExplode = true; + cfg.coordinateScale = 1.0f; + cfg.ambientLight = 0.04f; + cfg.seedSalt = 0x454e4421u; + break; + default: + cfg.floorY = 0; + cfg.ceilingY = 128; + cfg.hasSky = true; + cfg.hasCeiling = false; + cfg.hasDayCycle = true; + cfg.waterEvaporates = false; + cfg.bedsExplode = false; + cfg.coordinateScale = 1.0f; + cfg.ambientLight = 0.0f; + cfg.seedSalt = 0u; + break; + } + return cfg; +} + +const char* DimensionConfig::nameOf(DimensionId id) { + switch (id) { + case DimensionId::Nether: return "nether"; + case DimensionId::End: return "end"; + default: return "overworld"; + } +} + +int DimensionConfig::clampY(int y) const { + int low = lowestPlaceableY(); + int high = highestPlaceableY(); + if (y < low) return low; + if (y > high) return high; + return y; +} + +Dimension::Dimension(const DimensionConfig& dimensionConfig, uint32_t worldSeed) + : cfg(dimensionConfig), derivedSeed(deriveSeed(worldSeed, dimensionConfig.seedSalt)) { + switch (cfg.id) { + case DimensionId::Nether: { + NetherGenerator::Params netherParams; + netherParams.seed = derivedSeed; + netherParams.floorY = cfg.floorY; + netherParams.ceilingY = cfg.ceilingY; + nether = std::make_unique(netherParams); + break; + } + case DimensionId::End: { + EndGenerator::Params endParams; + endParams.seed = derivedSeed; + endParams.floorY = cfg.floorY; + endParams.ceilingY = cfg.ceilingY; + end = std::make_unique(endParams); + break; + } + default: { + WorldGenParams overworldParams; + overworldParams.seed = derivedSeed; + overworldParams.maxY = cfg.ceilingY; + overworld = std::make_unique(overworldParams); + break; + } + } +} + +bool Dimension::columnGenerated(int columnX, int columnZ) const { + return columns.find(Vec3i{columnX, 0, columnZ}) != columns.end(); +} + +void Dimension::generateColumn(int columnX, int columnZ) { + if (!columns.insert(Vec3i{columnX, 0, columnZ}).second) return; + + if (nether) nether->generateColumn(blocks, columnX, columnZ); + else if (end) end->generateColumn(blocks, columnX, columnZ); + else overworld->generateColumn(blocks, columnX, columnZ); +} + +int Dimension::generateArea(int columnX, int columnZ, int columnRadius) { + int made = 0; + for (int dx = -columnRadius; dx <= columnRadius; ++dx) { + for (int dz = -columnRadius; dz <= columnRadius; ++dz) { + if (columnGenerated(columnX + dx, columnZ + dz)) continue; + generateColumn(columnX + dx, columnZ + dz); + ++made; + } + } + return made; +} + +float Dimension::dayFraction() const { + if (!cfg.hasDayCycle) return 0.0f; + long long phase = elapsed % ticksPerDay; + if (phase < 0) phase += ticksPerDay; + return static_cast(phase) / static_cast(ticksPerDay); +} + +int Dimension::highestSolidY(int gx, int gz) const { + for (int y = cfg.ceilingY - 1; y >= cfg.floorY; --y) + if (blocks.solidAt(gx, y, gz)) return y; + return cfg.floorY - 1; +} +