Scouting Maiden's Grave a simple solo scout task in The Buried Sea. You just run around and get location updates to complete it. Captain Pete is the task giver. The task yields 50pp and about 9.5% AA per run at lvl 75. It takes almost exactly 3 minutes to complete it as a bard. I'm averaging about 1.6 - 1.7 AAs per hour. It would be a little higher but I have to med every 4-5 laps.
This macro is customized for a bard but there's no reason other classes can't use it with a few modifications. If anyone is interested then let me know and I can tell you exactly where to make changes. The succor point is not too far from the task giver so certain classes may be able to do it faster.
The are several important things you should know before running this macro.
1) At 3 minutes per lap thats 20 task completions per hour. If other PCs are near the task giver they will notice you. Don't run this macro when there are lots of people around or you might get reported. The macro does some basic PC radius checks and pauses when people are around the task giver. But use it with caution when the zone has a lot of people in it.
I personally never run this macro unattended during peak play time. However about 8-12 hours a day this zone is kinda dead on my server and is prime time for this macro.
2) One of the updates in the task is 'Find the beach of the undead'. You will get aggro here 90% of the time (unless you have IVU). As a bard this isn't a problem because I simply fade. If you can't loose aggro then you need some form of IVU. If you dont do this you will run a big risk either getting killed or training people.
You will see other PCs in the undead area from time to time. If you train them they won't be happy. This is why it's important to not run this macro unattended when there are lots of people in the zone.
IVU potions are about 10-15 pp each. You get 50pp per lap so theoretically you could buy a bunch of IVU potions, still make a small profit, and not have to worry about the undead aggro.
3) The island is usually empty but from time to time there are people in the area getting quest drops. If they happen to set up camp in the path this macro takes then you will repeatedly run through their camp (every 3 minutes). This will look very strange to say the least.
Again this is why I don't run this macro unattended when lots of people are in the zone.
4) You'll need some form of invis and levitation (and preferably something to make you run faster). The only things that see through invis are the undead and they are only encountered once. The rest of the time you will need to be invis'ed.
5) Occasionally their are KOS roamers near the task giver. They hit pretty hard, so be careful around him when you are not invis'ed. Note that you do not need to be visible to hail him once the task is complete. But you must be visible in order to get the task.
6) The reward must be accepted at the end in order to get the exp and plat. This macro can do that automatically but you need the MQ2MMouseTo plugin. (This is because MQ2 cannot currently automatically accept TBS rewards.) In order for the /mmouseto command to work the mouse must be free and the EQ window must have focus.
If you want to run EQ in the background then you will need to manually accept the rewards. EQ allows up to 5 pending rewards. So every 15 minutes or so you will need to do this. The macro can be set to exit every 5 laps for this reason.
I believe those are the main things to worry about. Personally I am really enjoying this macro. Its very decent exp with about zero chance of death and a small amount of plat to boot. Oh and your blacksail smugglers faction will be maxed after a few days.
The macro has lots of comments, but if you have questions about it let me know.
You will also need this include file. Its holds a lot of common functions I use in many of my macros. Save it as 'oc-utilities.inc' and place it in the same folder as your macros.
Edit: It will now select the reward for you without the MQ2MMouseTo plugin. To do this requires the MQ2Reward plugin. However, I had problems using the MQ2Reward that comes with RedQuest compile. I needed to select the 4th option and it wouldnt do it. So here's a plugin that works for me. It might work if the default MQ2Reward doesn't work for you.
Also, if you have problems with the reward then you might need to adjust the reward_option variable. Look for the following in the macro, somewhat near the top.
When you complete the task if you need to select a different option then change the above code to whatever option you need. For instance, I need to select "Option 4" so in my macro it looks like this:
--Oort
Here's the plugin code (.dll attachment below).
This macro is customized for a bard but there's no reason other classes can't use it with a few modifications. If anyone is interested then let me know and I can tell you exactly where to make changes. The succor point is not too far from the task giver so certain classes may be able to do it faster.
The are several important things you should know before running this macro.
1) At 3 minutes per lap thats 20 task completions per hour. If other PCs are near the task giver they will notice you. Don't run this macro when there are lots of people around or you might get reported. The macro does some basic PC radius checks and pauses when people are around the task giver. But use it with caution when the zone has a lot of people in it.
I personally never run this macro unattended during peak play time. However about 8-12 hours a day this zone is kinda dead on my server and is prime time for this macro.
2) One of the updates in the task is 'Find the beach of the undead'. You will get aggro here 90% of the time (unless you have IVU). As a bard this isn't a problem because I simply fade. If you can't loose aggro then you need some form of IVU. If you dont do this you will run a big risk either getting killed or training people.
You will see other PCs in the undead area from time to time. If you train them they won't be happy. This is why it's important to not run this macro unattended when there are lots of people in the zone.
IVU potions are about 10-15 pp each. You get 50pp per lap so theoretically you could buy a bunch of IVU potions, still make a small profit, and not have to worry about the undead aggro.
3) The island is usually empty but from time to time there are people in the area getting quest drops. If they happen to set up camp in the path this macro takes then you will repeatedly run through their camp (every 3 minutes). This will look very strange to say the least.
Again this is why I don't run this macro unattended when lots of people are in the zone.
4) You'll need some form of invis and levitation (and preferably something to make you run faster). The only things that see through invis are the undead and they are only encountered once. The rest of the time you will need to be invis'ed.
5) Occasionally their are KOS roamers near the task giver. They hit pretty hard, so be careful around him when you are not invis'ed. Note that you do not need to be visible to hail him once the task is complete. But you must be visible in order to get the task.
6) The reward must be accepted at the end in order to get the exp and plat. This macro can do that automatically but you need the MQ2MMouseTo plugin. (This is because MQ2 cannot currently automatically accept TBS rewards.) In order for the /mmouseto command to work the mouse must be free and the EQ window must have focus.
If you want to run EQ in the background then you will need to manually accept the rewards. EQ allows up to 5 pending rewards. So every 15 minutes or so you will need to do this. The macro can be set to exit every 5 laps for this reason.
I believe those are the main things to worry about. Personally I am really enjoying this macro. Its very decent exp with about zero chance of death and a small amount of plat to boot. Oh and your blacksail smugglers faction will be maxed after a few days.
The macro has lots of comments, but if you have questions about it let me know.
Rich (BB code):
#turbo
#include oc-utilities.inc
#Event Reward "#*#You have successfully been granted your reward for: Scouting Maiden's Grave#*#"
#Event Update "#*#Your task 'Scouting Maiden's Grave' has been updated.#*#"
Sub Main
|### Camp out when done?
/declare camp_out bool outer false
|### Reward option.
/declare reward_option int outer 1
|### The number of iterations to do.
/declare max_iters int outer 15
|### The number of laps to do per iteration.
/declare max_laps int outer 4
|### The number of seconds to pause between iterations when a PC is near task giver.
/declare pause_iter int outer 3000
|### The number of seconds to pause between laps when a PC is near task giver.
/declare pause_lap int outer 600
|### The radius to check for PCs near task giver.
/declare pc_radius int outer 150
|### The moveto sensitivity.
/declare moveto_sens int outer 20
|### PCT mana and health to pause for regen.
/declare med_limit int outer 50
|### This is the drum I use. Change it to your drum if you're a bard.
/declare drum_name string outer rime-coated drum of the avalanche
|### No need to change any of the variables below here.
/declare iters int outer 0
/declare laps int outer 0
/declare tot_laps int outer 0
/declare aas float outer 0
/declare aas_hr float outer 0
/declare start_aaexp float outer ${Me.PctAAExp}
/declare start_aas int outer ${Me.AAPointsTotal}
/declare got_reward bool outer false
/declare pc_timer timer outer 0
/declare update_cnt int outer 0
|### Start the exp!
/moveto ${moveto_sens}
/for iters 1 to ${max_iters}
/for laps 1 to ${max_laps}
/call GetTask "captain pete" willing "scouting maiden's grave"
:Loop_1
/call CheckHealthMana true
/call LevSpeedFade
/call GotoIsland
/call ScoutIsland
/call LeaveIsland
/doEvents
/if (${update_cnt} < 9) {
/echo Repeating. Only received ${update_cnt} of 9 updates.
/goto :Loop_1
}
/call ReturnNearCaptainPete
/call FinishTask "captain pete" ${reward_option}
/call NearbyCheck ${pause_lap}
/next laps
/call NearbyCheck ${pause_iter}
/next iters
/beep "c:/windows/media/tada.wav"
/sit
/if (${camp_out}) /camp desktop
/echo Exiting.
/return
Sub GotoIsland
/look 0
/call MoveTo -350 3230
/return
Sub ScoutIsland
|### 2 south eastern islands
/call MoveTo 1630 3130
/look -6
/call MoveTo 1675 2640
/call MoveTo 1735 2695
|### undead beach
/call MoveTo 2595 3050
/call MoveTo 2900 3141
/call MoveTo 3110 3065
/look 0
|### Almost always have aggro here.
/if (${Me.CombatState.Equal[combat]}) /call Fade
|### archway
/call MoveTo 3350 3000
/call MoveTo 3355 3260
/call MoveTo 3350 3000
|### waypoints
/call MoveTo 3860 3195
/call MoveTo 4025 3370
|### northern 2 islands
/call MoveTo 4205 3270
/call MoveTo 4650 4080
/look -12
|### north beach
/call MoveTo 3800 4130
/look -3
|### center pool
/call MoveTo 2770 3850
/look 0
|### waypoints
/call MoveTo 2555 3795
/call MoveTo 2040 3810
|### last southern island
/call MoveTo 1600 4460
|### Sometimes you can get aggro on the way here.
/if (${Me.CombatState.Equal[combat]}) /call Fade
/return
Sub LeaveIsland
/call MoveTo 105 3980
/return
Sub ReturnNearCaptainPete
/call MoveTo -1165 3545
/look -30
/call MoveTo -1480 3465
/return
Sub Event_Reward
/varset got_reward true
/return
Sub Event_Update
/varcalc update_cnt ${update_cnt} + 1
/return
You will also need this include file. Its holds a lot of common functions I use in many of my macros. Save it as 'oc-utilities.inc' and place it in the same folder as your macros.
Rich (BB code):
#define DEBUG false
#define CAST_LAG 30
|### delay is 10ths of second, remain is ticks
Sub CheckSwapCastWWSwap(string itemName, string slot, int delay, int remain)
/declare prevItem string local ${InvSlot[${slot}].Item.Name}
/call CheckSwapCastWW "${itemName}" ${slot} ${delay} ${remain}
/if (${prevItem.NotEqual["NULL"]}) {
/call Swap "${prevItem}" ${slot}
}
/return
Sub CheckSwapCastWW(string itemName, string slot, int delay, int remain)
/if (${Me.Buff[${FindItem[${itemName}].Spell.Name}].Duration} < ${remain}) {
/call SwapCastWW "${itemName}" ${slot} ${delay}
}
/return
Sub SwapCastWW(string itemName, string slot, int delay)
/call SwapCastWait "${itemName}" ${slot} ${delay}
:Loop_1
/delay 1
/if (${Me.Casting.ID} || !${Me.SpellReady[1]}) /goto :Loop_1
/return
Sub SwapCastWait(string itemName, string slot, int delay)
/call Swap "${itemName}" ${slot}
/call CastWait "${itemName}" ${delay}
/return
Sub SwapCast(string itemName, string slot)
/call Swap "${itemName}" ${slot}
/cast item "${itemName}"
/return
Sub Swap(string itemName, string slot)
/squelch /exchange "${itemName}" ${slot}
:Loop_1
/delay 1
/if (${InvSlot[${slot}].Item.Name.NotEqual[${itemName}]}) /goto :Loop_1
/return
Sub CheckCastWait(string itemName, int delay, int remain)
/if (${Me.Buff[${FindItem[${itemName}].Spell.Name}].Duration} < ${remain}) {
/call CastWait "${itemName}" ${delay}
}
/return
Sub CastWait(string itemName, int delay)
/declare tmp int local 0
/varcalc tmp ${delay} + CAST_LAG
/cast item "${itemName}"
/delay ${tmp} ${Me.Buff[${FindItem[${itemName}].Spell.Name}].ID}
/return
Sub CastSongWait(string songName, int delay)
/declare tmp int local 0
/varcalc tmp ${delay} + CAST_LAG
/cast "${songName}"
/delay CAST_LAG ${Me.Casting.ID} == ${Spell[${songName}].ID}
/delay ${tmp} ${Me.Casting.ID} != ${Spell[${songName}].ID}
/return
Sub StopTwistWait
/squelch /twist off
:Loop
/delay 1
/if (${Me.Casting.ID} || !${Me.SpellReady[1]}) /goto :Loop
/return
Sub MoveTo(int yLoc, int xLoc)
/squelch /moveto loc ${yLoc} ${xLoc}
:MoveToLoop
/delay 1
/if (${MoveTo.Moving}) /goto :MoveToLoop
/squelch /moveto off
/return
Sub GetTask(string taskGiver, string phrase, string task, bool sit, int pause)
/doEvents flush
/varset update_cnt 0
/call GotoTaskGiver "${taskGiver}"
/call BecomeVisible
/if (${sit}) /sit
/say ${phrase}
:Loop_1
/delay 1
/if (!${Window[TaskSelectWnd].Open}) /goto :Loop_1
/notify TaskSelectWnd TaskList leftmouseup
/notify TaskSelectWnd TaskList listselect ${Window[TaskSelectWnd].Child[TaskList].List[=${task}]}
/notify TaskSelectWnd AcceptButton leftmouseup
:Loop_2
/delay 1
/if (!${Window[TaskWnd].Open}) /goto :Loop_2
/call CloseTaskWindow
/if (${pause} > 0) /call PauseRandom ${pause}
/return
Sub FinishTask(string taskGiver, int option, bool sit, int pause)
/call GotoTaskGiver "${taskGiver}"
/if (${sit}) /sit
/hail
:Loop_1
/delay 1
/if (!${Window[RewardSelectionWnd].Open}) /goto :Loop_1
/reward ${option} ${option}
/varset got_reward false
:Loop_2
/delay 1
/doEvents
/if (!${got_reward}) /goto :Loop_2
/squelch /windowstate RewardSelectionWnd close
|### print progress
/varcalc aas (((${Me.AAPointsTotal} - ${start_aas}) * 100) + (${Me.PctAAExp} - ${start_aaexp})) / 100
/varcalc aas_hr ${aas} / (${Macro.RunTime} / 3600)
/varcalc tot_laps ${laps} + ((${iters} - 1) * ${max_laps})
/echo Laps: ${tot_laps} AA Exp: ${aas} AA Per Hour: ${aas_hr}
/if (${pause} > 0) /call PauseRandom ${pause}
/return
Sub GotoTaskGiver(string taskGiver)
/target ${taskGiver}
:Loop_1
/delay 1
/if (${Target.CleanName.NotEqual[${taskGiver}]}) /goto :Loop_1
/face
/squelch /moveto id
:Loop_2
/delay 1
/if (${MoveTo.Moving}) /goto :Loop_2
/look 0
/return
Sub LevSpeedFade
/if (${Me.Class.Name.Equal[bard]}) {
/call StopTwistWait
}
/call Levitate
/call Speed
/call Fade
/return
|### This is what I use to cast levitate on myself. If you dont have this item
|### then you will need to customize it for yourself.
Sub Levitate
/if (${FindItem[kelp-string lute of tide rituals].ID}) {
/call CheckSwapCastWWSwap "kelp-string lute of tide rituals" offhand 20 600
} else /if (${FindItem[singing steel boots].ID}) {
/call CheckSwapCastWWSwap "singing steel boots" feet 35 50
}
/return
|### This is what I use to run fast. Customize it as needed.
Sub Speed
/if (${Plugin[mq2speedutils].Name.NotEqual["NULL"]}) {
/squelch /speed 1.4
/return
}
/if (${Me.Class.Name.Equal[bard]}) {
|### equip your drum
/if (${FindItem[${drum_name}].ID}) {
/call Swap "${drum_name}" offhand
}
|### use composers greaves if you have it
/if (${FindItem[composers greaves].ID}) {
/call SwapCastWait "composers greaves" legs 10
|### otherwise sing selos
} else {
/cast "selo's accelerating chorus"
:Loop_1
/delay 1
/if (${Me.Casting.ID} != ${Spell[selo's accelerating chorus].ID}) /goto :Loop_1
:Loop_2
/delay 1
/if (!${Me.Buff[selo's accelerating chorus].ID}) /goto :Loop_2
}
}
/return
Sub Fade
/if (${Me.Class.Name.Equal[bard]}) {
/alt activate 212
}
/return
|### This is a very reliable way to become visible for a bard. If you're not a bard
|### then you'll need to do something different.
Sub BecomeVisible
/if (${Me.Class.Name.Equal[bard]}) {
/squelch /twist 1 2
:Loop_1
/delay 1
/if (${Me.Invis}) /goto :Loop_1
/squelch /twist off
}
/return
|### Checks for PCs near the task giver.
Sub NearbyCheck(int pause)
/if (${SpawnCount[pc radius ${pc_radius}]} > 1) {
/echo PC nearby - pausing.
/varset pc_timer ${pause}
/sit
:Loop_1
/delay 10
/if (${pc_timer} > 0 && ${SpawnCount[pc radius ${pc_radius}]} > 1) /goto :Loop_1
/stand
}
/return
|### Med if mana or health get too low.
Sub CheckHealthMana(bool invis)
/if (${Me.PctHPs} < ${med_limit} || ${Me.PctMana} < ${med_limit}) {
/echo Medding.
/if (${invis}) /call Fade
/sit
:Loop_1
/delay 10
/if (${Me.PctHPs} < 96 || ${Me.PctMana} < 96) /goto :Loop_1
/stand
}
/return
Sub WaitForBoat(string boat, int yloc, int xloc)
:Loop
/delay 1
/if (${Target.CleanName.NotEqual[${boat}]} || ${Target.Speed} != 0 || ${Target.Y.Int} != ${yloc} || ${Target.X.Int} != ${xloc}) {
/squelch /target ${boat}
/goto :Loop
}
/return
Sub PauseRandom(int pause)
/declare rnd int local 0
/varcalc rnd (${Math.Rand[${pause}]} + 1) * 10
/delay ${rnd}
/return
Sub FaceRandom
/face heading ${Math.Rand[360]}
/return
Sub WaitForUpdate
/declare old_update_cnt int local ${update_cnt}
:Loop_1
/doEvents
/delay 1
/if (${update_cnt} == ${old_update_cnt}) /goto :Loop_1
/return
Sub CloseTaskWindow
/if (${Window[TaskWnd].Open}) {
/squelch /windowstate TaskWnd close
}
/return
Edit: It will now select the reward for you without the MQ2MMouseTo plugin. To do this requires the MQ2Reward plugin. However, I had problems using the MQ2Reward that comes with RedQuest compile. I needed to select the 4th option and it wouldnt do it. So here's a plugin that works for me. It might work if the default MQ2Reward doesn't work for you.
Also, if you have problems with the reward then you might need to adjust the reward_option variable. Look for the following in the macro, somewhat near the top.
Rich (BB code):
|### Reward option.
/declare reward_option int outer 1
When you complete the task if you need to select a different option then change the above code to whatever option you need. For instance, I need to select "Option 4" so in my macro it looks like this:
Rich (BB code):
|### Reward option.
/declare reward_option int outer 4
--Oort
Here's the plugin code (.dll attachment below).
Rich (BB code):
// MQ2Reward.cpp : Defines the entry point for the DLL application.
//
// PLUGIN_API is only to be used for callbacks. All existing callbacks at this time
// are shown below. Remove the ones your plugin does not use. Always use Initialize
// and Shutdown for setup and cleanup, do NOT do it in DllMain.
#include "../MQ2Plugin.h"
PreSetup("MQ2Reward");
VOID AutoReward(PSPAWNINFO pChar, PCHAR szLine);
bool debug = false;
// Called once, when the plugin is to initialize
PLUGIN_API VOID InitializePlugin(VOID)
{
DebugSpewAlways("Initializing MQ2Reward");
AddCommand("/reward",AutoReward);
}
// Called once, when the plugin is to shutdown
PLUGIN_API VOID ShutdownPlugin(VOID)
{
DebugSpewAlways("Shutting down MQ2Reward");
RemoveCommand("/reward");
}
VOID AutoReward(PSPAWNINFO pChar, PCHAR szLine)
{
CHAR szBuffer[MAX_STRING] = {0};
CHAR szArg1[MAX_STRING] = {0};
GetArg(szArg1, szLine, 1);
int arg1 = atoi(szArg1) - 1;
if (arg1 < 0) {
sprintf(szBuffer, "Illegal option index: '%s'", szArg1);
WriteChatColor(szBuffer,USERCOLOR_DEFAULT);
return;
}
if (debug) {
sprintf(szBuffer, "Trying to find reward at option index: %s", arg1);
WriteChatColor(szBuffer,USERCOLOR_DEFAULT);
}
// Parent TabWindow PageTemplate
CXWnd *pWnd = FindMQ2Window("RewardSelectionWnd")->GetFirstChildWnd()->GetFirstChildWnd();
while (pWnd) {
if (((PCSIDLWND)pWnd)->Show) {
if (CListWnd *pListOpt = (CListWnd*)((CSidlScreenWnd*)(pWnd->GetChildItem("RewardSelectionOptionList")))) {
pListOpt->SetCurSel(arg1);
//CHAR cxStr[MAX_STRING] = {0};
//CXStr Str;
//CXMLData *pXMLData = ((CXWnd*)pListOpt)->GetXMLData();
//GetCXStr(pXMLData->Name.Ptr,cxStr,MAX_STRING);
//sprintf(szBuffer, "Name: %s", cxStr);
//WriteChatColor(szBuffer,USERCOLOR_DEFAULT);
if (CXWnd *pButton = pWnd->GetChildItem("RewardSelectionChooseButton")) {
if (debug) WriteChatColor("Clicking 'Select Option' button.",USERCOLOR_DEFAULT);
CXRect rect = pButton->GetScreenRect();
CXPoint pt = rect.CenterPoint();
pButton->HandleLButtonDown(&pt,0);
pButton->HandleLButtonUp(&pt,0);
return;
}
}
}
pWnd = pWnd->GetNextSib();
}
sprintf(szBuffer, "Could not find reward at %s.", arg1);
WriteChatColor(szBuffer,USERCOLOR_DEFAULT);
}
Last edited: