Add charge feature

This commit is contained in:
TomTom 2024-03-16 17:27:56 +01:00
parent 61532f1087
commit 41506f281a
13 changed files with 229 additions and 14 deletions

View File

@ -40,6 +40,16 @@ class Config(file: File, stream: InputStream) {
@JvmStatic
fun PRICES_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.prices", "ShopGUIPlus")
@JvmStatic
fun CHARGE_ENABLED() = AxMinionsAPI.INSTANCE.getConfig().get("charge.enabled", false)
@JvmStatic
fun CHARGE_AMOUNT() = AxMinionsAPI.INSTANCE.getConfig().get("charge.amount", 1800)
@JvmStatic
fun MAX_CHARGE() = AxMinionsAPI.INSTANCE.getConfig().get("charge.max-charge", 1440)
@JvmStatic
fun CHARGE_PRICE() = AxMinionsAPI.INSTANCE.getConfig().get("charge.price", 10000.0)
@JvmStatic
fun TIMER_FORMAT() = AxMinionsAPI.INSTANCE.getConfig().get("timer-format", 1)
@JvmStatic
fun GUI_SIZE() = AxMinionsAPI.INSTANCE.getConfig().get<Int>("gui.size")
@JvmStatic
fun PULL_FROM_CHEST() = AxMinionsAPI.INSTANCE.getConfig().get("pull-tools-from-chest", false)

View File

@ -27,6 +27,16 @@ class Messages(file: File, stream: InputStream) {
@JvmStatic
fun CONTAINER_FULL_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.container-full")
@JvmStatic
fun TIME_DAY() = AxMinionsAPI.INSTANCE.getMessages().get<Long>("time.day")
@JvmStatic
fun TIME_HOUR() = AxMinionsAPI.INSTANCE.getMessages().get<Long>("time.hour")
@JvmStatic
fun TIME_MINUTE() = AxMinionsAPI.INSTANCE.getMessages().get<Long>("time.minute")
@JvmStatic
fun TIME_SECOND() = AxMinionsAPI.INSTANCE.getMessages().get<Long>("time.second")
@JvmStatic
fun NO_CHARGE_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-charge")
@JvmStatic
fun RELOAD_SUCCESS() = AxMinionsAPI.INSTANCE.getMessages().get<String>("reload")
@JvmStatic
fun PLACE_SUCCESS() = AxMinionsAPI.INSTANCE.getMessages().get<String>("place.success")
@ -46,6 +56,10 @@ class Messages(file: File, stream: InputStream) {
fun ROTATION_NAME(direction: Direction) = AxMinionsAPI.INSTANCE.getMessages().get("directions.${direction.name.lowercase(
Locale.ENGLISH)}", direction.name)
@JvmStatic
fun CHARGE() = AxMinionsAPI.INSTANCE.getMessages().get<String>("charge.charge")
@JvmStatic
fun CHARGE_FAIL() = AxMinionsAPI.INSTANCE.getMessages().get<String>("charge.not-enough-money")
@JvmStatic
fun WRONG_TOOL() = AxMinionsAPI.INSTANCE.getMessages().get<String>("tools.wrong-tool")
@JvmStatic
fun ERROR_INVENTORY_FULL() = AxMinionsAPI.INSTANCE.getMessages().get<String>("errors.inventory-full")

View File

@ -113,4 +113,8 @@ interface Minion : InventoryHolder {
fun isOwnerOnline(): Boolean
fun setOwnerOnline(online: Boolean)
fun getCharge(): Long
fun setCharge(charge: Long)
}

View File

@ -73,11 +73,11 @@ abstract class MinionType(private val name: String, private val defaults: InputS
}
fun getDouble(key: String, level: Int): Double {
return get(key, level, -1.0, Double::class.java)!!
return get(key, level, 0.0, Double::class.java)!!
}
fun getLong(key: String, level: Int): Long {
return get(key, level, -1, Long::class.java)!!
return get(key, level, 0, Long::class.java)!!
}
fun getSection(key: String, level: Int): Section? {

View File

@ -0,0 +1,55 @@
package com.artillexstudios.axminions.api.utils
import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages
import java.time.Duration
object TimeUtils {
fun format(time: Long): String {
if (time < 0) return "---"
val remainingTime: Duration = Duration.ofMillis(time)
val total: Long = remainingTime.seconds
val days = total / 86400
val hours = (total % 86400) / 3600
val minutes = (total % 3600) / 60
val seconds = total % 60
when (Config.TIMER_FORMAT()) {
1 -> {
if (days > 0) return String.format("%02d:%02d:%02d:%02d", days, hours, minutes, seconds)
if (hours > 0) return String.format("%02d:%02d:%02d", hours, minutes, seconds)
return String.format("%02d:%02d", minutes, seconds)
}
2 -> {
if (days > 0) return days.toString() + Messages.TIME_DAY()
if (hours > 0) return hours.toString() + Messages.TIME_HOUR()
if (minutes > 0) return minutes.toString() + Messages.TIME_MINUTE()
return seconds.toString() + Messages.TIME_SECOND()
}
else -> {
if (days > 0) return java.lang.String.format(
((("%02d" + Messages.TIME_DAY()) + " %02d" + Messages.TIME_HOUR()) + " %02d" + Messages.TIME_MINUTE()) + " %02d" + Messages.TIME_SECOND(),
days,
hours,
minutes,
seconds
)
if (hours > 0) return java.lang.String.format(
(("%02d" + Messages.TIME_HOUR()) + " %02d" + Messages.TIME_MINUTE()) + " %02d" + Messages.TIME_SECOND(),
hours,
minutes,
seconds
)
return java.lang.String.format(
("%02d" + Messages.TIME_MINUTE()) + " %02d" + Messages.TIME_SECOND(),
minutes,
seconds
)
}
}
}
}

View File

@ -2,6 +2,7 @@ package com.artillexstudios.axminions.api.warnings
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.warnings.impl.WarningContainerFull
import com.artillexstudios.axminions.api.warnings.impl.WarningNoCharge
import com.artillexstudios.axminions.api.warnings.impl.WarningNoContainer
import com.artillexstudios.axminions.api.warnings.impl.WarningNoTool
import com.artillexstudios.axminions.api.warnings.impl.WarningNoWaterNearby
@ -21,6 +22,9 @@ object Warnings {
@JvmField
val NO_WATER_NEARBY = register(WarningNoWaterNearby())
@JvmField
val NO_CHARGE = register(WarningNoCharge())
@JvmStatic
fun register(warning: Warning): Warning {
WARNINGS[warning.getName()] = warning

View File

@ -0,0 +1,13 @@
package com.artillexstudios.axminions.api.warnings.impl
import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.warnings.Warning
import net.kyori.adventure.text.Component
class WarningNoCharge : Warning("no_charge") {
override fun getContent(): Component {
return StringUtils.format(Messages.NO_CHARGE_WARNING())
}
}

View File

@ -67,6 +67,12 @@ class H2DataHandler : DataHandler {
it.executeUpdate()
}
}
dataSource.connection.use { connection ->
connection.prepareStatement("ALTER TABLE `axminions_minions` ADD COLUMN IF NOT EXISTS `charge` BIGINT DEFAULT(0);").use {
it.executeUpdate()
}
}
}
override fun insertType(minionType: MinionType) {
@ -105,6 +111,7 @@ class H2DataHandler : DataHandler {
val storage = resultSet.getDouble("storage")
val actions = resultSet.getLong("actions")
val tool = resultSet.getString("tool")
val charge = resultSet.getLong("charge")
val location = getLocation(locationId)
var chestLocation: Location? = null
@ -129,7 +136,8 @@ class H2DataHandler : DataHandler {
actions,
storage,
locationId,
chestLocationId
chestLocationId,
charge
)
}
}
@ -255,7 +263,7 @@ class H2DataHandler : DataHandler {
}
dataSource.connection.use { connection ->
connection.prepareStatement("MERGE INTO `axminions_minions`(`location_id`, `chest_location_id`, `owner_id`, `type_id`, `direction`, `level`, `storage`, `actions`, `tool`) KEY(`location_id`) VALUES(?,?,?,?,?,?,?,?,?)")
connection.prepareStatement("MERGE INTO `axminions_minions`(`location_id`, `chest_location_id`, `owner_id`, `type_id`, `direction`, `level`, `storage`, `actions`, `tool`, `charge`) KEY(`location_id`) VALUES(?,?,?,?,?,?,?,?,?,?)")
.use { statement ->
statement.setInt(1, locationId)
if (linkedChestId == null) {
@ -274,6 +282,7 @@ class H2DataHandler : DataHandler {
} else {
statement.setString(9, Serializers.ITEM_STACK.serialize(minion.getTool()))
}
statement.setLong(10, minion.getCharge())
statement.executeUpdate()
}
}

View File

@ -11,6 +11,7 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.api.utils.CoolDown
import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor
import java.util.Locale
import net.md_5.bungee.api.ChatMessageType
import net.md_5.bungee.api.chat.TextComponent
import org.bukkit.Material
@ -146,7 +147,7 @@ class MinionInventoryListener : Listener {
}
if (Config.UPGRADE_SOUND().isNotBlank()) {
player.playSound(player, Config.UPGRADE_SOUND(), 1.0f, 1.0f)
player.playSound(player, Sound.valueOf(Config.UPGRADE_SOUND().uppercase(Locale.ENGLISH)), 1.0f, 1.0f)
}
minion.setLevel(minion.getLevel() + 1)
@ -171,6 +172,38 @@ class MinionInventoryListener : Listener {
minion.setStorage(0.0)
}
}
"charge" -> {
if ((AxMinionsPlugin.integrations.getEconomyIntegration()?.getBalance(player)
?: return) < Config.CHARGE_PRICE()
) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.CHARGE_FAIL()))
return
}
AxMinionsPlugin.integrations.getEconomyIntegration()?.let {
minion.getOwner()?.let { player ->
it.takeBalance(player, Config.CHARGE_PRICE())
}
}
val chargeSeconds = (minion.getCharge() - System.currentTimeMillis()) / 1000
if (chargeSeconds + Config.CHARGE_AMOUNT() > Config.MAX_CHARGE() * 60L) {
minion.setCharge(System.currentTimeMillis() + Config.MAX_CHARGE() * 60L * 1000L)
return
}
if (minion.getCharge() < System.currentTimeMillis()) {
minion.setCharge(System.currentTimeMillis() + Config.CHARGE_AMOUNT() * 1000)
} else {
minion.setCharge(minion.getCharge() + Config.CHARGE_AMOUNT() * 1000)
}
if (Messages.CHARGE().isNotBlank()) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.CHARGE()))
}
}
}
minion.updateInventories()

View File

@ -21,6 +21,7 @@ import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.TimeUtils
import com.artillexstudios.axminions.api.utils.fastFor
import com.artillexstudios.axminions.api.warnings.Warning
import com.artillexstudios.axminions.api.warnings.Warnings
@ -40,6 +41,7 @@ import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.persistence.PersistentDataType
import org.bukkit.util.EulerAngle
@ -55,7 +57,8 @@ class Minion(
private var actions: Long,
private var storage: Double,
private val locationID: Int,
private var chestLocationId: Int
private var chestLocationId: Int,
private var charge: Long
) : Minion {
companion object {
private val numberFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT)
@ -72,6 +75,7 @@ class Minion(
private val extraData = hashMapOf<String, String>()
private var linkedInventory: Inventory? = null
internal val openInventories = mutableListOf<Inventory>()
private var toolMeta: ItemMeta? = null
@Volatile
private var ticking = false
@ -188,6 +192,13 @@ class Minion(
debugHologram?.setLine(0, StringUtils.format("Ticking: $ticking"))
}
if (Config.CHARGE_ENABLED() && getCharge() < System.currentTimeMillis()) {
Warnings.NO_CHARGE.display(this)
return
}
Warnings.remove(this, Warnings.NO_CHARGE)
Scheduler.get().executeAt(location) {
type.tick(this)
}
@ -207,7 +218,7 @@ class Minion(
private fun updateInventory(inventory: Inventory) {
AxMinionsAPI.INSTANCE.getConfig().getConfig().getSection("gui.items").getRoutesAsStrings(false).forEach {
if (it.equals("filler")) return@forEach
val item: ItemStack
val item: ItemStack?
if (it.equals("upgrade", true) || it.equals("statistics", true)) {
val level = Placeholder.parsed("level", level.toString())
val nextLevel = Placeholder.parsed(
@ -287,6 +298,15 @@ class Minion(
).get()
} else if (it.equals("item")) {
item = tool?.clone() ?: ItemStack(Material.AIR)
} else if (it.equals("charge")) {
if (Config.CHARGE_ENABLED()) {
val charge = Placeholder.parsed("charge", TimeUtils.format(charge - System.currentTimeMillis()))
item = ItemBuilder(type.getConfig().getSection("gui.$it"), charge).storePersistentData(
Keys.GUI, PersistentDataType.STRING, it
).get()
} else {
item = null
}
} else {
val rotation = Placeholder.unparsed("direction", Messages.ROTATION_NAME(direction))
val linked = Placeholder.unparsed(
@ -304,7 +324,9 @@ class Minion(
).get()
}
inventory.setItem(AxMinionsAPI.INSTANCE.getConfig().get("gui.items.$it.slot"), item)
if (item != null) {
inventory.setItem(AxMinionsAPI.INSTANCE.getConfig().get("gui.items.$it.slot"), item)
}
}
}
@ -373,6 +395,12 @@ class Minion(
override fun setTool(tool: ItemStack, save: Boolean) {
this.tool = tool.clone()
toolMeta = if (!tool.type.isAir) {
tool.itemMeta
} else {
null
}
dirty = true
if (this.tool?.type == Material.AIR) {
@ -456,6 +484,8 @@ class Minion(
updateInventories()
}
} else {
linkedInventory = null
}
AxMinionsPlugin.dataQueue.submit {
@ -595,7 +625,7 @@ class Minion(
override fun damageTool(amount: Int) {
if (!Config.USE_DURABILITY()) return
val meta = this.tool?.itemMeta as? Damageable ?: return
val meta = toolMeta as? Damageable ?: return
if (Math.random() > 1f / (meta.getEnchantLevel(Enchantment.DURABILITY) + 1)) return
@ -641,7 +671,7 @@ class Minion(
}
override fun canUseTool(): Boolean {
val meta = this.tool?.itemMeta ?: return false
val meta = toolMeta ?: return false
if (!Config.USE_DURABILITY() && meta is Damageable) {
return true
@ -688,7 +718,7 @@ class Minion(
}
}
return false
return true
}
override fun isOwnerOnline(): Boolean {
@ -699,6 +729,18 @@ class Minion(
ownerOnline = online
}
override fun getCharge(): Long {
return charge
}
override fun setCharge(charge: Long) {
this.charge = charge
AxMinionsPlugin.dataQueue.submit {
AxMinionsPlugin.dataHandler.saveMinion(this)
}
}
override fun getInventory(): Inventory {
return Bukkit.createInventory(this, 9)
}

View File

@ -67,6 +67,17 @@ hooks:
# Supported prices providers: ShopGUIPlus, Essentials, EconomyShopGUI, CMI, custom
prices: "ShopGUIPlus"
charge:
enabled: false
amount: 1800 # in seconds
max-charge: 1440 # in minutes
price: 10000
# 1 - HH:MM:SS, for example 01:25:35
# 2 - short format, for example 20m
# 3 - text format, for example 01h 25m 35s
timer-format: 1
gui:
size: 27
items:
@ -100,10 +111,19 @@ gui:
slot: 15
statistics:
slot: 16
charge:
type: "lava_bucket"
name: "<#FB8E08><b>Charge"
lore:
- ""
- " <gray>- <white>Current charge: <#FFCC00><charge>"
- ""
- "<#FB8E08><b>(!)</b> Click here to charge your minion for <white>10 minutes</white> for <white>$10,000</white>!"
slot: 9
# Only change this, if you are asked to! This will make your performance worse, and will spam messages in your console!
# Requires restart!
debug: false
# Do not change!
config-version: 6
config-version: 7

View File

@ -23,6 +23,13 @@ warnings:
no-tool: "<#FF3333>⚠ <#FFAAAA>The minion needs a tool </#FFAAAA>⚠"
no-water-nearby: "<#FF3333>⚠ <#FFAAAA>The minion needs water nearby </#FFAAAA>⚠"
container-full: "<#FF3333>⚠ <#FFAAAA>The container of this minion is full </#FFAAAA>⚠"
no-charge: "<#FF3333>⚠ <#FFAAAA>The minion ran out of charge! Refuel it!</#FFAAAA>⚠"
time:
day: "d"
hour: "h"
minute: "m"
second: "s"
statistics: |
<white>Ticking minions: <gradient:#00aaff:#00ccff><ticking></gradient>
@ -58,8 +65,12 @@ directions:
south: "South"
west: "West"
charge:
charge: "<green>You have added <white>30 minutes</white> of charge to your minion!"
not-enough-money: "<red>You don't have enough money to charge this minion!"
tools:
wrong-tool: "<red>You can't place this item as the tool if this minion!"
# Do not change!
config-version: 2
config-version: 3

View File

@ -63,7 +63,7 @@ object DamageHandler {
DUMMY_ENTITY?.setItemSlot(EquipmentSlot.MAINHAND, nmsItem)
if (!nmsEntity.isAttackable) return
if (!nmsEntity.isAttackable || entity is Player) return
val f2 = 1.0f
var f1 = if (nmsEntity is LivingEntity) {