Lots of work & fixes

This commit is contained in:
TomTom 2023-10-08 19:44:25 +02:00
parent d1bf889107
commit 24f5e29381
18 changed files with 431 additions and 58 deletions

View File

@ -36,6 +36,8 @@ class Config(file: File, stream: InputStream) {
fun ECONOMY_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.economy", "Vault")
@JvmStatic
fun PRICES_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.prices", "ShopGUIPlus")
@JvmStatic
fun GUI_SIZE() = AxMinionsAPI.INSTANCE.getConfig().get<Int>("gui.size")
}
private val config = Config(
@ -55,6 +57,10 @@ class Config(file: File, stream: InputStream) {
return this.config.get(route)
}
fun getConfig(): Config {
return config
}
fun reload() {
config.reload()
}

View File

@ -6,10 +6,13 @@ import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.settings.dumper.Du
import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.settings.general.GeneralSettings
import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.settings.loader.LoaderSettings
import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.settings.updater.UpdaterSettings
import com.artillexstudios.axapi.utils.RotationType
import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.minions.Direction
import java.io.File
import java.io.InputStream
import java.util.Locale
class Messages(file: File, stream: InputStream) {
companion object {
@ -31,6 +34,19 @@ class Messages(file: File, stream: InputStream) {
fun PLACE_LIMIT_REACHED() = AxMinionsAPI.INSTANCE.getMessages().get<String>("place.limit-reached")
@JvmStatic
fun PLACE_MINION_AT_LOCATION() = AxMinionsAPI.INSTANCE.getMessages().get<String>("place.minion-at-location")
@JvmStatic
fun STATISTICS() = AxMinionsAPI.INSTANCE.getMessages().get<String>("statistics")
@JvmStatic
fun LEVEL_COLOR(level: Int = 1) = AxMinionsAPI.INSTANCE.getMessages().get("levels.$level", "<#33FF33>")
@JvmStatic
fun UPGRADES_MAX_LEVEL_REACHED() = AxMinionsAPI.INSTANCE.getMessages().get<String>("upgrades.limit-reached")
@JvmStatic
fun ROTATION_NAME(direction: Direction) = AxMinionsAPI.INSTANCE.getMessages().get("directions.${direction.name.lowercase(
Locale.ENGLISH)}", direction.name)
@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")
}
private val config = Config(
@ -46,6 +62,10 @@ class Messages(file: File, stream: InputStream) {
return this.config.get(route)
}
fun <T> get(route: String?, default: T): T {
return this.config.get(route, default)
}
fun getFormatted(route: String?): String {
return StringUtils.formatToString(this.config.get(route))
}

View File

@ -4,14 +4,15 @@ import com.artillexstudios.axapi.entity.impl.PacketEntity
import com.artillexstudios.axapi.hologram.Hologram
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.warnings.Warning
import java.util.UUID
import org.bukkit.Location
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryHolder
import org.bukkit.inventory.ItemStack
import java.util.UUID
interface Minion {
interface Minion : InventoryHolder {
fun getType(): MinionType
@ -21,7 +22,7 @@ interface Minion {
fun getLocation(): Location
fun updateInventory(inventory: Inventory)
fun updateInventories()
fun openInventory(player: Player)
@ -73,8 +74,6 @@ interface Minion {
fun getLinkedChest(): Location?
fun serializeExtraData(): String
fun setDirection(direction: Direction)
fun getDirection(): Direction
@ -92,4 +91,6 @@ interface Minion {
fun getLocationId(): Int
fun getChestLocationId(): Int
fun removeOpenInventory(inventory: Inventory)
}

View File

@ -2,6 +2,7 @@ package com.artillexstudios.axminions.api.minions.miniontype
import com.artillexstudios.axapi.config.Config
import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.block.implementation.Section
import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.minions.Minion
@ -28,7 +29,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
return true
}
private fun isChunkLoaded(location: Location): Boolean {
fun isChunkLoaded(location: Location): Boolean {
return location.world?.isChunkLoaded(location.blockX shr 4, location.blockZ shr 4) ?: return false
}
@ -40,7 +41,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
}
fun getItem(level: Int = 1): ItemStack {
val builder = ItemBuilder(config.getSection("item"))
val builder = ItemBuilder(config.getSection("item"), Placeholder.unparsed("level", level.toString()), Placeholder.unparsed("actions", "0"))
builder.storePersistentData(MinionTypes.getMinionKey(), PersistentDataType.STRING, name)
builder.storePersistentData(MinionTypes.getLevelKey(), PersistentDataType.INTEGER, level)
@ -81,5 +82,9 @@ abstract class MinionType(private val name: String, private val defaults: InputS
return n
}
fun hasReachedMaxLevel(minion: Minion): Boolean {
return !config.backingDocument.isSection("upgrades.${minion.getLevel() + 1}")
}
abstract fun run(minion: Minion)
}

View File

@ -9,6 +9,7 @@ object MinionTypes {
private val TYPES = hashMapOf<String, MinionType>()
private val MINION_KEY = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "minion_type")
private val LEVEL_KEY = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "level")
private val GUI_KEY = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "gui_item")
@JvmStatic
fun register(type: MinionType): MinionType {
@ -41,6 +42,11 @@ object MinionTypes {
return LEVEL_KEY
}
@JvmStatic
fun getGuiKey(): NamespacedKey {
return GUI_KEY
}
@JvmStatic
fun getMinionTypes(): Map<String, MinionType> {
return Collections.unmodifiableMap(TYPES)

View File

@ -15,7 +15,7 @@ abstract class Warning(private val name: String) {
fun display(minion: Minion) {
if (minion.getWarning() == null) {
val hologram = HologramFactory.get()
.spawnHologram(minion.getLocation().add(0.0, 0.15, 0.0), minion.getLocation().toString())
.spawnHologram(minion.getLocation().clone().add(0.0, 1.5, 0.0), minion.getLocation().toString())
hologram.addLine(getContent())
minion.setWarning(this)
}

View File

@ -14,6 +14,8 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.commands.AxMinionsCommand
import com.artillexstudios.axminions.data.H2DataHandler
import com.artillexstudios.axminions.listeners.LinkingListener
import com.artillexstudios.axminions.listeners.MinionInventoryListener
import com.artillexstudios.axminions.listeners.MinionPlaceListener
import com.artillexstudios.axminions.minions.MinionTicker
import com.artillexstudios.axminions.minions.miniontype.CollectorMinionType
@ -76,6 +78,8 @@ class AxMinionsPlugin : AxPlugin() {
handler.registerBrigadier()
Bukkit.getPluginManager().registerEvents(MinionPlaceListener(), this)
Bukkit.getPluginManager().registerEvents(LinkingListener(), this)
Bukkit.getPluginManager().registerEvents(MinionInventoryListener(), this)
MinionTicker.startTicking()
}

View File

@ -1,13 +1,20 @@
package com.artillexstudios.axminions.commands
import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import com.artillexstudios.axapi.libs.lamp.annotation.*
import com.artillexstudios.axapi.libs.lamp.annotation.AutoComplete
import com.artillexstudios.axapi.libs.lamp.annotation.Command
import com.artillexstudios.axapi.libs.lamp.annotation.Default
import com.artillexstudios.axapi.libs.lamp.annotation.Description
import com.artillexstudios.axapi.libs.lamp.annotation.Range
import com.artillexstudios.axapi.libs.lamp.annotation.Subcommand
import com.artillexstudios.axapi.libs.lamp.bukkit.annotation.CommandPermission
import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
@ -18,8 +25,17 @@ class AxMinionsCommand {
@CommandPermission("axminions.command.give")
@Description("Give a minion to a player")
@AutoComplete("* @minionTypes * *")
fun give(sender: CommandSender, @Default("me") receiver: Player, @Default("collector") minionType: MinionType, @Default("1") level: Int, @Default("1") @Range(min = 1.0, max = 64.0) amount: Int) {
receiver.inventory.addItem(minionType.getItem())
fun give(
sender: CommandSender,
receiver: Player,
minionType: MinionType,
@Default("1") level: Int,
@Default("1") @Range(min = 1.0, max = 64.0) amount: Int
) {
val item = minionType.getItem(level)
item.amount = amount
receiver.inventory.addItem(item)
}
@Subcommand("reload")
@ -34,13 +50,42 @@ class AxMinionsCommand {
it.value.getConfig().reload()
}
sender.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.RELOAD_SUCCESS(), Placeholder.unparsed("time", (System.currentTimeMillis() - start).toString())))
sender.sendMessage(
StringUtils.formatToString(
Messages.PREFIX() + Messages.RELOAD_SUCCESS(),
Placeholder.unparsed("time", (System.currentTimeMillis() - start).toString())
)
)
}
@Subcommand("convert")
@CommandPermission("axminions.command.convert")
@Description("Convert from a different plugin")
fun convert(sender: CommandSender, ) {
fun convert(sender: CommandSender) {
}
@Subcommand("stats", "statistics")
@CommandPermission("axminions.command.statistics")
@Description("Get statistics of plugin")
fun stats(sender: CommandSender) {
val minions = AxMinionsAPI.INSTANCE.getMinions()
var loaded = 0
val total = minions.size
minions.fastFor {
if (it.getType().isChunkLoaded(it.getLocation())) {
loaded++
}
}
sender.sendMessage(
StringUtils.formatToString(
Messages.STATISTICS(),
Placeholder.unparsed("ticking", loaded.toString()),
Placeholder.unparsed("not-ticking", (total - loaded).toString()),
Placeholder.unparsed("total", total.toString())
)
)
}
}

View File

@ -27,7 +27,7 @@ class H2DataHandler : DataHandler {
connection =
JdbcConnection("jdbc:h2:./${AxMinionsPlugin.INSTANCE.dataFolder}/data", Properties(), null, null, false)
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_types`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_types`(`id` INT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
.use {
it.executeUpdate()
}
@ -37,17 +37,17 @@ class H2DataHandler : DataHandler {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_worlds`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_worlds`(`id` INT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_locations`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `x` INT, `y` INT, `z` INT, `world_id` INT, FOREIGN KEY(world_id) REFERENCES `axminions_worlds`(`id`));")
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_locations`(`id` INT AUTO_INCREMENT PRIMARY KEY, `x` INT, `y` INT, `z` INT, `world_id` INT, FOREIGN KEY(world_id) REFERENCES `axminions_worlds`(`id`));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_minions`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `location_id` INT, `chest_location_id` INT, `owner_id` UUID, `type_id` INT, `direction` TINYINT, `level` SMALLINT, `storage` DOUBLE, `actions` BIGINT, `tool` CLOB, FOREIGN KEY(`location_id`) REFERENCES `axminions_locations`(id), FOREIGN KEY(`chest_location_id`) REFERENCES `axminions_locations`(`id`), FOREIGN KEY(`owner_id`) REFERENCES `axminions_users`(`uuid`), FOREIGN KEY(`type_id`) REFERENCES `axminions_types`(`id`));")
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_minions`(`id` INT AUTO_INCREMENT PRIMARY KEY, `location_id` INT, `chest_location_id` INT, `owner_id` UUID, `type_id` TINYINT, `direction` TINYINT, `level` SMALLINT, `storage` DOUBLE, `actions` BIGINT, `tool` CLOB, FOREIGN KEY(`location_id`) REFERENCES `axminions_locations`(id), FOREIGN KEY(`chest_location_id`) REFERENCES `axminions_locations`(`id`), FOREIGN KEY(`owner_id`) REFERENCES `axminions_users`(`uuid`), FOREIGN KEY(`type_id`) REFERENCES `axminions_types`(`id`));")
.use {
it.executeUpdate()
}
@ -151,7 +151,7 @@ class H2DataHandler : DataHandler {
}
override fun getLocation(locationId: Int): Location? {
connection.prepareStatement("SELECT `name` FROM `axminions_locations` WHERE `id` = ?").use { statement ->
connection.prepareStatement("SELECT * FROM `axminions_locations` WHERE `id` = ?;").use { statement ->
statement.setInt(1, locationId)
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
@ -169,7 +169,7 @@ class H2DataHandler : DataHandler {
}
override fun getWorld(worldId: Int): World? {
connection.prepareStatement("SELECT `name` FROM `axminions_worlds` WHERE `id` = ?").use { statement ->
connection.prepareStatement("SELECT `name` FROM `axminions_worlds` WHERE `id` = ?;").use { statement ->
statement.setInt(1, worldId)
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
@ -184,7 +184,7 @@ class H2DataHandler : DataHandler {
override fun saveMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
val locationId = getLocationID(minion.getLocation())
var linkedChestId: Int? = null
var userId: Int? = null
var userId: UUID? = null
var minionTypeId = 0
connection.prepareStatement("SELECT * FROM `axminions_types` WHERE `name` = ?;").use {
@ -210,34 +210,34 @@ class H2DataHandler : DataHandler {
statement.generatedKeys.use { resultSet ->
if (resultSet.next()) {
if (resultSet.next()) {
userId = resultSet.getInt(1)
}
userId = resultSet.getObject(1) as UUID
}
}
}
if (userId == null) {
println("Userid null")
return
}
connection.prepareStatement("MERGE INTO `axminions_minions`(`location_id`, `chest_location_id`, `owner_id`, `type_id`, `direction`, `level`, `storage`, `actions`, `tool`) KEY(`location_id`) VALUES(?,?,?,?,?,?,?,?,?)")
.use { statement ->
println("aaaa")
statement.setInt(1, locationId)
if (linkedChestId == null) {
statement.setNull(2, Types.INTEGER)
} else {
statement.setInt(2, linkedChestId)
}
statement.setInt(3, userId!!)
statement.setObject(3, userId!!)
statement.setInt(4, minionTypeId)
statement.setByte(5, minion.getDirection().ordinal.toByte())
statement.setInt(6, minion.getLevel())
statement.setDouble(7, minion.getStorage())
statement.setLong(8, minion.getActionAmount())
if (minion.getTool() == null || minion.getTool()?.type == Material.AIR) {
statement.setNull(9, Types.CLOB);
} else {
statement.setString(9, Serializers.ITEM_STACK.serialize(minion.getTool()))
}
statement.executeUpdate()
}
}

View File

@ -0,0 +1,35 @@
package com.artillexstudios.axminions.listeners
import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.minions.Minion
import java.util.UUID
import org.bukkit.Material
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEvent
class LinkingListener : Listener {
companion object {
val linking = hashMapOf<UUID, Minion>()
private val CONTAINERS = listOf(Material.BARREL, Material.CHEST, Material.TRAPPED_CHEST)
}
@EventHandler
fun onPlayerInteractEvent(event: PlayerInteractEvent) {
if (event.player.uniqueId !in linking) return
if (event.clickedBlock == null) return
if (event.clickedBlock!!.type !in CONTAINERS) return
// TODO Check if player can build at location
val minion = linking.remove(event.player.uniqueId) ?: return
event.isCancelled = true
if (minion.getLocation()
.distanceSquared(event.clickedBlock!!.location) > Config.MAX_LINKING_DISTANCE() * Config.MAX_LINKING_DISTANCE()
) {
// TODO Send too far message
return
}
minion.setLinkedChest(event.clickedBlock!!.location)
}
}

View File

@ -0,0 +1,117 @@
package com.artillexstudios.axminions.listeners
import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.minions.Direction
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
class MinionInventoryListener : Listener {
@EventHandler
fun onInventoryDragEvent(event: InventoryDragEvent) {
if (event.inventory.holder !is Minion) return
event.isCancelled = true
}
@EventHandler
fun onInventoryClickEvent(event: InventoryClickEvent) {
val minion = event.inventory.holder as? Minion ?: return
if (event.clickedInventory == null) return
event.isCancelled = true
val player = event.whoClicked as Player
val allowedTools = arrayListOf<Material>()
minion.getType().getConfig().getStringList("tool.material").fastFor {
allowedTools.add(Material.matchMaterial(it) ?: return@fastFor)
}
if (event.clickedInventory == player.inventory && event.currentItem != null) {
if (event.currentItem!!.type !in allowedTools) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.WRONG_TOOL()))
return
}
if (minion.getTool()?.type != Material.AIR) {
val current = event.currentItem!!.clone()
val tool = minion.getTool()?.clone()
minion.setTool(current)
event.currentItem!!.amount = 0
event.clickedInventory!!.addItem(tool)
} else {
minion.setTool(event.currentItem!!)
event.currentItem!!.amount = 0
}
minion.updateInventories()
return
}
if (event.slot == AxMinionsAPI.INSTANCE.getConfig().get("gui.items.item.slot")) {
if (minion.getTool()?.type == Material.AIR) return
if (player.inventory.firstEmpty() == -1) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.ERROR_INVENTORY_FULL()))
return
}
val tool = minion.getTool()?.clone() ?: return
minion.setTool(ItemStack(Material.AIR))
val toolMeta = tool.itemMeta ?: return
toolMeta.persistentDataContainer.remove(MinionTypes.getGuiKey())
tool.setItemMeta(toolMeta)
player.inventory.addItem(tool)
return
}
if (!(event.clickedInventory?.getItem(event.slot)?.hasItemMeta() ?: return)) {
return
}
val meta = event.clickedInventory?.getItem(event.slot)?.itemMeta ?: return
if (!meta.persistentDataContainer.has(MinionTypes.getGuiKey(), PersistentDataType.STRING)) return
val type = meta.persistentDataContainer.get(MinionTypes.getGuiKey(), PersistentDataType.STRING)
when (type) {
"rotate" -> {
when (minion.getDirection()) {
Direction.NORTH -> minion.setDirection(Direction.WEST)
Direction.EAST -> minion.setDirection(Direction.NORTH)
Direction.SOUTH -> minion.setDirection(Direction.EAST)
Direction.WEST -> minion.setDirection(Direction.SOUTH)
}
}
"link" -> {
if (minion.getLinkedChest() != null) {
minion.setLinkedChest(null)
// TODO Unlink message
return
}
LinkingListener.linking[player.uniqueId] = minion
}
}
minion.updateInventories()
}
@EventHandler
fun onInventoryClosEvent(event: InventoryCloseEvent) {
val holder = event.inventory.holder as? Minion ?: return
holder.removeOpenInventory(event.inventory)
}
}

View File

@ -30,7 +30,6 @@ class MinionPlaceListener : Listener {
event.isCancelled = true
val location = event.clickedBlock!!.getRelative(event.blockFace).location
location.add(0.5, 0.0, 0.5)
val maxMinions = AxMinionsAPI.INSTANCE.getMinionLimit(event.player)
@ -50,7 +49,7 @@ class MinionPlaceListener : Listener {
val minion = Minion(location, event.player.uniqueId, event.player, minionType, 1, ItemStack(Material.AIR), null, Direction.NORTH, 0, 0.0, AxMinionsPlugin.dataHandler.getLocationID(location), 0)
AxMinionsPlugin.dataHandler.saveMinion(minion)
event.player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.PLACE_SUCCESS()))
event.player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.PLACE_SUCCESS(), Placeholder.unparsed("type", minionType.getName()), Placeholder.unparsed("placed", (placed + 1).toString()), Placeholder.unparsed("max", (maxMinions).toString())))
}
}
}

View File

@ -4,16 +4,25 @@ import com.artillexstudios.axapi.entity.PacketEntityFactory
import com.artillexstudios.axapi.entity.impl.PacketArmorStand
import com.artillexstudios.axapi.entity.impl.PacketEntity
import com.artillexstudios.axapi.hologram.Hologram
import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import com.artillexstudios.axapi.scheduler.Scheduler
import com.artillexstudios.axapi.serializers.Serializers
import com.artillexstudios.axapi.utils.EquipmentSlot
import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axapi.utils.RotationType
import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.minions.Direction
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.warnings.Warning
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.listeners.LinkingListener
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.OfflinePlayer
import org.bukkit.block.Container
import org.bukkit.enchantments.Enchantment
@ -24,9 +33,12 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.util.EulerAngle
import java.util.UUID
import kotlin.math.roundToInt
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.persistence.PersistentDataType
class Minion(
private val location: Location,
private var location: Location,
private val ownerUUID: UUID,
private val owner: OfflinePlayer,
private val type: MinionType,
@ -48,6 +60,7 @@ class Minion(
private var hologram: Hologram? = null
private val extraData = hashMapOf<String, String>()
private var linkedInventory: Inventory? = null
private val openInventories = mutableListOf<Inventory>()
init {
spawn()
@ -60,15 +73,21 @@ class Minion(
}
override fun spawn() {
location.x += 0.5
location.z += 0.5
entity = PacketEntityFactory.get().spawnEntity(location, EntityType.ARMOR_STAND) as PacketArmorStand
entity.setHasBasePlate(false)
entity.setSmall(true)
entity.setHasArms(true)
setDirection(direction)
updateArmour()
entity.onClick { event ->
if (event.isAttack) {
println("LEFT CLICKED!")
} else {
println("RIGHT CLICKED!")
Scheduler.get().run {
openInventory(event.player)
}
}
}
}
@ -89,12 +108,63 @@ class Minion(
return this.location
}
override fun updateInventory(inventory: Inventory) {
override fun updateInventories() {
openInventories.fastFor {
updateInventory(it)
}
}
private fun updateInventory(inventory: Inventory) {
AxMinionsAPI.INSTANCE.getConfig().getConfig().getSection("gui.items").getRoutesAsStrings(false).forEach {
if (it.equals("filler")) return@forEach
val item: ItemStack
if (it.equals("upgrade", true) || it.equals("statistics", true)) {
val level = Placeholder.unparsed("level", level.toString())
val nextLevel = Placeholder.unparsed("next_level", when (type.hasReachedMaxLevel(this)) {
true -> Messages.UPGRADES_MAX_LEVEL_REACHED()
else -> (this.level + 1).toString()
})
val range = Placeholder.unparsed("range", type.getString("range", this.level))
val nextRange = Placeholder.unparsed("next_range", type.getString("range", this.level + 1))
val extra = Placeholder.unparsed("extra", type.getString("extra", this.level))
val nextExtra = Placeholder.unparsed("next_extra", type.getString("extra", this.level + 1))
val speed = Placeholder.unparsed("speed", type.getString("speed", this.level))
val nextSpeed = Placeholder.unparsed("next_speed", type.getString("speed", this.level + 1))
val price = Placeholder.unparsed("price", type.getString("requirements.money", this.level + 1))
val requiredActions = Placeholder.unparsed("required_actions", type.getString("requirements.actions", this.level + 1))
val stored = Placeholder.unparsed("storage", storage.toString())
val actions = Placeholder.unparsed("actions", actions.toString())
item = ItemBuilder(type.getConfig().getSection("gui.$it"), level, nextLevel, range, nextRange, extra, nextExtra, speed, nextSpeed, price, requiredActions, stored, actions).storePersistentData(
MinionTypes.getGuiKey(), PersistentDataType.STRING, it).get()
} else if (it.equals("item")) {
item = ItemBuilder(tool?.clone() ?: ItemStack(Material.AIR)).get()
} else {
val rotation = Placeholder.unparsed("direction", Messages.ROTATION_NAME(direction))
val linked = Placeholder.unparsed("linked", when (linkedChest) {
null -> "---"
else -> Serializers.LOCATION.serialize(linkedChest)
})
item = ItemBuilder(AxMinionsAPI.INSTANCE.getConfig().getConfig().getSection("gui.items.$it"), rotation, linked).storePersistentData(
MinionTypes.getGuiKey(), PersistentDataType.STRING, it).get()
}
inventory.setItem(AxMinionsAPI.INSTANCE.getConfig().get("gui.items.$it.slot"), item)
}
}
override fun openInventory(player: Player) {
LinkingListener.linking.remove(player.uniqueId)
val inventory = Bukkit.createInventory(this, Config.GUI_SIZE(), StringUtils.formatToString(type.getConfig().get("name"), Placeholder.unparsed("level_color", Messages.LEVEL_COLOR(level)), Placeholder.unparsed("level", level.toString()), Placeholder.unparsed("owner", owner.name ?: "???")))
val filler = ItemBuilder(AxMinionsAPI.INSTANCE.getConfig().getConfig().getSection("gui.items.filler")).get()
for (i in 0..< Config.GUI_SIZE()) {
inventory.setItem(i, filler)
}
updateInventory(inventory)
player.openInventory(inventory)
openInventories.add(inventory)
}
override fun getAsItem(): ItemStack {
@ -139,6 +209,12 @@ class Minion(
override fun setTool(tool: ItemStack) {
this.tool = tool
if (tool.type == Material.AIR) {
entity.setItem(EquipmentSlot.MAIN_HAND, null)
} else {
entity.setItem(EquipmentSlot.MAIN_HAND, tool)
}
}
override fun getTool(): ItemStack? {
@ -191,24 +267,13 @@ class Minion(
override fun setLinkedChest(location: Location?) {
this.linkedChest = location?.clone()
linkedInventory = (linkedChest?.block?.blockData as? Container)?.inventory
updateInventories()
}
override fun getLinkedChest(): Location? {
return this.linkedChest
}
override fun serializeExtraData(): String {
val builder = StringBuilder()
for (data in extraData) {
builder.append(data.key)
builder.append("=")
builder.append(data.value)
builder.append("|")
}
return builder.toString()
}
override fun setDirection(direction: Direction) {
this.direction = direction
location.yaw = direction.yaw
@ -245,26 +310,22 @@ class Minion(
override fun updateArmour() {
for (entry in EquipmentSlot.entries) {
entity.setItem(entry, ItemStack(Material.AIR))
entity.setItem(entry, null)
}
type.getSection("items.helmet", level)?.let {
println("helmet!")
entity.setItem(EquipmentSlot.HELMET, ItemBuilder(it).get())
}
type.getSection("items.chestplate", level)?.let {
println("CP!")
entity.setItem(EquipmentSlot.CHEST_PLATE, ItemBuilder(it).get())
}
type.getSection("items.leggings", level)?.let {
println("legs!")
entity.setItem(EquipmentSlot.LEGGINGS, ItemBuilder(it).get())
}
type.getSection("items.boots", level)?.let {
println("boots!")
entity.setItem(EquipmentSlot.BOOTS, ItemBuilder(it).get())
}
}
@ -276,4 +337,12 @@ class Minion(
override fun getChestLocationId(): Int {
return this.chestLocationId
}
override fun removeOpenInventory(inventory: Inventory) {
openInventories.remove(inventory)
}
override fun getInventory(): Inventory {
return Bukkit.createInventory(this, 9)
}
}

View File

@ -43,5 +43,39 @@ hooks:
# Supported prices providers: ShopGUIPlus, Essentials, EconomyShopGUI, CMI, custom
prices: "ShopGUIPlus"
gui:
size: 27
items:
filler:
type: "black_stained_glass_pane"
name: ""
rotate:
type: "ender_eye"
name: "<#33FF33><b>Rotate"
lore:
- ""
- " <gray>- <white>Current: <#FFCC00><rotation>"
- ""
- "<#FFCC00><b>(!)</b> Click here to rotate your minion!"
slot: 10
link:
type: "chest"
name: "<#FFCC00><b>Link to container"
lore:
- ""
- " <gray>- <white>Status: <#FFCC00><linked>"
- ""
- "<#FFCC00><b>(!)</b> Click here to link your minion to a container!"
slot: 11
item:
type: "air"
slot: 13
# These settings are minion-specific, you can edit them in each minion's file!
upgrade:
slot: 15
statistics:
slot: 16
# Do not change!
config-version: 1

View File

@ -9,7 +9,9 @@ place:
pickup:
success: "<green>Successfully picked up a minion! <gray>(<placed>/<max>)"
inventory-full: "<red>Could not pick up minion! Make some space in your inventory!"
errors:
inventory-full: "<red>You don't have enough space in your inventory to do this! Go, and make some space!"
convert:
start: "<green>Starting data conversion... "
@ -21,5 +23,35 @@ warnings:
no-water-nearby: "<#FF3333>⚠ <#FFAAAA>The minion needs water nearby </#FFAAAA>⚠"
container-full: "<#FF3333>⚠ <#FFAAAA>The container of this minion is full </#FFAAAA>⚠"
statistics: |
<white>Ticking minions: <gradient:#00aaff:#00ccff><ticking></gradient>
<white>Not ticking minions: <gradient:#00aaff:#00ccff><not-ticking></gradient>
<white>Total: <gradient:#00aaff:#00ccff><total></gradient>
upgrades:
upgraded: "<green>Successfully upgraded the minion to level: <white><level></white>!"
limit-reached: "<red>You have maxed this minion!"
levels:
1: "<#33FF33>"
2: "<#66FF00>"
3: "<#AAFF00>"
4: "<#FFFF00>"
5: "<#FFCC00>"
6: "<#FFAA00>"
7: "<#FF8800>"
8: "<#FF5500>"
9: "<#FF3333>"
10: "<#FF0000>"
directions:
north: "North"
east: "East"
south: "South"
west: "West"
tools:
wrong-tool: "<red>You can't place this item as the tool if this minion!"
# Do not change!
config-version: 1

View File

@ -22,7 +22,7 @@ item:
- ""
- "<#FFEE00>Statistics"
- " <#FFEE00>❙ <white>Level: <#FFEEAA><level>"
- " <#FFEE00>❙ <white>Collected items: <#FFEEAA><items>"
- " <#FFEE00>❙ <white>Collected items: <#FFEEAA><actions>"
- ""
- "<#FFEE00><b>(!)</b> Place the minion and give it a shovel!"
@ -32,9 +32,9 @@ gui:
name: "<#00CCFF><b>Upgrade minion"
lore:
- ""
- " <gray>- <white>Level: <green><current_level> » <dark_green><next_level>"
- " <gray>- <white>Range: <green><current_range> » <dark_green><next_range>"
- " <gray>- <white>Speed: <green><current_speed> » <dark_green><next_speed>"
- " <gray>- <white>Level: <green><level> » <dark_green><next_level>"
- " <gray>- <white>Range: <green><range> » <dark_green><next_range>"
- " <gray>- <white>Speed: <green><speed> » <dark_green><next_speed>"
- ""
- "<#00CCFF>Requirements:"
- " <gray>- <white>Money: <#33FF33><price>$"
@ -46,7 +46,7 @@ gui:
name: "<#FFEE00><b>Statistics"
lore:
- ""
- " <gray>- <white>Collected items: <#FFEE00><statistic_collected>"
- " <gray>- <white>Collected items: <#FFEE00><actions>"
- ""
upgrades:

View File

@ -15,14 +15,14 @@ tool:
item:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDAxZTAzNWEzZDhkNjEyNjA3MmJjYmU1MmE5NzkxM2FjZTkzNTUyYTk5OTk1YjVkNDA3MGQ2NzgzYTMxZTkwOSJ9fX0="
name: "<#33FF33>Collector <white>Minion"
name: "<#33FF33>Farmer <white>Minion"
lore:
- ""
- " <gray>- <white>Harvests and replants nearby crops."
- ""
- "<#33FF33>Statistics"
- " <#33FF33>❙ <white>Level: <#AAFFAA><level>"
- " <#33FF33>❙ <white>Harvested crops: <#AAFFAA><items>"
- " <#33FF33>❙ <white>Harvested crops: <#AAFFAA><actions>"
- ""
- "<#33FF33><b>(!)</b> Place the minion and give it a hoe!"

Binary file not shown.