跳转到内容

动态宏与完整实例

无需预先写死宏名称与正文:在游戏端初始化代码里指定槽位并调用 SuperDPS.SetMacro,桌面端逻辑再用 Macro 按序号或名称调用。两个环境通过经过校验的注册表连接。

最小实例:按游戏语言生成技能宏

Section titled “最小实例:按游戏语言生成技能宏”

在编辑器保留第 1 槽的有效热键,名称与正文可以为空。将以下代码粘贴到「初始化代码」;1766 是示例技能 ID,请替换成当前角色要使用的技能。保存、生成插件并在游戏中 /reload。

local spellID = 1766
local name = C_Spell and C_Spell.GetSpellName
and SuperDPS.SN(C_Spell.GetSpellName(spellID), "macro.name")
if type(name) == "string" and name ~= "" then
local body = "/cast " .. name
SuperDPS.SetMacro(1, "interrupt", body)
end

在桌面端逻辑里选择一种调用方式即可:下面按名称调用,换成 Macro(1) 则按序号调用。不要为了演示两种写法而连续调用两次。

local submitted = Macro("interrupt")
接口 规则
SuperDPS.SetMacro(slot, name, body) 仅游戏端。首次登记或更新返回 false, "pending";相同内容实际已生效返回 true, "ready"。非法参数报错,不改原注册表。
Macro(slotOrName) 仅桌面逻辑。按整数序号或精确名称匹配,返回布尔值;提交热键不等于游戏成功施法。
1..105 允许动态设置的槽位;106–150、reload、SelectTarget 名称保留。热键无效、显式为空或冲突的槽位不能注册。
name / body 名称为 1–96 字节有效 UTF-8,不可纯空白或含控制字符;不同序号不能重名。正文非空且不含 NUL,多行用 Lua 的 \n 或 table.concat。
SetMacro(1, ...) 相同序号覆盖名称与正文,热键不变,旧名称失效。修改只属于当前游戏会话,不回写编辑器或 .wa1;重载后重新初始化。

将初始化逻辑收在函数中,通过事件触发更新。接口会合并同一槽位的最新请求;战斗中只排队,不改安全按钮。以下展示多行正文与脱战更新,槽位 2 的热键也需有效。

local function refresh()
if InCombatLockdown() then return end
local body = table.concat({"/say ready", "/startattack"}, "\n")
SuperDPS.SetMacro(2, "start", body)
end
local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:SetScript("OnEvent", refresh)
refresh()

更新先标记不可调用,至少等待 250ms 并脱战后写入按钮,再读回正文与实际热键。注册表完整同步之前,Macro/Cast 暂停调用。热键被改变时会撤销就绪状态并排队重绑;重绑失败可再次提交相同请求重试。宏名越长、数量越多,同步越久,尽量使用简短稳定的名称。

完整实例:官方辅助作战精简模块

Section titled “完整实例:官方辅助作战精简模块”

初始化从官方轮转候选获取技能,以基础技能 ID 去重、分配稳定槽位,并用本地化技能名生成宏。一个 AssistMacro 属性回传当前推荐对应的槽位;一条逻辑完成调用。没有骑乘、聊天、死亡、距离等额外暂停条件;无推荐、未知技能或无效数据返回 0,不发键。

下载完整 .wa1 示例 · 下载初始化 Lua。导入后绑定 official 逻辑;宏表保留空名称与正文,热键使用模板。实例依赖正式服 C_AssistedCombat,API 不存在时不执行。

属性名填写 AssistMacro,自定义属性代码如下:

return SuperDPS.AssistMacro()

逻辑代码:

local slot = Prop("AssistMacro")
if slot > 0 then Macro(slot) end
展开完整初始化代码
-- 事件合并读取候选;只在脱战时更新变化的宏,保留仍在使用的槽位。
local slots, active, entries = {}, {}, {}
local dirty, queued, notified, announce = true, false, false, false
local checks, count = 0, 0
local schedule
-- 只扫描一次预设;缺少占用信息时拒绝猜测,避免旧客户端覆盖只有正文的宏。
local firstSlot, capacity, available, reservedNames = 1, 0, {}, {}
local compatible = true
for slot, row in pairs(SuperDPS.DynamicSlots) do
if row.name and row.name ~= "" then reservedNames[row.name] = true end
if slot >= 1 and slot <= 105 then
if type(row.occupied) ~= "boolean" then compatible = false end
if row.occupied then firstSlot = math.max(firstSlot, slot + 1) end
end
end
for slot = firstSlot, 105 do
local row = SuperDPS.DynamicSlots[slot]
if row and row.occupied == false and row.key and row.key ~= "" then
available[slot], capacity = true, capacity + 1
end
end
local function macroName(name)
while reservedNames[name] do name = name .. "_" end
return name
end
local function baseSpell(id)
id = SuperDPS.SN(id, "assist.spell")
if type(id) ~= "number" or id <= 0 then return 0 end
local base = SuperDPS.SN(C_Spell.GetBaseSpell(id), "assist.base")
return type(base) == "number" and base > 0 and base or 0
end
local function request(slot, name, body)
local entry = entries[slot]
if entry and entry.name == name and entry.body == body then return end
entries[slot] = {name = name, body = body, sentName = entry and entry.sentName}
announce = true
end
local function refresh()
if InCombatLockdown() then return end
if not compatible then
error("请更新客户端并重新生成插件:缺少预设宏占用信息")
end
if dirty then
if not (C_AssistedCombat and C_AssistedCombat.GetRotationSpells
and C_Spell and C_Spell.GetBaseSpell and C_Spell.GetSpellName) then
active = {}
return
end
local wanted, order, used, nextSlots = {}, {}, {}, {}
for _, id in ipairs(C_AssistedCombat.GetRotationSpells()) do
local base = baseSpell(id)
local name = base > 0 and SuperDPS.SN(C_Spell.GetSpellName(base), "assist.name")
if type(name) == "string" and name ~= "" and not wanted[base] then
wanted[base] = "/cast " .. name
order[#order + 1] = base
end
end
if #order > capacity then
active = {}
error("辅助宏槽位不足:需要" .. #order .. "个,预设宏之后可用" .. capacity .. "个")
end
-- 先保留仍在使用的槽位,再回收移出的技能;旧宏改为无动作正文。
for base, slot in pairs(slots) do
if wanted[base] then
nextSlots[base], used[slot] = slot, true
else
request(slot, macroName("assist_empty_" .. slot), "/stopmacro")
end
end
local free = firstSlot
for _, base in ipairs(order) do
local slot = nextSlots[base]
if not slot then
while not available[free] or used[free] do free = free + 1 end
slot = free
nextSlots[base], used[slot] = slot, true
end
request(slot, macroName("a" .. base), wanted[base])
end
slots, count, active = nextSlots, #order, {}
dirty, checks = false, 0
end
-- 先释放已提交的旧名称,避免连续切换列表时跨槽位名称冲突。
for slot, entry in pairs(entries) do
if entry.sentName and entry.sentName ~= entry.name then
if InCombatLockdown() then return end
local emptyName = macroName("assist_empty_" .. slot)
SuperDPS.SetMacro(slot, emptyName, "/stopmacro")
entry.sentName = emptyName
end
end
local ready = true
for slot, entry in pairs(entries) do
if not entry.ready then
if InCombatLockdown() then return end
entry.ready = SuperDPS.SetMacro(slot, entry.name, entry.body)
entry.sentName = entry.name
ready = entry.ready and ready
end
end
-- 仅公布已写入的宏;客户端仍通过完整快照和新鲜度校验后执行。
active = {}
for base, slot in pairs(slots) do
if entries[slot].ready then active[base] = slot end
end
if ready then
if announce and (notified or count > 0) then
local label = notified and "辅助宏更新完毕" or "辅助宏初始化完毕"
SuperDPS.Print(label .. "(" .. count .. " 个),请等待客户端同步")
notified = true
end
announce = false
elseif checks < 20 then
checks = checks + 1
schedule(0.3)
end
end
schedule = function(delay)
if queued or InCombatLockdown() then return end
queued = true
C_Timer.After(delay or 0.1, function()
queued = false
local ok, err = pcall(refresh)
if not ok then
active, dirty = {}, true
SuperDPS.Print("辅助宏更新失败:" .. tostring(err))
end
end)
end
SuperDPS.AssistMacro = function()
if not (C_AssistedCombat and C_AssistedCombat.GetNextCastSpell
and C_Spell and C_Spell.GetBaseSpell) then return 0 end
return active[baseSpell(C_AssistedCombat.GetNextCastSpell())] or 0
end
local frame = CreateFrame("Frame")
for _, event in ipairs({"PLAYER_LOGIN", "PLAYER_ENTERING_WORLD", "SPELLS_CHANGED",
"PLAYER_SPECIALIZATION_CHANGED", "PLAYER_REGEN_ENABLED"}) do
frame:RegisterEvent(event)
end
frame:SetScript("OnEvent", function(_, event, unit)
if event ~= "PLAYER_SPECIALIZATION_CHANGED" or unit == "player" then
dirty, checks = true, 0
schedule()
end
end)
schedule()

预设宏保持不变,辅助宏从最后一个预设宏之后追加,跳过无效或冲突热键。候选变化只在脱战后差异更新并复用辅助槽位;剩余容量不足时提示。新版示例依赖生成器 occupied 标记,需要客户端 20260913.120630 或更新版本。此实例已通过导入导出、云端往返、Lua 5.1 与跨端调用回归,尚未完成游戏实机验收;不能把代码测试通过当成实战施法成功率。