Skip to content

自定义条件读取组件

概述

条件读取组件用于从物品 Lore 中解析条件,并判断该条件是否满足。例如,插件自带的「等级需求」「权限需求」「装备类型」都属于条件读取。

核心任务: 判断「这一行的条件是否满足」。

基础结构

自定义条件读取组件需要:

  1. 继承 GroovyConditionReader
  2. 在构造函数中设置 nameprioritykey
  3. 定义 readFormat(正则匹配模式)
  4. 重写 read() 方法

构造函数参数

Groovy
GroovyConditionReader(String name, int priority, String key)
参数说明示例
name读取器名称,会作为 read.yml 中的配置键"health"
priority优先级,数字越小越先执行10
key条件标识——Lore 中匹配此 key 的行才会触发此读取器"血量需求"

有了 key = "血量需求",那么当解析到 血量需求: 100 这样的 Lore 行时,就会调用这个读取器。

readFormat 正则

Groovy
List<String> readFormat = Arrays.asList("{key}.*?@value")

格式中的特殊标记:

  • {key} — 自动替换为构造函数中传入的 key
  • @value — 自动替换为数值匹配正则(可以匹配 10050-100 等)

你也可以写更复杂的正则,比如:

Groovy
// 匹配 "血量需求: 100" 或 "血量需求:100" 格式
List<String> readFormat = Arrays.asList("{key}\\s*:\\s*@value")
// 匹配 "血量需求 >= 100" 格式
List<String> readFormat = Arrays.asList("{key}\\s*>=\\s*@value")

完整示例

下面实现一个「血量需求」条件读取器——Lore 中写 血量需求: 100 表示需要 100 血以上才能使用:

Groovy
package scripts.reads

import cn.org.bukkit.craneattribute.api.attribute.data.AttributeData
import cn.org.bukkit.craneattribute.api.attribute.source.AttributeSource
import cn.org.bukkit.craneattribute.api.attribute.source.Conditional
import cn.org.bukkit.craneattribute.api.attribute.source.ItemStackBased
import cn.org.bukkit.craneattribute.api.read.ReadableLine
import cn.org.bukkit.craneattribute.api.utils.StringUtilsKt
import cn.org.bukkit.craneattribute.core.attribute.data.PlayerAttributeData
import cn.org.bukkit.craneattribute.core.manager.CacheManager
import cn.org.bukkit.craneattribute.core.utils.BukkitUtils
import groovy.transform.CompileStatic
import scripts.libs.GroovyConditionReader

@CompileStatic
class HealthConditionReader extends GroovyConditionReader {

    // 正则匹配模式
    List<String> readFormat = Arrays.asList("{key}.*?@value")

    HealthConditionReader() {
        super("health", 10, "血量需求")
    }

    @Override
    protected boolean read(ReadableLine readableLine, AttributeData data, AttributeSource attributeSource) {
        // 第一步:只给玩家判断,非玩家直接放行
        if (data instanceof PlayerAttributeData) {

            // 第二步:使用缓存管理器获取正则解析结果
            List<String> list = CacheManager.INSTANCE.getStringList(readableLine) {
                // 缓存未命中时,调用 extractValues 进行正则解析
                extractValues(readableLine, readFormat)
            }

            // 第三步:解析结果可能为 null(该行不匹配此读取器)
            if (list == null) return true

            // 第四步:将字符串转为数值数组 [最小值, 最大值]
            double[] numbers = StringUtilsKt.toDoubleArray(list.first())
            double first = numbers[0]   // 最小值
            double last = numbers[1]    // 最大值

            // 第五步:获取玩家的实际血量
            double health = data.player.getHealth()

            // 第六步:判断是否满足条件
            if (first == last && health >= first) {
                // 单一值,如"血量需求: 100",玩家血量>=100即可
                return true
            } else if (first != last && health in first..last) {
                // 范围值,如"血量需求: 50-100",玩家血量在范围内即可
                return true
            }

            // 第七步:不满足时,可以选择发送提示信息
            if (attributeSource instanceof Conditional && attributeSource instanceof ItemStackBased) {
                def itemName = BukkitUtils.getItemName(attributeSource.itemStack, data.player)
                if (first == last) {
                    data.player.sendMessage("§b[CraneAttribute]§e §a${itemName}§e 要求玩家血量大于等于 §a${first}§e")
                } else {
                    data.player.sendMessage("§b[CraneAttribute]§e §a${itemName}§e 要求玩家血量为 §a${first}§e ~ §a${last}§e")
                }
            }

            // 不满足条件
            return false
        }

        // 非玩家实体 → 无条件放行
        return true
    }
}

代码逐步解析

1. if (data instanceof PlayerAttributeData)

属性数据有 PlayerAttributeData(玩家)和 AttributeData(非玩家)两种。条件判断通常只对玩家有意义。

2. CacheManager.INSTANCE.getStringList(...)

同一行 Lore 可能被多个读取器检查。缓存管理器确保了正则解析只执行一次,后续访问直接返回缓存结果。{ it -> extractValues(readableLine, readFormat) } 是缓存未命中时的回调。

3. if (list == null) return true

extractValues 返回 null 表示「这行文本不匹配我的正则模式」——不是我的事,直接返回 true 让后面的逻辑继续。

4. StringUtilsKt.toDoubleArray(list.first())

"100" 转为 [100.0, 100.0],将 "50-100" 转为 [50.0, 100.0]。这是插件提供的工具方法。

5. 判断逻辑

  • first == last:单一值 → 要求 ≥ 该值
  • first != last:范围值 → 要求在范围内

6. 发送提示

条件不满足时,可以给玩家发送一条消息说明原因。判断 ConditionalItemStackBased 是为了确保只在「物品上的条件」场景下发消息(而不是属性源更新等场景)。

更简单的示例:公会等级需求

如果你的需求很简单,代码可以更短:

Groovy
@CompileStatic
class GuildLevelCondition extends GroovyConditionReader {

    List<String> readFormat = Arrays.asList("{key}.*?@value")

    GuildLevelCondition() {
        super("guild_level", 15, "公会等级需求")
    }

    @Override
    protected boolean read(ReadableLine readableLine, AttributeData data, AttributeSource attributeSource) {
        if (data instanceof PlayerAttributeData) {
            List<String> list = CacheManager.INSTANCE.getStringList(readableLine) {
                extractValues(readableLine, readFormat)
            }
            if (list == null) return true

            double[] numbers = StringUtilsKt.toDoubleArray(list.first())

            // 假设你有一个方法来获取玩家公会等级
            int guildLevel = getPlayerGuildLevel(data.player)

            if (guildLevel >= numbers[0]) {
                return true
            }

            data.player.sendMessage("§c需要公会等级 ${numbers[0].toInteger()} 才能使用此装备")
            return false
        }
        return true
    }
}

注册与配置

保存到 scripts/reads/ 后,使用 /ca reload 重载。插件会自动在 read.ymlread condition 下生成配置:

YAML
read condition:
  health:
    priority: 10
    key: 血量需求
    format:
      - "{key}.*?@value"

你可以在这里直接修改 prioritykeyformat,下次重载时会覆盖代码中的设置(但代码中的 key 是初始值)。

注意事项

  1. 非玩家实体通常返回 true 放行
  2. 正则解析失败时应返回 true(容错优先)
  3. data.player 只在 PlayerAttributeData 类型下可用,使用前记得 instanceof 检查
  4. 条件读取组件决定了整行属性是否生效,而不仅仅是单个属性

下一步

了解条件读取后,继续学习 自定义数值读取组件 —— 决定属性值如何从文本中提取。