When I was first trying out MQ2Melee I loved the power of options added by the holy/downshits and I started at looking at examples and learning from them bit by bit as I needed to. It feels a lot like a more advanced version of the Final Fantasy XII Gambits system and is great when you do not want to full automate everything but like adding tasks bit by bit so you learn what your characters are doing. There is already a great guide for the plugin MQ2Melee but I wanted to give a few tips that I found helpful when starting out dabbling with it.
Overview
MQ2Melee does an amazing job of managing having characters position themselves and use spammable melee skills on enemies such as Bash and Taunt. While it supports a large range of abilities, by default it will not cast spells or abilities that require guidance from you. However, it has extra options that act as a list of conditional statements, that are constantly checked and if the values match up then it will perform what command you have entered after. The options are split between downshits for out of combat and holyshits for in combat. So generally you can put buffs in downshits and damage dealing in holyshits.
Most of the conditionals you can start with are pretty straightforward. Such as:
This translates to if you are out of combat and you do not have the "Blessing of Temperance" buff then cast it from gem8. It will even memorize that spell in that gem if you do not already have it. So you can use gem8 again for each buff and not have to stop and memorize each one yourself. You can add many different conditions onto each check and so customize when you want things to occur.
This is the same command but with extra conditions that need to be met, including not being in combat, not being invisible, not moving, not casting, mana being over 50% and the first group member not being below 70% health. A reference for all the option values can be seen on the wiki under Top Level Objects.
Checking with /echo
You can test your conditionals or what values the data in your TLOs are at any time by using /echo to print them in the mq2 chat window.
downshitif
This acts as a global set of rules for downshits so that mq2melee does not try to trigger downshits until they are met. A great example is from s0rcier in the MQ2Melee Post:
This means that you do not have to repeatedly put these condition checks in each downshit and so makes your checks more readable and more focused on what you actually want to trigger them.
/multiline
This allows you to run multiple commands on a single line, which as each conditional is a single line is hugely useful. It can be added after an /if in order to do multiple commands if that condition is met, or at the beginning of the line in order to do several checks at once. Each command is split with a semicolon and care should be given to make sure you have them in place correctly.
Declared variables and saving targets
You can prepare variables for use with /declare and set them with /varset afterwards. In this example you define a variable SavedTarget which is an int for storing a number, in this case a target id number. Store the current target id with /varset and then afterwards you can use /tar id to target the saved id number in the variable.
One downside of this is that the saved target can die in the process of you casting and so then it will fail to go back to a valid target. A method around this is to instead use another check to confirm the target still exists.
If preferred you can instead default to either remove any target with
Toggling flags on and off
You can enable or disable any of the checks by using a social button in game to change the flag value. Want to disable that stun spam? Set a social button with
You can also set social buttons for stopping and starting everything quickly in the event of you wanting to take full control or an error spamming. I recommend a Halt button with:
A Melee OFF
And a Melee ON
Stickbreak
By default characters will try to stick next to their target in order to stay in melee range and attack. Even to the point of ignoring you trying to manually take over. Setting
Ranged mode
If you have the range variable set to a value such as
Mixing healing and meleeing
When you try to mix healing and meleeing there are some awkward points. First, you need to change your target to the ally you want to heal, which can break autoattack out of being engaged because you cannot attack allies, and you would not be much of a healer if you were hitting your ally over the head while healing them! You then need to change back to your original target to continue meleeing and you also need to add a delay before you try and switch back while you cast the spell. If you have
Short term buffs
There is a limitation of the Group data values is that you cannot check other group members buffs directly. You can do it by targeting them but if you are constantly targetting party members then you are not able to melee. There are other plugins that can help remove this limitation but we are doing this in mq2melee so instead we can work out when to recast a short duration buff with a timer.
Full example of Cleric downshits/holyshits
Here is my solution to try to allow a healer to continue meleeing and keep up a short term buff like regen. It is limited but it does act as a quick way to have single target healing in MQ2Melee and to act as an example for different things people might try to do.
This setup assumes that position 1 (Group.Member[1]) in the group is your tank and that your healing spell is ready in the selected gem. It is also using earlier game spells as most examples are for higher level AA abilities and I wanted to try to give an example of lower level use. Note that the same healing entries are put in both downshits and holyshits to ensure that healing will be done regardless of state. The downshits will be used if you have not engaged the enemy or are healing health loss after a battle and holyshits will be used in combat. The conditions that check for corpse status or being in the zone are to ensure the poor healer does not have a fit trying to heal a corpse or someone outside the zone, both of which can happen if a party member dies.
If there are better ways of doing them then please point it out. I hope some of these points can help others and having a set example can make this easier for others to learn.
Overview
MQ2Melee does an amazing job of managing having characters position themselves and use spammable melee skills on enemies such as Bash and Taunt. While it supports a large range of abilities, by default it will not cast spells or abilities that require guidance from you. However, it has extra options that act as a list of conditional statements, that are constantly checked and if the values match up then it will perform what command you have entered after. The options are split between downshits for out of combat and holyshits for in combat. So generally you can put buffs in downshits and damage dealing in holyshits.
Most of the conditionals you can start with are pretty straightforward. Such as:
downshit9=/if (!${Me.Buff["Blessing of Temperance"].ID} ) /casting "Blessing of Temperance" gem8
This translates to if you are out of combat and you do not have the "Blessing of Temperance" buff then cast it from gem8. It will even memorize that spell in that gem if you do not already have it. So you can use gem8 again for each buff and not have to stop and memorize each one yourself. You can add many different conditions onto each check and so customize when you want things to occur.
downshit9=/if (!${Me.Buff["Blessing of Temperance"].ID} && !${Me.CombatState.Equal[COMBAT]} && !${Me.Invis} && !${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>50 && !${Group.Member[1].PctHPs}<70 ) /casting "Blessing of Temperance" gem8
This is the same command but with extra conditions that need to be met, including not being in combat, not being invisible, not moving, not casting, mana being over 50% and the first group member not being below 70% health. A reference for all the option values can be seen on the wiki under Top Level Objects.
Checking with /echo
You can test your conditionals or what values the data in your TLOs are at any time by using /echo to print them in the mq2 chat window.
/echo ${Melee.Status}
ENGAGED MELEE
/echo (!${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>30 && !${Group.Member[1].PctHPs}<70)
(TRUE && TRUE && FALSE && TRUE)
downshitif
This acts as a global set of rules for downshits so that mq2melee does not try to trigger downshits until they are met. A great example is from s0rcier in the MQ2Melee Post:
downshitif=${If[!${Me.Invis} && ${Me.Standing} && ${Melee.Immobilize} && !${Me.Moving} && !${Cast.Timing} && ${Cast.Ready[]} && ${Me.PctMana}>30,1,0]}
This means that you do not have to repeatedly put these condition checks in each downshit and so makes your checks more readable and more focused on what you actually want to trigger them.
/multiline
This allows you to run multiple commands on a single line, which as each conditional is a single line is hugely useful. It can be added after an /if in order to do multiple commands if that condition is met, or at the beginning of the line in order to do several checks at once. Each command is split with a semicolon and care should be given to make sure you have them in place correctly.
/multiline ; /casting "Celestial Healing" gem2 -targetid|${Group.Member[1].ID} ; /timed 60 /xtar 1 ; /if (${celehealtimer}==0) /varset celehealtimer 24s
Declared variables and saving targets
You can prepare variables for use with /declare and set them with /varset afterwards. In this example you define a variable SavedTarget which is an int for storing a number, in this case a target id number. Store the current target id with /varset and then afterwards you can use /tar id to target the saved id number in the variable.
/if (!${Defined[SavedTarget]}) /declare SavedTarget int global
/varset SavedTarget ${Target.ID}
/tar id ${SavedTarget}
One downside of this is that the saved target can die in the process of you casting and so then it will fail to go back to a valid target. A method around this is to instead use another check to confirm the target still exists.
/if (${SpawnCount[${SavedTarget}]} && !${Me.Casting.ID} && ${Target.ID}!=${SavedTarget}) /squelch /tar id ${SavedTarget}
If preferred you can instead default to either remove any target with
/tar clear
or /xtar 1
which is the first entry in the extended target list. If you have your group roles set and an main assist you can set the first target for the extended target list to be the target of the main assist to make it a safe fallback (via /xtar set 1 groupassisttarget
).Toggling flags on and off
You can enable or disable any of the checks by using a social button in game to change the flag value. Want to disable that stun spam? Set a social button with
/melee holyflag7=0
and it will no longer check the holyshit7 entry. Want it back on? /melee holyflag7=1
. I often have a Burn ON and Burn OFF buttons for casters that will turn on and off their main damage spam spells. And a similar Aggro ON and OFF buttons for tanks using /melee taunt=0
You can also set social buttons for stopping and starting everything quickly in the event of you wanting to take full control or an error spamming. I recommend a Halt button with:
/afollow off
/stick off
/melee reset
A Melee OFF
/melee off
And a Melee ON
/melee load
/melee on
Stickbreak
By default characters will try to stick next to their target in order to stay in melee range and attack. Even to the point of ignoring you trying to manually take over. Setting
stickbreak=1
allows you to move your character yourself instead of being locked to the target. The character will still try to run back to melee range when you stop moving if you have not stopped mq2melee.Ranged mode
If you have the range variable set to a value such as
range=200
then when you use /killthis
your character will try to attack with ranged weapons, even if they do not have any equipped. This can be used to keep casters in position instead of having them run up to melee, but still have them enter ENGAGED status so that they are using their holyshits to attack. Note that they will still melee if they are close enough.Mixing healing and meleeing
When you try to mix healing and meleeing there are some awkward points. First, you need to change your target to the ally you want to heal, which can break autoattack out of being engaged because you cannot attack allies, and you would not be much of a healer if you were hitting your ally over the head while healing them! You then need to change back to your original target to continue meleeing and you also need to add a delay before you try and switch back while you cast the spell. If you have
override=1
set then you will keep attack on while changing target but otherwise you will need to start attacking again after switching over./timed 60 /cast "Healing" gem2
will wait 6 seconds and then cast the Healing spell from the second spell gem. However, when we run commands together in a /multiline it does not wait until a /casting is complete so the /timed command has been started straight after the /casting one. So, we need to put the /timed entry *after* the cast spell with approximately the spell cast time + 1 second for recovery. So a 5 second spell would be 50 + 10 for one more second to have 60 as the /timed value.Short term buffs
There is a limitation of the Group data values is that you cannot check other group members buffs directly. You can do it by targeting them but if you are constantly targetting party members then you are not able to melee. There are other plugins that can help remove this limitation but we are doing this in mq2melee so instead we can work out when to recast a short duration buff with a timer.
/declare celehealtimer timer outer 0
is defining a variable called 'celehealtimer' that will count down any number it is set to in seconds. So if you have something like /if (${celehealtimer}==0) /varset celehealtimer 24s
at the end of a spell cast which will set the timer to start counting down from 24, you can add ${celehealtimer}==0
into the conditional for it to only run when at zero. So the rest of the time the 24 seconds are counting down this condition will be skipped.Full example of Cleric downshits/holyshits
Here is my solution to try to allow a healer to continue meleeing and keep up a short term buff like regen. It is limited but it does act as a quick way to have single target healing in MQ2Melee and to act as an example for different things people might try to do.
This setup assumes that position 1 (Group.Member[1]) in the group is your tank and that your healing spell is ready in the selected gem. It is also using earlier game spells as most examples are for higher level AA abilities and I wanted to try to give an example of lower level use. Note that the same healing entries are put in both downshits and holyshits to ensure that healing will be done regardless of state. The downshits will be used if you have not engaged the enemy or are healing health loss after a battle and holyshits will be used in combat. The conditions that check for corpse status or being in the zone are to ensure the poor healer does not have a fit trying to heal a corpse or someone outside the zone, both of which can happen if a party member dies.
If there are better ways of doing them then please point it out. I hope some of these points can help others and having a set example can make this easier for others to learn.
Cleric mq2melee down/holy examples:
; Out of Combat
; Define timers used for delaying recast until duration of spell is complete
downflag0=1
downshit0=/if (!${Defined[celehealtimer]}) /declare celehealtimer timer outer 0 ;
; Heal Self
downflag1=1
downshit1=/if (${Me.PctHPs}<50 && ${Me.PctHPs}>0 && ${Me.PctMana}>20 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Me.ID} ; /timed 60 /tar clear
; Regen Tank
downflag2=1
downshit2=/if (${celehealtimer}==0 && ${Group.Member[1].PctHPs}<90 && ${Group.Member[1].Distance}<${Spell["Celestial Healing"].Range} && ${Me.PctMana}>10 && ${Cast.Ready["Celestial Healing"]} ) /multiline ; /casting "Celestial Healing" gem2 -targetid|${Group.Member[1].ID} ; /timed 60 /xtar 1 ; /if (${celehealtimer}==0) /varset celehealtimer 24s
; Single Heal each party member if needed
downflag3=1
downshit3=/if (${Group.Member[1].PctHPs}<70 && ${Group.Member[1].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[1].OtherZone} && ${Group.Member[1].Present} && ${Group.Member[1].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[1].ID} ; /timed 60 /tar clear
downflag4=1
downshit4=/if (${Group.Member[2].PctHPs}<70 && ${Group.Member[2].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[2].OtherZone} && ${Group.Member[2].Present} && ${Group.Member[2].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[2].ID} ; /timed 60 /tar clear
downflag5=1
downshit5=/if (${Group.Member[3].PctHPs}<70 && ${Group.Member[3].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[3].OtherZone} && ${Group.Member[3].Present} && ${Group.Member[3].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[3].ID} ; /timed 60 /tar clear
downflag6=1
downshit6=/if (${Group.Member[4].PctHPs}<70 && ${Group.Member[4].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[4].OtherZone} && ${Group.Member[4].Present} && ${Group.Member[4].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[4].ID} ; /timed 60 /tar clear
downflag7=1
downshit7=/if (${Group.Member[5].PctHPs}<70 && ${Group.Member[5].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[5].OtherZone} && ${Group.Member[5].Present} && ${Group.Member[5].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[5].ID} ; /timed 60 /tar clear
; Emergency Invulnerability
downflag8=1
downshit8=/if (!${Me.Song["Divine Barrier"].ID} && ${Me.PctHPs}<15 && ${Me.PctHPs}>0 && ${Me.XTarget}>0 ) /casting "Divine Barrier" gem7
; Buffs
downflag8=1
downshit8=/if (!${Me.Buff["Blessing of Faith"].ID} && !${Me.Invis} && !${Me.Moving} && ${Me.PctMana}>30 && !${Group.Member[1].PctHPs}<70 ) /casting "Blessing of Faith" gem8 -targetid|${Me.ID}
downflag9=1
downshit9=/if (!${Me.Buff["Blessing of Temperance"].ID} && !${Me.CombatState.Equal[COMBAT]} && !${Me.Invis} && !${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>50 && !${Group.Member[1].PctHPs}<70 ) /casting "Blessing of Temperance" gem8
downflag10=1
downshit10=/if (!${Me.Buff["Guard of Vie"].ID} && !${Me.CombatState.Equal[COMBAT]} && !${Me.Invis} && !${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>30 && !${Group.Member[1].PctHPs}<70 ) /multiline ; /casting "Guard of Vie" gem8 -targetid|${Me.ID} ;/timed 50 /casting "Guard of Vie" gem8 -targetid|${Group.Member[1].ID}
downflag11=0
downshit11=/if (!${Me.Buff["Symbol of Naltron"].ID} && !${Me.CombatState.Equal[COMBAT]} && !${Me.Invis} && !${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>30 && !${Group.Member[1].PctHPs}<70 ) /multiline ; /casting "Symbol of Naltron" gem8 -targetid|${Me.ID} ;/timed 60 /casting "Symbol of Naltron" gem8 -targetid|${Group.Member[1].ID}
downflag12=0
downshit12=/if (!${Me.Buff["Armor of Faith"].ID} && !${Me.CombatState.Equal[COMBAT]} && !${Me.Invis} && !${Me.Moving} && !${Me.Casting.ID} && ${Me.PctMana}>30 && !${Group.Member[1].PctHPs}<70 ) /multiline ; /casting "Armor of Faith" gem8 -targetid|${Me.ID} ;/timed 60 /casting "Armor of Faith" gem8 -targetid|${Group.Member[1].ID}
; In Combat
; Heal Self
holyflag0=1
holyshit0=/if (${Me.PctHPs}<50 && ${Me.PctHPs}>0 && ${Me.PctMana}>20 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Me.ID} ; /timed 60 /xtar 1
; Regen Tank
holyflag1=1
holyshit1=/if (${celehealtimer}==0 && ${Group.Member[1].PctHPs}<90 && ${Group.Member[1].Distance}<${Spell["Celestial Healing"].Range} && ${Me.PctMana}>10 && ${Cast.Ready["Celestial Healing"]} ) /multiline ; /casting "Celestial Healing" gem2 -targetid|${Group.Member[1].ID} ; /timed 60 /xtar 1 ; /if (${celehealtimer}==0) /varset celehealtimer 24s
; Single Heal each party member if needed
holyflag2=1
holyshit2=/if (${Group.Member[1].PctHPs}<70 && ${Group.Member[1].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[1].OtherZone} && ${Group.Member[1].Present} && ${Group.Member[1].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[1].ID} ; /timed 60 /xtar 1
holyflag3=1
holyshit3=/if (${Group.Member[2].PctHPs}<70 && ${Group.Member[2].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[2].OtherZone} && ${Group.Member[2].Present} && ${Group.Member[2].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[2].ID} ; /timed 60 /xtar 1
holyflag4=1
holyshit4=/if (${Group.Member[3].PctHPs}<70 && ${Group.Member[3].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[3].OtherZone} && ${Group.Member[3].Present} && ${Group.Member[3].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[3].ID} ; /timed 60 /xtar 1
holyflag5=1
holyshit5=/if (${Group.Member[4].PctHPs}<70 && ${Group.Member[4].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[4].OtherZone} && ${Group.Member[4].Present} && ${Group.Member[4].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[4].ID} ; /timed 60 /xtar 1
holyflag6=1
holyshit6=/if (${Group.Member[5].PctHPs}<70 && ${Group.Member[5].Distance}<${Spell["Healing Light"].Range} && !${Group.Member[5].OtherZone} && ${Group.Member[5].Present} && ${Group.Member[5].Type.NotEqual[Corpse]} && ${Me.PctMana}>10 && ${Cast.Ready["Healing Light"]} ) /multiline ; /casting "Healing Light" gem1 -targetid|${Group.Member[5].ID} ; /timed 60 /xtar 1
; Stun
holyflag7=1
holyshit7=/if (${Target.PctHPs}<80 && ${Target.PctHPs}>20 && ${Cast.Ready["Sound of Force"]} && ${Target.Type.Equal[NPC]} && ${Me.PctMana}>10 ) /casting "Sound of Force" gem4
; Emergency Invulnerability
holyflag8=1
holyshit8=/if (!${Me.Song["Divine Barrier"].ID} && ${Me.PctHPs}<15 && ${Me.PctHPs}>0 && ${Me.XTarget}>0 ) /casting "Divine Barrier" gem7