diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt')
-rw-r--r-- | src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt | 267 |
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 |