Equinox

EquinoxZMICrafter.java

Mar 10th, 2011
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 108.76 KB | None | 0 0
  1. import java.awt.Color;
  2. import java.awt.Dimension;
  3. import java.awt.Font;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Image;
  7. import java.awt.Point;
  8. import java.awt.Polygon;
  9. import java.awt.Rectangle;
  10. import java.awt.event.MouseEvent;
  11. import java.awt.event.MouseListener;
  12. import java.awt.event.MouseMotionListener;
  13. import java.awt.font.FontRenderContext;
  14. import java.awt.geom.AffineTransform;
  15. import java.awt.geom.RoundRectangle2D;
  16. import java.awt.image.BufferedImage;
  17. import java.io.File;
  18. import java.io.IOException;
  19. import java.lang.reflect.Method;
  20. import java.net.URL;
  21. import java.text.DecimalFormat;
  22. import java.util.ArrayList;
  23. import java.util.EnumSet;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.logging.Logger;
  28.  
  29. import javax.imageio.ImageIO;
  30. import javax.swing.JOptionPane;
  31.  
  32. import org.rsbot.event.events.MessageEvent;
  33. import org.rsbot.event.listeners.MessageListener;
  34. import org.rsbot.event.listeners.PaintListener;
  35. import org.rsbot.script.Script;
  36. import org.rsbot.script.ScriptManifest;
  37. import org.rsbot.script.methods.Equipment;
  38. import org.rsbot.script.methods.Game;
  39. import org.rsbot.script.methods.GrandExchange;
  40. import org.rsbot.script.methods.GrandExchange.GEItem;
  41. import org.rsbot.script.methods.Magic;
  42. import org.rsbot.script.methods.Skills;
  43. import org.rsbot.script.util.Filter;
  44. import org.rsbot.script.wrappers.RSComponent;
  45. import org.rsbot.script.wrappers.RSInterface;
  46. import org.rsbot.script.wrappers.RSItem;
  47. import org.rsbot.script.wrappers.RSNPC;
  48. import org.rsbot.script.wrappers.RSObject;
  49. import org.rsbot.script.wrappers.RSPath.TraversalOption;
  50. import org.rsbot.script.wrappers.RSTile;
  51. import org.rsbot.script.wrappers.RSTilePath;
  52. import org.rsbot.util.GlobalConfiguration;
  53.  
  54. @ScriptManifest(authors = "Equinox_", keywords = "Runecrafting", name = "Equinox_ ZMI Crafter", version = 0.7, description = "Beta Version")
  55. /**
  56.  * @author Equinox_
  57.  */
  58. public class EquinoxZMICrafter extends Script implements MessageListener,
  59.         PaintListener, MouseMotionListener, MouseListener {
  60.  
  61.     // Variables
  62.     final String baseURL = "http://equinox-scripts.web44.net/scripts/natcrafter";
  63.     final double VERSION = getClass().getAnnotation(ScriptManifest.class)
  64.             .version();
  65.     final String author = PaintFormatter.formatList(getClass().getAnnotation(
  66.             ScriptManifest.class).authors());
  67.     final String name = getClass().getAnnotation(ScriptManifest.class).name();
  68.     public final static int COSMIC_RUNE_ID = 564;
  69.     public final static int ASTRAL_RUNE_ID = 9075;
  70.     public static final int AIR_STAFF_ID = 1405;
  71.     public final static int PURE_ESS_ID = 7936;
  72.     public final static int ZMI_ALTAR_ID = 26847;
  73.     public final static int AIR_RUNE_ID = 556;
  74.     public final static int NPC_CONTACT_ANIM = 4413;
  75.     public final static int BANKER_ID = 6362;
  76.     public final static int CRAFT_RUNE_ANIM = 791;
  77.     public final static int FIRE_RUNE_ID = 554;
  78.     public final static int CRACK_IN = 26844;
  79.     public final static int CRACK_OUT = 26845;
  80.     public final static RSTile alterDet = new RSTile(3314, 4813, 0);
  81.     public final static int PAY_BANK_INTERFACE = 619;
  82.     public final static int LAW_RUNE = 563;
  83.     public final static int ASTRAL_RUNE = 9075;
  84.     public final static int EARTH_RUNE = 557;
  85.     public final static String[] PAY_BANKER_ORDER = { "fire", "air", "mind",
  86.             "body", "water", "earth", "cosmic", "chaos", "astral", "law",
  87.             "death", "blood", "nature", "soul" };
  88.  
  89.     public final static RSTile[][] SAFE_MESH = { { new RSTile(3273, 4856, 0) },
  90.             { new RSTile(3280, 4848, 0) }, { new RSTile(3279, 4839, 0) },
  91.             { new RSTile(3286, 4832, 0) }, { new RSTile(3297, 4834, 0) },
  92.             { new RSTile(3309, 4836, 0) }, { new RSTile(3320, 4833, 0) },
  93.             { new RSTile(3310, 4828, 0) }, { new RSTile(3301, 4828, 0) },
  94.             { new RSTile(3289, 4829, 0) }, { new RSTile(3280, 4822, 0) },
  95.             { new RSTile(3290, 4820, 0) }, { new RSTile(3301, 4820, 0) },
  96.             { new RSTile(3308, 4820, 0) } };
  97.  
  98.     public final static RSTile[][] DANGER_MESH = {
  99.             { new RSTile(3272, 4851, 0), new RSTile(3269, 4854, 0) },
  100.             { new RSTile(3272, 4844, 0), new RSTile(3268, 4846, 0) },
  101.             { new RSTile(3271, 4837, 0), new RSTile(3268, 4838, 0) },
  102.             { new RSTile(3272, 4828, 0), new RSTile(3267, 4832, 0) },
  103.             { new RSTile(3273, 4821, 0), new RSTile(3268, 4825, 0) },
  104.             { new RSTile(3270, 4814, 0), new RSTile(3268, 4819, 0) },
  105.             { new RSTile(3275, 4808, 0), new RSTile(3276, 4814, 0) },
  106.             { new RSTile(3283, 4806, 0), new RSTile(3279, 4810, 0) },
  107.             { new RSTile(3288, 4812, 0), new RSTile(3287, 4807, 0) },
  108.             { new RSTile(3295, 4813, 0), new RSTile(3293, 4814, 0) },
  109.             { new RSTile(3300, 4807, 0), new RSTile(3298, 4813, 0) },
  110.             { new RSTile(3309, 4812, 0), new RSTile(3305, 4807, 0) } };
  111.     public final RSTile[] OURANIA_PATH = { new RSTile(2469, 3245),
  112.             new RSTile(2459, 3249), new RSTile(2455, 3239),
  113.             new RSTile(2453, 3232) };
  114.     public final static int POUCH_TRIES = 20;
  115.  
  116.     // Pouch IDs
  117.     public static class Pouch {
  118.         public final static int SMALL = 5509;
  119.         public final static int MEDIUM = 5510;
  120.         public final static int MEDIUM_DEGRADED = 5511;
  121.         public final static int LARGE = 5512;
  122.         public final static int LARGE_DEGRADED = 5513;
  123.         public final static int GIANT = 5514;
  124.         public final static int GIANT_DEGRADED = 5515;
  125.         public final static int[] ALL = { SMALL, MEDIUM, MEDIUM_DEGRADED,
  126.                 LARGE, LARGE_DEGRADED, GIANT, GIANT_DEGRADED };
  127.         public final static int[] ALL_REPAIRED = { SMALL, MEDIUM, LARGE, GIANT };
  128.         public final static int[] ALL_DEGRADED = { MEDIUM_DEGRADED,
  129.                 LARGE_DEGRADED, GIANT_DEGRADED };
  130.         public final static int[] ALL_DEGRADEABLE = { MEDIUM, MEDIUM_DEGRADED,
  131.                 LARGE, LARGE_DEGRADED, GIANT, GIANT_DEGRADED };
  132.     }
  133.  
  134.     public static String serverMsg = "";
  135.     public HashMap<PouchType, PouchState> pouches = new HashMap<PouchType, PouchState>();
  136.     State currentState = State.LOST;
  137.     public MeshPath alterMeshPath;
  138.     public MeshPath bankMeshPath;
  139.     public PaintInterface Paint;
  140.     public NPCContact npcContact = new NPCContact();
  141.     public ModifiedDrawMouse drawMouse = new ModifiedDrawMouse();
  142.     public List<String> errorLog = new ArrayList<String>();
  143.     public final static int[] food = { 333, 329, 379, 373, 7946, 361, 1901,
  144.             1899, 1897, 1895, 1893, 1891, 2142 };
  145.     private static final int LADDER_ID = 26849;
  146.     public Path pathTypeCurrent;
  147.     public Options Options = new Options();
  148.  
  149.     // Options
  150.     public class Options {
  151.         public pouchRepairOption pouchRepair = pouchRepairOption.ONE;
  152.         public boolean hasAirStaff = true;
  153.         public boolean hasEarthStaff = true;
  154.         public PouchCheckSystem pouchCheckSystem = PouchCheckSystem.SERVER_MSG;
  155.         public Range walkingDistance = new Range(4, 6);
  156.         public Path pathType = Path.Danger;
  157.         public boolean chargeEnergy = true;
  158.         public boolean tryOuraniaTeleport = true;
  159.         public boolean onLunars = false;
  160.     }
  161.  
  162.     public static int[] mergeArrays(int[] a1, int... a2) {
  163.         int[] newArr = new int[a1.length + a2.length];
  164.         for (int i = 0; i < a1.length; i++)
  165.             newArr[i] = a1[i];
  166.         for (int i = 0; i < a2.length; i++)
  167.             newArr[a1.length + i] = a2[i];
  168.         return newArr;
  169.     }
  170.  
  171.     enum Path {
  172.         Danger, Safe
  173.     }
  174.  
  175.     /**
  176.      * When to repair pouches
  177.      *
  178.      * @author Equinox_
  179.      *
  180.      */
  181.     public enum pouchRepairOption {
  182.         ONE, TWO, ALL, NEVER
  183.     }
  184.  
  185.     /**
  186.      * How to determine a pouch's state
  187.      *
  188.      * @author Equinox_
  189.      *
  190.      */
  191.     public enum PouchCheckSystem {
  192.         SERVER_MSG, INVENTORY_STATUS
  193.     }
  194.  
  195.     /**
  196.      * A range class for randomized items
  197.      *
  198.      * @author Equinox_
  199.      *
  200.      */
  201.     public static class Range {
  202.         public int MAX;
  203.         public int MIN;
  204.  
  205.         public Range(int min, int max) {
  206.             MAX = Math.max(min, max);
  207.             MIN = Math.min(min, max);
  208.         }
  209.  
  210.         public Range() {
  211.             MAX = 1;
  212.             MIN = -1;
  213.         }
  214.  
  215.         /**
  216.          * Get a random number in the range
  217.          *
  218.          * @return random int between min and max
  219.          */
  220.         public int random() {
  221.             return (int) (Math.random() * (MAX - MIN)) + MIN;
  222.         }
  223.  
  224.         public String toString() {
  225.             return MIN + "-" + MAX;
  226.         }
  227.     }
  228.  
  229.     /**
  230.      * Possible states in the process of nature runecrafting
  231.      *
  232.      * @author Equinox_
  233.      *
  234.      */
  235.     public enum State {
  236.         NEAR_BANK, NPC_CONTACT, BANK_WITHDRAW, BANK_DEPOSIT, FILL_POUCHES, TRAVERSE_ALTER, CRAFT_RUNES, EMPTY_POUCHES, TRAVERSE_BANK, LOST, EAT_FOOD, CHARGE_ENERGY, OURANIA_TELEPORT, LADDER_DOWN, TRAVERSE_OURANIA
  237.     }
  238.  
  239.     /**
  240.      * Possible states of pouches, Empty or Full
  241.      *
  242.      * @author Equinox_
  243.      *
  244.      */
  245.     public enum PouchState {
  246.         Empty, Full
  247.     }
  248.  
  249.     /**
  250.      * Different pouches and their possible IDs
  251.      *
  252.      * @author Equinox_
  253.      *
  254.      */
  255.     public enum PouchType {
  256.         Small(Pouch.SMALL), Medium(Pouch.MEDIUM, Pouch.MEDIUM_DEGRADED), Large(
  257.                 Pouch.LARGE, Pouch.LARGE_DEGRADED), Giant(Pouch.GIANT,
  258.                 Pouch.GIANT_DEGRADED);
  259.         public int[] ids;
  260.  
  261.         private PouchType(int... ids) {
  262.             this.ids = ids;
  263.         }
  264.     }
  265.  
  266.     /**
  267.      * Gets the current state
  268.      *
  269.      * @return State the current state
  270.      * @throws Exception
  271.      *             so we can pipe errors back to the recorder
  272.      */
  273.     public State getState() throws Exception {
  274.         if (objects.getNearest(LADDER_ID) != null
  275.                 && objects.getNearest(LADDER_ID).isOnScreen())
  276.             return State.LADDER_DOWN;
  277.         if (objects.getNearest(ZMI_ALTAR_ID) != null)
  278.             if (calc.tileOnMap(objects.getNearest(ZMI_ALTAR_ID).getLocation())
  279.                     && (Options.pathType.equals(Path.Danger) || calc
  280.                             .pathLengthTo(alterDet, true) <= 15)) {
  281.                 if ((inventory.isFull() || !pouches
  282.                         .containsValue(PouchState.Full))
  283.                         && inventory.contains(PURE_ESS_ID))
  284.                     return State.CRAFT_RUNES;
  285.                 else if (pouches.containsValue(PouchState.Full))
  286.                     return State.EMPTY_POUCHES;
  287.                 // Check path type change
  288.                 if (!pathTypeCurrent.equals(Options.pathType)) {
  289.                     pathTypeCurrent = Options.pathType;
  290.                     // Set up path:
  291.                     if (Options.pathType.equals(Path.Danger))
  292.                         alterMeshPath = new MeshPath(DANGER_MESH);
  293.                     else
  294.                         alterMeshPath = new MeshPath(SAFE_MESH);
  295.                     bankMeshPath = alterMeshPath.reverse();
  296.                 }
  297.             }
  298.  
  299.         // Banking
  300.         if (npcs.getNearest(BANKER_ID) != null) {
  301.             if (npcs.getNearest(BANKER_ID).isOnScreen() || bank.isOpen()) {
  302.                 // Check path type change
  303.                 if (!pathTypeCurrent.equals(Options.pathType)) {
  304.                     pathTypeCurrent = Options.pathType;
  305.                     // Set up path:
  306.                     if (Options.pathType.equals(Path.Danger))
  307.                         alterMeshPath = new MeshPath(DANGER_MESH);
  308.                     else
  309.                         alterMeshPath = new MeshPath(SAFE_MESH);
  310.                     bankMeshPath = alterMeshPath.reverse();
  311.                 }
  312.                 if (!bank.isOpen() && walking.getEnergy() < 10
  313.                         && Options.chargeEnergy)
  314.                     return State.CHARGE_ENERGY;
  315.                 // Pouch Repair
  316.                 if (shouldRepairPouches())
  317.                     return State.NPC_CONTACT;
  318.                 if ((combat.getLifePoints() + 200 < skills
  319.                         .getCurrentLevel(Skills.CONSTITUTION) * 10)
  320.                         || combat.getHealth() < 50)
  321.                     return State.EAT_FOOD;
  322.                 if (inventory.getCount(Pouch.ALL)
  323.                         + inventory.getCount(PURE_ESS_ID)
  324.                         + inventory.getCount(FIRE_RUNE_ID) != inventory
  325.                         .getCount())
  326.                     return State.BANK_DEPOSIT;
  327.  
  328.                 if (inventory.contains(PURE_ESS_ID)
  329.                         && pouches.containsValue(PouchState.Empty))
  330.                     return State.FILL_POUCHES;
  331.  
  332.                 if (!inventory.isFull())
  333.                     return State.BANK_WITHDRAW;
  334.             } else if (!inventory.contains(PURE_ESS_ID))
  335.                 return State.NEAR_BANK;
  336.  
  337.         }
  338.  
  339.         if (inventory.contains(PURE_ESS_ID))
  340.             return State.TRAVERSE_ALTER;
  341.         else {
  342.             if (Options.tryOuraniaTeleport
  343.                     && skills.getCurrentLevel(Skills.MAGIC) >= 71
  344.                     && inventory.contains(LAW_RUNE)
  345.                     && inventory.getCount(true, ASTRAL_RUNE) >= 2
  346.                     && (inventory.getCount(true, EARTH_RUNE) >= 6 || Options.hasEarthStaff))
  347.                 return State.OURANIA_TELEPORT;
  348.             if (objects.getNearest(LADDER_ID) != null)
  349.                 return State.TRAVERSE_OURANIA;
  350.             return State.TRAVERSE_BANK;
  351.         }
  352.         // return State.LOST;
  353.     }
  354.  
  355.     /**
  356.      * Internal use only. When the client receives a message this method is
  357.      * invoked, saving the message for later use.
  358.      */
  359.     public void messageReceived(MessageEvent msg) {
  360.         if (msg.getID() == MessageEvent.MESSAGE_ACTION
  361.                 || msg.getID() == MessageEvent.MESSAGE_SERVER) {
  362.             serverMsg = msg.getMessage().toLowerCase();
  363.         }
  364.     }
  365.  
  366.     /**
  367.      * Determines if pouches need repairing by using the method defined in the
  368.      * Options class
  369.      *
  370.      * @return boolean should repair pouches
  371.      */
  372.     public boolean shouldRepairPouches() {
  373.         int degradeable = inventory.getCount(Pouch.ALL_DEGRADEABLE);
  374.         int degraded = inventory.getCount(Pouch.ALL_DEGRADED);
  375.         switch (Options.pouchRepair) {
  376.         case ONE:
  377.             return degraded > 0;
  378.         case TWO:
  379.             return degraded > 1 || degradeable < 2;
  380.         case ALL:
  381.             return degradeable == degraded;
  382.         case NEVER:
  383.             return false;
  384.         default:
  385.             return degraded > 1 || degradeable < 2;
  386.         }
  387.     }
  388.  
  389.     /**
  390.      * Tries to open the bank
  391.      *
  392.      * @return boolean did bank open
  393.      */
  394.     public boolean openBank() {
  395.         if (!alterMeshPath.hasPath())
  396.             alterMeshPath.findPath();
  397.         if (bankMeshPath.hasPath())
  398.             bankMeshPath.clearPath();
  399.  
  400.         for (byte b = 0; b < 10; b++) {
  401.             if (interfaces.get(PAY_BANK_INTERFACE) != null
  402.                     && interfaces.get(PAY_BANK_INTERFACE).isValid())
  403.                 payBankCharge();
  404.             if (npcs.getNearest(BANKER_ID) != null) {
  405.                 if (bank.isOpen())
  406.                     return true;
  407.                 npcs.getNearest(BANKER_ID).doAction("Bank");
  408.                 sleep(100, 250);
  409.             }
  410.             sleep(1000);
  411.         }
  412.         return false;
  413.     }
  414.  
  415.     public void payBankCharge() {
  416.         RSInterface pay = interfaces.get(PAY_BANK_INTERFACE);
  417.         if (pay != null && pay.isValid())
  418.             for (String rune : PAY_BANKER_ORDER)
  419.                 for (RSComponent c : pay.getComponents())
  420.                     if (c != null
  421.                             && c.isValid()
  422.                             && c.getTooltip() != null
  423.                             && c.getTooltip().toLowerCase()
  424.                                     .contains(rune.toLowerCase()))
  425.                         if (inventoryCount(rune) >= 20)
  426.                             if (c.doClick()) {
  427.                                 sleep(1500);
  428.                                 return;
  429.                             }
  430.         log.severe("Unable to pay banker!");
  431.     }
  432.  
  433.     public int inventoryCount(String name) {
  434.         for (RSItem i : inventory.getCachedItems())
  435.             if (i != null
  436.                     && i.getName().toLowerCase().contains(name.toLowerCase()))
  437.                 return i.getStackSize();
  438.         return 0;
  439.     }
  440.  
  441.     /**
  442.      * Deposit all non necessary items.
  443.      */
  444.     public void deposit() {
  445.         if (!openBank())
  446.             return;
  447.  
  448.         int[] items = mergeArrays(Pouch.ALL, FIRE_RUNE_ID);
  449.         for (int tries = 0; tries < 5 && inventory.getCountExcept(items) > 0; tries++)
  450.             outer: for (RSItem item : inventory.getItems()) {
  451.                 if (item != null) {
  452.                     for (int id : items)
  453.                         if (item.getID() == id)
  454.                             continue outer;
  455.                     if (item.getID() > 0 && inventory.contains(item.getID()))
  456.                         if (bank.deposit(item.getID(), 0))
  457.                             sleep(random(200, 300));
  458.  
  459.                 }
  460.             }
  461.         Paint.runsMade++;
  462.     }
  463.  
  464.     /**
  465.      * Tries to open the bank if the bank is not open, then tries to withdraw
  466.      * the item
  467.      *
  468.      * @param id
  469.      *            the item to withdraw
  470.      * @param amount
  471.      *            the amount to withdraw, 0 for all
  472.      * @return boolean successfully withdraw
  473.      */
  474.     public boolean withdrawItem(int id, int amount) {
  475.         if (!openBank())
  476.             return false;
  477.         boolean returns = bank.withdraw(id, amount);
  478.         sleep(250, 500);
  479.         return returns;
  480.     }
  481.  
  482.     /**
  483.      * Withdraws pure essence
  484.      *
  485.      * @return boolean did withdraw item
  486.      */
  487.     public boolean withdrawEss() {
  488.         return withdrawItem(PURE_ESS_ID, 0);
  489.     }
  490.  
  491.     /**
  492.      * Gets the count of items by the provided id(s) in both the bank and
  493.      * inventory. Includes stack sizes
  494.      *
  495.      * @param id
  496.      *            item ids to search
  497.      * @return int number of items in bank and inventory
  498.      */
  499.     public int getCountBankInv(int... id) {
  500.         int count = 0;
  501.         if (bank.isOpen())
  502.             count += bank.getCount(id);
  503.         count += inventory.getCount(true, id);
  504.         return count;
  505.     }
  506.  
  507.     /**
  508.      * Emptys pouches using the method set in the Options class
  509.      */
  510.     public void emptyPouches() {
  511.         if (game.getCurrentTab() == Game.TAB_INVENTORY)
  512.             updatePouches();
  513.         for (PouchType pType : pouches.keySet()) {
  514.             if (inventory.isFull())
  515.                 break;
  516.             if (inventory.containsOneOf(pType.ids)) {
  517.                 if (pouches.get(pType).equals(PouchState.Full)) {
  518.                     RSItem item = inventory.getItem(pType.ids);
  519.                     if (item != null) {
  520.                         int invCount = inventory.getCount();
  521.                         if (item.doAction("Empty"))
  522.                             switch (Options.pouchCheckSystem) {
  523.                             case SERVER_MSG:
  524.                                 for (int i = 0; i < POUCH_TRIES; i++) {
  525.                                     if (serverMsg != null)
  526.                                         if (serverMsg
  527.                                                 .contains("pouch has no ess")
  528.                                                 || serverMsg
  529.                                                         .contains("are no essences in your pouch")) {
  530.                                             pouches.put(pType, PouchState.Empty);
  531.                                             serverMsg = "";
  532.                                             sleep(100, 150);
  533.                                             break;
  534.                                         }
  535.                                     sleep(100, 150);
  536.                                 }
  537.                                 break;
  538.                             case INVENTORY_STATUS:
  539.                                 for (int i = 1; i <= POUCH_TRIES; i++)
  540.                                     if (inventory.getCount() == invCount
  541.                                             && i != POUCH_TRIES)
  542.                                         sleep(100, 150);
  543.                                     else {
  544.                                         if (!inventory.isFull())
  545.                                             pouches.put(pType, PouchState.Empty);
  546.                                         break;
  547.                                     }
  548.  
  549.                                 break;
  550.                             }
  551.                     }
  552.                 }
  553.             }
  554.         }
  555.     }
  556.  
  557.     /**
  558.      * Fills pouches using the method set in the Options class
  559.      */
  560.     public void fillPouches() {
  561.         if (game.getCurrentTab() == Game.TAB_INVENTORY)
  562.             updatePouches();
  563.         for (PouchType pType : pouches.keySet()) {
  564.             if (!inventory.contains(PURE_ESS_ID))
  565.                 break;
  566.             if (inventory.containsOneOf(pType.ids)) {
  567.                 if (pouches.get(pType).equals(PouchState.Empty)) {
  568.                     RSItem item = inventory.getItem(pType.ids);
  569.                     if (item != null) {
  570.                         int invCount = inventory.getCount();
  571.                         if (item.doAction("Fill"))
  572.                             switch (Options.pouchCheckSystem) {
  573.                             case SERVER_MSG:
  574.                                 for (int i = 0; i < POUCH_TRIES; i++) {
  575.                                     if (serverMsg != null)
  576.                                         if (serverMsg.contains("pouch is ful")) {
  577.                                             pouches.put(pType, PouchState.Full);
  578.                                             serverMsg = "";
  579.                                             break;
  580.                                         }
  581.                                     sleep(80, 120);
  582.                                 }
  583.                                 break;
  584.                             case INVENTORY_STATUS:
  585.                                 for (int i = 1; i <= POUCH_TRIES; i++)
  586.                                     if (inventory.getCount() == invCount
  587.                                             && i != POUCH_TRIES)
  588.                                         sleep(100, 150);
  589.                                     else {
  590.                                         if (inventory.contains(PURE_ESS_ID))
  591.                                             pouches.put(pType, PouchState.Full);
  592.                                         break;
  593.                                     }
  594.  
  595.                                 break;
  596.                             }
  597.                     }
  598.                 }
  599.             }
  600.         }
  601.     }
  602.  
  603.     /**
  604.      * Rechecks pouches with the inventory's current contents. If a pouch in the
  605.      * list no longer exists it removes it, if there's a pouch in the inventory
  606.      * not in the list it adds it as an empty pouch
  607.      */
  608.     public void updatePouches() {
  609.         for (PouchType pouch : PouchType.values())
  610.             if (inventory.containsOneOf(pouch.ids)) {
  611.                 if (!pouches.containsKey(pouch))
  612.                     pouches.put(pouch, PouchState.Empty);
  613.             } else if (pouches.containsKey(pouch))
  614.                 pouches.remove(pouch);
  615.     }
  616.  
  617.     public int getRuneCount(boolean cache) {
  618.         int count = 0;
  619.         for (RSItem i : cache ? inventory.getCachedItems() : inventory
  620.                 .getItems())
  621.             if (i.getName() != null
  622.                     && i.getName().toLowerCase().contains("rune"))
  623.                 count += i.getStackSize();
  624.         return count;
  625.     }
  626.  
  627.     /**
  628.      * Crafts essence at the alter
  629.      */
  630.     public void craftEssence() {
  631.         if (alterMeshPath.hasPath())
  632.             alterMeshPath.clearPath();
  633.         if (!bankMeshPath.hasPath())
  634.             bankMeshPath.findPath();
  635.  
  636.         RSObject alter = objects.getNearest(ZMI_ALTAR_ID);
  637.         if (!alter.isOnScreen() && !getMyPlayer().isMoving()
  638.                 && walking.getDestination() == null)
  639.             walking.walkTileMM(alter.getArea().getNearestTile(
  640.                     getMyPlayer().getLocation()));
  641.  
  642.         int invBefore = inventory.getCount(PURE_ESS_ID);
  643.         int runesBefore = getRuneCount(false);
  644.         alter.doAction("Craft-rune");
  645.         sleep(1000);
  646.         for (byte b = 0; b < 50; b++)
  647.             if (getMyPlayer().getAnimation() == CRAFT_RUNE_ANIM
  648.                     || getMyPlayer().isMoving()
  649.                     || walking.getDestination() != null)
  650.                 sleep(100, 125);
  651.             else
  652.                 break;
  653.         Paint.essUsed += invBefore - inventory.getCount(PURE_ESS_ID);
  654.         Paint.runesMade += getRuneCount(false) - runesBefore;
  655.     }
  656.  
  657.     /**
  658.      * Turns the camera to the given angle, clicking the compass if its easier.
  659.      * Gets within param variance of the specified angle
  660.      *
  661.      * @param angle
  662.      *            as int
  663.      * @param variance
  664.      *            as int
  665.      */
  666.     public void turnToAngle(int angle, int variance) {
  667.         int r1 = angle - variance;
  668.         if (r1 < 0)
  669.             r1 += 360;
  670.         int r2 = angle + variance;
  671.         if (r2 >= 360)
  672.             r2 -= 360;
  673.         int currAngle = camera.getAngle();
  674.         if (Math.abs(Math.max(r1, r2) - Math.min(r1, r2)) == variance * 2) {
  675.             if (currAngle > Math.min(r1, r2) && currAngle < Math.max(r1, r2))
  676.                 return;
  677.         } else if (currAngle < Math.min(r1, r2) && currAngle > Math.max(r1, r2))
  678.             return;
  679.         if (angle > 360 - variance || angle < variance)
  680.             camera.setNorth();
  681.         else
  682.             camera.setAngle(angle + random(-variance, variance));
  683.     }
  684.  
  685.     public boolean onStart() {
  686.         if (game.getCurrentTab() != Game.TAB_MAGIC)
  687.             game.openTab(Game.TAB_MAGIC);
  688.         Options.onLunars = magic.getInterface() != null
  689.                 && magic.getInterface().getIndex() == Magic.Book.LUNAR
  690.                         .getInterfaceID();
  691.         Options.tryOuraniaTeleport = (skills.getCurrentLevel(Skills.MAGIC) >= 71 && Options.onLunars);
  692.  
  693.         pathTypeCurrent = Options.pathType;
  694.         // Set up path:
  695.         if (Options.pathType.equals(Path.Danger))
  696.             alterMeshPath = new MeshPath(DANGER_MESH);
  697.         else
  698.             alterMeshPath = new MeshPath(SAFE_MESH);
  699.         bankMeshPath = alterMeshPath.reverse();
  700.  
  701.         // Set up the pouches:
  702.         updatePouches();
  703.  
  704.         RSItem wep = equipment.getItem(Equipment.WEAPON);
  705.         if (wep != null) {
  706.             Options.hasAirStaff = (wep.getName().toLowerCase()
  707.                     .contains("staff") && wep.getName().toLowerCase()
  708.                     .contains("air"));
  709.             Options.hasEarthStaff = (wep.getName().toLowerCase()
  710.                     .contains("staff") && (wep.getName().toLowerCase()
  711.                     .contains("earth")
  712.                     || wep.getName().toLowerCase().contains("mud") || wep
  713.                     .getName().toLowerCase().contains("lava")));
  714.         }
  715.  
  716.         Paint = new PaintInterface(this, log, Skills.RUNECRAFTING);
  717.         new DataLoader(this);
  718.  
  719.         // optimal alt
  720.         camera.setPitch(random(0, 10));
  721.  
  722.         log.info("Script initiation successful!");
  723.         log.info("Current exp: "
  724.                 + PaintFormatter.getFormattedMoney(Paint.startExp));
  725.         log.info("Current level: " + Paint.startLevel);
  726.  
  727.         log.info("--Setup--");
  728.         log.info("Air staff: " + (Options.hasAirStaff ? "yes" : "no"));
  729.         log.info("Earth staff: " + (Options.hasEarthStaff ? "yes" : "no"));
  730.         log.info("Walking range: " + Options.walkingDistance.toString());
  731.         log.info("Pouch method: "
  732.                 + PaintFormatter.properCaseAll(Options.pouchCheckSystem.name()
  733.                         .replaceAll("_", " ")));
  734.         log.info("Pouch repair system: "
  735.                 + PaintFormatter.properCaseAll(Options.pouchRepair.name()
  736.                         .replaceAll("_", " ")));
  737.         log.info("Walking style: "
  738.                 + PaintFormatter.properCaseAll(Options.pathType.name()));
  739.         log.info("Recharge Energy: "
  740.                 + (Options.chargeEnergy ? "True" : "False"));
  741.         log.info("Try Ourania Teleport: "
  742.                 + (Options.tryOuraniaTeleport ? "True" : "False"));
  743.  
  744.         return true;
  745.     }
  746.  
  747.     public boolean isResting() {
  748.         int anim = getMyPlayer().getAnimation();
  749.         return (anim == 12108 || anim == 2033 || anim == 2716 || anim == 11786 || anim == 5713);
  750.     }
  751.  
  752.     public void rest() {
  753.         for (int d = 0; d < 5; d++) {
  754.             interfaces.getComponent(walking.INTERFACE_RUN_ORB, 1).doAction(
  755.                     "Rest");
  756.             mouse.moveSlightly();
  757.             sleep(random(400, 600));
  758.             int anim = players.getMyPlayer().getAnimation();
  759.             if (anim == 12108 || anim == 2033 || anim == 2716 || anim == 11786
  760.                     || anim == 5713) {
  761.                 break;
  762.             }
  763.         }
  764.     }
  765.  
  766.     public int loop() {
  767.         mouse.setSpeed(random(4, 6));
  768.         if (!walking.isRunEnabled() && walking.getEnergy() > 50 && !isResting())
  769.             walking.setRun(true);
  770.         try {
  771.             currentState = getState();
  772.             switch (currentState) {
  773.             case LADDER_DOWN:
  774.                 if (objects.getNearest(LADDER_ID).isOnScreen())
  775.                     if (objects.getNearest(LADDER_ID).doAction("Climb"))
  776.                         sleep(500, 750);
  777.                 break;
  778.             case OURANIA_TELEPORT:
  779.                 if (magic.castSpell(Magic.SPELL_OURANIA_TELEPORT))
  780.                     sleep(2500,2750);
  781.                 break;
  782.             case TRAVERSE_OURANIA:
  783.                 RSTilePath tPath = walking.newTilePath(OURANIA_PATH).randomize(
  784.                         2, 2);
  785.                 for (byte b = 0; b < 300 && tPath.isValid()
  786.                         && (objects.getNearest(LADDER_ID) != null) ? !objects
  787.                         .getNearest(LADDER_ID).isOnScreen() : true; b++) {
  788.                     tPath.traverse();
  789.                     sleep(95, 105);
  790.                 }
  791.                 break;
  792.             case CHARGE_ENERGY:
  793.                 if (!isResting())
  794.                     rest();
  795.                 if (isResting())
  796.                     while (walking.getEnergy() < 100 && isResting()
  797.                             && isActive())
  798.                         sleep(200);
  799.                 break;
  800.             case NEAR_BANK:
  801.                 RSNPC banker = npcs.getNearest(BANKER_ID);
  802.                 if (banker != null)
  803.                     if (!banker.isOnScreen()) {
  804.                         RSTile loc = banker.getLocation();
  805.                         if (calc.tileOnMap(loc)) {
  806.                             walking.walkTileMM(loc);
  807.                             for (byte b = 0; b < 22; b++)
  808.                                 if (banker.isOnScreen()) {
  809.                                     sleep(500);
  810.                                     break;
  811.                                 } else
  812.                                     sleep(100, 115);
  813.                         }
  814.                     }
  815.                 break;
  816.             case BANK_DEPOSIT:
  817.                 deposit();
  818.                 break;
  819.             case BANK_WITHDRAW:
  820.                 withdrawEss();
  821.                 break;
  822.             case CRAFT_RUNES:
  823.                 craftEssence();
  824.                 break;
  825.             case EMPTY_POUCHES:
  826.                 emptyPouches();
  827.                 break;
  828.             case EAT_FOOD:
  829.                 if (!inventory.containsOneOf(food)) {
  830.                     openBank();
  831.                     for (int id : food)
  832.                         if (bank.getCount(id) > 0)
  833.                             if (bank.withdraw(id, 1))
  834.                                 break;
  835.                 }
  836.                 if (bank.isOpen())
  837.                     bank.close();
  838.                 if (inventory.containsOneOf(food)) {
  839.                     RSItem foodI = inventory.getItem(food);
  840.                     if (foodI != null)
  841.                         foodI.doAction("Eat");
  842.                 }
  843.                 break;
  844.             case FILL_POUCHES:
  845.                 fillPouches();
  846.                 break;
  847.             case NPC_CONTACT:
  848.                 npcContact.main();
  849.                 break;
  850.             case TRAVERSE_BANK:
  851.                 if (!bankMeshPath.hasPath())
  852.                     bankMeshPath.findPath(MeshPathType.FULL);
  853.                 if (Options.pathType.equals(Path.Safe)
  854.                         && (bankMeshPath.getNearestByPath() == null || !bankMeshPath
  855.                                 .getNearestByPath().equals(
  856.                                         bankMeshPath.getEnd())))
  857.                     if (objects.getNearest(CRACK_OUT) != null) {
  858.                         if (!objects.getNearest(CRACK_OUT).isOnScreen()) {
  859.                             walking.walkTileMM(objects.getNearest(CRACK_OUT)
  860.                                     .getLocation());
  861.                             sleep(1250);
  862.                         }
  863.                         if (objects.getNearest(CRACK_OUT).isOnScreen()
  864.                                 && objects.getNearest(CRACK_OUT).doAction(
  865.                                         "Squeeze-through")) {
  866.                             sleep(900, 1250);
  867.                             break;
  868.                         }
  869.                     }
  870.                 bankMeshPath.traverse();
  871.                 break;
  872.             case TRAVERSE_ALTER:
  873.                 if (!alterMeshPath.hasPath())
  874.                     alterMeshPath.findPath(MeshPathType.ALL_CENTER);
  875.                 if ((calc.pathLengthTo(alterDet, true) > 25 || calc
  876.                         .pathLengthTo(alterDet, true) < 0)
  877.                         && Options.pathType.equals(Path.Safe))
  878.                     if (objects.getNearest(CRACK_IN) != null)
  879.                         if (objects.getNearest(CRACK_IN).isOnScreen()
  880.                                 && objects.getNearest(CRACK_IN).doAction(
  881.                                         "Squeeze-through")) {
  882.                             sleep(900, 1250);
  883.                             break;
  884.                         }
  885.                 alterMeshPath.traverse();
  886.                 break;
  887.             }
  888.         } catch (Exception e) {
  889.             logError(e);
  890.         }
  891.         if (walking.getEnergy() > 50 && !walking.isRunEnabled())
  892.             walking.setRun(true);
  893.         return random(100, 125);
  894.     }
  895.  
  896.     public void onFinish() {
  897.         env.saveScreenshot(true);
  898.         Paint.paintMenu.saveProggie();
  899.     }
  900.  
  901.     /**
  902.      * Logs an exception to the Logger and to the error cache
  903.      *
  904.      * @param e
  905.      *            as the Exception to log
  906.      */
  907.     public void logError(Exception e) {
  908.         String s = e.getMessage();
  909.         log.severe(s);
  910.         for (StackTraceElement ele : e.getStackTrace()) {
  911.             s += "\n" + ele.toString();
  912.             log.severe(ele.toString());
  913.         }
  914.         errorLog.add(s);
  915.     }
  916.  
  917.     /**
  918.      * {@inheritDoc}
  919.      */
  920.     public void onRepaint(Graphics g1) {
  921.         Graphics2D g = (Graphics2D) g1;
  922.         try {
  923.             // MeshPaths
  924.             bankMeshPath.paintAll(g);
  925.             alterMeshPath.paintAll(g);
  926.  
  927.             // Mouse Crosshair
  928.             drawMouse.onRepaint(g1);
  929.  
  930.             // main paint:
  931.             if (Paint != null)
  932.                 Paint.render(g);
  933.  
  934.         } catch (NullPointerException e) {
  935.             logError(e);
  936.         }
  937.     }
  938.  
  939.     public void mouseDragged(MouseEvent arg0) {
  940.         Paint.mouseLoc = arg0.getPoint();
  941.     }
  942.  
  943.     public void mouseMoved(MouseEvent arg0) {
  944.         Paint.mouseLoc = arg0.getPoint();
  945.     }
  946.  
  947.     public void mouseClicked(MouseEvent arg0) {
  948.         Paint.paintMenu.fireMouseEvent(arg0);
  949.     }
  950.  
  951.     public void mouseEntered(MouseEvent arg0) {
  952.  
  953.     }
  954.  
  955.     public void mouseExited(MouseEvent arg0) {
  956.  
  957.     }
  958.  
  959.     public void mousePressed(MouseEvent arg0) {
  960.  
  961.     }
  962.  
  963.     public void mouseReleased(MouseEvent arg0) {
  964.     }
  965.  
  966.     /**
  967.      * Used to format data into more conventional formats
  968.      *
  969.      * @author Equinox_
  970.      *
  971.      */
  972.     public static class PaintFormatter {
  973.         /**
  974.          * Converts the first letter of every word to uppercase, and the rest
  975.          * lowercase.
  976.          *
  977.          * @param s
  978.          *            The input String
  979.          * @return the properly formatted string
  980.          */
  981.         public static String properCaseAll(String s) {
  982.             String[] words = s.split(" ");
  983.             String result = "";
  984.             for (int i = 0; i < words.length; i++) {
  985.                 String f = words[i].substring(0, 1).toUpperCase();
  986.                 words[i] = f + words[i].substring(1).toLowerCase();
  987.                 result += " " + words[i];
  988.             }
  989.             return result.substring(1);
  990.         }
  991.  
  992.         /**
  993.          * Capitalizes the first letter of every sentence, with every other
  994.          * letter lowercase.
  995.          *
  996.          * @param s
  997.          *            The input string
  998.          * @return The properly formatted String
  999.          */
  1000.         public static String properCaseFirst(String s) {
  1001.             String[] sentences = new String[] { s };
  1002.             if (s.contains("."))
  1003.                 sentences = s.split(".");
  1004.             String result = "";
  1005.             for (int i = 0; i < sentences.length; i++) {
  1006.                 String f = sentences[i].substring(0, 1).toUpperCase();
  1007.                 sentences[i] = f + sentences[i].substring(1).toLowerCase();
  1008.                 result += " " + sentences[i];
  1009.             }
  1010.             return result.substring(1);
  1011.         }
  1012.  
  1013.         /**
  1014.          * Formats time to DD:HH:MM:SS:MSMS with days and millis optional
  1015.          *
  1016.          * @param timeMillis
  1017.          *            long milliseconds of time
  1018.          * @param sep
  1019.          *            String seperation character
  1020.          * @param millisInc
  1021.          *            boolean include milliseconds
  1022.          * @return
  1023.          */
  1024.         public static String getFormattedTime(final long timeMillis,
  1025.                 String sep, boolean millisInc) {
  1026.             long millis = timeMillis;
  1027.             final long days = millis / (24 * 1000 * 60 * 60);
  1028.             millis -= days * (24 * 1000 * 60 * 60);
  1029.             final long hours = millis / (1000 * 60 * 60);
  1030.             millis -= hours * 1000 * 60 * 60;
  1031.             final long minutes = millis / (1000 * 60);
  1032.             millis -= minutes * 1000 * 60;
  1033.             final long _seconds = millis / 1000;
  1034.             millis -= _seconds * 1000;
  1035.             String dayString = String.valueOf(days);
  1036.             String hoursString = String.valueOf(hours);
  1037.             String minutesString = String.valueOf(minutes);
  1038.             String secondsString = String.valueOf(_seconds);
  1039.             if (hours < 10) {
  1040.                 hoursString = 0 + hoursString;
  1041.             }
  1042.             if (minutes < 10) {
  1043.                 minutesString = 0 + minutesString;
  1044.             }
  1045.             if (_seconds < 10) {
  1046.                 secondsString = 0 + secondsString;
  1047.             }
  1048.             String s = "";
  1049.             if (days > 0)
  1050.                 s = dayString + sep;
  1051.             s += hoursString + sep + minutesString + sep + secondsString;
  1052.             if (millisInc)
  1053.                 s += sep + String.valueOf(millis);
  1054.             return s;
  1055.         }
  1056.  
  1057.         /**
  1058.          * Converts the milliseconds to DD:HH:MM:SS with the days optional.
  1059.          *
  1060.          * @param timeMillis
  1061.          *            the number of milliseconds
  1062.          * @return the properly formatted time as a String
  1063.          */
  1064.         public static String getFormattedTime(final long timeMillis) {
  1065.             return getFormattedTime(timeMillis, ":", false);
  1066.         }
  1067.  
  1068.         /**
  1069.          * Formats a specified amount into thousands, millions or billions, then
  1070.          * adds the proper suffix
  1071.          *
  1072.          * @param money
  1073.          *            the amount of money
  1074.          * @return the formatted string
  1075.          */
  1076.         public static String getFormattedMoney(long money) {
  1077.             /*
  1078.              * String suffix = ""; int t = 1; if (money >= 10000) { t *= 1000;
  1079.              * suffix = "K"; } if (money / t > 10000) { t *= 1000; suffix = "M";
  1080.              * } if (money / t > 10000) { t *= 1000; suffix = "B"; } return
  1081.              * String.valueOf((money / t)) + suffix;
  1082.              */
  1083.             return new DecimalFormat("###,###,###,###,###,###").format(money);
  1084.         }
  1085.  
  1086.         /**
  1087.          * Converts an array of strings into one string, using commas and 'and' <br>
  1088.          * <br>
  1089.          * Examples: <br>
  1090.          * {"option1"} returns "option1"<br>
  1091.          * {"option1","option2"} returns "option1 and option2"<br>
  1092.          * {"op1","op2","op3"} returns "op1, op2, and op3"<br>
  1093.          * <br>
  1094.          *
  1095.          * @param list
  1096.          *            The array of strings making up the list.
  1097.          * @return The properly formatted string
  1098.          */
  1099.         public static String formatList(String[] list) {
  1100.             String s = "";
  1101.             if (list != null)
  1102.                 if (list.length == 2) {
  1103.                     s = list[0] + " and " + list[1];
  1104.                 } else if (list.length > 2) {
  1105.                     for (int i = 0; i < list.length - 1; i++)
  1106.                         s += list[i] + ", ";
  1107.                     s += "and " + list[list.length - 1];
  1108.                 } else if (list.length == 1)
  1109.                     s = list[0];
  1110.             return s;
  1111.         }
  1112.     }
  1113.  
  1114.     /**
  1115.      * A class used to represent the paint interface for this script
  1116.      *
  1117.      * @author Equinox_
  1118.      *
  1119.      */
  1120.     public class PaintInterface {
  1121.         // Constant
  1122.         public final int[] SKILL_INTERFACES = { Skills.INTERFACE_ATTACK,
  1123.                 Skills.INTERFACE_DEFENSE, Skills.INTERFACE_STRENGTH,
  1124.                 Skills.INTERFACE_CONSTITUTION, Skills.INTERFACE_RANGE,
  1125.                 Skills.INTERFACE_PRAYER, Skills.INTERFACE_MAGIC,
  1126.                 Skills.INTERFACE_COOKING, Skills.INTERFACE_WOODCUTTING,
  1127.                 Skills.INTERFACE_FLETCHING, Skills.INTERFACE_FISHING,
  1128.                 Skills.INTERFACE_FIREMAKING, Skills.INTERFACE_CRAFTING,
  1129.                 Skills.INTERFACE_SMITHING, Skills.INTERFACE_MINING,
  1130.                 Skills.INTERFACE_HERBLORE, Skills.INTERFACE_AGILITY,
  1131.                 Skills.INTERFACE_THIEVING, Skills.INTERFACE_SLAYER,
  1132.                 Skills.INTERFACE_FARMING, Skills.INTERFACE_RUNECRAFTING,
  1133.                 Skills.INTERFACE_HUNTER, Skills.INTERFACE_CONSTRUCTION,
  1134.                 Skills.INTERFACE_SUMMONING, Skills.INTERFACE_DUNGEONEERING };
  1135.  
  1136.         // Start data
  1137.         public long startExp = 0;
  1138.         public long startTime = 0;
  1139.         public long startLevel = 1;
  1140.  
  1141.         // Goal data
  1142.         public int goalExp;
  1143.         public int goalLevel;
  1144.  
  1145.         // Usage data
  1146.         public int runesMade = 0;
  1147.         public int essUsed = 0;
  1148.  
  1149.         // Price data
  1150.         public long essPrice = 1;
  1151.  
  1152.         // Result data
  1153.         public int runsMade = 0;
  1154.  
  1155.         // Images
  1156.         public Map<String, Image> icons = new HashMap<String, Image>();
  1157.  
  1158.         public void putImage(String name, Image img) {
  1159.             icons.put(name.toLowerCase(), img);
  1160.         }
  1161.  
  1162.         public Image getImage(String name) {
  1163.             if (icons.containsKey(name.toLowerCase()))
  1164.                 return icons.get(name.toLowerCase());
  1165.             return null;
  1166.         }
  1167.  
  1168.         // End Images
  1169.  
  1170.         public FontRenderContext frc = new FontRenderContext(
  1171.                 new AffineTransform(), false, false);
  1172.  
  1173.         public PaintStyle style = new PaintStyle();
  1174.         public Point mouseLoc = new Point(0, 0);
  1175.  
  1176.         final EquinoxZMICrafter crafter;
  1177.         final Logger log;
  1178.         int SkillID = -1;
  1179.         public Menu paintMenu;
  1180.  
  1181.         /**
  1182.          * Constructs a PaintInterface using the provided parent and logger
  1183.          * object
  1184.          *
  1185.          * @param planker
  1186.          *            the parent NatCrafter
  1187.          * @param log
  1188.          *            the Logger object
  1189.          */
  1190.         public PaintInterface(EquinoxZMICrafter crafter, Logger log) {
  1191.             this(crafter, log, -1);
  1192.         }
  1193.  
  1194.         public PaintInterface(EquinoxZMICrafter crafter, Logger log, int SkillID) {
  1195.             this.crafter = crafter;
  1196.             this.log = log;
  1197.             this.SkillID = SkillID;
  1198.             startTime = System.currentTimeMillis();
  1199.             if (SkillID >= 0 && SkillID < Skills.SKILL_NAMES.length) {
  1200.                 startExp = skills.getCurrentExp(SkillID);
  1201.                 startLevel = skills.getCurrentLevel(SkillID);
  1202.                 setUpGoal();
  1203.             }
  1204.             init();
  1205.         }
  1206.  
  1207.         public void init() {
  1208.             RSComponent screen = interfaces.get(548).getComponent(3);
  1209.             Point bottomRight = new Point(screen.getAbsoluteX()
  1210.                     + screen.getWidth(), screen.getAbsoluteY()
  1211.                     + screen.getHeight());
  1212.             paintMenu = new Menu(this, bottomRight, Anchor.DOWN_RIGHT);
  1213.             paintMenu.setFlowType(FlowType.HORIZONTAL);
  1214.             paintMenu.addMouseListener(new MouseListen() {
  1215.                 void MouseAction(MouseEvent e) {
  1216.                     if (e.getID() == MouseEvent.MOUSE_CLICKED)
  1217.                         paintMenu.toggleShowAll();
  1218.                 }
  1219.             });
  1220.             if (SkillID >= 0 && SkillID < Skills.SKILL_NAMES.length) {
  1221.                 MenuProgressBar progressBar = new MenuProgressBar("nextlvl",
  1222.                         paintMenu, new GetPercent() {
  1223.                             int getPercent() {
  1224.                                 return skills.getPercentToNextLevel(SkillID);
  1225.                             }
  1226.                         }, "Next Level", new GetPopup() {
  1227.                             MenuPopUp getPopUp() {
  1228.                                 long millis = Math.abs(System
  1229.                                         .currentTimeMillis() - startTime);
  1230.                                 if (millis <= 0)
  1231.                                     millis = 1;
  1232.                                 long expGained = skills.getCurrentExp(SkillID)
  1233.                                         - startExp;
  1234.                                 long expPerHour = (expGained * 3600000l)
  1235.                                         / millis;
  1236.                                 String TTL = "";
  1237.                                 int TTLunf = (int) ((expPerHour > 0) ? (((float) skills
  1238.                                         .getExpToNextLevel(SkillID))
  1239.                                         / ((float) expPerHour) * 3600000f) : 0);
  1240.                                 if (TTLunf != 0)
  1241.                                     TTL = PaintFormatter
  1242.                                             .getFormattedTime(TTLunf);
  1243.                                 else
  1244.                                     TTL = "No exp gained";
  1245.                                 String[] lines = new String[4];
  1246.                                 lines[0] = "Exp gained: "
  1247.                                         + PaintFormatter
  1248.                                                 .getFormattedMoney(expGained);
  1249.                                 lines[1] = "Exp per Hour: "
  1250.                                         + PaintFormatter
  1251.                                                 .getFormattedMoney(expPerHour);
  1252.                                 lines[2] = "Exp 'till level: "
  1253.                                         + PaintFormatter.getFormattedMoney(skills
  1254.                                                 .getExpToNextLevel(SkillID));
  1255.                                 lines[3] = "Time 'till level: " + TTL;
  1256.                                 return new MenuPopUp(lines, "nextlevel");
  1257.                             }
  1258.  
  1259.                         });
  1260.                 paintMenu.add(progressBar);
  1261.  
  1262.                 if (goalExp > skills.getCurrentExp(SkillID)) {
  1263.                     MenuProgressBar goalBar = new MenuProgressBar("goal",
  1264.                             paintMenu, new GetPercent() {
  1265.                                 int getPercent() {
  1266.                                     float expTillGoalStart = goalExp - startExp;
  1267.                                     double expGained = skills
  1268.                                             .getCurrentExp(SkillID) - startExp;
  1269.                                     double result = expGained
  1270.                                             / expTillGoalStart;
  1271.                                     if (result > 1)
  1272.                                         result = 1;
  1273.                                     if (result < 0)
  1274.                                         result = 0;
  1275.                                     return (int) (result * 100);
  1276.                                 }
  1277.                             }, "Goal", new GetPopup() {
  1278.                                 MenuPopUp getPopUp() {
  1279.                                     long millis = Math.abs(System
  1280.                                             .currentTimeMillis() - startTime);
  1281.                                     if (millis <= 0)
  1282.                                         millis = 1;
  1283.                                     long expGained = skills
  1284.                                             .getCurrentExp(SkillID) - startExp;
  1285.                                     long expPerHour = (expGained * 3600000l)
  1286.                                             / millis;
  1287.                                     int expTillGoal = goalExp
  1288.                                             - skills.getCurrentExp(SkillID);
  1289.                                     String TTG = "";
  1290.                                     int TTGunf = (int) ((expPerHour > 0) ? (((float) expTillGoal)
  1291.                                             / ((float) expPerHour) * 3600000f)
  1292.                                             : 0);
  1293.                                     if (TTGunf != 0)
  1294.                                         TTG = PaintFormatter
  1295.                                                 .getFormattedTime(TTGunf);
  1296.                                     else
  1297.                                         TTG = "No exp gained";
  1298.                                     String[] lines = new String[goalLevel > 0 ? 6
  1299.                                             : 5];
  1300.                                     int off = goalLevel > 0 ? 1 : 0;
  1301.                                     if (goalLevel > 0)
  1302.                                         lines[0] = "Goal Level: " + goalLevel;
  1303.                                     lines[0 + off] = "Goal Exp: "
  1304.                                             + PaintFormatter
  1305.                                                     .getFormattedMoney(goalExp);
  1306.                                     lines[1 + off] = "Exp gained: "
  1307.                                             + PaintFormatter
  1308.                                                     .getFormattedMoney(expGained);
  1309.                                     lines[2 + off] = "Exp per Hour: "
  1310.                                             + PaintFormatter
  1311.                                                     .getFormattedMoney(expPerHour);
  1312.                                     lines[3 + off] = "Exp 'till goal: "
  1313.                                             + PaintFormatter
  1314.                                                     .getFormattedMoney(expTillGoal);
  1315.                                     lines[4 + off] = "Time 'till goal: " + TTG;
  1316.                                     return new MenuPopUp(lines, "goalbar");
  1317.                                 }
  1318.  
  1319.                             });
  1320.                     paintMenu.add(goalBar);
  1321.                 }
  1322.             }
  1323.  
  1324.             MenuButton rcing = new MenuButton("rcing", paintMenu, "rcicon",
  1325.                     new GetPopup() {
  1326.                         MenuPopUp getPopUp() {
  1327.                             long millis = System.currentTimeMillis()
  1328.                                     - crafter.Paint.startTime;
  1329.                             if (millis <= 0)
  1330.                                 millis = 1;
  1331.                             String runesPerEssence = "Runes per Ess: "
  1332.                                     + ((essUsed > 0) ? (new DecimalFormat(
  1333.                                             "#.##").format(((double) runesMade)
  1334.                                             / ((double) essUsed)))
  1335.                                             : "No essence used");
  1336.                             long runesPerHour = ((runesMade * 3600000l) / millis);
  1337.                             /*
  1338.                              * long profit = (runesMade * natPrice) - (essUsed *
  1339.                              * essPrice) - (graahksUsed * graahkPrice);
  1340.                              */
  1341.                             String[] lines = new String[7];
  1342.                             lines[0] = "Essence Used: "
  1343.                                     + PaintFormatter.getFormattedMoney(essUsed);
  1344.                             lines[1] = "Runes Crafted: "
  1345.                                     + PaintFormatter
  1346.                                             .getFormattedMoney(runesMade);
  1347.                             lines[2] = "Runes/Hour: "
  1348.                                     + PaintFormatter
  1349.                                             .getFormattedMoney(runesPerHour);
  1350.                             /*
  1351.                              * lines[4] = "Profit: " +
  1352.                              * PaintFormatter.getFormattedMoney(profit) + " gp";
  1353.                              * lines[5] = "Profit per hour: " + PaintFormatter
  1354.                              * .getFormattedMoney((profit * 3600000) / millis) +
  1355.                              * " gp";
  1356.                              */
  1357.                             lines[3] = runesPerEssence;
  1358.                             lines[4] = "Runs made: " + runsMade;
  1359.                             lines[5] = "Runes per run: "
  1360.                                     + (runsMade > 0 ? (int) (runesMade / runsMade)
  1361.                                             : "No runs made");
  1362.                             lines[6] = "Runes in inventory: "
  1363.                                     + crafter.getRuneCount(true);
  1364.                             return new MenuPopUp(lines, "runes");
  1365.                         }
  1366.                     });
  1367.             paintMenu.add(rcing);
  1368.  
  1369.             MenuButton home = new MenuButton("home", paintMenu, "homeicon",
  1370.                     new GetPopup() {
  1371.                         MenuPopUp getPopUp() {
  1372.                             long millis = System.currentTimeMillis()
  1373.                                     - crafter.Paint.startTime;
  1374.                             if (millis <= 0)
  1375.                                 millis = 1;
  1376.                             String[] lines = new String[5];
  1377.                             lines[0] = crafter.name;
  1378.                             lines[1] = "By: " + crafter.author;
  1379.                             lines[2] = "Version " + crafter.VERSION;
  1380.                             lines[3] = "Runtime: "
  1381.                                     + PaintFormatter.getFormattedTime(millis)
  1382.                                     + "\n";
  1383.                             lines[4] = PaintFormatter
  1384.                                     .properCaseAll(crafter.currentState.name()
  1385.                                             .replace("_", " "));
  1386.                             if (crafter.currentState.equals(State.NPC_CONTACT))
  1387.                                 lines[4] += " - "
  1388.                                         + PaintFormatter
  1389.                                                 .properCaseAll(npcContact.currentState
  1390.                                                         .name().replace("_",
  1391.                                                                 " "));
  1392.                             return new MenuPopUp(lines, "home");
  1393.                         }
  1394.                     });
  1395.             paintMenu.add(home);
  1396.  
  1397.             MenuButton tools = new MenuButton("tools", paintMenu, "toolsicon",
  1398.                     new GetPopup() {
  1399.                         MenuPopUp getPopUp() {
  1400.                             String[] lines = new String[5];
  1401.                             lines[0] = "Save proggie";
  1402.                             lines[1] = "Reload data";
  1403.                             if (crafter.pathTypeCurrent.equals(Path.Danger))
  1404.                                 lines[2] = "Set to safe path";
  1405.                             else
  1406.                                 lines[2] = "Set to dangerous path";
  1407.                             if (Options.chargeEnergy)
  1408.                                 lines[3] = "Do not recharge energy";
  1409.                             else
  1410.                                 lines[3] = "Recharge energy";
  1411.                             if (Options.tryOuraniaTeleport)
  1412.                                 lines[4] = "Do not try Ourania Teleport";
  1413.                             else
  1414.                                 lines[4] = "Try Ourania Teleport";
  1415.                             MenuPopUp popUp = new MenuPopUp(lines, "tools");
  1416.                             popUp.addLineListener(new LineListener() {
  1417.                                 void fireEvent(MouseEvent e, String line) {
  1418.                                     line = line.toLowerCase().trim();
  1419.                                     if (e.getID() == MouseEvent.MOUSE_CLICKED) {
  1420.                                         if (line.contains("save proggie"))
  1421.                                             paintMenu.saveProggie();
  1422.                                         else if (line.contains("reload data"))
  1423.                                             new DataLoader(crafter);
  1424.                                         else if (line.contains("safe path"))
  1425.                                             Options.pathType = Path.Safe;
  1426.                                         else if (line
  1427.                                                 .contains("dangerous path"))
  1428.                                             Options.pathType = Path.Danger;
  1429.                                         else if (line
  1430.                                                 .contains("do not recharge"))
  1431.                                             Options.chargeEnergy = false;
  1432.                                         else if (line.contains("recharge"))
  1433.                                             Options.chargeEnergy = true;
  1434.                                         else if (line
  1435.                                                 .contains("do not try ourania teleport"))
  1436.                                             Options.tryOuraniaTeleport = false;
  1437.                                         else if (line
  1438.                                                 .contains("try ourania teleport")) {
  1439.                                             if (skills
  1440.                                                     .getCurrentLevel(Skills.MAGIC) < 17) {
  1441.                                                 log.severe("You must have 71 magic to use this feature!");
  1442.                                                 return;
  1443.                                             }
  1444.                                             if (!Options.onLunars) {
  1445.                                                 log.severe("You must be on the Lunar spellbook to use this feature!");
  1446.                                                 return;
  1447.                                             }
  1448.                                             Options.tryOuraniaTeleport = true;
  1449.                                         }
  1450.                                     }
  1451.                                 }
  1452.  
  1453.                                 boolean hasAction(String line) {
  1454.                                     line = line.toLowerCase().trim();
  1455.                                     String[] lines = new String[5];
  1456.                                     lines[0] = "Save proggie";
  1457.                                     lines[1] = "Reload data";
  1458.                                     if (crafter.pathTypeCurrent
  1459.                                             .equals(Path.Danger))
  1460.                                         lines[2] = "Set to safe path";
  1461.                                     else
  1462.                                         lines[2] = "Set to dangerous path";
  1463.                                     if (Options.chargeEnergy)
  1464.                                         lines[3] = "Do not recharge energy";
  1465.                                     else
  1466.                                         lines[3] = "Recharge energy";
  1467.                                     if (Options.tryOuraniaTeleport)
  1468.                                         lines[4] = "Do not try Ourania Teleport";
  1469.                                     else
  1470.                                         lines[4] = "Try Ourania Teleport";
  1471.                                     for (String sub : lines)
  1472.                                         if (sub.equalsIgnoreCase(line))
  1473.                                             return true;
  1474.                                     return false;
  1475.                                 }
  1476.                             });
  1477.                             return popUp;
  1478.                         }
  1479.                     });
  1480.             paintMenu.add(tools);
  1481.  
  1482.             paintMenu.compile();
  1483.         }
  1484.  
  1485.         /**
  1486.          * Gets the current Goal for the setSkill, or sets all goal variables to
  1487.          * -1 if there is no goal
  1488.          */
  1489.         public void setUpGoal() {
  1490.             try {
  1491.                 for (byte b = 0; b < 10; b++)
  1492.                     if (game.getCurrentTab() != Game.TAB_STATS) {
  1493.                         game.openTab(Game.TAB_STATS);
  1494.                         sleep(1000);
  1495.                     } else
  1496.                         break;
  1497.  
  1498.                 RSInterface stats = interfaces.get(320);
  1499.                 if (stats != null && stats.isValid()) {
  1500.                     RSComponent mage = stats
  1501.                             .getComponent(SKILL_INTERFACES[SkillID]);
  1502.                     if (mage != null && mage.isValid()
  1503.                             && mage.getArea() != null) {
  1504.                         mouse.move((int) mage.getArea().getCenterX(),
  1505.                                 (int) mage.getArea().getCenterY());
  1506.                         sleep(500, 750);
  1507.                         // Now we see what the popup is doing
  1508.                         RSComponent pop = stats.getComponent(202);
  1509.                         if (pop != null && pop.isValid()) {
  1510.                             RSComponent def = pop.getComponent(5);
  1511.                             if (def != null && def.isValid()) {
  1512.                                 if (def.getText().trim()
  1513.                                         .equalsIgnoreCase("Target XP:")) {
  1514.                                     RSComponent goalXP = pop.getComponent(6);
  1515.                                     if (goalXP != null && goalXP.isValid()) {
  1516.                                         goalExp = Integer.valueOf(goalXP
  1517.                                                 .getText().replace(",", "")
  1518.                                                 .trim());
  1519.                                         goalLevel = -1;
  1520.                                     }
  1521.                                 } else {
  1522.                                     // Proceed
  1523.                                     RSComponent currExp = pop.getComponent(4);
  1524.                                     RSComponent goalRemainder = pop
  1525.                                             .getComponent(12);
  1526.                                     RSComponent goalLvl = pop.getComponent(10);
  1527.                                     if (currExp != null && currExp.isValid()
  1528.                                             && goalRemainder != null
  1529.                                             && goalRemainder.isValid()
  1530.                                             && goalLvl != null
  1531.                                             && goalLvl.isValid()) {
  1532.                                         goalExp = Integer.valueOf(currExp
  1533.                                                 .getText().replace(",", "")
  1534.                                                 .trim())
  1535.                                                 + Integer.valueOf(goalRemainder
  1536.                                                         .getText()
  1537.                                                         .replace(",", "")
  1538.                                                         .trim());
  1539.                                         goalLevel = Integer.valueOf(goalLvl
  1540.                                                 .getText().replace(",", "")
  1541.                                                 .trim());
  1542.                                     }
  1543.                                 }
  1544.                                 log.info("Target level: "
  1545.                                         + (goalLevel > 0 ? goalLevel : "None"));
  1546.                                 log.info("Target exp: "
  1547.                                         + (goalExp > 0 ? goalExp : "None"));
  1548.                                 return;
  1549.                             }
  1550.                         }
  1551.                     }
  1552.                 }
  1553.             } catch (Exception e) {
  1554.             }
  1555.             goalExp = -1;
  1556.             goalLevel = -1;
  1557.         }
  1558.  
  1559.         /**
  1560.          * Renders the current paint to the given graphics context.
  1561.          *
  1562.          * @param g
  1563.          *            as the Graphics2D object to render to
  1564.          */
  1565.         public void render(Graphics2D g) {
  1566.             paintPouches(g);
  1567.             paintMenu.render(g, mouseLoc);
  1568.             int mouseX = mouseLoc.x;
  1569.             int mouseY = mouseLoc.y;
  1570.             g.setColor(Color.RED);
  1571.             g.drawLine(mouseX - 5, mouseY - 5, mouseX + 5, mouseY + 5);
  1572.             g.drawLine(mouseX - 5, mouseY + 5, mouseX + 5, mouseY - 5);
  1573.         }
  1574.  
  1575.         /**
  1576.          * Paints the pouches loaded into the list using red for empty and green
  1577.          * for full. Only paints if inventory is visible.
  1578.          *
  1579.          * @param g
  1580.          *            as Graphics
  1581.          */
  1582.         public void paintPouches(Graphics g) {
  1583.             // Loop through pouches
  1584.             for (PouchType pouch : crafter.pouches.keySet()) {
  1585.                 if (game.getCurrentTab() == Game.TAB_INVENTORY) {
  1586.                     RSItem item = inventory.getItem(pouch.ids);
  1587.                     if (item != null) {
  1588.                         if (crafter.pouches.get(pouch).equals(PouchState.Empty))
  1589.                             g.setColor(new Color(1f, 0f, 0f, 0.5f));
  1590.                         else
  1591.                             g.setColor(new Color(0f, 1f, 0f, 0.5f));
  1592.  
  1593.                         RSComponent c = item.getComponent();
  1594.                         g.fillRect(c.getAbsoluteX(), c.getAbsoluteY(),
  1595.                                 c.getWidth(), c.getHeight());
  1596.                         // Decay:
  1597.                         for (int id : Pouch.ALL_DEGRADED)
  1598.                             if (item.getID() == id) {
  1599.                                 g.setColor(Color.WHITE);
  1600.                                 g.setFont(style.FONT.deriveFont(4));
  1601.                                 g.drawString("Degraded", c.getAbsoluteX(),
  1602.                                         c.getAbsoluteY() + c.getHeight());
  1603.                                 break;
  1604.                             }
  1605.                     }
  1606.                 }
  1607.             }
  1608.         }
  1609.  
  1610.         /**
  1611.          * Fires a MouseEvent in the paintMenu
  1612.          *
  1613.          * @param e
  1614.          *            MouseEvent
  1615.          */
  1616.         public void fireMouseEvent(MouseEvent e) {
  1617.             if (paintMenu.getBoundsFull().contains(e.getPoint()))
  1618.                 paintMenu.fireMouseEvent(e);
  1619.         }
  1620.  
  1621.         /**
  1622.          * Represents a Menu on a PaintInterface
  1623.          *
  1624.          * @author Equinox_
  1625.          *
  1626.          */
  1627.         public class Menu {
  1628.             final int padding = 1;
  1629.             Point location;
  1630.             PaintInterface parent;
  1631.             PaintStyle style;
  1632.             List<MenuItem> components = new ArrayList<MenuItem>();
  1633.             final Anchor anchor;
  1634.             boolean showAllPopups;
  1635.             MenuPopUp visiblePopup = null;
  1636.             List<MouseListen> mouseListeners;
  1637.             LineListener defaultLineListener;
  1638.             private FlowType flowType = FlowType.HORIZONTAL;
  1639.             public Dimension bounds;
  1640.  
  1641.             /**
  1642.              * Constructs a Menu using the provided PaintInterface, Location,
  1643.              * Anchor, and the GameScreen as the bounds
  1644.              *
  1645.              * @param parent
  1646.              *            PaintInterface
  1647.              * @param location
  1648.              *            Point
  1649.              * @param anchor
  1650.              *            Anchor
  1651.              */
  1652.             public Menu(PaintInterface parent, Point location, Anchor anchor) {
  1653.                 this.parent = parent;
  1654.                 this.style = parent.style;
  1655.                 this.location = location;
  1656.                 this.anchor = anchor;
  1657.  
  1658.                 // Setup dimension
  1659.                 Rectangle r = interfaces.get(548).getComponent(3).getArea();
  1660.                 bounds = new Dimension(r.width, r.height);
  1661.             }
  1662.  
  1663.             /**
  1664.              * Sets the FlowType and recompiles the menu
  1665.              *
  1666.              * @param flowType
  1667.              *            FlowType
  1668.              */
  1669.             public void setFlowType(FlowType flowType) {
  1670.                 this.flowType = flowType;
  1671.                 this.compile();
  1672.             }
  1673.  
  1674.             /**
  1675.              * Gets the FlowType
  1676.              *
  1677.              * @return FlowType
  1678.              */
  1679.             public FlowType getFlowType() {
  1680.                 return flowType;
  1681.             }
  1682.  
  1683.             /**
  1684.              * Toggles the showAllPopups state
  1685.              * */
  1686.             public void toggleShowAll() {
  1687.                 showAllPopups = !showAllPopups;
  1688.             }
  1689.  
  1690.             /**
  1691.              * Constructs a menu using the provided parent and location,
  1692.              * assuming Anchor.UP_LEFT
  1693.              *
  1694.              * @param parent
  1695.              *            PaintInterface
  1696.              * @param location
  1697.              *            Point
  1698.              */
  1699.             public Menu(PaintInterface parent, Point location) {
  1700.                 this(parent, location, Anchor.UP_LEFT);
  1701.             }
  1702.  
  1703.             /**
  1704.              * Adds the specified menu item
  1705.              *
  1706.              * @param i
  1707.              *            MenuItem
  1708.              */
  1709.             public void add(MenuItem i) {
  1710.                 i.setIndex(components.size());
  1711.                 components.add(i);
  1712.                 compile();
  1713.             }
  1714.  
  1715.             /**
  1716.              * Compiles this menu
  1717.              */
  1718.             public void compile() {
  1719.                 for (int i = 0; i < components.size(); i++)
  1720.                     components.get(i).compile();
  1721.             }
  1722.  
  1723.             /**
  1724.              * Gets position x based on the menu's anchor
  1725.              *
  1726.              * @return int
  1727.              */
  1728.             public int getAbsX() {
  1729.                 if (anchor.name().contains("LEFT"))
  1730.                     return location.x;
  1731.                 else
  1732.                     return location.x - getWidth();
  1733.             }
  1734.  
  1735.             /**
  1736.              * Gets position y based on the menu's anchor
  1737.              *
  1738.              * @return int
  1739.              */
  1740.             public int getAbsY() {
  1741.                 if (anchor.name().contains("UP"))
  1742.                     return location.y;
  1743.                 else
  1744.                     return location.y - getHeight();
  1745.             }
  1746.  
  1747.             /**
  1748.              * Gets the maximum width of this menu using the menu's FlowType
  1749.              *
  1750.              * @return int
  1751.              */
  1752.             public int getWidth() {
  1753.                 if (flowType.equals(FlowType.HORIZONTAL)) {
  1754.                     int width = padding * components.size();
  1755.                     for (MenuItem m : components)
  1756.                         if (m.bounds != null)
  1757.                             width += m.bounds.width;
  1758.                     return width + padding;
  1759.                 } else {
  1760.                     int max = Integer.MIN_VALUE;
  1761.                     for (MenuItem m : components)
  1762.                         if (m.bounds != null)
  1763.                             if (max < m.bounds.width)
  1764.                                 max = (int) m.bounds.width;
  1765.                     return max + (2 * padding);
  1766.                 }
  1767.             }
  1768.  
  1769.             /**
  1770.              * Gets the maximum height of this menu based of FlowType
  1771.              *
  1772.              * @return int
  1773.              */
  1774.             public int getHeight() {
  1775.                 if (flowType.equals(FlowType.HORIZONTAL)) {
  1776.                     int max = Integer.MIN_VALUE;
  1777.                     for (MenuItem m : components)
  1778.                         if (m.bounds != null)
  1779.                             if (max < m.bounds.height)
  1780.                                 max = (int) m.bounds.height;
  1781.                     return max + (2 * padding);
  1782.                 } else {
  1783.                     int height = padding * components.size();
  1784.                     for (MenuItem m : components)
  1785.                         if (m.bounds != null)
  1786.                             height += m.bounds.height;
  1787.                     return height + padding;
  1788.                 }
  1789.             }
  1790.  
  1791.             /**
  1792.              * Gets the current bounds of this menu, with rounded corners if
  1793.              * specified
  1794.              *
  1795.              * @param rounded
  1796.              *            boolean
  1797.              * @return RoundRectangle2D.Float
  1798.              */
  1799.             public RoundRectangle2D.Float getArea(boolean rounded) {
  1800.                 float round = rounded ? style.roundCorner : 0;
  1801.                 return new RoundRectangle2D.Float(getAbsX(), getAbsY(),
  1802.                         getWidth(), getHeight(), round, round);
  1803.             }
  1804.  
  1805.             public Rectangle getBoundsFull() {
  1806.                 RoundRectangle2D r1 = getArea(false);
  1807.                 RoundRectangle2D r2 = visiblePopup != null ? visiblePopup.bkg
  1808.                         : getArea(false);
  1809.                 int x = (int) Math.min(r1.getX(), r2.getX());
  1810.                 int y = (int) Math.min(r1.getY(), r2.getY());
  1811.                 int width = (int) Math.max(r1.getX() + r1.getWidth(), r2.getX()
  1812.                         + r2.getWidth())
  1813.                         - x;
  1814.                 int height = (int) Math.max(r1.getX() + r1.getHeight(),
  1815.                         r2.getX() + r2.getHeight())
  1816.                         - y;
  1817.                 return new Rectangle(x, y, width, height);
  1818.             }
  1819.  
  1820.             /**
  1821.              * Renders the menu to the specified Graphics object using the
  1822.              * provided mouse location for any popups
  1823.              *
  1824.              * @param g
  1825.              *            Graphics2D
  1826.              * @param mouseLoc
  1827.              *            Point
  1828.              */
  1829.             public void render(Graphics2D g, Point mouseLoc) {
  1830.                 g.setColor(style.bgColor);
  1831.                 g.fill(getArea(true));
  1832.                 MenuPopUp overPopup = null;
  1833.                 for (MenuItem m : components) {
  1834.                     m.render(g);
  1835.                     MenuPopUp p = m.getPopUp();
  1836.                     if (p != null) {
  1837.                         if (m.bounds.contains(mouseLoc)
  1838.                                 || (m.getPopUp().name
  1839.                                         .equalsIgnoreCase(visiblePopup != null ? visiblePopup.name
  1840.                                                 : "") && rectangleContainsApprox(
  1841.                                         visiblePopup.bkg, mouseLoc)))
  1842.                             overPopup = m.getPopUp();
  1843.                     }
  1844.                 }
  1845.                 if (showAllPopups)
  1846.                     getAllPopups(true,"tools").render(g);
  1847.                 else if (overPopup != null) {
  1848.                     overPopup.render(g);
  1849.                 }
  1850.                 visiblePopup = overPopup;
  1851.             }
  1852.  
  1853.             /**
  1854.              * Check if the specified point is within sqrt(2) of the Rectangle
  1855.              *
  1856.              * @param r
  1857.              *            RoundRectangle2D
  1858.              * @param p
  1859.              *            Point
  1860.              * @return boolean contains
  1861.              */
  1862.             public boolean rectangleContainsApprox(RoundRectangle2D r, Point p) {
  1863.                 for (int oX = -1; oX <= 1; oX++)
  1864.                     for (int oY = -1; oY <= 1; oY++)
  1865.                         if (r.contains(new Point(p.x + oX, p.y + oY)))
  1866.                             return true;
  1867.                 return false;
  1868.             }
  1869.  
  1870.             /**
  1871.              * Gets all popups in frontToBack or backToFront order and excluding
  1872.              * specified names
  1873.              *
  1874.              * @param rightToLeft
  1875.              *            boolean
  1876.              * @param excludeItems
  1877.              *            String...
  1878.              * @return MenuPopUp
  1879.              */
  1880.             public MenuPopUp getAllPopups(boolean backToFront,
  1881.                     String... excludeItems) {
  1882.                 List<String> lines = new ArrayList<String>();
  1883.                 if (components.size() > 0)
  1884.                     if (backToFront)
  1885.                         for (int i = components.size() - 1; i >= 0; i--) {
  1886.                             boolean ignored = false;
  1887.                             for (String subS : excludeItems)
  1888.                                 if (components.get(i).name
  1889.                                         .equalsIgnoreCase(subS))
  1890.                                     ignored = true;
  1891.                             if (!ignored) {
  1892.                                 MenuPopUp sub = components.get(i).getPopUp();
  1893.                                 if (sub != null)
  1894.                                     for (String line : sub.lines)
  1895.                                         if (!containsLine(lines, line))
  1896.                                             lines.add(line);
  1897.                             }
  1898.                         }
  1899.                     else
  1900.                         for (int i = 0; i < components.size(); i++) {
  1901.                             boolean ignored = false;
  1902.                             for (String subS : excludeItems)
  1903.                                 if (components.get(i).name
  1904.                                         .equalsIgnoreCase(subS))
  1905.                                     ignored = true;
  1906.                             if (!ignored) {
  1907.                                 MenuPopUp sub = components.get(i).getPopUp();
  1908.                                 if (sub != null)
  1909.                                     for (String line : sub.lines)
  1910.                                         if (!containsLine(lines, line))
  1911.                                             lines.add(line);
  1912.                             }
  1913.                         }
  1914.  
  1915.                 String[] linesArr = new String[lines.size()];
  1916.                 for (int i = 0; i < lines.size(); i++)
  1917.                     linesArr[i] = lines.get(i);
  1918.                 MenuPopUp p = new MenuPopUp(linesArr, "allpopups");
  1919.                 p.setParent(components.get(components.size() - 1));
  1920.                 return p;
  1921.             }
  1922.  
  1923.             /**
  1924.              * Checks if the specified array contains the specified line, just
  1925.              * the tag, not the data if possible
  1926.              *
  1927.              * @param lines
  1928.              *            List of Strings
  1929.              * @param line
  1930.              *            String
  1931.              * @return boolean contains
  1932.              */
  1933.             public boolean containsLine(List<String> lines, String line) {
  1934.                 for (String s : lines)
  1935.                     if (s.contains(":") && line.contains(":")) {
  1936.                         if (s.split(":")[0]
  1937.                                 .equalsIgnoreCase(line.split(":")[0]))
  1938.                             return true;
  1939.                     } else if (s.equalsIgnoreCase(line))
  1940.                         return true;
  1941.                 return false;
  1942.             }
  1943.  
  1944.             /**
  1945.              * Gets a proggie as a BufferedImage
  1946.              *
  1947.              * @return BufferedImage proggie
  1948.              */
  1949.             public BufferedImage getProggie() {
  1950.                 String[] lines = getAllPopups(true, "tools").lines;
  1951.                 Color color = style.FONT_COLOR;
  1952.                 Font font = style.FONT;
  1953.                 float line_height = font.getLineMetrics(lines[0], Paint.frc)
  1954.                         .getHeight();
  1955.                 int width = 0;
  1956.                 for (String line : lines)
  1957.                     if (font.getStringBounds(line, Paint.frc).getWidth() > width)
  1958.                         width = (int) font.getStringBounds(line, Paint.frc)
  1959.                                 .getWidth();
  1960.  
  1961.                 int height = (int) (lines.length * line_height);
  1962.  
  1963.                 width += style.roundCorner + 2;
  1964.                 height += style.roundCorner + 2;
  1965.  
  1966.                 RoundRectangle2D.Float bkg = new RoundRectangle2D.Float(0, 0,
  1967.                         width, height,
  1968.                         style.roundCorner > 0 ? style.roundCorner + 1 : 0,
  1969.                         style.roundCorner > 0 ? style.roundCorner + 1 : 0);
  1970.                 RoundRectangle2D.Float fg = new RoundRectangle2D.Float(
  1971.                         (float) bkg.getX() + 1, (float) bkg.getY() + 1,
  1972.                         width - 2, height - 2, style.roundCorner,
  1973.                         style.roundCorner);
  1974.                 Rectangle txtBounds = new Rectangle(
  1975.                         (int) (fg.getX() + (style.roundCorner / 2)),
  1976.                         (int) (fg.getY() + (style.roundCorner / 2)),
  1977.                         (int) (width - style.roundCorner - 2), (int) (height
  1978.                                 - style.roundCorner - 2));
  1979.                 BufferedImage bImg = new BufferedImage(width, height,
  1980.                         BufferedImage.TYPE_INT_ARGB);
  1981.                 Graphics2D g = (Graphics2D) bImg.getGraphics();
  1982.                 g.setColor(style.bgColor);
  1983.                 g.fill(bkg);
  1984.                 g.setColor(style.fgColor);
  1985.                 g.fill(fg);
  1986.                 g.setFont(font);
  1987.                 g.setColor(color);
  1988.                 for (int i = 0; i < lines.length; i++)
  1989.                     g.drawString(lines[i], txtBounds.x,
  1990.                             ((i + 1) * (line_height)) + txtBounds.y);
  1991.                 return bImg;
  1992.             }
  1993.  
  1994.             /**
  1995.              * Saves a progress report to the Screenshots directory
  1996.              */
  1997.             public void saveProggie() {
  1998.                 String name = crafter.name
  1999.                         + "_"
  2000.                         + PaintFormatter.getFormattedTime(
  2001.                                 System.currentTimeMillis() - Paint.startTime,
  2002.                                 "-", true);
  2003.                 final File dir = new File(
  2004.                         GlobalConfiguration.Paths.getScreenshotsDirectory());
  2005.                 if (dir.isDirectory() || dir.mkdirs()) {
  2006.                     try {
  2007.                         int ext = 0;
  2008.                         while (new File(dir, name
  2009.                                 + (ext > 0 ? ("(" + ext + ")") : "") + ".png")
  2010.                                 .exists())
  2011.                             ext++;
  2012.  
  2013.                         ImageIO.write(Paint.paintMenu.getProggie(), "png",
  2014.                                 new File(dir, name
  2015.                                         + (ext > 0 ? ("(" + ext + ")") : "")));
  2016.                         log.info("Saved proggie!");
  2017.                         log.info(new File(dir, name
  2018.                                 + (ext > 0 ? ("(" + ext + ")") : "") + ".png")
  2019.                                 .getAbsolutePath());
  2020.                     } catch (IOException e) {
  2021.                         log.severe("Failed to save proggie!");
  2022.                     }
  2023.                 }
  2024.             }
  2025.  
  2026.             /**
  2027.              * Adds the specified mouse listener
  2028.              *
  2029.              * @param listen
  2030.              *            MouseListen
  2031.              */
  2032.             public void addMouseListener(MouseListen listen) {
  2033.                 if (mouseListeners == null)
  2034.                     mouseListeners = new ArrayList<MouseListen>();
  2035.                 mouseListeners.add(listen);
  2036.             }
  2037.  
  2038.             /**
  2039.              * Sets the default line listener
  2040.              *
  2041.              * @param listen
  2042.              *            LineListener
  2043.              */
  2044.             public void setDefaultLineListener(LineListener listen) {
  2045.                 defaultLineListener = listen;
  2046.             }
  2047.  
  2048.             /**
  2049.              * Fires the MouseEvent in all containing children and this
  2050.              *
  2051.              * @param e
  2052.              *            MouseEvent
  2053.              */
  2054.             public void fireMouseEvent(MouseEvent e) {
  2055.                 if (getArea(true).contains(e.getPoint()))
  2056.                     if (mouseListeners != null)
  2057.                         for (MouseListen m : mouseListeners)
  2058.                             m.MouseAction(e);
  2059.                 for (MenuItem m : components)
  2060.                     if (m.bounds != null)
  2061.                         if (m.bounds.contains(e.getPoint()))
  2062.                             m.fireMouseEvent(e);
  2063.                 if (visiblePopup != null)
  2064.                     visiblePopup.fireMouseEvent(e);
  2065.  
  2066.             }
  2067.         }
  2068.  
  2069.         /**
  2070.          * Represents a class containing the method for getting the popup
  2071.          *
  2072.          * @author Equinox_
  2073.          *
  2074.          */
  2075.         public abstract class GetPopup {
  2076.             abstract MenuPopUp getPopUp();
  2077.         }
  2078.  
  2079.         /**
  2080.          * Represents a modified MouseListener used in MenuEvents
  2081.          *
  2082.          * @author Equinox_
  2083.          *
  2084.          */
  2085.         public abstract class MouseListen {
  2086.             abstract void MouseAction(MouseEvent e);
  2087.         }
  2088.  
  2089.         /**
  2090.          * An abstract class representing MenuItems
  2091.          *
  2092.          * @author Equinox_
  2093.          *
  2094.          */
  2095.         public abstract class MenuItem {
  2096.             final GetPopup getPopup;
  2097.             Menu parent;
  2098.             RoundRectangle2D.Float bounds;
  2099.             int index;
  2100.             final int width;
  2101.             final int height;
  2102.             List<MouseListen> mouseListeners;
  2103.             final String name;
  2104.  
  2105.             /**
  2106.              * Constructs a MenuItem with the given name, parent, getPopup
  2107.              * method, width and height
  2108.              *
  2109.              * @param name
  2110.              *            String
  2111.              * @param parent
  2112.              *            Menu
  2113.              * @param getPopup
  2114.              *            GetPopup
  2115.              * @param width
  2116.              *            int
  2117.              * @param height
  2118.              *            int
  2119.              */
  2120.             public MenuItem(String name, Menu parent, GetPopup getPopup,
  2121.                     int width, int height) {
  2122.                 this.name = name;
  2123.                 this.height = height;
  2124.                 this.width = width;
  2125.                 this.getPopup = getPopup;
  2126.                 this.parent = parent;
  2127.                 this.bounds = new RoundRectangle2D.Float(0, 0, width, height,
  2128.                         parent.style.roundCorner, parent.style.roundCorner);
  2129.             }
  2130.  
  2131.             /**
  2132.              * Constructs a MenuItem with the given name, parent, width and
  2133.              * height
  2134.              *
  2135.              * @param name
  2136.              *            String
  2137.              * @param parent
  2138.              *            Menu
  2139.              * @param width
  2140.              *            int
  2141.              * @param height
  2142.              *            int
  2143.              */
  2144.             public MenuItem(String name, Menu parent, int width, int height) {
  2145.                 this(name, parent, null, width, height);
  2146.             }
  2147.  
  2148.             /**
  2149.              * sets this item's index
  2150.              *
  2151.              * @param idx
  2152.              *            int
  2153.              */
  2154.             public void setIndex(int idx) {
  2155.                 this.index = idx;
  2156.             }
  2157.  
  2158.             /**
  2159.              * renders this item to the given Graphics2D
  2160.              *
  2161.              * @param g
  2162.              *            Graphics2D
  2163.              */
  2164.             abstract void render(Graphics2D g);
  2165.  
  2166.             /**
  2167.              * Compiles this item
  2168.              */
  2169.             public void compile() {
  2170.                 this.bounds = new RoundRectangle2D.Float(0, 0, width, height,
  2171.                         parent.style.roundCorner, parent.style.roundCorner);
  2172.                 bounds.x = getX();
  2173.                 bounds.y = getY();
  2174.             }
  2175.  
  2176.             /**
  2177.              * Gets the offset from upper left of parent depending on parent's
  2178.              * FlowType
  2179.              *
  2180.              * @return int offsetY
  2181.              */
  2182.             public int getOffX() {
  2183.                 if (parent.getFlowType().equals(FlowType.HORIZONTAL)) {
  2184.                     int x = 1;
  2185.                     for (int i = 0; i < index; i++)
  2186.                         x += 1 + parent.components.get(i).bounds.width;
  2187.  
  2188.                     return x;
  2189.                 }
  2190.                 if (parent.getWidth() > bounds.width)
  2191.                     return (int) (parent.getWidth() / 2 - bounds.width / 2);
  2192.                 return 1;
  2193.             }
  2194.  
  2195.             /**
  2196.              * Gets the offset from upper left of parent depending on parent's
  2197.              * FlowType
  2198.              *
  2199.              * @return int offsetX
  2200.              */
  2201.             public int getOffY() {
  2202.                 if (parent.getFlowType().equals(FlowType.VERTICAL)) {
  2203.                     int y = 1;
  2204.                     for (int i = 0; i < index; i++)
  2205.                         y += 1 + parent.components.get(i).bounds.height;
  2206.  
  2207.                     return y;
  2208.                 }
  2209.                 return 1;
  2210.             }
  2211.  
  2212.             /**
  2213.              * Gets the absolute x
  2214.              *
  2215.              * @return int absolute x
  2216.              */
  2217.             public int getX() {
  2218.                 int x = getOffX() + parent.getAbsX();
  2219.                 return x;
  2220.             }
  2221.  
  2222.             /**
  2223.              * Gets the absolute y
  2224.              *
  2225.              * @return int absolute y
  2226.              */
  2227.             public int getY() {
  2228.                 int y = getOffY() + parent.getAbsY();
  2229.                 return y;
  2230.             }
  2231.  
  2232.             /**
  2233.              * Gets this item's popup
  2234.              *
  2235.              * @return MenuPopUp
  2236.              */
  2237.             public MenuPopUp getPopUp() {
  2238.                 if (getPopup != null) {
  2239.                     MenuPopUp m = getPopup.getPopUp();
  2240.                     m.setParent(this);
  2241.                     if (parent.defaultLineListener != null)
  2242.                         m.addLineListener(parent.defaultLineListener);
  2243.                     return m;
  2244.                 }
  2245.                 return null;
  2246.             }
  2247.  
  2248.             /**
  2249.              * Adds a mouselistener
  2250.              *
  2251.              * @param listen
  2252.              *            MouseListen
  2253.              */
  2254.             public void addMouseListener(MouseListen listen) {
  2255.                 if (mouseListeners == null)
  2256.                     mouseListeners = new ArrayList<MouseListen>();
  2257.                 mouseListeners.add(listen);
  2258.             }
  2259.  
  2260.             /**
  2261.              * Sends the mouse event to all of this item's listeners
  2262.              *
  2263.              * @param e
  2264.              *            MouseEvent
  2265.              */
  2266.             public void fireMouseEvent(MouseEvent e) {
  2267.                 if (mouseListeners != null && mouseListeners.size() > 0)
  2268.                     for (MouseListen m : mouseListeners)
  2269.                         m.MouseAction(e);
  2270.             }
  2271.         }
  2272.  
  2273.         /**
  2274.          * A class representing a mouse over button on the parent menu
  2275.          *
  2276.          * @author Equinox_
  2277.          *
  2278.          */
  2279.         public class MenuButton extends MenuItem {
  2280.             final static int width = 30;
  2281.             final static int height = 30;
  2282.  
  2283.             String imageIcon;
  2284.  
  2285.             /**
  2286.              * Constructs a button using the specified name, parent, imageIcon,
  2287.              * and getPopup method.
  2288.              *
  2289.              * @param name
  2290.              *            String
  2291.              * @param parent
  2292.              *            Menu
  2293.              * @param imageIcon
  2294.              *            String
  2295.              * @param getPopUp
  2296.              *            GetPopup
  2297.              */
  2298.             public MenuButton(String name, Menu parent, String imageIcon,
  2299.                     GetPopup getPopUp) {
  2300.                 super(name, parent, getPopUp, width, height);
  2301.                 this.imageIcon = imageIcon.toLowerCase();
  2302.             }
  2303.  
  2304.             /**
  2305.              * Constructs a button using the specified name, parent, and
  2306.              * imageIcon
  2307.              *
  2308.              * @param name
  2309.              *            String
  2310.              * @param parent
  2311.              *            Menu
  2312.              * @param imageIcon
  2313.              *            String
  2314.              */
  2315.             public MenuButton(String name, Menu parent, String imageIcon) {
  2316.                 this(name, parent, imageIcon, null);
  2317.             }
  2318.  
  2319.             /**
  2320.              * Constructs a button using the specified name and parent
  2321.              *
  2322.              * @param name
  2323.              *            String
  2324.              * @param parent
  2325.              *            Menu
  2326.              */
  2327.             public MenuButton(String name, Menu parent) {
  2328.                 this(name, parent, "", null);
  2329.             }
  2330.  
  2331.             /**
  2332.              * Renders this MenuButton to the given Graphics2D context
  2333.              */
  2334.             public void render(Graphics2D g) {
  2335.                 // Compiling
  2336.                 if (parent.parent.icons.containsKey(imageIcon))
  2337.                     if (parent.parent.icons.get(imageIcon) != null)
  2338.                         if (!parent.parent.icons.containsKey("Button-"
  2339.                                 + imageIcon)) {
  2340.                             // Scale the image
  2341.                             int width = (int) (bounds.width - (parent.style.roundCorner));
  2342.                             int height = (int) (bounds.height - (parent.style.roundCorner));
  2343.                             int hints = Image.SCALE_FAST;
  2344.                             parent.parent.icons.put(
  2345.                                     "Button-" + imageIcon,
  2346.                                     parent.parent.icons.get(imageIcon)
  2347.                                             .getScaledInstance(width, height,
  2348.                                                     hints));
  2349.                         }
  2350.  
  2351.                 g.setColor(parent.style.fgColor);
  2352.                 g.fill(bounds);
  2353.                 if (parent.parent.icons.containsKey("Button-" + imageIcon))
  2354.                     if (parent.parent.icons.get("Button-" + imageIcon) != null)
  2355.                         g.drawImage(
  2356.                                 parent.parent.icons.get("Button-" + imageIcon),
  2357.                                 (int) (bounds.x + (parent.style.roundCorner / 2)),
  2358.                                 (int) (bounds.y + (parent.style.roundCorner / 2)),
  2359.                                 null);
  2360.             }
  2361.         }
  2362.  
  2363.         /**
  2364.          * A class representing a progress bar in a menu
  2365.          *
  2366.          * @author Equinox_
  2367.          *
  2368.          */
  2369.         public class MenuProgressBar extends MenuItem {
  2370.             final static int width = 100;
  2371.             final static int height = 30;
  2372.  
  2373.             final GetPercent getPercent;
  2374.             RoundRectangle2D.Float percent;
  2375.             Point textLoc;
  2376.             String prefix;
  2377.  
  2378.             /**
  2379.              * Constructs a MenuProgress bar using the specified name, parent,
  2380.              * getPercent method, text prefix, and getPopup method
  2381.              *
  2382.              * @param name
  2383.              *            String
  2384.              * @param parent
  2385.              *            Menu
  2386.              * @param getPercent
  2387.              *            GetPercent
  2388.              * @param prefix
  2389.              *            String
  2390.              * @param getPopup
  2391.              *            GetPopup
  2392.              */
  2393.             public MenuProgressBar(String name, Menu parent,
  2394.                     GetPercent getPercent, String prefix, GetPopup getPopup) {
  2395.                 super(name, parent, getPopup, width, height);
  2396.                 this.getPercent = getPercent;
  2397.                 this.prefix = prefix;
  2398.             }
  2399.  
  2400.             /**
  2401.              * Renders this progress bar to the given Graphics context
  2402.              */
  2403.             public void render(Graphics2D g) {
  2404.                 percent.width = getPercent.getPercent();
  2405.                 g.setColor(parent.style.progressBarBG);
  2406.                 g.fill(bounds);
  2407.                 g.setColor(parent.style.progressBarFG);
  2408.                 g.fill(percent);
  2409.                 String perct = String.valueOf(getPercent.getPercent());
  2410.                 if (perct.length() < 2)
  2411.                     perct = "0" + perct;
  2412.                 g.setFont(style.FONT);
  2413.                 g.setColor(style.FONT_COLOR);
  2414.                 g.drawString(prefix + ": " + perct + "%", textLoc.x, textLoc.y);
  2415.             }
  2416.  
  2417.             /**
  2418.              * Compiles this progress bar
  2419.              */
  2420.             public void compile() {
  2421.                 super.compile();
  2422.                 percent = new RoundRectangle2D.Float(bounds.x, bounds.y,
  2423.                         getPercent.getPercent(), bounds.height,
  2424.                         bounds.arcwidth, bounds.archeight);
  2425.                 textLoc = new Point();
  2426.                 textLoc.x = (int) bounds.getCenterX();
  2427.                 textLoc.y = (int) bounds.getCenterY();
  2428.                 textLoc.y += parent.style.FONT.getLineMetrics(prefix + ": 00%",
  2429.                         parent.parent.frc).getHeight() / 2f;
  2430.                 textLoc.x -= parent.style.FONT.getStringBounds(
  2431.                         prefix + ": 00%", parent.parent.frc).getWidth() / 2f;
  2432.             }
  2433.         }
  2434.  
  2435.         /**
  2436.          * Class representing the method for getting the current percentile for
  2437.          * a progress bar
  2438.          *
  2439.          * @author Equinox_
  2440.          *
  2441.          */
  2442.         public abstract class GetPercent {
  2443.             abstract int getPercent();
  2444.         }
  2445.  
  2446.         /**
  2447.          * Class representing the LineListeners in popups
  2448.          *
  2449.          * @author Equinox_
  2450.          *
  2451.          */
  2452.         public abstract class LineListener {
  2453.             abstract void fireEvent(MouseEvent e, String line);
  2454.  
  2455.             abstract boolean hasAction(String line);
  2456.         }
  2457.  
  2458.         /**
  2459.          * A class representing the popups in the paint interface
  2460.          *
  2461.          * @author Equinox_
  2462.          *
  2463.          */
  2464.         public class MenuPopUp {
  2465.             private Font font;
  2466.             private Color color;
  2467.             private PaintStyle style;
  2468.             private RoundRectangle2D bkg;
  2469.             public RoundRectangle2D fg;
  2470.             public Rectangle txtBounds;
  2471.             public MenuItem parent;
  2472.             public final String name;
  2473.             public String[] lines;
  2474.             int width;
  2475.             int height;
  2476.             List<LineListener> lineListeners;
  2477.  
  2478.             /**
  2479.              * Creates a popup using the provided array of strings making up the
  2480.              * lines and name
  2481.              *
  2482.              * @param lines
  2483.              *            as String[]
  2484.              * @param name
  2485.              *            as String
  2486.              */
  2487.             public MenuPopUp(String[] lines, String name) {
  2488.                 this.name = name;
  2489.                 this.lines = lines;
  2490.             }
  2491.  
  2492.             /**
  2493.              * Set this popup's parent and compiles locations
  2494.              *
  2495.              * @param parent
  2496.              *            MenuItem
  2497.              */
  2498.             public void setParent(MenuItem parent) {
  2499.                 this.parent = parent;
  2500.                 this.style = parent.parent.style;
  2501.                 // Calc dimensions
  2502.                 color = name.equals("tools") ? style.TOOLS_FONT_COLOR
  2503.                         : style.FONT_COLOR;
  2504.                 font = name.equals("tools") ? style.TOOLS_FONT : style.FONT;
  2505.                 float line_height = font.getLineMetrics(lines[0], Paint.frc)
  2506.                         .getHeight();
  2507.                 width = 0;
  2508.                 for (String line : lines)
  2509.                     if (font.getStringBounds(line, Paint.frc).getWidth() > width)
  2510.                         width = (int) font.getStringBounds(line, Paint.frc)
  2511.                                 .getWidth();
  2512.  
  2513.                 height = (int) (lines.length * line_height);
  2514.  
  2515.                 width += style.roundCorner + 2;
  2516.                 height += style.roundCorner + 2;
  2517.  
  2518.                 int halfPadding = (parent.parent.padding / 2);
  2519.                 if (halfPadding < 1 && halfPadding != 0)
  2520.                     halfPadding = 1;
  2521.                 int x = 0;
  2522.                 int y = 0;
  2523.                 if (parent.parent.getFlowType().equals(FlowType.HORIZONTAL)) {
  2524.                     if (parent.bounds.y - height - parent.parent.padding > 0) {
  2525.                         // Above
  2526.                         x = (int) (parent.bounds.x - width
  2527.                                 + parent.bounds.width + halfPadding);
  2528.                         if (x < 0)
  2529.                             x = (int) (parent.bounds.x - halfPadding);
  2530.                         y = (int) (parent.parent.getAbsY() - height);
  2531.                     } else {
  2532.                         // Below
  2533.                         x = (int) (parent.bounds.x - halfPadding);
  2534.                         if (x + width > parent.parent.bounds.width)
  2535.                             x = (int) (parent.bounds.x - halfPadding - width);
  2536.                         y = (int) (parent.parent.getAbsY() + parent.parent
  2537.                                 .getHeight());
  2538.                     }
  2539.                 } else {
  2540.                     // Vertical
  2541.                     if (parent.bounds.x - width - parent.parent.padding > 0) {
  2542.                         // Left
  2543.                         x = (int) (parent.parent.getAbsX() - width);
  2544.                         if (parent.bounds.y - height + halfPadding
  2545.                                 + parent.bounds.height > 0)
  2546.                             y = (int) (parent.bounds.y - height + halfPadding + parent.bounds.height);
  2547.                         else
  2548.                             y = (int) (parent.bounds.y - halfPadding);
  2549.                     } else {
  2550.                         // Right
  2551.                         x = (int) (parent.parent.getAbsX() + parent.parent
  2552.                                 .getWidth());
  2553.                         if (parent.bounds.y - height + halfPadding
  2554.                                 + parent.bounds.height > 0)
  2555.                             y = (int) (parent.bounds.y - height + halfPadding + parent.bounds.height);
  2556.                         else
  2557.                             y = (int) (parent.bounds.y - halfPadding);
  2558.                     }
  2559.                 }
  2560.                 bkg = new RoundRectangle2D.Float(x, y, width, height,
  2561.                         style.roundCorner > 0 ? style.roundCorner + 1 : 0,
  2562.                         style.roundCorner > 0 ? style.roundCorner + 1 : 0);
  2563.                 fg = new RoundRectangle2D.Float((float) bkg.getX() + 1,
  2564.                         (float) bkg.getY() + 1, width - 2, height - 2,
  2565.                         style.roundCorner, style.roundCorner);
  2566.                 txtBounds = new Rectangle(
  2567.                         (int) (fg.getX() + (style.roundCorner / 2)),
  2568.                         (int) (fg.getY() + (style.roundCorner / 2)),
  2569.                         (int) (width - style.roundCorner - 2), (int) (height
  2570.                                 - style.roundCorner - 2));
  2571.             }
  2572.  
  2573.             /**
  2574.              * Paints this popup to using the provided Graphics2D in relation to
  2575.              * it's parent PaintInterface
  2576.              *
  2577.              * @param g
  2578.              *            as Graphics2D
  2579.              */
  2580.             public void render(Graphics2D g) {
  2581.                 g.setColor(style.bgColor);
  2582.                 g.fill(bkg);
  2583.                 g.setColor(style.fgColor);
  2584.                 g.fill(fg);
  2585.  
  2586.                 float line_height = (txtBounds.height - 4) / lines.length;
  2587.  
  2588.                 g.setFont(font);
  2589.                 g.setColor(color);
  2590.                 for (int i = 0; i < lines.length; i++) {
  2591.                     if (new Rectangle(txtBounds.x,
  2592.                             (int) ((i * (line_height)) + txtBounds.y),
  2593.                             txtBounds.width, (int) line_height)
  2594.                             .contains(parent.parent.parent.mouseLoc)
  2595.                             && hasAction(lines[i])) {
  2596.                         g.setColor(style.highlightColor);
  2597.                         g.fill(new Rectangle(txtBounds.x,
  2598.                                 (int) ((i * (line_height)) + 2 + txtBounds.y),
  2599.                                 txtBounds.width, (int) line_height));
  2600.                     }
  2601.                     g.setColor(color);
  2602.                     g.drawString(lines[i], txtBounds.x,
  2603.                             ((i + 1) * (line_height)) + txtBounds.y);
  2604.                 }
  2605.             }
  2606.  
  2607.             boolean hasAction(String line) {
  2608.                 if (lineListeners != null)
  2609.                     for (LineListener l : lineListeners)
  2610.                         if (l.hasAction(line))
  2611.                             return true;
  2612.                 return false;
  2613.             }
  2614.  
  2615.             /**
  2616.              * Adds the specified line listener
  2617.              *
  2618.              * @param l
  2619.              *            LineListener
  2620.              */
  2621.             public void addLineListener(LineListener l) {
  2622.                 if (lineListeners == null)
  2623.                     lineListeners = new ArrayList<LineListener>();
  2624.                 this.lineListeners.add(l);
  2625.             }
  2626.  
  2627.             /**
  2628.              * fires the specified mouse event using this popup's line listeners
  2629.              *
  2630.              * @param e
  2631.              *            MouseEvent
  2632.              */
  2633.             public void fireMouseEvent(MouseEvent e) {
  2634.                 if (!bkg.contains(e.getPoint()) || lineListeners == null
  2635.                         || lineListeners.size() == 0)
  2636.                     return;
  2637.  
  2638.                 int line_height = (txtBounds.height - 4) / lines.length;
  2639.  
  2640.                 for (int i = 0; i < lines.length; i++) {
  2641.                     Rectangle stringRect = new Rectangle(txtBounds.x,
  2642.                             (i * (line_height)) + txtBounds.y, txtBounds.width,
  2643.                             line_height);
  2644.                     if (stringRect.contains(e.getPoint())) {
  2645.                         for (LineListener lineListener : lineListeners)
  2646.                             lineListener.fireEvent(e, lines[i]);
  2647.                         return;
  2648.                     }
  2649.                 }
  2650.             }
  2651.         }
  2652.  
  2653.     }
  2654.  
  2655.     /**
  2656.      * Used for specifing the way a menu is layed out.
  2657.      *
  2658.      * @author Equinox_
  2659.      *
  2660.      */
  2661.     private enum FlowType {
  2662.         VERTICAL, HORIZONTAL
  2663.     }
  2664.  
  2665.     /**
  2666.      * Used for anchoring the menu bar
  2667.      *
  2668.      * @author Equinox_
  2669.      *
  2670.      */
  2671.     private enum Anchor {
  2672.         UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT
  2673.     }
  2674.  
  2675.     /**
  2676.      * Used to style the paint
  2677.      *
  2678.      * @author Equinox_
  2679.      *
  2680.      */
  2681.     public static class PaintStyle {
  2682.         public final Color FONT_COLOR = Color.BLACK;
  2683.         public final Font FONT = new Font("Arial", 1, 10)
  2684.                 .deriveFont(Font.PLAIN);
  2685.  
  2686.         public final Font TOOLS_FONT = new Font("Arial", 1, 14)
  2687.                 .deriveFont(Font.PLAIN);
  2688.         public final Color TOOLS_FONT_COLOR = Color.BLACK;
  2689.  
  2690.         public final float roundCorner = 10f;
  2691.         public final Color progressBarFG = new Color(0f, 1f, 0f, 1f);
  2692.         public final Color progressBarBG = new Color(.3f, .3f, .3f, 1f);
  2693.         public final Color bgColor = new Color(0f, 0f, 0f, 0.75f);
  2694.         final Color fgColor = new Color(.3f, .3f, .3f, 1f);
  2695.         final Color highlightColor = new Color(1f, 0f, 0f, 0.05f);
  2696.     }
  2697.  
  2698.     /**
  2699.      * Use these enums to define the type of path <br />
  2700.      * ALL_Center: All points are near the middle of the lines <br />
  2701.      * Full: Points can be anywhere on the line<br />
  2702.      * WEIGHT_CENTER: Weighted to center, possibility of being on sides, but
  2703.      * lower
  2704.      *
  2705.      * @author Equinox_
  2706.      *
  2707.      */
  2708.     enum MeshPathType {
  2709.         ALL_CENTER, FULL, WEIGHT_CENTER
  2710.     }
  2711.  
  2712.     /**
  2713.      * A class that will take a specified mesh, generate a path through it, then
  2714.      * round the path off with Bezier curves.
  2715.      *
  2716.      * @author Equinox_
  2717.      *
  2718.      */
  2719.     public class MeshPath {
  2720.         private final Color MESH_FILL = new Color(0.5f, 0.5f, 0.5f, 0.75f);
  2721.         private final Color MESH_BORDER = new Color(0.75f, 0.75f, 0.75f, 0.75f);
  2722.         private final Color PATH_COLOR = new Color(0f, 1f, 0f, 1f);
  2723.         private final Color SMOOTH_PATH_COLOR = new Color(0f, 0f, 1f, 1f);
  2724.         private final Color SUBDIVIDED_PATH_COLOR = new Color(1f, 0f, 1f, 1f);
  2725.  
  2726.         private List<RSTile[]> mesh = new ArrayList<RSTile[]>();
  2727.         private List<RSTile> blockPath = new ArrayList<RSTile>();
  2728.         private List<RSTile> smoothPath = new ArrayList<RSTile>();
  2729.         private boolean end;
  2730.         private List<RSTile> subdividedPath = new ArrayList<RSTile>();
  2731.  
  2732.         /**
  2733.          * Creates a MeshPath with a blank mesh
  2734.          */
  2735.         public MeshPath() {
  2736.  
  2737.         }
  2738.  
  2739.         /**
  2740.          * Creates a MeshPath using the provided mesh
  2741.          *
  2742.          * @param mesh
  2743.          *            as RSTile[][]
  2744.          */
  2745.         public MeshPath(RSTile[][] mesh) {
  2746.             setMesh(mesh);
  2747.         }
  2748.  
  2749.         /**
  2750.          * Adds a line to the mesh with the given tiles
  2751.          *
  2752.          * @param tiles
  2753.          *            the RSTiles making up the line
  2754.          */
  2755.         public void addLine(RSTile... tiles) {
  2756.             mesh.add(tiles);
  2757.         }
  2758.  
  2759.         /**
  2760.          * Gets the number of lines in the mesh
  2761.          *
  2762.          * @return int size
  2763.          */
  2764.         public int meshSize() {
  2765.             return mesh.size();
  2766.         }
  2767.  
  2768.         /**
  2769.          * Adds all the lines of a specified mesh to the current mesh.
  2770.          *
  2771.          * @param mesh
  2772.          *            as RSTile[][]
  2773.          */
  2774.         public void addMesh(RSTile[][] mesh) {
  2775.             for (RSTile[] line : mesh)
  2776.                 this.mesh.add(line);
  2777.         }
  2778.  
  2779.         /**
  2780.          * Sets the current mesh to the provided mesh
  2781.          *
  2782.          * @param mesh
  2783.          *            as RSTile[][]
  2784.          */
  2785.         public void setMesh(RSTile[][] mesh) {
  2786.             this.mesh.clear();
  2787.             addMesh(mesh);
  2788.         }
  2789.  
  2790.         public List<RSTile> getTilesOnLine(RSTile a, RSTile b) {
  2791.             List<RSTile> sublineTiles = new ArrayList<RSTile>();
  2792.             int deltaX = b.getX() - a.getX();
  2793.             int deltaY = b.getY() - a.getY();
  2794.             double dist = calc.distanceBetween(a, b);
  2795.             if (dist == 0)
  2796.                 dist = 1;
  2797.             double eX = deltaX / dist, eY = deltaY / dist;
  2798.             for (int i = 0; i <= dist; i++)
  2799.                 sublineTiles.add(new RSTile(a.getX() + (int) (eX * i), a.getY()
  2800.                         + (int) (eY * i)));
  2801.             return sublineTiles;
  2802.         }
  2803.  
  2804.         /**
  2805.          * Gets all the tiles on the provided line
  2806.          *
  2807.          * @param line
  2808.          *            as RSTile...
  2809.          * @return List<RSTile> tiles on line
  2810.          */
  2811.         public List<RSTile> getTilesOnLines(RSTile... line) {
  2812.             List<RSTile> sublineTiles = new ArrayList<RSTile>();
  2813.             if (line.length == 1)
  2814.                 sublineTiles.add(line[0]);
  2815.             else
  2816.                 for (int i = 1; i < line.length; i++) {
  2817.                     RSTile a = line[i - 1];
  2818.                     RSTile b = line[i];
  2819.                     sublineTiles.addAll(getTilesOnLine(a, b));
  2820.                 }
  2821.             return sublineTiles;
  2822.         }
  2823.  
  2824.         /**
  2825.          * Gets a tile on the provided line using the ALL_CENTER method
  2826.          *
  2827.          * @param line
  2828.          *            as RSTile...
  2829.          * @return RSTile tile on line
  2830.          */
  2831.         public RSTile getTileOnLine(RSTile... line) {
  2832.             return getTileOnLine(MeshPathType.ALL_CENTER, line);
  2833.         }
  2834.  
  2835.         /**
  2836.          * Gets a tile on the provided line using the provided method
  2837.          *
  2838.          * @param placement
  2839.          *            as MeshPathType
  2840.          * @param line
  2841.          *            as RSTile...
  2842.          * @return RSTile tile on line
  2843.          */
  2844.         public RSTile getTileOnLine(MeshPathType placement, RSTile... line) {
  2845.             List<RSTile> tiles = getTilesOnLines(line);
  2846.             int index = 0;
  2847.             switch (placement) {
  2848.             case ALL_CENTER:
  2849.                 index = random(tiles.size() / 4, 3 * tiles.size() / 4 + 1);
  2850.                 // +1 due to exclusive upper bound
  2851.                 break;
  2852.             case FULL:
  2853.                 index = random(0, tiles.size());
  2854.                 break;
  2855.             case WEIGHT_CENTER:
  2856.                 index = random(tiles.size() / 4, 3 * tiles.size() / 4 + 1);
  2857.                 // +1 due to exclusive upper bound
  2858.  
  2859.                 int rand = random(-10, tiles.size() / 4);
  2860.                 index += rand > 0 ? rand : 0;
  2861.                 break;
  2862.             }
  2863.  
  2864.             if (index < 0)
  2865.                 index = 0;
  2866.             if (index >= tiles.size())
  2867.                 index = tiles.size() - 1;
  2868.  
  2869.             if (tiles.size() > 0)
  2870.                 return tiles.get(index);
  2871.             else
  2872.                 return null;
  2873.         }
  2874.  
  2875.         /**
  2876.          * Paints the mesh to the provided graphics context in the minimap
  2877.          *
  2878.          * @param g
  2879.          *            as Graphics2D
  2880.          */
  2881.         public void paintMesh(Graphics2D g) {
  2882.             if (mesh.size() > 1) {
  2883.                 for (int i = 1; i < mesh.size(); i++) {
  2884.                     RSTile[] lineA = mesh.get(i - 1).clone();
  2885.                     RSTile[] lineB = mesh.get(i).clone();
  2886.                     Polygon poly = new Polygon();
  2887.                     RSTile a = lineA[0];
  2888.                     RSTile b = lineB[0];
  2889.                     RSTile c = lineA[lineA.length - 1];
  2890.                     RSTile d = lineB[lineB.length - 1];
  2891.                     boolean onMap = true;
  2892.                     double distA = calc.distanceBetween(c, b)
  2893.                             + calc.distanceBetween(a, d);
  2894.                     double distB = calc.distanceBetween(a, b)
  2895.                             + calc.distanceBetween(c, d);
  2896.  
  2897.                     if (distA > distB) {
  2898.                         // Reverse lineB.
  2899.                         RSTile[] newLineB = new RSTile[lineB.length];
  2900.                         int start = lineB.length - 1;
  2901.                         for (RSTile t : lineB) {
  2902.                             if (start >= 0)
  2903.                                 newLineB[start] = t;
  2904.                             start--;
  2905.                         }
  2906.                         lineB = newLineB;
  2907.                     }
  2908.  
  2909.                     if (lineA != null)
  2910.                         for (RSTile t : lineA) {
  2911.                             Point p = calc.tileToMinimap(t);
  2912.                             if (p.x == -1 || p.y == -1) {
  2913.                                 onMap = false;
  2914.                                 break;
  2915.                             }
  2916.                             poly.addPoint(p.x, p.y);
  2917.                         }
  2918.  
  2919.                     if (lineB != null)
  2920.                         for (RSTile t : lineB) {
  2921.                             Point p = calc.tileToMinimap(t);
  2922.                             if (p.x == -1 || p.y == -1) {
  2923.                                 onMap = false;
  2924.                                 break;
  2925.                             }
  2926.                             poly.addPoint(p.x, p.y);
  2927.                         }
  2928.  
  2929.                     if (onMap) {
  2930.                         g.setColor(MESH_BORDER);
  2931.                         g.draw(poly);
  2932.                         g.setColor(MESH_FILL);
  2933.                         g.fill(poly);
  2934.                     }
  2935.                 }
  2936.             }
  2937.         }
  2938.  
  2939.         public RSTile getTileOnMap(RSTile t) {
  2940.             List<RSTile> tiles = getTilesOnLine(t, getMyPlayer().getLocation());
  2941.             for (RSTile tt : tiles)
  2942.                 if (calc.tileOnMap(tt))
  2943.                     return tt;
  2944.             return null;
  2945.         }
  2946.  
  2947.         /**
  2948.          * Paints the current un-smoothed path to the minimap using the provided
  2949.          * graphics object
  2950.          *
  2951.          * @param g
  2952.          *            as Graphics2D
  2953.          */
  2954.         public void paintPath(Graphics2D g) {
  2955.             if (blockPath.size() > 1) {
  2956.                 g.setColor(PATH_COLOR);
  2957.                 for (int i = 1; i < blockPath.size(); i++) {
  2958.                     Point a = calc.tileToMinimap(blockPath.get(i - 1));
  2959.                     Point b = calc.tileToMinimap(blockPath.get(i));
  2960.                     if (a.x > 0 && a.y > 0)
  2961.                         g.fillOval(a.x - 2, a.y - 2, 5, 5);
  2962.                     if (b.x > 0 && b.y > 0)
  2963.                         g.fillOval(b.x - 2, b.y - 2, 5, 5);
  2964.                     if (a.x > 0 && a.y > 0 && b.x > 0 && b.y > 0)
  2965.                         g.drawLine(a.x, a.y, b.x, b.y);
  2966.                 }
  2967.             }
  2968.         }
  2969.  
  2970.         /**
  2971.          * Paints the smoothed version of the current path to the minimap using
  2972.          * the provided graphics object
  2973.          *
  2974.          * @param g
  2975.          *            as Graphics2D
  2976.          */
  2977.         public void paintSmoothPath(Graphics2D g) {
  2978.             if (smoothPath.size() > 1) {
  2979.                 g.setColor(SMOOTH_PATH_COLOR);
  2980.                 for (int i = 1; i < smoothPath.size(); i++) {
  2981.                     Point a = calc.tileToMinimap(smoothPath.get(i - 1));
  2982.                     Point b = calc.tileToMinimap(smoothPath.get(i));
  2983.                     if (a.x > 0 && a.y > 0)
  2984.                         g.fillOval(a.x - 2, a.y - 2, 5, 5);
  2985.                     if (b.x > 0 && b.y > 0)
  2986.                         g.fillOval(b.x - 2, b.y - 2, 5, 5);
  2987.                     if (a.x > 0 && a.y > 0 && b.x > 0 && b.y > 0)
  2988.                         g.drawLine(a.x, a.y, b.x, b.y);
  2989.                 }
  2990.             }
  2991.         }
  2992.  
  2993.         /**
  2994.          * Paints the subdivided smoothed version of the current path to the
  2995.          * minimap using the provided graphics object
  2996.          *
  2997.          * @param g
  2998.          *            as Graphics2D
  2999.          */
  3000.         public void paintSubdividedPath(Graphics2D g) {
  3001.             if (subdividedPath.size() > 0) {
  3002.                 g.setColor(SUBDIVIDED_PATH_COLOR);
  3003.                 for (RSTile t : subdividedPath) {
  3004.                     Point tileLoc = calc.tileToMinimap(t);
  3005.                     if (tileLoc.x > 0 && tileLoc.y > 0)
  3006.                         g.fillOval(tileLoc.x, tileLoc.y, 5, 5);
  3007.                 }
  3008.             }
  3009.         }
  3010.  
  3011.         /**
  3012.          * Paints the mesh, and all versions of the current path to the minimap
  3013.          * using the provided graphics object
  3014.          *
  3015.          * @param g
  3016.          *            as Graphics2D
  3017.          */
  3018.         public void paintAll(Graphics2D g) {
  3019.             paintMesh(g);
  3020.             paintPath(g);
  3021.             paintSmoothPath(g);
  3022.             paintSubdividedPath(g);
  3023.         }
  3024.  
  3025.         /**
  3026.          * Finds a path between the start index and the end index using the
  3027.          * provided placement type, then sets the current path to the found path
  3028.          *
  3029.          * @param placement
  3030.          *            as MeshPathType
  3031.          * @param start
  3032.          *            index
  3033.          * @param end
  3034.          *            index
  3035.          */
  3036.         public void findPath(MeshPathType placement, int start, int end) {
  3037.             blockPath.clear();
  3038.             RSTile lineTile = getTileOnLine(mesh.get(start));
  3039.             String s = lineTile.toString();
  3040.             blockPath.add(lineTile);
  3041.             for (int i = start + 1; i <= end; i++) {
  3042.                 lineTile = getTileOnLine(placement, mesh.get(i));
  3043.                 blockPath.add(lineTile);
  3044.                 s += "," + lineTile.toString();
  3045.             }
  3046.             smoothPath = smoothPath(blockPath);
  3047.             subdividedPath = subdividePath(smoothPath);
  3048.         }
  3049.  
  3050.         /**
  3051.          * Finds a path between the beginning and end of the mesh using the
  3052.          * provided placement type, then sets the current path to the found path
  3053.          *
  3054.          * @param placement
  3055.          *            as MeshPathType
  3056.          */
  3057.         public void findPath(MeshPathType placement) {
  3058.             findPath(placement, 0, mesh.size() - 1);
  3059.         }
  3060.  
  3061.         /**
  3062.          * Finds a path between the beginning and end of the mesh using the
  3063.          * ALL_CENTER placement type, then sets the current path to the found
  3064.          * path
  3065.          */
  3066.         public void findPath() {
  3067.             findPath(MeshPathType.ALL_CENTER);
  3068.         }
  3069.  
  3070.         /**
  3071.          * Clears the current path and all refined versions of it.
  3072.          */
  3073.         public void clearPath() {
  3074.             blockPath.clear();
  3075.             smoothPath.clear();
  3076.             subdividedPath.clear();
  3077.         }
  3078.  
  3079.         /**
  3080.          * Smoothes the provided List of RSTiles using Bezier curves
  3081.          *
  3082.          * @param path
  3083.          *            as List
  3084.          * @return List<RSTile> smoothed path
  3085.          */
  3086.         public List<RSTile> smoothPath(List<RSTile> path) {
  3087.             List<RSTile> smoothed = new ArrayList<RSTile>();
  3088.             smoothed.addAll(path);
  3089.             if (path.size() >= 3) {
  3090.                 for (int b = 0; b < path.size(); b++) {
  3091.                     if (b + 2 < path.size()) {
  3092.                         double x2, y2;
  3093.  
  3094.                         double t = 0.5;
  3095.                         // use Berstein polynomials
  3096.                         x2 = (path.get(b + 0).getX() + t
  3097.                                 * (-path.get(b + 0).getX() * 3 + t
  3098.                                         * (3 * path.get(b + 0).getX() - path
  3099.                                                 .get(b + 0).getX() * t)))
  3100.                                 + t
  3101.                                 * (3 * path.get(b + 1).getX() + t
  3102.                                         * (-6 * path.get(b + 1).getX() + path
  3103.                                                 .get(b + 1).getX() * 3 * t))
  3104.                                 + t
  3105.                                 * t
  3106.                                 * (path.get(b + 1).getX() * 3 - path.get(b + 1)
  3107.                                         .getX() * 3 * t)
  3108.                                 + path.get(b + 2).getX() * t * t * t;
  3109.                         y2 = (path.get(b + 0).getY() + t
  3110.                                 * (-path.get(b + 0).getY() * 3 + t
  3111.                                         * (3 * path.get(b + 0).getY() - path
  3112.                                                 .get(b + 0).getY() * t)))
  3113.                                 + t
  3114.                                 * (3 * path.get(b + 1).getY() + t
  3115.                                         * (-6 * path.get(b + 1).getY() + path
  3116.                                                 .get(b + 1).getY() * 3 * t))
  3117.                                 + t
  3118.                                 * t
  3119.                                 * (path.get(b + 1).getY() * 3 - path.get(b + 1)
  3120.                                         .getY() * 3 * t)
  3121.                                 + path.get(b + 2).getY() * t * t * t;
  3122.                         RSTile loc = new RSTile((int) x2, (int) y2);
  3123.                         smoothed.set(b + 1, getTileInMesh(loc));
  3124.  
  3125.                     }
  3126.                 }
  3127.             }
  3128.             return smoothed;
  3129.         }
  3130.  
  3131.         /**
  3132.          * Gets the nearest tile in the mesh to the provided RSTile
  3133.          *
  3134.          * @param tile
  3135.          *            the RSTile to use
  3136.          * @return RSTile tile in mesh
  3137.          */
  3138.         public RSTile getTileInMesh(RSTile tile) {
  3139.             if (inMesh(tile))
  3140.                 return tile;
  3141.             double bestDist = Double.MAX_VALUE;
  3142.             RSTile nearest = null;
  3143.             if (mesh.size() > 1) {
  3144.                 for (int i = 1; i < mesh.size(); i++) {
  3145.                     RSTile[] lineA = mesh.get(i - 1);
  3146.                     RSTile[] lineB = mesh.get(i);
  3147.                     if (lineA != null && lineB != null) {
  3148.                         RSTile a = lineA[0];
  3149.                         RSTile b = lineB[0];
  3150.                         RSTile c = lineA[lineA.length - 1];
  3151.                         RSTile d = lineB[lineB.length - 1];
  3152.                         if (calc.distanceBetween(a, b) > calc.distanceBetween(
  3153.                                 a, d)) {
  3154.                             // Wrong order, switch b and d.
  3155.                             RSTile clone = new RSTile(b.getX(), b.getY(),
  3156.                                     b.getZ());
  3157.                             b = new RSTile(d.getX(), d.getY(), d.getZ());
  3158.                             d = clone;
  3159.                         }
  3160.                         for (RSTile t : getTilesOnLine(a, b)) {
  3161.                             double dist = calc.distanceBetween(tile, t);
  3162.                             if (dist < bestDist) {
  3163.                                 bestDist = dist;
  3164.                                 nearest = t;
  3165.                             }
  3166.                         }
  3167.                         for (RSTile t : getTilesOnLine(c, d)) {
  3168.                             double dist = calc.distanceBetween(tile, t);
  3169.                             if (dist < bestDist) {
  3170.                                 bestDist = dist;
  3171.                                 nearest = t;
  3172.                             }
  3173.                         }
  3174.                     }
  3175.                 }
  3176.             }
  3177.             return nearest;
  3178.         }
  3179.  
  3180.         /**
  3181.          * Checks to see if the provided RSTile is inside the mesh
  3182.          *
  3183.          * @param tile
  3184.          *            the RSTile to check
  3185.          * @return boolean is in mesh
  3186.          */
  3187.         public boolean inMesh(RSTile tile) {
  3188.             if (mesh.size() > 1) {
  3189.                 for (int i = 1; i < mesh.size(); i++) {
  3190.                     RSTile[] lineA = mesh.get(i - 1);
  3191.                     RSTile[] lineB = mesh.get(i);
  3192.                     Polygon poly = new Polygon();
  3193.                     if (lineA != null)
  3194.                         for (RSTile t : lineA)
  3195.                             poly.addPoint(t.getX(), t.getY());
  3196.  
  3197.                     if (lineB != null)
  3198.                         for (RSTile t : lineB)
  3199.                             poly.addPoint(t.getX(), t.getY());
  3200.  
  3201.                     if (poly.npoints >= 2)
  3202.                         if (poly.contains(new Point(tile.getX(), tile.getY())))
  3203.                             return true;
  3204.                 }
  3205.             }
  3206.             return false;
  3207.         }
  3208.  
  3209.         /**
  3210.          * Takes the provided path and subdivides it using the length provided
  3211.          * in the Options class
  3212.          *
  3213.          * @param smoothPath
  3214.          *            path to subdivide as a List<RSTile>
  3215.          * @return List<RSTile> subdivided path
  3216.          */
  3217.         public List<RSTile> subdividePath(List<RSTile> smoothPath) {
  3218.             RSTile[] smoothpathArray = new RSTile[smoothPath.size()];
  3219.             List<RSTile> path = getTilesOnLines(smoothPath
  3220.                     .toArray(smoothpathArray));
  3221.             List<RSTile> subdividedPath = new ArrayList<RSTile>();
  3222.             RSTile previous = null;
  3223.             for (RSTile next : path) {
  3224.                 if ((previous == null ? true : calc.distanceBetween(previous,
  3225.                         next) > Options.walkingDistance.random())
  3226.                         || next == path.get(path.size() - 1)) {
  3227.                     previous = new RSTile(next.getX(), next.getY());
  3228.                     subdividedPath.add(previous);
  3229.                 }
  3230.             }
  3231.             return subdividedPath;
  3232.         }
  3233.  
  3234.         /**
  3235.          * Takes a step along this path if appropriate. Specifies only
  3236.          * TraversalOption.SPACE_ACTIONS.
  3237.          *
  3238.          * @return <tt>true</tt> if this path is currently valid for the player;
  3239.          *         otherwise <tt>false</tt>.
  3240.          * @see #traverse(EnumSet)
  3241.          */
  3242.         public boolean traverse() {
  3243.             return this.traverse(EnumSet.of(TraversalOption.HANDLE_RUN,
  3244.                     TraversalOption.SPACE_ACTIONS));
  3245.         }
  3246.  
  3247.         /**
  3248.          * Takes a step along this path if appropriate. If the path cannot be
  3249.          * traversed due to the player being too far from its vertices or
  3250.          * already at the end vertex, <tt>false</tt> will be returned. In all
  3251.          * other cases, <tt>true</tt> will be returned, but an action will not
  3252.          * necessarily be performed (based on the given options).
  3253.          *
  3254.          * @param options
  3255.          *            Walking style options.
  3256.          * @return <tt>true</tt> if this path is currently valid for the player;
  3257.          *         otherwise <tt>false</tt>.
  3258.          */
  3259.         public boolean traverse(EnumSet<TraversalOption> options) {
  3260.             RSTile next = getNext();
  3261.             if (next == null)
  3262.                 return false;
  3263.             int nextIdx = getNearestIndexTo(next);
  3264.  
  3265.             if (next.equals(getEnd())) {
  3266.                 if (calc.distanceTo(next) <= 1
  3267.                         || (end && players.getMyPlayer().isMoving())
  3268.                         || next.equals(walking.getDestination())) {
  3269.                     return false;
  3270.                 }
  3271.                 end = true;
  3272.             } else {
  3273.                 end = false;
  3274.             }
  3275.             if (options.contains(TraversalOption.HANDLE_RUN)
  3276.                     && !walking.isRunEnabled() && walking.getEnergy() > 50) {
  3277.                 walking.setRun(true);
  3278.                 sleep(300);
  3279.             }
  3280.             if (walking.getDestination() != null
  3281.                     && getNearestIndexTo(walking.getDestination()) >= nextIdx)
  3282.                 return false;
  3283.             if (options.contains(TraversalOption.SPACE_ACTIONS)) {
  3284.                 RSTile dest = walking.getDestination();
  3285.                 if (dest != null && players.getMyPlayer().isMoving()
  3286.                         && calc.distanceTo(dest) > 5
  3287.                         && calc.distanceBetween(next, dest) < 7) {
  3288.                     return true;
  3289.                 }
  3290.             }
  3291.             return walking.walkTileMM(next, 0, 0);
  3292.         }
  3293.  
  3294.         /**
  3295.          * Checks whether or not this path can be traversed by the player. This
  3296.          * will be the case provided that the player near to one of its
  3297.          * vertices, but not already standing on the end vertex.
  3298.          *
  3299.          * @return <tt>true</tt> if the player can walk along this path;
  3300.          *         otherwise <tt>false</tt>.
  3301.          */
  3302.         public boolean isValid() {
  3303.             return subdividedPath.size() > 0 && getNext() != null
  3304.                     && !players.getMyPlayer().getLocation().equals(getEnd());
  3305.         }
  3306.  
  3307.         /**
  3308.          * Gets the start tile of this path.
  3309.          *
  3310.          * @return The start <code>RSTile</code>.
  3311.          */
  3312.         public RSTile getStart() {
  3313.             if (subdividedPath.size() > 0)
  3314.                 return subdividedPath.get(0);
  3315.             else
  3316.                 return null;
  3317.         }
  3318.  
  3319.         /**
  3320.          * Gets the next immediately available vertex in this path.
  3321.          *
  3322.          * @return The next walkable <code>RSTile</code>.
  3323.          */
  3324.         public RSTile getNext() {
  3325.             if (subdividedPath.size() > 0) {
  3326.                 for (int i = subdividedPath.size() - 1; i >= 0; i--)
  3327.                     if (calc.tileOnMap(subdividedPath.get(i)))
  3328.                         return subdividedPath.get(i);
  3329.                 return getTileOnMinimap(getNearest());
  3330.             } else
  3331.                 return null;
  3332.         }
  3333.  
  3334.         public RSTile getTileOnMinimap(RSTile base) {
  3335.             if (calc.tileOnMap(base))
  3336.                 return base;
  3337.             RSTile me = getMyPlayer().getLocation();
  3338.             int deltaX = base.getX() - me.getX();
  3339.             int deltaY = base.getY() - me.getY();
  3340.             double dist = calc.distanceBetween(base, me);
  3341.             double eX = deltaX / dist, eY = deltaY / dist;
  3342.             return new RSTile(me.getX() + (int) (eX * 14), me.getY()
  3343.                     + (int) (eY * 14));
  3344.         }
  3345.  
  3346.         public RSTile getNearest() {
  3347.             int len = Integer.MAX_VALUE;
  3348.             RSTile best = null;
  3349.             for (RSTile t : subdividedPath)
  3350.                 if (calc.distanceTo(t) < len) {
  3351.                     best = t;
  3352.                     len = calc.distanceTo(t);
  3353.                 }
  3354.             return best;
  3355.         }
  3356.  
  3357.         public RSTile getNearestByPath() {
  3358.             int len = Integer.MAX_VALUE;
  3359.             RSTile best = null;
  3360.             for (RSTile t : subdividedPath) {
  3361.                 int pLen = calc.pathLengthTo(t, false);
  3362.                 if (pLen < len && pLen >= 0) {
  3363.                     best = t;
  3364.                     len = pLen;
  3365.                 }
  3366.             }
  3367.             return best;
  3368.         }
  3369.  
  3370.         public int getNearestIndexTo(RSTile t) {
  3371.             int len = Integer.MAX_VALUE;
  3372.             int bestIndex = -1;
  3373.             if (walking.getDestination() != null)
  3374.                 for (int i = 0; i < subdividedPath.size(); i++)
  3375.                     if (calc.distanceBetween(t, subdividedPath.get(i)) < len) {
  3376.                         bestIndex = i;
  3377.                         len = calc.distanceTo(t);
  3378.                     }
  3379.             return bestIndex;
  3380.         }
  3381.  
  3382.         /**
  3383.          * Gets the end tile of this path.
  3384.          *
  3385.          * @return The end <code>RSTile</code>.
  3386.          */
  3387.         public RSTile getEnd() {
  3388.             if (subdividedPath.size() > 0)
  3389.                 return subdividedPath.get(subdividedPath.size() - 1);
  3390.             else
  3391.                 return null;
  3392.         }
  3393.  
  3394.         /**
  3395.          * Reverses this mesh.
  3396.          *
  3397.          * @return
  3398.          *
  3399.          * @return This path.
  3400.          */
  3401.         public MeshPath reverse() {
  3402.             MeshPath newPath = new MeshPath();
  3403.             for (int i = mesh.size() - 1; i >= 0; i--)
  3404.                 newPath.addLine(mesh.get(i));
  3405.             return newPath;
  3406.         }
  3407.  
  3408.         /**
  3409.          * Returns an array containing all of the vertices in this path.
  3410.          *
  3411.          * @return an array containing all of the vertices in this path.
  3412.          */
  3413.         public RSTile[] getTilePath() {
  3414.             RSTile[] tiles = new RSTile[subdividedPath.size()];
  3415.             for (int i = 0; i < tiles.length; i++)
  3416.                 tiles[i] = new RSTile(subdividedPath.get(i).getX(),
  3417.                         subdividedPath.get(i).getY());
  3418.             return tiles;
  3419.         }
  3420.  
  3421.         /**
  3422.          * Checks if we have a path
  3423.          *
  3424.          * @return boolean has a path
  3425.          */
  3426.         public boolean hasPath() {
  3427.             return subdividedPath.size() > 0;
  3428.         }
  3429.     }
  3430.  
  3431.     /**
  3432.      * The different states the NPC contact process can be in
  3433.      *
  3434.      * @author Equinox_
  3435.      *
  3436.      */
  3437.     enum npcContactState {
  3438.         CAST, SCROLL, SELECT, WAIT, CONTINUE, MENU, WITHDRAW_ASTRAL, WITHDRAW_COSMIC, WITHDRAW_AIR, OUT_OF_RUNES
  3439.     }
  3440.  
  3441.     /**
  3442.      * A class to contact people using NPC contact
  3443.      *
  3444.      * @author Equinox_
  3445.      *
  3446.      */
  3447.     class NPCContact {
  3448.         public npcContactState currentState = npcContactState.WAIT;
  3449.         private final int MAIN_FRAME = 88;
  3450.         private final int SCROLL_BAR = 20;
  3451.         private final int SCROLL_BAR_DOWN = 0;
  3452.         private final int VISIBLE_NPCS_CONTAINER = 21;
  3453.         private final int NPCS_CONTAINER = 22;
  3454.         private final int NPC_CONTACT_ANIM = 4413;
  3455.  
  3456.         private final int NPC_TO_SELECT = 14;
  3457.         private final String[] MENU_ITEM = { "repair", "pouch" };
  3458.  
  3459.         /**
  3460.          * Gets the current npcContactState
  3461.          *
  3462.          * @return the current npcContactState
  3463.          */
  3464.         private npcContactState getState() {
  3465.             if (interfaces.get(MAIN_FRAME).isValid()) {
  3466.                 if (interfaces.get(MAIN_FRAME).getComponent(NPCS_CONTAINER)
  3467.                         .isValid()) {
  3468.                     Point npcPoint = interfaces.get(MAIN_FRAME)
  3469.                             .getComponent(NPCS_CONTAINER)
  3470.                             .getComponent(NPC_TO_SELECT).getCenter();
  3471.                     Rectangle visible = interfaces.get(MAIN_FRAME)
  3472.                             .getComponent(VISIBLE_NPCS_CONTAINER).getArea();
  3473.                     if (!visible.contains(npcPoint))
  3474.                         return npcContactState.SCROLL;
  3475.                     else
  3476.                         return npcContactState.SELECT;
  3477.                 }
  3478.             }
  3479.  
  3480.             if (interfaces.canContinue())
  3481.                 return npcContactState.CONTINUE;
  3482.  
  3483.             if (getComponent(MENU_ITEM) != null)
  3484.                 return npcContactState.MENU;
  3485.  
  3486.             if (!interfaces.canContinue()
  3487.                     && getMyPlayer().getAnimation() != NPC_CONTACT_ANIM
  3488.                     && getComponent(MENU_ITEM) == null
  3489.                     && getComponent("lease wai") == null) {
  3490.                 if (!inventory.contains(ASTRAL_RUNE_ID))
  3491.                     return npcContactState.WITHDRAW_ASTRAL;
  3492.                 if (!inventory.contains(COSMIC_RUNE_ID))
  3493.                     return npcContactState.WITHDRAW_COSMIC;
  3494.                 if (inventory.getCount(true, AIR_RUNE_ID) < 2
  3495.                         && !Options.hasAirStaff)
  3496.                     return npcContactState.WITHDRAW_AIR;
  3497.                 return npcContactState.CAST;
  3498.             }
  3499.             return npcContactState.WAIT;
  3500.         }
  3501.  
  3502.         /**
  3503.          * Finds a RSComponent with the specified string
  3504.          *
  3505.          * @param s
  3506.          *            The String to search
  3507.          * @return RSComponent with string, otherwise null
  3508.          */
  3509.         private RSComponent getComponent(String... s) {
  3510.             RSInterface[] valid = interfaces.getAll();
  3511.             for (RSInterface iface : valid) {
  3512.                 if (iface.getIndex() != 137) {
  3513.                     int len = iface.getChildCount();
  3514.                     for (int i = 0; i < len; i++) {
  3515.                         RSComponent child = iface.getComponent(i);
  3516.                         boolean b = (child.isValid()
  3517.                                 && child.getAbsoluteX() > 10 && child
  3518.                                 .getAbsoluteY() > 300);
  3519.                         for (String sub : s)
  3520.                             if (!child.getText().toLowerCase()
  3521.                                     .contains(sub.toLowerCase()))
  3522.                                 b = false;
  3523.                         if (b)
  3524.                             return child;
  3525.                     }
  3526.                 }
  3527.             }
  3528.             return null;
  3529.         }
  3530.  
  3531.         /**
  3532.          * Checks if the inventory or bank contains the specified item
  3533.          *
  3534.          * @param id
  3535.          *            the item id
  3536.          * @return <tt>true</tt> if the bank or inventory contains the item,
  3537.          *         otherwise, <tt>false</tt>
  3538.          */
  3539.         boolean invBank(int id) {
  3540.             return bank.getCount(id) > 0 || inventory.containsOneOf(id);
  3541.         }
  3542.  
  3543.         /**
  3544.          * Gets the count of a specified item in the inventory and bank
  3545.          *
  3546.          * @param int itemId
  3547.          * @return the item count
  3548.          */
  3549.         int invBankCount(int id) {
  3550.             return bank.getCount(id) + inventory.getCount(true, id);
  3551.         }
  3552.  
  3553.         /**
  3554.          * The main loop. Call this to do the next action for the contact
  3555.          * process.
  3556.          */
  3557.         public void main() {
  3558.             switch (currentState = getState()) {
  3559.             case WITHDRAW_ASTRAL:
  3560.                 withdrawItem(ASTRAL_RUNE_ID, 1);
  3561.                 if (!invBank(ASTRAL_RUNE_ID))
  3562.                     removePouches();
  3563.                 break;
  3564.             case WITHDRAW_COSMIC:
  3565.                 withdrawItem(COSMIC_RUNE_ID, 1);
  3566.                 if (!invBank(COSMIC_RUNE_ID))
  3567.                     removePouches();
  3568.                 break;
  3569.             case WITHDRAW_AIR:
  3570.                 if (!inventory.contains(AIR_RUNE_ID)) {
  3571.                     if (inventory.isFull())
  3572.                         bank.deposit(PURE_ESS_ID, 1);
  3573.                     withdrawItem(AIR_RUNE_ID, 1);
  3574.                 }
  3575.                 withdrawItem(AIR_RUNE_ID, 1);
  3576.                 if (invBankCount(AIR_RUNE_ID) < 2)
  3577.                     removePouches();
  3578.                 break;
  3579.             case CAST:
  3580.                 for (byte b = 0; b < 5; b++)
  3581.                     if (bank.isOpen()) {
  3582.                         bank.close();
  3583.                         for (byte b2 = 0; b2 < 25; b2++)
  3584.                             if (bank.isOpen())
  3585.                                 break;
  3586.                             else
  3587.                                 sleep(95, 115);
  3588.                     }
  3589.                 if (game.getCurrentTab() != Game.TAB_MAGIC)
  3590.                     game.openTab(Game.TAB_MAGIC);
  3591.  
  3592.                 sleep(500, 1250);
  3593.                 magic.castSpell(Magic.SPELL_NPC_CONTACT);
  3594.                 sleep(750, 1250);
  3595.                 break;
  3596.             case SCROLL:
  3597.                 // Scroll
  3598.                 RSComponent bar = interfaces.get(MAIN_FRAME)
  3599.                         .getComponent(SCROLL_BAR).getComponent(SCROLL_BAR_DOWN);
  3600.                 Point p = bar.getCenter();
  3601.                 p.y = bar.getAbsoluteY() + bar.getHeight() - 5;
  3602.                 mouse.click(p, true);
  3603.                 break;
  3604.             case SELECT:
  3605.                 interfaces.get(MAIN_FRAME).getComponent(NPCS_CONTAINER)
  3606.                         .getComponent(NPC_TO_SELECT).doClick();
  3607.                 break;
  3608.             case WAIT:
  3609.                 break;
  3610.             case CONTINUE:
  3611.                 if (interfaces.canContinue())
  3612.                     interfaces.clickContinue();
  3613.                 break;
  3614.             case MENU:
  3615.                 if (getComponent(MENU_ITEM) != null)
  3616.                     getComponent(MENU_ITEM).doClick();
  3617.                 break;
  3618.             }
  3619.             sleep(900, 1200);
  3620.         }
  3621.  
  3622.         public void removePouches() {
  3623.             if (!openBank())
  3624.                 return;
  3625.             // not enough runes, bank pouches:
  3626.             for (int id : Pouch.ALL_DEGRADED)
  3627.                 if (inventory.contains(id)) {
  3628.                     if (bank.deposit(id, 1))
  3629.                         sleep(500, 600);
  3630.                 }
  3631.             updatePouches();
  3632.         }
  3633.     }
  3634.  
  3635.     /**
  3636.      * Loads script data as the actual script runs. Useful for images that are
  3637.      * non-essential.
  3638.      *
  3639.      * @author Equinox_
  3640.      *
  3641.      */
  3642.     public static class DataLoader extends Thread {
  3643.         EquinoxZMICrafter crafter;
  3644.  
  3645.         public DataLoader(EquinoxZMICrafter crafter) {
  3646.             super("Data Loader");
  3647.             this.crafter = crafter;
  3648.             this.start();
  3649.         }
  3650.  
  3651.         // Item Info
  3652.         public void checkGE() {
  3653.             long before = System.currentTimeMillis();
  3654.             GEItem item = crafter.getGE().lookup(PURE_ESS_ID);
  3655.             if (item != null) {
  3656.                 crafter.Paint.essPrice = getItemPrice(item);
  3657.                 crafter.log("Price of " + item.getName() + ": "
  3658.                         + getItemPrice(item));
  3659.                 crafter.log("Loaded essence information in "
  3660.                         + PaintFormatter.getFormattedTime(System
  3661.                                 .currentTimeMillis() - before));
  3662.             }
  3663.         }
  3664.  
  3665.         public void run() {
  3666.             checkGE();
  3667.             try {
  3668.                 if (JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(
  3669.                         null, "Would you like to load some images?",
  3670.                         "Equinox_ Data Loader", JOptionPane.YES_NO_OPTION))
  3671.                     throw new Exception();
  3672.  
  3673.                 long before = System.currentTimeMillis();
  3674.                 crafter.Paint.putImage(
  3675.                         "homeicon",
  3676.                         ImageIO.read(new URL(crafter.baseURL
  3677.                                 + "/images/houseIcon.png")));
  3678.                 crafter.log("Loaded home image in "
  3679.                         + PaintFormatter.getFormattedTime(System
  3680.                                 .currentTimeMillis() - before));
  3681.  
  3682.                 before = System.currentTimeMillis();
  3683.                 crafter.Paint
  3684.                         .putImage(
  3685.                                 "rcicon",
  3686.                                 ImageIO.read(new URL(
  3687.                                         "http://www.runescape.com/img/hiscores/skill_icon_Runecrafting1.gif")));
  3688.                 crafter.log("Loaded runecrafting image in "
  3689.                         + PaintFormatter.getFormattedTime(System
  3690.                                 .currentTimeMillis() - before));
  3691.  
  3692.                 before = System.currentTimeMillis();
  3693.                 crafter.Paint.putImage(
  3694.                         "toolsicon",
  3695.                         ImageIO.read(new URL(crafter.baseURL
  3696.                                 + "/images/tools.png")));
  3697.                 crafter.log("Loaded tools icon in "
  3698.                         + PaintFormatter.getFormattedTime(System
  3699.                                 .currentTimeMillis() - before));
  3700.             } catch (Exception e) {
  3701.                 crafter.log("Failed to load images");
  3702.             }
  3703.         }
  3704.     }
  3705.  
  3706.     /**
  3707.      * Gets the grandExchange singleton for reference by other classes
  3708.      *
  3709.      * @return GrandExchange reference
  3710.      */
  3711.     public GrandExchange getGE() {
  3712.         return grandExchange;
  3713.     }
  3714.  
  3715.     /**
  3716.      * gets the item price based on class. Works with all versions of
  3717.      * grandExchange.class
  3718.      *
  3719.      * @param item
  3720.      *            the GEItem to get the price from
  3721.      * @return int the price
  3722.      *
  3723.      * @author Equinox_
  3724.      */
  3725.     public static int getItemPrice(GEItem item) {
  3726.         for (Method m : item.getClass().getMethods())
  3727.             if (m.getName().equals("getGuidePrice")
  3728.                     || m.getName().equals("getMinPrice"))
  3729.                 try {
  3730.                     return (Integer) (m.invoke(item));
  3731.                 } catch (Exception e) {
  3732.                 }
  3733.         return 0;
  3734.     }
  3735.  
  3736.     /**
  3737.      * Modified Copy of DrawMouse
  3738.      */
  3739.     public class ModifiedDrawMouse {
  3740.         private MouseTrailing mouseTrailing = new MouseTrailing();
  3741.         private ArrayList<Mouses> Click = new ArrayList<Mouses>();
  3742.         private long clickLifeTime = 1500;
  3743.  
  3744.         public void onRepaint(final Graphics render) {
  3745.             if (mouse != null) {
  3746.                 Point Location = mouse.getLocation();
  3747.                 if (mouse.isPressed())
  3748.                     if (Click.size() <= 5)
  3749.                         Click.add(new Mouses(System.currentTimeMillis(),
  3750.                                 Location));
  3751.                 mouseTrailing.draw((Graphics2D) render, Color.WHITE);
  3752.                 render.setColor(Color.GREEN);
  3753.                 render.drawLine(Location.x - 5, Location.y, Location.x + 5,
  3754.                         Location.y);
  3755.                 render.drawLine(Location.x, Location.y - 5, Location.x,
  3756.                         Location.y + 5);
  3757.                 if (Click.size() > 1)
  3758.                     for (Mouses click : Click) {
  3759.                         if (mouse.isPressed())
  3760.                             if (Click.size() <= 10)
  3761.                                 Click.add(new Mouses(
  3762.                                         System.currentTimeMillis(), Location));
  3763.                         float i = ((float) (click.getTime() + clickLifeTime - System
  3764.                                 .currentTimeMillis()) / (float) clickLifeTime) + 0.25f;
  3765.                         if (i > 0) {
  3766.                             render.setColor(new Color(1, 0, 0, i > 1 ? 1 : i));
  3767.                             render.drawLine(click.getLocation().x - 5,
  3768.                                     click.getLocation().y,
  3769.                                     click.getLocation().x + 5,
  3770.                                     click.getLocation().y);
  3771.                             render.drawLine(click.getLocation().x,
  3772.                                     click.getLocation().y - 5,
  3773.                                     click.getLocation().x,
  3774.                                     click.getLocation().y + 5);
  3775.                         } else if (System.currentTimeMillis() - click.getTime() >= 1000) {
  3776.                             Click.remove(click);
  3777.                         }
  3778.                     }
  3779.  
  3780.             }
  3781.         }
  3782.  
  3783.         /**
  3784.          * @author Baheer (Doout).
  3785.          */
  3786.         public class MouseTrailing {
  3787.             private int LifeTime = 1500;
  3788.             private ArrayList<Mouses> Info = new ArrayList<Mouses>();
  3789.  
  3790.             private void getLocation() {
  3791.                 Point m = mouse.getLocation();
  3792.                 if (Add(m)) {
  3793.                     Info.add(new Mouses(System.currentTimeMillis(), m));
  3794.                 }
  3795.             }
  3796.  
  3797.             public boolean Add(Point p) {
  3798.                 for (Mouses Trailing : Info)
  3799.                     if (Trailing.getLocation().equals(p))
  3800.                         return false;
  3801.                 return true;
  3802.             }
  3803.  
  3804.             public void draw(Graphics2D g, Color c) {
  3805.                 getLocation();
  3806.                 if (Info.size() > 1)
  3807.                     for (int t = 1; t < Info.size(); t++) {
  3808.                         Mouses Trailing1 = Info.get(t);
  3809.                         Mouses Trailing2 = Info.get(t - 1);
  3810.                         float i = (float) (Trailing1.getTime() + LifeTime - System
  3811.                                 .currentTimeMillis()) / (float) LifeTime;
  3812.                         if (i > 0) {
  3813.                             g.setColor(new Color(c.getRed() / 255f, c
  3814.                                     .getGreen() / 255f, c.getBlue() / 255f, (c
  3815.                                     .getAlpha() / 255f) * i));
  3816.                             g.drawLine(Trailing1.getLocation().x,
  3817.                                     Trailing1.getLocation().y,
  3818.                                     Trailing2.getLocation().x,
  3819.                                     Trailing2.getLocation().y);
  3820.                         }
  3821.                     }
  3822.                 if (Info.size() > 1) {
  3823.                     if (System.currentTimeMillis() > Info.get(0).getTime()
  3824.                             + LifeTime) {
  3825.                         Info.remove(0);
  3826.                     }
  3827.                 }
  3828.             }
  3829.  
  3830.         }
  3831.  
  3832.         private class Mouses {
  3833.             private long Time;
  3834.             private Point Location;
  3835.  
  3836.             public Mouses(long time, Point loc) {
  3837.                 Time = time;
  3838.                 Location = loc;
  3839.             }
  3840.  
  3841.             public long getTime() {
  3842.                 return Time;
  3843.             }
  3844.  
  3845.             public Point getLocation() {
  3846.                 return Location;
  3847.             }
  3848.         }
  3849.     }
  3850. }
Advertisement
Add Comment
Please, Sign In to add comment