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 {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
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"
}

@ -1,7 +1,9 @@
package pw.hamzantal.shopreborn
import hazae41.minecraft.kutils.bukkit.execute
import hazae41.minecraft.kutils.bukkit.msg
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType.*
import org.bukkit.event.inventory.InventoryClickEvent
@ -84,14 +86,26 @@ 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()
block.commands.forEach {
if (it.contains("%PLAYER%"))
Bukkit.getConsoleSender().execute(it.replace("%PLAYER%", p.name))
else p.chat(it)
}
}
when (e.click) {
LEFT -> buy(e, p, shop, block as ShopConfig.Item)
RIGHT -> TODO()
MIDDLE -> TODO()
else -> return
if (block is ShopConfig.Item) {
when (e.click) {
LEFT -> buy(e, p, shop, block)
RIGHT -> sell(e, p, shop, block)
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
}
}
}
@ -99,6 +113,7 @@ fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) {
e.isCancelled = true
val block = pe.block
val origin = block.item
val p = pe.p
val singleCost =
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
else GlobalConfig.messages.sellLore
val op = Bukkit.getOfflinePlayer(p.uniqueId)
if (!econ.hasAccount(op))
econ.createPlayerAccount(op)
when (e.currentItem) {
PurchaseItems.set1 -> {
pe.item.amount = 1
@ -137,8 +156,80 @@ fun clickInventory(e: InventoryClickEvent, pe: PurchaseEvent) {
pe.item.amount = 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)
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 {
@ -150,19 +241,24 @@ val base = Array<ItemStack?>(54) { null }.apply {
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
if (block.buy < 0) {
p.msg(GlobalConfig.messages.buyUnavailable)
return
}
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()
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(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))
}
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() {
GlobalConfig.shops.clear()
GlobalConfig.shops.addAll(shops!!.keys.map {
ShopConfig(
it,
@ -71,7 +72,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: 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)
init {
@ -106,17 +107,18 @@ class ShopConfig(val name: String, file: File) : ConfigFile(file) {
val type = it.getString("type")
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)
when (type) {
"command" -> Command(
item,
it.getConfigurationSection("item").asItem(),
it.getBoolean("asplayer"),
it.getStringList("commands")
)
"item" -> Item(
item,
item.addLore(GlobalConfig.messages.itemLore),
it.getConfigurationSection("item").asItem(),
buy,
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 buyLore by string("buyLore")
val sellLore by string("stringLore")
val sellLore by string("sellLore")
val itemLore by string("itemLore")
val set1 by string("set1")
@ -19,13 +19,26 @@ class Messages(file: File, pl: ShopReborn) : ConfigFile(file) {
val set64 by string("set64")
val sellConfirm by string("sell.confirm")
val sellAll by string("sell.sellAll")
val sellAllLore by string("sell.sellAllLore")
val sellAll by string("sell.all")
val sellAllLore by string("sell.allLore")
val buyConfirm by string("buy.confirm")
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 {
if (!file.exists()) {
val messages = pl.getResource("messages.yml").bufferedReader().readLines().joinToString("\n")
@ -33,12 +46,4 @@ class Messages(file: File, pl: ShopReborn) : ConfigFile(file) {
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
}

@ -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 sub10 = paneStack(14, msgs.sub10)
val sub1 = paneStack(14, msgs.sub1)
@ -36,12 +40,20 @@ object PurchaseItems {
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)
}
fun reload() {
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.listen
import hazae41.minecraft.kutils.bukkit.msg
import hazae41.minecraft.kutils.get
import net.milkbowl.vault.economy.Economy
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
import kotlin.system.measureTimeMillis
lateinit var econ: Economy
class ShopReborn : JavaPlugin() {
override fun onEnable() {
saveDefaultConfig()
GlobalConfig.dataFolder = dataFolder
GlobalConfig.messages = Messages(dataFolder["messages.yml"], this)
GlobalConfig.main =
MainConfig(dataFolder["config.yml"])
GlobalConfig.main.addShops()
val rsp =
server.servicesManager.getRegistration(
Economy::class.java
) ?: hazae41.minecraft.kutils.error("Economy not found".c)
econ = rsp.provider
initConfig()
listen(callback = ::baseListener)
listen(callback = ::closeListener)
@ -27,8 +34,34 @@ class ShopReborn : JavaPlugin() {
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
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
import org.bukkit.Bukkit
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
get() = ChatColor.translateAlternateColorCodes('&', this)
val String.c: String
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"
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:
confirm: "&a&lConfirm"
sellAll: "&a&lSell all"
sellAllLore: "&7Sell all for &a%PRICE%"
# Confirm stained glass block within sell menu
confirm: "&a&lConfirm Sell"
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:
confirm: "&a&lConfirm"
cancel: "&c&lCancel"
# 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"

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