Example: Dual-Wield Fury Warrior — Single Target
Dual-Wield Fury Warrior Single-Target Macro
Using a level 60 dual-wield Fury warrior as an example. The core mechanics are:
Smart Heroic Strike queueing and canceling — by monitoring main-hand and off-hand swing timers, Heroic Strike is queued when the main-hand is ahead of the off-hand, and canceled when the off-hand is ahead and rage is low, ensuring that when rage is tight, Heroic Strike only lands on main-hand swings.
Smart ability usage — uses cooldowns and attack power to intelligently decide which ability to cast.
Principle:
Bloodrage / Battle Shout (maintain) → Execute (priority) → Bloodthirst → Whirlwind → Flurry proc → Heroic Strike (event callback)Warning
Requires Heitu version >= 1.11.17. If your stable version is below this, enable Beta Updates in Heitu settings.
Ability Priority:
- Execute — Prioritized when target is below 20% HP (AP < 2000 or Bloodthirst on cooldown)
- Bloodthirst — Use when off cooldown and rage ≥ 30
- Whirlwind — Use when off cooldown and rage ≥ 25
- Flurry Proc — Use Hamstring to proc Flurry when the aura is missing and rage ≥ 70
- Heroic Strike — Auto-managed via event callbacks: queued when main-hand is faster than off-hand, canceled when off-hand is faster and rage < 50
Core technique: Heroic Strike does not trigger the GCD. By precisely controlling when it is queued or canceled, Heroic Strike rides along with main-hand swings, maximizing rage efficiency.
How to Use
- Open Heitu → Super Macro
- Paste the complete code below into the editor
- Click the [保存并且同步到游戏] button in the top-right corner
- In-game, create a macro with
/s S Fury_DualWield()and set a hotkey to spam it
Tips
Heroic Strike queueing and canceling is auto-managed by RegisterOnceEvent. Each Fury_DualWield() call re-registers the event callbacks, which fire on the next swing completion and auto-delete after firing once.
Full Code
-- ============ Dual-Wield Fury Warrior One-Button Macro ============
function Fury_DualWield()
local DEBUG = false -- Debug switch: true=output info, false=silent
local DEBUG_SWING = true -- Heroic Strike callback debug switch
-- Spell/Aura ID constants
local SPELL_BLOODRAGE = 2687 -- Bloodrage
local SPELL_BATTLE_SHOUT = 11551 -- Battle Shout (if you learned TAQ shout, change to 25289)
local SPELL_BLOODTHIRST = 23894 -- Bloodthirst
local SPELL_WHIRLWIND = 1680 -- Whirlwind
local SPELL_EXECUTE = 20662 -- Execute
local SPELL_FLURRY_PROC = 7373 -- Hamstring
local AURA_FLURRY = 12970 -- Flurry aura
if DEBUG or DEBUG_SWING then print("====================================") end
M('/startattack')
M("/cancelaura Blood Craze")
local rage = Power(1) -- Current rage
if DEBUG then print("[Rage] initial=" .. rage) end
-- If rage is too low for Bloodthirst, use Bloodrage
if rage < 30 and SCT(SPELL_BLOODRAGE) == 0 then
if DEBUG then print("[Ability] Bloodrage (rage=" .. rage .. ")") end
Spell(SPELL_BLOODRAGE)
rage = Power(1)
if DEBUG then print("[Rage] refreshed=" .. rage) end
end
-- If Battle Shout is missing, refresh it
if rage > 10 and AuraRemainingTime(SPELL_BATTLE_SHOUT) == 0 then
if DEBUG then print("[Ability] Battle Shout (rage=" .. rage .. ")") end
Spell(SPELL_BATTLE_SHOUT)
return
end
-- Regular rotation phase
local bloodthirstCD = SpellCooldownTime(SPELL_BLOODTHIRST) or 0
local whirlwindCD = SpellCooldownTime(SPELL_WHIRLWIND) or 0
local flurryActive = AuraRemainingTime(AURA_FLURRY) > 0
if DEBUG then
print("[Status] BT CD=" ..
bloodthirstCD .. " WW CD=" .. whirlwindCD .. " Flurry=" .. tostring(flurryActive) .. " Rage=" .. rage)
end
-- Prioritize Execute when AP < 2000 or Bloodthirst is on cooldown
if CanWarriorExecute() and (AP() < 2000 or bloodthirstCD > 0) then
if DEBUG then print("[Decision] Execute available (AP=" .. AP() .. " BT CD=" .. bloodthirstCD .. ")") end
if rage >= 15 then
if DEBUG then print("[Ability] Execute (rage=" .. rage .. ")") end
Spell(SPELL_EXECUTE)
rage = Power(1)
if DEBUG then print("[Rage] refreshed=" .. rage) end
elseif DEBUG then
print("[Skip] Execute — insufficient rage (rage=" .. rage .. " < 15)")
end
else
-- Bloodthirst
if rage >= 30 and bloodthirstCD == 0 then
if DEBUG then print("[Ability] Bloodthirst (rage=" .. rage .. ")") end
Spell(SPELL_BLOODTHIRST)
rage = Power(1)
if DEBUG then print("[Rage] refreshed=" .. rage) end
-- Whirlwind
elseif whirlwindCD == 0 and rage >= 25 then
if DEBUG then print("[Ability] Whirlwind (rage=" .. rage .. ")") end
Spell(SPELL_WHIRLWIND)
rage = Power(1)
if DEBUG then print("[Rage] refreshed=" .. rage) end
-- No Flurry and rage is high — proc it with Hamstring
elseif not flurryActive and rage >= 70 then
if DEBUG then print("[Ability] Hamstring — proc Flurry (rage=" .. rage .. ")") end
Spell(SPELL_FLURRY_PROC)
rage = Power(1)
if DEBUG then print("[Rage] refreshed=" .. rage) end
elseif DEBUG then
print("[Skip] No usable ability (rage=" ..
rage .. " BT CD=" .. bloodthirstCD .. " WW CD=" .. whirlwindCD .. " Flurry=" .. tostring(flurryActive) .. ")")
end
end
-- Register main-hand swing callback
RegisterOnceEvent("SWING_RESULT_HAND1", function()
local mainHandTime = AttackTime1()
local offHandTime = AttackTime2()
if mainHandTime > offHandTime then
if DEBUG_SWING then print("[HS] MH faster than OH (MH=" .. mainHandTime .. " OH=" .. offHandTime .. ") → queue HS") end
M('/cast Heroic Strike')
elseif DEBUG_SWING then
print("[HS] Skip (MH=" .. mainHandTime .. " <= OH=" .. offHandTime .. ")")
end
end)
-- Register off-hand swing callback
RegisterOnceEvent("SWING_RESULT_HAND2", function()
local mainHandTime = AttackTime1()
local offHandTime = AttackTime2()
if mainHandTime < offHandTime then
-- If rage is below 50, cancel Heroic Strike immediately
if rage < 50 then
if DEBUG_SWING then print("[HS] Cancel (rage=" .. rage .. " < 50)") end
SC()
elseif DEBUG_SWING then
print("[HS] Keep (rage=" .. rage .. " >= 50)")
end
elseif DEBUG_SWING then
print("[HS] Skip check (MH=" .. mainHandTime .. " >= OH=" .. offHandTime .. ")")
end
end)
endCustomization
Adjust Rage Thresholds
if rage < 45 then -- Lower to 45 for more aggression, raise to 60 for more conservative play50 rage is the safe line for canceling Heroic Strike. If your gear is good and rage is plentiful, lower it to 45. If you frequently run out of rage, raise it to 60.
Disable Heroic Strike
If you don't want to use Heroic Strike (e.g., when rage is tight), comment out or remove both RegisterOnceEvent blocks. Alternatively:
-- Do not register any Heroic Strike callbacks
-- RegisterOnceEvent(...)Switching Versions: Change Spell IDs
Spell IDs are concentrated at the top of the file — just change those constants:
Enable Debug Logging
local DEBUG = true -- View main logic output
local DEBUG_SWING = false -- Heroic Strike callbacks are spammy; turn off separatelyThe two switches are independent: DEBUG controls ability usage and status changes, DEBUG_SWING only controls Heroic Strike callback logging.
Notes
- This macro must be spammed — bind it to mouse wheel, a high-frequency key, or use Heitu's Key Wizard
- Heroic Strike is auto-managed by events; the macro's spam rate determines how often events are re-registered
RegisterOnceEventauto-deletes after firing once; each macro execution re-registers it- You need to enable the Mod Model feature in Heitu first; Super Macro will then take effect automatically
M('/cancelaura Blood Craze')cancels the Blood Craze aura to prevent excessive threat generation- Spell IDs correspond to level 60 (1.12.1). If you use another version, adjust the constants at the top
- You must dual-wield one-handed weapons (main-hand + off-hand). For two-handed weapons, see Two-Handed Slam Warrior
FAQ
Heroic Strike is not going off
Check three things:
- Set
DEBUG_SWINGtotrueand read the chat output - Is rage ≥ 50? (Below 50, it gets canceled by the off-hand)
- Is the main-hand timer actually ahead of the off-hand?