脚本
声明式条件 conditions 能解决「等级够不够、有没有目标、在不在某世界」这类简单门。 但有些判断写不出来:要读其他插件的数据、要做组合计算、要在施放成功后给玩家发奖励…… 这时候上 JS 脚本:script.pre_js(拦门)和 script.post_js(事后副作用)。
📌 先用
conditions,写不出来再上脚本。 脚本更强但更复杂,能用声明式条件解决的别上 JS。
🖼️ [图片占位] pre_js 拦门 / post_js 事后两个钩子在管线里的位置 · 建议
assets/script-hooks.png
🪝 两个钩子
script:
pre_js: "qinhskills:demo.js:canCast" # 施放前:返回 false 就拦下
post_js: "qinhskills:demo.js:onCast" # 施放成功后:跑副作用| 钩子 | 时机 | 返回值 | 拦不拦施放 |
|---|---|---|---|
pre_js | 门控阶段,放出之前 | 返回 false → 拦下,不扣资源,结果码 CONDITION_FAILED | 拦 |
post_js | 施放成功之后 | 返回值忽略 | 不拦(fire-and-forget) |
post_js 是「点火即忘」——它失败也不影响技能已经放出去这件事。
🔖 引用格式
脚本用「命名空间 : 路径 : 函数名」三段引用:
命名空间:路径.js[:函数名]QS 用 qinhskills 命名空间,例如:
pre_js: "qinhskills:demo.js:canCast"
# └─ 命名空间 └─ 文件 └─ 函数名脚本文件由 QCL 的脚本加载器统一管理,放在 QCL 约定的脚本目录里。
qinhskills命名空间下的脚本归 QS 这套用。
⚙️ 脚本引擎从哪来
脚本引擎是 QinhCoreLib(QCL)的 GraalJS,不是 QS 自带的。这意味着:
- 需要 Paper / Purpur 拉取 GraalJS 运行库(QCL 配置
javascript.enabled)。 - 引擎没就绪也不会崩——QS 自动降级(见下文)。
📥 注入对象
脚本里有两个全局对象可用:
ctx —— 技能执行上下文
| 方法 | 作用 |
|---|---|
ctx.player() | 返回 Player(施放者) |
ctx.get(键) | 读变量 |
ctx.set(键, 值) | 写变量 |
ctx.vars() | 拿到整个变量表 |
ctx.get(键) 能读到的变量(和 消耗条件与变量 里说的一致):
| 键 | 来源 / 含义 |
|---|---|
skill | 技能 id(注意:是 skill,不是 skillId) |
level | 玩家该技能等级 |
mode | 触发模式(无则 default) |
source | 触发来源(如 PLAYER / EVENT_LISTENER / COMMAND) |
player | 玩家名 |
toggle_state | on / off(仅 toggle 技能时存在) |
has_target / target_type / target_uuid | 有目标时才有(准星 / 索敌 / 被动锁到的目标) |
var_<名> | 技能变量,来自 variables: 和 levels.params:,统一 var_ 前缀(如 var_element、var_power) |
⚠️ 注意:脚本里没有
skillId/castMode/targetCount/slot/param_这些键。技能变量和等级参数都用var_前缀(不是param_);技能 id 的键是skill。
qcl —— QCL 全局工具
| 方法 | 作用 |
|---|---|
qcl.logInfo(msg) | 打日志 |
qcl.itemGive(player, item) | 给物品 |
qcl.economy*(...) | 经济相关(扣钱 / 给钱 / 查余额等) |
qcl.runSync(runnable) | 把一段逻辑切回主线程跑(操作世界 / 实体时用) |
🧪 示例一:pre_js 拦门(等级 ≥ 2 才放)
技能 yml:
script:
pre_js: "qinhskills:demo.js:canCast"demo.js 里:
// 等级 < 2 就拦下;返回 false → QS 不扣资源,结果码 CONDITION_FAILED
function canCast(ctx) {
var level = ctx.get("level"); // 玩家该技能等级
if (level == null || level < 2) {
var p = ctx.player();
p.sendMessage("§c该技能需要 2 级才能施放");
return false; // ← 拦下施放
}
return true; // ← 放行
}这个简单例子其实用
conditions: ["player_level:>=2"]也能做到。真正需要pre_js的是「读别的插件数据」「多条件组合算分」这类声明式条件表达不出来的情况。
🎁 示例二:post_js 事后副作用(发消息 / 给物品)
技能 yml:
script:
post_js: "qinhskills:demo.js:onCast"demo.js 里:
// 技能成功放出后跑:发消息、给一个奖励物品
function onCast(ctx) {
var p = ctx.player();
p.sendMessage("§a技能命中!获得战利品 §e×1");
// 操作世界 / 给物品切回主线程更稳妥
qcl.runSync(function() {
qcl.itemGive(p, "DIAMOND");
});
qcl.logInfo("[demo] " + p.getName() + " 触发了 onCast 副作用");
}post_js 不影响技能本身是否放出——它只是「放完之后顺手做点事」。
🛟 降级行为(引擎没就绪时)
GraalJS 没装好 / 没启用时,QS 不会报错卡住,而是安全降级:
| 钩子 | 降级行为 |
|---|---|
pre_js | 不拦(视作返回 true,照常施放) |
post_js | 空跑(什么都不做) |
降级时带诊断日志提示你引擎没就绪,但不中断施放。所以即使脚本环境没配好,技能照样能放,只是脚本逻辑失效——线上更安全。