1
0
mirror of https://github.com/chylex/Hardcore-Ender-Expansion-2.git synced 2025-04-11 03:15:44 +02:00

Rework Blob generation and populator picking

This commit is contained in:
chylex 2019-08-10 17:35:58 +02:00
parent 56c88eca52
commit b25c38cb8f
8 changed files with 138 additions and 50 deletions

View File

@ -1,32 +1,45 @@
package chylex.hee.game.world.feature.basic.blobs
import chylex.hee.game.world.feature.basic.blobs.BlobSmoothing.FULL
import chylex.hee.game.world.feature.basic.blobs.BlobSmoothing.MILD
import chylex.hee.game.world.generation.SegmentedWorld
import chylex.hee.game.world.generation.segments.SegmentFull
import chylex.hee.init.ModBlocks
import chylex.hee.system.collection.WeightedList
import chylex.hee.system.collection.WeightedList.Companion.weightedListOf
import chylex.hee.system.util.Facing6
import chylex.hee.system.util.allInBoxMutable
import chylex.hee.system.util.allInCenteredSphereMutable
import chylex.hee.system.util.ceilToInt
import chylex.hee.system.util.max
import net.minecraft.block.Block
import net.minecraft.init.Blocks
import net.minecraft.util.math.BlockPos
import java.util.Random
class BlobGenerator(private val patterns: WeightedList<BlobPattern>){
constructor(pattern: BlobPattern) : this(weightedListOf(1 to pattern))
object BlobGenerator{
private val SCAFFOLDING = ModBlocks.SCAFFOLDING.defaultState
private val BASE = Blocks.END_STONE
private companion object{
private val SCAFFOLDING = ModBlocks.SCAFFOLDING.defaultState
fun place(world: SegmentedWorld, center: BlockPos, radius: Double, block: Block = BASE): Boolean{
val offset = radius.ceilToInt()
if (!world.isInside(center) || Facing6.any { !world.isInside(center.offset(it, offset)) }){
return false
}
for(pos in center.allInCenteredSphereMutable(radius)){
world.setBlock(pos, block)
}
return true
}
fun generate(world: SegmentedWorld, rand: Random, pos: BlockPos): Boolean{
val pattern = patterns.generateItem(rand)
fun generate(world: SegmentedWorld, rand: Random, center: BlockPos, smoothing: BlobSmoothing, pattern: BlobPattern): Boolean{
val generator = pattern.pickGenerator(rand)
val populators = pattern.pickPopulators(rand)
val extraSize = populators.fold(BlockPos.ORIGIN){ acc, populator -> acc.max(populator.expandSizeBy) }
val allocatedSize = generator.size.expand(extraSize)
val origin = pos.subtract(allocatedSize.centerPos)
val origin = center.subtract(allocatedSize.centerPos)
if (!allocatedSize.toBoundingBox(origin).isInside(world.worldSize.toBoundingBox(BlockPos.ORIGIN))){
return false
@ -35,8 +48,14 @@ class BlobGenerator(private val patterns: WeightedList<BlobPattern>){
val blobWorld = SegmentedWorld(rand, allocatedSize, allocatedSize){ SegmentFull(allocatedSize, SCAFFOLDING) }
generator.generate(blobWorld, rand)
runSmoothingPass(blobWorld, adjacentAirCount = 4)
runSmoothingPass(blobWorld, adjacentAirCount = 5)
if (smoothing == FULL){
runSmoothingPass(blobWorld, adjacentAirCount = 4)
}
if (smoothing == FULL || smoothing == MILD){
runSmoothingPass(blobWorld, adjacentAirCount = 5)
}
for(populator in populators){
populator.generate(blobWorld, rand)
@ -61,7 +80,7 @@ class BlobGenerator(private val patterns: WeightedList<BlobPattern>){
val size = blobWorld.worldSize
for(pos in size.minPos.allInBoxMutable(size.maxPos)){
if (blobWorld.getBlock(pos) === Blocks.END_STONE && Facing6.count { facing -> pos.offset(facing).let { !blobWorld.isInside(it) || blobWorld.getState(it) === SCAFFOLDING } } >= adjacentAirCount){
if (blobWorld.getBlock(pos) === BASE && Facing6.count { facing -> pos.offset(facing).let { !blobWorld.isInside(it) || blobWorld.getState(it) === SCAFFOLDING } } >= adjacentAirCount){
blobWorld.setState(pos, SCAFFOLDING)
}
}

View File

@ -1,31 +1,34 @@
package chylex.hee.game.world.feature.basic.blobs
import chylex.hee.system.collection.WeightedList
import chylex.hee.system.collection.WeightedList.Companion.weightedListOf
import java.util.Random
class BlobPattern private constructor(
private val generators: WeightedList<IBlobGenerator>,
private val populators: Pair<WeightedList<IBlobPopulator>, ((Random) -> Int)>?
private val populators: List<IPopulatorPicker>?
){
constructor(generators: WeightedList<IBlobGenerator>) : this(generators, null)
constructor(generators: WeightedList<IBlobGenerator>, populators: PopulatorBuilder) : this(generators, populators.build())
constructor(generators: WeightedList<IBlobGenerator>, populators: WeightedList<IBlobPopulator>, populatorAmount: (Random) -> Int) : this(
generators, populators to populatorAmount
)
constructor(generator: IBlobGenerator) : this(weightedListOf(1 to generator), null)
constructor(generator: IBlobGenerator, populators: PopulatorBuilder) : this(weightedListOf(1 to generator), populators.build())
fun pickGenerator(rand: Random): IBlobGenerator{
return generators.generateItem(rand)
}
fun pickPopulators(rand: Random): List<IBlobPopulator>{
val (list, amount) = populators ?: return emptyList()
val remaining = list.mutableCopy()
val pickers = populators ?: return emptyList()
val picked = mutableListOf<IBlobPopulator>()
repeat(amount(rand)){
remaining.removeItem(rand)?.let(picked::add)
for(picker in pickers){
picker.pick(rand, picked)
}
return picked
}
interface IPopulatorPicker{
fun pick(rand: Random, list: MutableList<IBlobPopulator>)
}
}

View File

@ -0,0 +1,7 @@
package chylex.hee.game.world.feature.basic.blobs
enum class BlobSmoothing{
NONE,
MILD,
FULL
}

View File

@ -1,31 +1,9 @@
package chylex.hee.game.world.feature.basic.blobs
import chylex.hee.game.world.generation.SegmentedWorld
import chylex.hee.game.world.util.Size
import chylex.hee.system.util.Facing6
import chylex.hee.system.util.allInCenteredSphereMutable
import chylex.hee.system.util.ceilToInt
import net.minecraft.block.Block
import net.minecraft.init.Blocks
import net.minecraft.util.math.BlockPos
import java.util.Random
interface IBlobGenerator{
val size: Size
fun generate(world: SegmentedWorld, rand: Random)
companion object{
fun placeBlob(world: SegmentedWorld, center: BlockPos, radius: Double, block: Block = Blocks.END_STONE): Boolean{
val offset = radius.ceilToInt()
if (!world.isInside(center) || Facing6.any { !world.isInside(center.offset(it, offset)) }){
return false
}
for(pos in center.allInCenteredSphereMutable(offset, avoidNipples = true)){
world.setBlock(pos, block)
}
return true
}
}
}

View File

@ -0,0 +1,78 @@
package chylex.hee.game.world.feature.basic.blobs
import chylex.hee.game.world.feature.basic.blobs.BlobPattern.IPopulatorPicker
import chylex.hee.system.collection.WeightedList
import java.util.Random
class PopulatorBuilder{
private val list = mutableListOf<IPopulatorPicker>()
fun custom(picker: IPopulatorPicker){
list.add(picker)
}
fun guarantee(vararg populators: IBlobPopulator){
list.add(Guarantee(*populators))
}
fun pick(options: WeightedList<IBlobPopulator>, amount: (Random) -> Int, keepOrder: Boolean){
if (keepOrder){
list.add(PickOrdered(options, amount))
}
else{
list.add(PickUnordered(options, amount))
}
}
fun shuffle(){
list.add(Shuffle)
}
fun build(): List<IPopulatorPicker>{
return list.toList()
}
// Implementations
private class Guarantee(private vararg val populators: IBlobPopulator) : IPopulatorPicker{
override fun pick(rand: Random, list: MutableList<IBlobPopulator>){
list.addAll(populators)
}
}
private class PickOrdered(private val options: WeightedList<IBlobPopulator>, private val amount: (Random) -> Int) : IPopulatorPicker{
override fun pick(rand: Random, list: MutableList<IBlobPopulator>){
val total = amount(rand)
if (total > 0){
val remaining = options.mutableCopy()
val picked = mutableListOf<IBlobPopulator>()
repeat(total){
remaining.removeItem(rand)?.let(picked::add)
}
list.addAll(picked.sortedBy(options.values::indexOf))
}
}
}
private class PickUnordered(private val options: WeightedList<IBlobPopulator>, private val amount: (Random) -> Int) : IPopulatorPicker{
override fun pick(rand: Random, list: MutableList<IBlobPopulator>){
val total = amount(rand)
if (total > 0){
val remaining = options.mutableCopy()
repeat(total){
remaining.removeItem(rand)?.let(list::add)
}
}
}
}
private object Shuffle : IPopulatorPicker{
override fun pick(rand: Random, list: MutableList<IBlobPopulator>){
list.shuffle(rand)
}
}
}

View File

@ -1,4 +1,5 @@
package chylex.hee.game.world.feature.basic.blobs.impl
import chylex.hee.game.world.feature.basic.blobs.BlobGenerator
import chylex.hee.game.world.feature.basic.blobs.IBlobGenerator
import chylex.hee.game.world.generation.SegmentedWorld
import chylex.hee.game.world.util.Size
@ -61,7 +62,7 @@ open class BlobGeneratorAttaching(
world.worldSize.centerPos to radius(0, rand)
)
if (!IBlobGenerator.placeBlob(world, generated[0].first, generated[0].second)){
if (!BlobGenerator.place(world, generated[0].first, generated[0].second)){
return
}
@ -72,7 +73,7 @@ open class BlobGeneratorAttaching(
val nextRad = radius(1 + it, rand)
val nextDistance = (attachRad + nextRad) * distance(rand)
if (IBlobGenerator.placeBlob(world, Pos(attachPos.center.add(rand.nextVector(nextDistance))), nextRad)){
if (BlobGenerator.place(world, Pos(attachPos.center.add(rand.nextVector(nextDistance))), nextRad)){
break
}
}

View File

@ -1,4 +1,5 @@
package chylex.hee.game.world.feature.basic.blobs.impl
import chylex.hee.game.world.feature.basic.blobs.BlobGenerator
import chylex.hee.game.world.feature.basic.blobs.IBlobGenerator
import chylex.hee.game.world.generation.SegmentedWorld
import chylex.hee.game.world.util.Size
@ -12,6 +13,6 @@ class BlobGeneratorSingle(
override val size = Size(1 + (radius.max.ceilToInt() * 2))
override fun generate(world: SegmentedWorld, rand: Random){
IBlobGenerator.placeBlob(world, world.worldSize.centerPos, radius(rand))
BlobGenerator.place(world, world.worldSize.centerPos, radius(rand))
}
}

View File

@ -7,6 +7,7 @@ import chylex.hee.game.world.feature.basic.NoiseGenerator.NoiseValue
import chylex.hee.game.world.feature.basic.PortalGenerator
import chylex.hee.game.world.feature.basic.blobs.BlobGenerator
import chylex.hee.game.world.feature.basic.blobs.BlobPattern
import chylex.hee.game.world.feature.basic.blobs.BlobSmoothing
import chylex.hee.game.world.feature.basic.blobs.impl.BlobGeneratorAttaching
import chylex.hee.game.world.feature.basic.blobs.impl.BlobGeneratorAttaching.AttachingStrategy.FIRST_BLOB
import chylex.hee.game.world.feature.basic.blobs.impl.BlobGeneratorSingle
@ -511,8 +512,8 @@ object Generator_TheHub : ITerritoryGenerator{
}
private object EndstoneBlobs{
private val BLOB = BlobGenerator(BlobPattern(
generators = weightedListOf(
private val BLOB = BlobPattern(
weightedListOf(
75 to BlobGeneratorAttaching(
amount = { rand -> rand.nextInt(2, rand.nextInt(3, 4)) },
radiusFirst = Linear(2.6, 4.4),
@ -525,7 +526,7 @@ object Generator_TheHub : ITerritoryGenerator{
radius = Linear(2.3, 4.6)
)
)
))
)
fun generate(world: SegmentedWorld, rand: Random, size: Size){
val center = size.centerPos
@ -538,7 +539,7 @@ object Generator_TheHub : ITerritoryGenerator{
rand.nextInt(5, size.maxZ - 5)
)
if (blobPos.distanceSqTo(center) > square(MainIsland.RADIUS + 28.0) && BLOB.generate(world, rand, blobPos)){
if (blobPos.distanceSqTo(center) > square(MainIsland.RADIUS + 28.0) && BlobGenerator.generate(world, rand, blobPos, BlobSmoothing.FULL, BLOB)){
break
}
}