看板素材対応・クリック方向対応

This commit is contained in:
Keisuke Hirata 2025-12-20 06:05:47 +09:00
parent 84c4252592
commit 65b3cfd8e2
3 changed files with 312 additions and 124 deletions

View File

@ -17,6 +17,60 @@ object ShopVisuals {
org.bukkit.Material.COPPER_CHEST org.bukkit.Material.COPPER_CHEST
) )
val VALID_SIGNS = setOfNotNull(
// Standard Standing Signs
org.bukkit.Material.OAK_SIGN, org.bukkit.Material.BIRCH_SIGN, org.bukkit.Material.SPRUCE_SIGN, org.bukkit.Material.JUNGLE_SIGN,
org.bukkit.Material.ACACIA_SIGN, org.bukkit.Material.DARK_OAK_SIGN, org.bukkit.Material.MANGROVE_SIGN, org.bukkit.Material.CHERRY_SIGN,
org.bukkit.Material.BAMBOO_SIGN, org.bukkit.Material.CRIMSON_SIGN, org.bukkit.Material.WARPED_SIGN, org.bukkit.Material.getMaterial("PALE_OAK_SIGN"),
// Wall Signs
org.bukkit.Material.OAK_WALL_SIGN, org.bukkit.Material.BIRCH_WALL_SIGN, org.bukkit.Material.SPRUCE_WALL_SIGN, org.bukkit.Material.JUNGLE_WALL_SIGN,
org.bukkit.Material.ACACIA_WALL_SIGN, org.bukkit.Material.DARK_OAK_WALL_SIGN, org.bukkit.Material.MANGROVE_WALL_SIGN, org.bukkit.Material.CHERRY_WALL_SIGN,
org.bukkit.Material.BAMBOO_WALL_SIGN, org.bukkit.Material.CRIMSON_WALL_SIGN, org.bukkit.Material.WARPED_WALL_SIGN, org.bukkit.Material.getMaterial("PALE_OAK_WALL_SIGN"),
// Hanging Signs (Ceiling)
org.bukkit.Material.OAK_HANGING_SIGN, org.bukkit.Material.BIRCH_HANGING_SIGN, org.bukkit.Material.SPRUCE_HANGING_SIGN, org.bukkit.Material.JUNGLE_HANGING_SIGN,
org.bukkit.Material.ACACIA_HANGING_SIGN, org.bukkit.Material.DARK_OAK_HANGING_SIGN, org.bukkit.Material.MANGROVE_HANGING_SIGN, org.bukkit.Material.CHERRY_HANGING_SIGN,
org.bukkit.Material.BAMBOO_HANGING_SIGN, org.bukkit.Material.CRIMSON_HANGING_SIGN, org.bukkit.Material.WARPED_HANGING_SIGN, org.bukkit.Material.getMaterial("PALE_OAK_HANGING_SIGN"),
// Wall Hanging Signs
org.bukkit.Material.OAK_WALL_HANGING_SIGN, org.bukkit.Material.BIRCH_WALL_HANGING_SIGN, org.bukkit.Material.SPRUCE_WALL_HANGING_SIGN, org.bukkit.Material.JUNGLE_WALL_HANGING_SIGN,
org.bukkit.Material.ACACIA_WALL_HANGING_SIGN, org.bukkit.Material.DARK_OAK_WALL_HANGING_SIGN, org.bukkit.Material.MANGROVE_WALL_HANGING_SIGN, org.bukkit.Material.CHERRY_WALL_HANGING_SIGN,
org.bukkit.Material.BAMBOO_WALL_HANGING_SIGN, org.bukkit.Material.CRIMSON_WALL_HANGING_SIGN, org.bukkit.Material.WARPED_WALL_HANGING_SIGN, org.bukkit.Material.getMaterial("PALE_OAK_WALL_HANGING_SIGN")
)
fun isShopSign(material: org.bukkit.Material): Boolean {
return VALID_SIGNS.contains(material)
}
fun isWallSign(material: org.bukkit.Material): Boolean {
return material.name.contains("WALL_SIGN") && !material.name.contains("HANGING")
}
fun isHangingSign(material: org.bukkit.Material): Boolean {
return material.name.contains("HANGING_SIGN")
}
fun getSignMaterial(key: String): org.bukkit.Material? {
val upperKey = key.uppercase()
// Map short names to materials
return when (upperKey) {
"OAK" -> org.bukkit.Material.OAK_SIGN
"BIRCH" -> org.bukkit.Material.BIRCH_SIGN
"SPRUCE" -> org.bukkit.Material.SPRUCE_SIGN
"JUNGLE" -> org.bukkit.Material.JUNGLE_SIGN
"ACACIA" -> org.bukkit.Material.ACACIA_SIGN
"DARK_OAK" -> org.bukkit.Material.DARK_OAK_SIGN
"MANGROVE" -> org.bukkit.Material.MANGROVE_SIGN
"CHERRY" -> org.bukkit.Material.CHERRY_SIGN
"BAMBOO" -> org.bukkit.Material.BAMBOO_SIGN
"CRIMSON" -> org.bukkit.Material.CRIMSON_SIGN
"WARPED" -> org.bukkit.Material.WARPED_SIGN
"PALE" -> org.bukkit.Material.getMaterial("PALE_OAK_SIGN")
else -> null
}
}
fun updateSign(plugin: JavaPlugin, block: org.bukkit.block.Block, shopData: ShopData, side: Side) { fun updateSign(plugin: JavaPlugin, block: org.bukkit.block.Block, shopData: ShopData, side: Side) {
val state = block.state as? Sign ?: return val state = block.state as? Sign ?: return
val signSide = state.getSide(side) val signSide = state.getSide(side)
@ -84,24 +138,28 @@ object ShopVisuals {
} }
return count return count
} }
fun removeDisplay(location: org.bukkit.Location, face: String) { fun removeDisplay(plugin: JavaPlugin, location: org.bukkit.Location, shopId: Int? = null) {
val world = location.world ?: return val world = location.world ?: return
val block = location.block
val data = block.blockData as? org.bukkit.block.data.Rotatable ?: return
val rotation = data.rotation
val yaw = yawFromBlockFace(rotation) + 90f // Search at center of block (0.5, 0.5, 0.5)
val center = location.clone().add(0.5, 0.5, 0.5)
val offset = if (face == "FRONT") org.joml.Vector3f(0.25f, 0.25f, 0.0f) else org.joml.Vector3f(-0.25f, 0.25f, 0.0f) val key = org.bukkit.NamespacedKey(plugin, "shop_id")
val rad = Math.toRadians(yaw.toDouble())
offset.rotateY(-rad.toFloat())
val center = location.clone().add(0.5, 0.0, 0.5) // Search for entity within strict block bounds
val targetPos = center.clone().add(offset.x.toDouble(), offset.y.toDouble(), offset.z.toDouble()) val nearby = world.getNearbyEntities(center, 0.45, 0.45, 0.45)
nearby.filterIsInstance<org.bukkit.entity.ItemDisplay>()
// Search for entity .filter { it.scoreboardTags.contains("shop_display") }
val nearby = world.getNearbyEntities(targetPos, 0.2, 0.2, 0.2) .forEach { entity ->
nearby.filterIsInstance<org.bukkit.entity.ItemDisplay>().forEach { it.remove() } if (shopId == null) {
entity.remove() // Wipe all
} else {
val storedId = entity.persistentDataContainer.get(key, org.bukkit.persistence.PersistentDataType.STRING)
if (storedId == shopId.toString()) {
entity.remove() // Target match
}
}
}
} }
fun yawFromBlockFace(face: org.bukkit.block.BlockFace): Float { fun yawFromBlockFace(face: org.bukkit.block.BlockFace): Float {

View File

@ -37,16 +37,47 @@ object ShopCommands {
defaultValue = org.bukkit.permissions.PermissionDefault.OP defaultValue = org.bukkit.permissions.PermissionDefault.OP
} }
// Logic for /shop give (No tier specified, default 1) // Logic for /shop give (No arguments -> Oak, Tier 1)
executes { executes {
giveShopSign(plugin, sender, 1, tierKey) giveShopSign(plugin, sender, 1, tierKey, Material.OAK_SIGN)
} }
// Logic for /shop give <tier> // Logic for /shop give <tier> (Default Oak)
integer("tier", min = 1, max = 5) { integer("tier", min = 1, max = 5) {
executes { executes {
val tier: Int = argument("tier") val tier: Int = argument("tier")
giveShopSign(plugin, sender, tier, tierKey) giveShopSign(plugin, sender, tier, tierKey, Material.OAK_SIGN)
}
}
// Logic for /shop give <type> [tier]
string("type") {
suggests {
listOf("oak", "birch", "spruce", "jungle", "acacia", "dark_oak", "mangrove", "cherry", "bamboo", "crimson", "warped", "pale")
}
executes {
val typeName: String = argument("type")
val material = net.hareworks.hcu.shop.ShopVisuals.getSignMaterial(typeName)
if (material != null) {
giveShopSign(plugin, sender, 1, tierKey, material)
} else {
sender.sendMessage(Component.text("Invalid sign type: $typeName", NamedTextColor.RED))
}
}
integer("tier", min = 1, max = 5) {
executes {
val typeName: String = argument("type")
val tier: Int = argument("tier")
val material = net.hareworks.hcu.shop.ShopVisuals.getSignMaterial(typeName)
if (material != null) {
giveShopSign(plugin, sender, tier, tierKey, material)
} else {
sender.sendMessage(Component.text("Invalid sign type: $typeName", NamedTextColor.RED))
}
}
} }
} }
} }
@ -81,13 +112,13 @@ object ShopCommands {
} }
} }
private fun giveShopSign(plugin: JavaPlugin, sender: org.bukkit.command.CommandSender, tier: Int, key: NamespacedKey) { private fun giveShopSign(plugin: JavaPlugin, sender: org.bukkit.command.CommandSender, tier: Int, key: NamespacedKey, signType: Material) {
if (sender !is Player) { if (sender !is Player) {
sender.sendMessage(Component.text("Only players can use this command.", NamedTextColor.RED)) sender.sendMessage(Component.text("Only players can use this command.", NamedTextColor.RED))
return return
} }
val signItem = ItemStack(Material.OAK_SIGN) val signItem = ItemStack(signType)
val meta = signItem.itemMeta val meta = signItem.itemMeta
meta.displayName(Component.text("Shop Sign (Tier $tier)").color(NamedTextColor.GOLD)) meta.displayName(Component.text("Shop Sign (Tier $tier)").color(NamedTextColor.GOLD))
@ -96,7 +127,7 @@ object ShopCommands {
signItem.itemMeta = meta signItem.itemMeta = meta
sender.inventory.addItem(signItem) sender.inventory.addItem(signItem)
sender.sendMessage(Component.text("Given Shop Sign Tier $tier", NamedTextColor.GREEN)) sender.sendMessage(Component.text("Given Shop Sign Tier $tier (${signType.name.lowercase()})", NamedTextColor.GREEN))
} }
private fun displayShopList(plugin: ShopPlugin, player: Player) { private fun displayShopList(plugin: ShopPlugin, player: Player) {
@ -150,10 +181,10 @@ object ShopCommands {
val world = org.bukkit.Bukkit.getWorld(shop.worldUid) val world = org.bukkit.Bukkit.getWorld(shop.worldUid)
if (world != null) { if (world != null) {
val location = org.bukkit.Location(world, shop.x.toDouble(), shop.y.toDouble(), shop.z.toDouble()) val location = org.bukkit.Location(world, shop.x.toDouble(), shop.y.toDouble(), shop.z.toDouble())
net.hareworks.hcu.shop.ShopVisuals.removeDisplay(location, shop.face) net.hareworks.hcu.shop.ShopVisuals.removeDisplay(plugin, location, shop.id)
val block = location.block val block = location.block
if (block.type == Material.OAK_SIGN) { if (net.hareworks.hcu.shop.ShopVisuals.isShopSign(block.type)) {
// Check for other shops at this location // Check for other shops at this location
val otherFace = if (shop.face == "FRONT") "BACK" else "FRONT" val otherFace = if (shop.face == "FRONT") "BACK" else "FRONT"
val otherShop = net.hareworks.hcu.shop.database.ShopRepository.findByLocationAndFace(location, otherFace) val otherShop = net.hareworks.hcu.shop.database.ShopRepository.findByLocationAndFace(location, otherFace)

View File

@ -42,8 +42,8 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
// Check if it is a shop sign // Check if it is a shop sign
val tier = meta.persistentDataContainer.get(tierKey, PersistentDataType.INTEGER) ?: return val tier = meta.persistentDataContainer.get(tierKey, PersistentDataType.INTEGER) ?: return
// Check block type (Standing Sign) // Check block type (Standing Sign, Wall Sign, Hanging Sign)
if (event.blockPlaced.type != Material.OAK_SIGN) { if (!ShopVisuals.isShopSign(event.blockPlaced.type)) {
event.isCancelled = true event.isCancelled = true
return return
} }
@ -99,13 +99,21 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
) )
plugin.logger.info("Front shop created: ${frontShop != null} (ID: ${frontShop?.id})") plugin.logger.info("Front shop created: ${frontShop != null} (ID: ${frontShop?.id})")
// Create BACK shop // Create BACK shop handling
val backShop = ShopRepository.create( val backShop = if (!ShopVisuals.isWallSign(event.blockPlaced.type)) {
ShopRepository.create(
actorId = actorId, actorId = actorId,
location = event.blockPlaced.location, location = event.blockPlaced.location,
face = "BACK" face = "BACK"
) )
plugin.logger.info("Back shop created: ${backShop != null} (ID: ${backShop?.id})") } else {
null // Wall signs don't have a back shop
}
if (backShop != null) {
plugin.logger.info("Back shop created: (ID: ${backShop.id})")
}
// Close UI and Update Visuals (Delayed to override client-side editor opening) // Close UI and Update Visuals (Delayed to override client-side editor opening)
// Close UI immediately (Next tick) // Close UI immediately (Next tick)
@ -128,10 +136,17 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
plugin.logger.info("Updating BACK sign.") plugin.logger.info("Updating BACK sign.")
ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK) ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK)
} else { } else {
plugin.logger.warning("Back shop was null in delayed task.") // For wall signs, back shop is null is expected
} }
// Spawn Displays Only for Standard Standing Signs
// User requested "DisplayEntity無しで" for the new types (Wall/Hanging)
// Existing Standing Signs logic uses Rotatable (which Wall/Hanging might not fully support in spawnDisplays logic)
// So restrict to Standing Signs (Rotatable && !Wall && !Hanging)
val type = event.blockPlaced.type
if (!ShopVisuals.isWallSign(type) && !ShopVisuals.isHangingSign(type)) {
spawnDisplays(event.blockPlaced) spawnDisplays(event.blockPlaced)
}
}, 1L) }, 1L)
} }
} }
@ -141,60 +156,124 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
val data = block.blockData as? Rotatable ?: return val data = block.blockData as? Rotatable ?: return
val rotation = data.rotation // BlockFace val rotation = data.rotation // BlockFace
val yaw = ShopVisuals.yawFromBlockFace(rotation) + 90f // Keys
val shopIdKey = org.bukkit.NamespacedKey(plugin, "shop_id")
// Calculate offsets relative to center (0.5, 0, 0.5) // Find Shops
// Original request assumes 0,0,0 is block origin. val frontShop = net.hareworks.hcu.shop.database.ShopRepository.findByLocationAndFace(origin, "FRONT")
// Front: 0.75 0.25 0.5 -> Rel: 0.25, 0.25, 0.0 val backShop = net.hareworks.hcu.shop.database.ShopRepository.findByLocationAndFace(origin, "BACK")
// Back: 0.25 0.25 0.5 -> Rel: -0.25, 0.25, 0.0
val frontOffset = Vector3f(0.25f, 0.25f, 0.0f) val rawYaw = ShopVisuals.yawFromBlockFace(rotation)
val backOffset = Vector3f(-0.25f, 0.25f, 0.0f)
// Rotate offsets // Final positions (No offsets, just center of block)
val rad = Math.toRadians(yaw.toDouble())
frontOffset.rotateY(-rad.toFloat())
backOffset.rotateY(-rad.toFloat())
// Final positions
val center = origin.clone().add(0.5, 0.0, 0.5) val center = origin.clone().add(0.5, 0.0, 0.5)
val frontPos = center.clone().add(frontOffset.x.toDouble(), frontOffset.y.toDouble(), frontOffset.z.toDouble())
val backPos = center.clone().add(backOffset.x.toDouble(), backOffset.y.toDouble(), backOffset.z.toDouble())
val world = block.world val world = block.world
// Spawn Front // Check existing entities in block area
// Search at center of block (0.5, 0.5, 0.5)
val blockCenter = origin.clone().add(0.5, 0.5, 0.5)
val existing = world.getNearbyEntities(blockCenter, 0.45, 0.45, 0.45)
.filterIsInstance<ItemDisplay>()
.filter { it.scoreboardTags.contains("shop_display") }
// --- FRONT ---
if (frontShop != null) {
val frontPos = center.clone().add( // Recalculate Front Pos for spawning if needed (though now we use center... wait, step 178 restored offsets?)
// Step 178 restored offsets. I need to keep that logic if I want positions.
// Re-reading Step 178: "Final positions ... frontPos ... backPos"
// My previous spawnDisplays (Step 255) used frontPos/backPos.
// I need to calculate them here again.
0.0, 0.0, 0.0 // Placeholder, will fix below
)
// recalculate offsets
val yawForOffset = rawYaw + 90f
val frontOffset = Vector3f(0.25f, 0.25f, 0.0f)
val rad = Math.toRadians(yawForOffset.toDouble())
frontOffset.rotateY(-rad.toFloat())
frontPos.set(center.x + frontOffset.x, center.y + frontOffset.y, center.z + frontOffset.z)
val existingEntity = existing.find {
it.persistentDataContainer.get(shopIdKey, org.bukkit.persistence.PersistentDataType.STRING) == frontShop.id.toString()
}
val itemStack = frontShop.item ?: ItemStack(Material.AIR)
if (existingEntity != null) {
// Update
existingEntity.setItemStack(itemStack)
} else {
// Spawn
world.spawn(frontPos, ItemDisplay::class.java) { display -> world.spawn(frontPos, ItemDisplay::class.java) { display ->
display.setRotation(yaw, 0f) display.setItemDisplayTransform(org.bukkit.entity.ItemDisplay.ItemDisplayTransform.FIXED)
display.setRotation(rawYaw, 0f)
// Transformation val left = Quaternionf()
val left = Quaternionf(0.0f, -0.7071068f, 0.0f, 0.7071068f) val right = Quaternionf()
val right = Quaternionf(0.0f, 0.0f, 0.0f, 1.0f) val scale = Vector3f(1.0f, 1.0f, 1.0f)
val scale = Vector3f(0.5f, 0.5f, 0.5f)
val translation = Vector3f(0.0f, 0.0f, 0.0f) val translation = Vector3f(0.0f, 0.0f, 0.0f)
display.addScoreboardTag("shop_display")
display.persistentDataContainer.set(shopIdKey, org.bukkit.persistence.PersistentDataType.STRING, frontShop.id.toString())
display.setTransformation(Transformation(translation, left, scale, right)) display.setTransformation(Transformation(translation, left, scale, right))
display.interpolationDuration = 0 display.interpolationDuration = 0
// Add custom tag or PDC to link to sign if needed? display.setItemStack(itemStack)
}
}
} }
// Spawn Back // --- BACK ---
if (backShop != null) {
// recalculate offsets
val yawForOffset = rawYaw + 90f
val backOffset = Vector3f(-0.25f, 0.25f, 0.0f)
val rad = Math.toRadians(yawForOffset.toDouble())
backOffset.rotateY(-rad.toFloat())
val backPos = center.clone().add(backOffset.x.toDouble(), backOffset.y.toDouble(), backOffset.z.toDouble())
val existingEntity = existing.find {
it.persistentDataContainer.get(shopIdKey, org.bukkit.persistence.PersistentDataType.STRING) == backShop.id.toString()
}
val itemStack = backShop.item ?: ItemStack(Material.AIR)
if (existingEntity != null) {
// Update
existingEntity.setItemStack(itemStack)
} else {
// Spawn
world.spawn(backPos, ItemDisplay::class.java) { display -> world.spawn(backPos, ItemDisplay::class.java) { display ->
// display.setItemStack(ItemStack(Material.AIR)) // Default empty display.setItemDisplayTransform(org.bukkit.entity.ItemDisplay.ItemDisplayTransform.FIXED)
display.setRotation(yaw - 90f, 0f) display.setRotation(rawYaw + 180f, 0f)
// Transformation val left = Quaternionf()
val left = Quaternionf(0.0f, 1.0f, 0.0f, 0.0f) val right = Quaternionf()
val right = Quaternionf(0.0f, 0.0f, 0.0f, 1.0f) val scale = Vector3f(1.0f, 1.0f, 1.0f)
val scale = Vector3f(0.5f, 0.5f, 0.5f)
val translation = Vector3f(0.0f, 0.0f, 0.0f) val translation = Vector3f(0.0f, 0.0f, 0.0f)
display.addScoreboardTag("shop_display")
display.persistentDataContainer.set(shopIdKey, org.bukkit.persistence.PersistentDataType.STRING, backShop.id.toString())
display.setTransformation(Transformation(translation, left, scale, right)) display.setTransformation(Transformation(translation, left, scale, right))
display.interpolationDuration = 0 display.interpolationDuration = 0
display.setItemStack(itemStack)
} }
} }
}
}
private fun updateShopDisplay(shopData: net.hareworks.hcu.shop.database.ShopData) {
val world = org.bukkit.Bukkit.getWorld(shopData.worldUid) ?: return
val block = world.getBlockAt(shopData.x, shopData.y, shopData.z)
// Spawn/Update displays
spawnDisplays(block)
}
@EventHandler @EventHandler
fun onSignChange(event: SignChangeEvent) { fun onSignChange(event: SignChangeEvent) {
@ -275,15 +354,61 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
// Check if shop sign // Check if shop sign
if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return
// Determine Face (Front/Back) // Use Player vs Block relative position
val data = block.blockData as? Rotatable ?: return val playerLoc = event.player.location
val rotation = data.rotation val blockCenter = block.location.add(0.5, 0.5, 0.5)
val clickedFace = event.blockFace val vectorToPlayer = playerLoc.toVector().subtract(blockCenter.toVector())
val face = when (clickedFace) { var face = "FRONT"
rotation -> "FRONT"
rotation.oppositeFace -> "BACK" // Handle Directional vs Rotatable
else -> return // Clicked side/top/bottom, ignore or handle? Ignoring for now. // Standard Standing Sign: Rotatable
// Wall Sign: Directional
// Hanging Sign (Ceiling): Rotatable (but different?) -> It is Rotatable.
// Hanging Sign (Wall): Directional
val bData = block.blockData
val forwardVector: org.bukkit.util.Vector
if (bData is org.bukkit.block.data.Directional) {
// Wall Sign / Wall Hanging Sign
val facing = bData.facing // The direction the text faces (usually)
// Create vector from facing
forwardVector = facing.direction
// Dot product
val dot = vectorToPlayer.normalize().dot(forwardVector)
// For Wall Signs (Strict), Back is impossible/invalid.
// If dot < 0 (Behind), it means player is inside the wall? Or side?
// User said: "Wall signs: Back unclickable".
if (dot > 0) {
face = "FRONT"
} else {
face = "BACK"
// If it is strictly a Wall Sign (not hanging), BACK is disabled.
if (ShopVisuals.isWallSign(block.type)) {
// Ignore click
return
}
}
} else if (bData is Rotatable) {
// Standing Sign / Ceiling Hanging Sign
val rotation = bData.rotation
val signYaw = ShopVisuals.yawFromBlockFace(rotation)
val rad = Math.toRadians(signYaw.toDouble())
// Sign Yaw 0 = South (0,1).
val signDirX = -Math.sin(rad)
val signDirZ = Math.cos(rad)
forwardVector = org.bukkit.util.Vector(signDirX, 0.0, signDirZ)
val dot = vectorToPlayer.normalize().dot(forwardVector)
face = if (dot > 0) "FRONT" else "BACK"
} else {
return
} }
// Check Owner // Check Owner
@ -363,7 +488,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
// Check for Shop Sign above // Check for Shop Sign above
val signBlock = block.getRelative(BlockFace.UP) val signBlock = block.getRelative(BlockFace.UP)
if (signBlock.type != Material.OAK_SIGN) return if (!ShopVisuals.isShopSign(signBlock.type)) return
val state = signBlock.state as? Sign ?: return val state = signBlock.state as? Sign ?: return
if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return
@ -491,7 +616,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
updateShopDisplay(updatedShop) updateShopDisplay(updatedShop)
val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid) val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid)
val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z) val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z)
if (block != null && block.type == Material.OAK_SIGN) { if (block != null && ShopVisuals.isShopSign(block.type)) {
val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK
ShopVisuals.updateSign(plugin, block, updatedShop, side) ShopVisuals.updateSign(plugin, block, updatedShop, side)
} }
@ -517,7 +642,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
updateShopDisplay(updatedShop) updateShopDisplay(updatedShop)
val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid) val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid)
val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z) val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z)
if (block != null && block.type == Material.OAK_SIGN) { if (block != null && ShopVisuals.isShopSign(block.type)) {
val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK
ShopVisuals.updateSign(plugin, block, updatedShop, side) ShopVisuals.updateSign(plugin, block, updatedShop, side)
} }
@ -560,7 +685,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
shopData.z.toDouble() shopData.z.toDouble()
) )
val block = location.block val block = location.block
if (block.type != Material.OAK_SIGN) return if (!ShopVisuals.isShopSign(block.type)) return
val sign = block.state as? Sign ?: return val sign = block.state as? Sign ?: return
@ -595,7 +720,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
// Handle Container Break Protection // Handle Container Break Protection
if (block.type in ShopVisuals.VALID_CONTAINERS) { if (block.type in ShopVisuals.VALID_CONTAINERS) {
val signBlock = block.getRelative(BlockFace.UP) val signBlock = block.getRelative(BlockFace.UP)
if (signBlock.type == Material.OAK_SIGN) { if (ShopVisuals.isShopSign(signBlock.type)) {
val state = signBlock.state as? Sign val state = signBlock.state as? Sign
if (state != null && state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) { if (state != null && state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) {
event.isCancelled = true event.isCancelled = true
@ -606,7 +731,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
} }
// Handle Shop Sign Break // Handle Shop Sign Break
if (block.type != Material.OAK_SIGN) return if (!ShopVisuals.isShopSign(block.type)) return
val state = block.state as? Sign ?: return val state = block.state as? Sign ?: return
if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return
@ -635,50 +760,24 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
// Delete Shops // Delete Shops
if (frontShop != null) { if (frontShop != null) {
ShopRepository.delete(frontShop.id) ShopRepository.delete(frontShop.id)
ShopVisuals.removeDisplay(location, "FRONT") ShopVisuals.removeDisplay(plugin, location, frontShop.id)
} }
if (backShop != null) { if (backShop != null) {
ShopRepository.delete(backShop.id) ShopRepository.delete(backShop.id)
ShopVisuals.removeDisplay(location, "BACK") ShopVisuals.removeDisplay(plugin, location, backShop.id)
} }
event.player.sendMessage(Component.text("Shop removed.", NamedTextColor.GREEN)) event.player.sendMessage(Component.text("Shop removed.", NamedTextColor.GREEN))
} }
private fun updateShopDisplay(shopData: net.hareworks.hcu.shop.database.ShopData) {
val world = org.bukkit.Bukkit.getWorld(shopData.worldUid) ?: return
val block = world.getBlockAt(shopData.x, shopData.y, shopData.z)
// Ensure block is still a sign and rotatable
val data = block.blockData as? Rotatable ?: return
val rotation = data.rotation
val yaw = ShopVisuals.yawFromBlockFace(rotation) + 90f
val offset = if (shopData.face == "FRONT") Vector3f(0.25f, 0.25f, 0.0f) else Vector3f(-0.25f, 0.25f, 0.0f)
val rad = Math.toRadians(yaw.toDouble())
offset.rotateY(-rad.toFloat())
val center = block.location.clone().add(0.5, 0.0, 0.5)
val targetPos = center.clone().add(offset.x.toDouble(), offset.y.toDouble(), offset.z.toDouble())
// Search for entity
val nearby = world.getNearbyEntities(targetPos, 0.2, 0.2, 0.2)
val display = nearby.filterIsInstance<ItemDisplay>().firstOrNull()
if (display != null) {
if (shopData.item != null) {
display.setItemStack(shopData.item)
} else {
display.setItemStack(ItemStack(Material.AIR))
}
}
}
} }
object ShopGuiMap { object ShopGuiMap {
val openInvs = java.util.WeakHashMap<Inventory, Int>() val openInvs = java.util.WeakHashMap<Inventory, Int>()
} }