Skip to main content

Spellhandling and Rotation Quickstart Guide

This guide will walk you through the process of using PAL's spellhandling system to create a combat routine. We'll cover populating the spellbook, creating callbacks for spells, and executing them in your rotation. We'll use an Arcane Mage as our example throughout this guide.

Table of Contents

  1. Setting Up Your Environment
  2. Populating the Spellbook
  3. Creating Spell Callbacks
  4. Defining Action Lists
  5. Implementing the Rotation
  6. Advanced Features

Setting Up Your Environment

First, ensure you have the necessary files set up:

  1. Routines/Mage/Arcane/Spellbook_Arcane.lua: Spellbook for Arcane Mage
  2. Routines/Mage/Arcane/Arcane.lua: Rotation logic for Arcane Mage
  3. routines.lua: Update the spec to load your files

In routines.lua, set up the loading of the files - this is a snippet:

    [8] = {
className = "Mage",
basePath = "Routines/Mage/Mage",
specializations = {
[1] = "Routines/Mage/Arcane/Arcane",
[2] = "Routines/Mage/Fire/Fire",
[3] = "Routines/Mage/Frost/Frost"
},
specHelperPaths = {
[1] = {
"Routines/Mage/Arcane/Spellbook_Arcane",
},
[2] = {
"Routines/Mage/Fire/Spellbook_Fire",
},
[3] = {
"Routines/Mage/Frost/Spellbook_Frost",
},
}
},

Populating the Spellbook

In Routines/Mage/Arcane/Spellbook_Arcane.lua, create spell objects for your Arcane Mage:

local Tinkr = ... ---@type Tinkr
local LT = select(2, ...) ---@type LT
local Routine = Tinkr.Routine
local Exports = Tinkr.Routine.Exports
local mage = LT.spellhandling.spellbooks.mage

local NewSpell = LT.spellhandling.NewSpell

LT.spellhandling.PopulateSpellbook({
ArcaneBlast = NewSpell(30451),
ArcaneMissiles = NewSpell(5143),
ArcaneBarrage = NewSpell(44425),
TouchOfTheMagi = NewSpell(321507),
ArcaneSurge = NewSpell(365350, {ignoreCost = true}),
-- Add more spells as needed
}, "MAGE")

This creates spell objects for each Arcane Mage ability. The NewSpell function takes a spell ID as its first argument and an optional table of properties as its second argument.

Creating Spell Callbacks

After populating the spellbook, define callbacks for each spell. These callbacks determine when and how each spell should be cast:

mage.ArcaneBlast:Callback(function(spell, unit)
return spell:Cast(unit)
end)

mage.ArcaneMissiles:Callback(function(spell, unit, logic)
if logic == "clearcasting" then
if Exports:buff(mage.Clearcasting.id, "player") then
return spell:Cast(unit)
end
elseif logic == "opener_st" then
if mage.Evocation:wasLastCast() then
item.StatPotionR3:execute("player")
return spell:Cast(unit)
end
end
end)

mage.ArcaneBarrage:Callback(function(spell, unit, logic)
if logic == "max_charges" then
if getArcaneCharges() == 4 then
return spell:Cast(unit)
end
elseif logic == "low_mana" then
if getPlayerManaPercent() < 70 and mage.Evocation:GetCooldown() > 45 then
return spell:Cast(unit)
end
end
end)

-- Add more callbacks for other spells

Callbacks can take additional arguments (like logic) to define different conditions for casting the same spell.

Defining Action Lists

In Routines/Mage/Arcane/Arcane.lua, define action lists for different scenarios:

local actionList = {}

actionList.Cooldowns = function()
if mage.ArcaneSurge:execute(target, "burn_phase") then return true end
if mage.TouchOfTheMagi:execute(target, "burn_phase_after_arcane_barrage") then return true end
end

actionList.SingleTarget = function()
if mage.ArcaneBarrage:execute(target, "low_mana") then return true end
if mage.ArcaneMissiles:execute(target, "clearcasting") then return true end
if mage.ArcaneBlast:execute(target) then return true end
end

actionList.AOE = function()
if mage.ArcaneExplosion:execute("player") then return true end
if mage.ArcaneOrb:execute(target) then return true end
-- Add more AOE actions
end

These action lists group related actions together, making the main rotation logic cleaner and more modular.

Implementing the Rotation

Now, implement the main rotation logic:

Routine:RegisterRoutine(function()
if LT.ShouldISkip() or casting() or channeling() then
return true
end

local enemyCount = LT.mobCount(10, target)

if combat() then
if LT.button_CD then
if actionList.Cooldowns() then return true end
end
if enemyCount > 1 and LT.config.read('buttons_multitarget', false) then
return actionList.AOE()
else
return actionList.SingleTarget()
end
end
end, Routine.Classes.Mage, Routine.Specs.Mage.Arcane)

This rotation checks for combat, executes cooldowns if enabled, and then chooses between AOE and single-target rotations based on the number of enemies and user settings.

Advanced Features

Custom Buttons

PAL allows you to create custom buttons for your rotation:

local custom_buttons = {
{
key = "autoDefensives",
buttonname = "autoDefensives",
texture = "spell_arcane_arcaneresilience",
tooltip = "Automatically uses defensives",
text = "Automatically uses defensives",
},
-- Add more custom buttons
}

LT.BuildGUI()
LT.button_factory(custom_buttons)

Defensive Abilities

You can create a separate action list for defensive abilities:

actionList.Defensive = function()
if mage.GreaterInvisibility:execute("heavy_mitigation") then return true end
if mage.PrismaticBarrier:execute(player, "medium_mitigation") then return true end
end

Then, call this action list in your main rotation when appropriate.

By following this guide, you should now have a good understanding of how to use PAL's spellhandling system to create a combat routine. Remember to test your rotation thoroughly and adjust as needed for optimal performance.