diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/WorldGenerator.kt')
-rw-r--r-- | src/main/kotlin/io/dico/parcels2/WorldGenerator.kt | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt new file mode 100644 index 0000000..8f6a3aa --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt @@ -0,0 +1,263 @@ +package io.dico.parcels2 + +import io.dico.parcels2.math.Vec2i +import io.dico.parcels2.math.clamp +import io.dico.parcels2.math.even +import io.dico.parcels2.math.umod +import org.bukkit.* +import org.bukkit.Bukkit.createBlockData +import org.bukkit.block.Biome +import org.bukkit.block.Block +import org.bukkit.block.BlockFace +import org.bukkit.block.Skull +import org.bukkit.block.data.BlockData +import org.bukkit.block.data.type.Sign +import org.bukkit.block.data.type.Slab +import org.bukkit.entity.Entity +import org.bukkit.generator.BlockPopulator +import org.bukkit.generator.ChunkGenerator +import java.util.* +import kotlin.coroutines.experimental.buildIterator +import kotlin.reflect.KClass + +abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider { + abstract val world: ParcelWorld + + abstract val factory: GeneratorFactory + + abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData + + abstract fun populate(world: World?, random: Random?, chunk: Chunk?) + + abstract override fun getFixedSpawnLocation(world: World?, random: Random?): Location + + override fun getDefaultPopulators(world: World?): MutableList<BlockPopulator> { + return Collections.singletonList(object : BlockPopulator() { + override fun populate(world: World?, random: Random?, chunk: Chunk?) { + this@ParcelGenerator.populate(world, random, chunk) + } + }) + } + + abstract fun updateOwner(parcel: Parcel) + + abstract fun getBottomCoord(parcel: Parcel): Vec2i + + abstract fun getHomeLocation(parcel: Parcel): Location + + abstract fun setBiome(parcel: Parcel, biome: Biome) + + abstract fun getEntities(parcel: Parcel): Collection<Entity> + + abstract fun getBlocks(parcel: Parcel, yRange: IntRange = 0..255): Iterator<Block> + +} + +interface GeneratorFactory { + companion object GeneratorFactories { + private val map: MutableMap<String, GeneratorFactory> = HashMap() + + fun registerFactory(generator: GeneratorFactory): Boolean = map.putIfAbsent(generator.name, generator) == null + + fun getFactory(name: String): GeneratorFactory? = map.get(name) + + init { + registerFactory(DefaultParcelGenerator.Factory) + } + + } + + val name: String + + val optionsClass: KClass<out GeneratorOptions> + + fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator + +} + +class DefaultParcelGenerator(name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() { + override val world: ParcelWorld by lazy { TODO() } + override val factory = Factory + + companion object Factory : GeneratorFactory { + override val name get() = "default" + override val optionsClass get() = DefaultGeneratorOptions::class + override fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator { + return DefaultParcelGenerator(worldName, options as DefaultGeneratorOptions) + } + } + + val sectionSize = o.parcelSize + o.pathSize + val pathOffset = (if (o.pathSize % 2 == 0) o.pathSize + 2 else o.pathSize + 1) / 2 + val makePathMain = o.pathSize > 2 + val makePathAlt = o.pathSize > 4 + + private inline fun <T> generate(chunkX: Int, + chunkZ: Int, + floor: T, wall: + T, pathMain: T, + pathAlt: T, + fill: T, + setter: (Int, Int, Int, T) -> Unit) { + + val floorHeight = o.floorHeight + val parcelSize = o.parcelSize + val sectionSize = sectionSize + val pathOffset = pathOffset + val makePathMain = makePathMain + val makePathAlt = makePathAlt + + // parcel bottom x and z + // umod is unsigned %: the result is always >= 0 + val pbx = ((chunkX shl 4) - o.offsetX) umod sectionSize + val pbz = ((chunkZ shl 4) - o.offsetZ) umod sectionSize + + var curHeight: Int + var x: Int + var z: Int + for (cx in 0..15) { + for (cz in 0..15) { + x = (pbx + cx) % sectionSize - pathOffset + z = (pbz + cz) % sectionSize - pathOffset + curHeight = floorHeight + + val type = when { + (x in 0 until parcelSize && z in 0 until parcelSize) -> floor + (x in -1..parcelSize && z in -1..parcelSize) -> { + curHeight++ + wall + } + (makePathAlt && x in -2 until parcelSize + 2 && z in -2 until parcelSize + 2) -> pathAlt + (makePathMain) -> pathMain + else -> { + curHeight++ + wall + } + } + + for (y in 0 until curHeight) { + setter(cx, y, cz, fill) + } + setter(cx, curHeight, cz, type) + } + } + } + + override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData { + val out = Bukkit.createChunkData(world) + generate(chunkX, chunkZ, o.floorType, o.wallType, o.pathMainType, o.pathAltType, o.fillType) { x, y, z, type -> + out.setBlock(x, y, z, type) + } + return out + } + + + override fun populate(world: World?, random: Random?, chunk: Chunk?) { + /* + generate(chunk!!.x, chunk.z, o.floorType.data, o.wallType.data, o.pathMainType.data, o.pathAltType.data, o.fillType.data) { x, y, z, type -> + if (type == 0.toByte()) chunk.getBlock(x, y, z).setData(type, false) + } + */ + } + + override fun getFixedSpawnLocation(world: World?, random: Random?): Location { + val fix = if (o.parcelSize.even) 0.5 else 0.0 + return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix) + } + + override fun parcelAt(x: Int, z: Int): Parcel? { + val sectionSize = sectionSize + val parcelSize = o.parcelSize + val absX = x - o.offsetX - pathOffset + val absZ = z - o.offsetZ - pathOffset + val modX = absX umod sectionSize + val modZ = absZ umod sectionSize + if (0 <= modX && modX < parcelSize && 0 <= modZ && modZ < parcelSize) { + return world.parcelByID((absX - modX) / sectionSize, (absZ - modZ) / sectionSize) + } + return null + } + + override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX, + sectionSize * parcel.pos.z + pathOffset + o.offsetZ) + + override fun getHomeLocation(parcel: Parcel): Location { + val bottom = getBottomCoord(parcel) + return Location(world.world, bottom.x.toDouble(), o.floorHeight + 1.0, bottom.z + (o.parcelSize - 1) / 2.0, -90F, 0F) + } + + override fun updateOwner(parcel: Parcel) { + val world = this.world.world + val b = getBottomCoord(parcel) + + val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1) + val signBlock = world.getBlockAt(b.x - 2, o.floorHeight + 1, b.z - 1) + val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1) + + val owner = parcel.data?.owner + if (owner == null) { + wallBlock.blockData = o.wallType + signBlock.type = Material.AIR + skullBlock.type = Material.AIR + } else { + + val wallBlockType: BlockData = if (o.wallType is Slab) + (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE } + else + o.wallType + + wallBlock.blockData = wallBlockType + + signBlock.blockData = (createBlockData(Material.WALL_SIGN) as Sign).apply { rotation = BlockFace.NORTH } + + val sign = signBlock.state as org.bukkit.block.Sign + sign.setLine(0, parcel.id) + sign.setLine(2, owner.playerName) + sign.update() + + skullBlock.type = Material.PLAYER_HEAD + val skull = skullBlock.state as Skull + if (owner.uuid != null) { + skull.owningPlayer = owner.offlinePlayer + } else { + skull.owner = owner.name + } + skull.rotation = BlockFace.WEST + skull.update() + } + } + + override fun setBiome(parcel: Parcel, biome: Biome) { + val world = this.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + for (x in b.x until b.x + parcelSize) { + for (z in b.z until b.z + parcelSize) { + world.setBiome(x, z, biome) + } + } + } + + override fun getEntities(parcel: Parcel): Collection<Entity> { + val world = this.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + val center = Location(world, (b.x + parcelSize) / 2.0, 128.0, (b.z + parcelSize) / 2.0) + return world.getNearbyEntities(center, parcelSize / 2.0 + 0.2, 128.0, parcelSize / 2.0 + 0.2) + } + + override fun getBlocks(parcel: Parcel, yRange: IntRange): Iterator<Block> = buildIterator { + val range = yRange.clamp(0, 255) + val world = this@DefaultParcelGenerator.world.world + val b = getBottomCoord(parcel) + val parcelSize = o.parcelSize + for (x in b.x until b.x + parcelSize) { + for (z in b.z until b.z + parcelSize) { + for (y in range) { + yield(world.getBlockAt(x, y, z)) + } + } + } + } + +}
\ No newline at end of file |