This made me over a million platinum.

A forum for you to dump all the macros you create, allowing users to use, modify, and comment on your work.

Moderator: MacroQuest Developers

lestor
orc pawn
orc pawn
Posts: 16
Joined: Sat Jan 10, 2004 6:31 am

This made me over a million platinum.

Post by lestor » Fri Jan 23, 2004 2:54 am

As many of you already know, there was a tradeskill bug discovered recently. Well I wrote a little script that utilizes this loophole, and have been running it for nearly a month on about 5 characters.

This exploit worked in several cities across Norrath and Luclin. Need to make the macro work in another city? It's simple! Just write up a simple INI file, and off it goes to make you thousands of platinum!

It combines very fast, buys and sells when it needs to, and supports random pathing. (There are 3 forges in the Bazaar.. you wouldn't want to look like you were macroing by going to the same one over and over again would you?)


Credits, in no particular:

-cronic for help starting the project and writing the original macro until I overhauled it.
-Dont_Know_At_All for the original tradeskill code. Got the idea from your code.
-Kaitain and Diggler for the great recipes.ini.
-Kint for his GoToLoc macro.

Here's the code, commented for your reading pleasure.. Enjoy!
Also included a few INI files I used. :)

Synaxt: /macro makesheets <inifile> [starting path]

makesheets.mac

Code: Select all

| MakeSheets.

#turbo

#event Full "There was no place to put that" 
#event Error "You cannot combine these items in this container type!" 
#event Failure "You lacked the skills" 
#event Success "You have fashioned the items together to create something new" 

Sub Main
| - Declare global variables.
	/declare cursor global
	/declare phase array
	/declare count1 global
	/declare count2 global
	/declare inifile global
	/declare recipefile global
	/declare path global
	
	/varset cursor FALSE

| - Define INI filename.
	/if $defined(Param0)==FALSE /call EndMacro "You must specify an INI file."
	/varset inifile @Param0
	/varset recipefile $ini(@inifile,Settings,RecipeINI)

| - Check random pathing settings.
	/if $ini(@inifile,Settings,RandomPathing)==FALSE {
		/if $defined(Param1)==FALSE {
			/echo Starting path not defined, using default.
			/varset path 0
			}
		} else {
		/varset path @Param1
		}
	/if $ini(@inifile,Settings,RandomPathing)==TRUE {
		/if $defined(Param1)==FALSE {
			/echo Starting path not defined, using default.
			/varset path 0
			}
		} else {
		/varset path 0
		}
		
| - Checks for recipe list in INI file.
	/if $ini(@inifile,Recipes)~~NOTFOUND /call EndMacro "Recipes list not defined in INI file."
	
| - Opens your bags.
	/if $ini(@inifile,Settings,OpenPacks)==TRUE /call Packs

| - Main Loop
	:Loop
		/if $envopen=="ENVIRO_NOT_OPEN" /call OpenEnviro "Forge" "$ini(@inifile,Settings,ContainerInUse)"
		/if $return=="INUSE" /goto :BuySell

		/call DoRecipes

		/echo Finished making sheets... returning to merchant.
		/click left enviro done
	:BuySell
		/call BuySell
	/goto :Loop
/return

| - This is the order in which the main functions of the macro are executed.
Sub BuySell
	/if n $count("Water Flask")<$ini(@inifile,Settings,WaterLow) {
		/call WalkPath "ForgeToWater" "FALSE"
		/call OpenMerchant "$ini(@inifile,Settings,WaterMerchant)"
		/call Barter "buy" "Water Flask" "$ini(@inifile,Settings,WaterMax)"
		/if "$ini(@inifile,Settings,WaterMerchant)"!="$ini(@inifile,Settings,OreMerchant)" {
			/click left merchant done
			/call WalkPath "WaterToOre" "FALSE"
			}
		/goto :Skip
		}
	/call WalkPath "ForgeToOre" "FALSE"
	/if "$ini(@inifile,Settings,WaterMerchant)"=="$ini(@inifile,Settings,OreMerchant)" /call WalkPath "ForgeToWater" "FALSE"
	:Skip
	/call OpenMerchant "$ini(@inifile,Settings,OreMerchant)"
	/call Barter "sell" "Medium Quality Folded Sheet Metal"
	/delay 5
	/call Barter "buy" "Small Brick of Medium Quality Ore"
	/echo Finished buying and selling.. returning to forge.
	/call WalkPath "OreToForge" "$ini(@inifile,Settings,RandomPathing)"
/return

| - This sub grabs the recipes from your recipes.ini file.
Sub DoRecipes
	/varset phase(1) 1
	:Loop
		/if "$ini(@inifile,Recipes,Recipe$int(@phase(1)))"==NOTFOUND /return
		/varset phase(0) "$ini(@inifile,Recipes,Recipe$int(@phase(1)))"
		/echo Excuting recipe $ini(@inifile,Recipes,Recipe$int(@phase(1)))...
		/call TradeSkills "$ini(@inifile,Recipes,Recipe$int(@phase(1)))"
		/varadd phase(1) 1
		/goto :Loop
/return

| - This sub goes through the recipe entry and puts each item into the forge.
Sub TradeSkills(recipe)
	/if "$ini(@recipefile,@recipe))"~~NOTFOUND /Call EndMacro "Could not find @recipe in Recipe INI file."
	:Begin
	/call CleanPack
	/call ClearCursor "FALSE"
	/doevents 
	/call Pause
	/varset count1 0
	:Loop
		/if "$ini(@recipefile,@recipe,Comp$int(@count1))"==NOTFOUND /goto :Combine
		/if n $count("$ini(@recipefile,@recipe,Comp$int(@count1))")==0 /goto :End
		/call AddComponent "$ini(@recipefile,@recipe,Comp$int(@count1))" "@count1"
		/varadd count1 1
		/goto :Loop
        :Combine 
        	/call DoCombine
	:CheckProduct
		/doevents
		/if @cursor==FALSE /goto :CheckProduct
		/varset cursor FALSE
    	/goto :Begin
    	:End
    		/call CleanPack
    		/call ClearCursor "FALSE"
/return

| - Adds items to an enviromental container given itemname and slot.
Sub AddComponent(item,slot)
	/sendkey down ctrl
	:Loop
		/finditem "@item"
		/if $cursor()==NULL /goto :Loop
	/click left enviro @slot
	/sendkey up ctrl
	/if $cursor()!=NULL /call ClearCursor "FALSE"
/return

| - Clicks combine button until combine registers.
sub DoCombine 
	:Loop
		/varset count1 0
		/click left enviro combine 
		/doevents
	:Check
		/if $pack(e,empty)==FALSE {
			/if @count1>5 /goto :Loop
			/varadd count1 1
			/delay 1
			/goto :Check
			}
	/delay 1
/return

| - Opens the enviromental container given itemname.
Sub OpenEnviro(item,action)
	/varset count1 0
	:Loop
		/if $envopen=="@item" /return
		/itemtarget forge
		/face fast nolook
		/click left item
	:Check
		/varadd count1 1
		/delay 10
		/if $envopen=="@item" /return
		/if n @count1==5 {
			/if @action==WAIT /varset count1 0
			/goto :Loop
			}
		/if n @count1==10 /goto :Loop
		/if n @count1>=15 {
			/echo Screw it..
			/return INUSE
			}
		/goto :Check
/return

| - Opens a merchant given merchantname.
Sub OpenMerchant(merchant)
	:Check
		/target @merchant
		/call GoToLoc "$target(y),$target(x)" "5"
		/face fast nolook
		/click right target
		/delay 10
		/if $merchant(open)==FALSE /goto :Check
		/if $merchant("@merchant")==FALSE {
			/click left merchant done
			/goto :Check
			}
/return

| - This sub will buy or sell any amount of any given item.
Sub Barter(action,item,amount)
	/echo $if(@action==sell,Sell,Buy)ing @item...
	/if @action==sell /goto :Sell
	:Check
		/delay 10
		/selectitem "@item" merchant
		/delay 3
		/if "$selecteditem()"!="@item" {
			/if @action==buy /echo Cannot find @item...
			/return
			}
	/if @action==buy /goto :Buy
	:Sell
		/if n $count("@item")<=0 /return
		/delay 1
		/finditem "@item"
		/delay 1
		/if "$selecteditem()"!="@item" /return
		/sellitem $if($selecteditem(count)>=1,$selecteditem(count),1)
		/delay 3
		/goto :Sell
	:Buy
		/if $defined(amount)==FALSE /goto :FillUp
		/if n $count("@item")>=@amount /return
	:StackLoop
		/if n $calc(@amount-$count("@item"))<=20 /goto :RegLoop
		/buyitem $if($selecteditem(count)==1,20,1)
		/delay 3
    		/goto :StackLoop
	:FillUp
		/if n $freeinv(space)<2 /return
		/buyitem $if($selecteditem(count)==1,20,1)
		/delay 3
		/goto :FillUp
	:RegLoop
		/if n $calc(@amount-$count("@item"))<=0 /return
		/buyitem $if($selecteditem(count)==1,$calc(@amount-$count("@item")),1)
		/delay 3
		/goto :RegLoop

/return

| - Will walk a path listed in the INI file, given pathname.  Random is used when randomize pathing is enabled.
Sub WalkPath(pathname,random)
	/call PickPath "@pathname" "@random"
	/varset count1 1
	/echo Excuting path $ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Name)
	:Loop
		/if $ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))==NOTFOUND /return
		/if $left(4,$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1)))==DOOR /goto :Door
		/call GoToLoc "$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))" "2"
		/delay 1
		/varadd count1 1
		/goto :Loop
	:Door
		/call OpenDoor "$mid(5,$calc($instr(;,"$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))")-5),"$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))")" "$right($calc($strlen("$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))")-$calc($instr(;,"$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))")+1)),"$ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path),Loc$int(@count1))")"
		/delay 1
		/varadd count1 1
		/goto :Loop
/return

| - Picks a random path from any listed in the INI file with the same name ending in a number.
Sub PickPath(pathname,random)
	/if @random==FALSE /return
	/varset path 0
	:Loop
		/if $ini(@inifile,$left($strlen("@pathname"),"@pathname")$int(@path))~~NOTFOUND /goto :Random
		/delay 1
		/varadd path 1
		/goto :Loop
	:Random
		/if @path==0 /call EndMacro "Path not found."
		/varset path $rand(@path)
		/echo RANDOM @path
/return

| - Opens a door given a location behind the door and the door keyword.
Sub OpenDoor(location,keyword,check)
	/face fast nolook loc @location
	/doortarget @keyword
	:Check
		/press u
		/delay 5
		/if $door(open)==TRUE /return
		/if "@check"=="nocheck" /goto :End
		/delay 5
		/goto :Check
	:End
		/delay 5
/return

| - Used in the WalkPath sub, will go to a location given location(x,y) and distance to ending point.
Sub GoToLoc(location,distance) 
	:Start
		/if n $distance(@location)<=@distance /goto :End
		/face nolook loc @location
		/if n $distance(@location)>=@distance /sendkey down up
	:Move
		/delay 0 
		/if n $distance(@location)<=@distance /goto :End
		/face fast nolook loc @location
		/if n $distance(@location)>=@distance /goto :Move
	:End
		/sendkey up up
/return

| - Opens or closes all of the bags in your inventory.
Sub Packs(action)
	/varset count1 0
	:Loop
		/if @count1>=8 {
			/press i
			/return
			}
		/if $pack(@count1,open)==NULL {
			/varadd count1 1
			/goto :Loop
			}
	/if @action==CLOSE /goto :Close
	:Open
		/if $pack(@count1,open)==FALSE {
			/if $invpanel==FALSE /windowstate inventory open
			/click right inv @count1
			/goto :Open
			}
		/varadd count1 1
		/goto :Loop
	:Close
		/if $pack(@count1,open)==TRUE {
			/if $invpanel==FALSE /windowstate inventory open
			/click right inv @count1
			/goto :Close
			}
		/varadd count1 1
		/goto :Loop
/return

| - Cleans on the enviromental container incase there is an error in a combine attempt.
Sub CleanPack
	/if $pack(e,empty)==TRUE /return
	/varset count1 0
	/press ctrl
	/sendkey down shift
	/delay 1
	:Loop
		/if n @count1==10 /goto :End
        	/if $item(e,@count1)==TRUE {
        		/echo $int(@count1) slot
        		/delay 1
        		/click left enviro $int(@count1)
			/call ClearCursor "FALSE"
        		}
		/varadd count1 1
		/goto :Loop
	:End
		/sendkey up shift 
		/press shift 
/return 

| - Clears your cursor of items by dropping them into your inventory or destroying them.
Sub ClearCursor(clear)
		/if @clear==TRUE /varset cursor @clear
	:Loop
        	/if $ini(@inifile,Settings,DestroyItems)==TRUE /call DestroyItem
        	/autoinv
        	/if $cursor()!=NULL {
			/delay 1
			/goto :Loop
			}
/return 

| - Destroys an item on your cursor if not listed in the INI file.
Sub DestroyItem
	/varset count2 1
	:Loop
		/if "$ini(@inifile,NoDestroy,Item$int(@count2))"==NOTFOUND /goto :Destroy
		/if "$ini(@inifile,NoDestroy,Item$int(@count2))"=="$cursor(name)" /return
		/varadd count2 1
		/goto :Loop
	:Destroy
		/if n @count2==1 /call EndMacro "Must define a list of items no destory if DestroyItems is on."
		/if "$cursor(name)"==NULL /return
		/echo Destroying $cursor(name)...
		/destroy
/return

Sub Event_Full
	/call CleanPack 
	/Call EndMacro "Your inventory is full."
	/endmacro
/return

Sub Event_Error
	/delay 1
	/call ClearCursor "TRUE"
	/call CleanPack
/return

Sub Event_Success
	:Loop
		/if $cursor()==NULL /goto :Loop
		/call ClearCursor "TRUE"
		/varset cursor TRUE
/return

Sub Event_Failure
	:Loop
		/if @phase(0)==MQFoldedSheet {
			/if $cursor()==NULL /goto :Loop
			}
		/call ClearCursor "TRUE"
		/delay 1
		/varset cursor TRUE
/return

| - Pause sub routine.
Sub Pause
	/if "$target(name,clean)"!="$char(name)" /return
	/echo Pausing!! To resume, type /target clear or target something else. 
	:HoldingPattern 
		/delay 20
		/if "$target(name,clean)"=="$char(name)" /goto :HoldingPattern 
/return

| - Error reporting.
Sub EndMacro(error)
	/echo @error
	/echo An error has occured.. ending macro.
	/endmacro
/return

template.ini

Code: Select all

| MakeSheets INI Template.

[Settings]
	| - Name of your recipe INi file.
RecipeINI=recipes
	| - Open all bags when you start the macro?
OpenPacks=TRUE
	| - Use randomized pathing?
RandomPathing=FALSE
	| - Destroy items not listed on your NoDestroy list?
DestroyItems=TRUE
	| - What would you like to do if your enviromental container is in use?
ContainerInUse=WAIT
	| - Name of water merchant.
WaterMerchant=WaterMerchant_01
	| - How low should your water supply get before you return to the merchant to get more?
WaterLow=0
	| - Amount of water to buy.
WaterMax=0
	| - Name of ore merchant.
OreMerchant=OreMerchant_01

| - List of recipes to execute.
[Recipes]
Recipe1=MQLargeBrick
Recipe2=MQBlock
Recipe3=MQFoldedSheet

| - Items not to destory.
[NoDestroy]
Item1=Smithy Hammer
Item2=Water Flask
Item3=Small Brick of Medium Quality Ore
Item4=Large Brick of Medium Quality Ore
item5=Medium Quality Folded Sheet Metal
Item6=Block of Medium Quality Ore


| - In pathing, you must specify each x and y in the format x,y.
| - Doors are in the following format:  DOOR:x,y,;keyword  .
|
| - If you wish to use random paths you will need a different
|   path with the same name and a different ending number.
|   In this example we have OreToForge0 and OreToForge1.  When
|   the macro chooses a random path, it will pick between 0 or 1.
| - 0 is the default path, thus it must exsist. 1,2,3 etc are for 
|   randomized pathing.


| - Path from the ore merchant to the forge
[OreToForge0]
Name=OreToForge
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

[OreToForge1]
Name=OreToForge
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

| - Path from the forge to the ore merchant.  
|   If the ore and water are on the same merchant, such
|   as in the Bazaar or in Kaladim, you do not need a
|   ForgeToOre or WaterToOre path.
[ForgeToOre0]
Name=ForgeToOre
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

[ForgeToOre1]
Name=ForgeToOre
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

| - Path from the forge to the water merchant.
[ForgeToWater0]
Name=ForgeToWater
Loc1=00,00
Loc2=DOOR:68,-867;door1
Loc3=00,00

[ForgeToWater1]
Name=ForgeToWater
Loc1=00,00
Loc2=DOOR:68,-867;door1
Loc3=00,00

| - Path from water merchant to forge.
[WaterToOre0]
Name=WaterToOre
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

[WaterToOre1]
Name=WaterToOre
Loc1=00,00
Loc2=DOOR:00,00;door1
Loc3=00,00

neriak.ini

Code: Select all

| Settings for Neriak - Commons.

[Settings]
RecipeINI=recipes
OpenPacks=TRUE
RandomPathing=FALSE
DestroyItems=TRUE
ContainerInUse=WAIT
WaterMerchant=Dunred M`Trik
WaterLow=40
WaterMax=200
OreMerchant=Treven Ru`Soe

[Recipes]
Recipe1=MQLargeBrick
Recipe2=MQBlock
Recipe3=MQFoldedSheet

[NoDestroy]
Item1=Smithy Hammer
Item2=Water Flask
Item3=Small Brick of Medium Quality Ore
Item4=Large Brick of Medium Quality Ore
item5=Medium Quality Folded Sheet Metal
Item6=Block of Medium Quality Ore

[OreToForge0]
Name=OreToForge
Loc1=35,-902
Loc2=DOOR:34,-890;door1
Loc3=34,-890
Loc4=20,-877

[ForgeToOre0]
Name=ForgeToOre
Loc1=34,-890
Loc2=DOOR:35,-902;door1
Loc3=35,-902
Loc4=49,-907

[ForgeToWater0]
Name=ForgeToWater
Loc1=35,-867
Loc2=DOOR:68,-867;door1
Loc3=68,-867

[WaterToOre0]
Name=WaterToOre
Loc1=47,-867
Loc2=DOOR:35,-867;door1
Loc3=35,-867
Loc4=34,-890
Loc5=DOOR:35,-902;door1
Loc6=35,-902
Loc7=49,-907

bazaar.ini

Code: Select all

| Settings for The Bazaar

[Settings]
RecipeINI=recipes
OpenPacks=TRUE
RandomPathing=TRUE
DestroyItems=FALSE
ContainerInUse=LEAVE
WaterMerchant=Gliblixl Rocktok
WaterLow=40
WaterMax=60
OreMerchant=Gliblixl Rocktok

[Recipes]
Recipe1=MQLargeBrick
Recipe2=MQBlock
Recipe3=MQFoldedSheet

[NoDestroy]
Item1=Smithy Hammer
Item2=Water Flask
Item3=Small Brick of Medium Quality Ore
Item4=Large Brick of Medium Quality Ore
item5=Medium Quality Folded Sheet Metal
Item6=Block of Medium Quality Ore

[OreToForge0]
Name=Stable forge
Loc1=-681,-28
Loc2=-569,-6
Loc3=-568,89
Loc4=-539,177
Loc5=-519,208

[ForgeToWater0]
Name=Stable forge
Loc1=-519,208
Loc2=-539,177
Loc3=-568,89
Loc4=-569,-6
Loc5=-681,-28

[OreToForge1]
Name=Arena forge
Loc1=-683,-29
Loc2=-569,-7
Loc3=-573,-126
Loc4=-373,-138
Loc5=-349,-111

[ForgeToWater1]
Name=Arena forge
Loc1=-349,-111
Loc2=-373,-138
Loc3=-573,-126
Loc4=-569,-7
Loc5=-683,-29

katta.ini

Code: Select all

| Settings for Katta Castellum.

[Settings]
RecipeINI=recipes
OpenPacks=TRUE
RandomPathing=FALSE
DestroyItems=FALSE
ContainerInUse=WAIT
WaterMerchant=Kitty Barleou
WaterLow=40
WaterMax=200
OreMerchant=Bole Bricktop

[Recipes]
Recipe1=MQLargeBrick
Recipe2=MQBlock
Recipe3=MQFoldedSheet

[NoDestroy]
Item1=Smithy Hammer
Item2=Water Flask
Item3=Small Brick of Medium Quality Ore
Item4=Large Brick of Medium Quality Ore
item5=Medium Quality Folded Sheet Metal
Item6=Block of Medium Quality Ore

[OreToForge0]
Name=OreToForge
Loc1=40,-1526
Loc2=18,-1535

[ForgeToOre0]
Name=ForgeToOre
Loc1=40,-1526
Loc2=54,-1535

[ForgeToWater0]
Name=ForgeToWater
Loc1=-13,-1530
Loc2=DOOR:-13,-1577;kat
Loc3=-13,-1577
Loc4=199,-1601
Loc5=351,-1444
Loc6=329,-1243
Loc7=DOOR:276,-1248;kat
Loc8=276,-1248
Loc9=225,-1117

[WaterToOre0]
Name=WaterToOre
Loc1=276,-1248
Loc2=DOOR:329,-1243;kat
Loc3=329,-1243
Loc4=351,-1444
Loc5=199,-1601
Loc6=-13,-1577
Loc7=DOOR:-13,-1530;kat
Loc8=-13,-1530
Loc9=40,-1526
Loc10=54,-1535

recipes.ini Entries:

Code: Select all

[MQLargeBrick]
Cont=Enviro
Comp0=Small Brick Of Medium Quality Ore
Comp1=Small Brick Of Medium Quality Ore
Comp2=Small Brick Of Medium Quality Ore
Comp3=Water Flask

[MQBlock]
cont=Enviro
Comp0=Large Brick of Medium Quality Ore
Comp1=Large Brick of Medium Quality Ore
Comp2=Large Brick of Medium Quality Ore
comp3=Water Flask

[MQFoldedSheet]
Cont=Enviro
Comp0=Block Of Medium Quality Ore
Comp1=Smithy Hammer
Comp2=Water Flask

Go ahead, give it a try. :)

Cheers,

Lestor

User avatar
dont_know_at_all
Developer
Developer
Posts: 5450
Joined: Sun Dec 01, 2002 4:15 am
Location: Florida, USA
Contact:

Post by dont_know_at_all » Fri Jan 23, 2004 3:24 am

I'm only leaving this up because the combine is nerfed. You cannot make plat off this any more.

Kaitain
a ghoul
a ghoul
Posts: 109
Joined: Fri Oct 10, 2003 1:49 pm

Post by Kaitain » Fri Jan 23, 2004 9:53 am

<deleted>
Last edited by Kaitain on Thu Apr 15, 2004 4:59 pm, edited 1 time in total.

User avatar
wilso132
Contributing Member
Contributing Member
Posts: 106
Joined: Thu Oct 17, 2002 11:53 am

Post by wilso132 » Fri Jan 23, 2004 10:44 am

Jesus Christ... you exploiters write some nice code :lol: . Thanks for nice code to use as a base.

cronic
a grimling bloodguard
a grimling bloodguard
Posts: 536
Joined: Wed Nov 26, 2003 11:01 pm

Post by cronic » Fri Jan 23, 2004 11:54 am

He did a good job. I made the original and it worked. He made it cool.

Shali
a ghoul
a ghoul
Posts: 87
Joined: Sun Dec 07, 2003 12:39 pm

re

Post by Shali » Sat Jan 24, 2004 2:14 am

i must admit i have done this as while, used ini support and had some added stuff for casting sow and str for shamans and druids, took quite a long time to write and debug though, learned alot about MQ in that time