Skip to content

脚本

上一页:消耗条件与变量 · 下一页:配置文件


声明式条件 conditions 能解决「等级够不够、有没有目标、在不在某世界」这类简单门。 但有些判断写不出来:要读其他插件的数据、要做组合计算、要在施放成功后给玩家发奖励…… 这时候上 JS 脚本script.pre_js(拦门)和 script.post_js(事后副作用)。

📌 先用 conditions,写不出来再上脚本。 脚本更强但更复杂,能用声明式条件解决的别上 JS。

🖼️ [图片占位] pre_js 拦门 / post_js 事后两个钩子在管线里的位置 · 建议 assets/script-hooks.png


🪝 两个钩子

yaml
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 命名空间,例如:

yaml
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_stateon / off toggle 技能时存在)
has_target / target_type / target_uuid有目标时才有(准星 / 索敌 / 被动锁到的目标)
var_<名>技能变量,来自 variables: levels.params:统一 var_ 前缀(如 var_elementvar_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:

yaml
script:
  pre_js: "qinhskills:demo.js:canCast"

demo.js 里:

javascript
// 等级 < 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:

yaml
script:
  post_js: "qinhskills:demo.js:onCast"

demo.js 里:

javascript
// 技能成功放出后跑:发消息、给一个奖励物品
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空跑(什么都不做)

降级时带诊断日志提示你引擎没就绪,但不中断施放。所以即使脚本环境没配好,技能照样能放,只是脚本逻辑失效——线上更安全。


📚 继续阅读