Compare commits

..

7 Commits

Author SHA1 Message Date
1e9a797143 更新 taboolib 版本。 2024-11-08 18:38:44 +08:00
00c0e138f3 更新自述文件。 2024-11-08 18:18:33 +08:00
771e16ff43 修复 lifetime 不生效的 bug。修复指令补全内容错误的问题。 2024-11-08 18:13:18 +08:00
ec0230794e 防止你看不到 2024-11-06 23:25:53 +08:00
a8a7a8fb8b 修复了模式为 ONCE 的聊天气泡会在进入玩家可视范围内的时播放一次的问题。 2024-11-06 23:20:21 +08:00
03137e6115 大规模重构修改弹出机制,优化性能。
新的配置项目: rest-delay。
时间相关皆可设置随机时间。
优化游戏内聊天泡泡的编辑体验。
无破坏性修改。
2024-11-06 23:02:11 +08:00
f763c8d53c 更新文档。 2024-11-01 22:49:54 +08:00
10 changed files with 401 additions and 145 deletions

View File

@ -1,20 +1,52 @@
# AdyBubbles
```text
___ __ ____ __ __ __
/ | ____/ /_ __/ __ )__ __/ /_ / /_ / /__ _____
/ /| |/ __ / / / / __ / / / / __ \/ __ \/ / _ \/ ___/
/ ___ / /_/ / /_/ / /_/ / /_/ / /_/ / /_/ / / __(__ )
/_/ |_\__,_/\__, /_____/\__,_/_.___/_.___/_/\___/____/
/____/
```
版本: 2.5.2
## 指令
- `bubbles popup <NPC_ID> <TEXT>` - 用于弹出泡泡
- `bubbles popup <NPC_ID> <TEXT> [LIFE]` - 用于弹出泡泡
- `bubbles clear <NPC_ID>` - 用于清除NPC的泡泡
- `bubbles-chat edit <NPC_ID>` - 用于编辑NPC顺序弹出的泡泡
- `bubbles-chat play <NPC_UUID>` - 用于播放NPC模式为ONCE弹出的泡泡
## 编辑NPC顺序弹出的泡泡
## 游戏内编辑NPC的对话泡泡 (推荐)
使用指令 `bubbles-chat edit <NPC_ID>`
使用指令 bubbles-chat edit <NPC_ID> 并根据提示操作。
获得书本后, 可以在书本中编辑 NPC 的顺序弹出的泡泡
## 通过文件编辑NPC的对话泡泡 (不推荐)
一行为一句
本插件的对话泡泡以 Adyeshach 的 Trait 形式存在,详见 特征。
## 配置
Adyeshach 的特征保存为 yaml 存在于 `~/Adyeshach/npc/traits` 下。
本插件的对话泡泡特征文件名为 bubbles-chat。
则需要编辑 `~/Adyeshach/npc/traits/bubbles-chat.yml` 内的内容。
Example:
```yaml
44cb049b-c385-4654-b52f-cdc5a09dfb66: # NPC UUID
mode: loop # 弹出模式。
items: # 对话内容队列,每一个元素为一条。
- 你好
- 再见
period: 60 # 泡泡弹出间隔时间,可以是随机区间(单位:Tick) 可选参数,不填则跟随全局设置
rest-delay: 0 # 泡泡弹出每轮后的等待时长(区间同理) 可选参数,不填则跟随全局设置
# 模式:
# loop 只要NPC在玩家视野内就循环队列播放
# random 只要NPC在玩家视野内就一直随机队列播放
# once 仅当指令触发时播放一次
```
## 全局配置
Example:
@ -22,8 +54,12 @@ Example:
offset: 0.5 # 泡泡的偏移量(向上)
line-height: 0.5 # 泡泡的间距(向上)
chat:
period: 60 # 泡泡弹出间隔时间
period: 60 # 泡泡弹出间隔时间,可以是随机区间(单位:Tick)
# period: [60, 100] # 三秒到五秒内随机时长
rest-delay: 0 # 泡泡弹出每轮后的等待时长(区间同理)
# rest-delay: [0, 100] # 泡泡弹出一轮后等待随机时长后再开启新一轮
limit: 2 # 泡泡弹出数量限制(超出会自动清楚最早的泡泡)
lifetime: 20 # 泡泡时间限制(超出会自动清除泡泡)
```
## 构建发行版本

View File

@ -1,20 +1,33 @@
import io.izzel.taboolib.gradle.BUKKIT
import io.izzel.taboolib.gradle.BUKKIT_ALL
import io.izzel.taboolib.gradle.UNIVERSAL
import io.izzel.taboolib.gradle.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
java
id("io.izzel.taboolib") version "2.0.11"
id("io.izzel.taboolib") version "2.0.18"
id("org.jetbrains.kotlin.jvm") version "1.8.22"
}
taboolib {
env {
// 安装模块
install(UNIVERSAL, BUKKIT_ALL)
install(Basic)
install(Bukkit)
install(BukkitUtil)
install(BukkitNMS)
install(BukkitNMSUtil)
}
version { taboolib = "6.1.2-beta10" }
description {
name("AdyBubbles")
desc("为Ady实体显示气泡")
contributors {
name("俗手")
}
dependencies {
name("Adyeshach")
}
load("POSTWORLD")
}
version { taboolib = "6.2.0-beta33" }
}
repositories {

View File

@ -1,5 +1,5 @@
group=io.github.beradq.adybubbles
version=2.0.0-SNAPSHOT
version=2.5.2
kotlin.incremental=true
kotlin.incremental.java=true
kotlin.caching.enabled=true

View File

@ -1,14 +1,40 @@
package io.github.beradq.adybubbles
import ink.ptms.adyeshach.core.Adyeshach
import ink.ptms.adyeshach.core.AdyeshachAPI
import taboolib.common.platform.Plugin
import taboolib.common.platform.function.info
import taboolib.common.platform.function.submit
// ___ __ ____ __ __ __
// / | ____/ /_ __/ __ )__ __/ /_ / /_ / /__ _____
// / /| |/ __ / / / / __ / / / / __ \/ __ \/ / _ \/ ___/
// / ___ / /_/ / /_/ / /_/ / /_/ / /_/ / /_/ / / __(__ )
// /_/ |_\__,_/\__, /_____/\__,_/_.___/_.___/_/\___/____/
// /____/
// 作者: 俗手
object AdyBubbles : Plugin() {
val adyApi = Adyeshach.api()
private const val VERSION_NAME = "2.5.2"
val adyApi: AdyeshachAPI = Adyeshach.api()
val hologramHandler = adyApi.getHologramHandler()
val entityFinder = adyApi.getEntityFinder()
override fun onEnable() {
info("Successfully running AdyBubbles!")
info("AdyBubbles 已启用!")
}
override fun onActive() {
submit(delay = 600L) {
info("\u001B[36m" + " ___ __ ____ __ __ __ " + "\u001B[0m")
info("\u001B[36m" + " / | ____/ /_ __/ __ )__ __/ /_ / /_ / /__ _____" + "\u001B[0m")
info("\u001B[36m" + " / /| |/ __ / / / / __ / / / / __ \\/ __ \\/ / _ \\/ ___/" + "\u001B[0m")
info("\u001B[36m" + " / ___ / /_/ / /_/ / /_/ / /_/ / /_/ / /_/ / / __(__ ) " + "\u001B[0m")
info("\u001B[36m" + "/_/ |_\\__,_/\\__, /_____/\\__,_/_.___/_.___/_/\\___/____/ " + "\u001B[0m")
info("\u001B[36m" + " /____/ " + "\u001B[0m")
info("\u001B[34mAdyBubbles \u001B[33m$VERSION_NAME\u001B[32m 已加载!\u001B[0m")
this.cancel()
}
}
}

View File

@ -1,37 +1,78 @@
package io.github.beradq.adybubbles
class Bubbles(val items: MutableList<String>, val mode: ChatPopupMode = ChatPopupMode.LOOP) : Cloneable {
private val state = BubbleState(0)
class Bubbles(val items: MutableList<String>, val config: BubbleConfig) : Cloneable {
private val state = BubbleState(0, if (config.mode == ChatPopupMode.ONCE) -1L else 0)
private var restDelay = config.restDelayRange.random()
private var period = config.period.random()
operator fun get(index: Int): String {
return items[index]
}
class BubbleState(
data class BubbleConfig(
val mode: ChatPopupMode,
val _restDelayRange: LongRange? = null,
val _period: LongRange? = null
) {
val period: LongRange get() = _period ?: Config.bubblesConfig.chat.period
val restDelayRange: LongRange get() = _restDelayRange ?: Config.bubblesConfig.chat.restDelay
}
data class BubbleState(
var index: Int,
var timeCount: Long
) : Cloneable
fun next(): String? {
if (config.mode == ChatPopupMode.RANDOM) return items.random()
if (items.isEmpty()) return null
val item = items[state.index]
state.index++
if (state.index >= items.size && mode == ChatPopupMode.LOOP) {
if (state.index >= items.size && config.mode == ChatPopupMode.LOOP) {
state.index = 0
}
return item
}
fun resetState() {
state.index = 0
fun tick(uuid: String) {
if (state.timeCount == -1L) return
state.timeCount += 1
if (state.timeCount == period) {
popup(uuid, next() ?: return)
}
if (state.timeCount >= period) {
if (state.index != 0 || config.mode != ChatPopupMode.LOOP) {
state.timeCount = if (config.mode == ChatPopupMode.ONCE) -1 else 0
period = config.period.random()
}
if (state.timeCount >= period + restDelay) {
state.timeCount = 0
period = config.period.random()
restDelay = config.restDelayRange.random()
}
}
}
fun random(): String {
return items.random()
private fun popup(uuid: String, text: String) {
if (Config.bubblesConfig.chat.lifetime != null) {
BubblesBundle.popupTick(uuid, text, Config.bubblesConfig.chat.lifetime!!)
} else {
BubblesBundle.popup(uuid, text)
}
BubblesBundle.limit(uuid, Config.bubblesConfig.chat.limit)
}
fun resetState() {
state.index = 0
state.timeCount = 0
restDelay = config.restDelayRange.random()
period = config.period.random()
}
public override fun clone(): Bubbles {
val bubbles = Bubbles(items.toMutableList(), mode)
val bubbles = Bubbles(items.toMutableList(), config.copy())
bubbles.state.index = state.index
bubbles.state.timeCount = state.timeCount
return bubbles
}
}

View File

@ -7,44 +7,94 @@ import taboolib.common.platform.command.CommandHeader
import taboolib.common.platform.command.subCommand
import io.github.beradq.adybubbles.AdyBubbles.entityFinder
import taboolib.common.platform.ProxyCommandSender
import taboolib.common.platform.command.bool
import taboolib.common.platform.function.adaptCommandSender
import taboolib.common.platform.function.adaptPlayer
import taboolib.module.chat.component
import taboolib.module.nms.*
private fun clearMessageScreen(player: ProxyCommandSender) {
for (i in 1..40) player.sendMessage(" ")
for (i in 1..5) player.sendMessage(" ")
}
fun sendEditMessage(player: ProxyCommandSender, uuid: String, title: String) {
private fun edd(uuid: String, cmd: String): String {
return "/bubbles-chat editdata true $uuid $cmd"
}
private fun String.replaceBracketForSafety(): String {
return this.replace("[", "\\[").replace("]", "\\]")
}
private fun sendEditMessage(player: ProxyCommandSender, uuid: String, title: String) {
clearMessageScreen(player)
val bubbles = TraitBubblesChat.getBubbles(uuid) ?: Bubbles(mutableListOf(), ChatPopupMode.LOOP)
val bubbles = TraitBubblesChat.getOrCreateBubbles(uuid)
player.sendMessage("当前正在编辑: $title 的弹出泡泡")
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage("弹出模式(点击切换):")
when (bubbles.mode) {
"[§b\\[ 弹出内容 \\]§f](cmd=${edd(uuid, "items")})".component().sendTo(player)
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage("弹出模式:")
when (bubbles.config.mode) {
ChatPopupMode.LOOP ->
"[\\[循环\\]](b;u) [\\[随机\\]](cmd=/bubbles-chat editdata $uuid mode random) [\\[单次\\]](cmd=/bubbles-chat editdata $uuid mode once)"
"[§a\\[循环\\]§f](b;u) [§b\\[随机\\]§f](cmd=${edd(uuid, "mode random")}) [§b\\[单次\\]§f](cmd=${
edd(
uuid,
"mode once"
)
})"
.component().sendTo(player)
ChatPopupMode.RANDOM ->
"[\\[循环\\]](cmd=/bubbles-chat editdata $uuid mode loop) [\\[随机\\]](b;u) [\\[单次\\]](cmd=/bubbles-chat editdata $uuid mode once)"
"[§b\\[循环\\]§f](cmd=${edd(uuid, "mode loop")}) [§a\\[随机\\]§f](b;u) [§b\\[单次\\]§f](cmd=${
edd(
uuid,
"mode once"
)
})"
.component().sendTo(player)
ChatPopupMode.ONCE ->
"[\\[循环\\]](cmd=/bubbles-chat editdata $uuid mode loop) [\\[随机\\]](cmd=/bubbles-chat editdata $uuid mode random) [\\[单次\\]](b;u)"
"[§b\\[循环\\]§f](cmd=${edd(uuid, "mode loop")}) [§b\\[随机\\]§f](cmd=${
edd(
uuid,
"mode random"
)
}) [§a\\[单次\\]§f](b;u)"
.component().sendTo(player)
}
player.sendMessage(" ")
"[\\[弹出内容\\](点击编辑)](cmd=/bubbles-chat editdata $uuid items)".component().sendTo(player)
player.sendMessage(" ")
if (bubbles.config._period != null) {
"弹出间隔: [§b\\[ ${bubbles.config._period.toMString().replaceBracketForSafety()} \\]§f](cmd=${
edd(
uuid,
"period"
)
})"
.component().sendTo(player)
} else {
"弹出间隔: [§b\\[ 默认值 \\]§f](cmd=${edd(uuid, "period")})"
.component().sendTo(player)
}
if (bubbles.config._restDelayRange != null) {
"每轮间隔: [§b\\[ ${bubbles.config._restDelayRange.toMString().replaceBracketForSafety()} \\]§f](cmd=${
edd(
uuid,
"rest-delay"
)
})"
.component().sendTo(player)
} else {
"每轮间隔: [§b\\[ 默认值 \\]§f](cmd=${edd(uuid, "rest-delay")})"
.component().sendTo(player)
}
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage("蓝色选项可点击编辑")
player.sendMessage("请展开聊天栏编辑")
player.sendMessage(" ")
player.sendMessage(" ")
player.sendMessage(" ")
}
@CommandHeader("bubbles-chat")
@ -55,9 +105,7 @@ object BubblesChatCommand {
@Suppress("unused")
val play = subCommand {
dynamic("UUID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
entityFinder.getEntities().map { it.uniqueId }
}
suggestEntityListUnique()
execute<CommandSender> { _, context, _ ->
val uuid = context["UUID"]
TraitBubblesChat.startOnce(uuid)
@ -68,40 +116,87 @@ object BubblesChatCommand {
@CommandBody
@Suppress("unused")
val editdata = subCommand {
dynamic("UUID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
entityFinder.getEntities().map { it.uniqueId }
}
dynamic("FIELD") {
suggestion<CommandSender>(uncheck = false) { _, _ ->
listOf("items", "mode")
}
execute<CommandSender> { sender, context, _ ->
val field = context["FIELD"]
if (field != "items") {
sender.sendMessage("缺少参数")
return@execute
bool("RECALL") {
dynamic("UUID") {
suggestEntityListUnique()
dynamic("FIELD") {
suggestion<CommandSender>(uncheck = false) { _, _ ->
listOf("items", "mode", "period", "rest-delay")
}
val uuid = context["UUID"]
val npc = entityFinder.getEntityFromUniqueId(uuid) ?: throw Exception("Entity not found")
TraitBubblesChat.edit(sender as Player, npc)
sendEditMessage(adaptCommandSender(sender), uuid, npc.id)
}
dynamic("VALUE") {
execute<CommandSender> { sender, context, _ ->
val field = context["FIELD"]
val value = context["VALUE"]
val recall = context.bool("RECALL")
val uuid = context["UUID"]
val npc = entityFinder.getEntityFromUniqueId(uuid) ?: throw Exception("Entity not found")
val bubbles = TraitBubblesChat.getOrCreateBubbles(uuid)
val npc =
entityFinder.getEntityFromUniqueId(uuid) ?: throw Exception("Entity not found")
when (field) {
"mode" -> {
val mode = ChatPopupMode.fromString(value)
TraitBubblesChat.setMode(uuid, mode)
sendEditMessage(adaptCommandSender(sender), uuid, npc.id)
"items" -> {
TraitBubblesChat.edit(sender as Player, npc)
sender.sendTitle("请编辑书", null, 1, 20, 1)
}
"period" -> {
(sender as Player).inputSign(
arrayOf(
bubbles.config._period?.toMString() ?: "",
"请在第一行输入数字或",
"区间代表范围内随机"
)
) {
val content = it.first().trim()
if (content.isEmpty()) {
TraitBubblesChat.setPeriod(uuid, null)
} else {
val range = content.toLongRange()
TraitBubblesChat.setPeriod(uuid, range)
}
if (recall) sendEditMessage(adaptCommandSender(sender), uuid, npc.id)
}
}
"rest-delay" -> {
(sender as Player).inputSign(
arrayOf(
bubbles.config._restDelayRange?.toMString() ?: "",
"请在第一行输入数字或",
"区间代表范围内随机"
)
) {
val content = it.first().trim()
if (content.isEmpty()) {
TraitBubblesChat.setRestDelay(uuid, null)
} else {
val range = content.toLongRange()
TraitBubblesChat.setRestDelay(uuid, range)
}
if (recall) sendEditMessage(adaptCommandSender(sender), uuid, npc.id)
}
}
else -> {
sender.sendMessage("Unknown field: $field")
sender.sendMessage("缺少参数")
return@execute
}
}
}
dynamic("VALUE") {
execute<CommandSender> { sender, context, _ ->
val field = context["FIELD"]
val recall = context.bool("RECALL")
val value = context["VALUE"]
val uuid = context["UUID"]
val npc = entityFinder.getEntityFromUniqueId(uuid) ?: throw Exception("Entity not found")
when (field) {
"mode" -> {
val mode = ChatPopupMode.fromString(value)
TraitBubblesChat.setMode(uuid, mode)
if (recall) sendEditMessage(adaptCommandSender(sender), uuid, npc.id)
}
else -> {
sender.sendMessage("Unknown field: $field")
}
}
}
}
@ -113,12 +208,10 @@ object BubblesChatCommand {
@CommandBody
@Suppress("unused")
val edit = subCommand {
dynamic("NPCID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
entityFinder.getEntities().map { it.id }
}
dynamic("NPC_ID") {
suggestEntityList()
execute<CommandSender> { sender, context, _ ->
val npcs = entityFinder.getEntitiesFromId(context["NPCID"])
val npcs = entityFinder.getEntitiesFromId(context["NPC_ID"])
if (npcs.size == 1) {
val uuid = npcs[0].uniqueId
sendEditMessage(adaptPlayer(sender as Player), uuid, npcs[0].id)
@ -138,9 +231,7 @@ object BubblesChatCommand {
@Suppress("unused")
val edit2 = subCommand {
dynamic("UUID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
entityFinder.getEntities().map { it.uniqueId }
}
suggestEntityListUnique()
execute<CommandSender> { sender, context, _ ->
val uuid = context["UUID"]
val npc = entityFinder.getEntityFromUniqueId(uuid)!!

View File

@ -4,6 +4,7 @@ import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import taboolib.common.platform.command.*
import taboolib.platform.util.*
import io.github.beradq.adybubbles.AdyBubbles.entityFinder
@CommandHeader("bubbles")
@Suppress("unused")
@ -12,9 +13,7 @@ object BubblesCommand {
@Suppress("unused")
val popup = subCommand {
dynamic("NPC_ID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
AdyBubbles.adyApi.getEntityFinder().getEntities().map { it.id }
}
suggestEntityList()
dynamic("TEXT") {
execute<CommandSender> { sender, context, _ ->
val npcId = context["NPC_ID"]
@ -67,8 +66,9 @@ object BubblesCommand {
@Suppress("unused")
val clear = subCommand {
dynamic("NPC_ID") {
suggestion<CommandSender>(uncheck = true) { _, _ ->
AdyBubbles.adyApi.getEntityFinder().getEntities().map { it.id }
suggestEntityList()
suggestion<CommandSender>(uncheck = true) { sender, _ ->
entityFinder.getEntities(sender as? Player) { it.isDerived() }.map { it.id }
}
execute<CommandSender> { sender, context, _ ->
val npcId = context["NPC_ID"]

View File

@ -11,7 +11,8 @@ object Config {
lateinit var bubblesConfig: BubblesConfig
class BubblesChatConfig(
val period: Long,
val period: LongRange,
val restDelay: LongRange,
val limit: Int,
val lifetime: Long?
)
@ -25,12 +26,13 @@ object Config {
@Awake(LifeCycle.ENABLE)
fun reload() {
configFile.reload()
val offset = configFile["offset"] as? Double ?: 0.5
val lineHeight = configFile["line-height"] as? Double ?: 0.5
val offset = configFile.getDouble("offset", .5)
val lineHeight = configFile.getDouble("line-height", 0.5)
val chat = configFile.getConfigurationSection("chat")
val period = chat?.get("period") as? Long ?: (20 * 3)
val limit = chat?.get("limit") as? Int ?: 3
val lifetime = chat?.get("lifetime") as? Long
bubblesConfig = BubblesConfig(offset, lineHeight, BubblesChatConfig(period, limit, lifetime))
val period = chat?.getString("period")?.toLongRange() ?: (20L * 3)..(20 * 3)
val limit = chat?.getInt("limit", 3) ?: 3
val restDelayRange = chat?.getString("rest-delay")?.toLongRange() ?: 0L..0
val lifetime = chat?.getLong("lifetime")
bubblesConfig = BubblesConfig(offset, lineHeight, BubblesChatConfig(period, restDelayRange, limit, lifetime))
}
}

View File

@ -4,49 +4,55 @@ import ink.ptms.adyeshach.core.entity.EntityInstance
import ink.ptms.adyeshach.core.event.AdyeshachEntityVisibleEvent
import ink.ptms.adyeshach.impl.entity.trait.Trait
import ink.ptms.adyeshach.impl.util.Inputs.inputBook
import taboolib.common.LifeCycle
import org.bukkit.entity.Player
import taboolib.common.platform.Awake
import taboolib.common.platform.Schedule
import taboolib.common.platform.event.EventPriority
import taboolib.common.platform.event.SubscribeEvent
import taboolib.common.platform.function.submit
import taboolib.common.platform.service.PlatformExecutor
import taboolib.module.chat.uncolored
import java.util.concurrent.CompletableFuture
object TraitBubblesChat : Trait() {
private val bubbles = mutableMapOf<String, Bubbles>()
private val visibleEntity = mutableListOf<String>()
private var handle: PlatformExecutor.PlatformTask? = null
fun getBubbles(uuid: String): Bubbles? {
val conf = data.getConfigurationSection(uuid) ?: return null
if (!data.contains(uuid)) return null
val conf = data.getConfigurationSection(uuid)!!
val items = conf.getStringList("items")
val mode = conf.getString("mode")?.let { ChatPopupMode.fromString(it) } ?: ChatPopupMode.LOOP
return Bubbles(items.toMutableList(), mode)
val period = conf.getString("period")?.toLongRange()
val restDelay = conf.getString("rest-delay")?.toLongRange()
return Bubbles(items.toMutableList(), Bubbles.BubbleConfig(mode, restDelay, period))
}
@Suppress("unused")
fun setBubbles(uuid: String, bubbles: Bubbles) {
data[uuid] = mapOf(
"items" to bubbles.items,
"mode" to bubbles.mode.toString()
"mode" to bubbles.config.mode.toString(),
"period" to bubbles.config._period?.toMString(),
"rest-delay" to bubbles.config._restDelayRange?.toMString()
)
uuid pipe this::update
}
fun getOrCreateBubbles(uuid: String): Bubbles {
val bubbles = bubbles.getOrPut(uuid) { Bubbles(mutableListOf(), Bubbles.BubbleConfig(ChatPopupMode.LOOP)) }
update(uuid)
return bubbles
}
override fun edit(player: Player, entityInstance: EntityInstance): CompletableFuture<Void> {
val future = CompletableFuture<Void>()
val uuid = entityInstance.uniqueId
val content = bubbles.getOrPut(uuid) { Bubbles(mutableListOf()) }.items
val content = getOrCreateBubbles(uuid).items
player.inputBook(content) {
(future::complete.bind(null))()
future.complete(null)
if (it.all { s -> s.isBlank() }) {
data[uuid] = null
uuid also bubbles::remove also BubblesBundle::clear
} else {
if (!data.contains(uuid)) data[uuid] = mapOf("items" to it.uncolored())
else data.getConfigurationSection(uuid)!!["items"] = it.uncolored()
getConfiguration(uuid)["items"] = it.uncolored()
}
uuid pipe this::update
}
@ -54,17 +60,27 @@ object TraitBubblesChat : Trait() {
}
fun setMode(uuid: String, mode: ChatPopupMode) {
if (!data.contains(uuid)) data[uuid] = mapOf("mode" to mode.toString())
else data.getConfigurationSection(uuid)!!["mode"] = mode.toString()
getConfiguration(uuid)["mode"] = mode.toString()
uuid pipe this::update
}
fun setPeriod(uuid: String, period: LongRange?) {
getConfiguration(uuid)["period"] = period?.toMString()
uuid pipe this::update
}
fun setRestDelay(uuid: String, restDelay: LongRange?) {
getConfiguration(uuid)["rest-delay"] = restDelay?.toMString()
uuid pipe this::update
}
fun getConfiguration(uuid: String): ink.ptms.adyeshach.taboolib.library.configuration.ConfigurationSection {
if (!data.contains(uuid)) data[uuid] = mapOf<String, String>()
return data.getConfigurationSection(uuid)!!
}
private fun update(uuid: String) {
if (!data.contains(uuid)) return
val innerData = data.getConfigurationSection(uuid)!!
val items = if (innerData.contains("items")) innerData.getStringList("items") else emptyList()
val mode = ChatPopupMode.fromString(innerData.getString("mode") ?: "loop")
bubbles[uuid] = Bubbles(items.toMutableList(), mode)
bubbles[uuid] = getBubbles(uuid) ?: return
}
private fun fistUpdate(uuid: String) {
@ -73,44 +89,18 @@ object TraitBubblesChat : Trait() {
}
@Awake(LifeCycle.ACTIVE)
private fun startTicking() {
handle?.cancel()
handle = submit(period = Config.bubblesConfig.chat.period, async = true) {
val ve = visibleEntity.toList()
ve.forEach {
val bubble = bubbles[it] ?: return@forEach
val item = when (bubble.mode) {
ChatPopupMode.LOOP -> {
bubble.next()
}
ChatPopupMode.RANDOM -> bubble.random()
else -> return@forEach
} ?: return@forEach
popup(it, item)
}
@Schedule(async = true, period = 1)
fun tick() {
for (uuid in visibleEntity.toList()) {
val bubble = bubbles[uuid] ?: continue
bubble.tick(uuid)
}
}
fun startOnce(uuid: String) {
val bubble = bubbles[uuid] ?: return
if (bubble.mode != ChatPopupMode.ONCE) return
if (bubble.config.mode != ChatPopupMode.ONCE) return
bubble.resetState()
val cloneBubble = bubble.clone()
submit(period = Config.bubblesConfig.chat.period, async = true) {
val item = cloneBubble.next() ?: return@submit this.cancel()
popup(uuid, item)
}
}
fun popup(uuid: String, text: String) {
if (Config.bubblesConfig.chat.lifetime != null) {
BubblesBundle.popupTick(uuid, text, Config.bubblesConfig.chat.lifetime!!)
} else {
BubblesBundle.popup(uuid, text)
}
BubblesBundle.limit(uuid, Config.bubblesConfig.chat.limit)
}
@SubscribeEvent(priority = EventPriority.MONITOR, ignoreCancelled = true)

View File

@ -1,18 +1,75 @@
package io.github.beradq.adybubbles
inline infix fun <T, R> T.pipe(func: (T) -> R): R {
import ink.ptms.adyeshach.module.command.Command
import io.github.beradq.adybubbles.AdyBubbles.entityFinder
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import taboolib.common.platform.command.component.CommandComponentDynamic
fun CommandComponentDynamic.suggestEntityList() {
suggestion<CommandSender>(uncheck = true) { sender, _ ->
Command.finder.getEntities(sender as? Player) { !it.isDerived() }.map { it.id }
}
}
fun CommandComponentDynamic.suggestEntityListUnique() {
suggestion<CommandSender>(uncheck = true) { sender, _ ->
entityFinder.getEntities(sender as? Player) { !it.isDerived() }.map { it.uniqueId }
}
}
inline infix fun <reified T, reified R> T.pipe(func: (T) -> R): R {
return func(this)
}
infix fun <T, R, S> T.pipe(func: Pair<(T) -> R, (T) -> S>): Pair<R, S> {
return func.first(this) to func.second(this)
}
inline infix fun <T, R> T.also(func: (T) -> R): T {
inline infix fun <reified T, reified R> T.also(func: (T) -> R): T {
func(this)
return this
}
infix fun <F : (T) -> R, T, R> F.bind(value: T): () -> R {
return { this(value) }
fun parseLongRange(range: String): LongRange? {
val rangeStr = range.trim()
val start: Long
val end: Long
val startInclusive: Boolean = when (rangeStr[0]) {
'[' -> true
'(' -> false
else -> {
// 如果只有一个数字,则返回该数字
return rangeStr.toLongOrNull()?.let { it..it }
}
}
val endInclusive: Boolean = when (rangeStr[rangeStr.length - 1]) {
']' -> true
')' -> false
else -> return null
}
val rangeParts = rangeStr.substring(1, rangeStr.length - 1).split(',').map { it.trim() }
if (rangeParts.size != 2) {
return null
}
start = rangeParts[0].toLongOrNull() ?: return null
end = rangeParts[1].toLongOrNull() ?: return null
return when {
startInclusive && endInclusive -> start..end
startInclusive && !endInclusive -> start until end
!startInclusive && endInclusive -> (start + 1)..end
else -> (start + 1) until end
}
}
fun String.toLongRange() = parseLongRange(this)
fun longRangeToString(range: LongRange): String {
val start = range.first
val end = range.last
if (start == end) return "$start"
return "[$start, $end]"
}
fun LongRange.toMString() = longRangeToString(this)