Writing a basic script [Bags of status]

free(?!) clues!

Writing a basic script [Bags of status]

Postby Kannkor » 03 Jul 2014, 09:03

Note: The wiki links for isxgames do not work because it was moved to the forge. You can find the information on the wiki there: https://forge.isxgames.com/projects/isxeq2/wiki
If I get time I'll update the links.


Quite frequently people ask me about writing scripts. I always send them here first:

https://www.isxgames.com/forums/threads/5695-Want-to-learn-LavishScript-scripting-Here-is-your-chance%21

Note: I will be referencing / saying to do things in this document. I will NOT be re-explaining everything here.

Then after, everyone wants to be told what they should script. With these new bags of status, it is a relatively easy example of a script, that can have a LOT of possibilities and addons. It all started with Cheesy. I'm actually going to walkthrough to create the exact same script Cheesy made, then going to add more to it, for the purpose of learning.

Lets get to it!

The very first thing you need to do, is determine what you want to do. And yes, you should write this down. What I do, is open a new .iss file, and make a block comment at the top with what we have to do. For example:
Code: Select all
/* Status Bag script
Buy Status Bags
Use status bags
*/

function main()
{

}


The first thing to note, is we have done a TERRIBLE job of writing down what we want to do. "Buy status bags" isn't that simple. We have to break it down further.
Code: Select all
/* Status Bag script
Buy status Bags
-Target merchant
-Open merchant window
-Buy Bag of status

Use status Bags
-Close merchant window down (this is important, as you can NOT unpack them with the merchant window open).
-Examine bag
-Select option to unpack
-Accept rewards.

*/

function main()
{
   
}


Having a good understanding of what you want to do, is probably one of the most important things to do. Once you have enough information, you can simply turn each line into code.
For the purpose of this script, going to use 'Rabbleson' in qeynos.
Source: http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Actor_(Top-Level_Object) and http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Actor_(Data_Type)
Step 1: Target merchant.
Actor[Rabbleson]:DoTarget
Step 2: Open merchant window:
Actor[Rabbleson]:DoubleClick

One thing to remember, whenever we as humans have to "wait" for something to happen, don't forget to tell your script to wait also. For example, you can't open the merchant, and buy, at the exact same moment. The merchant window has to open first.
Source: http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Vendor_(Top-Level_Object) and http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Vendor_(Data_Type)
Step 3: Buy a bag of status
Vendor.Merchant[A bag of random status items]:Buy[1]

This is what we have so far.
Code: Select all
function main()
{
   Actor[Rabbleson]:DoTarget
   wait 5
   Actor[Rabbleson]:DoubleClick
   wait 5
   Vendor.Merchant[A bag of random status items]:Buy[1]
   wait 5
}

Next, close the merchant window.
Note: EQ2Execute, just means do the following command in game. /close_top_window is a command you can type into eq2, and it will close the top window. Nothing fancy or special.
EQ2Execute /close_top_window
wait 2
EQ2Execute /close_top_window
wait 10

Examine the bag:
Source: http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Me_(Top-Level_Object) and http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Character_(Data_Type) and http://eq2.isxgames.com/wiki/index.php?title=ISXEQ2:Item_(Data_Type)
Me.Inventory[A bag of random status items]:Examine

Select the option to unpack. This one is tricky, and just takes experience to know what kind of window it is, and how to interact with it.
ReplyDialog:Choose[1]

And finally, to accept it. Again, this is one from experience and testing.
RewardWindow:Receive

Note: Both the above commands are in the ISXEQ2 wiki.

Our code now looks like this:
Code: Select all
function main()
{
   Actor[Rabbleson]:DoTarget
   wait 5
   Actor[Rabbleson]:DoubleClick
   wait 5
   Vendor.Merchant[A bag of random status items]:Buy[1]
   wait 5
   EQ2Execute /close_top_window
   Wait 2
   EQ2Execute /close_top_window
   wait 10
   Me.Inventory[A bag of random status items]:Examine
   wait 5
   ReplyDialog:Choose[1]
   wait 5
   RewardWindow:Receive
   wait 5
}


Okay, the script is done. It will buy 1 status bag, and get the reward. We're all done.. ... ... Except it BUYS ONE STATUS BAG.

There are a ton of ways to make this "loop". Here is an example of 1.
Allow the user to pass in how many times they want it to loop. We'll make a default value also.
Note: The use of "..." simply means I've cut out the code to keep it shorter and easier to read. Nothing was changed in the "..." from the last time it was posted.
Code: Select all
function main(int loops=10)
{
   variable int counter=0
   while ${loops} >= ${counter:Inc}
   {
      Actor[Rabbleson]:DoTarget
    ...
      RewardWindow:Receive
      wait 5
   }
}

Assuming the file is named "BagsOfStatus.iss".
Now you can:
run BagsOfStatus
It would buy 10.
You could:
run BagsOfStatus 20
It would buy 20.

For completeness, here is the entire script. Note: This is nearly identical to what Cheesy made/posted for people. It works.
Code: Select all
function main(int loops=10)
{
   variable int counter=0
   while ${loops} >= ${counter:Inc}
   {
      Actor[Rabbleson]:DoTarget
      wait 5
      Actor[Rabbleson]:DoubleClick
      wait 5
      Vendor.Merchant[A bag of random status items]:Buy[1]
      wait 5
      EQ2Execute /close_top_window
      Wait 2
      EQ2Execute /close_top_window
      wait 10
      Me.Inventory[A bag of random status items]:Examine
      wait 5
      ReplyDialog:Choose[1]
      wait 5
      RewardWindow:Receive
      wait 5
   }
}


But... There's always a but... For some, that's just not good enough. We can beef it up. Add a few extra horsepower, make it pew pew more, whatever you want to call it.

There are a few things I'd like to change. For example, it is extremely inefficient to buy 1, examine it, then buy 1, examine it, etc. Because we are wasting time opening and closing down the merchant window! Lets make a change so it does it all at once.

To do this, all we're going to do, is remove the merchant, and examing out of the loop we buy.
Code: Select all
function main(int loops=10)
{
   variable int counter=0
   
   Actor[Rabbleson]:DoTarget
   wait 5
   Actor[Rabbleson]:DoubleClick
   wait 5
   while ${loops} >= ${counter:Inc}
   {
      Vendor.Merchant[A bag of random status items]:Buy[1]
      wait 5
   }
   
   EQ2Execute /close_top_window
   Wait 2
   EQ2Execute /close_top_window
   wait 10
   while ${Me.Inventory[A bag of random status items](exists)}
   {
      Me.Inventory[A bag of random status items]:Examine
      wait 5
      ReplyDialog:Choose[1]
      wait 5
      RewardWindow:Receive
      wait 5
   }
}


Excellent. We now made it way more efficient. It targets/opens the merchant once, buys them all, closes the merchant, then examines them all.
In a perfect world, it's completely done. Time to move on... but.. what if... What, if. These are the things you have to consider!
What if... You don't have enough status?
What if... You don't have enough platinum?
What if... You don't have enough bag space?
What if... You're not in qeynos?
What if... You're not near the merchant?

Oh gosh, can't people just know all this ahead of time?! Why don't they just read the instructions! (HAHAHAHA).
While we will never be able to make it 100% user proof, we can make it at least a little bit more user friendly.

The good news is, all those what if's, are things we can add, and work from.
Lets use the first one. Don't have enough status?
If they do have enough status: All good.
If they don't, then what? Exit the script? Well.. if they wanted to buy 100, but had enough status for only 50... Why not let them buy 50 instead of ending the script?
The exact same situation can be said for platinum.
If they don't have enough platinum for 100, but they do for 75, let them buy 75.
Same for bag space.

Excellent, lets get started. Lets start with Platinum, it's actually the easiest. You'll see why here shortly.
One minor change I'm going to make first, is changing our function main to the following:
Code: Select all
function main(int _HowManyToBuy=100)

It's more notable than "loops", and will be more clear as we start to modify it.

Lets determine how much platinum we have:
Code: Select all
variable int64 MyPlat=${Me.Platinum}

This will save the amount of Platinum into a local variable, MyPlat
We will hardcode the amount of plat each bag costs.
Code: Select all
variable int64 PlatCostOfBag=1

Now, we're going to store how many we can actually buy.
Code: Select all
variable int CanAffordViaPlat

Then, lets do some math on it.
Should be simple, take the total amount of plat, divide by how much they cost in plat, and that is how many.
In this exact case, we're dividing by 1. So we know the number will just be MyPlat. Lets pretend for a moment the price changes, and it is 15 plat each.
If we have 100 plat total, then divide by 15, we get 6.66666667. How do you buy 6.67 bags? We want to make sure we divide by integer only. When doing so, it returns the whole numbers only. This means 100/15 is 6. That would mean we can buy 6.
Code: Select all
;// The "\" is an escape character, so we need to use 2 of them, to get a \.
;// "/" is divide by, and will return a float (IE: 5.32). We can't buy a faction of a bag.
;// "\" is divide by integer, so it will return a full number only (IE: 5).
CanAffordViaPlat:Set[ ${Math.Calc[ ${MyPlat} \\ ${PlatCostOfBag} ]} ]


Now, lets assume the user wanted to buy 50 bags.
If we have 100 plat, it means we can afford to buy up to 100.
OR: If the user wants to buy 50 bags, but we can only afford up to 25 bags.
This means, we always want to use the smaller number of the two.
Code: Select all
;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaPlat}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough platinum. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaPlat}]
   }

Since _HowManyToBuy already holds the amount the user wants to buy, we only need to see if this value is more than CanAffordViaPlat. If it is, change it to the most we can afford.

Going to do status, then we'll tie them both together.
Status can get tricky. Here is the code to find your status.
Code: Select all
${EQ2DataSourceContainer[GameData].GetDynamicData[Stats.CurrentStatus].ShortLabel}

Lets assume I have 2,123,000 status. Why lets do what we did for platinum.
Code: Select all
variable int64 MyStatus=${EQ2DataSourceContainer[GameData].GetDynamicData[Stats.CurrentStatus].ShortLabel}

Guess what "MyStatus" has? 2.
Yes, TWO. Because the value is a string, and it contains commas! Commas generally mean move onto the next parameter, or argument. So it ignored everything after the first comma.
Since it is a string, lets capture it as a string, modify it to suit our needs, then get a number out of it. It's pretty simple, just take it one step at a time. Capture it.
Code: Select all
variable string sMyStatus=${EQ2DataSourceContainer[GameData].GetDynamicData[Stats.CurrentStatus].ShortLabel}

Lets get rid of those pesky commas, and replace them with nothing (making them disappear).
Code: Select all
sMyStatus:Set[${sMyStatus.Replace[",",""]}]

This line says, take the contents of "sMyStatus" and replace any "," (comma's) with "". There is nothing between those double quotes, so it just removes the commas.
This means our sMyStatus is now 2123000. Now we can save that as our int.
Code: Select all
MyStatus:Set[${sMyStatus}]

Great!

Now we're going to do exactly what we did above. See if we can afford how many bags we want.
Code: Select all
;// Do we have enough status to buy all the bags we want?
   variable int CanAffordViaStatus
   ;// The "\" is an escape character, so we need to use 2 of them, to get a \.
   ;// "/" is divide by, and will return a float (IE: 5.32). We can't buy a faction of a bag.
   ;// "\" is divide by integer, so it will return a full number only (IE: 5).
   CanAffordViaStatus:Set[ ${Math.Calc[ ${MyStatus} \\ ${StatusCostOfBag} ]} ]
   
   ;// Lets see if we can afford how many we want to buy
   ;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaStatus}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough status. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaStatus}]
   }


Great, lets recap what we have so far, in it's entirety. It's always fine to add lots of comments to your code, for anyone else looking at it, or if you need to look at it later.
Code: Select all
function main(int _HowManyToBuy=100)
{
   variable int64 MyStatus
   variable string sMyStatus=${EQ2DataSourceContainer[GameData].GetDynamicData[Stats.CurrentStatus].ShortLabel}
   ;// This value comes in the form of a string, IE: 2,123,000. If you try to assign it to a numerical value, it stops at the first comma, resulting in "2".
   ;// We store it as a string, remove the commas, then convert it to an int64.
   ;// Below may be hard to read, it's replace ","   with "".
   sMyStatus:Set[${sMyStatus.Replace[",",""]}]
   MyStatus:Set[${sMyStatus}]
   
   variable int64 MyPlat=${Me.Platinum}
   variable int64 StatusCostOfBag=32500
   variable int64 PlatCostOfBag=1
   
   ;// Do we have enough status to buy all the bags we want?
   variable int CanAffordViaStatus
   ;// The "\" is an escape character, so we need to use 2 of them, to get a \.
   ;// "/" is divide by, and will return a float (IE: 5.32). We can't buy a faction of a bag.
   ;// "\" is divide by integer, so it will return a full number only (IE: 5).
   CanAffordViaStatus:Set[ ${Math.Calc[ ${MyStatus} \\ ${StatusCostOfBag} ]} ]
   
   ;// Lets see if we can afford how many we want to buy
   ;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaStatus}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough status. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaStatus}]
   }
   
   ;// Do we have enough platinum to buy all the bags we want?
   variable int CanAffordViaPlat
   CanAffordViaPlat:Set[ ${Math.Calc[ ${MyPlat} \\ ${PlatCostOfBag} ]} ]
   
   ;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaPlat}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough platinum. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaPlat}]
   }
   
   if ${_HowManyToBuy} <= 0
   {
      echo ${Time}: You don't wish to buy any. Good bye!
      Script:End
   }
}

I added some information to tell the user when we modified their value.
Next, lets check to see what zone we're in, and assign the appropriate merchant name. This way it can work for any zone that we code.
Code: Select all
variable string MerchantName
   switch ${Zone.Name}
   {
      case Qeynos Province District
         MerchantName:Set[Rabbleson]
      break
      default
         echo ${Time}: Unable to determine who the merchant is for this zone.
         Script:End
      break
   }

Switches are a wonderful thing. If you aren't familiar with them, check the link to learning at the top of this post!

Lets also check to see if the merchant exists, and if we're close to him.
Code: Select all
if !${Actor["${MerchantName}"](exists)}
   {
      echo ${Time}: No city merchant found.
      Script:End
   }
   if ${Actor["${MerchantName}"].Distance} > 10
   {
      echo ${Time}: ${MerchantName} too far away ( ${Actor["${MerchantName}"].Distance} ). Needs to be less than 10 meters away.
      Script:End
   }

You'll notice we are using our variable "MerchantName" that we set previously. This allows us to use the exact same code, no matter what zone we're in! No one likes duplicating code for no reason!

We should probably check to make sure the user isn't a complete backrat. You'll need at least 1 inventory slot open. If not, we can't do anything!
Code: Select all
;// Make sure we have at least ONE inventory slot available.
   if ${Me.InventorySlotsFree} <= 0
   {
      echo ${Time}: You don't have any inventory slots free! You need at least 1 to purchase bags.
      Script:End
   }


We're up to 80 lines of code/comments, and we haven't even done anything yet!
At this stage, we should plan out exactly what we want to do. Planning planning planning!
Lets assume the user wants to buy 100 bags total, but they have 10 slots available. What are our options?
Only allow the user to buy 10.
Allow the user to buy 10 at a time, use them, then buy 10 more,etc etc.

Which one of these options would you rather as a user? Obviously the second one. And yes, it is a lot more work than the first option, but it's about pleasing the user.. right... right.. anyone...

We're going to need a loop of some sort to do this.
Generally speaking in loops, you always want to make sure you have some way to stop the loop at the end. While you can stop the loop immediately, some times you need it to finish that loop then end. One way of doing this, is to make a true/false variable, and check it. You'll notice I do this, but it's never actually used. I was going to add a few other little things, but felt it would just complicate it and not add value.

In this loop, we want to keep going until we have bought all the bags we're suppose too, or until we're suppose to stop early.
Code: Select all
variable bool StopLooping=FALSE
   variable int BagsBought=0
   
   ;// Loop as long as we're not suppose to stop (StopLooping) and as long as there are more bags to buy.
   while !${StopLooping} && ${BagsBought} < ${_HowManyToBuy}

First thing first, how many can we buy? We can buy the lower number of: _HowManyToBuy, and how many inventory slots we have.
Same as the platinuim/status, lets set it to one of the values, then see if the other value is lower.
Remember, we need to determine how many bags left to buy. In this case, it's NOT HowManyToBuy, because what if it bought 10 already in the last loop? That's why we created BagsBought!
Code: Select all
variable int MaxBagsWeCanBuy
      MaxBagsWeCanBuy:Set[0]
      ;// The max we can buy, will be the LOWER number of inventory space, and How many we want to buy.
      MaxBagsWeCanBuy:Set[${Me.InventorySlotsFree}]
      
      ;// Determine how many bags we still need to buy.
      variable int BagsStillToBuy
      BagsStillToBuy:Set[0]
      BagsStillToBuy:Set[ ${Math.Calc[ ${_HowManyToBuy} - ${BagsBought} ]} ]
      
      ;// If we have less buys than inventory space, only buy the max amount of bags.
      if ${MaxBagsWeCanBuy} > ${BagsStillToBuy}
      {
         MaxBagsWeCanBuy:Set[${BagsStillToBuy}]
      }

We know how many bags we need to buy this loop. To buy, we need to open up the merchant window!
Code: Select all
;// We need to buy -> MaxBagsWeCanBuy <- many bags.
      ;// Target the merchant.
      Actor["${MerchantName}"]:DoTarget
      
      ;// We don't actually need to "wait" for it to target him to double click him. We only need to target him to activate the ISXEQ2 merchant TLO. Don't worry about this.
      Actor["${MerchantName}"]:DoubleClick
      
      ;// Now we should wait, to make sure the target happened, and the merchant window populated. (We should verify the window is open etc).
      ;// 1/2 second should be fine. If you have big delay, you can increase it (wait 10 = 1second)
      wait 5

We have the merchant window open, now we have to make another loop to buy the bags! For this, lets use a for statement.
Note: You could use a while loop like we have, but the purpose of this is to use different things and learn. So for statement it is!
We want it to loop until we've bought "MaxBagsWeCanBuy"
Code: Select all
;// Merchant window is open! Lets commence buying.
      variable int BoughtThisLoop
      
      ;// Set up a 'for' loop. At the start, set the BoughtThisLoop to 0, before each loop we check if we have bought less than our max. and at the end of each loop, we increase that we bought by one.
      for ( BoughtThisLoop:Set[0] ; ${BoughtThisLoop} < ${MaxBagsWeCanBuy} ; BoughtThisLoop:Inc )
      {
         Vendor.Merchant["A bag of random status items"]:Buy[1]
         ;// Small delay after each purchase
         variable int RandomDelay
         ;// Math.Rand randoms a number from 0 to the value-1. In our example, Math.Rand[3], it will return a number between 0 and 2.
         RandomDelay:Set[ ${Math.Rand[3]} ]
         ;// Now we increase it by 5, giving us a number between 5 and 7.
         RandomDelay:Inc[5]
         wait ${RandomDelay}
      }

We want to update out values. We'll increase BagsBought by how many bags we bought this loop.
Code: Select all
;// Update that we bought them.
      BagsBought:Inc[${BoughtThisLoop}]

We need to unpack the bags.
To do this, I'm going to make the unpack bags into a function, then call the function. The main reason is to just show calling a function.
First, lets create our function.
Code: Select all
function UnpackStatusBags()
{
   ;// Make sure we're not dealing with the merchant.
   EQ2UIPage[Inventory,Merchant].Child[button,Merchant.WindowFrame.Close]:LeftClick   
   ;// For FetishUI
   EQ2UIPage[Inventory,Merchant].Child[button,Merchant.WC_CloseButton]:LeftClick
   wait 5
   
   ;// Check and make sure we have a bag.
   while ${Me.Inventory["A bag of random status items"](exists)}
   {
      ;// Examine it!
      Me.Inventory[A bag of random status items]:Examine
    wait 5
    ;// Click to unpack it.
    ReplyDialog:Choose[1]
    wait 5
      ;// Accept the rewards
   RewardWindow:Receive
    wait 5
   }
}

You will notice we're closing the actual window, instead of just /close_top_window. They both work, here is just another example (technically this is a cleaner way of doing it, as you're closing the exact window you want).
Even have some love for the FetishUI users!
Now, we want to call this function after our BagsBought:Inc code. That's simple.
Code: Select all
call UnpackStatusBags


We took a very basic 25 line script that worked, and we turned it into a monster 160 line code/comment monster with added functionality!
Here is our final script:
Code: Select all

function main(int _HowManyToBuy=100)
{
   variable int64 MyStatus
   variable string sMyStatus=${EQ2DataSourceContainer[GameData].GetDynamicData[Stats.CurrentStatus].ShortLabel}
   ;// This value comes in the form of a string, IE: 2,123,000. If you try to assign it to a numerical value, it stops at the first comma, resulting in "2".
   ;// We store it as a string, remove the commas, then convert it to an int64.
   ;// Below may be hard to read, it's replace ","   with "".
   sMyStatus:Set[${sMyStatus.Replace[",",""]}]
   MyStatus:Set[${sMyStatus}]
   
   variable int64 MyPlat=${Me.Platinum}
   variable int64 StatusCostOfBag=32500
   variable int64 PlatCostOfBag=1
   
   ;// Do we have enough status to buy all the bags we want?
   variable int CanAffordViaStatus
   ;// The "\" is an escape character, so we need to use 2 of them, to get a \.
   ;// "/" is divide by, and will return a float (IE: 5.32). We can't buy a faction of a bag.
   ;// "\" is divide by integer, so it will return a full number only (IE: 5).
   CanAffordViaStatus:Set[ ${Math.Calc[ ${MyStatus} \\ ${StatusCostOfBag} ]} ]
   
   ;// Lets see if we can afford how many we want to buy
   ;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaStatus}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough status. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaStatus}]
   }
   
   ;// Do we have enough platinum to buy all the bags we want?
   variable int CanAffordViaPlat
   CanAffordViaPlat:Set[ ${Math.Calc[ ${MyPlat} \\ ${PlatCostOfBag} ]} ]
   
   ;// If the amount we WANT to buy, is MORE than we can buy, lets just change how many we want to buy, to how many we can afford.
   if ${_HowManyToBuy} > ${CanAffordViaPlat}
   {
      ;// We should probably notify the user we're doing it, so they don't report a "bug"
      echo ${Time}: You can't afford to buy ${_HowManyToBuy} because you don't have enough platinum. Reducing the number to ${CanAffordViaStatus}.
      _HowManyToBuy:Set[${CanAffordViaPlat}]
   }
   
   if ${_HowManyToBuy} <= 0
   {
      echo ${Time}: You don't wish to buy any. Good bye!
      Script:End
   }
   
   ;// Determine our zone, and where add the name of the merchant.
   variable string MerchantName
   switch ${Zone.Name}
   {
      case Qeynos Province District
         MerchantName:Set[Rabbleson]
      break
      case The City of Freeport
         MerchantName:Set[Progenitus]
      break
      default
         echo ${Time}: Unable to determine who the merchant is for this zone.
         Script:End
      break
   }
   ;// Are we near a city merchant? When we say "near", we want to use the approximate max distance you can interact. Generally this is 10m+, so to be safe, lets use 10m.
   ;// For this, we're going to use the "opposites". If a city merchant does NOT exist, or if he is  OVER 10 meters, we give an error, and exit the script.
   if !${Actor["${MerchantName}"](exists)}
   {
      echo ${Time}: No city merchant found.
      Script:End
   }
   if ${Actor["${MerchantName}"].Distance} > 10
   {
      echo ${Time}: ${MerchantName} too far away ( ${Actor["${MerchantName}"].Distance} ). Needs to be less than 10 meters away.
      Script:End
   }
   
   ;// Make sure we have at least ONE inventory slot available.
   if ${Me.InventorySlotsFree} <= 0
   {
      echo ${Time}: You don't have any inventory slots free! You need at least 1 to purchase bags.
      Script:End
   }
   
   ;// We we create a loop to buy. It's possible we will only loop once, or we may have to loop more than once.
   ;// Create a variable, we can stop looping.
   variable bool StopLooping=FALSE
   variable int BagsBought=0
   
   ;// Loop as long as we're not suppose to stop (StopLooping) and as long as there are more bags to buy.
   while !${StopLooping} && ${BagsBought} < ${_HowManyToBuy}
   {
      variable int MaxBagsWeCanBuy
      MaxBagsWeCanBuy:Set[0]
      ;// The max we can buy, will be the LOWER number of inventory space, and How many we want to buy.
      MaxBagsWeCanBuy:Set[${Me.InventorySlotsFree}]
      
      ;// Determine how many bags we still need to buy.
      variable int BagsStillToBuy
      BagsStillToBuy:Set[0]
      BagsStillToBuy:Set[ ${Math.Calc[ ${_HowManyToBuy} - ${BagsBought} ]} ]
      
      ;// If we have less buys than inventory space, only buy the max amount of bags.
      if ${MaxBagsWeCanBuy} > ${BagsStillToBuy}
      {
         MaxBagsWeCanBuy:Set[${BagsStillToBuy}]
      }
      
      ;// We need to buy -> MaxBagsWeCanBuy <- many bags.
      ;// Target the merchant.
      Actor["${MerchantName}"]:DoTarget
      
      ;// We don't actually need to "wait" for it to target him to double click him. We only need to target him to activate the ISXEQ2 merchant TLO. Don't worry about this.
      Actor["${MerchantName}"]:DoubleClick
      
      ;// Now we should wait, to make sure the target happened, and the merchant window populated. (We should verify the window is open etc).
      ;// 1/2 second should be fine. If you have big delay, you can increase it (wait 10 = 1second)
      wait 5
      
      ;// Merchant window is open! Lets commence buying.
      variable int BoughtThisLoop
      
      ;// Set up a 'for' loop. At the start, set the BoughtThisLoop to 0, before each loop we check if we have bought less than our max. and at the end of each loop, we increase that we bought by one.
      for ( BoughtThisLoop:Set[0] ; ${BoughtThisLoop} < ${MaxBagsWeCanBuy} ; BoughtThisLoop:Inc )
      {
         Vendor.Merchant["A bag of random status items"]:Buy[1]
         ;// Small delay after each purchase
         variable int RandomDelay
         ;// Math.Rand randoms a number from 0 to the value-1. In our example, Math.Rand[3], it will return a number between 0 and 2.
         RandomDelay:Set[ ${Math.Rand[3]} ]
         ;// Now we increase it by 5, giving us a number between 5 and 7.
         RandomDelay:Inc[5]
         wait ${RandomDelay}
      }
      ;// Once it has bought all it is suppose to above
      ;// Update that we bought them.
      BagsBought:Inc[${BoughtThisLoop}]
      ;// Now we need to unpack them.
      ;// We're going to call a function to do this for us.
      call UnpackStatusBags
      ;// It will loop back to the start of the while ( while !${StopLooping} && ${BagsBought} < ${_HowManyToBuy} )
      ;// If BagsBought is still less than _HowManyToBuy, then it starts all over again! Otherwise, it exits the while.
   }
   ;// This is outside of the while statement. We should be all finished. Lets report how many we bought.
   echo ${Time}: Bought ${BagsBought}! All finished.
}
function UnpackStatusBags()
{
   ;// Make sure we're not dealing with the merchant.
   EQ2UIPage[Inventory,Merchant].Child[button,Merchant.WindowFrame.Close]:LeftClick   
   ;// For FetishUI
   EQ2UIPage[Inventory,Merchant].Child[button,Merchant.WC_CloseButton]:LeftClick
   wait 5
   
   ;// Check and make sure we have a bag.
   while ${Me.Inventory["A bag of random status items"](exists)}
   {
      ;// Examine it!
      Me.Inventory[A bag of random status items]:Examine
    wait 5
    ;// Click to unpack it.
    ReplyDialog:Choose[1]
    wait 5
      ;// Accept the rewards
    RewardWindow:Receive
    wait 5
   }
}


Hopefully you learned something along the way. At the very least, I hope you can see how when you suggest something like "it would be easy for you to add ____", there are a lot of considerations to be had, even for simple things.

If you have any questions, or comments, please post them in this thread, or ask in IRC #ogre channel. Please do not PM me with questions regarding this. This is for people to learn, and the Q&A should be public also (and there's no reason to ask only me).

Also worth noting, there are 100 other ways this script could be written. There are other functions it should have that return values, make better use of reusing code etc, but it would certainly make it more complex (IMO).

Again, special thanks to Cheesy for posting his original code.

-Kannkor
Last edited by Kannkor on 04 Jan 2017, 20:32, edited 4 times in total.
Reason: Fixed a bunch of typos.
Kannkor
 
Posts: 354
Joined: 06 Nov 2013, 11:49

Re: Writing a basic script [Bags of status]

Postby Plavok » 04 Jul 2014, 07:20

In your preliminary examples (not your final solution), I think you forgot to increment the counter or is that not needed for these loops?

(Now read through it in full) Great tutorial, thank you!

Do you know if there is a way to make a station cash store purchase (for DM marks), since there is no vendor for this?

/Plavok
Last edited by Plavok on 04 Jul 2014, 07:34, edited 1 time in total.
Plavok
 
Posts: 5
Joined: 11 Dec 2013, 08:28

Re: Writing a basic script [Bags of status]

Postby Kannkor » 04 Jul 2014, 07:25

Plavok wrote:In your preliminary examples (not your final solution), I think you forgot to increment the counter or is that not needed for these loops?

/Plavok


Nice catch. I believe I've fixed them all.
Kannkor
 
Posts: 354
Joined: 06 Nov 2013, 11:49

Re: Writing a basic script [Bags of status]

Postby ethreayd » 06 Jul 2014, 04:54

For Freeport I add that to the script :
Code: Select all
      case The City of Freeport
    MerchantName:Set[Progenitus]


So this part is now :
Code: Select all
;// Determine our zone, and where add the name of the merchant.
   variable string MerchantName
   switch ${Zone.Name}
   {
      case Qeynos Province District
         MerchantName:Set[Rabbleson]
     break
      case The City of Freeport
        MerchantName:Set[Progenitus]
      break
      default
         echo ${Time}: Unable to determine who the merchant is for this zone.
         Script:End
      break
   }


(don't know from where the indent problem come when showing the code)

Cheers
Last edited by Kannkor on 09 Jul 2014, 20:45, edited 1 time in total.
Reason: Edit by Kannkor: Added a missing "break" and cleaned up spacing a little bit.
ethreayd
 
Posts: 2
Joined: 06 Jul 2014, 04:51

Re: Writing a basic script [Bags of status]

Postby ethreayd » 03 Jan 2015, 12:51

Just try the script again and got an error message when recieving bag content :
Code: Select all
nospace: execute run statusbags.iss
Error:No such 'rewardwindow' method 'Recieve' @RewardWindow:Recieve
Error parsing data sequence ''
Dumping script stack
--------------------
-->C:/Program Files (x86)/InnerSpace/Scripts/statusbags.iss:164 UnpackStatusBags() RewardWindow:Recieve
C:/Program Files (x86)/InnerSpace/Scripts/statusbags.iss:139 main() call UnpackStatusBags


You need to change :
Code: Select all
RewardWindow:Recieve


by

Code: Select all
RewardWindow:Receive
ethreayd
 
Posts: 2
Joined: 06 Jul 2014, 04:51


Return to Guides & Strats

Who is online

Users browsing this forum: No registered users and 9 guests

cron