Reload feature, vault integration, complete all

- /shop
- /shop reload
- Buy Item by left click
- Sell Item by right click
- Sell all with middle/shift click
- Items to execute commands
- Automatic pages
- Automatic menu row
master
ALI Hamza 2020-03-12 09:43:26 +07:00
parent 1e4f3dab01
commit 3a5cd8cda1
No known key found for this signature in database
GPG Key ID: BCA8A46C87798C4C
9 changed files with 330 additions and 87 deletions

@ -16,6 +16,7 @@ repositories {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.github.hazae41:mc-kutils:master-SNAPSHOT" implementation "com.github.hazae41:mc-kutils:master-SNAPSHOT"
compileOnly "com.github.MilkBowl:VaultAPI:1.7"
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT" compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
} }

@ -1,7 +1,9 @@
package pw.hamzantal.shopreborn package pw.hamzantal.shopreborn
import hazae41.minecraft.kutils.bukkit.execute
import hazae41.minecraft.kutils.bukkit.msg
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType.* import org.bukkit.event.inventory.ClickType.*
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
@ -84,21 +86,34 @@ fun shopClick(e: InventoryClickEvent, shop: ShopConfig) {
//Buy / Sell Item //Buy / Sell Item
val block = shop.blocks.firstOrNull { it.item == e.currentItem } ?: return val block = shop.blocks.firstOrNull { it.item == e.currentItem } ?: return
if (block is ShopConfig.Command) { if (block is ShopConfig.Command) {
TODO() block.commands.forEach {
if (it.contains("%PLAYER%"))
Bukkit.getConsoleSender().execute(it.replace("%PLAYER%", p.name))
else p.chat(it)
}
} }
if (block is ShopConfig.Item) {
when (e.click) { when (e.click) {
LEFT -> buy(e, p, shop, block as ShopConfig.Item) LEFT -> buy(e, p, shop, block)
RIGHT -> TODO() RIGHT -> sell(e, p, shop, block)
MIDDLE -> TODO() MIDDLE, SHIFT_LEFT -> {
val raw = block.raw
val howMany = p containing raw
val single = block.sell / block.item.amount
val op = Bukkit.getOfflinePlayer(p.uniqueId)
executeSell(raw, howMany, single, p, op)
}
else -> return else -> return
} }
} }
}
fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) { fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) {
e.isCancelled = true e.isCancelled = true
val block = pe.block val block = pe.block
val origin = block.item val origin = block.item
val p = pe.p
val singleCost = val singleCost =
if (pe.type == PurchaseType.BUY) pe.block.buy / origin.amount if (pe.type == PurchaseType.BUY) pe.block.buy / origin.amount
@ -108,6 +123,10 @@ fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) {
if (pe.type == PurchaseType.BUY) GlobalConfig.messages.buyLore if (pe.type == PurchaseType.BUY) GlobalConfig.messages.buyLore
else GlobalConfig.messages.sellLore else GlobalConfig.messages.sellLore
val op = Bukkit.getOfflinePlayer(p.uniqueId)
if (!econ.hasAccount(op))
econ.createPlayerAccount(op)
when (e.currentItem) { when (e.currentItem) {
PurchaseItems.set1 -> { PurchaseItems.set1 -> {
pe.item.amount = 1 pe.item.amount = 1
@ -137,8 +156,80 @@ fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) {
pe.item.amount = 64 pe.item.amount = 64
pe.item.setLore(lore.mixPlaceholder(price = singleCost * 64)) pe.item.setLore(lore.mixPlaceholder(price = singleCost * 64))
} }
PurchaseItems.cancel -> {
pe.p.openInventory(pe.shop.inventory)
purchases -= pe
}
PurchaseItems.sellAll -> {
val howMany = p containing block.raw
executeSell(block.raw, howMany, singleCost, p, op)
}
} }
pe.inv.setItem(22, pe.item) pe.inv.setItem(22, pe.item)
if (e.currentItem == PurchaseItems.sellConfirm && pe.type == PurchaseType.SELL) {
val howMany = pe.item.amount
executeSell(block.raw, howMany, singleCost, p, op)
}
if (e.currentItem == PurchaseItems.buyConfirm && pe.type == PurchaseType.BUY) {
val item = block.raw.clone()
val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName()
val balance = econ.getBalance(op)
val amount = pe.item.amount
val cost = singleCost * amount
if (cost > balance) {
p.msg(GlobalConfig.messages.buyNotEnough)
return
}
if (p.inventory.firstEmpty() == -1) {
p.msg(GlobalConfig.messages.buyInvFull)
return
}
val r = econ.withdrawPlayer(op, cost)
if (r.transactionSuccess()) {
p.inventory.addItem(item.apply { this.amount = amount })
p.msg(
GlobalConfig.messages.buySuccess
.replace("%AMOUNT%", amount.toString())
.replace("%NAME%", name)
.replace("%COST%", cost.withCurrency())
)
return
}
p.msg(GlobalConfig.messages.purchaseError.replace("%MSG%", r.errorMessage))
}
}
fun executeSell(item: ItemStack, howMany: Int, singleCost: Double, p: Player, op: OfflinePlayer) {
val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName()
if (howMany <= 0) {
p.msg(GlobalConfig.messages.sellNotEnough.replace("%NAME%", name))
return
}
if (singleCost < 0) {
p.msg(GlobalConfig.messages.sellUnavailable)
return
}
val total = singleCost * howMany
val r = econ.depositPlayer(op, total)
if (r.transactionSuccess()) {
p.inventory.removeItem(item.clone().apply { amount = howMany })
p.msg(
GlobalConfig.messages.sellSuccess
.replace("%AMOUNT%", howMany.toString())
.replace("%NAME%", name)
.replace("%COST%", total.withCurrency())
)
return
}
p.msg(GlobalConfig.messages.purchaseError.replace("%MSG%", r.errorMessage))
} }
val base = Array<ItemStack?>(54) { null }.apply { val base = Array<ItemStack?>(54) { null }.apply {
@ -150,19 +241,24 @@ val base = Array<ItemStack?>(54) { null }.apply {
set(26, PurchaseItems.set64) set(26, PurchaseItems.set64)
} }
fun Material.prettyName() = name.split("_").joinToString(" ") { it.toLowerCase().capitalize() }
fun buy(e: InventoryClickEvent, p: Player, shop: ShopConfig, block: ShopConfig.Item) { fun buy(e: InventoryClickEvent, p: Player, shop: ShopConfig, block: ShopConfig.Item) {
val item = e.currentItem.clone() val item = e.currentItem.clone()
if (block.buy == -1.0) return if (block.buy < 0) {
p.msg(GlobalConfig.messages.buyUnavailable)
return
}
val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName() val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName()
val inv = Bukkit.createInventory(null, 54, "&2Buying $name".c).apply { val inv = Bukkit.createInventory(
null,
54,
GlobalConfig.messages.buyShopTitle.replace("%NAME%", name).c
).apply {
contents = base.clone() contents = base.clone()
val buyLore = GlobalConfig.messages.buyLore.mixPlaceholder(price = block.buy) val buyLore = GlobalConfig.messages.buyLore.mixPlaceholder(price = block.buy)
setItem(22, e.currentItem.setLore(buyLore)) setItem(22, item.setLore(buyLore))
setItem(39, PurchaseItems.buyConfirm) setItem(39, PurchaseItems.buyConfirm)
setItem(41, PurchaseItems.cancel) setItem(41, PurchaseItems.cancel)
} }
@ -171,3 +267,33 @@ fun buy(e: InventoryClickEvent, p: Player, shop: ShopConfig, block: ShopConfig.I
purchases.add(PurchaseEvent(inv, item, shop, p, block, PurchaseType.BUY)) purchases.add(PurchaseEvent(inv, item, shop, p, block, PurchaseType.BUY))
} }
fun sell(e: InventoryClickEvent, p: Player, shop: ShopConfig, block: ShopConfig.Item) {
val item = e.currentItem.clone()
if (block.sell == -1.0) {
p.msg(GlobalConfig.messages.sellUnavailable)
return
}
val name = if (item.itemMeta.hasDisplayName()) item.itemMeta.displayName else item.type.prettyName()
val inv = Bukkit.createInventory(
null,
54,
GlobalConfig.messages.sellShopTitle.replace("%NAME%", name).c
).apply {
contents = base.clone()
val sellLore = GlobalConfig.messages.sellLore.mixPlaceholder(price = block.sell)
val possibleSellAll = p containing block.raw
val cost = block.sell / item.amount * possibleSellAll
val sellAllLore = GlobalConfig.messages.sellAllLore.mixPlaceholder(possibleSellAll, cost)
setItem(22, item.setLore(sellLore))
setItem(39, PurchaseItems.sellConfirm)
setItem(40, PurchaseItems.sellAll.setLore(sellAllLore))
setItem(41, PurchaseItems.cancel)
}
p.openInventory(inv)
purchases.add(PurchaseEvent(inv, item, shop, p, block, PurchaseType.SELL))
}

@ -60,6 +60,7 @@ class MainConfig(file: File) : ConfigFile(file) {
} }
fun addShops() { fun addShops() {
GlobalConfig.shops.clear()
GlobalConfig.shops.addAll(shops!!.keys.map { GlobalConfig.shops.addAll(shops!!.keys.map {
ShopConfig( ShopConfig(
it, it,
@ -71,7 +72,7 @@ class MainConfig(file: File) : ConfigFile(file) {
class ShopConfig(val name: String, file: File) : ConfigFile(file) { class ShopConfig(val name: String, file: File) : ConfigFile(file) {
open class Block(val item: ItemStack) open class Block(val item: ItemStack)
class Item(item: ItemStack, val buy: Double, val sell: Double) : Block(item) class Item(item: ItemStack, val raw: ItemStack, val buy: Double, val sell: Double) : Block(item)
class Command(item: ItemStack, val asPlayer: Boolean, val commands: List<String>) : Block(item) class Command(item: ItemStack, val asPlayer: Boolean, val commands: List<String>) : Block(item)
init { init {
@ -106,17 +107,18 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) {
val type = it.getString("type") val type = it.getString("type")
val buy = it.getDouble("buyPrice", -1.0) val buy = it.getDouble("buyPrice", -1.0)
val sell = it.getDouble("sellPrice", -.01) val sell = it.getDouble("sellPrice", -1.0)
val item = it.getConfigurationSection("item").asItem(buy, sell) val item = it.getConfigurationSection("item").asItem(buy, sell)
when (type) { when (type) {
"command" -> Command( "command" -> Command(
item, it.getConfigurationSection("item").asItem(),
it.getBoolean("asplayer"), it.getBoolean("asplayer"),
it.getStringList("commands") it.getStringList("commands")
) )
"item" -> Item( "item" -> Item(
item, item.addLore(GlobalConfig.messages.itemLore),
it.getConfigurationSection("item").asItem(),
buy, buy,
sell sell
) )
@ -138,40 +140,3 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) {
} }
} }
} }
fun makeInventory(size: Int, title: String, menuRow: Int = -1, items: List<ShopConfig.Block>, page: Int): Inventory {
return Bukkit.createInventory(null, size, title.c).apply {
if (menuRow > 0) {
val buttons = GlobalConfig.main.buttons
for (i in menuRow until menuRow + 9) {
setItem(i, buttons.pane)
}
setItem(menuRow, buttons.previous)
setItem(menuRow + 4, buttons.menu)
setItem(menuRow + 8, buttons.forward)
}
val dropping = (page - 1) * (size - max(0, menuRow))
items.drop(dropping).forEach {
if (firstEmpty() != -1) setItem(firstEmpty(), it.item)
else return@apply
}
}
}
fun ConfigurationSection.asItem(buy: Double = -1.0, sell: Double = -1.0): ItemStack {
val material = Material.valueOf(getString("material").toUpperCase())
val item = ItemStack(material, getInt("quantity", 1), getInt("damage", 0).toShort())
item.itemMeta = item.itemMeta.apply {
displayName = getString("name", "").c
val configLore = getStringList("lore").map { it.c }.toMutableList()
val messages = GlobalConfig.messages
if (buy > 0) configLore += messages.buyLore.mixPlaceholder(price = buy)
if (sell > 0) configLore += messages.sellLore.mixPlaceholder(price = sell)
lore = configLore
}
return item
}

@ -7,7 +7,7 @@ class Messages(file: File, pl: ShopReborn) : ConfigFile(file) {
val currency by string("currency", "$") val currency by string("currency", "$")
val buyLore by string("buyLore") val buyLore by string("buyLore")
val sellLore by string("stringLore") val sellLore by string("sellLore")
val itemLore by string("itemLore") val itemLore by string("itemLore")
val set1 by string("set1") val set1 by string("set1")
@ -19,13 +19,26 @@ class Messages(file: File, pl: ShopReborn) : ConfigFile(file) {
val set64 by string("set64") val set64 by string("set64")
val sellConfirm by string("sell.confirm") val sellConfirm by string("sell.confirm")
val sellAll by string("sell.sellAll") val sellAll by string("sell.all")
val sellAllLore by string("sell.sellAllLore") val sellAllLore by string("sell.allLore")
val buyConfirm by string("buy.confirm") val buyConfirm by string("buy.confirm")
val cancel by string("cancel") val cancel by string("cancel")
val buyShopTitle by string("title.buy")
val sellShopTitle by string("title.sell")
val sellNotEnough by string("sell.notEnoughItems")
val sellUnavailable by string("sell.itemNotSellable")
val sellSuccess by string("sell.success")
val buyNotEnough by string("buy.notEnoughBalance")
val buyInvFull by string("buy.invFull")
val buyUnavailable by string("buy.itemNotPurchasable")
val buySuccess by string("buy.success")
val purchaseError by string("purchaseError")
init { init {
if (!file.exists()) { if (!file.exists()) {
val messages = pl.getResource("messages.yml").bufferedReader().readLines().joinToString("\n") val messages = pl.getResource("messages.yml").bufferedReader().readLines().joinToString("\n")
@ -34,11 +47,3 @@ class Messages(file: File, pl: ShopReborn) : ConfigFile(file) {
} }
} }
} }
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

@ -20,8 +20,12 @@ object PurchaseItems {
} }
} }
fun ItemStack.name(name: String) = apply {
itemMeta = itemMeta.apply { displayName = name.c }
}
val msgs = GlobalConfig.messages var msgs = GlobalConfig.messages
val set1 = paneStack(14, msgs.set1) val set1 = paneStack(14, msgs.set1)
val sub10 = paneStack(14, msgs.sub10) val sub10 = paneStack(14, msgs.sub10)
val sub1 = paneStack(14, msgs.sub1) val sub1 = paneStack(14, msgs.sub1)
@ -36,12 +40,20 @@ object PurchaseItems {
val buyConfirm = blockStack(5, msgs.buyConfirm) val buyConfirm = blockStack(5, msgs.buyConfirm)
val cancel = blockStack(14, msgs.cancel) val cancel = blockStack(14, msgs.cancel)
}
fun ItemStack.setLore(s: String): ItemStack {
return apply { fun reload() {
itemMeta = itemMeta.apply {
lore = listOf(s.c) msgs = GlobalConfig.messages
} set1.name(msgs.set1)
sub10.name(msgs.sub10)
sub1.name(msgs.sub1)
add1.name(msgs.add1)
add10.name(msgs.add10)
set64.name(msgs.set64)
sellConfirm.name(msgs.sellConfirm)
sellAll.name(msgs.sellAll)
buyConfirm.name(msgs.buyConfirm)
cancel.name(msgs.cancel)
} }
} }

@ -2,20 +2,27 @@ package pw.hamzantal.shopreborn
import hazae41.minecraft.kutils.bukkit.command import hazae41.minecraft.kutils.bukkit.command
import hazae41.minecraft.kutils.bukkit.listen import hazae41.minecraft.kutils.bukkit.listen
import hazae41.minecraft.kutils.bukkit.msg
import hazae41.minecraft.kutils.get import hazae41.minecraft.kutils.get
import net.milkbowl.vault.economy.Economy
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import kotlin.system.measureTimeMillis
lateinit var econ: Economy
class ShopReborn : JavaPlugin() { class ShopReborn : JavaPlugin() {
override fun onEnable() { override fun onEnable() {
saveDefaultConfig() saveDefaultConfig()
GlobalConfig.dataFolder = dataFolder val rsp =
GlobalConfig.messages = Messages(dataFolder["messages.yml"], this) server.servicesManager.getRegistration(
GlobalConfig.main = Economy::class.java
MainConfig(dataFolder["config.yml"]) ) ?: hazae41.minecraft.kutils.error("Economy not found".c)
GlobalConfig.main.addShops() econ = rsp.provider
initConfig()
listen(callback = ::baseListener) listen(callback = ::baseListener)
listen(callback = ::closeListener) listen(callback = ::closeListener)
@ -27,8 +34,34 @@ class ShopReborn : JavaPlugin() {
return@command return@command
} }
if (args.component1() == "reload") {
if (sender.hasPermission("shop.reload")) sender.msg("&cPlugin reloaded in ${reload()}ms.")
else sender.msg("&cYou can't do this")
}
val shop = GlobalConfig.shops.firstOrNull { it.name == args.component1() } ?: return@command val shop = GlobalConfig.shops.firstOrNull { it.name == args.component1() } ?: return@command
sender.openInventory(shop.inventory) sender.openInventory(shop.inventory)
} }
} }
fun initConfig() {
GlobalConfig.dataFolder = dataFolder
GlobalConfig.messages = Messages(dataFolder["messages.yml"], this)
GlobalConfig.main =
MainConfig(dataFolder["config.yml"])
GlobalConfig.main.addShops()
}
fun reload() = measureTimeMillis {
initConfig()
PurchaseItems.reload()
base.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)
}
}
} }

@ -1,6 +1,81 @@
package pw.hamzantal.shopreborn package pw.hamzantal.shopreborn
import org.bukkit.Bukkit
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Material
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
val String.c val String.c: String
get() = ChatColor.translateAlternateColorCodes('&', this) get() = ChatColor.translateAlternateColorCodes('&', this)
fun Material.prettyName() = name.split("_").joinToString(" ") { it.toLowerCase().capitalize() }
fun ItemStack.setLore(s: String): ItemStack {
return apply {
itemMeta = itemMeta.apply {
lore = listOf(s.c)
}
}
}
fun ItemStack.addLore(s: String): ItemStack {
return apply {
itemMeta = itemMeta.apply {
lore = lore + s.c
}
}
}
fun makeInventory(size: Int, title: String, menuRow: Int = -1, items: List<ShopConfig.Block>, page: Int): Inventory {
return Bukkit.createInventory(null, size, title.c).apply {
if (menuRow > 0) {
val buttons = GlobalConfig.main.buttons
for (i in menuRow until menuRow + 9) {
setItem(i, buttons.pane)
}
setItem(menuRow, buttons.previous)
setItem(menuRow + 4, buttons.menu)
setItem(menuRow + 8, buttons.forward)
}
val dropping = (page - 1) * (size - kotlin.math.max(0, menuRow))
items.drop(dropping).forEach {
if (firstEmpty() != -1) setItem(firstEmpty(), it.item)
else return@apply
}
}
}
fun String.mixPlaceholder(amount: Int = 0, price: Double = 0.0): String =
replace("%AMOUNT%", amount.toString())
.replace(
"%PRICE%",
price.withCurrency()
)
.c
fun Double.withCurrency() = String.format("%s%.2f", GlobalConfig.messages.currency, this)
fun ConfigurationSection.asItem(buy: Double = -1.0, sell: Double = -1.0): ItemStack {
val material = Material.valueOf(getString("material").toUpperCase())
val item = ItemStack(material, getInt("quantity", 1), getInt("damage", 0).toShort())
item.itemMeta = item.itemMeta.apply {
displayName = getString("name", "").c
val configLore = getStringList("lore").map { it.c }.toMutableList()
val messages = GlobalConfig.messages
if (buy > 0) configLore += messages.buyLore.mixPlaceholder(price = buy)
if (sell > 0) configLore += messages.sellLore.mixPlaceholder(price = sell)
lore = configLore
}
return item
}
infix fun Player.containing(item: ItemStack): Int {
return inventory.contents.sumBy { if (it?.isSimilar(item) == true) it.amount else 0 }
}

@ -10,13 +10,38 @@ sub1: "&c&lRemove 1"
add1: "&a&lAdd 1" add1: "&a&lAdd 1"
add10: "&a&lAdd 10" add10: "&a&lAdd 10"
set64: "&a&lSet 64" set64: "&a&lSet to 64"
# Title for inventory
title:
buy: "&2Buying %NAME%"
sell: "&2Selling %NAME%"
# Confirm for selling/buying (after selecting the item)
sell: sell:
confirm: "&a&lConfirm" # Confirm stained glass block within sell menu
sellAll: "&a&lSell all" confirm: "&a&lConfirm Sell"
sellAllLore: "&7Sell all for &a%PRICE%" all: "&a&lSell all"
allLore: "&7Sell all (%AMOUNT%) for &a%PRICE%"
#Sell event player messages
notEnoughItems: "&c&lYou do not have enough %NAME%!"
itemNotSellable: "&cYou are not allowed to sell this item."
success: "&a&lSuccessfully sold %AMOUNT% %NAME% for %COST%"
buy: buy:
confirm: "&a&lConfirm" # Confirm stained glass block within buy menu
confirm: "&a&lConfirm Purchase"
notEnoughBalance: "&c&lYou do not have enough balance!"
itemNotPurchasable: "&cYou are not allowed to purchase this item."
invFull: "&c&lYour inventory is full."
success: "&a&lSuccessfully purchased %AMOUNT% %NAME% for %COST%"
purchaseError: "&cAn error has occurred while completing the transaction: %MSG%"
cancel: "&c&lCancel" cancel: "&c&lCancel"

@ -3,6 +3,7 @@ version: 1.0.0
description: Custom Shop GUI Plugin created for Premiere Setups description: Custom Shop GUI Plugin created for Premiere Setups
author: hhhapz author: hhhapz
main: pw.hamzantal.shopreborn.ShopReborn main: pw.hamzantal.shopreborn.ShopReborn
depend: [Vault]
commands: commands:
shop: shop:
description: "Shop GUI Basic command" description: "Shop GUI Basic command"