- Joined
- Dec 12, 2007
- Messages
- 3,956
- Reaction score
- 49
- Points
- 38
Background: I get asked to write or mod macros quite a bit. As people start learning it themselves, they start really enjoying the process. I was asked to write a fast and concise wizard mac that uses hardcoded spells/AAs. It seemed easier to write it from scratch than to mod his existing one. So while I was writing it, I decided to write a how-to guide for this person and anyone else that may find it of benefit.
First go read: Getting Started - Macro Creation - MMOBugs Wiki
Ok, so you read that. Figure out something you want to do. It can be something easy, or something super complicated. The goal is to do it 1 piece at a time in manageable chunks. Start by writing down the skills you want to use.
A few considerations:
-You can use an INI or hardcode items. Hardcoding loads/executes faster but is less flexible and harder to modify.
-You can be very elaborate or do minimal checks
-Every line checks at the same relative speed. Checking 20 variables in 1 line is 20x faster than checking 1 variable in 20 lines.
-Be clear and concise.
Special stuff usually found at top of macro:
This is a comprehensive list of available TLO/Members for base compile: http://www.macroquest2.com/wiki/index.php/Data_Types
There are 5 commonly used types of variables (there are more, just ive never seen anyone actually use them):
There are 3 variable scopes, or levels of existance.
To create variables you must /declare a type and a scope and value (default values will always be 0 or FALSE)
Looping:
Getting started:
So let's get started by figuring out what I want to do and grouping them into natural groups.
I want to make a wizard macro that will do the following things in this generic grouping/order:
Assist
Combat {
Aggro
Twincast
GoM
Nuke
AANuke
}
Rest {
Med
Harvest
SelfBuff
ModRod
}
Step 1. I feel like using spell_routines.inc to handle my casting, and I want to use the max default turbo setting. Let's start with that blank shell.
Step 2. Add some variables. I prefer modular macros that separate activities in logical groupings so that it is easier for me to remember how to edit them. Others create macros in a single subroutine that does everything. Do whatever works best for you. To add my variables, I will create an Initalize routine.
Step 3. Add the assist name, amount, and command so that you can launch the macro. /mac wiz petesampras 95, default value is group tank and 95%
Also add the /call Initialize to load the variables
Step 4. Add the /calls for the other routines.
Step 5. Actually write the routines.
Here is a basic assist routine:
Here is a generic routine to cast spells/AA/items on you or your target that you dont have to worry about recast time:
Here is a generic casting routine that you do care about the recast timer and dont want to use it as soon its available again:
Here is a generic aggro check that is checked every cast:
Generic sub to cast something once per mob
Generic med routine:
Here is a sub for things you want to cast on groupmates that you may or may not have a recast issue. Ie. Heals, HoTs, delayed heals, Buffs.
EDITOR's NOTE: The errors are fixed in the final example, but not in the other sub sections. Im too lazy.
First go read: Getting Started - Macro Creation - MMOBugs Wiki
Ok, so you read that. Figure out something you want to do. It can be something easy, or something super complicated. The goal is to do it 1 piece at a time in manageable chunks. Start by writing down the skills you want to use.
A few considerations:
-You can use an INI or hardcode items. Hardcoding loads/executes faster but is less flexible and harder to modify.
-You can be very elaborate or do minimal checks
-Every line checks at the same relative speed. Checking 20 variables in 1 line is 20x faster than checking 1 variable in 20 lines.
-Be clear and concise.
Special stuff usually found at top of macro:
Subroutines:#turbo - how fast a macro cycles. base compile cap is #turbo 80, mmobugs is 500.
#define This That - replaces a phrase (This) with another phrase (That) whenever it appears in the macro
#include spell_routines.inc - loads another file to supplment the macro
#event EventName "#1# tells the group, '#2#'#*# - whenever this chat line is seen in game, it triggers an event routine that will be called Sub Event_EventName
Plugins/TLO/members:This is how anything gets done. You can have only 1 subroutine to unlimited subroutines to perform functions. All macros MUST have a Sub Main. Sub Main is automatically accessed when the macro starts, all other subroutines must use the /call command. Ie. /Call Nuke
Subroutines can receive variables and create new ones via using parentheses and you can pass them via the /call command. ie. /call Cast "Spell Name" gem5
Sub Cast(string spell,string gem) - That would receive the "Spell Name" as the new variable ${spell} and "gem5" as the new variable $gem}
Note that quoatation marks are used so that spaces are not an issue.
#chat eqbc - recognizes this as a chat to monitor
This is a comprehensive list of available TLO/Members for base compile: http://www.macroquest2.com/wiki/index.php/Data_Types
Variables:Plugins create Top Level Objects for you to use. Refer to wiki and datatypes to see what you can use. This is how you will access the information you want to use.
These are your ${Me.PctHPs}, ${Spell[Complete Heal].ID}, ${Target.CleanName}, ${Me.Class} type of things that will be required for you to interact with the game.
There are 5 commonly used types of variables (there are more, just ive never seen anyone actually use them):
Code:
int - whole numbers
float - decimal point numbers
timer - timers that count down
bool - TRUE/FALSE
string - phrase or word or sequence of characters. can ghetto function as an int
Code:
local - exists only in the subroutine
outer - exists while the macro is running
global - exists until you shut down mq2/eq
Code:
/declare Nuke1 string outer Flames of War
/declare Timer1 timer global 30s
/declare HitPoints int outer 95
/declare i int local
Mastering those basics will allow you to complete anything you can imagine as long as it is possible.If you want a macro to repeat its sub routines, you need to use loops. There are several types of loops that always need to be paired. Loops are local to the subroutine.
Example for/next loop:
/declare i int local
/for i 1 to 5
/echo Nuke${i}
/next i
That will declare i as a local integer variable. It will then cycle i as if its value was 1, 2, 3, 4, 5 and then continue on. For/Next loops can be cycled up or down, and can use different intervals if needed.
Similar example, but this time going from 20 to 2, with a 2 interval, ie. 20,18,16,...,6,4,2
/declare i int local
/for i 20 downto 2 step 2
/echo Nuke${i}
/next i
/goto loops:
:loop
stuff
/goto :loop
Do/while loops:
MQ2Loops - MMOBugs Wiki
Getting started:
So let's get started by figuring out what I want to do and grouping them into natural groups.
I want to make a wizard macro that will do the following things in this generic grouping/order:
Assist
Combat {
Aggro
Twincast
GoM
Nuke
AANuke
}
Rest {
Med
Harvest
SelfBuff
ModRod
}
Step 1. I feel like using spell_routines.inc to handle my casting, and I want to use the max default turbo setting. Let's start with that blank shell.
Code:
#turbo 80
#include spell_routines.inc
Sub Main(string assist,int amount)
:mainloop
/goto :mainloop
/return
Code:
Sub Initialize
|generic stuff
/noparse /declare GoM string outer (${Me.Song[Gracious Mana].ID}||${Me.Song[Gift of Mana].ID}||${Me.Song[Gift of Radiant Mana].ID}||${Me.Song[Gift of Dreamlike Exquisite Radiant Mana].ID}||${Me.Song[Gift of Exquisite Radiant Mana].ID}||${Me.Song[Gift of Amazing Exquisite Radiant Mana].ID}||${Me.Song[Gift of Phantasmal Exquisite Radiant Mana].ID})
/noparse /declare Twincast string outer (${Me.Song[Twincast].ID}||${Me.Song[Twincast Rk. II].ID}||${Me.Song[TwincastRk. III].ID}||${Me.Song[Improved Twincast].ID}||${Me.Buff[Twincast].ID}||${Me.Buff[Improved Twincast].ID})
/noparse /declare Named string outer (${Target.Named}||${Target.Name.Find[#]} && !${Target.Master.ID})
/noparse /declare CombatConditions string outer (${Target.ID}==${TarID} && ${Target.PctHPs}<=98 && ${Target.LineOfSight})
/declare MedAt int outer 30
/declare MedTimer timer outer 5s
/declare TarID int outer
/declare CurrentSub string outer
/declare loop int outer
/declare ReAssist timer outer 2s
|Nukes
/declare Nuke1 string outer Claw of the Flamewing Rk. II
/noparse /declare NukeConditions1 string outer (!${Twincast})
/declare Nuke2 string outer Ethereal Weave Rk. II
/noparse /declare NukeConditions2 string outer (${Twincast}||${Me.Song[Elemental Flames].ID}||${GoM})
/declare Nuke3 string outer Ethereal Hoarfrost Rk. II
/noparse /declare NukeConditions3 string outer 1
/declare Nuke4 string outer Ethereal Incandescence Rk. II
/noparse /declare NukeConditions4 string outer 1
/declare Nuke5 string outer Ethereal Barrage Rk. II
/noparse /declare NukeConditions5 string outer 1
/declare Nuke6 string outer Force of Will
/noparse /declare NukeConditions6 string outer (!${Me.SpellReady[${Me.Gem[1]}]} && !${Me.SpellReady[${Me.Gem[2]}]} && !${Me.SpellReady[${Me.Gem[3]}]})
/declare Nuke7 string outer Force of Flame
/noparse /declare NukeConditions7 string outer (!${Me.SpellReady[${Me.Gem[1]}]} && !${Me.SpellReady[${Me.Gem[2]}]} && !${Me.SpellReady[${Me.Gem[3]}]})
/declare Nuke8 string outer Force of Fire
/noparse /declare NukeConditions8 string outer (!${Me.SpellReady[${Me.Gem[1]}]} && !${Me.SpellReady[${Me.Gem[2]}]} && !${Me.SpellReady[${Me.Gem[3]}]})
|Jolts
/declare Jolt1 string outer A Hole in Space
/noparse /declare JoltConditions1 string outer (${Me.PctHPs<70||${Me.PctAggro}>90)
/declare Jolt2 string outer Concussive Intuition
/noparse /declare JoltConditions2 string outer (${Me.PctAggro}>70)
/declare Jolt3 string outer Arcane Whisper
/noparse /declare JoltConditions3 string outer (${Named} && !${Me.Song[Silent Casting].ID})
/declare Jolt4 string outer Mind Crash
/noparse /declare JoltConditions4 string outer (${Named} && ${Me.PctAggro}>95)
/declare Jolt5 string outer Concussive Salvo Rk. II
/noparse /declare JoltConditions5 string outer (${Named} && ${Me.PctAggro}>85)
|Freeze
/declare Freeze string outer Skullfreeze Rk. II
/noparse /declare FreezeConditions string outer 1
/declare LastFreeze int outer
|AA
/declare AA1 string outer Empowered Focus of Arcanum
/noparse /declare AAConditions1 string outer 1
/declare AA2 string outer Improved Twincast
/noparse /declare AAConditions2 string outer (!${Twincast})
/declare AA3 string outer Twincast Rk. II
/noparse /declare AAConditions3 string outer (!${Twincast})
/declare AA4 string outer Prolonged Destruction
/noparse /declare AAConditions4 string outer (${Named})
/declare AA5 string outer Lower Element
/noparse /declare AAConditions5 string outer (${Named})
/declare AA6 string outer Distorted Robe of the Frozen Flame
/noparse /declare AAConditions6 string outer 1
/declare AA7 string outer Fury of Ro
/noparse /declare AAConditions7 string outer 1
/declare AA8 string outer Fundament: Second Spire of Arcanum
/noparse /declare AAConditions8 string outer 1
|Harvest
/declare Harvest1 string outer Bucolic Harvest Rk. II
/noparse /declare HarvestConditions1 string outer (!${Me.Invis} && ${Me.PctMana}<=65)
/declare Harvest2 string outer Harvest of Druzzil
/noparse /declare HarvestConditions2 string outer (!${Me.Invis} && ${Me.PctMana}<=65)
/declare Harvest3 string outer Summoned: Large Modulation Shard
/noparse /declare HarvestConditions3 string outer (!${Me.Invis} && ${Me.PctMana}<=65 && ${Me.CurrentHPs}>24000)
|Buffs
/declare SelfBuff1 string outer Armor of the Stonescale
/noparse /declare SelfBuffConditions1 string outer (${Spell[Armor of the Stonescale].Stacks})
/declare SelfBuff2 string outer Improved Familiar
/noparse /declare SelfBuffConditions2 string outer (${Spell[Improved Familiar].Stacks})
|snare
/declare LastSnare int outer
/declare Snare string outer Atol's Unresistable Shackles
/noparse /declare SnareConditions string outer (${Range.Between[0,25:${Target.PctHPs}]}
/return
Also add the /call Initialize to load the variables
Code:
#turbo 80
#include spell_routines.inc
Sub Main(string assist,int amount)
/declare Assist string outer ${If[${assist.Length},${assist},${Group.MainTank}]}
/declare AssistAt int outer ${If[${amount},${amount},95]}
/call Initialize
:mainloop
/doevents
/call Target
/goto :mainloop
/return
Code:
#turbo 80
#include spell_routines.inc
Sub Main(string assist,int amount)
/declare Assist string outer ${If[${assist.Length},${assist},${Group.MainTank}]}
/declare AssistAt int outer ${If[${amount},${amount},95]}
/call Initialize
:mainloop
/doevents
/call Target
/if (${Me.CombatState.NotEqual[COMBAT]}) /call Rest
/if (${CombatConditions}) /call Combat
/goto :mainloop
/return
Sub Rest
/call Always SelfBuff
/call Always Harvest
/call Med
/return
Sub Combat
/call Once Freeze
/call Always AA
/call Always Jolt
/call Always Nuke
/call Once Snare
/return
Here is a basic assist routine:
Code:
Sub Target
/if ((!${Target.ID} || ${Target.Type.Equal[PC]} || !${ReAssist}) && ${SpawnCount[${Assist} radius 300]}) {
/assist ${Assist}
/varset ReAssist ${ReAssist.OriginalValue}
}
/if (${Target.ID} && (${Target.Type.Equal[NPC]}||${Target.Master.Type.Equal[NPC]}) && ${Target.ID}!=${TarID}) {
/varset TarID ${Target.ID}
}
/return
Code:
Sub Instant(subname,int force)
/varset CurrentSub ${subname}
/declare i int local
/for i 1 to 20
/if (!${Defined[${subname}${i}]}) /return
/if (${${subname}Conditions${i}}||!${Defined[${subname}Conditions${i}]}) {
/if (${FindItem[${${subname}${i}}].InvSlot} && !${FindItem[${${subname}${i}}].Timer}) /call Cast "${${subname}${i}}" item ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]}
/if (${Me.AltAbilityReady[${${subname}${i}}]}) /call Cast "${${subname}${i}}" alt ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]} CheckAggro
/if (${Me.SpellReady[${${subname}${i}}]}||${Me.Book[${${subname}${i}}]} && ${force} && !${Me.AltAbility[${${subname}${i}}]}) /call Cast "${${subname}${i}}" ${DefaultGem} ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]} CheckAggro
}
/next i
/return
Code:
Sub Duration(subname,int force)
/varset CurrentSub ${subname}
/declare i int local
/for i 1 to 20
/if (!${Defined[${subname}${i}]}) /return
/if ((!${${CurrentSub}Recast${i}}||${Last${CurrentSub}${i}}!=${Target.ID}) && (${${subname}Conditions${i}}||!${Defined[${subname}Conditions${i}]})) {
/if (${FindItem[${${subname}${i}}].InvSlot} && !${FindItem[${${subname}${i}}].Timer}) {
/call Cast "${${CurrentSub}${i}}" item ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]}
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/varset Last${CurrentSub}${i} ${Target.ID}
/varset ${CurrentSub}Recast${i} ${${CurrentSub}Recast${i}.OriginalValue}
}
}
/if (${Me.AltAbilityReady[${${subname}${i}}]}) {
/call Cast "${${CurrentSub}${i}}" alt ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]} CheckAggro
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/varset Last${CurrentSub}${i} ${Target.ID}
/varset ${CurrentSub}Recast${i} ${${CurrentSub}Recast${i}.OriginalValue}
}
}
/if (${Me.SpellReady[${${subname}${i}}]}||${Me.Book[${${subname}${i}}]} && ${force} && !${Me.AltAbility[${${subname}${i}}]}) {
/call Cast "${${CurrentSub}${i}}" ${DefaultGem} ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]} CheckAggro
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/varset Last${CurrentSub}${i} ${Target.ID}
/varset ${CurrentSub}Recast${i} ${${CurrentSub}Recast${i}.OriginalValue}
}
}
}
/next i
/return
Code:
Sub CheckAggro
/if (${CurrentSub.Equal[Jolt]}) /return
/for loop 1 to 20
/if (!${Defined[Jolt${loop}]}) /goto :skip
/if (${subname}Conditions${loop}}) {
/if (${FindItem[${Jolt${loop}}].InvSlot} && !${FindItem[${Jolt${loop}}].Timer}) {
/call Interrupt
/call Cast "${Jolt${loop}}" item
}
/if (${Me.AltAbilityReady[${Jolt${loop}}]}) {
/call Interrupt
/alt act ${Me.AltAbility[${Jolt${loop}}].ID}
}
/if (${Me.SpellReady[${Jolt${loop}}]}) {
/call Interrupt
/call Cast "${Jolt${loop}}" gem1 5s CheckAggro
}
/next loop
:skip
/return
Code:
Sub Once(subname)
/if (${Last${subname}==${Target.ID}) /return
/varset CurrentSub ${subname}
/if (!${Defined[${subname}]}) /goto :skip
/if (${subname}Conditions}) {
/if (${FindItem[${${subname}}].InvSlot} && !${FindItem[${${subname}}].Timer}) /call Cast "${${subname}}" item
/if (${Me.AltAbilityReady[${${subname}}]}) /call Cast "${${subname}}" alt
/if (${Me.SpellReady[${${subname}}]}) /call Cast "${${subname}}" gem1 5s CheckAggro
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) /varset Last${CurrentSub} ${Target.ID}
/next loop
:skip
/return
Code:
Sub Med
/if (${Me.PctMana}<${MedAt} && !${MedTimer} && !${Me.Mount.ID} && ${Me.State.Equal[STAND]} && (${Me.CombatState.Equal[ACTIVE]}||${Me.CombatState.Equal[DEBUFFED]} && !${Debuff.Count})) {
/sit
/varset MedTimer ${MedTimer.OriginalValue}
}
/return
Code:
Sub Group(subname,int force)
/varset CurrentSub ${subname}
/declare i int local
/declare x int local
/for x 1 to 20
/if (!${Defined[${CurrentSub}${x}]}) /return
/for i 0 to 5
/if (${${CurrentSub}${x}${Group.Member[${i}].ID}}||!${Group.Member[${i}].ID}) /goto :skip
/if (${${CurrentSub}Conditions${x}}||!${Defined[${CurrentSub}Conditions${x}]}) {
/if (${Spell[${${CurrentSub}${x}}].TargetType.NotEqual[self]}) /squelch /tar id ${Group.Member[${i}].ID}
/delay 1s ${Target.ID}==${Group.Member[${i}].ID}
/if (${FindItem[${${CurrentSub}${x}}].InvSlot} && !${FindItem[${${CurrentSub}${x}}].Timer}) {
/call Cast "${${CurrentSub}${x}}" item ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]}
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/if (!${Defined[${CurrentSub}${x}${Group.Member[${i}].ID}]}) /declare ${CurrentSub}${x}${Group.Member[${i}].ID} timer outer
/if (${Defined[${CurrentSub}Recast${x}]}) /varset ${CurrentSub}${x}${Group.Member[${i}].ID} ${${CurrentSub}Recast${x}}
}
}
/if (${Me.AltAbilityReady[${${CurrentSub}${x}}]}) {
/call Cast "${${CurrentSub}${x}}" alt ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]}
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/if (!${Defined[${CurrentSub}${x}${Group.Member[${i}].ID}]}) /declare ${CurrentSub}${x}${Group.Member[${i}].ID} timer outer
/if (${Defined[${CurrentSub}Recast${x}]}) /varset ${CurrentSub}${x}${Group.Member[${i}].ID} ${${CurrentSub}Recast${x}}
}
}
/if (${Me.SpellReady[${${CurrentSub}${x}}]}||${Me.Book[${${CurrentSub}${x}}]} && ${force} && !${Me.AltAbility[${${CurrentSub}${x}}]}) {
/call Cast "${${CurrentSub}${x}}" ${DefaultGem} ${If[${Defined[${CurrentSub}Color]},${${CurrentSub}Color},Orange]}
/if (${Macro.Return.Equal[CAST_SUCCESS]}||${Macro.Return.Equal[CAST_NOTHOLD]}) {
/if (!${Defined[${CurrentSub}${x}${Group.Member[${i}].ID}]}) /declare ${CurrentSub}${x}${Group.Member[${i}].ID} timer outer
/if (${Defined[${CurrentSub}Recast${x}]}) /varset ${CurrentSub}${x}${Group.Member[${i}].ID} ${${CurrentSub}Recast${x}}
}
}
}
:skip
/next i
/next x
/return
Attachments
Last edited: