Compare commits
2 Commits
bbb88cea8f
...
84c4252592
| Author | SHA1 | Date | |
|---|---|---|---|
| 84c4252592 | |||
| 47bfcfc1e4 |
|
|
@ -9,6 +9,14 @@ import org.bukkit.block.sign.Side
|
|||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
object ShopVisuals {
|
||||
val VALID_CONTAINERS = setOfNotNull(
|
||||
org.bukkit.Material.CHEST,
|
||||
org.bukkit.Material.TRAPPED_CHEST,
|
||||
org.bukkit.Material.BARREL,
|
||||
org.bukkit.Material.DECORATED_POT,
|
||||
org.bukkit.Material.COPPER_CHEST
|
||||
)
|
||||
|
||||
fun updateSign(plugin: JavaPlugin, block: org.bukkit.block.Block, shopData: ShopData, side: Side) {
|
||||
val state = block.state as? Sign ?: return
|
||||
val signSide = state.getSide(side)
|
||||
|
|
@ -46,18 +54,75 @@ object ShopVisuals {
|
|||
// L3: (Empty)
|
||||
// L4: <Owner Name> (Gray)
|
||||
|
||||
signSide.line(0, Component.text(itemName, NamedTextColor.AQUA))
|
||||
signSide.line(1, Component.text("x$amount - $priceStr", NamedTextColor.BLACK))
|
||||
signSide.line(2, Component.empty())
|
||||
signSide.line(0, Component.empty())
|
||||
signSide.line(1, Component.text(itemName, NamedTextColor.AQUA))
|
||||
signSide.line(2, Component.text("x$amount - $priceStr", NamedTextColor.BLACK))
|
||||
signSide.line(3, Component.text(ownerName, NamedTextColor.GRAY))
|
||||
signSide.isGlowingText = true
|
||||
|
||||
// Update the sign one tick later to ensure persistence compatibility
|
||||
// Or if called from command, update immediately?
|
||||
// Safer to always schedule if in doubt, or check constraints.
|
||||
// For command, immediate update is fine, but for event, delay is needed.
|
||||
// Let's us runTask always for safety.
|
||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||
state.update()
|
||||
})
|
||||
// Update the sign immediately
|
||||
state.isWaxed = true // Force lock visually
|
||||
state.update()
|
||||
}
|
||||
|
||||
fun calculateStock(block: org.bukkit.block.Block, item: org.bukkit.inventory.ItemStack?): Int {
|
||||
if (item == null) return 0
|
||||
val downBlock = block.getRelative(org.bukkit.block.BlockFace.DOWN)
|
||||
|
||||
if (downBlock.type !in VALID_CONTAINERS) return 0
|
||||
|
||||
// Use InventoryHolder to support Chests, Barrels, etc.
|
||||
val container = downBlock.state as? org.bukkit.inventory.InventoryHolder ?: return 0
|
||||
val inventory = container.inventory
|
||||
|
||||
var count = 0
|
||||
for (i in 0 until inventory.size) {
|
||||
val slotItem = inventory.getItem(i)
|
||||
if (slotItem != null && slotItem.isSimilar(item)) {
|
||||
count += slotItem.amount
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
fun removeDisplay(location: org.bukkit.Location, face: String) {
|
||||
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
|
||||
|
||||
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 rad = Math.toRadians(yaw.toDouble())
|
||||
offset.rotateY(-rad.toFloat())
|
||||
|
||||
val center = 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)
|
||||
nearby.filterIsInstance<org.bukkit.entity.ItemDisplay>().forEach { it.remove() }
|
||||
}
|
||||
|
||||
fun yawFromBlockFace(face: org.bukkit.block.BlockFace): Float {
|
||||
return when (face) {
|
||||
org.bukkit.block.BlockFace.SOUTH -> 0f
|
||||
org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST -> 22.5f
|
||||
org.bukkit.block.BlockFace.SOUTH_WEST -> 45f
|
||||
org.bukkit.block.BlockFace.WEST_SOUTH_WEST -> 67.5f
|
||||
org.bukkit.block.BlockFace.WEST -> 90f
|
||||
org.bukkit.block.BlockFace.WEST_NORTH_WEST -> 112.5f
|
||||
org.bukkit.block.BlockFace.NORTH_WEST -> 135f
|
||||
org.bukkit.block.BlockFace.NORTH_NORTH_WEST -> 157.5f
|
||||
org.bukkit.block.BlockFace.NORTH -> 180f
|
||||
org.bukkit.block.BlockFace.NORTH_NORTH_EAST -> 202.5f
|
||||
org.bukkit.block.BlockFace.NORTH_EAST -> 225f
|
||||
org.bukkit.block.BlockFace.EAST_NORTH_EAST -> 247.5f
|
||||
org.bukkit.block.BlockFace.EAST -> 270f
|
||||
org.bukkit.block.BlockFace.EAST_SOUTH_EAST -> 292.5f
|
||||
org.bukkit.block.BlockFace.SOUTH_EAST -> 315f
|
||||
org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST -> 337.5f
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,14 +51,30 @@ object ShopCommands {
|
|||
}
|
||||
}
|
||||
|
||||
literal("update-signs") {
|
||||
literal("list") {
|
||||
permission {
|
||||
description = "Force update all shop signs"
|
||||
defaultValue = org.bukkit.permissions.PermissionDefault.OP
|
||||
description = "List all shops sorted by distance"
|
||||
defaultValue = org.bukkit.permissions.PermissionDefault.TRUE
|
||||
}
|
||||
executes {
|
||||
val count = updateAllSigns(plugin)
|
||||
sender.sendMessage(Component.text("Updated $count shop signs.", NamedTextColor.GREEN))
|
||||
if (sender !is Player) {
|
||||
sender.sendMessage(Component.text("Only players can use this command.", NamedTextColor.RED))
|
||||
return@executes
|
||||
}
|
||||
displayShopList(plugin, sender as Player)
|
||||
}
|
||||
}
|
||||
|
||||
literal("delete") {
|
||||
permission {
|
||||
description = "Delete a shop by ID"
|
||||
defaultValue = org.bukkit.permissions.PermissionDefault.OP
|
||||
}
|
||||
integer("id") {
|
||||
executes {
|
||||
val id: Int = argument("id")
|
||||
deleteShop(plugin, sender, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,20 +99,87 @@ object ShopCommands {
|
|||
sender.sendMessage(Component.text("Given Shop Sign Tier $tier", NamedTextColor.GREEN))
|
||||
}
|
||||
|
||||
private fun updateAllSigns(plugin: ShopPlugin): Int {
|
||||
private fun displayShopList(plugin: ShopPlugin, player: Player) {
|
||||
val shops = net.hareworks.hcu.shop.database.ShopRepository.findAll()
|
||||
var count = 0
|
||||
for (shop in shops) {
|
||||
val world = org.bukkit.Bukkit.getWorld(shop.worldUid) ?: continue
|
||||
val block = world.getBlockAt(shop.x, shop.y, shop.z)
|
||||
// Ideally we check if loaded, but getBlockAt loads chunk if not async?
|
||||
// In main thread command, it's fine for now, but beware of lag with thousands of shops.
|
||||
if (block.type == Material.OAK_SIGN) {
|
||||
val side = if (shop.face == "FRONT") org.bukkit.block.sign.Side.FRONT else org.bukkit.block.sign.Side.BACK
|
||||
net.hareworks.hcu.shop.ShopVisuals.updateSign(plugin, block, shop, side)
|
||||
count++
|
||||
val playerLoc = player.location
|
||||
val playerWorldUid = player.world.uid
|
||||
|
||||
// Filter by world and sort by distance
|
||||
val sortedShops = shops.filter { it.worldUid == playerWorldUid }
|
||||
.map { shop ->
|
||||
val loc = org.bukkit.Location(player.world, shop.x.toDouble(), shop.y.toDouble(), shop.z.toDouble())
|
||||
shop to playerLoc.distance(loc)
|
||||
}
|
||||
.sortedBy { it.second }
|
||||
|
||||
player.sendMessage(Component.text("--- Shop List (Nearest) ---", NamedTextColor.GOLD))
|
||||
if (sortedShops.isEmpty()) {
|
||||
player.sendMessage(Component.text("No shops found in this world.", NamedTextColor.GRAY))
|
||||
} else {
|
||||
sortedShops.forEach { (shop, distance) ->
|
||||
val distStr = String.format("%.1fm", distance)
|
||||
val itemStack = shop.item
|
||||
val itemName = if (itemStack != null) {
|
||||
itemStack.type.name.lowercase().split("_")
|
||||
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
} else {
|
||||
"Not Set"
|
||||
}
|
||||
val price = net.hareworks.hcu.economy.api.EconomyService.format(shop.price.toInt())
|
||||
|
||||
player.sendMessage(
|
||||
Component.text("#${shop.id}", NamedTextColor.YELLOW)
|
||||
.append(Component.text(" - ", NamedTextColor.GRAY))
|
||||
.append(Component.text(itemName, NamedTextColor.AQUA))
|
||||
.append(Component.text(" ($price)", NamedTextColor.GREEN))
|
||||
.append(Component.text(" - ", NamedTextColor.GRAY))
|
||||
.append(Component.text(distStr, NamedTextColor.GRAY))
|
||||
)
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
private fun deleteShop(plugin: ShopPlugin, sender: org.bukkit.command.CommandSender, id: Int) {
|
||||
val shop = net.hareworks.hcu.shop.database.ShopRepository.findById(id)
|
||||
if (shop == null) {
|
||||
sender.sendMessage(Component.text("Shop with ID $id not found.", NamedTextColor.RED))
|
||||
return
|
||||
}
|
||||
|
||||
// Remove Display
|
||||
val world = org.bukkit.Bukkit.getWorld(shop.worldUid)
|
||||
if (world != null) {
|
||||
val location = org.bukkit.Location(world, shop.x.toDouble(), shop.y.toDouble(), shop.z.toDouble())
|
||||
net.hareworks.hcu.shop.ShopVisuals.removeDisplay(location, shop.face)
|
||||
|
||||
val block = location.block
|
||||
if (block.type == Material.OAK_SIGN) {
|
||||
// Check for other shops at this location
|
||||
val otherFace = if (shop.face == "FRONT") "BACK" else "FRONT"
|
||||
val otherShop = net.hareworks.hcu.shop.database.ShopRepository.findByLocationAndFace(location, otherFace)
|
||||
|
||||
if (otherShop == null) {
|
||||
// No other shops, remove the block
|
||||
block.type = Material.AIR
|
||||
// Create breakage particles/sound? Optional.
|
||||
} else {
|
||||
// Clear text for that side
|
||||
val sign = block.state as? org.bukkit.block.Sign
|
||||
if (sign != null) {
|
||||
val side = if (shop.face == "FRONT") org.bukkit.block.sign.Side.FRONT else org.bukkit.block.sign.Side.BACK
|
||||
val signSide = sign.getSide(side)
|
||||
signSide.line(0, Component.empty())
|
||||
signSide.line(1, Component.empty())
|
||||
signSide.line(2, Component.empty())
|
||||
signSide.line(3, Component.empty())
|
||||
sign.update()
|
||||
sender.sendMessage(Component.text("Sign block kept because another shop exists on the other side.", NamedTextColor.YELLOW))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
net.hareworks.hcu.shop.database.ShopRepository.delete(id)
|
||||
sender.sendMessage(Component.text("Shop #$id deleted.", NamedTextColor.GREEN))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import org.bukkit.entity.ItemDisplay
|
|||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.block.BlockPlaceEvent
|
||||
import org.bukkit.event.block.BlockBreakEvent
|
||||
import org.bukkit.event.block.SignChangeEvent
|
||||
import org.bukkit.event.player.PlayerInteractEvent
|
||||
import org.bukkit.event.block.Action
|
||||
|
|
@ -33,7 +34,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
|
||||
private val tierKey = NamespacedKey(plugin, "shop_tier")
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onShopSignPlace(event: BlockPlaceEvent) {
|
||||
val item = event.itemInHand
|
||||
val meta = item.itemMeta ?: return
|
||||
|
|
@ -47,9 +48,9 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
return
|
||||
}
|
||||
|
||||
// Check block against (Chest)
|
||||
if (event.blockAgainst.type != Material.CHEST) {
|
||||
// Only valid on top of chests
|
||||
// Check block against (Chest/Container)
|
||||
if (event.blockAgainst.type !in ShopVisuals.VALID_CONTAINERS) {
|
||||
// Only valid on top of supported containers
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
|
@ -64,12 +65,12 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
// Just ensure color is black initially if needed, but Visuals will overwrite.
|
||||
val frontSide = signState.getSide(Side.FRONT)
|
||||
frontSide.color = DyeColor.BLACK
|
||||
frontSide.isGlowingText = false
|
||||
frontSide.isGlowingText = true
|
||||
|
||||
// Back Text
|
||||
val backSide = signState.getSide(Side.BACK)
|
||||
backSide.color = DyeColor.BLACK
|
||||
backSide.isGlowingText = false
|
||||
backSide.isGlowingText = true
|
||||
|
||||
// Save Tier to Block and Wax (Lock)
|
||||
signState.persistentDataContainer.set(tierKey, PersistentDataType.INTEGER, tier)
|
||||
|
|
@ -88,15 +89,15 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
} else {
|
||||
val actorId = playerEntry.actorId
|
||||
|
||||
plugin.logger.info("Creating shops for player ${event.player.name} at ${event.blockPlaced.location}")
|
||||
|
||||
// Create FRONT shop
|
||||
val frontShop = ShopRepository.create(
|
||||
actorId = actorId,
|
||||
location = event.blockPlaced.location,
|
||||
face = "FRONT"
|
||||
)
|
||||
if (frontShop != null) {
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, frontShop, Side.FRONT)
|
||||
}
|
||||
plugin.logger.info("Front shop created: ${frontShop != null} (ID: ${frontShop?.id})")
|
||||
|
||||
// Create BACK shop
|
||||
val backShop = ShopRepository.create(
|
||||
|
|
@ -104,13 +105,35 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
location = event.blockPlaced.location,
|
||||
face = "BACK"
|
||||
)
|
||||
if (backShop != null) {
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK)
|
||||
}
|
||||
}
|
||||
plugin.logger.info("Back shop created: ${backShop != null} (ID: ${backShop?.id})")
|
||||
|
||||
// Spawn Displays
|
||||
spawnDisplays(event.blockPlaced)
|
||||
// Close UI and Update Visuals (Delayed to override client-side editor opening)
|
||||
// Close UI immediately (Next tick)
|
||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||
event.player.closeInventory()
|
||||
})
|
||||
|
||||
// Update Visuals Delayed to ensure client state is settled
|
||||
plugin.server.scheduler.runTaskLater(plugin, Runnable {
|
||||
plugin.logger.info("Executing delayed visual update task.")
|
||||
|
||||
if (frontShop != null) {
|
||||
plugin.logger.info("Updating FRONT sign.")
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, frontShop, Side.FRONT)
|
||||
} else {
|
||||
plugin.logger.warning("Front shop was null in delayed task.")
|
||||
}
|
||||
|
||||
if (backShop != null) {
|
||||
plugin.logger.info("Updating BACK sign.")
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK)
|
||||
} else {
|
||||
plugin.logger.warning("Back shop was null in delayed task.")
|
||||
}
|
||||
|
||||
spawnDisplays(event.blockPlaced)
|
||||
}, 1L)
|
||||
}
|
||||
}
|
||||
|
||||
private fun spawnDisplays(block: org.bukkit.block.Block) {
|
||||
|
|
@ -118,7 +141,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
val data = block.blockData as? Rotatable ?: return
|
||||
val rotation = data.rotation // BlockFace
|
||||
|
||||
val yaw = yawFromBlockFace(rotation) + 90f
|
||||
val yaw = ShopVisuals.yawFromBlockFace(rotation) + 90f
|
||||
|
||||
// Calculate offsets relative to center (0.5, 0, 0.5)
|
||||
// Original request assumes 0,0,0 is block origin.
|
||||
|
|
@ -129,14 +152,6 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
val backOffset = Vector3f(-0.25f, 0.25f, 0.0f)
|
||||
|
||||
// Rotate offsets
|
||||
// yaw is degrees clockwise from South (Z+).
|
||||
// radians need to be negated for standard CCW rotation logic if we use that,
|
||||
// or just use rotateY with appropriate sign.
|
||||
// rotateY in JOML: "Rotates this vector by the given angle in radians around the Y axis"
|
||||
// Standard geometric Y rotation is CCW.
|
||||
// Minecraft Yaw 90 (West) should rotate (0,0,1) to (-1,0,0).
|
||||
// Std Rot 90 CCW: (0,0,1) -> (1,0,0).
|
||||
// So they are opposite. We should rotate by -yaw.
|
||||
|
||||
val rad = Math.toRadians(yaw.toDouble())
|
||||
frontOffset.rotateY(-rad.toFloat())
|
||||
|
|
@ -151,7 +166,6 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
|
||||
// Spawn Front
|
||||
world.spawn(frontPos, ItemDisplay::class.java) { display ->
|
||||
// display.setItemStack(ItemStack(Material.AIR)) // Default empty
|
||||
display.setRotation(yaw, 0f)
|
||||
|
||||
// Transformation
|
||||
|
|
@ -182,28 +196,6 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun yawFromBlockFace(face: BlockFace): Float {
|
||||
return when (face) {
|
||||
BlockFace.SOUTH -> 0f
|
||||
BlockFace.SOUTH_SOUTH_WEST -> 22.5f
|
||||
BlockFace.SOUTH_WEST -> 45f
|
||||
BlockFace.WEST_SOUTH_WEST -> 67.5f
|
||||
BlockFace.WEST -> 90f
|
||||
BlockFace.WEST_NORTH_WEST -> 112.5f
|
||||
BlockFace.NORTH_WEST -> 135f
|
||||
BlockFace.NORTH_NORTH_WEST -> 157.5f
|
||||
BlockFace.NORTH -> 180f
|
||||
BlockFace.NORTH_NORTH_EAST -> 202.5f
|
||||
BlockFace.NORTH_EAST -> 225f
|
||||
BlockFace.EAST_NORTH_EAST -> 247.5f
|
||||
BlockFace.EAST -> 270f
|
||||
BlockFace.EAST_SOUTH_EAST -> 292.5f
|
||||
BlockFace.SOUTH_EAST -> 315f
|
||||
BlockFace.SOUTH_SOUTH_EAST -> 337.5f
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onSignChange(event: SignChangeEvent) {
|
||||
val player = event.player
|
||||
|
|
@ -230,15 +222,29 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
player.sendMessage(Component.text("Shop price set to $$price", NamedTextColor.GREEN))
|
||||
|
||||
// Update Actual Sign Visuals
|
||||
ShopVisuals.updateSign(plugin, event.block, shopData.copy(price = price), event.side)
|
||||
// Run 1 tick later to override server-side sign handling if needed
|
||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||
ShopVisuals.updateSign(plugin, event.block, shopData.copy(price = price), event.side)
|
||||
})
|
||||
} else {
|
||||
player.sendMessage(Component.text("Invalid price format. Please enter a valid number.", NamedTextColor.RED))
|
||||
// Revert lock if invalid? Or keep it unlocked?
|
||||
// Better to relock to be safe, regardless of valid input or not.
|
||||
}
|
||||
|
||||
// Clean up Map
|
||||
ShopInputMap.pendingInputs.remove(player.uniqueId)
|
||||
|
||||
event.isCancelled = true
|
||||
event.isCancelled = true // Cancel the actual text update since we handled it virtually
|
||||
|
||||
// RELOCK THE SIGN
|
||||
val signState = event.block.state as? Sign
|
||||
if (signState != null) {
|
||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||
signState.isWaxed = true
|
||||
signState.update()
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
// Normal sign editing protection
|
||||
|
|
@ -246,6 +252,14 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
if (state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) {
|
||||
// If strictly preventing editing of shop signs unless in setup mode
|
||||
event.isCancelled = true
|
||||
|
||||
val sideStr = if (event.side == Side.FRONT) "FRONT" else "BACK"
|
||||
val shopData = ShopRepository.findByLocationAndFace(event.block.location, sideStr)
|
||||
if (shopData != null) {
|
||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||
ShopVisuals.updateSign(plugin, event.block, shopData, event.side)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -283,15 +297,99 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
if (shopData.actorId == playerEntry.actorId) {
|
||||
// Is Owner -> Open Edit GUI
|
||||
openEditGui(event.player, shopData)
|
||||
|
||||
// Also show info to owner
|
||||
val item = shopData.item
|
||||
val itemName = if (item != null) {
|
||||
item.type.name.lowercase().split("_")
|
||||
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
} else {
|
||||
"Not Set"
|
||||
}
|
||||
val stock = ShopVisuals.calculateStock(block, item)
|
||||
val priceStr = net.hareworks.hcu.economy.api.EconomyService.format(shopData.price.toInt())
|
||||
val amount = item?.amount ?: 0
|
||||
val ownerName = event.player.name // Use current player name since they are owner
|
||||
|
||||
event.player.sendMessage(Component.empty())
|
||||
event.player.sendMessage(Component.text("Shop Owner: ", NamedTextColor.GRAY).append(Component.text(ownerName, NamedTextColor.WHITE)))
|
||||
event.player.sendMessage(Component.text("Item: ", NamedTextColor.GRAY).append(Component.text(itemName, NamedTextColor.AQUA)))
|
||||
event.player.sendMessage(Component.text("Price: ", NamedTextColor.GRAY).append(Component.text("$priceStr for x$amount", NamedTextColor.GREEN)))
|
||||
event.player.sendMessage(Component.text("Stock: ", NamedTextColor.GRAY).append(Component.text(stock, NamedTextColor.YELLOW)))
|
||||
event.player.sendMessage(Component.empty())
|
||||
|
||||
} else {
|
||||
// Is Customer -> Buy Logic (TODO)
|
||||
event.player.sendMessage(Component.text("Customer interactions not implemented yet.", NamedTextColor.GRAY))
|
||||
// Display Shop Info (Shared Logic)
|
||||
// We want to show this to everyone, including owner? Or just customers?
|
||||
// User request implies showing it on click.
|
||||
// Let's refactor display logic or just copy-paste for safety for now.
|
||||
|
||||
val item = shopData.item
|
||||
val itemName = if (item != null) {
|
||||
item.type.name.lowercase().split("_")
|
||||
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
} else {
|
||||
"Not Set"
|
||||
}
|
||||
|
||||
// Calculate Stock
|
||||
val stock = ShopVisuals.calculateStock(block, item)
|
||||
val priceStr = net.hareworks.hcu.economy.api.EconomyService.format(shopData.price.toInt())
|
||||
val amount = item?.amount ?: 0
|
||||
|
||||
val ownerName = try {
|
||||
org.bukkit.Bukkit.getOfflinePlayer(playerEntry.uuid).name ?: playerEntry.uuid.toString()
|
||||
} catch (e: Exception) {
|
||||
playerEntry.uuid.toString()
|
||||
}
|
||||
|
||||
event.player.sendMessage(Component.empty())
|
||||
event.player.sendMessage(Component.text("Shop Owner: ", NamedTextColor.GRAY).append(Component.text(ownerName, NamedTextColor.WHITE)))
|
||||
event.player.sendMessage(Component.text("Item: ", NamedTextColor.GRAY).append(Component.text(itemName, NamedTextColor.AQUA)))
|
||||
event.player.sendMessage(Component.text("Price: ", NamedTextColor.GRAY).append(Component.text("$priceStr for x$amount", NamedTextColor.GREEN)))
|
||||
event.player.sendMessage(Component.text("Stock: ", NamedTextColor.GRAY).append(Component.text(stock, NamedTextColor.YELLOW)))
|
||||
event.player.sendMessage(Component.empty())
|
||||
}
|
||||
} else {
|
||||
plugin.logger.warning("Shop data missing for location ${block.location} face $face")
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onContainerInteract(event: PlayerInteractEvent) {
|
||||
if (event.action != Action.RIGHT_CLICK_BLOCK) return
|
||||
val block = event.clickedBlock ?: return
|
||||
if (block.type !in ShopVisuals.VALID_CONTAINERS) return
|
||||
|
||||
// Check for Shop Sign above
|
||||
val signBlock = block.getRelative(BlockFace.UP)
|
||||
if (signBlock.type != Material.OAK_SIGN) return
|
||||
|
||||
val state = signBlock.state as? Sign ?: return
|
||||
if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return
|
||||
|
||||
// It is a shop chest. Check ownership.
|
||||
val playerIdService = PlayerIdServiceImpl(plugin.logger)
|
||||
val playerEntry = playerIdService.find(event.player.uniqueId)
|
||||
|
||||
// We need to find at least one shop associated with this sign to determine owner
|
||||
// Since both front and back shops have same owner, just checking FRONT is enough if it exists, or BACK.
|
||||
var shopData = ShopRepository.findByLocationAndFace(signBlock.location, "FRONT")
|
||||
if (shopData == null) {
|
||||
shopData = ShopRepository.findByLocationAndFace(signBlock.location, "BACK")
|
||||
}
|
||||
|
||||
if (shopData != null && playerEntry != null) {
|
||||
if (shopData.actorId != playerEntry.actorId) {
|
||||
// Not owner -> Cancel open
|
||||
event.isCancelled = true
|
||||
event.player.sendMessage(Component.text("This chest is locked by the shop.", NamedTextColor.RED))
|
||||
} else {
|
||||
// Owner -> Allow open (Default behavior)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openEditGui(player: org.bukkit.entity.Player, shopData: net.hareworks.hcu.shop.database.ShopData) {
|
||||
// 1x5 UI using Hopper
|
||||
val inventory = org.bukkit.Bukkit.createInventory(player, InventoryType.HOPPER, Component.text("Shop Setup: ${shopData.face}"))
|
||||
|
|
@ -482,9 +580,74 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
ShopInputMap.pendingInputs[player.uniqueId] = shopId
|
||||
|
||||
// Open Editor
|
||||
|
||||
// UNLOCK SIGN SO PLAYER CAN EDIT
|
||||
sign.isWaxed = false
|
||||
sign.update()
|
||||
|
||||
player.openSign(sign, side)
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onBlockBreak(event: org.bukkit.event.block.BlockBreakEvent) {
|
||||
val block = event.block
|
||||
|
||||
// Handle Container Break Protection
|
||||
if (block.type in ShopVisuals.VALID_CONTAINERS) {
|
||||
val signBlock = block.getRelative(BlockFace.UP)
|
||||
if (signBlock.type == Material.OAK_SIGN) {
|
||||
val state = signBlock.state as? Sign
|
||||
if (state != null && state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) {
|
||||
event.isCancelled = true
|
||||
event.player.sendMessage(Component.text("Please remove the shop sign first.", NamedTextColor.RED))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Shop Sign Break
|
||||
if (block.type != Material.OAK_SIGN) return
|
||||
|
||||
val state = block.state as? Sign ?: return
|
||||
if (!state.persistentDataContainer.has(tierKey, PersistentDataType.INTEGER)) return
|
||||
|
||||
// It is a shop sign.
|
||||
val playerIdService = PlayerIdServiceImpl(plugin.logger)
|
||||
val playerEntry = playerIdService.find(event.player.uniqueId)
|
||||
|
||||
// Find Shop Data
|
||||
val location = block.location
|
||||
val frontShop = ShopRepository.findByLocationAndFace(location, "FRONT")
|
||||
val backShop = ShopRepository.findByLocationAndFace(location, "BACK")
|
||||
|
||||
// Check ownership (Optional: assume if they can break block they can remove shop)
|
||||
if (playerEntry != null) {
|
||||
val ownerId = frontShop?.actorId ?: backShop?.actorId
|
||||
if (ownerId != null && ownerId != playerEntry.actorId) {
|
||||
if (!event.player.hasPermission("hcu.shop.admin")) {
|
||||
event.isCancelled = true
|
||||
event.player.sendMessage(Component.text("You are not the owner of this shop.", NamedTextColor.RED))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Shops
|
||||
if (frontShop != null) {
|
||||
ShopRepository.delete(frontShop.id)
|
||||
ShopVisuals.removeDisplay(location, "FRONT")
|
||||
}
|
||||
|
||||
if (backShop != null) {
|
||||
ShopRepository.delete(backShop.id)
|
||||
ShopVisuals.removeDisplay(location, "BACK")
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -493,7 +656,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
val data = block.blockData as? Rotatable ?: return
|
||||
val rotation = data.rotation
|
||||
|
||||
val yaw = yawFromBlockFace(rotation) + 90f
|
||||
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())
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user