本帖最后由 碎碎冰先生 于 2026-5-29 10:01 AM 编辑
为了庆祝论坛恢复,写一篇关于 SecureActionButtonTemplate(SABT)的介绍。
本文仅作技术介绍,并适配 7.35 单机服务器非正式服。私服风险自行甄别,本帖只讨论技术原理,不鼓励任何违规行为,请勿恶意联想。各版本 API 不同,实现方式亦有差异。
NGA 上有人这么比喻:暴雪规定上班时间禁止饮食,但可以喝水,而水的属性是透明的。于是有人发现,只要是透明液体就行——就有人喝透明奶茶(三得利确实出过透明奶茶)。
通过暴雪允许的安全操作对这杯"奶茶"进行包装,同样能通过安全检查。SABT 的价值不在于"允许在战斗中点击",而在于:只要通过该模板转发,操作就在安全执行路径内合法执行。Taint 系统本身就允许通过安全模板运行——这是在规则内合法通行,而非绕过规则。
有人可能会问:这东西有什么用?我打怪写脚本直接模拟键盘输入不就行了?
对于一般的输出职业和坦克职业确实用不到,治疗职业打个 3v3 也未必需要。
但我有过这样的经历:以前尝试写治疗插件时,团队框架上动辄 30 个人。最直观的思路是——遍历距离范围内的团队成员血量,找到血量最低的人,然后对他施放技能。但现实操作中,脚本需要先将鼠标移动到对应角色的具体坐标、点中血条、再施法(以前这么做倒也凑合,但总归浪费资源)。这一套流程非常呆:来个弹窗遮住血条,软件点不上、选不中人,就全炸了。那直接用宏选择 raid1~30 不就解决问题了吗?但接着就衍生出更多问题:一个治疗技能就得做 30 个宏,如果我还有 7 个治疗技能呢?7 × 30 = 210 个宏,键盘根本不够放。
这时安全模板就有用了,它的本质是一个容器。解决方案是:一个按键对应一个目标,210 个治疗宏就能全部绑定,而且不影响正常使用。如果改了天赋只需要在脱战后自动重新生成210个宏SetAttribute("macrotext", ...)
讲一下原理。SecureActionButton 将"按键按下"这个硬件事件与"施放法术/执行宏"这个安全操作绑定在一起,意味着战斗中点击是被允许的。
回到前面按键不够用的问题:同一个技能,为什么非要分配 30 个键?不需要。可以设计一个按键池,通过修饰键 × 基础键的组合来扩充——实际上这就是一个笛卡尔积。
每个槽位按顺序生成绑定: ``` slot 1 = CTRL-NUMPAD1 slot 2 = CTRL-NUMPAD2 ... slot 11 = CTRL-NUMPADDECIMAL (ctrl+小键盘上的点,测试时用的这个,按了之后自动打寒冰锁链) ... slot 35 = ALT-NUMPAD1 ``` 具体代码这里就不贴了,我已经测试成功。我玩的是空穴来枫单机 7.35,空穴那个服的思路是直接改服务器端(单机服),通过改数据库赋予机器人权限,同时把自己的角色也变成机器人。旧世界的 Boss AI 很智能,强烈推荐,机器人陪玩确实顶中顶。
大佬们把全自动打本的机器人都研究出来了,我为什么还要研究图色呢? 爱好,闲的……
下面以 7.35 版本讲一下 Lua 插件的实现思路: 1. 创建框架 2. 创建 SecureActionButton 3. 设置类型为宏文本:`SetAttribute("type", "macro")` 4. 注册点击方式:`RegisterForClicks("AnyUp", "AnyDown")` 5. 绑定按键到按钮点击:`SetOverrideBindingClick(UIParent, false, key, name, "LeftButton")` 6. 设置宏文本:`SetAttribute("macrotext", macro)` 注意:战斗中禁止创建绑定或清除绑定,就是说改了天赋要脱战才会创建新的宏。 为什么战斗中可以改天赋呢?这个确实有过但是我不会,怀疑是把那个API弄成空的。
SABT 的宏执行依赖 PreClick/PostClick 脚本处理器来动态设置属性(比如根据当前状态决定 /cast 谁)。注册两个事件的作用是: AnyDown:按下时触发 PreClick,可以设置 self:SetAttribute("unit", target),动态地修改按钮的unit目标等属性,决定这次点击要对谁施法。 AnyUp:松开时触发 PostClick,可以清理,重置之前动态设置的unit属性。
按键绑定会被缓存,走的是 `SetOverrideBindingClick` 路径,不经过动作条。`SetOverrideBindingClick` 是覆盖绑定,优先级高于动作条,但不会写入系统按键设置。
对应的职业模块也需要建立法术列表,技能 ID 用数组索引映射,例如:
```lua local staticSpells = { [1] = 49143, -- 冰霜打击 Frost Strike [2] = 49184, -- 湮灭 Howling Blast [3] = 49020, -- 凛风冲击 Obliterate ... [11] = 45524, -- 寒冰锁链 Chains of Ice ... } ```
数组索引= 按键池的槽位编号。`staticSpells[11]` 对应 `macroKind[11] = CTRL-NUMPADDECIMAL`。
再做个比喻,理解为一个严格的生产工具:如果有100种工作需要完成,就生产100种特定的工具并用标签贴好。当工人工作时,只需要看一眼需要完成什么任务,就知道该从墙上摘取哪个工具,完全不需要把工具销毁。当它需要施放技能时,再去调用对应的宏。
我自己也踩过坑:`staticSpells` 中存储的是数字法术 ID(如 45524),在生成宏文本时,必须先转换为法术名称——法术名称是本地化字符串。
错误写法——直接拼接数字: ```lua macroBody = "/cast " .. spell -- 生成 /cast 45524 ```
正确写法——数字 ID 先转为法术名: ```lua local spellName = type(spell) == "number" and GetSpellInfo(spell) or spell if spellName then macroBody = "/cast " .. spellName -- 生成 "/cast 寒冰锁链" end ```
进一步发散:还可以写什么宏呢?比如动态目标、buff条件宏、优先级宏、使用物品宏、设置焦点、宠物攻击等等。
有时候能看到有些玩具就算没有把技能放到技能栏也能输出
或者技能栏有技能但是没有绑定按键也能输出
再或者玩具自动输出的时候不影响你手动操作,可能就是这个原理。
|