Skip to content

Commit

Permalink
feat: custom block with facing
Browse files Browse the repository at this point in the history
  • Loading branch information
acrylic-style committed Jan 31, 2024
1 parent 8ccfa89 commit 82e593f
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 22 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = "net.azisaba"
version = "6.9.0"
version = "6.9.1"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ import org.bukkit.inventory.EquipmentSlot

class CommandCustomBlock(
override val name: String,
override val lockFacing: Boolean,
override val axisShift: Int,
override val backgroundBlock: Material,
material: Material,
displayName: String? = null,
lore: List<String>? = null,
private val commands: List<String>,
private val consoleCommands: List<String>,
private val destroyWithoutWrench: Boolean,
) : CustomBlock(material, displayName, lore) {
override fun onInteract(e: PlayerInteractEvent, state: CustomBlockState) {
if (e.hand != EquipmentSlot.HAND) return
commands.forEach { e.player.performCommand(it) }
consoleCommands.forEach { e.player.server.dispatchCommand(e.player.server.consoleSender, it.replace("<player>", e.player.name)) }
}

override fun canDestroy(state: CustomBlockState, wrench: Boolean): Boolean = wrench || destroyWithoutWrench
}
30 changes: 20 additions & 10 deletions src/main/java/com/github/mori01231/lifecore/block/CustomBlock.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.util.AxisX
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import net.minecraft.server.v1_15_R1.NBTTagCompound
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack
import org.bukkit.event.Event
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.player.PlayerInteractEvent
Expand All @@ -18,22 +18,29 @@ abstract class CustomBlock(
private val lore: List<String>? = null,
) : Listener {
open val name: String = javaClass.simpleName
open val lockFacing: Boolean = false
open val axisShift: Int = 0
open val backgroundBlock: Material = Material.BARRIER
var customModelData = 0

internal fun handleInteract(e: PlayerInteractEvent, state: CustomBlockState) {
//e.isCancelled = true
e.setUseItemInHand(Event.Result.ALLOW)
e.setUseInteractedBlock(Event.Result.ALLOW)
if (state.blockName != name) {
return
}
if (state.blockName != name) error("Block name mismatch: state ${state.blockName} != block $name")
//e.isCancelled = true // player wouldn't be able to place blocks on top of this block
onInteract(e, state)
}

open fun onInteract(e: PlayerInteractEvent, state: CustomBlockState) {
}

open fun canDestroy(state: CustomBlockState) = true
open fun canDestroy(state: CustomBlockState, wrench: Boolean) = wrench

/**
* Executes the pre-destroy process, and returns whether the block can be dropped.
* @return Whether the block can be dropped. `true` if the block can be dropped. `false` if the block drop will be cancelled.
*/
open fun preDestroy(state: CustomBlockState): Boolean {
return true
}

open fun onPlace(e: BlockPlaceEvent): CustomBlockState {
val nms = CraftItemStack.asNMSCopy(e.itemInHand)
Expand All @@ -43,10 +50,13 @@ abstract class CustomBlock(
""
}
if (tagString.isBlank()) {
return CustomBlockState(this)
return CustomBlockState(this, AxisX.valueOf(e.player.facing.name))
}
val tag = Json.decodeFromString<MutableMap<String, JsonElement>>(tagString)
return CustomBlockState(this, tag)
return CustomBlockState(this, AxisX.valueOf(e.player.facing.name), tag)
}

open fun postPlace(e: BlockPlaceEvent, state: CustomBlockState) {
}

open fun getItemStack(state: CustomBlockState?): ItemStack {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.LifeCore
import com.github.mori01231.lifecore.listener.CustomBlockListener
import com.github.mori01231.lifecore.util.AxisX
import com.github.mori01231.lifecore.util.LRUCache
import kotlinx.serialization.json.Json
import org.bukkit.ChatColor
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.block.data.Directional
import org.bukkit.block.data.Orientable
import org.bukkit.block.data.type.Leaves
import org.bukkit.entity.ArmorStand
import org.bukkit.event.HandlerList
import org.bukkit.inventory.ItemStack
Expand Down Expand Up @@ -56,7 +60,7 @@ class CustomBlockManager(val plugin: LifeCore) {
findArmorStand(location)?.remove()
location.block.type = Material.AIR
} else {
spawnBlock(location, state.getBlock())
spawnBlock(location, state.getBlock(), state.axis)
}
region.setState(location.blockX, location.blockY, location.blockZ, state)
region.save()
Expand Down Expand Up @@ -106,17 +110,25 @@ class CustomBlockManager(val plugin: LifeCore) {
blocks.forEach { block ->
HandlerList.unregisterAll(block)
}
blocks.clear()

// add blocks
plugin.config.getMapList("custom-blocks").forEach { map ->
val blockName = map["name"]?.toString() ?: return plugin.logger.warning("name is required")
val axisShift = map["axisShift"]?.toString()?.toIntOrNull() ?: 0
val lockFacing = map["lockFacing"]?.toString()?.toBoolean() ?: false
val backgroundBlock = Material.valueOf(map["backgroundBlock"]?.toString()?.uppercase() ?: "BARRIER")
if (backgroundBlock.isAir) {
return plugin.logger.warning("$blockName: backgroundBlock must not be air")
}
val material = Material.valueOf(map["material"]?.toString()?.uppercase() ?: return plugin.logger.warning("material is required"))
val displayName = map["displayName"]?.toString()?.let { ChatColor.translateAlternateColorCodes('&', it) }
val lore = map["lore"]?.toString()?.let { ChatColor.translateAlternateColorCodes('&', it) }?.split("\n")
val customModelData = map["customModelData"]?.toString()?.toIntOrNull() ?: 0
val commands = map["commands"] as List<String>?
val consoleCommands = map["consoleCommands"] as List<String>?
val block = CommandCustomBlock(blockName, material, displayName, lore, commands ?: emptyList(), consoleCommands ?: emptyList())
val destroyWithoutWrench = map["destroyWithoutWrench"]?.toString()?.toBoolean() ?: false
val block = CommandCustomBlock(blockName, lockFacing, axisShift, backgroundBlock, material, displayName, lore, commands ?: emptyList(), consoleCommands ?: emptyList(), destroyWithoutWrench)
block.customModelData = customModelData
registerCustomBlock(block)
}
Expand All @@ -131,9 +143,19 @@ class CustomBlockManager(val plugin: LifeCore) {
.firstOrNull { it.customName == "custom_block" && !it.isVisible && it.isInvulnerable && it.isSmall }
}

private fun spawnBlock(location: Location, block: CustomBlock): ArmorStand {
private fun spawnBlock(location: Location, block: CustomBlock, axis: AxisX): ArmorStand {
findArmorStand(location)?.remove()
location.block.type = Material.BARRIER
location.block.type = block.backgroundBlock
location.block.blockData.let {
if (!block.lockFacing && it is Directional && axis.blockFace in it.faces) {
it.facing = axis.blockFace
location.block.blockData = it
}
if (it is Leaves) {
it.isPersistent = true
location.block.blockData = it
}
}
val x = location.blockX + 0.5
val y = location.blockY.toDouble()
val z = location.blockZ + 0.5
Expand All @@ -145,6 +167,7 @@ class CustomBlockManager(val plugin: LifeCore) {
it.customName = "custom_block"
it.isCustomNameVisible = false
it.isSilent = true
it.setRotation(((axis.yaw + block.axisShift) % 360).toFloat(), 0f)
it.equipment?.helmet = ItemStack(block.material).apply {
itemMeta = itemMeta?.apply {
setCustomModelData(block.customModelData)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.LifeCore
import com.github.mori01231.lifecore.util.AxisX
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import org.bukkit.plugin.java.JavaPlugin

@Serializable
data class CustomBlockState(
val blockName: String,
val axis: AxisX = AxisX.SOUTH,
val tag: MutableMap<String, JsonElement> = mutableMapOf(),
) {
constructor(block: CustomBlock, tag: MutableMap<String, JsonElement> = mutableMapOf()) :
this(block.name, tag)
constructor(block: CustomBlock, axis: AxisX = AxisX.SOUTH, tag: MutableMap<String, JsonElement> = mutableMapOf()) :
this(block.name, axis, tag)

init {
getBlock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,21 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {
val state = plugin.customBlockManager.getState(e.clickedBlock!!.location) ?: return
if (wrench) {
e.isCancelled = true
if (!state.getBlock().canDestroy(state, true)) return
if (!e.player.hasPermission("lifecore.customblock.destroy.${state.blockName}")) {
e.player.sendActionBar("このブロックを破壊する権限がありません。")
return
}
val drop = state.getBlock().preDestroy(state)
plugin.customBlockManager.setState(e.clickedBlock!!.location, null)
e.player.playSound(e.player.location, Sound.ENTITY_ITEM_PICKUP, 1f, 1f)
e.player.inventory.addItem(state.getBlock().getItemStack(state)).forEach { (_, item) ->
e.player.world.dropItem(e.player.location, item)
if (drop) {
e.player.playSound(e.player.location, Sound.ENTITY_ITEM_PICKUP, 1f, 1f)
e.player.inventory.addItem(state.getBlock().getItemStack(state)).forEach { (_, item) ->
e.player.world.dropItemNaturally(
e.clickedBlock!!.location.clone().apply { add(0.5, 0.0, 0.5) },
item
)
}
}
} else {
if (!e.player.hasPermission("lifecore.customblock.interact.${state.blockName}")) {
Expand Down Expand Up @@ -57,18 +64,28 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {
}
val state = block.onPlace(e)
plugin.customBlockManager.setState(e.block.location, state)
block.postPlace(e, state)
}

@EventHandler
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun onBlockBreak(e: BlockBreakEvent) {
if (e.player.gameMode != GameMode.CREATIVE) return
val state = plugin.customBlockManager.getState(e.block.location) ?: return
e.isCancelled = true
if (!e.player.hasPermission("lifecore.customblock.destroy.${state.blockName}")) {
e.player.sendActionBar("このブロックを破壊する権限がありません。")
return
}
if (e.player.gameMode != GameMode.CREATIVE && !state.getBlock().canDestroy(state, false)) {
return
}
val drop = state.getBlock().preDestroy(state)
plugin.customBlockManager.setState(e.block.location, null)
if (drop && e.player.gameMode != GameMode.CREATIVE) {
e.player.world.dropItemNaturally(
e.block.location.clone().apply { add(0.5, 0.0, 0.5) },
state.getBlock().getItemStack(state)
)
}
}

@EventHandler
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/github/mori01231/lifecore/util/AxisX.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.github.mori01231.lifecore.util;

import org.bukkit.block.BlockFace;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

public enum AxisX {
SOUTH(270),
WEST(0),
NORTH(90),
EAST(180);

private final int yaw;

AxisX(int yaw) {
this.yaw = yaw;
}

public int getYaw() {
return yaw;
}

@Contract(pure = true)
public @NotNull BlockFace getBlockFace() {
switch (this) {
case SOUTH:
return BlockFace.SOUTH;
case WEST:
return BlockFace.WEST;
case NORTH:
return BlockFace.NORTH;
case EAST:
return BlockFace.EAST;
default:
throw new AssertionError();
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ custom-model-data:

custom-blocks:
- name: reinforced_glass
backgroundBlock: barrier
material: glass
displayName: "&fReinforced Glass"
customModelData: 1
Expand Down

0 comments on commit 82e593f

Please sign in to comment.