Something is really broken with the minions

This commit is contained in:
TomTom 2023-10-14 21:07:11 +02:00
parent 8b83c4867f
commit f34b153da2
43 changed files with 1190 additions and 142 deletions

View File

@ -38,6 +38,8 @@ class Config(file: File, stream: InputStream) {
fun PRICES_HOOK() = AxMinionsAPI.INSTANCE.getConfig().get("hooks.prices", "ShopGUIPlus")
@JvmStatic
fun GUI_SIZE() = AxMinionsAPI.INSTANCE.getConfig().get<Int>("gui.size")
@JvmStatic
fun DEBUG() = AxMinionsAPI.INSTANCE.getConfig().get<Boolean>("debug")
}
private val config = Config(

View File

@ -1,8 +1,10 @@
package com.artillexstudios.axminions.api.minions
enum class Direction(val yaw: Float) {
NORTH(180f),
WEST(90f),
SOUTH(0f),
EAST(-90f);
import org.bukkit.block.BlockFace
enum class Direction(val yaw: Float, val facing: BlockFace) {
NORTH(180f, BlockFace.NORTH),
WEST(90f, BlockFace.WEST),
SOUTH(0f, BlockFace.SOUTH),
EAST(-90f, BlockFace.EAST);
}

View File

@ -103,4 +103,10 @@ interface Minion : InventoryHolder {
fun setRange(range: Double)
fun setNextAction(nextAction: Int)
fun markDirty()
fun damageTool(amount: Int = 1)
fun canUseTool(): Boolean
}

View File

@ -6,6 +6,7 @@ import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resol
import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.utils.Keys
import org.bukkit.Location
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
@ -41,13 +42,14 @@ abstract class MinionType(private val name: String, private val defaults: InputS
if (!minion.isTicking()) return
if (!shouldRun(minion)) return
minion.resetAnimation()
run(minion)
}
fun getItem(level: Int = 1): ItemStack {
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)
fun getItem(level: Int = 1, actions: Long = 0): ItemStack {
val builder = ItemBuilder(config.getSection("item"), Placeholder.unparsed("level", level.toString()), Placeholder.unparsed("actions", actions.toString()))
builder.storePersistentData(Keys.MINION_TYPE, PersistentDataType.STRING, name)
builder.storePersistentData(Keys.LEVEL, PersistentDataType.INTEGER, level)
return builder.clonedGet()
}

View File

@ -7,9 +7,7 @@ import java.util.Collections
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 {
@ -32,21 +30,6 @@ object MinionTypes {
return TYPES[name]
}
@JvmStatic
fun getMinionKey(): NamespacedKey {
return MINION_KEY
}
@JvmStatic
fun getLevelKey(): NamespacedKey {
return LEVEL_KEY
}
@JvmStatic
fun getGuiKey(): NamespacedKey {
return GUI_KEY
}
@JvmStatic
fun getMinionTypes(): Map<String, MinionType> {
return Collections.unmodifiableMap(TYPES)

View File

@ -1,4 +1,4 @@
package com.artillexstudios.axminions.utils
package com.artillexstudios.axminions.api.utils
inline fun <T> Array<T>.fastFor(action: (T) -> Unit) {
val indices = indices

View File

@ -0,0 +1,36 @@
package com.artillexstudios.axminions.api.utils
class CoolDown<T> : HashMap<T, Long>() {
fun add(value: T, time: Long) {
expire()
put(value, System.currentTimeMillis() + time)
}
override fun containsKey(key: T): Boolean {
expire()
return super.containsKey(key)
}
fun contains(value: T): Boolean {
return containsKey(value)
}
override fun remove(key: T): Long? {
expire()
return super.remove(key)
}
private fun expire() {
val currentTime = System.currentTimeMillis()
val iterator = iterator()
while (iterator.hasNext()) {
val next = iterator.next()
if (next.value <= currentTime) {
iterator.remove()
}
}
}
}

View File

@ -0,0 +1,15 @@
package com.artillexstudios.axminions.api.utils
import com.artillexstudios.axminions.api.AxMinionsAPI
import org.bukkit.NamespacedKey
object Keys {
@JvmField
val MINION_TYPE = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "minion_type")
@JvmField
val LEVEL = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "level")
@JvmField
val GUI = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "gui_item")
@JvmField
val STATISTICS = NamespacedKey(AxMinionsAPI.INSTANCE.getAxMinionsInstance(), "statistics")
}

View File

@ -1,6 +1,9 @@
package com.artillexstudios.axminions.utils
package com.artillexstudios.axminions.api.utils
import kotlin.math.roundToInt
import org.bukkit.Location
import org.bukkit.block.BlockFace
object LocationUtils {
@JvmStatic
@ -34,4 +37,17 @@ object LocationUtils {
return blocks
}
fun getAllBlocksFacing(location: Location, radius: Double, face: BlockFace): ArrayList<Location> {
val blocks = ArrayList<Location>(radius.toInt())
val modX = face.modX
val modZ = face.modZ
for (i in 1..radius.roundToInt()) {
blocks.add(location.clone().add(i * modX.toDouble(), 0.0, i * modZ.toDouble()))
}
return blocks
}
}

View File

@ -0,0 +1,91 @@
package com.artillexstudios.axminions.api.utils
import java.util.LinkedList
import java.util.Queue
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.block.BlockFace
object MinionUtils {
private val FACES =
arrayOf(BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST)
@JvmStatic
fun getPlant(block: Block): ArrayList<Block> {
val blocks = arrayListOf<Block>()
var loc = block.location.clone()
for (i in block.y downTo -65) {
loc.add(0.0, -1.0, 0.0)
val locBlock = loc.block
if (!(locBlock.type == block.type && locBlock.getRelative(BlockFace.DOWN) == block)) break
blocks.add(locBlock)
}
loc = block.location.clone()
for (i in block.y..328) {
loc.add(0.0, 1.0, 0.0)
val locBlock = loc.block
if (locBlock.type != block.type) break
blocks.add(locBlock)
}
return blocks
}
@JvmStatic
fun isStoneGenerator(location: Location): Boolean {
var lava = false
var water = false
FACES.fastFor {
val relative = location.block.getRelative(it)
val type = relative.type
if (!lava) {
lava = type == Material.LAVA
}
if (!water) {
water = type == Material.WATER
}
if (water && lava) {
return true
}
}
return false
}
@JvmStatic
fun getTree(startBlock: Block): List<Block> {
val queue: Queue<Block> = LinkedList()
val visited = mutableSetOf<Block>()
val tree = mutableListOf<Block>()
queue.add(startBlock)
while (queue.isNotEmpty()) {
val block = queue.poll()
val type = block.type.toString()
if (type.endsWith("_WOOD") || type.endsWith("_LOG")) {
tree.add(block)
FACES.fastFor {
val relative = block.getRelative(it)
if (!visited.contains(relative)) {
queue.add(relative)
visited.add(relative)
}
}
}
}
return tree
}
}

View File

@ -1,4 +1,4 @@
package com.artillexstudios.axminions.utils
package com.artillexstudios.axminions.api.utils
// Thanks a lot to this hero! https://stackoverflow.com/a/44332139
infix fun ClosedRange<Double>.step(step: Double): Iterable<Double> {

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().clone().add(0.0, 1.15, 0.0), minion.getLocation().toString())
.spawnHologram(minion.getLocation().clone().add(0.0, 1.35, 0.0), minion.getLocation().toString())
hologram.addLine(getContent())
minion.setWarning(this)
minion.setWarningHologram(hologram)

View File

@ -61,6 +61,30 @@ allprojects {
maven {
url = uri('https://repo.alessiodp.com/releases/')
}
maven {
url = uri('https://repo.rosewooddev.io/repository/public/')
}
maven {
url = uri('https://repo.bg-software.com/repository/api/')
}
maven {
url = uri('https://repo.essentialsx.net/releases/')
}
maven {
url = uri('https://maven.enginehub.org/repo/')
}
maven {
url = uri('https://repo.codemc.org/repository/maven-snapshots')
}
maven {
url = uri('https://repo.codemc.org/repository/maven-public/')
}
}
dependencies {
@ -71,6 +95,20 @@ allprojects {
compileOnly 'com.github.brcdev-minecraft:shopgui-api:3.0.0'
compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.9.0'
compileOnly 'com.h2database:h2:2.2.220'
compileOnly 'dev.rosewood:rosestacker:1.5.9'
compileOnly 'com.bgsoftware:WildStackerAPI:2023.2'
compileOnly 'net.essentialsx:EssentialsX:2.19.0'
compileOnly 'com.github.Gypopo:EconomyShopGUI-API:1.6.0'
compileOnly 'com.github.Gypopo:EconomyShopGUI-API:1.6.0'
compileOnly 'com.github.brcdev-minecraft:shopgui-api:3.0.0'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT'
compileOnly 'com.bgsoftware:SuperiorSkyblockAPI:2023.2'
compileOnly 'world.bentobox:bentobox:1.24.0-SNAPSHOT'
compileOnly 'com.github.TechFortress:GriefPrevention:16.18'
compileOnly 'com.github.angeschossen:LandsAPI:6.29.12'
compileOnly 'com.intellectualsites.plotsquared:plotsquared-core:7.0.0-rc.4'
compileOnly 'com.intellectualsites.plotsquared:plotsquared-bukkit:7.0.0-rc.4'
implementation platform('com.intellectualsites.bom:bom-newest:1.35')
implementation files('../libs/AxAPI-1.0-SNAPSHOT.jar')
}

View File

@ -22,6 +22,8 @@ import com.artillexstudios.axminions.listeners.MinionPlaceListener
import com.artillexstudios.axminions.minions.MinionTicker
import com.artillexstudios.axminions.minions.miniontype.CollectorMinionType
import com.artillexstudios.axminions.minions.miniontype.FarmerMinionType
import com.artillexstudios.axminions.minions.miniontype.LumberMinionType
import com.artillexstudios.axminions.minions.miniontype.MinerMinionType
import org.bukkit.Bukkit
import java.io.File
@ -47,12 +49,13 @@ class AxMinionsPlugin : AxPlugin() {
override fun load() {
INSTANCE = this
AxMinionsAPI.INSTANCE = AxMinionsAPIImpl(this)
integrations = Integrations()
}
override fun enable() {
AxMinionsPlugin.config = Config(File(dataFolder, "config.yml"), getResource("config.yml")!!)
messages = Messages(File(dataFolder, "messages.yml"), getResource("messages.yml")!!)
integrations = Integrations()
integrations.reload()
loadDataHandler()
dataQueue = ThreadedQueue("AxMinions-Database-Queue")
@ -60,6 +63,8 @@ class AxMinionsPlugin : AxPlugin() {
MinionTypes.also {
it.register(CollectorMinionType())
it.register(FarmerMinionType())
it.register(MinerMinionType())
it.register(LumberMinionType())
}
val handler = BukkitCommandHandler.create(this)

View File

@ -14,7 +14,7 @@ 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 com.artillexstudios.axminions.api.utils.fastFor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
@ -50,6 +50,10 @@ class AxMinionsCommand {
it.value.getConfig().reload()
}
AxMinionsAPI.INSTANCE.getMinions().fastFor {
it.markDirty()
}
sender.sendMessage(
StringUtils.formatToString(
Messages.PREFIX() + Messages.RELOAD_SUCCESS(),

View File

@ -16,6 +16,11 @@ import com.artillexstudios.axminions.integrations.prices.CMIIntegration
import com.artillexstudios.axminions.integrations.prices.EconomyShopGUIIntegration
import com.artillexstudios.axminions.integrations.prices.EssentialsIntegration
import com.artillexstudios.axminions.integrations.prices.ShopGUIPlusIntegration
import com.artillexstudios.axminions.integrations.protection.BentoBoxIntegration
import com.artillexstudios.axminions.integrations.protection.GriefPreventionIntegration
import com.artillexstudios.axminions.integrations.protection.LandsIntegration
import com.artillexstudios.axminions.integrations.protection.SuperiorSkyBlock2Integration
import com.artillexstudios.axminions.integrations.protection.WorldGuardIntegration
import com.artillexstudios.axminions.integrations.stacker.DefaultStackerIntegration
import com.artillexstudios.axminions.integrations.stacker.RoseStackerIntegration
import com.artillexstudios.axminions.integrations.stacker.WildStackerIntegration
@ -26,7 +31,7 @@ class Integrations : Integrations {
private lateinit var stackerIntegration: StackerIntegration
private lateinit var pricesIntegration: PricesIntegration
private lateinit var economyIntegration: EconomyIntegration
private lateinit var protectionIntegrations: ProtectionIntegrations
private val protectionIntegrations = com.artillexstudios.axminions.integrations.protection.ProtectionIntegrations()
override fun getStackerIntegration(): StackerIntegration {
return stackerIntegration
@ -107,7 +112,27 @@ class Integrations : Integrations {
}
}
protectionIntegrations.clear()
if (Bukkit.getPluginManager().getPlugin("SuperiorSkyblock2") != null) {
register(SuperiorSkyBlock2Integration())
}
if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) {
register(WorldGuardIntegration())
}
if (Bukkit.getPluginManager().getPlugin("BentoBox") != null) {
register(BentoBoxIntegration())
}
if (Bukkit.getPluginManager().getPlugin("GriefPrevention") != null) {
register(GriefPreventionIntegration())
}
if (Bukkit.getPluginManager().getPlugin("Lands") != null) {
register(LandsIntegration())
}
}
override fun register(integration: Integration) {
@ -117,7 +142,7 @@ class Integrations : Integrations {
}
is ProtectionIntegration -> {
// Hook into protection
protectionIntegrations.register(integration)
}
is EconomyIntegration -> {
@ -132,6 +157,7 @@ class Integrations : Integrations {
throw InvalidIntegrationException("There is no builtin integration that the following class extends: ${integration::class.java}")
}
}
integration.register()
}
private fun isPluginLoaded(pluginName: String): Boolean {
@ -147,6 +173,14 @@ class Integrations : Integrations {
}
override fun deregister(integration: Integration) {
TODO("Not yet implemented")
when (integration) {
is ProtectionIntegration -> {
protectionIntegrations.deregister(integration)
}
else -> {
throw InvalidIntegrationException("You can only unregister a protection integration!")
}
}
}
}

View File

@ -4,6 +4,7 @@ import com.artillexstudios.axminions.api.integrations.types.EconomyIntegration
import org.bukkit.entity.Player
class PlayerPointsIntegration : EconomyIntegration {
override fun getBalance(player: Player): Double {
TODO("Not yet implemented")
}

View File

@ -1,22 +1,28 @@
package com.artillexstudios.axminions.integrations.economy
import com.artillexstudios.axminions.api.integrations.types.EconomyIntegration
import net.milkbowl.vault.economy.Economy
import org.bukkit.Bukkit
import org.bukkit.entity.Player
class VaultIntegration : EconomyIntegration {
private lateinit var economy: Economy
override fun getBalance(player: Player): Double {
TODO("Not yet implemented")
return economy.getBalance(player)
}
override fun giveBalance(player: Player, amount: Double) {
TODO("Not yet implemented")
economy.depositPlayer(player, amount)
}
override fun takeBalance(player: Player, amount: Double) {
TODO("Not yet implemented")
economy.withdrawPlayer(player, amount)
}
override fun register() {
TODO("Not yet implemented")
val rsp = Bukkit.getServicesManager().getRegistration(Economy::class.java) ?: return
economy = rsp.provider
}
}

View File

@ -4,6 +4,7 @@ import com.artillexstudios.axminions.api.integrations.types.PricesIntegration
import org.bukkit.inventory.ItemStack
class CMIIntegration : PricesIntegration {
override fun getPrice(itemStack: ItemStack): Double {
TODO("Not yet implemented")
}

View File

@ -1,14 +1,17 @@
package com.artillexstudios.axminions.integrations.prices
import com.artillexstudios.axminions.api.integrations.types.PricesIntegration
import me.gypopo.economyshopgui.api.EconomyShopGUIHook
import org.bukkit.inventory.ItemStack
class EconomyShopGUIIntegration : PricesIntegration {
override fun getPrice(itemStack: ItemStack): Double {
TODO("Not yet implemented")
val item = EconomyShopGUIHook.getShopItem(itemStack) ?: return 0.0
return EconomyShopGUIHook.getItemSellPrice(item, itemStack) ?: 0.0
}
override fun register() {
TODO("Not yet implemented")
}
}

View File

@ -1,14 +1,18 @@
package com.artillexstudios.axminions.integrations.prices
import com.artillexstudios.axminions.api.integrations.types.PricesIntegration
import com.earth2me.essentials.IEssentials
import org.bukkit.Bukkit
import org.bukkit.inventory.ItemStack
class EssentialsIntegration : PricesIntegration {
private lateinit var manager: IEssentials;
override fun getPrice(itemStack: ItemStack): Double {
TODO("Not yet implemented")
return manager.worth.getPrice(manager, itemStack).toDouble() * itemStack.amount
}
override fun register() {
TODO("Not yet implemented")
manager = Bukkit.getPluginManager().getPlugin("Essentials") as IEssentials
}
}

View File

@ -1,15 +1,16 @@
package com.artillexstudios.axminions.integrations.prices
import com.artillexstudios.axminions.api.integrations.types.PricesIntegration
import net.brcdev.shopgui.ShopGuiPlusApi
import org.bukkit.inventory.ItemStack
class ShopGUIPlusIntegration : PricesIntegration {
override fun getPrice(itemStack: ItemStack): Double {
TODO("Not yet implemented")
return ShopGuiPlusApi.getItemStackPriceSell(itemStack)
}
override fun register() {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,21 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import org.bukkit.Location
import org.bukkit.entity.Player
import world.bentobox.bentobox.BentoBox
class BentoBoxIntegration : ProtectionIntegration {
override fun canBuildAt(player: Player, location: Location): Boolean {
val island = BentoBox.getInstance().islands.getIslandAt(location)
return island.map {
player.uniqueId in it.memberSet
}.orElse(true)
}
override fun register() {
}
}

View File

@ -0,0 +1,17 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import me.ryanhamshire.GriefPrevention.GriefPrevention
import org.bukkit.Location
import org.bukkit.entity.Player
class GriefPreventionIntegration : ProtectionIntegration {
override fun canBuildAt(player: Player, location: Location): Boolean {
return GriefPrevention.instance.allowBuild(player, location) == null
}
override fun register() {
}
}

View File

@ -0,0 +1,22 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import me.angeschossen.lands.api.LandsIntegration
import me.angeschossen.lands.api.flags.type.Flags
import org.bukkit.Location
import org.bukkit.entity.Player
class LandsIntegration : ProtectionIntegration {
override fun canBuildAt(player: Player, location: Location): Boolean {
val api = LandsIntegration.of(AxMinionsPlugin.INSTANCE)
val world = api.getWorld(location.world ?: return true) ?: return true
return world.hasRoleFlag(player.uniqueId, location, Flags.BLOCK_PLACE);
}
override fun register() {
}
}

View File

@ -0,0 +1,38 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegrations
import com.artillexstudios.axminions.api.utils.fastFor
import java.util.Collections
import org.bukkit.Location
import org.bukkit.entity.Player
class ProtectionIntegrations : ProtectionIntegrations {
private val integrations = arrayListOf<ProtectionIntegration>()
override fun getProtectionIntegrations(): List<ProtectionIntegration> {
return Collections.unmodifiableList(integrations)
}
override fun canBuildAt(player: Player, location: Location): Boolean {
integrations.fastFor {
if (!it.canBuildAt(player, location)) {
return false
}
}
return true
}
fun clear() {
integrations.clear()
}
fun register(integration: ProtectionIntegration) {
integrations.add(integration)
}
fun deregister(integration: ProtectionIntegration) {
integrations.remove(integration)
}
}

View File

@ -0,0 +1,21 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import com.bgsoftware.superiorskyblock.api.SuperiorSkyblockAPI
import org.bukkit.Location
import org.bukkit.entity.Player
class SuperiorSkyBlock2Integration : ProtectionIntegration {
override fun canBuildAt(player: Player, location: Location): Boolean {
val localPlayer = SuperiorSkyblockAPI.getPlayer(player.uniqueId)
val island = SuperiorSkyblockAPI.getIslandAt(location) ?: return true
return island.isMember(localPlayer)
}
override fun register() {
}
}

View File

@ -0,0 +1,30 @@
package com.artillexstudios.axminions.integrations.protection
import com.artillexstudios.axminions.api.integrations.types.ProtectionIntegration
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldguard.WorldGuard
import com.sk89q.worldguard.bukkit.WorldGuardPlugin
import com.sk89q.worldguard.protection.flags.Flags
import org.bukkit.Location
import org.bukkit.entity.Player
class WorldGuardIntegration : ProtectionIntegration {
override fun canBuildAt(player: Player, location: Location): Boolean {
val localPlayer = WorldGuardPlugin.inst().wrapPlayer(player)
val world = BukkitAdapter.adapt(player.world)
if (WorldGuard.getInstance().platform.sessionManager.hasBypass(localPlayer, world)) {
return true
}
val container = WorldGuard.getInstance().platform.regionContainer
val query = container.createQuery()
return query.testState(BukkitAdapter.adapt(location), localPlayer, Flags.BUILD)
}
override fun register() {
}
}

View File

@ -1,25 +1,28 @@
package com.artillexstudios.axminions.integrations.stacker
import com.artillexstudios.axminions.api.integrations.types.StackerIntegration
import dev.rosewood.rosestacker.api.RoseStackerAPI
import org.bukkit.Location
import org.bukkit.entity.Item
import org.bukkit.entity.LivingEntity
import org.bukkit.inventory.ItemStack
class RoseStackerIntegration : StackerIntegration {
lateinit var instance: RoseStackerAPI
override fun getStackSize(entity: LivingEntity): Long {
TODO("Not yet implemented")
return instance.getStackedEntity(entity)?.stackSize?.toLong() ?: 1
}
override fun getStackSize(item: Item): Long {
TODO("Not yet implemented")
return instance.getStackedItem(item)?.stackSize?.toLong() ?: 1
}
override fun dropItemAt(itemStack: ItemStack, amount: Int, location: Location) {
TODO("Not yet implemented")
instance.dropItemStack(itemStack, amount, location, false)
}
override fun register() {
TODO("Not yet implemented")
instance = RoseStackerAPI.getInstance()
}
}

View File

@ -1,6 +1,7 @@
package com.artillexstudios.axminions.integrations.stacker
import com.artillexstudios.axminions.api.integrations.types.StackerIntegration
import com.bgsoftware.wildstacker.api.WildStackerAPI
import org.bukkit.Location
import org.bukkit.entity.Item
import org.bukkit.entity.LivingEntity
@ -9,18 +10,18 @@ import org.bukkit.inventory.ItemStack
class WildStackerIntegration : StackerIntegration {
override fun getStackSize(entity: LivingEntity): Long {
TODO("Not yet implemented")
return WildStackerAPI.getStackedEntity(entity)?.stackAmount?.toLong() ?: 1
}
override fun getStackSize(item: Item): Long {
TODO("Not yet implemented")
return WildStackerAPI.getStackedItem(item)?.stackAmount?.toLong() ?: 1
}
override fun dropItemAt(itemStack: ItemStack, amount: Int, location: Location) {
TODO("Not yet implemented")
WildStackerAPI.getWildStacker().systemManager.spawnItemWithAmount(location, itemStack, amount);
}
override fun register() {
TODO("Not yet implemented")
}
}

View File

@ -5,8 +5,11 @@ 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 com.artillexstudios.axminions.api.utils.CoolDown
import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor
import java.util.UUID
import java.util.concurrent.TimeUnit
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
@ -18,6 +21,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
class MinionInventoryListener : Listener {
private val coolDown = CoolDown<UUID>()
@EventHandler
fun onInventoryDragEvent(event: InventoryDragEvent) {
@ -33,6 +37,12 @@ class MinionInventoryListener : Listener {
event.isCancelled = true
val player = event.whoClicked as Player
if (coolDown.contains(player.uniqueId)) {
return
}
coolDown.add(player.uniqueId, 250)
val allowedTools = arrayListOf<Material>()
minion.getType().getConfig().getStringList("tool.material").fastFor {
allowedTools.add(Material.matchMaterial(it) ?: return@fastFor)
@ -48,6 +58,7 @@ class MinionInventoryListener : Listener {
val current = event.currentItem!!.clone()
val tool = minion.getTool()?.clone()
minion.setTool(current)
minion.updateArmour()
event.currentItem!!.amount = 0
event.clickedInventory!!.addItem(tool)
} else {
@ -68,8 +79,9 @@ class MinionInventoryListener : Listener {
val tool = minion.getTool()?.clone() ?: return
minion.setTool(ItemStack(Material.AIR))
minion.updateArmour()
val toolMeta = tool.itemMeta ?: return
toolMeta.persistentDataContainer.remove(MinionTypes.getGuiKey())
toolMeta.persistentDataContainer.remove(Keys.GUI)
tool.setItemMeta(toolMeta)
player.inventory.addItem(tool)
@ -82,8 +94,8 @@ class MinionInventoryListener : Listener {
}
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)
if (!meta.persistentDataContainer.has(Keys.GUI, PersistentDataType.STRING)) return
val type = meta.persistentDataContainer.get(Keys.GUI, PersistentDataType.STRING)
when (type) {
"rotate" -> {

View File

@ -1,13 +1,16 @@
package com.artillexstudios.axminions.listeners
import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import com.artillexstudios.axapi.scheduler.Scheduler
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.Direction
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.minions.Minion
import com.artillexstudios.axminions.minions.Minions
import org.bukkit.Material
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
@ -25,10 +28,13 @@ class MinionPlaceListener : Listener {
if (event.item == null) return
if (!event.item!!.hasItemMeta()) return
val type = event.item!!.itemMeta!!.persistentDataContainer.get(MinionTypes.getMinionKey(), PersistentDataType.STRING) ?: return
val type = event.item!!.itemMeta!!.persistentDataContainer.get(Keys.MINION_TYPE, PersistentDataType.STRING) ?: return
val minionType = MinionTypes.valueOf(type) ?: return
event.isCancelled = true
val level = event.item!!.itemMeta!!.persistentDataContainer.get(Keys.LEVEL, PersistentDataType.INTEGER) ?: 0
val stats = event.item!!.itemMeta!!.persistentDataContainer.get(Keys.STATISTICS, PersistentDataType.LONG) ?: 0
val location = event.clickedBlock!!.getRelative(event.blockFace).location
val maxMinions = AxMinionsAPI.INSTANCE.getMinionLimit(event.player)
@ -46,9 +52,16 @@ class MinionPlaceListener : Listener {
return@submit
}
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)
val locationId = AxMinionsPlugin.dataHandler.getLocationID(location)
Scheduler.get().run {
val minion = Minion(location, event.player.uniqueId, event.player, minionType, 1, ItemStack(Material.AIR), null, Direction.NORTH, 0, 0.0, locationId, 0)
minion.setLevel(level)
minion.setActions(stats)
minion.setTicking(true)
Minions.addTicking(location.chunk)
event.player.sendMessage("Placed minion $minion. Ticking? ${minion.isTicking()} Is chunk ticking? ${Minions.isTicking(location.chunk)}")
AxMinionsPlugin.dataHandler.saveMinion(minion)
}
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,6 +4,7 @@ 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.hologram.HologramFactory
import com.artillexstudios.axapi.libs.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import com.artillexstudios.axapi.scheduler.Scheduler
import com.artillexstudios.axapi.serializers.Serializers
@ -18,23 +19,25 @@ 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.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor
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 java.util.UUID
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.OfflinePlayer
import org.bukkit.block.Container
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.EntityType
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.util.EulerAngle
import java.util.UUID
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.inventory.meta.Damageable
import org.bukkit.persistence.PersistentDataType
import org.bukkit.util.EulerAngle
class Minion(
private var location: Location,
@ -61,6 +64,7 @@ class Minion(
private var linkedInventory: Inventory? = null
private val openInventories = mutableListOf<Inventory>()
private var ticking = false
private var debugHologram: Hologram? = null
init {
spawn()
@ -79,6 +83,18 @@ class Minion(
entity.setHasBasePlate(false)
entity.setSmall(true)
entity.setHasArms(true)
entity.name = StringUtils.format(
type.getConfig().get("entity.name"),
Placeholder.unparsed("owner", owner.name ?: "???"),
Placeholder.unparsed("level", level.toString()),
Placeholder.parsed("level_color", Messages.LEVEL_COLOR(level))
)
if (Config.DEBUG()) {
debugHologram = HologramFactory.get().spawnHologram(location.clone().add(0.0, 2.0, 0.0), "$locationID")
debugHologram?.addLine(StringUtils.format("ticking: $ticking"))
}
setDirection(direction)
updateArmour()
entity.onClick { event ->
@ -98,6 +114,10 @@ class Minion(
type.onToolDirty(this)
}
if (Config.DEBUG() && debugHologram != null) {
debugHologram?.setLine(0, StringUtils.format("Ticking: $ticking Chunk ticking: ${Minions.isTicking(location.chunk)} CHUNK X: ${location.chunk.x} CHUNK Z: ${location.chunk.z}"))
}
type.tick(this)
animate()
}
@ -118,10 +138,12 @@ class Minion(
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)) {
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))
@ -129,22 +151,45 @@ class Minion(
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 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()
item = ItemBuilder(
type.getConfig().getSection("gui.$it"),
level,
nextLevel,
range,
nextRange,
extra,
nextExtra,
speed,
nextSpeed,
price,
requiredActions,
stored,
actions
).storePersistentData(
Keys.GUI, PersistentDataType.STRING, it
).get()
} else if (it.equals("item")) {
item = tool?.clone() ?: ItemStack(Material.AIR)
} else {
val rotation = Placeholder.unparsed("direction", Messages.ROTATION_NAME(direction))
val linked = Placeholder.unparsed("linked", when (linkedChest) {
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()
}
)
item = ItemBuilder(
AxMinionsAPI.INSTANCE.getConfig().getConfig().getSection("gui.items.$it"),
rotation,
linked
).storePersistentData(
Keys.GUI, PersistentDataType.STRING, it
).get()
}
inventory.setItem(AxMinionsAPI.INSTANCE.getConfig().get("gui.items.$it.slot"), item)
@ -153,10 +198,19 @@ class Minion(
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 inventory = Bukkit.createInventory(
this,
Config.GUI_SIZE(),
StringUtils.formatToString(
type.getConfig().get("name"),
Placeholder.parsed("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()) {
for (i in 0..<Config.GUI_SIZE()) {
inventory.setItem(i, filler)
}
@ -166,7 +220,7 @@ class Minion(
}
override fun getAsItem(): ItemStack {
return type.getItem(level)
return type.getItem(level, actions)
}
override fun getLevel(): Int {
@ -207,7 +261,7 @@ class Minion(
override fun setTool(tool: ItemStack) {
this.tool = tool.clone()
updateInventories()
dirty = true
if (tool.type == Material.AIR) {
entity.setItem(EquipmentSlot.MAIN_HAND, null)
@ -230,6 +284,17 @@ class Minion(
override fun setLevel(level: Int) {
this.level = level
entity.name = StringUtils.format(
type.getConfig().get("entity.name"),
Placeholder.unparsed("owner", owner.name ?: "???"),
Placeholder.unparsed("level", level.toString()),
Placeholder.parsed("level_color", Messages.LEVEL_COLOR(level))
)
AxMinionsPlugin.dataQueue.submit {
AxMinionsPlugin.dataHandler.saveMinion(this)
}
}
override fun getData(key: String): String? {
@ -306,10 +371,15 @@ class Minion(
}
override fun addToContainerOrDrop(itemStack: ItemStack) {
if (linkedInventory == null) {
AxMinionsPlugin.integrations.getStackerIntegration().dropItemAt(itemStack, itemStack.amount, location)
return
}
val remaining = linkedInventory?.addItem(itemStack)
remaining?.forEach { (_, u) ->
location.world?.dropItem(location, u)
AxMinionsPlugin.integrations.getStackerIntegration().dropItemAt(u, u.amount, location)
}
}
@ -371,6 +441,46 @@ class Minion(
this.nextAction = nextAction
}
override fun markDirty() {
dirty = true
}
override fun damageTool(amount: Int) {
val meta = this.tool?.itemMeta as? Damageable ?: return
if (Math.random() > 1f / (meta.getEnchantLevel(Enchantment.DURABILITY) + 1)) return
if ((tool?.type?.maxDurability ?: return) <= meta.damage + amount) {
if (Config.CAN_BREAK_TOOLS()) {
setTool(ItemStack(Material.AIR))
}
} else {
meta.damage += amount
tool?.itemMeta = meta
}
}
override fun canUseTool(): Boolean {
val meta = this.tool?.itemMeta ?: return false
if (!Config.USE_DURABILITY() && meta is Damageable) {
return true
}
if (meta is Damageable) {
if ((tool?.type?.maxDurability ?: return false) <= meta.damage + 1) {
if (Config.CAN_BREAK_TOOLS()) {
setTool(ItemStack(Material.AIR))
}
return false
} else {
return true
}
}
return false
}
override fun getInventory(): Inventory {
return Bukkit.createInventory(this, 9)
}

View File

@ -1,7 +1,7 @@
package com.artillexstudios.axminions.minions
import com.artillexstudios.axapi.scheduler.Scheduler
import com.artillexstudios.axminions.utils.fastFor
import com.artillexstudios.axminions.api.utils.fastFor
object MinionTicker {
private var tick = 0L

View File

@ -2,78 +2,71 @@ package com.artillexstudios.axminions.minions
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.utils.ChunkPos
import com.artillexstudios.axminions.utils.fastFor
import com.artillexstudios.axminions.api.utils.fastFor
import java.util.Collections
import java.util.concurrent.ConcurrentHashMap
import org.bukkit.Chunk
object Minions {
private val mutex = Object()
private val minions = hashMapOf<ChunkPos, ArrayList<Minion>>()
private val mutableChunkPos = ChunkPos(0, 0)
private val minions = ConcurrentHashMap<ChunkPos, ArrayList<Minion>>()
fun addTicking(chunk: Chunk) {
synchronized(mutex) {
mutableChunkPos.x = chunk.x
mutableChunkPos.z = chunk.z
val pos = ChunkPos(chunk.x, chunk.z)
minions[mutableChunkPos]?.fastFor {
minions[pos]?.fastFor {
it.setTicking(true)
} ?: return
println("LOADING CHUNK! X: ${chunk.x} Z: ${chunk.z}")
}
fun isTicking(chunk: Chunk): Boolean {
val pos = ChunkPos(chunk.x, chunk.z)
return minions.contains(pos)
}
fun removeTicking(chunk: Chunk) {
synchronized(mutex) {
mutableChunkPos.x = chunk.x
mutableChunkPos.z = chunk.z
val pos = ChunkPos(chunk.x, chunk.z)
val minions = this.minions[mutableChunkPos] ?: return
val minions = this.minions[pos] ?: return
minions.fastFor {
it.setTicking(false)
}
}
}
fun load(minion: Minion) {
synchronized(mutex) {
mutableChunkPos.x = round(minion.getLocation().x) shr 4
mutableChunkPos.z = round(minion.getLocation().z) shr 4
val pos = minions[mutableChunkPos] ?: arrayListOf()
println("LOADING MINION!!")
val chunkPos = ChunkPos(minion.getLocation().chunk.x, minion.getLocation().chunk.z)
val pos = minions[chunkPos] ?: arrayListOf()
pos.add(minion)
minions[mutableChunkPos] = pos
}
minions[chunkPos] = pos
}
fun remove(minion: Minion) {
synchronized(mutex) {
mutableChunkPos.x = minion.getLocation().blockX shr 4
mutableChunkPos.z = minion.getLocation().blockZ shr 4
val pos = minions[mutableChunkPos] ?: return
val chunkPos = ChunkPos(minion.getLocation().chunk.x, minion.getLocation().chunk.z)
val pos = minions[chunkPos] ?: return
pos.remove(minion)
if (pos.isEmpty()) {
minions.remove(mutableChunkPos)
}
minions.remove(chunkPos)
}
}
fun getMinions(): List<Minion> {
synchronized(mutex) {
val list = mutableListOf<Minion>()
minions.forEach { (_, value) ->
list.addAll(value)
}
return Collections.unmodifiableList(list)
}
}
internal fun get(): HashMap<ChunkPos, ArrayList<Minion>> {
synchronized(mutex) {
internal fun get(): ConcurrentHashMap<ChunkPos, ArrayList<Minion>> {
return minions
}
}
private infix fun round(double: Double): Int {
return (double + 0.5).toInt()

View File

@ -24,7 +24,6 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
}
override fun run(minion: Minion) {
minion.resetAnimation()
if (minion.getLinkedChest() == null) {
Warnings.NO_CONTAINER.display(minion)
return
@ -45,6 +44,13 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
Warnings.remove(minion)
if (!minion.canUseTool()) {
Warnings.NO_TOOL.display(minion)
return
}
Warnings.remove(minion)
val entities = minion.getLocation().world?.getNearbyEntities(
minion.getLocation(),
minion.getRange(),
@ -58,7 +64,14 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
return
}
//TODO: Add to inventory and remove items
val amount = AxMinionsPlugin.integrations.getStackerIntegration().getStackSize(item)
val stack = item.itemStack.clone()
stack.amount = amount.toInt()
minion.addToContainerOrDrop(stack)
minion.setActions(minion.getActionAmount() + 1)
minion.damageTool()
item.remove()
}
}
}

View File

@ -4,8 +4,10 @@ import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.minions.MinionTicker
import com.artillexstudios.axminions.utils.LocationUtils
import com.artillexstudios.axminions.utils.fastFor
import com.artillexstudios.axminions.api.utils.LocationUtils
import com.artillexstudios.axminions.api.utils.MinionUtils
import com.artillexstudios.axminions.api.utils.fastFor
import com.artillexstudios.axminions.api.warnings.Warnings
import kotlin.math.roundToInt
import org.bukkit.Material
import org.bukkit.block.data.Ageable
@ -26,16 +28,41 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
}
override fun run(minion: Minion) {
minion.resetAnimation()
if (minion.getLinkedChest() != null) {
val type = minion.getLinkedChest()!!.block.type
if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL) {
minion.setLinkedChest(null)
}
}
if (minion.getLinkedInventory() == null) {
minion.setLinkedChest(null)
}
if (!minion.canUseTool()) {
Warnings.NO_TOOL.display(minion)
return
}
Warnings.remove(minion)
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 -> {
Material.CACTUS, Material.SUGAR_CANE, Material.BAMBOO -> {
MinionUtils.getPlant(block).fastFor {
drops.addAll(it.getDrops(minion.getTool()))
it.type = Material.AIR
}
}
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
@ -43,6 +70,7 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
ageable.age = 0
block.blockData = ageable
}
Material.SWEET_BERRY_BUSH -> {
val ageable = block.blockData as Ageable
if (ageable.age != ageable.maximumAge) return@fastFor
@ -50,10 +78,14 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
ageable.age = 1
block.blockData = ageable
}
else -> return@fastFor
}
minion.addToContainerOrDrop(drops)
val dropsSize = drops.size
minion.damageTool(dropsSize)
minion.setActions(minion.getActionAmount() + dropsSize)
}
}
}

View File

@ -3,10 +3,92 @@ package com.artillexstudios.axminions.minions.miniontype
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.utils.LocationUtils
import com.artillexstudios.axminions.api.utils.MinionUtils
import com.artillexstudios.axminions.api.utils.fastFor
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.minions.MinionTicker
import kotlin.math.roundToInt
import org.bukkit.Material
import org.bukkit.block.BlockFace
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
class LumberMinionType : MinionType("lumber", AxMinionsPlugin.INSTANCE.getResource("minions/lumber.yml")!!) {
override fun run(minion: Minion) {
override fun shouldRun(minion: Minion): Boolean {
return MinionTicker.getTick() % minion.getNextAction() == 0L
}
override fun onToolDirty(minion: Minion) {
val minionImpl = minion as com.artillexstudios.axminions.minions.Minion
minionImpl.setRange(getDouble("range", minion.getLevel()))
val efficiency = 1.0 - (minion.getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1)
minionImpl.setNextAction((getLong("speed", minion.getLevel()) * efficiency).roundToInt())
}
override fun run(minion: Minion) {
if (minion.getLinkedChest() != null) {
val type = minion.getLinkedChest()!!.block.type
if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL) {
minion.setLinkedChest(null)
}
}
if (minion.getLinkedInventory() == null) {
minion.setLinkedChest(null)
}
if (!minion.canUseTool()) {
Warnings.NO_TOOL.display(minion)
return
}
Warnings.remove(minion)
val loot = ArrayList<ItemStack>()
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
MinionUtils.getTree(location.block).fastFor {
val down = it.getRelative(BlockFace.DOWN).type
loot.addAll(it.getDrops(minion.getTool()))
if (down == Material.DIRT || down == Material.GRASS_BLOCK || down == Material.COARSE_DIRT || down == Material.ROOTED_DIRT || down == Material.DIRT_PATH) {
it.type = getSaplingType(it.type)
} else {
it.type = Material.AIR
}
}
}
val lootSize = loot.size
minion.damageTool(lootSize)
minion.setActions(minion.getActionAmount() + lootSize)
minion.addToContainerOrDrop(loot)
}
private fun getSaplingType(material: Material): Material {
return when (material) {
Material.DARK_OAK_LOG -> {
Material.DARK_OAK_SAPLING
}
Material.BIRCH_LOG -> {
Material.BIRCH_SAPLING
}
Material.ACACIA_LOG -> {
Material.ACACIA_SAPLING
}
Material.JUNGLE_LOG -> {
Material.JUNGLE_SAPLING
}
Material.SPRUCE_LOG -> {
Material.SPRUCE_SAPLING
}
else -> Material.OAK_SAPLING
}
}
}

View File

@ -1,12 +1,110 @@
package com.artillexstudios.axminions.minions.miniontype
import com.artillexstudios.axapi.scheduler.Scheduler
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.utils.LocationUtils
import com.artillexstudios.axminions.api.utils.MinionUtils
import com.artillexstudios.axminions.api.utils.fastFor
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.minions.MinionTicker
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.math.roundToInt
import org.bukkit.Material
import org.bukkit.block.BlockFace
import org.bukkit.enchantments.Enchantment
class MinerMinionType : MinionType("miner", AxMinionsPlugin.INSTANCE.getResource("minions/miner.yml")!!) {
private var asyncExecutor: ExecutorService? = null
private val faces = arrayOf(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST)
override fun shouldRun(minion: Minion): Boolean {
return MinionTicker.getTick() % minion.getNextAction() == 0L
}
override fun onToolDirty(minion: Minion) {
val minionImpl = minion as com.artillexstudios.axminions.minions.Minion
minionImpl.setRange(getDouble("range", minion.getLevel()))
val efficiency = 1.0 - (minion.getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1)
minionImpl.setNextAction((getLong("speed", minion.getLevel()) * efficiency).roundToInt())
}
override fun run(minion: Minion) {
if (minion.getLinkedChest() != null) {
val type = minion.getLinkedChest()!!.block.type
if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL) {
minion.setLinkedChest(null)
}
}
if (minion.getLinkedInventory() == null) {
minion.setLinkedChest(null)
}
if (!minion.canUseTool()) {
Warnings.NO_TOOL.display(minion)
return
}
Warnings.remove(minion)
when (getConfig().getString("mode").lowercase(Locale.ENGLISH)) {
"sphere" -> {
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val isStoneGenerator = MinionUtils.isStoneGenerator(location)
if (isStoneGenerator) {
minion.addToContainerOrDrop(location.block.getDrops(minion.getTool()))
location.block.type = Material.AIR
}
}
}
"asphere" -> {
if (asyncExecutor == null) {
asyncExecutor = Executors.newFixedThreadPool(3)
}
asyncExecutor!!.execute {
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val isStoneGenerator = MinionUtils.isStoneGenerator(location)
if (isStoneGenerator) {
Scheduler.get().run {
minion.addToContainerOrDrop(location.block.getDrops(minion.getTool()))
location.block.type = Material.AIR
}
}
}
}
}
"line" -> {
faces.fastFor {
LocationUtils.getAllBlocksFacing(minion.getLocation(), minion.getRange(), it).fastFor { location ->
val isStoneGenerator = MinionUtils.isStoneGenerator(location)
if (isStoneGenerator) {
minion.addToContainerOrDrop(location.block.getDrops(minion.getTool()))
location.block.type = Material.AIR
}
}
}
}
"face" -> {
LocationUtils.getAllBlocksFacing(minion.getLocation(), minion.getRange(), minion.getDirection().facing).fastFor { location ->
val isStoneGenerator = MinionUtils.isStoneGenerator(location)
if (isStoneGenerator) {
minion.addToContainerOrDrop(location.block.getDrops(minion.getTool()))
location.block.type = Material.AIR
}
}
}
}
}
}

View File

@ -77,5 +77,9 @@ gui:
statistics:
slot: 16
# 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: 1

View File

@ -0,0 +1,138 @@
name: "<#00AAFF>Lumberjack <white>Minion"
entity:
name: "<level_color>[<level>] <#00AAFF>Lumberjack <white>Minion <gray>[<owner>]"
tool:
material:
- "WOODEN_AXE"
- "STONE_AXE"
- "GOLDEN_AXE"
- "IRON_AXE"
- "DIAMOND_AXE"
- "NETHERITE_AXE"
item:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzk2MjdiZTYyY2VkNzE0MTEzOWQzZjE1NTc5MGE1ZDQzNTZlYjdiOWVlOTVlNTA0YjMzMjI5NzRjYmM1MTVlYSJ9fX0="
name: "<#00AAFF>Lumberjack <white>Minion"
lore:
- ""
- " <gray>- <white>Collects all items around itself."
- ""
- "<#00AAFF>Statistics"
- " <#00AAFF>❙ <white>Level: <#FFEEAA><level>"
- " <#00AAFF>❙ <white>Broken blocks: <#FFEEAA><actions>"
- ""
- "<#00AAFF><b>(!)</b> Place the minion and give it an axe!"
gui:
upgrade:
type: "gold_ingot"
name: "<#00CCFF><b>Upgrade minion"
lore:
- ""
- " <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>$"
- " <gray>- <white>Collected items: <#33FF33><required_actions>"
- ""
- "<#00CCFF><b>(!)</b> Click here to upgrade your minion!"
statistics:
type: "paper"
name: "<#00AAFF><b>Statistics"
lore:
- ""
- " <gray>- <white>Broken blocks: <#00AAFF><actions>"
- ""
upgrades:
1:
range: 3
speed: 200
items:
helmet:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzk2MjdiZTYyY2VkNzE0MTEzOWQzZjE1NTc5MGE1ZDQzNTZlYjdiOWVlOTVlNTA0YjMzMjI5NzRjYmM1MTVlYSJ9fX0="
chestplate:
type: "leather_chestplate"
color: "0, 150, 255"
glow: false
leggings:
type: "leather_leggings"
color: "0, 150, 255"
glow: false
boots:
type: "leather_boots"
color: "0, 150, 255"
glow: false
2:
range: 4
speed: 190
requirements:
money: 1000
actions: 100
3:
range: 5
speed: 175
requirements:
money: 3000
actions: 300
4:
range: 6
speed: 160
requirements:
money: 10000
actions: 1000
5:
range: 7
speed: 150
requirements:
money: 20000
actions: 3000
6:
range: 8
speed: 130
requirements:
money: 50000
actions: 6000
7:
range: 9
speed: 115
requirements:
money: 150000
actions: 10000
8:
range: 10
speed: 100
requirements:
money: 500000
actions: 17500
9:
range: 11
speed: 85
requirements:
money: 1000000
actions: 25000
10:
range: 12
speed: 60
requirements:
money: 5000000
actions: 50000
items:
chestplate:
type: LEATHER_CHESTPLATE
color: "255, 200, 0"
glow: true
leggings:
type: LEATHER_LEGGINGS
color: "255, 200, 0"
glow: true
boots:
type: LEATHER_BOOTS
color: "255, 200, 0"
glow: true

View File

@ -0,0 +1,147 @@
name: "<#00AAFF>Miner <white>Minion"
entity:
name: "<level_color>[<level>] <#00AAFF>Miner <white>Minion <gray>[<owner>]"
tool:
material:
- "WOODEN_PICKAXE"
- "STONE_PICKAXE"
- "GOLDEN_PICKAXE"
- "IRON_PICKAXE"
- "DIAMOND_PICKAXE"
- "NETHERITE_PICKAXE"
# Can be: sphere, line, face, asphere
# If set to sphere: checks blocks in a sphere with the range of the minion as radius (Most resource intensive, the larger the radius, the worse)
# If set to line: checks blocks in all 4 directions from the minion in the range of the minion
# If set to face: checks blocks only in the facing of the minion (Least resource intensive)
# If set to asphere: checks blocks in a sphere with the range of the minion as the radius, around the minion
# Only switch to asphere mode, if you know what you're doing,
# as it may cause some issues due to states being concurrently modified!
mode: line
item:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzk2MjdiZTYyY2VkNzE0MTEzOWQzZjE1NTc5MGE1ZDQzNTZlYjdiOWVlOTVlNTA0YjMzMjI5NzRjYmM1MTVlYSJ9fX0="
name: "<#00AAFF>Miner <white>Minion"
lore:
- ""
- " <gray>- <white>Collects all items around itself."
- ""
- "<#00AAFF>Statistics"
- " <#00AAFF>❙ <white>Level: <#FFEEAA><level>"
- " <#00AAFF>❙ <white>Broken blocks: <#FFEEAA><actions>"
- ""
- "<#00AAFF><b>(!)</b> Place the minion and give it a pickaxe!"
gui:
upgrade:
type: "gold_ingot"
name: "<#00CCFF><b>Upgrade minion"
lore:
- ""
- " <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>$"
- " <gray>- <white>Collected items: <#33FF33><required_actions>"
- ""
- "<#00CCFF><b>(!)</b> Click here to upgrade your minion!"
statistics:
type: "paper"
name: "<#00AAFF><b>Statistics"
lore:
- ""
- " <gray>- <white>Broken blocks: <#00AAFF><actions>"
- ""
upgrades:
1:
range: 3
speed: 200
items:
helmet:
type: "player_head"
texture: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzk2MjdiZTYyY2VkNzE0MTEzOWQzZjE1NTc5MGE1ZDQzNTZlYjdiOWVlOTVlNTA0YjMzMjI5NzRjYmM1MTVlYSJ9fX0="
chestplate:
type: "leather_chestplate"
color: "0, 150, 255"
glow: false
leggings:
type: "leather_leggings"
color: "0, 150, 255"
glow: false
boots:
type: "leather_boots"
color: "0, 150, 255"
glow: false
2:
range: 4
speed: 190
requirements:
money: 1000
actions: 100
3:
range: 5
speed: 175
requirements:
money: 3000
actions: 300
4:
range: 6
speed: 160
requirements:
money: 10000
actions: 1000
5:
range: 7
speed: 150
requirements:
money: 20000
actions: 3000
6:
range: 8
speed: 130
requirements:
money: 50000
actions: 6000
7:
range: 9
speed: 115
requirements:
money: 150000
actions: 10000
8:
range: 10
speed: 100
requirements:
money: 500000
actions: 17500
9:
range: 11
speed: 85
requirements:
money: 1000000
actions: 25000
10:
range: 12
speed: 60
requirements:
money: 5000000
actions: 50000
items:
chestplate:
type: LEATHER_CHESTPLATE
color: "255, 200, 0"
glow: true
leggings:
type: LEATHER_LEGGINGS
color: "255, 200, 0"
glow: true
boots:
type: LEATHER_BOOTS
color: "255, 200, 0"
glow: true

View File

@ -2,3 +2,6 @@ name: "AxMinions"
main: "com.artillexstudios.axminions.AxMinionsPlugin"
version: "1.0"
api-version: 1.19
softdepend:
- RoseStacker
- WildStacker