Skip to content

触发器与施法

主动触发器

EmakiSkills 内置了 14 种主动触发器,覆盖了鼠标点击、Q 键丢弃和数字键切换三类操作。主动触发器需要玩家在施法模式下手动按键触发:

触发器 ID显示名称事件来源冲突规则
left_click[左键]InteractTriggerSourceshift_left_click 冲突
right_click[右键]InteractTriggerSourceshift_right_click 冲突
shift_left_click[Shift + 左键]InteractTriggerSourceleft_click 冲突
shift_right_click[Shift + 右键]InteractTriggerSourceright_click 冲突
drop_q[Q 键]DropTriggerSource无冲突
hotbar_1[数字键 1]HotbarTriggerSource无冲突
hotbar_2[数字键 2]HotbarTriggerSource无冲突
hotbar_3[数字键 3]HotbarTriggerSource无冲突
hotbar_4[数字键 4]HotbarTriggerSource无冲突
hotbar_5[数字键 5]HotbarTriggerSource无冲突
hotbar_6[数字键 6]HotbarTriggerSource无冲突
hotbar_7[数字键 7]HotbarTriggerSource无冲突
hotbar_8[数字键 8]HotbarTriggerSource无冲突
hotbar_9[数字键 9]HotbarTriggerSource无冲突

冲突规则

冲突规则通过 incompatible_with 配置。当两个触发器互相冲突时,同一玩家不能同时把它们绑定到不同槽位。

为什么 left_clickshift_left_click 要冲突?因为在 Bukkit 中,玩家按 Shift+左键时会同时触发 PlayerInteractEvent 的左键事件。如果两个都绑了技能,系统无法确定玩家到底想放哪个。所以设计上让它们互斥,避免歧义。

事件来源

来源类监听事件说明
InteractTriggerSourcePlayerInteractEvent左键/右键/Shift 组合
DropTriggerSourcePlayerDropItemEventQ 键丢弃物品(事件会被取消,物品不会真的丢出去)
HotbarTriggerSourcePlayerItemHeldEvent数字键切换快捷栏(事件会被取消,快捷栏不会真的切换)

被动触发器

被动触发器由游戏事件自动触发,不需要施法模式。被动技能在定义文件中通过 passive_triggers 列表声明使用哪些被动触发器。

EmakiSkills 内置了 22 种被动触发器:

触发器 ID显示名称触发事件target 传递
attack[攻击命中]玩家攻击实体被攻击实体
damaged_by_entity[被实体伤害]玩家被实体伤害攻击者
damaged[受到伤害]玩家受到任意伤害
death[死亡]玩家死亡
kill_entity[击杀实体]击杀非玩家实体被杀实体
kill_player[击杀玩家]击杀玩家被杀玩家
shoot_bow[射出弓箭]射出弓箭
arrow_hit[箭矢命中实体]箭矢命中实体命中实体
arrow_land[箭矢落地]箭矢命中方块无(传递落点)
shoot_trident[掷出三叉戟]掷出三叉戟
trident_hit[三叉戟命中实体]三叉戟命中实体命中实体
trident_land[三叉戟落地]三叉戟命中方块无(传递落点)
break_block[破坏方块]破坏方块无(传递方块位置)
place_block[放置方块]放置方块无(传递方块位置)
drop_item[丢弃物品]丢弃物品(非潜行)掉落物实体
shift_drop_item[Shift+丢弃物品]丢弃物品(潜行中)掉落物实体
swap_items[交换主副手]交换主副手(非潜行)
shift_swap_items[Shift+交换主副手]交换主副手(潜行中)
login[登录]玩家加入服务器
sneak[潜行]开始潜行
teleport[传送]玩家传送无(传递目标位置)
timer[定时]定时检查

timer 触发器的间隔由 passive_trigger_settings.timer_interval_ticks 配置,默认 20 tick(1 秒)。

被动施法流程

被动技能的施法流程比主动技能简单——跳过了施法模式检查和槽位绑定查找:

游戏事件 → PassiveTriggerDispatcher.dispatch(invocation)

    ├─ 1. 检查触发器是否存在且启用

    ├─ 2. 遍历玩家所有已解锁技能
    │     过滤:trigger_type == passive && passive_triggers 包含当前触发器

    ├─ 3. 检查强制延迟 / 全局冷却 / 技能冷却

    ├─ 4. 检查资源消耗

    ├─ 5. MythicMobs 施法(传递事件目标信息)

    └─ 成功后:消耗资源、记录冷却

target 传递

被动技能触发时,事件中的目标实体和位置会传递给 MythicMobs 的 @target。比如 attack 触发器会把被攻击的实体作为 target,MythicMobs 技能中的 @target 就指向这个实体。没有目标实体的触发器以玩家自身为施法者,不传递额外目标。


施法模式入口

主动技能需要玩家先进入施法模式,触发器事件才会被分发到技能系统。非施法模式下,主动触发器事件会被忽略,玩家的正常操作不受影响。被动技能不受施法模式影响,始终处于监听状态。

施法模式固定使用 F 键(交换主副手键)切换。按一下 F 键进入施法模式,再按一下退出。进入施法模式后,F 键原本的交换主副手功能会被拦截(事件被取消)。

yaml
cast_mode:
  entry_key: "f"
  restore_last_state_on_join: true

为什么不支持 G 键?

G 键是 Minecraft 客户端的本地按键,按键事件不会发送到服务端。Spigot、ProtocolLib、PacketEvents 都无法监听到 G 键。F 键(PlayerSwapHandItemsEvent)是服务端能可靠捕获的按键,所以固定使用 F 键。


主动技能施法流程

当主动触发器事件到达 CastAttemptService 时,会依次执行以下 10 步检查。任何一步失败都会中止施法并返回对应的失败原因:

触发器事件 → CastAttemptService.attemptCast(player, triggerId)

    ├─ 1. 验证输入
    │     player 非空,triggerId 非空

    ├─ 2. 检查施法模式
    │     CastModeService.isCastModeEnabled(player) == true
    │     ✗ → 失败: NOT_IN_CAST_MODE

    ├─ 3. 查找绑定
    │     遍历 PlayerSkillProfile.bindings,找到 triggerId 匹配的 SkillSlotBinding
    │     ✗ → 失败: NO_BINDING

    ├─ 4. 查找技能定义
    │     SkillRegistryService.getDefinition(binding.skillId)
    │     ✗ → 失败: SKILL_NOT_FOUND

    ├─ 5. 验证技能仍在解锁池中
    │     PlayerSkillStateService.getUnlockedSkills(player) 包含 binding.skillId
    │     ✗ → 失败: SOURCE_LOST(装备已卸下)

    ├─ 6. 检查强制延迟
    │     PlayerCastTimingState.isForcedDelayActive() == false
    │     ✗ → 失败: FORCED_DELAY_ACTIVE

    ├─ 7. 检查全局冷却
    │     PlayerCastTimingState.isGlobalCooldownActive() == false
    │     ✗ → 失败: GLOBAL_COOLDOWN_ACTIVE

    ├─ 8. 检查技能冷却
    │     PlayerCastTimingState.isSkillOnCooldown(skillId) == false
    │     ✗ → 失败: SKILL_COOLDOWN_ACTIVE

    ├─ 9. 检查资源消耗
    │     遍历 SkillDefinition.resourceCosts:
    │     ├─ local-resource → 检查 PlayerLocalResourceState
    │     ├─ ea-resource → 通过 EaBridge 检查 EA 资源
    │     └─ ea-attribute-check → 通过 EaBridge 检查 EA 属性
    │     ✗ → 失败: RESOURCE_INSUFFICIENT

    ├─ 10. MythicMobs 施法
    │      MythicSkillCastService.cast(player, mythicSkillId)
    │      ✗ → 失败: MYTHIC_CAST_FAILED

    └─ 成功后处理:
         ├─ 消耗资源(operation = CONSUME 的资源)
         ├─ 记录技能冷却 (cooldown_ticks)
         ├─ 记录全局冷却 (global_cooldown_ticks)
         └─ 记录强制延迟 (forced_global_cast_delay_ticks)

注意资源消耗的时机:检查在第 9 步,但实际扣除在第 10 步成功之后。这样如果 MythicMobs 施法失败,资源不会被白白消耗。

触发器有效性验证

同步技能池时(resync),系统会额外检查每个槽位绑定的触发器是否仍然有效。如果某个触发器在配置中被禁用了(enabled: false),对应的槽位绑定会被自动清除。这避免了"绑了技能但按键没反应"的困惑——与其让玩家自己排查,不如直接清掉无效绑定。

失败原因枚举

失败原因说明
NOT_IN_CAST_MODE未进入施法模式
NO_BINDING触发器未绑定技能
SKILL_NOT_FOUND技能定义不存在(可能被删除或配置错误)
SOURCE_LOST技能来源丢失(装备已卸下,技能不再解锁)
FORCED_DELAY_ACTIVE强制延迟中(刚释放过技能,还在全局最小间隔内)
GLOBAL_COOLDOWN_ACTIVE全局冷却中
SKILL_COOLDOWN_ACTIVE技能冷却中
RESOURCE_INSUFFICIENT资源不足
MYTHIC_SKILL_NOT_FOUNDMythicMobs 技能不存在
MYTHIC_CAST_FAILEDMythicMobs 施法失败

ActionBar 服务

ActionBarService 在施法模式下定时刷新玩家的 ActionBar,显示当前技能槽位状态。玩家可以一眼看到自己绑定了哪些技能、哪个在冷却中。

模板变量

变量说明
{slot_1} ~ {slot_N}各槽位的技能名称(未绑定显示为空)
{slot_display}所有槽位的格式化展示
{forced_delay}当前强制延迟剩余时间

配置示例

yaml
actionbar:
  enabled: true
  refresh_interval_ticks: 10
  template_cast_mode: "&aCast Mode &7| {slot_display}"
  template_idle: "&7Idle"

EaBridge 与 MythicBridge

这两个桥接服务是 EmakiSkills 和外部插件通信的通道。

EaBridge

EaBridge 通过 Bukkit ServicesManager 获取 EmakiAttributeBridge 服务,用于读取和消耗 EA 管理的资源与属性:

方法说明
isAvailable()EA 桥接是否可用(EA 插件是否已加载)
readResourceCurrent()读取 EA 资源当前值
consumeResource()消耗 EA 资源
readAttributeValue()读取 EA 属性值
providerMode()当前提供者模式

MythicBridge

MythicBridge 封装 MythicMobs API,负责实际的技能释放:

方法说明
isAvailable()MythicMobs 是否可用
skillExists()检查 MythicMobs 技能是否存在
cast()执行 MythicMobs 技能释放(主动技能,无 target)
cast(target, location)执行 MythicMobs 技能释放(被动技能,传递 target 实体和位置)

如果 MythicMobs 未安装,isAvailable() 返回 false,所有施法尝试都会在第 10 步失败。


GUI 布局

skills_gui — 技能 GUI(6 行)

行 1: [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框]
行 2: [边框] [技能1] [技能2] [技能3] [技能4] [技能5] [技能6] [技能7] [边框]
行 3: [边框] [技能8] [技能9] [技能10] [...] [...] [...] [...] [边框]
行 4: [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框]
行 5: [边框] [槽位1] [槽位2] [槽位3] [边框] [施法模式] [边框] [边框] [边框]
行 6: [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框]
  • 技能区域(行 2-3):显示所有已解锁的技能,点击选择要绑定的技能
  • 槽位区域(行 5):显示当前绑定的技能槽位,点击后选择触发器
  • 施法模式按钮:切换施法模式开关

trigger_select_gui — 触发器选择 GUI(4 行)

行 1: [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框] [边框]
行 2: [边框] [触发器1] [触发器2] [触发器3] [触发器4] [触发器5] [边框] [边框] [边框]
行 3: [边框] [触发器6] [触发器7] [...] [...] [...] [边框] [边框] [边框]
行 4: [边框] [边框] [边框] [边框] [返回] [边框] [边框] [边框] [边框]
  • 触发器区域(行 2-3):显示所有可用触发器。已被其他槽位使用的触发器会标记,与已选触发器冲突的会显示为灰色不可选
  • 返回按钮(行 4):返回技能 GUI

Released under the GPL-3.0 License