summaryrefslogtreecommitdiff
path: root/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt')
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
new file mode 100644
index 0000000..90eb631
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
@@ -0,0 +1,267 @@
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.RegionTraversal
+import io.dico.parcels2.blockvisitor.Worker
+import io.dico.parcels2.blockvisitor.WorktimeLimiter
+import io.dico.parcels2.util.*
+import org.bukkit.*
+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 java.util.Random
+
+private val airType = Bukkit.createBlockData(Material.AIR)
+
+data class DefaultGeneratorOptions(var defaultBiome: Biome = Biome.JUNGLE,
+ var wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
+ var floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ var fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ var pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
+ var pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
+ var parcelSize: Int = 101,
+ var pathSize: Int = 9,
+ var floorHeight: Int = 64,
+ var offsetX: Int = 0,
+ var offsetZ: Int = 0) : GeneratorOptions() {
+
+ override fun generatorFactory(): GeneratorFactory = DefaultParcelGenerator.Factory
+}
+
+class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() {
+ private var _world: World? = null
+ override val world: World
+ get() {
+ if (_world == null) _world = Bukkit.getWorld(name)!!.also {
+ maxHeight = it.maxHeight
+ return it
+ }
+ return _world!!
+ }
+
+ private var maxHeight = 0
+
+ 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?) {
+ // do nothing
+ }
+
+ 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 makeParcelBlockManager(worktimeLimiter: WorktimeLimiter): ParcelBlockManager {
+ return ParcelBlockManagerImpl(worktimeLimiter)
+ }
+
+ override fun makeParcelLocator(container: ParcelContainer): ParcelLocator {
+ return ParcelLocatorImpl(container)
+ }
+
+ private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
+ 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 (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
+ return mapper((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
+ }
+ return null
+ }
+
+ private inner class ParcelLocatorImpl(val container: ParcelContainer) : ParcelLocator {
+ override val world: World = this@DefaultParcelGenerator.world
+ override fun getParcelAt(x: Int, z: Int): Parcel? {
+ return convertBlockLocationToId(x, z, container::getParcelById)
+ }
+
+ override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
+ return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(world.name, world.uid, idx, idz) }
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ private inner class ParcelBlockManagerImpl(override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManager {
+ override val world: World = this@DefaultParcelGenerator.world
+
+ override fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i(
+ sectionSize * parcel.pos.x + pathOffset + o.offsetX,
+ sectionSize * parcel.pos.z + pathOffset + o.offsetZ
+ )
+
+ override fun getHomeLocation(parcel: ParcelId): Location {
+ val bottom = getBottomBlock(parcel)
+ return Location(world, bottom.x.toDouble(), o.floorHeight + 1.0, bottom.z + (o.parcelSize - 1) / 2.0, -90F, 0F)
+ }
+
+ override fun setOwnerBlock(parcel: ParcelId, owner: ParcelOwner?) {
+ val b = getBottomBlock(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)
+
+ 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 = (Bukkit.createBlockData(Material.WALL_SIGN) as Sign).apply { rotation = BlockFace.NORTH }
+
+ val sign = signBlock.state as org.bukkit.block.Sign
+ sign.setLine(0, "${parcel.x},${parcel.z}")
+ sign.setLine(2, owner.name)
+ 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: ParcelId, biome: Biome): Worker = worktimeLimiter.submit {
+ val world = world
+ val b = getBottomBlock(parcel)
+ val parcelSize = o.parcelSize
+ for (x in b.x until b.x + parcelSize) {
+ for (z in b.z until b.z + parcelSize) {
+ markSuspensionPoint()
+ world.setBiome(x, z, biome)
+ }
+ }
+ }
+
+ override fun clearParcel(parcel: ParcelId): Worker = worktimeLimiter.submit {
+ val bottom = getBottomBlock(parcel)
+ val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
+ val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
+ val blockCount = region.blockCount.toDouble()
+
+ val world = world
+ val floorHeight = o.floorHeight
+ val airType = airType
+ val floorType = o.floorType
+ val fillType = o.fillType
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val y = vec.y
+ val blockType = when {
+ y > floorHeight -> airType
+ y == floorHeight -> floorType
+ else -> fillType
+ }
+ world[vec].blockData = blockType
+ setProgress((index + 1) / blockCount)
+ }
+ }
+
+ override fun doBlockOperation(parcel: ParcelId, direction: RegionTraversal, operation: (Block) -> Unit): Worker = worktimeLimiter.submit {
+ val bottom = getBottomBlock(parcel)
+ val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
+ val blocks = direction.regionTraverser(region)
+ val blockCount = region.blockCount.toDouble()
+ val world = world
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ operation(world[vec])
+ setProgress((index + 1) / blockCount)
+ }
+ }
+
+ }
+
+} \ No newline at end of file