My Rotations

My Rotations

Name Class / Spec Hero Tree Version Status
Loading…

Edit Rotation

Manifest

Pricing

Empty price = no SKU. Saving creates or updates the SKU for that tier.

Rotation Script (Lua)

Reference the Custom Rotation API: player, target, spell("foo"), buff("foo"), debuff("foo"), cooldown("foo"), resource("foo"), toggle("x"), talent("x"), should_defensive("x"), cast("x" [, "reason"]), defensive(...), interrupt(...). First cast() wins.

Versions

    Simulate

    Run the current saved version against a synthetic combat state for N seconds and inspect the per-tick action timeline. The simulator reuses the same engine the desktop app runs, so a rotation that fires here will fire in-game.

    Custom Rotation API

    Lua scripting surface for custom rotations. The same engine runs in the desktop app and in the simulator, so anything below that compiles + runs here will run identically in-game.

    Execution model: your script runs once per rotation tick (~100 ms). First successful cast() wins — write spells in priority order with if cast("x") then return end. Errors are captured into the trace, never crash the runtime.

    Globals

    Top-level scalars set on every tick.

    in_combatbool — player is in combat
    in_groupbool — player is in a party/raid
    stealthedbool
    mounted / in_vehiclebool
    movingbool
    gcd / gcd_max / gcd_remainsnumber — seconds
    hastenumber — spell haste multiplier
    time / wow_timenumber — engine clock + WoW GetTime()
    fight_remainsnumber — TTD on current target
    enemies / active_enemiesinteger — nearby hostile count
    aoe_modeinteger — 0=ST, 1=auto, 2=AoE
    interrupt_modeinteger — 1=OFF, 2=ALL, 3=Whitelist, 4=Dangerous
    player

    Everything about the local player. All fields read-only; mutate the world via the action functions instead.

    player.hp / player.hp_max / player.hp_pctnumber
    player.class_id / player.spec_idinteger — Blizzard ids
    player.levelinteger
    player.hero_treestring — empty if unknown
    player.in_combat / moving / stealthed / mounted / in_vehiclebool
    player.gcd / gcd_max / gcd_remains / hastenumber
    player.casting / channelingbool
    player.cast_spell_id / channel_spell_idinteger — 0 when idle
    player.cast_end_time / channel_end_timenumber — WoW time
    player.has_cursebool — at least one curse debuff on player
    player.assisted_combat_keybindstring
    player.assisted_combat_spell_idinteger
    player.previous_caststring — last cast spell name
    player.previous_gcd(n)string — nth previous GCD (1 = most recent)
    if player.hp_pct < 35 and not player.in_vehicle then
      if defensive("ice_block", "low hp") then return end
    end
    target

    Current target. Inherits the same fields as any unit, plus stealable + counter-cast info.

    target.exists / alive / deadbool
    target.enemy / friend / in_combatbool
    target.casting / channelingbool
    target.cast_spell_id / channel_spell_idinteger
    target.should_interruptbool — engine-side gate; honor it before interrupt()
    target.health_pct / hp_pctnumber — 0..100
    target.power / power_maxnumber — target's primary resource
    target.rangenumber — yards
    target.time_to_die / ttdnumber — seconds
    target.has_stealable_buffbool — triggers Spellsteal

    target.counter — set when the cast is in the counter database:

    .knownbool — counter DB has this cast
    .is_interrupt / .is_stop / .is_importantbool — cast type
    .is_tank_buster / .is_party_damage / .is_debuffbool
    .is_physical / .is_magic / .is_bleedbool — damage school
    .util_mask / .type_mask / .extra_maskinteger — raw bits if you want to roll your own
    spell(name) / cooldown(name)

    Look up a spell by SimC name (lowercase, underscores). Returns a table even if unknown so .ready is always safe to read.

    spell(n).idinteger — Blizzard spell id, 0 when unknown
    spell(n).name / keybindstring
    spell(n).knownbool — true when the spell book has it AND a keybind is set
    spell(n).usablebool — current power + reagent allow casting
    spell(n).cooldown / cooldown_remains / cooldown_durationnumber — seconds
    spell(n).chargesinteger
    spell(n).pmultipliernumber — persistent DoT multiplier
    spell(n).readybool — usable AND (off-cd OR has charges) AND keybind set

    cooldown(name) returns the cooldown view directly:

    cooldown(n).remains / durationnumber
    cooldown(n).chargesinteger
    cooldown(n).readybool
    cooldown(n).full_recharge_timenumber — secs to all charges
    buff(name) / debuff(name) / aura(name)

    buff reads the player's buff table. debuff reads debuffs on the current target. aura searches buffs first, then debuffs — useful for class-agnostic checks like aura("bloodlust").up.

    .up / .activebool — present (alias)
    .downbool — absent
    .remains / .durationnumber — seconds
    .stack / .stacks / .max_stackinteger
    .pmultipliernumber
    .source_spell_idinteger
    Resources

    Lowercase resource names: mana, rage, focus, energy, combo_points, etc.

    resource(n)number — current
    resource_max(n)number
    resource_pct(n)number — 0..100
    resource_regen(n)number — per second
    Toggles, Talents, Tuning

    User-facing settings from the desktop app's panels.

    toggle(name)bool — names: cooldowns, interrupts, defensive, potion, movement, dispel, cc, auto-target, ooc-attack, …
    talent(name)bool — SimC talent slug
    hero_tree()string — active hero talent tree
    tuning(name)integer — per-spell mode (0=Default, 1=Disabled, 2=Bypass)
    defensive_tuning(name).enabledbool
    defensive_tuning(name).health_pctnumber — threshold
    Defensives + Interrupts (helpers)

    Convenience gates that wrap the user's tuning. Use these instead of rolling your own thresholds — they respect the desktop app's defensive panel + interrupt selector.

    should_defensive(name)bool — user enabled it AND hp_pct ≤ threshold AND spell ready
    interrupt_allowed()bool — top-level "may I press an interrupt right now?" — combines the toggle, selector mode, and target's counter info
    counter_spell_allow(name)bool — per-spell user override (default true)
    counter_rule(spell_id)table — direct counter-DB lookup (.known, .type_mask, etc.)
    Actions — cast / defensive / interrupt / skip

    All four return true if the action was accepted. First-cast-wins: once cast() succeeds, subsequent calls return false without firing. Use the pattern if cast("x") then return end to chain priorities.

    cast(name [, reason])bool — primary rotation press; requires usable + ready + keybind set
    defensive(name [, reason])bool — suggests a defensive press (runtime re-checks tuning)
    interrupt(name [, reason])bool — suggests an interrupt press (runtime re-checks whitelist)
    skip(reason)true — explicit no-op marker recorded in the trace
    -- Priority order — first match wins.
    if interrupt_allowed() and target.casting then
      if interrupt("counterspell") then return end
    end
    if should_defensive("ice_block") then
      if defensive("ice_block", "low hp") then return end
    end
    if buff("brain_freeze").up and cast("flurry") then return end
    if cast("frostbolt") then return end
    Logging + helpers
    print(...)append a line to the tick trace + lumen.log
    trace(...)append only to the tick trace (no log file)
    any{a, b, c}bool — true if any element is truthy
    all{a, b, c}bool — true if every element is truthy (false if empty)
    none{a, b, c}bool — true if every element is falsy
    if all{buff("a").up, buff("b").up, not moving} then
      if cast("burst") then return end
    end
    Full example — Frost Mage opener
    -- 1. Interrupts gated by the user's selector + whitelist.
    if interrupt_allowed() and target.casting then
      if interrupt("counterspell") then return end
    end
    
    -- 2. Defensives gated by the user's defensive panel.
    if should_defensive("ice_block") then
      if defensive("ice_block", "panic") then return end
    end
    
    -- 3. Burst window when cooldowns toggle is on.
    if buff("icy_veins").down and toggle("cooldowns") then
      if cast("icy_veins") then return end
    end
    
    -- 4. Proc consumption (no GCD spent).
    if buff("brain_freeze").up then
      if cast("flurry") then return end
    end
    
    -- 5. Spend Fingers of Frost charges.
    if resource("fingers_of_frost") > 0 then
      if cast("ice_lance") then return end
    end
    
    -- 6. Default filler.
    if cast("frostbolt") then return end

    Developer Profile