1
0
mirror of https://github.com/chylex/Hardcore-Ender-Expansion-2.git synced 2025-09-15 23:32:08 +02:00

4 Commits

16 changed files with 1389 additions and 25 deletions

View File

@@ -7,7 +7,7 @@ import java.util.Random
import java.util.stream.IntStream
import kotlin.math.pow
sealed class NoiseGenerator(private val xScale: Double, private val zScale: Double) {
sealed class Noise2D(private val xScale: Double, private val zScale: Double) {
// Generation
@@ -27,7 +27,7 @@ sealed class NoiseGenerator(private val xScale: Double, private val zScale: Doub
// Implementations
open class OldPerlin(rand: Random, private val xScale: Double, private val zScale: Double, octaves: Int) : NoiseGenerator(xScale, zScale) {
open class OldPerlin(rand: Random, private val xScale: Double, private val zScale: Double, octaves: Int) : Noise2D(xScale, zScale) {
constructor(rand: Random, scale: Double, octaves: Int) : this(rand, scale, scale, octaves)
private val noiseLevels = Array(octaves) { SimplexNoiseGenerator(rand) }
@@ -55,7 +55,7 @@ sealed class NoiseGenerator(private val xScale: Double, private val zScale: Doub
override fun getRawValue(x: Double, z: Double) = (super.getRawValue(x, z) + normalizationApproxBoundary) / (2 * normalizationApproxBoundary)
}
class NewPerlin(rand: Random, private val xScale: Double, private val zScale: Double, octaves: Int) : NoiseGenerator(xScale, zScale) {
class NewPerlin(rand: Random, private val xScale: Double, private val zScale: Double, octaves: Int) : Noise2D(xScale, zScale) {
constructor(rand: Random, scale: Double, octaves: Int) : this(rand, scale, scale, octaves)
private val generator = PerlinNoiseGenerator(SharedSeedRandom(rand.nextLong()), IntStream.rangeClosed(-octaves + 1, 0))

View File

@@ -0,0 +1,75 @@
package chylex.hee.game.world.generation.noise
import chylex.hee.game.world.generation.noise.OpenSimplex2S.GenerateContext3D
import chylex.hee.game.world.generation.noise.OpenSimplex2S.LatticeOrientation3D
import chylex.hee.util.math.MutablePos
import net.minecraft.util.math.BlockPos
import java.util.Random
sealed class Noise3D {
// Generation
abstract fun getRawValue(x: Double, y: Double, z: Double): Double
// Implementations
sealed class SuperSimplex(rand: Random) : Noise3D() {
protected val generator = OpenSimplex2S(rand.nextLong())
protected abstract val orientation: LatticeOrientation3D
class AreaNoise(val buffer: Array<Array<DoubleArray>>) {
operator fun get(x: Int, y: Int, z: Int) = buffer[z][y][x]
inline fun forEach(callback: (BlockPos.Mutable, Double) -> Unit) {
val pos = MutablePos()
for ((z, yArray) in buffer.withIndex()) {
pos.z = z
for ((y, xArray) in yArray.withIndex()) {
pos.y = y
for ((x, value) in xArray.withIndex()) {
pos.x = x
callback(pos, value)
}
}
}
}
}
class AreaGenerator(private val generator: OpenSimplex2S, private val context: GenerateContext3D) {
fun generate(x: Int, y: Int, z: Int, xSize: Int, ySize: Int, zSize: Int): AreaNoise {
val buffer = Array(zSize) { Array(ySize) { DoubleArray(xSize) { 0.0 } } }
return buffer.apply { generator.generate3(context, this, x, y, z) }.let(::AreaNoise)
}
}
fun getAreaGenerator(xFreq: Double, yFreq: Double, zFreq: Double, amplitude: Double): AreaGenerator {
return AreaGenerator(generator, GenerateContext3D(orientation, xFreq, yFreq, zFreq, amplitude))
}
fun getAreaGenerator(xzFreq: Double, yFreq: Double, amplitude: Double): AreaGenerator {
return getAreaGenerator(xzFreq, yFreq, xzFreq, amplitude)
}
class Classic(rand: Random) : SuperSimplex(rand) {
override val orientation
get() = LatticeOrientation3D.Classic
override fun getRawValue(x: Double, y: Double, z: Double): Double {
return generator.noise3_Classic(x, y, z)
}
}
class Terrain(rand: Random) : SuperSimplex(rand) {
override val orientation
get() = LatticeOrientation3D.XZBeforeY
override fun getRawValue(x: Double, y: Double, z: Double): Double {
return generator.noise3_XZBeforeY(x, y, z)
}
}
}
}

View File

@@ -1,11 +1,26 @@
package chylex.hee.game.world.generation.structure.world.segments
import chylex.hee.game.world.generation.structure.world.segments.ISegment.Companion.index
import chylex.hee.util.math.MutablePos
import chylex.hee.util.math.Size
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
/**
* A segment that supports arbitrary block states in arbitrary positions.
*/
class SegmentFull(private val size: Size, fillState: BlockState) : ISegment {
constructor(size: Size, fillState: BlockState, posToStateMap: Long2ObjectOpenHashMap<BlockState>) : this(size, fillState) {
val pos = MutablePos()
val iter = posToStateMap.long2ObjectEntrySet().fastIterator()
while (iter.hasNext()) {
val entry = iter.next()
pos.setPos(entry.longKey)
data[index(pos, size)] = entry.value
}
}
private val data = Array(size.x * size.y * size.z) { fillState }
override fun getState(pos: BlockPos): BlockState {

View File

@@ -0,0 +1,43 @@
package chylex.hee.game.world.generation.structure.world.segments
import chylex.hee.util.math.Size
import chylex.hee.util.math.floorToInt
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import kotlin.math.pow
/**
* A segment mostly filled with one type of block state, but allowing for a few [exceptions].
* When the amount of exceptions reaches a [threshold] based on the segment size, it gets converted to [SegmentFull].
*/
class SegmentMultiState(private val size: Size, private val fillState: BlockState) : ISegment {
constructor(size: Size, block: Block) : this(size, block.defaultState)
/**
* The threshold power was determined using the WorldSegmentProfiling test.
* The segment gets converted when the amount of memory used by the [exceptions] map reaches roughly half of the memory used by the [SegmentFull] array.
*/
private val threshold = (size.x * size.y * size.z).toDouble().pow(0.825).floorToInt()
private val exceptions = Long2ObjectOpenHashMap<BlockState>(threshold, 0.75F).apply { defaultReturnValue(fillState) }
override fun getState(pos: BlockPos): BlockState {
return exceptions.get(pos.toLong())
}
override fun withState(pos: BlockPos, state: BlockState): ISegment {
if (state == fillState) {
exceptions.remove(pos.toLong())
return this
}
@Suppress("ReplacePutWithAssignment")
exceptions.put(pos.toLong(), state) // kotlin indexer boxes the values
return if (exceptions.size < threshold)
this
else
SegmentFull(size, fillState, exceptions)
}
}

View File

@@ -5,6 +5,10 @@ import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
/**
* A segment completely filled with only one type of block state.
* When any block state is changed, gets converted to [SegmentMultiState].
*/
class SegmentSingleState(private val size: Size, private val fillState: BlockState) : ISegment {
constructor(size: Size, block: Block) : this(size, block.defaultState)
@@ -16,6 +20,6 @@ class SegmentSingleState(private val size: Size, private val fillState: BlockSta
return if (state == fillState)
this
else
SegmentFull(size, fillState).withState(pos, state)
SegmentMultiState(size, fillState).withState(pos, state)
}
}

View File

@@ -4,11 +4,13 @@ import chylex.hee.game.mechanics.causatum.CausatumStage
import chylex.hee.game.mechanics.causatum.CausatumStage.S2_ENTERED_END
import chylex.hee.game.mechanics.causatum.CausatumStage.S3_FINISHED_CURSED_LIBRARY
import chylex.hee.game.territory.description.Territory_ArcaneConjunctions
import chylex.hee.game.territory.description.Territory_EnderCity
import chylex.hee.game.territory.description.Territory_ForgottenTombs
import chylex.hee.game.territory.description.Territory_LostGarden
import chylex.hee.game.territory.description.Territory_ObsidianTowers
import chylex.hee.game.territory.description.Territory_TheHub
import chylex.hee.game.territory.generator.Generator_ArcaneConjunctions
import chylex.hee.game.territory.generator.Generator_EnderCity
import chylex.hee.game.territory.generator.Generator_ForgottenTombs
import chylex.hee.game.territory.generator.Generator_LostGarden
import chylex.hee.game.territory.generator.Generator_ObsidianTowers
@@ -79,8 +81,8 @@ enum class TerritoryType(
ENDER_CITY(
title = "ender_city",
desc = TerritoryDummy,
gen = GeneratorDummy,
desc = Territory_EnderCity,
gen = Generator_EnderCity,
chunks = 52,
height = 128 until 256
),

View File

@@ -0,0 +1,35 @@
package chylex.hee.game.territory.description
import chylex.hee.game.territory.system.ITerritoryDescription
import chylex.hee.game.territory.system.properties.TerritoryColors
import chylex.hee.game.territory.system.properties.TerritoryDifficulty
import chylex.hee.game.territory.system.properties.TerritoryEnvironment
import chylex.hee.system.random.nextFloat
import chylex.hee.util.color.RGB
import java.util.Random
object Territory_EnderCity : ITerritoryDescription {
override val difficulty
get() = TerritoryDifficulty.HOSTILE
override val colors = object : TerritoryColors() {
override val tokenTop = RGB(172, 123, 172)
override val tokenBottom = RGB(214, 216, 157)
override val portalSeed = 3527911999188999811L
override fun nextPortalColor(rand: Random, color: FloatArray) {
color[0] = rand.nextFloat(0.5F, 1F)
color[1] = color[0] - rand.nextFloat(0F, 0.2F)
color[2] = rand.nextFloat(0.1F, 0.3F)
}
}
override val environment = object : TerritoryEnvironment() {
override val fogColor = RGB(240, 255, 32).asVec
override val fogDensity = 0.001F
override val voidRadiusMpXZ = 1.1F
override val voidRadiusMpY = 1.1F
}
}

View File

@@ -16,7 +16,7 @@ object Territory_LostGarden : ITerritoryDescription {
get() = TerritoryDifficulty.PEACEFUL
override val colors = object : TerritoryColors() {
override val tokenTop = RGB(148, 169, 54)
override val tokenTop = RGB(148, 169, 54)
override val tokenBottom = RGB( 98, 93, 102)
override val dryVines = RGB(133, 117, 72).i

View File

@@ -0,0 +1,14 @@
package chylex.hee.game.territory.generator
import chylex.hee.game.territory.system.ITerritoryGenerator
import chylex.hee.game.territory.system.TerritoryGenerationInfo
import chylex.hee.game.world.generation.structure.world.SegmentedWorld
import chylex.hee.util.math.Size
object Generator_EnderCity : ITerritoryGenerator {
override val segmentSize = Size(32, 8, 32)
override fun provide(world: SegmentedWorld): TerritoryGenerationInfo {
TODO("not implemented")
}
}

View File

@@ -11,7 +11,7 @@ import chylex.hee.game.world.generation.feature.basic.PortalGenerator
import chylex.hee.game.world.generation.feature.tombdungeon.TombDungeonBuilder
import chylex.hee.game.world.generation.feature.tombdungeon.TombDungeonPieces
import chylex.hee.game.world.generation.feature.tombdungeon.piece.TombDungeonStart
import chylex.hee.game.world.generation.noise.NoiseGenerator
import chylex.hee.game.world.generation.noise.Noise2D
import chylex.hee.game.world.generation.ore.OreGenerator
import chylex.hee.game.world.generation.ore.impl.OreTechniqueAdjacent
import chylex.hee.game.world.generation.ore.impl.withAdjacentAirCheck
@@ -117,8 +117,8 @@ object Generator_ForgottenTombs : ITerritoryGenerator {
val radXZ = RADIUS_XZ.toFloat()
val radY = RADIUS_Y.toFloat()
val noiseXZ = NoiseGenerator.OldPerlinNormalized(rand, scale = 24.0, octaves = 2)
val noiseY = NoiseGenerator.OldPerlinNormalized(rand, scale = 48.0, octaves = 2)
val noiseXZ = Noise2D.OldPerlinNormalized(rand, scale = 24.0, octaves = 2)
val noiseY = Noise2D.OldPerlinNormalized(rand, scale = 48.0, octaves = 2)
for ((x, y, z) in BlockPos.ZERO.allInCenteredBoxMutable(RADIUS_XZ, RADIUS_Y, RADIUS_XZ)) {
val normalizedY = remapRange(y.toFloat(), -radY..radY, 0F..1F)

View File

@@ -24,7 +24,8 @@ import chylex.hee.game.world.generation.cave.impl.CavePatherRotatingBase
import chylex.hee.game.world.generation.cave.impl.CaveRadiusSine
import chylex.hee.game.world.generation.feature.basic.AutumnTreeGenerator
import chylex.hee.game.world.generation.feature.basic.PortalGenerator
import chylex.hee.game.world.generation.noise.NoiseGenerator
import chylex.hee.game.world.generation.noise.Noise2D
import chylex.hee.game.world.generation.noise.Noise3D
import chylex.hee.game.world.generation.noise.NoiseValue
import chylex.hee.game.world.generation.structure.world.ScaffoldedWorld
import chylex.hee.game.world.generation.structure.world.SegmentedWorld
@@ -186,19 +187,23 @@ object Generator_LostGarden : ITerritoryGenerator {
private class Island(val offset: BlockPos, val radius: Double, val height: Double) {
fun generate(world: SegmentedWorld, rand: Random) {
val noiseXZ = NoiseGenerator.OldPerlinNormalized(rand, scale = 80.0, octaves = 3)
val noiseXY = NoiseGenerator.OldPerlinNormalized(rand, scale = 32.0, octaves = 2)
val noiseZY = NoiseGenerator.OldPerlinNormalized(rand, scale = 32.0, octaves = 2)
val noise3D = Noise3D.SuperSimplex.Terrain(rand).getAreaGenerator(xzFreq = 16.0, yFreq = 8.0, amplitude = 2.0)
val noiseValley = NoiseGenerator.OldPerlinNormalized(rand, scale = 136.0, octaves = 1)
val noiseThreshold = NoiseGenerator.OldPerlinNormalized(rand, scale = 44.0, octaves = 2)
val noiseEndersol = NoiseGenerator.OldPerlinNormalized(rand, scale = 32.0, octaves = 3)
val noiseXZ = Noise2D.OldPerlinNormalized(rand, scale = 40.0, octaves = 5)
val noiseXY = Noise2D.OldPerlinNormalized(rand, scale = 32.0, octaves = 2)
val noiseZY = Noise2D.OldPerlinNormalized(rand, scale = 32.0, octaves = 2)
val noiseValley = Noise2D.OldPerlinNormalized(rand, scale = 136.0, octaves = 1)
val noiseThreshold = Noise2D.OldPerlinNormalized(rand, scale = 44.0, octaves = 2)
val noiseEndersol = Noise2D.OldPerlinNormalized(rand, scale = 32.0, octaves = 3)
val maxDistXZ = radius.ceilToInt()
val baseDistY = (height * 0.32).ceilToInt()
val minDistY = -baseDistY
val maxDistY = baseDistY * 2
// val noise = noise3D.generate(0, 0, 0, maxDistXZ, maxDistY, maxDistXZ)
for (x in -maxDistXZ..maxDistXZ) for (z in -maxDistXZ..maxDistXZ) {
val distRatioXZ = square(sqrt((square(x) + square(z)).toDouble()) / radius)
@@ -466,8 +471,8 @@ object Generator_LostGarden : ITerritoryGenerator {
val top = xz.withY(size.maxY).offsetUntil(DOWN, offsetRange) { !world.isAir(it) } ?: continue
if (center.y - bottom.y > 12 && top.y - center.y > 16) {
val noise1 = NoiseGenerator.OldPerlinNormalized(rand, scale = 8.0, octaves = 2)
val noise2 = NoiseGenerator.OldPerlinNormalized(rand, xScale = 8.0, zScale = 4.0, octaves = 2)
val noise1 = Noise2D.OldPerlinNormalized(rand, scale = 8.0, octaves = 2)
val noise2 = Noise2D.OldPerlinNormalized(rand, xScale = 8.0, zScale = 4.0, octaves = 2)
val radiusX = rand.nextFloat(11.0, 16.0)
val radiusY = rand.nextFloat(7.2, 9.4)

View File

@@ -21,7 +21,7 @@ import chylex.hee.game.world.generation.feature.basic.PortalGenerator
import chylex.hee.game.world.generation.feature.obsidiantower.ObsidianTowerBuilder
import chylex.hee.game.world.generation.feature.obsidiantower.ObsidianTowerPieces
import chylex.hee.game.world.generation.feature.obsidiantower.piece.ObsidianTowerLevel_Top
import chylex.hee.game.world.generation.noise.NoiseGenerator
import chylex.hee.game.world.generation.noise.Noise2D
import chylex.hee.game.world.generation.ore.IOreTechnique
import chylex.hee.game.world.generation.ore.OreGenerator
import chylex.hee.game.world.generation.ore.impl.OreTechniqueSingle
@@ -275,7 +275,7 @@ object Generator_ObsidianTowers : ITerritoryGenerator {
var offset = 0.0
val tested = mutableSetOf<BlockPos>()
val noise = NoiseGenerator.OldPerlinNormalized(rand, scale = 3.0, octaves = 1)
val noise = Noise2D.OldPerlinNormalized(rand, scale = 3.0, octaves = 1)
while (offset < dist) {
offset += 0.33

View File

@@ -19,7 +19,7 @@ import chylex.hee.game.world.generation.cave.impl.CaveCarverSphere
import chylex.hee.game.world.generation.cave.impl.CavePatherRotatingBase
import chylex.hee.game.world.generation.cave.impl.CaveRadiusSine
import chylex.hee.game.world.generation.feature.basic.PortalGenerator
import chylex.hee.game.world.generation.noise.NoiseGenerator
import chylex.hee.game.world.generation.noise.Noise2D
import chylex.hee.game.world.generation.noise.NoiseValue
import chylex.hee.game.world.generation.ore.OreGenerator
import chylex.hee.game.world.generation.ore.impl.OreTechniqueAdjacent
@@ -101,9 +101,9 @@ object Generator_TheHub : ITerritoryGenerator {
const val ELEVATION_BOTTOM = 13.0
fun generate(world: SegmentedWorld, rand: Random, size: Size) {
val noiseIslandTop = NoiseGenerator.OldPerlinNormalized(rand, scale = 48.0, octaves = 2)
val noiseIslandBottom = NoiseGenerator.OldPerlinNormalized(rand, scale = 104.0, octaves = 1)
val noiseIslandEdge = NoiseGenerator.OldPerlinNormalized(rand, scale = 52.0, octaves = 1)
val noiseIslandTop = Noise2D.OldPerlinNormalized(rand, scale = 48.0, octaves = 2)
val noiseIslandBottom = Noise2D.OldPerlinNormalized(rand, scale = 104.0, octaves = 1)
val noiseIslandEdge = Noise2D.OldPerlinNormalized(rand, scale = 52.0, octaves = 1)
val centerPos = size.centerPos

View File

@@ -0,0 +1,5 @@
package chylex.hee.test.main
fun main() {
}

View File

@@ -0,0 +1,46 @@
package chylex.hee.test.main
import chylex.hee.game.world.generation.structure.world.segments.ISegment
import chylex.hee.game.world.generation.structure.world.segments.SegmentFull
import chylex.hee.game.world.generation.structure.world.segments.SegmentMultiState
import chylex.hee.game.world.util.allInBoxMutable
import chylex.hee.util.math.Pos
import chylex.hee.util.math.Size
import net.minecraft.block.Blocks
import net.minecraft.util.registry.Bootstrap
fun main() {
println("Initializing...")
Bootstrap.register()
val segments = mutableListOf<ISegment>()
val thresholdField = SegmentMultiState::class.java.getDeclaredField("threshold").also { it.isAccessible = true }
println("Waiting 20 seconds for data fixers...")
Thread.sleep(20000L)
println("Waiting 4 seconds to start memory profiling...")
Thread.sleep(4000L)
for (s in 8..128 step 12) {
var segment: ISegment = SegmentMultiState(Size(s), Blocks.AIR).also(segments::add)
val threshold = thresholdField.getInt(segment)
println("Testing size $s (total ${s * s * s} threshold $threshold)")
for ((index, pos) in Pos(0, 0, 0).allInBoxMutable(Pos(s - 1, s - 1, s - 1)).withIndex()) {
if (index < threshold - 1) {
segment = segment.withState(pos, Blocks.END_STONE.defaultState)
continue
}
check(segment is SegmentMultiState)
segment = segment.withState(pos, Blocks.END_STONE.defaultState).also(segments::add)
check(segment is SegmentFull)
break
}
}
println("Done!")
readLine()
segments.toString()
}