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:
parent
56c88eca52
commit
b25c38cb8f
src/main/java/chylex/hee/game/world
feature/basic/blobs
territory/generators
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package chylex.hee.game.world.feature.basic.blobs
|
||||
|
||||
enum class BlobSmoothing{
|
||||
NONE,
|
||||
MILD,
|
||||
FULL
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user