From 1e4f3dab01dc19135f169481b6c7d1dadae81bf8 Mon Sep 17 00:00:00 2001 From: Hamza Ali Date: Wed, 11 Mar 2020 18:11:27 +0700 Subject: [PATCH] Add messages.yml and functionality to purchase GUI The plugin now has a functioning messages.yml that has the options to set messages (improving customisability), as well as the shop now has a working buy menu where u can see the price changes and what not. Supports prices and rounding to 2 decimal places. --- .../pw.hamzantal.shopreborn/ClickListeners.kt | 130 ++++++++++++++++-- .../pw.hamzantal.shopreborn/Configuration.kt | 24 ++-- .../pw.hamzantal.shopreborn/Messages.kt | 44 ++++++ .../pw.hamzantal.shopreborn/PurchaseItems.kt | 47 +++++++ .../pw.hamzantal.shopreborn/ShopReborn.kt | 13 +- src/main/resources/messages.yml | 22 +++ 6 files changed, 252 insertions(+), 28 deletions(-) create mode 100644 src/main/kotlin/pw.hamzantal.shopreborn/Messages.kt create mode 100644 src/main/kotlin/pw.hamzantal.shopreborn/PurchaseItems.kt create mode 100644 src/main/resources/messages.yml diff --git a/src/main/kotlin/pw.hamzantal.shopreborn/ClickListeners.kt b/src/main/kotlin/pw.hamzantal.shopreborn/ClickListeners.kt index f43eef0..c971ab2 100644 --- a/src/main/kotlin/pw.hamzantal.shopreborn/ClickListeners.kt +++ b/src/main/kotlin/pw.hamzantal.shopreborn/ClickListeners.kt @@ -1,24 +1,52 @@ package pw.hamzantal.shopreborn -import hazae41.minecraft.kutils.bukkit.msg +import org.bukkit.Bukkit +import org.bukkit.Material import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType.* import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryCloseEvent +import org.bukkit.inventory.Inventory +import org.bukkit.inventory.ItemStack +import kotlin.math.max +import kotlin.math.min + +enum class PurchaseType { BUY, SELL } + +class PurchaseEvent( + val inv: Inventory, + val item: ItemStack, + val shop: ShopConfig, + val p: Player, + val block: ShopConfig.Item, + val type: PurchaseType +) + +val purchases = mutableListOf() + +fun closeListener(e: InventoryCloseEvent) { + val purchase = purchases.firstOrNull { it.inv == e.inventory } ?: return + purchases -= purchase +} fun baseListener(e: InventoryClickEvent) { - if (e.inventory == Configurations.main.inventory) { + if (e.inventory == GlobalConfig.main.inventory) { mainClick(e) return } - val shop = Configurations.shops.firstOrNull { it.inventories.contains(e.inventory)} ?: return - shopClick(e, shop) + val purchase = purchases.firstOrNull { it.inv == e.inventory } + if (purchase != null) clickInventory(e, purchase) + + val shop = GlobalConfig.shops.firstOrNull { it.inventories.contains(e.inventory) } ?: return + shopClick(e, shop) } fun mainClick(e: InventoryClickEvent) { e.isCancelled = true if (e.rawSlot > e.inventory.size) return - val main = Configurations.main + val main = GlobalConfig.main val p = e.whoClicked as Player val block = main.blocks.firstOrNull { it.slot == e.slot } ?: return @@ -31,7 +59,7 @@ fun shopClick(e: InventoryClickEvent, shop: ShopConfig) { e.isCancelled = true if (e.rawSlot > e.inventory.size) return - val main = Configurations.main + val main = GlobalConfig.main val p = e.whoClicked as Player //Check Menu Buttons @@ -47,7 +75,7 @@ fun shopClick(e: InventoryClickEvent, shop: ShopConfig) { return } - if(e.currentItem == main.buttons.previous) { + if (e.currentItem == main.buttons.previous) { val current = shop.inventories.indexOf(e.inventory) if (current == 0) return p.openInventory(shop.inventories[current - 1]) @@ -55,13 +83,91 @@ fun shopClick(e: InventoryClickEvent, shop: ShopConfig) { //Buy / Sell Item val block = shop.blocks.firstOrNull { it.item == e.currentItem } ?: return + if (block is ShopConfig.Command) { + TODO() + } - when(block) { - is ShopConfig.Item -> { - p.msg("Buying ${block.item.type.name} for ${block.buy}") + when (e.click) { + LEFT -> buy(e, p, shop, block as ShopConfig.Item) + RIGHT -> TODO() + MIDDLE -> TODO() + else -> return + } +} + +fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) { + e.isCancelled = true + val block = pe.block + val origin = block.item + + val singleCost = + if (pe.type == PurchaseType.BUY) pe.block.buy / origin.amount + else pe.block.sell / origin.amount + + val lore = + if (pe.type == PurchaseType.BUY) GlobalConfig.messages.buyLore + else GlobalConfig.messages.sellLore + + when (e.currentItem) { + PurchaseItems.set1 -> { + pe.item.amount = 1 + pe.item.setLore(lore.mixPlaceholder(price = singleCost)) + } + PurchaseItems.sub10 -> { + val now = max(1, pe.item.amount - 10) + pe.item.amount = now + pe.item.setLore(lore.mixPlaceholder(price = singleCost * now)) } - is ShopConfig.Command -> { - p.msg("Buying ${block.item.type.name} producing ${block.commands.joinToString(",")}") + PurchaseItems.sub1 -> { + val now = max(1, pe.item.amount - 1) + pe.item.amount = now + pe.item.setLore(lore.mixPlaceholder(price = singleCost * now)) } + PurchaseItems.add1 -> { + val now = min(64, pe.item.amount + 1) + pe.item.amount = now + pe.item.setLore(lore.mixPlaceholder(price = singleCost * now)) + } + PurchaseItems.add10 -> { + val now = min(64, pe.item.amount + 10) + pe.item.amount = now + pe.item.setLore(lore.mixPlaceholder(price = singleCost * now)) + } + PurchaseItems.set64 -> { + pe.item.amount = 64 + pe.item.setLore(lore.mixPlaceholder(price = singleCost * 64)) + } + } + pe.inv.setItem(22, pe.item) +} + +val base = Array(54) { null }.apply { + set(18, PurchaseItems.set1) + set(19, PurchaseItems.sub10) + set(20, PurchaseItems.sub1) + set(24, PurchaseItems.add1) + set(25, PurchaseItems.add10) + set(26, PurchaseItems.set64) +} + +fun Material.prettyName() = name.split("_").joinToString(" ") { it.toLowerCase().capitalize() } + +fun buy(e: InventoryClickEvent, p: Player, shop: ShopConfig, block: ShopConfig.Item) { + val item = e.currentItem.clone() + if (block.buy == -1.0) return + + val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName() + + val inv = Bukkit.createInventory(null, 54, "&2Buying $name".c).apply { + contents = base.clone() + + val buyLore = GlobalConfig.messages.buyLore.mixPlaceholder(price = block.buy) + setItem(22, e.currentItem.setLore(buyLore)) + setItem(39, PurchaseItems.buyConfirm) + setItem(41, PurchaseItems.cancel) } + + p.openInventory(inv) + purchases.add(PurchaseEvent(inv, item, shop, p, block, PurchaseType.BUY)) } + diff --git a/src/main/kotlin/pw.hamzantal.shopreborn/Configuration.kt b/src/main/kotlin/pw.hamzantal.shopreborn/Configuration.kt index c34f373..dcd62b2 100644 --- a/src/main/kotlin/pw.hamzantal.shopreborn/Configuration.kt +++ b/src/main/kotlin/pw.hamzantal.shopreborn/Configuration.kt @@ -12,9 +12,10 @@ import java.io.File import kotlin.math.ceil import kotlin.math.max -object Configurations { +object GlobalConfig { var dataFolder = File("") lateinit var main: MainConfig + lateinit var messages: Messages val shops = mutableListOf() } @@ -59,10 +60,10 @@ class MainConfig(file: File) : ConfigFile(file) { } fun addShops() { - Configurations.shops.addAll(shops!!.keys.map { + GlobalConfig.shops.addAll(shops!!.keys.map { ShopConfig( it, - Configurations.dataFolder[shops!!.getString(it)] + GlobalConfig.dataFolder[shops!!.getString(it)] ) }) } @@ -70,7 +71,7 @@ class MainConfig(file: File) : ConfigFile(file) { class ShopConfig(val name: String, file: File) : ConfigFile(file) { open class Block(val item: ItemStack) - class Item(item: ItemStack, val buy: Int, val sell: Int) : Block(item) + class Item(item: ItemStack, val buy: Double, val sell: Double) : Block(item) class Command(item: ItemStack, val asPlayer: Boolean, val commands: List) : Block(item) init { @@ -104,8 +105,8 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) { ?.map { val type = it.getString("type") - val buy = it.getInt("buyPrice", -1) - val sell = it.getInt("sellPrice", -1) + val buy = it.getDouble("buyPrice", -1.0) + val sell = it.getDouble("sellPrice", -.01) val item = it.getConfigurationSection("item").asItem(buy, sell) when (type) { @@ -128,6 +129,7 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) { val inventories = mutableListOf() val inventory: Inventory + init { inventory = makeInventory(size, title, menuRow, blocks, 1) inventories += inventory @@ -140,7 +142,7 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) { fun makeInventory(size: Int, title: String, menuRow: Int = -1, items: List, page: Int): Inventory { return Bukkit.createInventory(null, size, title.c).apply { if (menuRow > 0) { - val buttons = Configurations.main.buttons + val buttons = GlobalConfig.main.buttons for (i in menuRow until menuRow + 9) { setItem(i, buttons.pane) } @@ -157,15 +159,17 @@ fun makeInventory(size: Int, title: String, menuRow: Int = -1, items: List 0) configLore += "&fPurchase for $$buy".c - if (sell > 0) configLore += "&fSell for $$sell".c + val messages = GlobalConfig.messages + + if (buy > 0) configLore += messages.buyLore.mixPlaceholder(price = buy) + if (sell > 0) configLore += messages.sellLore.mixPlaceholder(price = sell) lore = configLore } diff --git a/src/main/kotlin/pw.hamzantal.shopreborn/Messages.kt b/src/main/kotlin/pw.hamzantal.shopreborn/Messages.kt new file mode 100644 index 0000000..acf9823 --- /dev/null +++ b/src/main/kotlin/pw.hamzantal.shopreborn/Messages.kt @@ -0,0 +1,44 @@ +package pw.hamzantal.shopreborn + +import hazae41.minecraft.kutils.bukkit.ConfigFile +import java.io.File + +class Messages(file: File, pl: ShopReborn) : ConfigFile(file) { + val currency by string("currency", "$") + + val buyLore by string("buyLore") + val sellLore by string("stringLore") + val itemLore by string("itemLore") + + val set1 by string("set1") + val sub10 by string("sub10") + val sub1 by string("sub1") + + val add1 by string("add1") + val add10 by string("add10") + val set64 by string("set64") + + val sellConfirm by string("sell.confirm") + val sellAll by string("sell.sellAll") + val sellAllLore by string("sell.sellAllLore") + + val buyConfirm by string("buy.confirm") + + val cancel by string("cancel") + + init { + if (!file.exists()) { + val messages = pl.getResource("messages.yml").bufferedReader().readLines().joinToString("\n") + file.createNewFile() + file.writeText(messages) + } + } +} + +fun String.mixPlaceholder(amount: Int = 0, price: Double = 0.0): String = + replace("%AMOUNT%", amount.toString()) + .replace( + "%PRICE%", + String.format("%s%.2f", GlobalConfig.messages.currency, price) + ) + .c \ No newline at end of file diff --git a/src/main/kotlin/pw.hamzantal.shopreborn/PurchaseItems.kt b/src/main/kotlin/pw.hamzantal.shopreborn/PurchaseItems.kt new file mode 100644 index 0000000..e1fd931 --- /dev/null +++ b/src/main/kotlin/pw.hamzantal.shopreborn/PurchaseItems.kt @@ -0,0 +1,47 @@ +package pw.hamzantal.shopreborn + +import org.bukkit.Material +import org.bukkit.inventory.ItemStack + +object PurchaseItems { + fun paneStack(damage: Int, name: String): ItemStack { + return ItemStack(Material.STAINED_GLASS_PANE, 1, damage.toShort()).apply { + itemMeta = itemMeta.apply { + displayName = name.c + } + } + } + + fun blockStack(damage: Int, name: String): ItemStack { + return ItemStack(Material.STAINED_GLASS, 1, damage.toShort()).apply { + itemMeta = itemMeta.apply { + displayName = name.c + } + } + } + + + val msgs = GlobalConfig.messages + val set1 = paneStack(14, msgs.set1) + val sub10 = paneStack(14, msgs.sub10) + val sub1 = paneStack(14, msgs.sub1) + + val add1 = paneStack(5, msgs.add1) + val add10 = paneStack(5, msgs.add10) + val set64 = paneStack(5, msgs.set64) + + val sellConfirm = blockStack(5, msgs.sellConfirm) + val sellAll = blockStack(5, msgs.sellAll) + + val buyConfirm = blockStack(5, msgs.buyConfirm) + + val cancel = blockStack(14, msgs.cancel) +} + +fun ItemStack.setLore(s: String): ItemStack { + return apply { + itemMeta = itemMeta.apply { + lore = listOf(s.c) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/pw.hamzantal.shopreborn/ShopReborn.kt b/src/main/kotlin/pw.hamzantal.shopreborn/ShopReborn.kt index f97b72f..3677083 100644 --- a/src/main/kotlin/pw.hamzantal.shopreborn/ShopReborn.kt +++ b/src/main/kotlin/pw.hamzantal.shopreborn/ShopReborn.kt @@ -1,7 +1,6 @@ package pw.hamzantal.shopreborn import hazae41.minecraft.kutils.bukkit.command -import hazae41.minecraft.kutils.bukkit.info import hazae41.minecraft.kutils.bukkit.listen import hazae41.minecraft.kutils.get import org.bukkit.entity.Player @@ -12,21 +11,23 @@ class ShopReborn : JavaPlugin() { override fun onEnable() { saveDefaultConfig() - Configurations.dataFolder = dataFolder - Configurations.main = + GlobalConfig.dataFolder = dataFolder + GlobalConfig.messages = Messages(dataFolder["messages.yml"], this) + GlobalConfig.main = MainConfig(dataFolder["config.yml"]) - Configurations.main.addShops() + GlobalConfig.main.addShops() listen(callback = ::baseListener) + listen(callback = ::closeListener) command("shop") { sender, args -> if (sender !is Player) return@command if (args.isEmpty()) { - sender.openInventory(Configurations.main.inventory) + sender.openInventory(GlobalConfig.main.inventory) return@command } - val shop = Configurations.shops.firstOrNull { it.name == args.component1() } ?: return@command + val shop = GlobalConfig.shops.firstOrNull { it.name == args.component1() } ?: return@command sender.openInventory(shop.inventory) } } diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml new file mode 100644 index 0000000..b9f2e3e --- /dev/null +++ b/src/main/resources/messages.yml @@ -0,0 +1,22 @@ +currency: "$" + +buyLore: "&6Buy price: &c%PRICE%" +sellLore: "&6Sell price: &c%PRICE%" +itemLore: "&6Left click to buy, Right click to sell" + +set1: "&c&lSet to 1" +sub10: "&c&lRemove 10" +sub1: "&c&lRemove 1" + +add1: "&a&lAdd 1" +add10: "&a&lAdd 10" +set64: "&a&lSet 64" + +# Confirm for selling/buying (after selecting the item) +sell: + confirm: "&a&lConfirm" + sellAll: "&a&lSell all" + sellAllLore: "&7Sell all for &a%PRICE%" +buy: + confirm: "&a&lConfirm" +cancel: "&c&lCancel" \ No newline at end of file