Tutorial How to write an Alchemy script

Discussion in 'Tutorials & Resources' started by Aidden, Feb 2, 2015.

  1. Hey guys, just posting the source for my old alcher here as it was originally open source and fully commented. I'm moving it to my new framework and didn't want that to be public too :)

    This isn't quite a tutorial in the normal sense of the word but it's so heavily commented that it may as well be :p

    I've only included the main script bot here as the other classes are really just helpers. Enjoy!


    Code (Text):
    1.  
    2. package com.runemate.maxiscripts.looping.alchemy;
    3.  
    4. //Imports are all the classes that we are going to use methods from
    5. import java.awt.Color;
    6. import java.awt.Graphics2D;
    7. import java.awt.Rectangle;
    8. import java.awt.event.MouseEvent;
    9. import java.awt.event.MouseListener;
    10.  
    11. import com.runemate.api.Calculations;
    12. import com.runemate.api.actionbar;
    13. import com.runemate.game.api.client.paint.PaintListener;
    14. import com.runemate.game.api.hybrid.Environment;
    15. import com.runemate.game.api.hybrid.RuneScape;
    16. import com.runemate.game.api.hybrid.entities.definitions.ItemDefinition;
    17. import com.runemate.game.api.hybrid.local.Skill;
    18. import com.runemate.game.api.hybrid.local.hud.interfaces.InterfaceWindows;
    19. import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
    20. import com.runemate.game.api.hybrid.local.hud.interfaces.SpriteItem;
    21. import com.runemate.game.api.hybrid.util.StopWatch;
    22. import com.runemate.game.api.osrs.local.hud.interfaces.Magic;
    23. import com.runemate.game.api.rs3.local.CombatMode;
    24. import com.runemate.game.api.rs3.local.hud.Powers;
    25. import com.runemate.game.api.rs3.local.hud.interfaces.eoc.ActionBar;
    26. import com.runemate.game.api.rs3.local.hud.interfaces.eoc.ActionWindow;
    27. import com.runemate.game.api.rs3.local.hud.interfaces.eoc.SlotAction;
    28. import com.runemate.game.api.script.Execution;
    29. import com.runemate.game.api.script.framework.LoopingScript;
    30.  
    31. /* LoopingScript is the script framework we're going to use. Extending it forces us to have the onLoop method.
    32. * It also gives us access to a heap of methods like onStart, getEventDispatcher and setLoopDelay to name a few.
    33. * By extending it, our class is now of that type. For a full list of methods we inherit from LoopingScript look in
    34. * the api or type 'this.' to list all methods we have direct access to.
    35. * Implementing PaintListener gives us access to the onPaint method which is what draws things on the screen.
    36. * For it to actually draw on the screen though you need to add this class to the event dispatcher as we do in onStart.
    37. */
    38. public class MaxiAlcher extends LoopingScript implements PaintListener, MouseListener{
    39.  
    40.     private boolean cast = false; //This will store whether the alchemy spell is currently selected
    41.     private String status = "Loading..."; //this will be used to store and print out the status
    42.     private final StopWatch runtime = new StopWatch(); //This will be used to show how long the script has been running
    43.     private int casts = 0; //stores how many times we've cast a spell and will be displayed to the user
    44.     private int startXp = -1; //this will store how much magic experience the user had when the script started.
    45.     private int startLvl = -1; //this will store what the users magic level was when the script started
    46.     private int xpGained = 0; //this will store how much magic experience the user has gained while running the script
    47.     private int lvlsGained = 0; //this will store how many magic levels the user has gained while running the script
    48.     private GUI gui; //this is an object of the gui that pops up when the script starts. without it the gui wouldn't show up
    49.     private Rectangle showGUI = new Rectangle(445, 300, 60, 25);
    50.  
    51.     /* This method gets called automatically when the script first starts.
    52.     * args is a parameter parsed to the script
    53.     * String... is called a varargs and basically just means it can either be a single String, multiple Strings separated by a comma or a String array(String[])
    54.     * @Override means that you've inherited this method from one of the classes you've extended or implemented and that you want to call this version of the method
    55.     * instead of the one in the class it's inherited from
    56.     */
    57.     @Override
    58.     public void onStart(String... args) {
    59.         getEventDispatcher().addListener(this); //This tells the client that this class has events that need to be listened for. In this case it's for the PaintListener to draw on the screen.
    60.         setLoopDelay(100, 200); //Sets the length of time in milliseconds to wait before calling onLoop again
    61.         System.out.println(getMetaData().getName() + " V" + getMetaData().getVersion() + " started!"); //This just prints to the console and consequentially the log file saying 'Thanks for using MaxiAlcher!'
    62.         gui = new GUI(); //This is where we actually instantiate the gui object.
    63.     }
    64.  
    65.     //This is called when the script gets paused via the pause button on the client
    66.     @Override
    67.     public void onPause() {
    68.         runtime.stop(); //Stops the runtime timer so that the runtime doesn't keep counting time while the script is paused
    69.     }
    70.  
    71.     //This is called when the script is unpaused via the resume button on the client
    72.     @Override
    73.     public void onResume() {
    74.         runtime.start(); //Resumes the runtime timer
    75.     }
    76.  
    77.     //This is called just before the script stops
    78.     @Override
    79.     public void onStop() {
    80.         //All of the println's below print the session stats to the console/log before the script stops
    81.         System.out.println("---"+getMetaData().getName()+"---");
    82.         System.out.println("Runtime: " + runtime.getRuntimeAsString());
    83.         System.out.println("Last status: "+status);
    84.         System.out.println("Level: " + Skill.MAGIC.getCurrentLevel() + "(+" + lvlsGained + ")");
    85.         System.out.println("Xp gained: " + xpGained);
    86.         System.out.println("Casts: " + casts);
    87.         System.out.println("---Thanks for using " + getMetaData().getName() + "!---");
    88.         if (gui != null) {
    89.             gui.setVisible(false); //Makes sure the gui isn't visible
    90.             gui.dispose(); //Tells the garbage collector that the gui's memory can be freed for use elsewhere
    91.         }
    92.     }
    93.  
    94.     //This gets called over and over and is the main body of your script.
    95.     @Override
    96.     public void onLoop() {
    97.         if (RuneScape.isLoggedIn() && !gui.isVisible()) { //If we're logged in to the game and the gui isn't visible continue inside this set of {}
    98.             /* If the startXp is still -1 (the initial value) and the players constitution experience is greater than 0 continue inside this set of {}
    99.             * We check the constitution to make sure the user is fully logged in as to avoid false startXp values
    100.             */
    101.             if (startXp == -1 && Skill.CONSTITUTION.getExperience() > 0) {
    102.                 runtime.start(); //Start the runtime timer
    103.                 startXp = Skill.MAGIC.getExperience(); //Set the startXp to the players current magic experience
    104.                 startLvl = Skill.MAGIC.getBaseLevel(); //Set the startLvl to the players current magic level
    105.             }
    106.  
    107.             if (Inventory.containsAllOf("Nature rune") && Inventory.containsAnyOf(gui.getItems())) { //If the Inventory contains at least one Nature rune and one of the item the user has said to alch continue inside this set of {}
    108.                 if (Environment.isOSRS() || CombatMode.LEGACY.isCurrent()) { //If we're playing osrs or the CombatMode is set to legacy continue inside this set of {}
    109.                     if (InterfaceWindows.getMagic().isOpen()) { //If the magic interface is open continue inside this set of {}
    110.  
    111.                         /*
    112.                         * This will set the status to 'Activating High Alchemy' if gui.highAlchemy() returns true
    113.                         * If it returns false it will set the status to 'Activating Low Alchemy'.
    114.                         * (gui.highAlchemy() ? "High Alchemy" : "Low Alchemy") is called a ternary statement
    115.                         * and tells the script whether to say "High Alchemy" or "Low Alchemy"
    116.                         * It's the same as writing the following:
    117.                         * if (gui.highAlchemy() {
    118.                         *     return "High Alchemy";
    119.                         * }
    120.                         * else {
    121.                         *     return "Low Alchemy";
    122.                         * }
    123.                         */
    124.                         status = "Activating " + (gui.highAlchemy() ? "High Alchemy" : "Low Alchemy");
    125.  
    126.                         /*
    127.                         * This time we've got a ternary statement as the return type of the first ternary statement
    128.                         * This single line is the same as writing the following 16 lines:
    129.                         * if (Environment.isOSRS()) {
    130.                         *     if (gui.highAlchemy()) {
    131.                         *         Magic.HIGH_LEVEL_ALCHEMY.activate();
    132.                         *     }
    133.                         *     else {
    134.                         *         Magic.LOW_LEVEL_ALCHEMY.activate();
    135.                         *     }
    136.                         * }
    137.                         * else {
    138.                         *       if (gui.highAlchemy()) {
    139.                         *         Powers.Magic.HIGH_LEVEL_ALCHEMY.activate();
    140.                         *     }
    141.                         *     else {
    142.                         *         Powers.Magic.HIGH_LEVEL_ALCHEMY.activate();
    143.                         *     }
    144.                         * }
    145.                         */
    146.                         if (Environment.isOSRS() ? (gui.highAlchemy() ? Magic.HIGH_LEVEL_ALCHEMY.activate() : Magic.LOW_LEVEL_ALCHEMY.activate()) : (gui.highAlchemy() ? Powers.Magic.HIGH_LEVEL_ALCHEMY.activate() : Powers.Magic.LOW_LEVEL_ALCHEMY.activate())) {
    147.                             cast = true; //Set cast to true so the script knows the alchemy spell has been selected
    148.                             Execution.delay(100, 200); //Wait a random amount of time between 100ms and 200ms before continuing
    149.                         }
    150.                     }//If the magic window wasn't open, check if the alchemy spell is selected. If not call openMagic()
    151.                     else if (!cast) {
    152.                         openMagic();
    153.                     }
    154.                     else {//If the magic window wasn't open and the alchemy spell is selected continue in here
    155.  
    156.                         if (InterfaceWindows.getInventory().isOpen()) {//if the inventory is open call castOn()
    157.                             castOn();
    158.                         }
    159.                         else { //if the inventory isn't open, open it
    160.                             if (InterfaceWindows.getInventory().open()) {
    161.                                 /*
    162.                                 * Here we wait for either the inventory to open or for 1 second to have passed.
    163.                                 * () -> is a new feature in java 8 and is called a lambda
    164.                                 * If i wasn't to use a lambda here this is what the code would have looked like:
    165.                                 * Execution.delayUntil(new Callable<Boolean>() {
    166.                                 *
    167.                                 *  @Override
    168.                                 *  public Boolean call() throws Exception {
    169.                                 *     return InterfaceWindows.getInventory().open();
    170.                                 *  }
    171.                                 * }, 1000);
    172.                                 *
    173.                                 * Lambdas strip away all that extra junk and leaves you with the functional line you actually wanted to write.
    174.                                 * () represents the call method and if it needed to take parameters we put the parameters like this (param1, param2, etc...) -> doSomething
    175.                                 */
    176.                                 Execution.delayUntil(() -> InterfaceWindows.getInventory().open(), 1000);
    177.                             }
    178.                         }
    179.                     }
    180.                 }
    181.                 else if (cast){ //If we're in RS3 and the CombatMode isn't Legacy and we have selected the alchemy spell call castOn()
    182.                     castOn();
    183.                 }
    184.                 else { //If we're in RS3 and the CombatMode isn't Legacy and we haven't selected the alchemy spell call alchEoC()
    185.                     alchEoC();
    186.                 }
    187.             }
    188.         }
    189.     }
    190.  
    191.     private void castOn() {
    192.         final SpriteItem item = Inventory.getItems(gui.getItems()).first(); //Get the first item in the inventory with the name that the user specified in the gui
    193.         if (item != null) { //If the item exists
    194.             String name = "item";
    195.             final ItemDefinition def = item.getDefinition();
    196.             if (def != null) {
    197.                 final String temp = def.getName();
    198.                 if (temp != null && !temp.isEmpty()) {
    199.                     name = temp;
    200.                 }
    201.             }
    202.             status = "Casting alchemy on " + name;
    203.             if (item.interact("Cast")) { //Try to interact with the item and if successful
    204.                 cast = false; //Set cast to false as the spell is no longer selected
    205.                 casts++; //Increase our spell cast counter
    206.                 Execution.delayUntil(() -> InterfaceWindows.getMagic().isOpen(), 2000); //Wait until the magic window has opened again or until 2000 ms (2 seconds) has passed
    207.             }
    208.             else {
    209.                 cast = false;
    210.             }
    211.         }
    212.     }
    213.     private void alchEoC() {
    214.         if (!ActionBar.isExpanded()) { //If the actionbar isn't expanded call expandActionbar()
    215.             expandActionbar();
    216.         }
    217.         else { //If it is expanded
    218.             final SlotAction action = actionbar.getSlot(gui.highAlchemy() ? "High Level Alch" : "Low Level Alch"); //Get the slot in the actionbar that contains the selected alchemy spell
    219.  
    220.             if (action != null) { //If there actually is a slot with the spell on it call activateAlchemy() and parse the actionbar slot as a parameter
    221.                 activateAlchemy(action);
    222.             }
    223.             else { //If there isn't a slot with the spell on it
    224.                 if (InterfaceWindows.getMagic().isOpen()) { //If the magic interface is open
    225.                     final Powers.Magic spell = (gui.highAlchemy() ? Powers.Magic.HIGH_LEVEL_ALCHEMY : Powers.Magic.LOW_LEVEL_ALCHEMY); //Get the spell
    226.                     status = "Adding "+(gui.highAlchemy() ? "High Level Alchemy" : "Low Level Alchemy")+" to the action bar";
    227.                     if (spell != null) { //if the spell exists
    228.                         ActionBar.Slot empty = actionbar.getEmptySlot(); //Get the first empty actionbar slot
    229.                         if (empty != null) { //if there was an empty slot
    230.                             if (actionbar.addToActionbar(spell.getComponent().getBounds(), empty)) { //Add the spell to the actionbar in the empty slot
    231.                                 if (ActionWindow.MAGIC_ABILITIES.close()) { //close the magic window as we no longer need it (the spell is now on the actionbar)
    232.                                     Execution.delay(100, 200);
    233.                                 }
    234.                             }
    235.                         }
    236.                     }
    237.                 }
    238.                 else { //If the magic interface isn't open call openMagic()
    239.                     openMagic();
    240.                 }
    241.             }
    242.         }
    243.     }
    244.  
    245.     private void expandActionbar() {
    246.         status = "Expanding the action bar";
    247.         if (ActionBar.toggleExpansion()) { //Attempt to toggle the actionbars expansion, if successful wait until it has expanded or until 1 second has passed
    248.             Execution.delayUntil(() -> ActionBar.isExpanded(), 1000);
    249.         }
    250.     }
    251.  
    252.     private void activateAlchemy(SlotAction action) {
    253.         final ActionBar.Slot slot = action.getSlot(); //Get the slot that contains the action
    254.         if (slot != null) { //If the slot exists
    255.             status = "Activating "+(gui.highAlchemy() ? "High Level Alchemy" : "Low Level Alchemy");
    256.             if (slot.getAction().activate()) { //Activate the slot (cast the spell)
    257.                 cast = true;
    258.                 Execution.delay(100, 200);
    259.             }
    260.         }
    261.     }
    262.  
    263.     private void openMagic() {
    264.         status = "Opening spellbook";
    265.         if (InterfaceWindows.getMagic().open()) { //Attempt to open the magic book and is successful
    266.             Execution.delayUntil(() -> InterfaceWindows.getMagic().isOpen(), 1000);
    267.         }
    268.     }
    269.  
    270.  
    271.     /*
    272.     * This is where we put everything that we want to draw to the screen
    273.     * Graphics2D is the class that contains all the paint methods
    274.     */
    275.     @Override
    276.     public void onPaint(final Graphics2D g) {
    277.         int x=50,y=50; //Set the initial drawing coordinates to 50 px to the right and down from the top left of the applet
    278.         g.setColor(Color.black);
    279.         g.fillRect(445, 300, 60, 25);
    280.         g.setColor(Color.white);
    281.         g.drawString("Settings", 450, 315);
    282.         xpGained = startXp == -1 ? 0 : Skill.MAGIC.getExperience() - startXp; //Set the xpGained to 0 if we haven't set the startXp yet, otherwise set it to the currentXp - startXp
    283.         final int lvl = Skill.MAGIC.getCurrentLevel(); //this will store the current magic level
    284.         lvlsGained = (startLvl == -1 ? 0 : lvl - startLvl); //Same deal as with xpGained
    285.  
    286.         g.drawString("Version " + getMetaData().getVersion(), x, y+=15); //This will draw a string at the current x value and the current y value and then add 15 to the y value
    287.         g.drawString("Runtime: " + runtime.getRuntimeAsString(), x, y+=15);
    288.         g.drawString("Status: " + status, x, y+=15);
    289.         if (gui != null) g.drawString("Type: " + (gui.highAlchemy() ? "High Alchemy" : "Low Alchemy"), x, y+=15); //If the gui exists draw the spell that the user selected
    290.         g.drawString("Level: " + lvl + "(+" + lvlsGained + ")" , x, y+=15);
    291.         g.drawString("Xp Gained(/hr): " + xpGained + "(/" + Calculations.getHourly(xpGained, runtime.getRuntime()) + ")", x, y+=15); //Draw the xpGained as well as the amount of Xp they're gaining every hour at the current rate
    292.         g.drawString("Xp to next level: "+Skill.MAGIC.getExperienceToNextLevel(), x, y+=15);
    293.         g.drawString("Casts(/hr): " + casts + "(/" + Calculations.getHourly(casts, runtime.getRuntime()) + ")", x, y+=15);
    294.     }
    295.  
    296.     @Override
    297.     public void mouseClicked(MouseEvent m) {
    298.         if (showGUI.contains(m.getPoint())) {
    299.             if (gui != null && !gui.isVisible())
    300.                 gui.setVisible(true);
    301.         }
    302.     }
    303.  
    304.     @Override
    305.     public void mouseEntered(MouseEvent arg0) {}
    306.  
    307.     @Override
    308.     public void mouseExited(MouseEvent arg0) {}
    309.  
    310.     @Override
    311.     public void mousePressed(MouseEvent arg0) {}
    312.  
    313.     @Override
    314.     public void mouseReleased(MouseEvent arg0) {}
    315. }
    316.  
    317.  
    And here's the manifest. If you don't write a manifest for your script bot it won't show in the script bot selector. Save it as a .xml file anywhere in your script bot folder
    Code (Text):
    1.  
    2. <manifest>
    3.     <main-class>com.runemate.maxiscripts.looping.alchemy.MaxiAlcher</main-class>
    4.     <name>MaxiAlcher</name>
    5.     <description>Casts Low/High Alchemy</description>
    6.     <version>1.0.5</version>
    7.     <open-source>false</open-source>
    8.     <hidden>false</hidden>
    9.     <compatibility>
    10.         <game-type>OSRS</game-type>
    11.         <game-type>RS3</game-type>
    12.     </compatibility>
    13.     <categories>
    14.         <category>MAGIC</category>
    15.         <category>MONEYMAKING</category>
    16.     </categories>
    17.     <tags>
    18.         <tag>alchemy</tag>
    19.         <tag>money</tag>
    20.     </tags>
    21.     <tag>magic</tag><!--Required to publish on the bot store--><internal-id>alcher</internal-id>
    22. </manifest>
     
    #1 Aidden, Feb 2, 2015
    Last edited: Feb 7, 2015
    Serene, Microsoft, Arcurus and 3 others like this.

Share This Page

Loading...