Compare commits
2 commits
a074ba56ae
...
2d9e35312b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d9e35312b | |||
| 20541c744e |
9 changed files with 174 additions and 13 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package ch.offbeatwit.bestbuds
|
||||
|
||||
import ch.offbeatwit.bestbuds.managers.Context
|
||||
import ch.offbeatwit.bestbuds.modules.AdoptionModule
|
||||
import ch.offbeatwit.bestbuds.modules.AntiGriefModule
|
||||
import ch.offbeatwit.bestbuds.modules.CommandsModule
|
||||
import ch.offbeatwit.bestbuds.modules.InteractionModule
|
||||
|
|
@ -16,6 +17,7 @@ class BestBudsPlugin : JavaPlugin() {
|
|||
moduleLoader.load(::InteractionModule)
|
||||
moduleLoader.load(::CommandsModule)
|
||||
moduleLoader.load(::AntiGriefModule)
|
||||
moduleLoader.load(::AdoptionModule)
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
|
|
|
|||
6
src/main/kotlin/ch/offbeatwit/bestbuds/DataKeys.kt
Normal file
6
src/main/kotlin/ch/offbeatwit/bestbuds/DataKeys.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package ch.offbeatwit.bestbuds
|
||||
|
||||
import org.bukkit.NamespacedKey
|
||||
|
||||
val ADOPTION_TARGET = NamespacedKey("bestbuds", "adoption_target")
|
||||
val IS_ADOPTABLE = NamespacedKey("bestbuds", "is_adoptable")
|
||||
106
src/main/kotlin/ch/offbeatwit/bestbuds/modules/AdoptionModule.kt
Normal file
106
src/main/kotlin/ch/offbeatwit/bestbuds/modules/AdoptionModule.kt
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package ch.offbeatwit.bestbuds.modules
|
||||
|
||||
import ch.offbeatwit.bestbuds.ADOPTION_TARGET
|
||||
import ch.offbeatwit.bestbuds.isOwnerOf
|
||||
import ch.offbeatwit.bestbuds.managers.Context
|
||||
import ch.offbeatwit.bestbuds.pet.PetInfo
|
||||
import ch.offbeatwit.bestbuds.util.UUIDDataType
|
||||
import ch.offbeatwit.bestbuds.util.error
|
||||
import ch.offbeatwit.bestbuds.util.tr
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.Component.text
|
||||
import net.kyori.adventure.text.JoinConfiguration
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.entity.Tameable
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent
|
||||
import org.bukkit.inventory.EquipmentSlot
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.BookMeta
|
||||
|
||||
class AdoptionModule(ctx: Context) : BestBudsModule(ctx), Listener {
|
||||
val adoptionItem = Material.PAPER
|
||||
|
||||
@EventHandler
|
||||
fun onInteract(ev: PlayerInteractEntityEvent) {
|
||||
val target = ev.rightClicked
|
||||
if (target !is Tameable || !target.isTamed) return
|
||||
val info = PetInfo(target)
|
||||
|
||||
val heldItem = ev.player.inventory.getItem(ev.hand)
|
||||
|
||||
if (ev.player.isSneaking && ev.player.isOwnerOf(target) && heldItem.type == adoptionItem) {
|
||||
ev.isCancelled = true
|
||||
setupAdoption(ev.player, ev.hand, target)
|
||||
} else if (heldItem.type == Material.WRITTEN_BOOK) {
|
||||
val bookTarget = heldItem.persistentDataContainer[ADOPTION_TARGET, UUIDDataType] ?: return
|
||||
ev.isCancelled = true
|
||||
|
||||
if (target.uniqueId == bookTarget && info.isAdoptable) {
|
||||
info.isAdoptable = false
|
||||
heldItem.subtract()
|
||||
|
||||
if (ev.player.isOwnerOf(target)) {
|
||||
ev.player.sendActionBar(tr(info.nameComponent, text("is no longer adoptable")))
|
||||
} else {
|
||||
target.owner = ev.player
|
||||
|
||||
ev.player.sendActionBar(
|
||||
tr(
|
||||
text("Adopted ")
|
||||
.append(info.nameComponent)
|
||||
.append(text("! Take good care of them!"))
|
||||
)
|
||||
)
|
||||
}
|
||||
} else if (target.uniqueId == bookTarget) {
|
||||
ev.player.sendActionBar(error("That pet is not currently adoptable"))
|
||||
} else {
|
||||
ev.player.sendActionBar(error("Those papers aren't for this pet!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setupAdoption(sender: Player, hand: EquipmentSlot, pet: Tameable) {
|
||||
val info = PetInfo(pet)
|
||||
val stack = sender.inventory.getItem(hand)
|
||||
if (stack.type != adoptionItem) return // sanity check
|
||||
if (stack.amount == 0) return
|
||||
|
||||
stack.subtract()
|
||||
|
||||
val adoptionPaper = ItemStack(Material.WRITTEN_BOOK, 1).apply {
|
||||
editMeta(BookMeta::class.java) {
|
||||
it.title(Component.join(JoinConfiguration.spaces(),
|
||||
text("Adoption papers for"), info.nameComponent))
|
||||
it.author(sender.displayName())
|
||||
|
||||
Component.join(JoinConfiguration.newlines(),
|
||||
text("Adopt ").append(info.nameComponent).append(text(" today!")),
|
||||
text(""),
|
||||
text("Right-click on ").append(info.nameComponent)
|
||||
.append(text(" with this book to adopt them."))
|
||||
).apply { it.addPages(this) }
|
||||
}
|
||||
|
||||
editPersistentDataContainer {
|
||||
it.set(ADOPTION_TARGET, UUIDDataType, pet.uniqueId)
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.isEmpty) {
|
||||
sender.inventory.setItem(hand, adoptionPaper)
|
||||
} else {
|
||||
val res = sender.inventory.addItem(adoptionPaper)
|
||||
if (res.isNotEmpty()) {
|
||||
sender.world.dropItem(pet.location, adoptionPaper)
|
||||
}
|
||||
}
|
||||
|
||||
info.isAdoptable = true
|
||||
|
||||
sender.sendActionBar(tr("Set pet as adoptable!"))
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,8 @@ class CommandsModule(ctx: Context) : BestBudsModule(ctx), CommandRegistrar {
|
|||
val root = literal("bestbuds")
|
||||
.then(transfer())
|
||||
.then(release())
|
||||
.then(allow())
|
||||
.then(disallow())
|
||||
.then(trust())
|
||||
.then(untrust())
|
||||
.executes(BestBudsHelpTopic)
|
||||
.build()
|
||||
|
||||
|
|
@ -78,8 +78,8 @@ class CommandsModule(ctx: Context) : BestBudsModule(ctx), CommandRegistrar {
|
|||
}
|
||||
}
|
||||
|
||||
fun allow(): CommandNode<CommandSourceStack> {
|
||||
return literal("allow")
|
||||
fun trust(): CommandNode<CommandSourceStack> {
|
||||
return literal("trust")
|
||||
.then(argument("player", ArgumentTypes.playerProfiles())
|
||||
.executes { ctx ->
|
||||
val sender = ctx.source.sender
|
||||
|
|
@ -95,7 +95,7 @@ class CommandsModule(ctx: Context) : BestBudsModule(ctx), CommandRegistrar {
|
|||
if (this.ctx.whitelistManager.addToWhitelist(sender, target)) {
|
||||
sender.sendMessage(tr("${target.name} can now interact with your pets"))
|
||||
} else {
|
||||
sender.sendMessage(error("${target.name} is already on the allowlist"))
|
||||
sender.sendMessage(error("${target.name} is already trusted"))
|
||||
}
|
||||
|
||||
Command.SINGLE_SUCCESS
|
||||
|
|
@ -103,8 +103,8 @@ class CommandsModule(ctx: Context) : BestBudsModule(ctx), CommandRegistrar {
|
|||
.build()
|
||||
}
|
||||
|
||||
fun disallow(): CommandNode<CommandSourceStack> {
|
||||
return literal("disallow")
|
||||
fun untrust(): CommandNode<CommandSourceStack> {
|
||||
return literal("untrust")
|
||||
.then(argument("player", ArgumentTypes.playerProfiles())
|
||||
.executes { ctx ->
|
||||
val sender = ctx.source.sender
|
||||
|
|
@ -120,7 +120,7 @@ class CommandsModule(ctx: Context) : BestBudsModule(ctx), CommandRegistrar {
|
|||
if (this.ctx.whitelistManager.removeFromWhitelist(sender, target)) {
|
||||
sender.sendMessage(tr("${target.name} can no longer interact with your pets"))
|
||||
} else {
|
||||
sender.sendMessage(error("${target.name} isn't on the allowlist"))
|
||||
sender.sendMessage(error("${target.name} isn't trusted"))
|
||||
}
|
||||
|
||||
Command.SINGLE_SUCCESS
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ object BestBudsHelpTopic : HelpTopic(), Command<CommandSourceStack> {
|
|||
val commands = listOf(
|
||||
CommandHelp("/bb transfer <player>", "Change the owner of a pet to another player"),
|
||||
CommandHelp("/bb release", "Un-tame a pet, releasing them back into the wild"),
|
||||
CommandHelp("/bb allow <player>", "Allow another player to ride, leash, hurt and rename your pets"),
|
||||
CommandHelp("/bb disallow <player>", "Remove a player from your allowlist"),
|
||||
CommandHelp("/bb trust <player>", "Allow another player to ride, leash, hurt and rename your pets"),
|
||||
CommandHelp("/bb untrust <player>", "Remove a player from your trusted list"),
|
||||
)
|
||||
|
||||
return Component.join(JoinConfiguration.newlines(), commands.map {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import org.bukkit.entity.ChestedHorse
|
|||
import org.bukkit.entity.Player
|
||||
import org.bukkit.entity.Tameable
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent
|
||||
import org.bukkit.util.Transformation
|
||||
|
|
@ -23,8 +24,9 @@ import org.joml.Vector3f
|
|||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class InteractionModule(ctx: Context) : BestBudsModule(ctx), Listener {
|
||||
@EventHandler
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
fun onClickPet(ev: PlayerInteractEntityEvent) {
|
||||
if (ev.isCancelled) return
|
||||
if (!ev.player.isSneaking) return
|
||||
|
||||
val target = ev.rightClicked
|
||||
|
|
@ -54,11 +56,13 @@ class InteractionModule(ctx: Context) : BestBudsModule(ctx), Listener {
|
|||
?.also { it.completeFromCache() }
|
||||
?.let { text(it.name ?: "unknown", NamedTextColor.GOLD) }
|
||||
?: text("nobody", NamedTextColor.RED)
|
||||
|
||||
val component = Component.join(
|
||||
JoinConfiguration.newlines(),
|
||||
info.nameComponent,
|
||||
text("Owned by ").append(owner),
|
||||
text("Health: ").append(info.healthComponent)
|
||||
text("Health: ").append(info.healthComponent),
|
||||
*(if (info.isAdoptable) arrayOf(text("Adoptable").color(NamedTextColor.GREEN)) else emptyArray())
|
||||
).style(INFO_TEXT_STYLE)
|
||||
|
||||
ctx.hologramManager.showSingle(this, pet.location, component) {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,15 @@ class InfoDialog(val pet: Tameable) : Supplier<DialogLike> {
|
|||
@Suppress("UnstableApiUsage")
|
||||
override fun get(): DialogLike {
|
||||
return Dialog.create { builder ->
|
||||
val body = listOf(
|
||||
val body = mutableListOf(
|
||||
DialogBody.plainMessage(text("Type: ").append(text(pet.type.name, NamedTextColor.GOLD))),
|
||||
DialogBody.plainMessage(text("Health: ").append(info.healthComponent)),
|
||||
)
|
||||
|
||||
if (info.isAdoptable) {
|
||||
body.add(DialogBody.plainMessage(text("Adoptable!", NamedTextColor.GREEN)))
|
||||
}
|
||||
|
||||
val base = DialogBase.builder(text("Managing ").append(pet.customName() ?: text(pet.name)))
|
||||
.body(body)
|
||||
.canCloseWithEscape(true)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package ch.offbeatwit.bestbuds.pet
|
||||
|
||||
import ch.offbeatwit.bestbuds.IS_ADOPTABLE
|
||||
import ch.offbeatwit.bestbuds.util.PET_NAME_STYLE
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.JoinConfiguration
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import org.bukkit.attribute.Attribute
|
||||
import org.bukkit.entity.Tameable
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
|
||||
class PetInfo(val pet: Tameable) {
|
||||
val nameComponent get() = (pet.customName() ?: Component.text(pet.name)).style(PET_NAME_STYLE)
|
||||
|
|
@ -21,4 +23,8 @@ class PetInfo(val pet: Tameable) {
|
|||
Component.text(pet.health, healthColor), Component.text(maxHealth?.toString() ?: "???", healthColor)
|
||||
)
|
||||
}
|
||||
|
||||
var isAdoptable
|
||||
get() = pet.persistentDataContainer[IS_ADOPTABLE, PersistentDataType.BOOLEAN] ?: false
|
||||
set(value) = pet.persistentDataContainer.set(IS_ADOPTABLE, PersistentDataType.BOOLEAN, value)
|
||||
}
|
||||
|
|
|
|||
32
src/main/kotlin/ch/offbeatwit/bestbuds/util/UUIDDataType.kt
Normal file
32
src/main/kotlin/ch/offbeatwit/bestbuds/util/UUIDDataType.kt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package ch.offbeatwit.bestbuds.util
|
||||
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
import java.lang.Long
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import kotlin.ByteArray
|
||||
|
||||
object UUIDDataType : PersistentDataType<ByteArray, UUID> {
|
||||
override fun getPrimitiveType(): Class<ByteArray> {
|
||||
return ByteArray::class.java
|
||||
}
|
||||
|
||||
override fun getComplexType(): Class<UUID> {
|
||||
return UUID::class.java
|
||||
}
|
||||
|
||||
override fun toPrimitive(complex: UUID, context: PersistentDataAdapterContext): ByteArray {
|
||||
val bb = ByteBuffer.allocate(Long.BYTES * 2)
|
||||
bb.putLong(complex.mostSignificantBits)
|
||||
bb.putLong(complex.leastSignificantBits)
|
||||
return bb.array()
|
||||
}
|
||||
|
||||
override fun fromPrimitive(primitive: ByteArray, context: PersistentDataAdapterContext): UUID {
|
||||
val bb = ByteBuffer.wrap(primitive)
|
||||
val msb = bb.getLong()
|
||||
val lsb = bb.getLong()
|
||||
return UUID(msb, lsb)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue