简体中文
技能定义
技能定义文件这样写: 每一项字段是什么意思, 默认值是多少, 能填哪些值
一个技能一个文件, 放在 plugins/EmakiSkills/skills/*.yml. 你只需要在技能文件里写「技能本身是什么」这类管理信息 (名字, 冷却, 消耗, 升级等), 真正的技能效果交给原生脚本系统或 MythicMobs 执行. 本地资源定义放在 plugins/EmakiSkills/resources/*.yml
加载规则
- 一份文件只加载一个技能; 文件里的
id写成空值时, 整份文件会被跳过, 并计入加载问题 - 想临时停用某个技能, 把它设为
enabled: false就行, 不会进注册表, 等于这个技能不存在 - 技能 id 会统一转成小写; 列表字段里的 id 也一样处理, 并把
-换成_ - 列表字段支持别名键 (见下表), 两个位置都写时会按顺序合并并去重
顶层字段
yaml
id: "fireball"
enabled: true
display_name: "<red>火球术"
description:
- "<gray>向前方释放一枚火球。"
icon_material: "fire_charge"
mythic_skill: "EmakiFireball"
trigger_type: "active"
cooldown_ticks: 100
global_cooldown_ticks: 20| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
id | string | 是 | — | 非空; 转小写 | 技能的唯一 ID. 写成空值会让整份文件被跳过 |
enabled | boolean | 否 | true | true / false | 是否启用. 填 false 时技能不加载, 等于不存在 |
display_name | string | 否 | id | 任意 MiniMessage 文本 | 显示名, 用在 GUI, ActionBar 和反馈文案的 %skill% 里 |
description | list<\string> | 否 | [] | — | 技能描述, 分行写在 GUI 里显示 |
icon_material | string | 否 | "" | Bukkit Material 名 (转小写) | GUI 图标的材质. 留空时用槽位配置里的材质, 都没有就用内置的备用材质 |
mythic_skill | string | 否 | "" | MythicMobs 技能 id | mythic / hybrid 模式下要执行的 MythicMobs 技能, 非玩家施法者那条路径也用它 |
trigger_type | string | 否 | "active" | active / passive (其他值一律按 active) | 主动技能还是被动技能 |
passive_triggers | list<\string> | 否 | [] | 已注册的被动触发器 id | 被动技能的事件来源. 没注册的 id 在派发时会被直接丢掉, 不会报错 |
cron_expression | string | 否 | "" | Quartz cron 表达式 | 填了非空值时, 会自动注册一个 cron_<id> 被动触发器并加进 passive_triggers; 格式见 CoreLib 表达式 - Cron 表达式 |
cron_max_executions | integer | 否 | 0 | ≥ 0, 负数按 0 | cron 触发器总共能触发几次; 0 表示不限 |
variables | map | 否 | {} | 键名不以 emaki_ 开头 | 运行时变量块, 写法见 变量类型 |
script | map | 否 | 禁用 | — | 原生脚本块, 写法见 原生脚本系统 |
upgrade | map | 否 | 禁用 | — | 升级规则, 见下文 |
cooldown_ticks | integer | 否 | 0 | ≥ 0, 负数按 0 | 技能自己的冷却, 单位是 tick (20 tick = 1 秒) |
global_cooldown_ticks | integer | 否 | 0 | ≥ 0, 负数按 0 | 全局冷却, 单位 tick; 释放后所有技能共用这个冷却 |
resource_costs | list<\map> | 否 | [] | — | 资源消耗或资源检查, 条目字段见下文 |
lore_aliases | list<\string> | 否 | [] | — | 从装备的 Lore 里认出这个技能时要匹配的别名 |
pdc_skill_id | string | 否 | id | — | 从装备的 PDC 里认出这个技能时用的 ID |
tags | list<\string> | 否 | [] | 命中 skill_tags.equip_limits 时受数量限制 | 技能标签, 用来做装备数量限制和分类筛选 |
tab_tags | list<\string> | 否 | [] | 别名 tabTags | GUI 页签标签 |
required_skills | list<\string> | 否 | [] | 别名 dependencies | 装备这个技能前必须先装备好的技能 ID |
conflicting_skills | list<\string> | 否 | [] | 别名 conflicts | 不能和这个技能同时装备的技能 ID |
ui_category | string | 否 | "default" | — | GUI 分类 |
sort_order | integer | 否 | 0 | 越小越靠前 | GUI 排序, 数值越小越靠前 |
show_in_slots | boolean | 否 | true | true / false | 是否允许它出现在玩家的主动技能槽选择列表里. 强制触发器的技能常设为 false |
condition | map | 否 | 无 | — | 技能释放 (和升级) 的条件块, 见下文 |
condition 条件块
技能释放和升级共用同一套条件块. 条件表达式在计算前会先交给 PlaceholderAPI 渲染
yaml
condition:
type: "all_of"
entries:
- "%player_level% >= 5"
- "%emakiskills_level_fireball% >= 2"| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
condition | map | 否 | 无 | — | 条件块. 必须写成映射节点; 缺了 entries 就当成没有条件 |
condition.type | string | 否 | all_of | all_of, any_of, none_of, at_least, exactly | 多个条件怎么组合 |
condition.entries | list<\string> | 否 | [] | 表达式字符串 | 条件表达式列表; 不写这个键, 整块条件就是空的 |
condition.required_count | integer | 否 | 0 | ≥ 0 | at_least / exactly 这两种组合方式下, 需要命中的条件数量 |
condition.invalid_as_failure | boolean | 否 | true | true / false | 由通用条件解析器读取; EmakiSkills 的释放和升级路径固定按「表达式不合法就算不通过」处理 |
condition.on_pass.actions / condition.on_fail.actions | list<\string> | 否 | [] | — | 由通用条件解析器读取; EmakiSkills 的技能不用这些字段 |
condition.on_fail.block_output / condition.on_fail.message | boolean / string | 否 | false / "" | — | 同上, EmakiSkills 的技能不用 |
条件没解析出来就当不通过: 技能不会释放, 升级也会被拒绝. 释放失败的反馈键是 cast.condition_not_met, 升级失败的反馈键是 upgrade.condition_not_met
条件文本会先交给 PlaceholderAPI 渲染, 所以条件里写的 %...% 必须能被当前装的 PlaceholderAPI 扩展认出来 (例如 %emakiskills_level_fireball%); 认不出的占位符会原样进入表达式计算, 按上一条规则判为不通过
变量与脚本
yaml
variables:
fire_damage:
type: expression
value: "10 + %level% * 5"
battle_cry:
type: random_text
lines:
- "<red>燃烧吧!"
- "<red>烈焰之力!"
script:
mode: native # native / mythic / hybrid
stop_on_failure: true
actions:
cast:
- 'trigger | damage amount=%var.fire_damage%'
hit:
- 'trigger | ignite duration=3s'resource_costs 资源消耗
yaml
resource_costs:
- type: "ea-resource"
target_id: "mana"
amount: 20
operation: "consume"
failure_message: "<red>法力不足。"| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
type | string | 是 | — | ea-resource, attribute-check (别名 ea-attribute-check), local-resource, auraskills-mana (别名 aura-skills-mana, aura-mana), mythiclib-mana (别名 mmocore-mana, mythic-mana) | 资源类型. 认不出的取值会让整条被丢掉 |
target_id | string | 否 | "" | 资源 / 属性 ID | 对应的本地资源, Attribute 资源或属性 ID |
amount | double | 否 | 0 | ≥ 0, 负数按 0 | 要检查或扣除的数量 |
operation | string | 否 | consume | consume (扣除), require (只检查) | 其他取值按 consume 处理 |
failure_message | string | 否 | "" | MiniMessage, 经 PlaceholderAPI 渲染 | 不够时的提示; 留空时回退成 target_id 这段文字 |
要注意插件不在时的行为: auraskills-mana 要求装了 AuraSkills, mythiclib-mana 要求装了 MythicLib 或 MMOCore; 对应插件不在时这一条就当成不可用, 检查直接放行, 扣除会被跳过. ea-resource 和 attribute-check 在 EmakiAttribute 不可用时同样直接放行. attribute-check 永远不扣除
本地资源文件
resources/*.yml 用来定义 local-resource 用的资源. 资源 ID 取文件里写的 id, 和文件名无关
yaml
id: "mana"
display_name: "<gradient:#4DA6FF:#00BFFF>魔力</gradient>"
max: 100.0
default_current: 100.0
regen_amount: 2.0
regen_interval_ticks: 20
clamp_min: 0.0
clamp_max: 100.0| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
id | string | 是 | — | 非空; 转小写 | 资源唯一 ID; 空值会让整份文件被跳过 |
display_name | string | 否 | id | MiniMessage | 显示名 |
max | double | 否 | 100.0 | ≥ 0, 负数按 0 | 这个资源能到达的最大值 |
default_current | double | 否 | 100.0 | ≥ 0, 负数按 0 | 玩家第一次加载时的当前值 |
regen_amount | double | 否 | 0.0 | ≥ 0, 负数按 0 | 每次自然回复增加多少 |
regen_interval_ticks | integer | 否 | 0 | ≥ 0, 负数按 0 | 回复间隔, 单位 tick; 0 表示不回复 |
clamp_min | double | 否 | 0.0 | ≥ 0, 负数按 0 | 当前值的下限 |
clamp_max | double | 否 | 0.0 | <= 0 时回退 max, 否则 ≥ clamp_min | 当前值的上限 |
本地资源与其他资源系统
本地资源只存在技能自己的数据里, 不会自动和 EmakiAttribute 互通. 如果你已经在用 EA 维护同名资源, 技能费用就改用 ea-resource, 免得两套数值并存
升级配置
yaml
upgrade:
enabled: true
max_level: 5
gui_template: "upgrade_gui"
failure_penalty: "none"
economy:
enabled: true
currencies:
- provider: "vault"
currency_id: ""
base_cost: 1200
cost_formula: "%base_cost% * %target_level%"
display_name: "<gold>金币</gold>"
success_rates:
2: 100.0
3: 90.0
levels:
2:
materials:
- item_sources:
- "minecraft-blaze_powder"
amount: 2
actions:
success:
- 'send_message text="<yellow>火球术更强了。</yellow>"'| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
upgrade.enabled | boolean | 否 | false | true / false | 是否启用升级. 关掉时最大等级就是 1 |
upgrade.max_level | integer | 否 | 1 | ≥ 1, 小于 1 被抬升为 1 | 最大等级, 也是 level 命令和 API 能取到的上限 |
upgrade.gui_template | string | 否 | upgrade_gui | gui/ 下的模板名 | 升级 GUI 用的模板. 留空, 等于 upgrade_gui, 或指向不存在的模板时, 都会回退到 upgrade_gui (模板不存在会记一条警告) |
upgrade.failure_penalty | string | 否 | none | none / downgrade | 升级失败的惩罚. 只有 downgrade 会真的降一级 (最低降到 1 级). 写 reset 只会影响 GUI 文案, 不会重置等级 |
upgrade.economy.enabled | boolean | 否 | false | true / false | 是否启用经济消耗 |
upgrade.economy.currencies | list<\map> | 否 | [] | — | 货币列表, 字段见下 |
upgrade.success_rates | map<int,double> | 否 | 100.0 | 键为 > 0 的整数, 值夹取到 0~100 | 按目标等级配置的成功率; 没配的等级按 100 算. 键是非正数或非整数时会被跳过 |
upgrade.levels | map<int,map> | 否 | {} | 键为 > 1 的整数映射节点 | 按目标等级覆盖材料, 经济, 成功率和升级动作 |
upgrade.economy.currencies 每项:
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
provider | string | 否 | auto (转小写) | 经济提供者名 | 经济来源; 留空按 auto 交给 CoreLib 去选 |
currency_id | string | 否 | "" | — | 货币 ID |
base_cost | double | 否 | 0 | ≥ 0, 负数按 0 | cost_formula 里 %base_cost% 的取值 |
cost_formula | string | 否 | %base_cost% | 表达式 | 实际消耗的表达式. 可以按 %base_cost%, %level% (当前等级), %target_level%, %max_level% 来算; 结果 ≤ 0 的条目不参与消耗 |
display_name | string | 否 | "" | MiniMessage | 显示名; 留空回退成 currency_id, 再留空回退成 provider |
upgrade.levels.<n> 每项 (<n> 为目标等级, 必须 > 1):
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
levels.<n>.materials | list<\map> | 否 | [] | — | 这一级需要的物品材料, 字段见下 |
levels.<n>.economy.enabled | boolean | 否 | true | true / false | 这一级是否把经济配置覆盖成「启用」 |
levels.<n>.economy.currencies | list<\map> | 否 | [] | 同 upgrade.economy.currencies | 这一级的货币覆盖列表; enabled: false 时这一级不收费 |
levels.<n>.success_rate | double | 否 | 取 upgrade.success_rates | 夹取到 0~100 | 这一级的成功率覆盖, 优先于 upgrade.success_rates |
levels.<n>.actions.success | list<\string> | 否 | [] | 动作管道行 | 升级成功后执行的动作 |
levels.<n>.actions.failure | list<\string> | 否 | [] | 动作管道行 | 升级失败后执行的动作 (包括摇点失败和降级) |
levels.<n>.materials 每项:
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
item_sources | list<\string> | 否 | "" | 物品源写法, 见 物品匹配器 | 允许的物品源; 只取列表里第一项作为物品标识. 与同级的 matcher 取 AND |
matcher | map | 否 | 无 | 组件 / PDC / Lore 条件 | 非物品源条件. matcher 里不能再写物品源 |
amount | integer | 否 | 1 | ≥ 1, 0 按 1 | 需要几个 |
optional | boolean | 否 | false | true / false | 会被解析, 但当前升级判定和扣除流程不读这个字段 |
protection | boolean | 否 | false | true / false | 会被解析, 但当前升级判定和扣除流程不读这个字段 |
item_sources 和 matcher 都省略时, 这个材料永远匹配不上, 也就等于不需要它. 同类的 item_sources 条目在扣除时会合并累加; 带 matcher 的条目各自独立
升级流程与 GUI
- 先算目标等级 (当前等级加 1), 再被
max_level卡住上限; 技能必须已经解锁, 且upgrade.enabled: true - 流程顺序是: 检查条件 → 触发
SkillPreUpgradeEvent(可取消, 也能改成功率) → 检查消耗 → 扣除消耗 → 摇点 → 应用等级或失败惩罚 - 消耗在摇点之前就扣掉了: 摇点失败也不会退还已经扣掉的货币和材料 (只有提交阶段出异常时才会尝试补偿回滚)
- 玩家看到的反馈来自
lang/<language>.yml的upgrade.*文案键 (例如upgrade.success,upgrade.failed,upgrade.insufficient_funds)
升级 GUI 由 upgrade.gui_template 指定的模板渲染, 你在槽位里写的 type 决定它显示什么:
槽位 type | 显示内容 |
|---|---|
skill_display | 技能图标和名称, 可替换的变量有等级, 成功率, 失败惩罚文案 |
cost_currency | 货币消耗清单; 没有消耗时显示 gui.upgrade_cost_free. 变量有 %currencies%, %currency_count% |
cost_material | 材料消耗清单, 逐项显示「已有/需要」并按是否满足上色; 变量有 %materials%, %material_count%, %missing_count% |
success_rate | 成功率标题, 变量有 %success_rate% |
confirm | 确认按钮; 按未开启升级 / 已满级 / 材料不足 / 可升级切换图标和状态文案. 点击后调用的和命令是同一套升级服务 |
不用担心报价和实际扣费对不上: GUI 和升级流程用的是同一个报价函数 (算这次升级要花多少东西的函数, preview). GUI 渲染时按当前状态展示消耗, 点升级时重新报一次价并按结果实际扣除, 所以玩家状态没变的时候两边数值一样; 如果在点击之前状态已经变了 (例如材料被用掉), 就以点击时重新报的价和实际扣除为准, 不够的话整笔失败, 也不会扣费
config.yml 中的来源与标签
下面这两项要写在 plugins/EmakiSkills/config.yml, 它们决定技能怎么被认出来, 以及玩家最多能装备几个
装备技能来源开关
yaml
skill_sources:
read_lore_skills: true
read_pdc_skills: true
require_lore_pdc_match: false| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
skill_sources.read_lore_skills | boolean | 否 | true | true / false | 是否从装备的 Lore 里读技能 |
skill_sources.read_pdc_skills | boolean | 否 | true | true / false | 是否从装备的 PDC 里读技能 |
skill_sources.require_lore_pdc_match | boolean | 否 | false | true / false | 严格模式. 开启后只保留同一物品, 同一槽位下 Lore 和 PDC 的交集, 两边对不上的技能不生效 |
记住: 两侧都关闭, 或开启严格模式却只开启了一侧时, 配置预检会给出警告
标签装备数量限制
当技能定义里的 tags 命中了这里的标签, 玩家最多就只能同时装备对应的数量:
yaml
skill_tags:
equip_limits:
ultimate: 1
mobility: 2| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
skill_tags.equip_limits.<tag> | integer | 否 | 无 (不限) | > 0, <= 0 或非整数时忽略该键 | 这个标签最多能同时装备几个. 标签名会被规范化, 并把 - 换成 _ |
tag_limits | map | 否 | 无 | 同上 | 旧键名. skill_tags.equip_limits 为空时回退读这个键 |
照上面的配置, 带 ultimate 标签的技能最多装备 1 个, 带 mobility 的最多装备 2 个. 没列出来的标签不受限制
最小示例
yaml
id: fireball
enabled: true
display_name: '<red>火球术'
description:
- '<gray>向前方释放一枚火球。'
icon_material: FIRE_CHARGE
mythic_skill: EmakiFireball
trigger_type: active
cooldown_ticks: 100
global_cooldown_ticks: 20
resource_costs:
- type: ea-resource
target_id: mana
amount: 20
operation: consume
failure_message: '<red>法力不足。'