This commit is contained in:
TomTom 2023-10-04 20:30:39 +02:00
parent 985af5f6b6
commit 5495652ea9
31 changed files with 575 additions and 78 deletions

View File

@ -1,8 +1,11 @@
package com.artillexstudios.axminions.api package com.artillexstudios.axminions.api
import com.artillexstudios.axapi.AxPlugin
import com.artillexstudios.axminions.api.config.Config import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.data.DataHandler import com.artillexstudios.axminions.api.data.DataHandler
import com.artillexstudios.axminions.api.minions.Minion
import org.bukkit.entity.Player
import java.io.File import java.io.File
interface AxMinionsAPI { interface AxMinionsAPI {
@ -15,6 +18,12 @@ interface AxMinionsAPI {
fun getDataHandler(): DataHandler fun getDataHandler(): DataHandler
fun getMinions(): List<Minion>
fun getAxMinionsInstance(): AxPlugin
fun getMinionLimit(player: Player): Int
companion object { companion object {
@JvmStatic @JvmStatic
lateinit var INSTANCE: AxMinionsAPI lateinit var INSTANCE: AxMinionsAPI

View File

@ -12,30 +12,30 @@ import java.io.InputStream
class Config(file: File, stream: InputStream) { class Config(file: File, stream: InputStream) {
companion object { companion object {
@JvmField @JvmStatic
val AUTO_SAVE_MINUTES = AxMinionsAPI.INSTANCE.getConfig().get("autosave-minutes", 3L) fun AUTO_SAVE_MINUTES() = AxMinionsAPI.INSTANCE.getConfig().get("autosave-minutes", 3L)
@JvmField @JvmStatic
val MAX_LINKING_DISTANCE = AxMinionsAPI.INSTANCE.getConfig().get("max-linking-distance", 30) fun MAX_LINKING_DISTANCE() = AxMinionsAPI.INSTANCE.getConfig().get("max-linking-distance", 30)
@JvmField @JvmStatic
val DEFAULT_MINION_LIMIT = AxMinionsAPI.INSTANCE.getConfig().get("default-minion-limit", 5) fun DEFAULT_MINION_LIMIT() = AxMinionsAPI.INSTANCE.getConfig().get("default-minion-limit", 5)
@JvmField @JvmStatic
val ALLOW_FLOATING_MINIONS = AxMinionsAPI.INSTANCE.getConfig().get("allow-floating-minions", false) fun ALLOW_FLOATING_MINIONS() = AxMinionsAPI.INSTANCE.getConfig().get("allow-floating-minions", false)
@JvmField @JvmStatic
val ONLY_OWNER_BREAK = AxMinionsAPI.INSTANCE.getConfig().get("only-owner-break", true) fun ONLY_OWNER_BREAK() = AxMinionsAPI.INSTANCE.getConfig().get("only-owner-break", true)
@JvmField @JvmStatic
val DISPLAY_WARNINGS = AxMinionsAPI.INSTANCE.getConfig().get("display-warnings", true) fun DISPLAY_WARNINGS() = AxMinionsAPI.INSTANCE.getConfig().get("display-warnings", true)
@JvmField @JvmStatic
val CAN_BREAK_TOOLS = AxMinionsAPI.INSTANCE.getConfig().get("can-break-tools", true) fun CAN_BREAK_TOOLS() = AxMinionsAPI.INSTANCE.getConfig().get("can-break-tools", true)
@JvmField @JvmStatic
val USE_DURABILITY = AxMinionsAPI.INSTANCE.getConfig().get("use-durability", true) fun USE_DURABILITY() = AxMinionsAPI.INSTANCE.getConfig().get("use-durability", true)
@JvmField @JvmStatic
val DATABASE_TYPE = AxMinionsAPI.INSTANCE.getConfig().get("database.type", "H2") fun DATABASE_TYPE() = AxMinionsAPI.INSTANCE.getConfig().get("database.type", "H2")
@JvmField @JvmStatic
val STACKER_HOOK = AxMinionsAPI.INSTANCE.getConfig().get("hooks.stacker", "none") fun STACKER_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.stacker", "none")
@JvmField @JvmStatic
val ECONOMY_HOOK = AxMinionsAPI.INSTANCE.getConfig().get("hooks.economy", "Vault") fun ECONOMY_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.economy", "Vault")
@JvmField @JvmStatic
val PRICES_HOOK = AxMinionsAPI.INSTANCE.getConfig().get("hooks.prices", "ShopGUIPlus") fun PRICES_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.prices", "ShopGUIPlus")
} }
private val config = Config( private val config = Config(

View File

@ -13,16 +13,16 @@ import java.io.InputStream
class Messages(file: File, stream: InputStream) { class Messages(file: File, stream: InputStream) {
companion object { companion object {
@JvmField @JvmStatic
var PREFIX = AxMinionsAPI.INSTANCE.getMessages().get<String>("prefix") fun PREFIX() = AxMinionsAPI.INSTANCE.getMessages().get<String>("prefix")
@JvmField @JvmStatic
var NO_CONTAINER_WARNING = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-container") fun NO_CONTAINER_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-container")
@JvmField @JvmStatic
var NO_TOOL_WARNING = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-tool") fun NO_TOOL_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-tool")
@JvmField @JvmStatic
var NO_WATER_NEARBY_WARNING = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-water-nearby") fun NO_WATER_NEARBY_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.no-water-nearby")
@JvmField @JvmStatic
var CONTAINER_FULL_WARNING = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.container-full") fun CONTAINER_FULL_WARNING() = AxMinionsAPI.INSTANCE.getMessages().get<String>("warnings.container-full")
} }
private val config = Config( private val config = Config(

View File

@ -72,4 +72,12 @@ interface Minion {
fun setDirection(direction: Direction) fun setDirection(direction: Direction)
fun getDirection(): Direction fun getDirection(): Direction
fun remove()
fun getLinkedInventory(): Inventory?
fun addToContainerOrDrop(itemStack: ItemStack)
fun addToContainerOrDrop(itemStack: Iterable<ItemStack>)
} }

View File

@ -1,9 +1,13 @@
package com.artillexstudios.axminions.api.minions.miniontype package com.artillexstudios.axminions.api.minions.miniontype
import com.artillexstudios.axapi.config.Config import com.artillexstudios.axapi.config.Config
import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axminions.api.AxMinionsAPI import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import dev.dejvokep.boostedyaml.block.implementation.Section
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -11,7 +15,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
private lateinit var config: Config private lateinit var config: Config
fun load() { fun load() {
config = Config(File(AxMinionsAPI.INSTANCE.getAxMinionsDataFolder(), "$name.yml"), defaults) config = Config(File(AxMinionsAPI.INSTANCE.getAxMinionsDataFolder(), "/minions/$name.yml"), defaults)
AxMinionsAPI.INSTANCE.getDataHandler().loadMinionsOfType(this) AxMinionsAPI.INSTANCE.getDataHandler().loadMinionsOfType(this)
} }
@ -34,9 +38,50 @@ abstract class MinionType(private val name: String, private val defaults: InputS
run(minion) run(minion)
} }
fun getItem(): ItemStack {
val builder = ItemBuilder(config.getSection("item"))
val itemStack = builder.clonedGet()
val itemMeta = itemStack.itemMeta ?: return itemStack
itemMeta.persistentDataContainer.set(MinionTypes.getMinionKey(), PersistentDataType.STRING, name)
itemStack.setItemMeta(itemMeta)
return itemStack
}
fun getConfig(): Config { fun getConfig(): Config {
return this.config return this.config
} }
fun getString(key: String, level: Int): String {
return get(key, level, "---", String::class.java)!!
}
fun getDouble(key: String, level: Int): Double {
return get(key, level, -1.0, Double::class.java)!!
}
fun getLong(key: String, level: Int): Long {
return get(key, level, -1, Long::class.java)!!
}
fun getSection(key: String, level: Int): Section? {
return get(key, level, null, Section::class.java)
}
private fun <T> get(key: String, level: Int, defaultValue: T?, clazz: Class<T>): T? {
var n = defaultValue
config.getSection("upgrades").getRoutesAsStrings(false).forEach {
if (it.toInt() > level) return n
if (config.backingDocument.getAsOptional("upgrades.$it.$key", clazz).isEmpty) return@forEach
n = config.get("upgrades.$it.$key")
}
return n
}
abstract fun run(minion: Minion) abstract fun run(minion: Minion)
} }

View File

@ -1,11 +1,15 @@
package com.artillexstudios.axminions.api.minions.miniontype package com.artillexstudios.axminions.api.minions.miniontype
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.exception.MinionTypeAlreadyRegisteredException import com.artillexstudios.axminions.api.exception.MinionTypeAlreadyRegisteredException
import org.bukkit.NamespacedKey
import java.util.Collections import java.util.Collections
object MinionTypes { object MinionTypes {
private val TYPES = hashMapOf<String, MinionType>() private val TYPES = hashMapOf<String, MinionType>()
private val MINION_KEY = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "minion_type")
@JvmStatic
fun register(type: MinionType): MinionType { fun register(type: MinionType): MinionType {
if (TYPES.containsKey(type.getName())) { if (TYPES.containsKey(type.getName())) {
throw MinionTypeAlreadyRegisteredException("A minion with type ${type.getName()} has already been registered!") throw MinionTypeAlreadyRegisteredException("A minion with type ${type.getName()} has already been registered!")
@ -16,10 +20,22 @@ object MinionTypes {
return type return type
} }
@JvmStatic
fun unregister(key: String) { fun unregister(key: String) {
TYPES.remove(key) TYPES.remove(key)
} }
@JvmStatic
fun valueOf(name: String): MinionType? {
return TYPES[name]
}
@JvmStatic
fun getMinionKey(): NamespacedKey {
return MINION_KEY
}
@JvmStatic
fun getMinionTypes(): Map<String, MinionType> { fun getMinionTypes(): Map<String, MinionType> {
return Collections.unmodifiableMap(TYPES) return Collections.unmodifiableMap(TYPES)
} }

View File

@ -8,6 +8,6 @@ import net.kyori.adventure.text.Component
class WarningContainerFull : Warning("container_full") { class WarningContainerFull : Warning("container_full") {
override fun getContent(): Component { override fun getContent(): Component {
return StringUtils.format(Messages.CONTAINER_FULL_WARNING) return StringUtils.format(Messages.CONTAINER_FULL_WARNING())
} }
} }

View File

@ -8,6 +8,6 @@ import net.kyori.adventure.text.Component
class WarningNoContainer : Warning("no_container") { class WarningNoContainer : Warning("no_container") {
override fun getContent(): Component { override fun getContent(): Component {
return StringUtils.format(Messages.NO_CONTAINER_WARNING) return StringUtils.format(Messages.NO_CONTAINER_WARNING())
} }
} }

View File

@ -8,6 +8,6 @@ import net.kyori.adventure.text.Component
class WarningNoTool : Warning("no_tool") { class WarningNoTool : Warning("no_tool") {
override fun getContent(): Component { override fun getContent(): Component {
return StringUtils.format(Messages.NO_TOOL_WARNING) return StringUtils.format(Messages.NO_TOOL_WARNING())
} }
} }

View File

@ -8,6 +8,6 @@ import net.kyori.adventure.text.Component
class WarningNoWaterNearby : Warning("no_water_nearby") { class WarningNoWaterNearby : Warning("no_water_nearby") {
override fun getContent(): Component { override fun getContent(): Component {
return StringUtils.format(Messages.NO_WATER_NEARBY_WARNING) return StringUtils.format(Messages.NO_WATER_NEARBY_WARNING())
} }
} }

View File

@ -19,6 +19,7 @@ repositories {
dependencies { dependencies {
implementation project(path: ":api") implementation project(path: ":api")
implementation project(path: ":common")
} }
allprojects { allprojects {
@ -61,11 +62,9 @@ allprojects {
compileOnly 'dev.rosewood:rosestacker:1.5.11' compileOnly 'dev.rosewood:rosestacker:1.5.11'
compileOnly 'com.bgsoftware:WildStackerAPI:2023.2' compileOnly 'com.bgsoftware:WildStackerAPI:2023.2'
compileOnly 'com.github.MilkBowl:VaultAPI:1.7' compileOnly 'com.github.MilkBowl:VaultAPI:1.7'
compileOnly 'com.h2database:h2:2.2.220'
compileOnly 'com.github.NEZNAMY:YamlAssist:1.0.5'
compileOnly 'com.github.brcdev-minecraft:shopgui-api:3.0.0' compileOnly 'com.github.brcdev-minecraft:shopgui-api:3.0.0'
compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.9.0' compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.9.0'
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4' compileOnly 'com.h2database:h2:2.2.220'
implementation files('../libs/AxAPI-1.0-SNAPSHOT.jar') implementation files('../libs/AxAPI-1.0-SNAPSHOT.jar')
} }

View File

@ -1,12 +1,39 @@
package com.artillexstudios.axminions package com.artillexstudios.axminions
import com.artillexstudios.axapi.AxPlugin import com.artillexstudios.axapi.AxPlugin
import com.artillexstudios.axapi.data.ThreadedQueue
import com.artillexstudios.axminions.api.AxMinionsAPI import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.AxMinionsAPIImpl import com.artillexstudios.axminions.api.AxMinionsAPIImpl
import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.data.DataHandler
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.minions.miniontype.CollectorMinionType
import com.artillexstudios.axminions.minions.miniontype.FarmerMinionType
import net.byteflux.libby.BukkitLibraryManager
import net.byteflux.libby.Library
import revxrsal.commands.bukkit.BukkitCommandHandler
import java.io.File
class AxMinionsPlugin : AxPlugin() { class AxMinionsPlugin : AxPlugin() {
companion object { companion object {
lateinit var INSTANCE: AxMinionsPlugin lateinit var INSTANCE: AxMinionsPlugin
lateinit var messages: Messages
lateinit var config: Config
lateinit var dataHandler: DataHandler
lateinit var dataQueue: ThreadedQueue<Runnable>
}
init {
val manager = BukkitLibraryManager(this)
val stdLib = Library.builder().groupId("org.jetbrains.kotlin").artifactId("kotlin-stdlib").relocate("org.jetbrains.kotlin", "com.artillexstudios.axminions.libs.kotlin").version("1.9.0").build()
val h2 = Library.builder().groupId("com.h2database").artifactId("h2").version("2.2.220").relocate("org.h2", "com.artillexstudios.axminions.libs.h2").build()
manager.addMavenCentral()
manager.loadLibrary(stdLib)
manager.loadLibrary(h2)
} }
override fun load() { override fun load() {
@ -15,6 +42,43 @@ class AxMinionsPlugin : AxPlugin() {
} }
override fun enable() { override fun enable() {
AxMinionsPlugin.config = Config(File(dataFolder, "config.yml"), getResource("config.yml")!!)
messages = Messages(File(dataFolder, "messages.yml"), getResource("messages.yml")!!)
loadDataHandler()
dataQueue = ThreadedQueue("AxMinions-Database-Queue")
MinionTypes.also {
it.register(CollectorMinionType())
it.register(FarmerMinionType())
}
val handler = BukkitCommandHandler.create(this)
handler.registerValueResolver(MinionType::class.java) { c ->
val type = c.popForParameter()
val minionType = MinionTypes.valueOf(type) ?: return@registerValueResolver MinionTypes.valueOf("collector")
return@registerValueResolver minionType
}
handler.autoCompleter.registerSuggestion("minionTypes") { _, _, _ ->
return@registerSuggestion MinionTypes.getMinionTypes().values.map { it.getName() }
}
handler.register(AxMinionsCommand())
handler.registerBrigadier()
}
private fun loadDataHandler() {
if (Config.DATABASE_TYPE().equals("H2", true)) {
dataHandler = H2DataHandler()
dataHandler.setup()
} else {
} }
}
} }

View File

@ -1,28 +1,58 @@
package com.artillexstudios.axminions.api package com.artillexstudios.axminions.api
import com.artillexstudios.axapi.AxPlugin
import com.artillexstudios.axminions.AxMinionsPlugin import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.config.Config import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.data.DataHandler import com.artillexstudios.axminions.api.data.DataHandler
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.minions.Minions
import org.bukkit.entity.Player
import java.io.File import java.io.File
class AxMinionsAPIImpl(private val plugin: AxMinionsPlugin) : AxMinionsAPI { class AxMinionsAPIImpl(private val plugin: AxMinionsPlugin) : AxMinionsAPI {
private val messages = Messages(File(getAxMinionsDataFolder(), "messages.yml"), plugin.getResource("messages.yml")!!)
private val config = Config(File(getAxMinionsDataFolder(), "config.yml"), plugin.getResource("config.yml")!!)
override fun getAxMinionsDataFolder(): File { override fun getAxMinionsDataFolder(): File {
return plugin.dataFolder return plugin.dataFolder
} }
override fun getMessages(): Messages { override fun getMessages(): Messages {
return messages return AxMinionsPlugin.messages
} }
override fun getConfig(): Config { override fun getConfig(): Config {
return config return AxMinionsPlugin.config
} }
override fun getDataHandler(): DataHandler { override fun getDataHandler(): DataHandler {
TODO("Not yet implemented") return AxMinionsPlugin.dataHandler
}
override fun getMinions(): List<Minion> {
return Minions.getMinions()
}
override fun getAxMinionsInstance(): AxPlugin {
return plugin
}
override fun getMinionLimit(player: Player): Int {
var limit = Config.DEFAULT_MINION_LIMIT()
player.effectivePermissions.forEach {
val permission = it.permission
if (!permission.startsWith("axminions.limit.")) return@forEach
if (permission.contains("*")) {
return Int.MAX_VALUE
}
val value = permission.substring(permission.lastIndexOf('.') + 1).toInt()
if (value > limit) {
limit = value
}
}
return limit
} }
} }

View File

@ -0,0 +1,32 @@
package com.artillexstudios.axminions.commands
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import revxrsal.commands.annotation.AutoComplete
import revxrsal.commands.annotation.Command
import revxrsal.commands.annotation.Default
import revxrsal.commands.annotation.Description
import revxrsal.commands.annotation.Range
import revxrsal.commands.annotation.Subcommand
import revxrsal.commands.bukkit.annotation.CommandPermission
@Command("axminions", "minion", "minions")
class AxMinionsCommand {
@Subcommand("give")
@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())
}
@Subcommand("reload")
@CommandPermission("axminions.command.reload")
@Description("Reload the configurations of the plugin")
fun reload(sender: CommandSender) {
}
}

View File

@ -3,10 +3,8 @@ package com.artillexstudios.axminions.data
import com.artillexstudios.axapi.serializers.Serializers import com.artillexstudios.axapi.serializers.Serializers
import com.artillexstudios.axminions.AxMinionsPlugin import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Direction 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.MinionType
import com.artillexstudios.axminions.api.data.DataHandler import com.artillexstudios.axminions.api.data.DataHandler
import com.artillexstudios.axminions.minions.MinionImpl
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
@ -58,7 +56,7 @@ class H2DataHandler : DataHandler {
itemStack = Serializers.ITEM_STACK.deserialize(tool) itemStack = Serializers.ITEM_STACK.deserialize(tool)
} }
MinionImpl( com.artillexstudios.axminions.minions.Minion(
Serializers.LOCATION.deserialize(location), Serializers.LOCATION.deserialize(location),
uuid, uuid,
ownerPlayer, ownerPlayer,
@ -74,7 +72,7 @@ class H2DataHandler : DataHandler {
} }
} }
override fun saveMinion(minion: Minion) { override fun saveMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
connection.prepareStatement("MERGE INTO `axminions_data`(`location`, `owner`, `linked-chest-location`, `extra_data`, `direction`, `type`, `level`, `tool`) KEY(`location`) VALUES(?,?,?,?,?,?,?,?);") connection.prepareStatement("MERGE INTO `axminions_data`(`location`, `owner`, `linked-chest-location`, `extra_data`, `direction`, `type`, `level`, `tool`) KEY(`location`) VALUES(?,?,?,?,?,?,?,?);")
.use { preparedStatement -> .use { preparedStatement ->
preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation())) preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation()))
@ -96,7 +94,7 @@ class H2DataHandler : DataHandler {
} }
} }
override fun deleteMinion(minion: Minion) { override fun deleteMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
connection.prepareStatement("DELETE FROM `axminions_data` WHERE `location` = ?;").use { preparedStatement -> connection.prepareStatement("DELETE FROM `axminions_data` WHERE `location` = ?;").use { preparedStatement ->
preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation())) preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation()))
preparedStatement.executeUpdate() preparedStatement.executeUpdate()

View File

@ -9,9 +9,12 @@ import com.artillexstudios.axminions.api.minions.Direction
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.warnings.Warning import com.artillexstudios.axminions.api.warnings.Warning
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.utils.fastFor import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.block.Container
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.EntityType import org.bukkit.entity.EntityType
import org.bukkit.entity.Player import org.bukkit.entity.Player
@ -21,7 +24,7 @@ import org.bukkit.util.EulerAngle
import java.util.UUID import java.util.UUID
import kotlin.math.roundToInt import kotlin.math.roundToInt
class MinionImpl( class Minion(
private val location: Location, private val location: Location,
private val ownerUUID: UUID, private val ownerUUID: UUID,
private val owner: OfflinePlayer, private val owner: OfflinePlayer,
@ -40,10 +43,13 @@ class MinionImpl(
private var warning: Warning? = null private var warning: Warning? = null
private var hologram: Hologram? = null private var hologram: Hologram? = null
private val extraData = hashMapOf<String, String>() private val extraData = hashMapOf<String, String>()
private var linkedInventory: Inventory? = null
init { init {
spawn() spawn()
loadExtraData(savedExtraData) loadExtraData(savedExtraData)
Minions.load(this)
linkedInventory = (linkedChest?.block?.blockData as? Container)?.inventory
} }
override fun getType(): MinionType { override fun getType(): MinionType {
@ -54,14 +60,21 @@ class MinionImpl(
entity = PacketEntityFactory.get().spawnEntity(location, EntityType.ARMOR_STAND) as PacketArmorStand entity = PacketEntityFactory.get().spawnEntity(location, EntityType.ARMOR_STAND) as PacketArmorStand
entity.setHasBasePlate(false) entity.setHasBasePlate(false)
entity.setSmall(true) entity.setSmall(true)
entity.onClick { event ->
if (event.isAttack) {
println("LEFT CLICKED!")
} else {
println("RIGHT CLICKED!")
}
}
} }
override fun tick() { override fun tick() {
if (dirty) { if (dirty) {
dirty = false dirty = false
range = type.getConfig().get("range", 0.0) range = type.getDouble("range", level)
val efficiency = 1.0 - (getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1) val efficiency = 1.0 - (getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1)
nextAction = (type.getConfig().get("speed", 0L) * efficiency).roundToInt() nextAction = (type.getLong("speed", level) * efficiency).roundToInt()
} }
type.tick(this) type.tick(this)
@ -73,15 +86,15 @@ class MinionImpl(
} }
override fun updateInventory(inventory: Inventory) { override fun updateInventory(inventory: Inventory) {
TODO("Not yet implemented")
} }
override fun openInventory(player: Player) { override fun openInventory(player: Player) {
TODO("Not yet implemented")
} }
override fun getAsItem(): ItemStack { override fun getAsItem(): ItemStack {
TODO("Not yet implemented") return ItemStack(Material.STONE)
} }
override fun getLevel(): Int { override fun getLevel(): Int {
@ -166,6 +179,7 @@ class MinionImpl(
override fun setLinkedChest(location: Location?) { override fun setLinkedChest(location: Location?) {
this.linkedChest = location?.clone() this.linkedChest = location?.clone()
linkedInventory = (linkedChest?.block?.blockData as? Container)?.inventory
} }
override fun getLinkedChest(): Location? { override fun getLinkedChest(): Location? {
@ -194,6 +208,30 @@ class MinionImpl(
return this.direction return this.direction
} }
override fun remove() {
Warnings.remove(this)
Minions.remove(this)
entity.remove()
}
override fun getLinkedInventory(): Inventory? {
return linkedInventory
}
override fun addToContainerOrDrop(itemStack: ItemStack) {
val remaining = linkedInventory?.addItem(itemStack)
remaining?.forEach { (_, u) ->
location.world?.dropItem(location, u)
}
}
override fun addToContainerOrDrop(itemStack: Iterable<ItemStack>) {
itemStack.forEach {
addToContainerOrDrop(it)
}
}
private fun loadExtraData(data: String) { private fun loadExtraData(data: String) {
data.split("|").fastFor { split -> data.split("|").fastFor { split ->
val secondSplit = split.split("=") val secondSplit = split.split("=")

View File

@ -1,5 +1,7 @@
package com.artillexstudios.axminions.minions package com.artillexstudios.axminions.minions
import com.artillexstudios.axapi.scheduler.Scheduler
object MinionTicker { object MinionTicker {
private var tick = 0L private var tick = 0L
@ -9,6 +11,12 @@ object MinionTicker {
tick++ tick++
} }
fun startTicking() {
Scheduler.get().runTimer({ task ->
}, 0, 0)
}
fun getTick(): Long { fun getTick(): Long {
return this.tick return this.tick
} }

View File

@ -0,0 +1,20 @@
package com.artillexstudios.axminions.minions
import com.artillexstudios.axminions.api.minions.Minion
import java.util.Collections
object Minions {
private val entities = mutableListOf<Minion>()
fun load(minion: Minion) {
entities.add(minion)
}
fun remove(minion: Minion) {
entities.remove(minion)
}
fun getMinions(): List<Minion> {
return Collections.unmodifiableList(entities)
}
}

View File

@ -5,7 +5,7 @@ import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.warnings.Warnings import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.minions.MinionTicker import com.artillexstudios.axminions.minions.MinionTicker
import org.bukkit.block.Container import org.bukkit.Material
import org.bukkit.entity.Item import org.bukkit.entity.Item
class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.getResource("minions/collector.yml")!!) { class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.getResource("minions/collector.yml")!!) {
@ -21,8 +21,14 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
return return
} }
val state = minion.getLinkedChest()?.block?.state val type = minion.getLinkedChest()!!.block.type
if (state !is Container) { if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL && !type.name.lowercase().contains("shulker_box")) {
Warnings.NO_CONTAINER.display(minion)
minion.setLinkedChest(null)
return
}
if (minion.getLinkedInventory() == null) {
Warnings.NO_CONTAINER.display(minion) Warnings.NO_CONTAINER.display(minion)
minion.setLinkedChest(null) minion.setLinkedChest(null)
return return
@ -38,7 +44,7 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
) )
entities?.filterIsInstance<Item>()?.forEach { item -> entities?.filterIsInstance<Item>()?.forEach { item ->
if (state.inventory.firstEmpty() == -1) { if (minion.getLinkedInventory()?.firstEmpty() == -1) {
Warnings.CONTAINER_FULL.display(minion) Warnings.CONTAINER_FULL.display(minion)
return return
} }

View File

@ -3,10 +3,42 @@ package com.artillexstudios.axminions.minions.miniontype
import com.artillexstudios.axminions.AxMinionsPlugin import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.utils.LocationUtils
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Material
import org.bukkit.block.data.Ageable
import org.bukkit.inventory.ItemStack
class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResource("minions/farmer.yml")!!) { class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResource("minions/farmer.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented") LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val block = location.block
val drops = arrayListOf<ItemStack>()
when (block.type) {
Material.CACTUS, Material.SUGAR_CANE, Material.BAMBOO, Material.MELON, Material.PUMPKIN -> {
drops.addAll(block.getDrops(minion.getTool()))
block.type = Material.AIR
}
Material.COCOA_BEANS, Material.COCOA, Material.NETHER_WART, Material.WHEAT, Material.CARROTS, Material.BEETROOTS, Material.POTATOES -> {
val ageable = block.blockData as Ageable
if (ageable.age != ageable.maximumAge) return@fastFor
drops.addAll(block.getDrops(minion.getTool()))
ageable.age = 0
block.blockData = ageable
}
Material.SWEET_BERRY_BUSH -> {
val ageable = block.blockData as Ageable
if (ageable.age != ageable.maximumAge) return@fastFor
drops.addAll(block.getDrops(minion.getTool()))
ageable.age = 1
block.blockData = ageable
}
else -> return@fastFor
}
minion.addToContainerOrDrop(drops)
}
} }
} }

View File

@ -3,10 +3,11 @@ package com.artillexstudios.axminions.minions.miniontype
import com.artillexstudios.axminions.AxMinionsPlugin import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import org.bukkit.loot.LootTables
class FisherMinionType : MinionType("fisher", AxMinionsPlugin.INSTANCE.getResource("minions/fisher.yml")!!) { class FisherMinionType : MinionType("fisher", AxMinionsPlugin.INSTANCE.getResource("minions/fisher.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented")
} }
} }

View File

@ -7,6 +7,6 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
class LumberMinionType : MinionType("lumber", AxMinionsPlugin.INSTANCE.getResource("minions/lumber.yml")!!) { class LumberMinionType : MinionType("lumber", AxMinionsPlugin.INSTANCE.getResource("minions/lumber.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented")
} }
} }

View File

@ -7,6 +7,6 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
class MinerMinionType : MinionType("miner", AxMinionsPlugin.INSTANCE.getResource("minions/miner.yml")!!) { class MinerMinionType : MinionType("miner", AxMinionsPlugin.INSTANCE.getResource("minions/miner.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented")
} }
} }

View File

@ -7,6 +7,6 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
class SellerMinionType : MinionType("seller", AxMinionsPlugin.INSTANCE.getResource("minions/seller.yml")!!) { class SellerMinionType : MinionType("seller", AxMinionsPlugin.INSTANCE.getResource("minions/seller.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented")
} }
} }

View File

@ -7,6 +7,6 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
class SlayerMinionType : MinionType("slayer", AxMinionsPlugin.INSTANCE.getResource("minions/slayer.yml")!!) { class SlayerMinionType : MinionType("slayer", AxMinionsPlugin.INSTANCE.getResource("minions/slayer.yml")!!) {
override fun run(minion: Minion) { override fun run(minion: Minion) {
TODO("Not yet implemented")
} }
} }

View File

@ -0,0 +1,36 @@
package com.artillexstudios.axminions.utils
import org.bukkit.Location
object LocationUtils {
@JvmStatic
fun getAllBlocksInRadius(location: Location, radius: Double, filterEmpty: Boolean): ArrayList<Location> {
// Approximate the volume of the sphere
val blocks = ArrayList<Location>((2 * radius * radius * radius).toInt())
val blockX = location.blockX
val blockY = location.blockY
val blockZ = location.blockZ
val rangeX = (blockX - radius).rangeTo((blockX + radius)).step(1.0)
val rangeY = (blockY - radius).rangeTo((blockY + radius)).step(1.0)
val rangeZ = (blockZ - radius).rangeTo((blockZ + radius)).step(1.0)
val radiusSquared = radius * radius
val smallRadiusSquared = (radius - 1) * (radius -1)
for (x in rangeX) {
for (y in rangeY) {
for (z in rangeZ) {
val distance = ((blockX - x) * (blockX - x) + ((blockZ - z) * (blockZ - z)) + ((blockY - y) * (blockY - y)))
if (distance < radiusSquared && !(filterEmpty && distance < smallRadiusSquared)) {
blocks.add(Location(location.world, x, y, z))
}
}
}
}
return blocks
}
}

View File

@ -0,0 +1,16 @@
package com.artillexstudios.axminions.utils
// Thanks a lot to this hero! https://stackoverflow.com/a/44332139
infix fun ClosedRange<Double>.step(step: Double): Iterable<Double> {
require(start.isFinite())
require(endInclusive.isFinite())
require(step > 0.0) { "Step must be positive, was: $step." }
val sequence = generateSequence(start) { previous ->
if (previous == Double.POSITIVE_INFINITY) return@generateSequence null
val next = previous + step
if (next > endInclusive) null else next
}
return sequence.asIterable()
}

View File

@ -10,7 +10,7 @@ tool:
- "NETHERITE_SHOVEL" - "NETHERITE_SHOVEL"
item: item:
material: "player_head" type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2UzZDM2MzVjZTQxMWFiZjFlNGYzNzNkMTYxZDA3YjhjNDdlMzU5YjZjNTZmNzRiNDEzY2I0OTRhYzc0NmUyZCJ9fX0=" texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2UzZDM2MzVjZTQxMWFiZjFlNGYzNzNkMTYxZDA3YjhjNDdlMzU5YjZjNTZmNzRiNDEzY2I0OTRhYzc0NmUyZCJ9fX0="
name: "<#FFEE00>Collector <white>Minion" name: "<#FFEE00>Collector <white>Minion"
lore: lore:
@ -25,7 +25,7 @@ item:
gui: gui:
upgrade: upgrade:
material: "gold_ingot" type: "gold_ingot"
name: "<#00CCFF><b>Upgrade minion" name: "<#00CCFF><b>Upgrade minion"
lore: lore:
- "" - ""
@ -39,7 +39,7 @@ gui:
- "" - ""
- "<#00CCFF><b>(!)</b> Click here to upgrade your minion!" - "<#00CCFF><b>(!)</b> Click here to upgrade your minion!"
statistics: statistics:
material: "paper" type: "paper"
name: "<#FFEE00><b>Statistics" name: "<#FFEE00><b>Statistics"
lore: lore:
- "" - ""
@ -52,18 +52,18 @@ upgrades:
speed: 160 speed: 160
items: items:
helmet: helmet:
material: "player_head" type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2UzZDM2MzVjZTQxMWFiZjFlNGYzNzNkMTYxZDA3YjhjNDdlMzU5YjZjNTZmNzRiNDEzY2I0OTRhYzc0NmUyZCJ9fX0=" texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2UzZDM2MzVjZTQxMWFiZjFlNGYzNzNkMTYxZDA3YjhjNDdlMzU5YjZjNTZmNzRiNDEzY2I0OTRhYzc0NmUyZCJ9fX0="
chestplate: chestplate:
material: "leather_chestplate" type: "leather_chestplate"
color: "255, 230, 0" color: "255, 230, 0"
glow: false glow: false
leggings: leggings:
material: "leather_leggings" type: "leather_leggings"
color: "255, 230, 0" color: "255, 230, 0"
glow: false glow: false
boots: boots:
material: "leather_boots" type: "leather_boots"
color: "255, 230, 0" color: "255, 230, 0"
glow: false glow: false
2: 2:
@ -118,14 +118,14 @@ upgrades:
actions: 50000 actions: 50000
items: items:
chestplate: chestplate:
material: LEATHER_CHESTPLATE type: LEATHER_CHESTPLATE
color: "255, 200, 0" color: "255, 200, 0"
glow: true glow: true
leggings: leggings:
material: LEATHER_LEGGINGS type: LEATHER_LEGGINGS
color: "255, 200, 0" color: "255, 200, 0"
glow: true glow: true
boots: boots:
material: LEATHER_BOOTS type: LEATHER_BOOTS
color: "255, 200, 0" color: "255, 200, 0"
glow: true glow: true

View File

@ -0,0 +1,135 @@
name: "<level_color>[<level>] <#33FF33>Farmer <white>Minion <gray>[<owner>]"
tool:
material:
- "WOODEN_HOE"
- "STONE_HOE"
- "GOLDEN_HOE"
- "IRON_HOE"
- "DIAMOND_HOE"
- "NETHERITE_HOE"
item:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDAxZTAzNWEzZDhkNjEyNjA3MmJjYmU1MmE5NzkxM2FjZTkzNTUyYTk5OTk1YjVkNDA3MGQ2NzgzYTMxZTkwOSJ9fX0="
name: "<#33FF33>Collector <white>Minion"
lore:
- ""
- " <gray>- <white>Harvests and replants nearby crops."
- ""
- "<#33FF33>Statistics"
- " <#33FF33>❙ <white>Level: <#AAFFAA><level>"
- " <#33FF33>❙ <white>Harvested crops: <#AAFFAA><items>"
- ""
- "<#33FF33><b>(!)</b> Place the minion and give it a hoe!"
gui:
upgrade:
type: "gold_ingot"
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>"
- ""
- "<#00CCFF>Requirements:"
- " <gray>- <white>Money: <#33FF33><price>$"
- " <gray>- <white>Crops harvested: <#33FF33><required_actions>"
- ""
- "<#00CCFF><b>(!)</b> Click here to upgrade your minion!"
statistics:
type: "paper"
name: "<#33FF33><b>Statistics"
lore:
- ""
- " <gray>- <white>Crops harvested: <#33FF33><statistic_collected>"
- ""
upgrades:
1:
range: 2
speed: 200
items:
helmet:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDAxZTAzNWEzZDhkNjEyNjA3MmJjYmU1MmE5NzkxM2FjZTkzNTUyYTk5OTk1YjVkNDA3MGQ2NzgzYTMxZTkwOSJ9fX0="
chestplate:
type: "leather_chestplate"
color: "50, 255, 50"
glow: false
leggings:
type: "leather_leggings"
color: "50, 255, 50"
glow: false
boots:
type: "leather_boots"
color: "50, 255, 50"
glow: false
2:
range: 2.5
speed: 190
requirements:
money: 1000
actions: 10
3:
range: 3
speed: 180
requirements:
money: 3000
actions: 30
4:
range: 3.5
speed: 170
requirements:
money: 10000
actions: 100
5:
range: 3.5
speed: 160
requirements:
money: 20000
actions: 300
6:
range: 4
speed: 150
requirements:
money: 50000
actions: 600
7:
range: 4.5
speed: 140
requirements:
money: 150000
actions: 1000
8:
range: 5
speed: 120
requirements:
money: 500000
actions: 1750
9:
range: 5.5
speed: 110
requirements:
money: 1000000
actions: 25000
10:
range: 6
speed: 100
requirements:
money: 5000000
actions: 5000
items:
chestplate:
type: LEATHER_CHESTPLATE
color: "50, 225, 50"
glow: true
leggings:
type: LEATHER_LEGGINGS
color: "50, 225, 50"
glow: true
boots:
type: LEATHER_BOOTS
color: "50, 225, 50"
glow: true

View File

@ -0,0 +1,4 @@
name: "AxMinions"
main: "com.artillexstudios.axminions.AxMinionsPlugin"
version: "1.0"
api-version: 1.19

Binary file not shown.