技能与 BUFF
技能与 BUFF
技能定义放在 plugins/SnowyGems/skills/ 下,文件名任意。技能不是绑在宝石上的,而是绑在装备的 lore 文本上:装备 lore 里出现某段文字,就获得对应技能。
一个最小可用的技能:
向前闪现: # 技能 ID
Lore: "右键即可闪现" # 装备 lore 含这段文字就绑定此技能
Cooldown: 10.0 # 冷却 10 秒
Skills:
- action: Blink
distance: 32
trigger: onUse
target: Forward
玩家主手持有 Lore 含「右键即可闪现」的装备时,右键尝试向前闪现,遇到阻挡会缩短距离。
技能定义的字段
| 字段 | 说明 |
|---|---|
Lore | 绑定标记。装备 lore 含这段文字即生效(去色比较,可含颜色码) |
Cooldown | 非负有限秒数,不写或 0 表示无冷却;成功施放后开始 |
Slot | 来源槽位列表:mainhand / offhand / head / chest / legs / feet;空列表不额外限制 |
CooldownTip | 冷却中的提示,本身也是一个函数表达式 |
Skills | 技能行列表 |
Lore 留空的技能定义不会被任何装备触发——它只能作为形态切换的目标(见下文 Switch),或者被 Reward{...} 嵌套调用。冷却按玩家 + 技能 ID计算,同一定义的主动与定时技能共享。至少一条效果成功才进入冷却;所有条件不满足、全部无效时不扣冷却。成功安排 Delay/Repeat 任务也算施放成功,不等待延迟效果完成。
Slot 限制技能来源:主动交互目前以主手为入口;定时 BUFF 支持六个装备 / 手持槽位;耐久事件按损耗物品所在槽位检查。这里的 mainhand/offhand 与 Attribute 奖励的 hand/off_hand 是两套参数,不能混写。
定时引擎按主手、副手、头、胸、腿、脚顺序检查。同一技能 ID 每名玩家每轮至多成功施放一次,不会自动选择最高等级的装备。仅带 Lore 且具有 onTimer 行的定义参与定时扫描。
潜行左键同一服务器 tick 去重。发射物通用与专用触发合并为一次施放,一行同时匹配两种标记也只执行一次。发射物原物品快照缺失时不借用玩家当前主手;切换装备不能给旧发射物补上另一件装备的技能。
技能隐藏粉尘不会停用技能;匹配标记与 $LORE: 等级读取同时读取可见和隐藏内容。
技能动作结构
推荐把动作写成 YAML 对象:
Skills:
- action: PotionBuff
type: SLOW_FALLING
trigger: onTimer
action 指函数;其他函数参数直接列在下方。trigger 必填,多个触发器用逗号隔开;target 可省略,默认 Self。完整迁移与编辑器说明见新语法、迁移与游戏内编辑器。旧的技能行继续兼容:
函数名{参数1=值;参数2=值} ~触发标记 @作用目标
旧文本行分三部分:
| 部分 | 必填 | 说明 |
|---|---|---|
函数名{参数} | 是 | 做什么。参数用 ; 分隔 |
~触发标记 | 是 | 什么时候触发。不写则永远不会触发 |
@作用目标 | 否 | 作用于谁,默认 @Self |
作用目标:
| 写法 | 含义 |
|---|---|
@Self | 作用于自己(默认) |
@Entity | 作用于本次攻击/命中事件中的生物;没有目标时跳过,不会回退到自己 |
@Location | 作用于命中坐标(闪现、粒子、闪电常用) |
0.0.9 起,控制流子动作没有填写 target 时会继承外层目标。例如 Repeat 使用 target: Entity,其 then 下的 All、Damage、Particle 也作用于被攻击的生物;子动作明确写 target: Self 可改回玩家。onAttack 使用伤害事件实际命中的生物,不会另取准心附近的实体。
触发标记
| 标记 | 触发时机 |
|---|---|
~onUse | 右键使用 |
~onShiftUse | 潜行 + 右键 |
~onSwing | 左键挥臂 |
~onShiftSwing | 潜行 + 左键(三叉戟/矛也可用) |
~onAttack | 近战攻击命中实体 |
~onKill | 击杀实体 |
~onHit | 发射物命中(任意类型) |
~onHit:类型 | 发射物命中指定类型 |
~onLaunch | 发射物射出(任意类型) |
~onLaunch:类型 | 发射物射出指定类型 |
~onBreak | 挖掉方块 |
~onDamaged | 物品即将损失耐久 |
~onTimer | 每秒轮询(常驻 BUFF 用) |
~onHit:类型 的类型直接取实体类型名,不是固定清单:ARROW、TRIDENT、EGG、SNOWBALL、FIREBALL… 服务端有什么发射物实体就能写什么。1.21.11 的矛是近战武器,不会触发 onLaunch 或 onHit:SPEAR;矛应使用 onUse、onShiftUse、onSwing 或 onAttack。
游戏内用 /sgem triggers 查看清单(内容来自 lang/<语言代码>.yml,可自行增删措辞)。
函数清单
游戏内用 /sgem skills [关键字] 查看,会标出每个函数在当前版本是否可用。
位移
| 函数 | 别名 | 用法 |
|---|---|---|
Blink | Teleport 闪现 | {distance=10} 沿视线瞬移;配合 @Location 传送到命中处 |
Velocity | Push 击退 冲刺 | {forward=1.5;up=0.5} 或 {x=0;y=1;z=0} |
Blink 沿视线逐格检查非空气方块并缩短距离;不是完整的双格安全传送检测。使用命中坐标传送时也应在目标场景测试。
战斗
| 函数 | 别名 | 用法 |
|---|---|---|
Damage | 伤害 | {amount=5} |
Heal | 治疗 恢复 | {amount=5} |
Fire | 点燃 燃烧 | {ticks=100}(20 tick = 1 秒) |
Lightning | 闪电 雷击 | {damage=true},false 则只有视觉效果 |
反馈
| 函数 | 别名 | 用法 |
|---|---|---|
Chat | Message Msg 消息 | {m=&a技能发动!} |
ActionBar | 动作栏 | {m=&b蓄力完成} |
Title | 标题 | {m=主标题;s=副标题;fadeIn=10;stay=40;fadeOut=10} |
Sound | 音效 | {name=ENTITY_PLAYER_LEVELUP;volume=1;pitch=1} |
Particle | 粒子 | {name=FLAME;count=20;ox=0.5;oy=0.5;oz=0.5;speed=0} |
音效名和粒子名随版本增删,写错或版本不支持会跳过并在日志说明,不会报错崩溃。
药水效果
| 函数 | 别名 | 用法 |
|---|---|---|
Potion | PotionBuff Buff 药水 | {type=SPEED;level=2;duration=10},duration 单位为秒 |
Debuff | 负面 | {type=move;duration=5} 或 {type=BLINDNESS;level=1;duration=5} |
ClearPotion | RemovePotion 清除效果 | {type=POISON},不带参数则清除全部 |
Debuff 有两个语义化的特殊值:type=move 是强力缓慢(定身),type=fly 是禁飞。其余一律按效果名解析,所以 Debuff{type=weaving} 这种新版本效果也能写。
level 是「第几级」(从 1 起),不是 amplifier。
物品操作
| 函数 | 别名 | 用法 |
|---|---|---|
Enchant | 附魔 | {name=sharpness;level=5} 或 {name=fortune;limit=3} |
Switch | 形态切换 切换 | {s=精准;时运} |
Repair | 修复 耐久 | {amount=100},amount=full 则完全修复 |
Enchant 的四种语义:
| 写法 | 效果 |
|---|---|
level=5 | 设为 5 级 |
level=0 | 移除,并记住原等级(供 restore 用) |
level=restore | 恢复之前记住的等级 |
不写 level | 当前等级 +1,受 limit 限制 |
控制流
这一层是「技能引擎足够聪明」的关键:有了条件和组合,你可以自己拼出复杂逻辑,不需要等插件加新函数。
| 函数 | 别名 | 用法 |
|---|---|---|
All | Group 组合 | then 列表,依次执行多个子动作 |
Chance | Random 概率 | p: 0.3 加 then,p 为 0~1 |
If | Condition 条件 | c: "health<0.3" 加 then |
Delay | 延迟 | t: 20 加 then,延迟 20 tick |
Repeat | 循环 | times: 3、interval: 6 加 then |
Reward | Do 执行 | then 子动作,兼容旧的奖励包装写法 |
Command | Cmd 命令 | {c=give %player% diamond 1;as=console} |
then 可写单个对象,也可写动作列表。动作列表会按 All 的顺序执行;需要显式组合时可在 then 中写 action: All。嵌套里还可以继续嵌套,最多 16 层。旧的 {...} 行仍可读取,/sgem migrate 会转换可验证等价的嵌套行。
Skills:
- action: Chance
p: 0.1
then:
action: Potion
type: HASTE
level: 2
duration: 8
trigger: onBreak
If 支持的条件变量:
| 变量 | 含义 |
|---|---|
health | 自己的生命比例(0~1) |
raw_health | 自己的生命绝对值 |
max_health | 自己的生命上限 |
food | 饱食度(0~20) |
level | 经验等级 |
y | 所在高度 |
target_health | 目标的生命比例(0~1) |
target_raw_health | 目标的生命绝对值 |
运算符:> >= < <= == !=
target_* 在这次触发没有目标实体时取不到值,条件直接判为不成立(不是报错)。Command 的 as 参数:player(默认)、op(临时提权后还原)、console。命令里可用 %player% 和 %uuid% 占位符。
Command{as=op} 会临时给玩家 OP 权限执行命令。虽然执行完立即还原,但如果命令内容里含玩家可控的文本(比如从 lore 读来的值),存在被利用的风险。能用 as=console 就别用 as=op。Repeat.times 限制在 1~100;Delay.t 和 Repeat.interval 各限制在 0~72000 tick。单次施放及其嵌套、延迟子任务共享执行预算:最多 256 次函数分派、16 层嵌套、128 个延迟任务。这些是代码中的固定上限,没有对应 config.yml 开关。在 Folia 上,延迟任务跟随玩家或目标实体所属区域;若执行时施法者和目标已分属不同区域,该次子动作会跳过,避免跨区域访问。
重载或停用后旧延迟任务失效;执行时玩家已离线 / 死亡或目标已无效,也会跳过。配置复杂连招时应在测试服验证,不要用高频 onTimer 无限安排延迟任务。
CooldownTip:冷却提示
CooldownTip 本身也是一个函数表达式,支持三种:
CooldownTip: ActionBar{p=&c冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75}
CooldownTip: Chat{m=&c技能还在冷却中}
CooldownTip: Title{m=&c冷却中}
ActionBar 模式的参数:
| 参数 | 说明 |
|---|---|
p | 前缀文字 |
s | 后缀模板,%.2f 会被替换成剩余秒数 |
empty | 进度条未填充部分的字符 |
full | 已填充部分的字符 |
length | 进度条长度 |
完整示例
条件技能:濒死保命
濒死守护:
Lore: "&c濒死守护"
Cooldown: 30.0
CooldownTip: ActionBar{p=&c守护冷却;s=&c%.2fs;empty=&8|;full=&c|;length=40}
Skills:
# 物品即将损失耐久时,若当前血量低于 30%,尝试回血 + 抗性 + 音效
- action: "If"
c: "health<0.3"
then:
action: "All"
then:
- action: "Heal"
amount: "20"
- action: "Potion"
type: "RESISTANCE"
level: "3"
duration: "6"
- action: "Sound"
name: "ITEM_TOTEM_USE"
trigger: "onDamaged"
- action: "If"
c: "health<0.3"
then:
action: "Title"
m: "&c&l守护发动"
s: "&7生命已恢复"
trigger: "onDamaged"
概率技能:攻击时随机触发
雷霆之刃:
Lore: "&b雷霆之刃"
Skills:
- action: "Chance"
p: "0.2"
then:
action: "Lightning"
damage: "true"
trigger: "onAttack"
target: "Entity"
- action: "Chance"
p: "0.3"
then:
action: "Fire"
ticks: "80"
trigger: "onAttack"
target: "Entity"
- action: "Chance"
p: "0.2"
then:
action: "ActionBar"
m: "&e&l雷霆!"
trigger: "onAttack"
连招:延迟与重复
三段斩:
Lore: "&6三段斩"
Cooldown: 8.0
Skills:
- action: "Repeat"
times: "3"
interval: "6"
then:
action: "All"
then:
- action: "Damage"
amount: "4"
- action: "Particle"
name: "SWEEP_ATTACK"
count: "8"
trigger: "onAttack"
target: "Entity"
- action: "Sound"
name: "ENTITY_PLAYER_ATTACK_SWEEP"
trigger: "onAttack"
发射物技能:飞雷神
飞雷神三叉戟:
Lore: "飞雷神三叉戟"
Cooldown: 15.0
Skills:
- action: "Blink"
distance: "120"
trigger: "onHit:TRIDENT"
target: "Location"
- action: "Reward"
then:
action: "Title"
m: "&e飞雷神之术"
trigger: "onHit:TRIDENT"
矛技能(1.21.11+)
穿云长矛:
Lore: "&f穿云长矛"
Cooldown: 6.0
Skills:
- action: "ActionBar"
m: "&f长矛已准备"
trigger: "onUse"
- action: "ActionBar"
m: "&e长矛蓄力中"
trigger: "onShiftUse"
- action: "Particle"
name: "CRIT"
count: "8"
trigger: "onAttack"
target: "Entity"
Require: [SPEAR],不需要按材质分别写。矛的攻击技能使用 onAttack 等通用近战标记;onLaunch 与 onHit 仅用于真正的发射物。这些分类和 lunge 附魔由 Compat.Modules.Spear 管辖,在没有矛的版本上自动失效。从 lore 读数值:$LORE:$
技能参数可以从装备 lore 里动态读数值:
$LORE:标签?默认值$
向前闪现:
Lore: "右键即可闪现"
Skills:
- action: "Blink"
distance: "$LORE:闪现距离?32$"
trigger: "onUse"
target: "Forward"
装备 lore 里写「闪现距离 64」就用 64,没写就用 32。标签要写去掉颜色码的形式——插件会先把 lore 去色再比较,所以颜色码穿插在中间也能匹配。
这套机制让「同一个技能定义,不同装备不同数值」成为可能,配合宝石的 LoreVar 就能做出可升级的技能。
形态切换:Switch
「精准 ↔ 时运」这类切换:两个形态各自是一个技能定义,用 RewardSwitch 声明进入该形态要做什么。
精准时运切换:
Lore: "§2Shift+右键切换模式(精准/时运)"
Cooldown: 4.0
CooldownTip: ActionBar{p=&c切换冷却;s=&a%.2fs;empty=&f|;full=&c|;length=75}
Skills:
- Switch{s=精准;时运} ~onShiftUse
# 精准模式:收起时运,恢复精准
精准:
Skills:
- RewardSwitch{Enchant{name=fortune;level=0}}
- RewardSwitch{Enchant{name=silk_touch;level=restore}}
- RewardSwitch{ActionBar{m=&b&l切换为精准模式}}
# 时运模式:收起精准,恢复时运
时运:
Skills:
- RewardSwitch{Enchant{name=silk_touch;level=0}}
- RewardSwitch{Enchant{name=fortune;level=restore}}
- RewardSwitch{ActionBar{m=&a&l切换为时运模式}}
工作原理:level=0 移除附魔并记住原等级,level=restore 恢复记住的等级。切换状态存在物品的 PDC 里。
level=restore 或 level>0 的那些),看物品上实际带着谁的附魔,据此反推当前形态。所以「忠诚 ↔ 激流」「经验修补 ↔ 无限」「火焰附加 ↔ 击退」都能照抄上面的结构直接用。两个形态定义不需要写 Lore——它们不由装备直接触发,而是被 Switch 调用。
如果目标形态自己配了 Chat/ActionBar/Title,就以配置为准;没配的话插件会发一条来自 lang/<语言代码>.yml 的兜底提示,避免按下去毫无反应像是插件坏了。
常驻 BUFF
BUFF 引擎每秒扫描每个在线玩家的主手、副手、四件盔甲,命中 lore 标记就执行该定义里带 ~onTimer 的技能行。
发光:
Lore: "&6&l[&c&lBUFF&6&l] &b&l发光"
Skills:
- action: "Potion"
type: "GLOWING"
trigger: "onTimer"
定时技能每秒扫描一次,药水效果时长至少为 40 tick。想让效果持续,应让持续时间覆盖成功施放之间的间隔;如果配置了较长的共享冷却,只有 2 秒的效果仍会中断。
可叠加等级的 BUFF
配合宝石的 LoreVar 写数字,这里用 $LORE:$ 读回来:
抗性提升:
Lore: "&6&l[&c&lBUFF&6&l] &b&l抗性提升 "
Skills:
- action: "Potion"
type: "DAMAGE_RESISTANCE"
level: "$LORE:[BUFF] 抗性提升?1$"
trigger: "onTimer"
$LORE: 引用的标记必须和自己的 Lore 一致(去色后比较),否则等级永远读默认值 1——不报错,只是等级不涨。智能 BUFF
~onTimer 上同样能用控制流,于是可以写「满足条件才生效」的 BUFF:
濒死爆发:
Lore: "&6&l[&c&lBUFF&6&l] &c&l濒死爆发"
Skills:
- action: "If"
c: "health<0.3"
then:
action: "All"
then:
- action: "Potion"
type: "resistance"
level: "2"
- action: "Potion"
type: "regeneration"
level: "2"
trigger: "onTimer"
矿工直觉:
Lore: "&6&l[&c&lBUFF&6&l] &e&l矿工直觉"
Skills:
# y<0 = 深板岩层/深暗之域
- action: "If"
c: "y<0"
then:
action: "All"
then:
- action: "Potion"
type: "night_vision"
duration: "15"
- action: "Potion"
type: "haste"
level: "2"
trigger: "onTimer"
不稳定祝福:
Lore: "&6&l[&c&lBUFF&6&l] &d&l不稳定祝福"
Skills:
- action: "Chance"
p: "0.1"
then:
action: "Potion"
type: "speed"
level: "2"
duration: "5"
trigger: "onTimer"
- action: "Chance"
p: "0.1"
then:
action: "Potion"
type: "strength"
duration: "5"
trigger: "onTimer"
- action: "Chance"
p: "0.02"
then:
action: "Potion"
type: "wind_charged"
duration: "5"
trigger: "onTimer"
in_water 这类环境变量。想判断「在水下」只能用 y<你服的海平面高度 近似。BUFF 与宝石配对
BUFF 定义只是「准备好一个标记」。要让玩家能获得它,二选一:
- 在
gems/里写一颗宝石,Rewards用LoreAdd{lore=这里的 Lore 文本} - 直接把这行 lore 加到装备上(用其他插件或命令)
skills/ModernBuffs.yml 里有 7 个已标注为「模板」的定义就属于第一种情况——标记准备好了但没配套宝石,需要你自己加。
性能提醒
BUFF 引擎每秒对每个在线玩家扫 6 个物品槽 × 所有带 Lore 的技能定义。定义数量大(几百个)且在线人数多(上百)时会有可观开销。建议:
- 不需要的 BUFF 定义直接删掉,别留着
- 常驻 BUFF 用
~onTimer,一次性效果用~onUse等事件触发标记 Repeat+ 短interval会在短时间内塞入大量调度任务,谨慎用于高频触发的技能
验证
改完技能配置后 /sgem reload,然后:
/sgem skills确认函数名没写错/sgem triggers确认触发标记没写错/sgem compat all查看配置自检结果
配置自检会明确报出「不是已注册的技能函数」(含拼写建议)和「这一行没有写触发标记,永远不会被触发」这两类最常见的笔误。