Equinox

EquinoxGraahkCrafter.java

Apr 16th, 2011
4,183
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 148.35 KB | None | 0 0
  1. import java.awt.Color;
  2.  
  3. @ScriptManifest(authors = "Equinox_", keywords = { "Runecrafting", "Graahk",
  4.         "Nature", "Karamja" }, name = "EquinoxGraahkCrafter", version = 1.4, description = "Nature Runecrafter with Graahks, pouches and more!")
  5. /**
  6.  * @author Equinox_
  7.  */
  8. public class EquinoxGraahkCrafter extends Script implements MessageListener,
  9.         PaintListener, MouseMotionListener, MouseListener {
  10.  
  11.     // Variables
  12.     final String baseURL = "http://equinox-scripts.web44.net/scripts/natcrafter";
  13.     public final static int NATURE_RUNE_ID = 561;
  14.     public final static int COSMIC_RUNE_ID = 564;
  15.     public final static int ASTRAL_RUNE_ID = 9075;
  16.     public static final int AIR_STAFF_ID = 1405;
  17.     public final static int PURE_ESS_ID = 7936;
  18.     public final static int NATURE_PORTAL_ID = 2460;
  19.     public final static int NATURE_ALTER_ID = 2486;
  20.     public final static int AIR_RUNE_ID = 556;
  21.     public final static int SPIRIT_GRAAHK_POUCH_ID = 12810;
  22.     public final static int OBELISK_OBJ_ID = 29938;
  23.     public final static int KINSHIP_TELEPORT_ANIM = 13652;
  24.     public final static int[] GRAAHK_TELEPORT_ANIM = { 8939, 8941 };
  25.     public final static int NPC_CONTACT_ANIM = 4413;
  26.     public final static int BANKER_ID = 9710;
  27.     public final static int CRAFT_RUNE_ANIM = 791;
  28.     public final static int DUELING_TELEPORT_ANIM = 9603;
  29.  
  30.     public final static String getChargeCountAsString(String s) {
  31.         s = s.trim().toLowerCase();
  32.         if (s != null && s.contains("(") && s.contains(")"))
  33.             return s.substring(s.indexOf("(") + 1, s.indexOf(")"));
  34.         return "0";
  35.     }
  36.  
  37.     public final static int getChargeCount(String s) {
  38.         try {
  39.             return Integer.valueOf(getChargeCountAsString(s));
  40.         } catch (Exception e) {
  41.             return 0;
  42.         }
  43.     }
  44.  
  45.     public final static RSTile[][] DAEMONHEIM_MESH = {
  46.             { new RSTile(3453, 3897), new RSTile(3447, 3897) },
  47.             { new RSTile(3448, 3702), new RSTile(3451, 3702) },
  48.             { new RSTile(3451, 3707), new RSTile(3448, 3707) },
  49.             { new RSTile(3448, 3711), new RSTile(3451, 3711) },
  50.             { new RSTile(3451, 3716), new RSTile(3448, 3716) },
  51.             { new RSTile(3448, 3719), new RSTile(3449, 3719),
  52.                     new RSTile(3449, 3718) } };
  53.  
  54.     public final static RSTile[][] KARAMJA_MESH = {
  55.             { new RSTile(2793, 3009), new RSTile(2793, 3009) },
  56.             { new RSTile(2798, 3009), new RSTile(2796, 3008) },
  57.             { new RSTile(2808, 3007), new RSTile(2809, 3013) },
  58.             { new RSTile(2815, 3013), new RSTile(2815, 3007) },
  59.             { new RSTile(2822, 3008), new RSTile(2823, 3013) },
  60.             { new RSTile(2829, 3016), new RSTile(2834, 3009) },
  61.             { new RSTile(2838, 3017), new RSTile(2831, 3021) },
  62.             { new RSTile(2837, 3028), new RSTile(2842, 3025) },
  63.             { new RSTile(2847, 3025), new RSTile(2847, 3030) },
  64.             { new RSTile(2852, 3030), new RSTile(2852, 3025) },
  65.             { new RSTile(2855, 3027), new RSTile(2856, 3031) },
  66.             { new RSTile(2861, 3029), new RSTile(2859, 3024) },
  67.             { new RSTile(2864, 3017), new RSTile(2864, 3023) },
  68.             { new RSTile(2869, 3021), new RSTile(2866, 3021),
  69.                     new RSTile(2866, 3017), new RSTile(2869, 3017) } };
  70.  
  71.     public final static int POUCH_TRIES = 20;
  72.  
  73.     // Pouch IDs
  74.     public static class Pouch {
  75.         public final static int SMALL = 5509;
  76.         public final static int MEDIUM = 5510;
  77.         public final static int MEDIUM_DEGRADED = 5511;
  78.         public final static int LARGE = 5512;
  79.         public final static int LARGE_DEGRADED = 5513;
  80.         public final static int GIANT = 5514;
  81.         public final static int GIANT_DEGRADED = 5515;
  82.         public final static int[] ALL = { SMALL, MEDIUM, MEDIUM_DEGRADED,
  83.                 LARGE, LARGE_DEGRADED, GIANT, GIANT_DEGRADED };
  84.         public final static int[] ALL_REPAIRED = { SMALL, MEDIUM, LARGE, GIANT };
  85.         public final static int[] ALL_DEGRADED = { MEDIUM_DEGRADED,
  86.                 LARGE_DEGRADED, GIANT_DEGRADED };
  87.         public final static int[] ALL_DEGRADEABLE = { MEDIUM, MEDIUM_DEGRADED,
  88.                 LARGE, LARGE_DEGRADED, GIANT, GIANT_DEGRADED };
  89.     }
  90.  
  91.     public static String serverMsg = "";
  92.     public HashMap<PouchType, PouchState> pouches = new HashMap<PouchType, PouchState>();
  93.     State currentState = State.LOST;
  94.     public MeshPath karamjaMeshPath;
  95.     public MeshPath daemonheimMeshPath;
  96.     public PaintInterface Paint;
  97.     public NPCContact npcContact = new NPCContact();
  98.     public List<String> errorLog = new ArrayList<String>();
  99.     public ModelMouse modelMouse;
  100.     public int ringCharges = -1;
  101.     public OptionsClass Options = new OptionsClass();
  102.     public CameraControl camera;
  103.  
  104.     public static class OptionsClass {
  105.         public Range mouseSpeed = new Range(4, 6);
  106.         public pouchRepairOption pouchRepair = pouchRepairOption.ONE;
  107.         public boolean hasAirStaff = true;
  108.         public Range summoningRecharge = new Range(20, 25);
  109.         public PouchCheckSystem pouchCheckSystem = PouchCheckSystem.SERVER_MSG;
  110.         public Range walkingDistance = new Range(4, 6);
  111.         public boolean save1ChargeRings = true;
  112.         public TeleportSystem teleportSystem = TeleportSystem.DAEMONHEIM;
  113.  
  114.         /**
  115.          * Teleport system
  116.          *
  117.          * @author Equinox
  118.          */
  119.         enum TeleportSystem {
  120.             CASTLE_WARS, DAEMONHEIM
  121.         }
  122.  
  123.         /**
  124.          * When to repair pouches
  125.          *
  126.          * @author Equinox_
  127.          *
  128.          */
  129.         public enum pouchRepairOption {
  130.             ONE, TWO, ALL, NEVER
  131.         }
  132.  
  133.         /**
  134.          * How to determine a pouch's state
  135.          *
  136.          * @author Equinox_
  137.          *
  138.          */
  139.         public enum PouchCheckSystem {
  140.             SERVER_MSG, INVENTORY_STATUS
  141.         }
  142.     }
  143.  
  144.     /**
  145.      * Possible states in the process of nature runecrafting
  146.      *
  147.      * @author Equinox_
  148.      *
  149.      */
  150.     public enum State {
  151.         TRAVERSE_DAEMONHEIM, BANK_WITHDRAW_GRAAHK, SUMMON_GRAAHK, DISMISS_WRONG_FAMILIAR, BANK_WITHDRAW_ESS, BANK_DEPOSIT_STUFF, FILL_POUCH, NPC_CONTACT, TELE_TO_KARAMJA, TRAVERSE_KARAMJA, CHARGE_SUMMONING, ENTER_PORTAL, CRAFT_ESSENCE, EMPTY_POUCH, TELE_TO_BANK, REMOVE_RING, EQUIP_RING, NEAR_BANK, LOST, WITHDRAW_RING
  152.     }
  153.  
  154.     /**
  155.      * Possible states of pouches, Empty or Full
  156.      *
  157.      * @author Equinox_
  158.      *
  159.      */
  160.     public enum PouchState {
  161.         Empty, Full
  162.     }
  163.  
  164.     /**
  165.      * Different pouches and their possible IDs
  166.      *
  167.      * @author Equinox_
  168.      *
  169.      */
  170.     public enum PouchType {
  171.         Small(Pouch.SMALL), Medium(Pouch.MEDIUM, Pouch.MEDIUM_DEGRADED), Large(
  172.                 Pouch.LARGE, Pouch.LARGE_DEGRADED), Giant(Pouch.GIANT,
  173.                 Pouch.GIANT_DEGRADED);
  174.         public int[] ids;
  175.  
  176.         private PouchType(int... ids) {
  177.             this.ids = ids;
  178.         }
  179.     }
  180.  
  181.     RSArea NATURE_ALTER = new RSArea(2390, 4835, 2410, 4850);
  182.     RSArea KARAMJA_TRAVEL = new RSArea(2784, 2998, 2872, 3040);
  183.     RSArea DAEMONHEIM_TRAVEL = new RSArea(3435, 3680, 3456, 3726);
  184.  
  185.     /**
  186.      * Gets the current state
  187.      *
  188.      * @return State the current state
  189.      * @throws Exception
  190.      *             so we can pipe errors back to the recorder
  191.      */
  192.     public State getState() throws Exception {
  193.         if (objects.getNearest(OBELISK_OBJ_ID) != null
  194.                 && summoning.getSummoningPoints() <= Options.summoningRecharge
  195.                         .randomNormalDistribution())
  196.             if (calc.tileOnMap(objects.getNearest(OBELISK_OBJ_ID).getLocation()))
  197.                 return State.CHARGE_SUMMONING;
  198.  
  199.         if (objects.getNearest(NATURE_PORTAL_ID) != null)
  200.             if (calc.tileOnMap(objects.getNearest(NATURE_PORTAL_ID)
  201.                     .getLocation()))
  202.                 return State.ENTER_PORTAL;
  203.  
  204.         if (objects.getNearest(NATURE_ALTER_ID) != null)
  205.             if (calc.tileOnMap(objects.getNearest(NATURE_ALTER_ID)
  206.                     .getLocation()))
  207.                 if ((inventory.isFull() || !pouches
  208.                         .containsValue(PouchState.Full))
  209.                         && inventory.contains(PURE_ESS_ID))
  210.                     return State.CRAFT_ESSENCE;
  211.                 else if (pouches.containsValue(PouchState.Full))
  212.                     return State.EMPTY_POUCH;
  213.                 else if (!pouches.containsValue(PouchState.Full)
  214.                         && !inventory.contains(PURE_ESS_ID))
  215.                     return State.TELE_TO_BANK;
  216.  
  217.         // Banking
  218.         if (npcs.getNearest(Bank.BANKERS) != null
  219.                 || objects.getNearest(Bank.BANK_CHESTS) != null) {
  220.             if ((npcs.getNearest(Bank.BANKERS) != null && npcs.getNearest(
  221.                     Bank.BANKERS).isOnScreen())
  222.                     || (objects.getNearest(Bank.BANK_CHESTS) != null && objects
  223.                             .getNearest(Bank.BANK_CHESTS).isOnScreen())) {
  224.                 // Familiar
  225.                 if (summoning.isFamiliarSummoned()) {
  226.                     if (summoning.getFamiliar() != null)
  227.                         if (!summoning.getFamiliar().getName().toLowerCase()
  228.                                 .contains("graahk"))
  229.                             return State.DISMISS_WRONG_FAMILIAR;
  230.                 } else if (!inventory.contains(SPIRIT_GRAAHK_POUCH_ID))
  231.                     return State.BANK_WITHDRAW_GRAAHK;
  232.                 else
  233.                     return State.SUMMON_GRAAHK;
  234.  
  235.                 // Pouch Repair
  236.                 if (shouldRepairPouches())
  237.                     return State.NPC_CONTACT;
  238.                 if (ringCharges <= (Options.save1ChargeRings ? 1 : 0)
  239.                         && Options.teleportSystem
  240.                                 .equals(OptionsClass.TeleportSystem.CASTLE_WARS)) {
  241.                     if (Options.save1ChargeRings) {
  242.                         boolean hasChargedRing = false;
  243.                         for (int i = 2; i <= 8; i++)
  244.                             if (inventory.getItemID("Ring of Dueling (" + i
  245.                                     + ")") >= 0) {
  246.                                 hasChargedRing = true;
  247.                                 break;
  248.                             }
  249.                         for (int i = 2; i <= 8; i++)
  250.                             if (inventory.getItemID("Ring of Dueling (" + i
  251.                                     + ")") == -1
  252.                                     && inventory
  253.                                             .getItemID("Ring of Dueling (1)") >= 0
  254.                                     && !hasChargedRing)
  255.                                 return State.WITHDRAW_RING;
  256.                             else if (inventory.getItemID("Ring of Dueling ("
  257.                                     + i + ")") >= 0
  258.                                     && inventory
  259.                                             .getItemID("Ring of Dueling (1)") == -1)
  260.                                 return State.EQUIP_RING;
  261.                         if (inventory.getItemID("Ring of Dueling (1)") == -1)
  262.                             return State.REMOVE_RING;
  263.                         if (inventory.getItemID("Ring of Dueling (1)") >= 0)
  264.                             return State.BANK_DEPOSIT_STUFF;
  265.                     } else {
  266.                         boolean hasChargedRing = false;
  267.                         for (int i = 2; i <= 8; i++)
  268.                             if (inventory.getItemID("Ring of Dueling (" + i
  269.                                     + ")") >= 0) {
  270.                                 hasChargedRing = true;
  271.                                 break;
  272.                             }
  273.                         for (int i = 2; i <= 8; i++)
  274.                             if (inventory.getItemID("Ring of Dueling (" + i
  275.                                     + ")") == -1
  276.                                     && !hasChargedRing)
  277.                                 return State.WITHDRAW_RING;
  278.                             else if (inventory.getItemID("Ring of Dueling ("
  279.                                     + i + ")") >= 0)
  280.                                 return State.EQUIP_RING;
  281.                     }
  282.                 }
  283.                 if (inventory.getCount(Pouch.ALL)
  284.                         + inventory.getCount(PURE_ESS_ID) != inventory
  285.                         .getCount())
  286.                     return State.BANK_DEPOSIT_STUFF;
  287.  
  288.                 if (inventory.contains(PURE_ESS_ID)
  289.                         && pouches.containsValue(PouchState.Empty))
  290.                     return State.FILL_POUCH;
  291.  
  292.                 if (!inventory.isFull())
  293.                     return State.BANK_WITHDRAW_ESS;
  294.             } else {
  295.                 return State.NEAR_BANK;
  296.             }
  297.         }
  298.  
  299.         if (KARAMJA_TRAVEL.contains(getMyPlayer().getLocation()))
  300.             return State.TRAVERSE_KARAMJA;
  301.  
  302.         if (inventory.isFull() && inventory.contains(PURE_ESS_ID))
  303.             return State.TELE_TO_KARAMJA;
  304.  
  305.         if (DAEMONHEIM_TRAVEL.contains(getMyPlayer().getLocation()))
  306.             return State.TRAVERSE_DAEMONHEIM;
  307.  
  308.         return State.LOST;
  309.     }
  310.  
  311.     /**
  312.      * Internal use only. When the client receives a message this method is
  313.      * invoked, saving the message for later use.
  314.      */
  315.     public void messageReceived(MessageEvent msg) {
  316.         if (msg.getID() == MessageEvent.MESSAGE_ACTION
  317.                 || msg.getID() == MessageEvent.MESSAGE_SERVER) {
  318.             serverMsg = msg.getMessage().toLowerCase();
  319.         }
  320.     }
  321.  
  322.     /**
  323.      * Teleports to Karamja using the summoning bubble to interact with a
  324.      * summoned graahk
  325.      *
  326.      * @throws Exception
  327.      *             to pipe the Exception back to the logger
  328.      */
  329.     public void teleportToKaramja() throws Exception {
  330.         daemonheimMeshPath.clearPath();
  331.         if (summoning.doAction("Interact")) {
  332.             for (byte b = 0; b < 15; b++)
  333.                 if (interfaces.getComponent(228, 3) != null
  334.                         && interfaces.getComponent(228, 3).isValid())
  335.                     break;
  336.                 else
  337.                     sleep(100, 125);
  338.  
  339.             if (interfaces.getComponent(228, 3).doClick()) {
  340.                 sleep(1000, 1250);
  341.                 for (byte b = 0; b < 15
  342.                         && players.getMyPlayer().getAnimation() == GRAAHK_TELEPORT_ANIM[0]; b++)
  343.                     sleep(125, 175);
  344.                 return;
  345.             }
  346.         }
  347.     }
  348.  
  349.     /**
  350.      * Teleports to Daemonheim using an equipped ring of kinship
  351.      *
  352.      * @throws Exception
  353.      *             to pipe the Exception back to the logger
  354.      */
  355.     public void teleportToDaemonheim() throws Exception {
  356.         if (getMyPlayer().getAnimation() != KINSHIP_TELEPORT_ANIM)
  357.             for (byte b = 0; b < 5; b++)
  358.                 if (equipment.getItem(Equipment.RING) != null) {
  359.                     RSComponent comp = equipment.getItem(Equipment.RING)
  360.                             .getComponent();
  361.                     Point pt = comp.getCenter();
  362.                     pt.translate(random(-2, 3), random(-2, 3));
  363.                     mouse.click(pt, false);
  364.                     sleep(500, 750);
  365.                     if (menu.doAction("Teleport")) {
  366.                         for (byte b2 = 0; b2 < 25; b2++)
  367.                             if (getMyPlayer().getAnimation() != KINSHIP_TELEPORT_ANIM)
  368.                                 sleep(100, 115);
  369.                             else {
  370.                                 Paint.runsMade++;
  371.                                 return;
  372.                             }
  373.                     }
  374.                 } else {
  375.                     log.severe("Somehow we are missing a ring :(");
  376.                     stopScript(true);
  377.                 }
  378.     }
  379.  
  380.     /**
  381.      * Teleports to Castlewars using an equipped ring of dueling
  382.      *
  383.      * @throws Exception
  384.      *             to pipe the Exception back to the logger
  385.      */
  386.     public void teleportToCastleWars() throws Exception {
  387.         if (getMyPlayer().getAnimation() != DUELING_TELEPORT_ANIM)
  388.             for (byte b = 0; b < 5; b++)
  389.                 if (equipment.getItem(Equipment.RING) != null) {
  390.                     RSComponent comp = equipment.getItem(Equipment.RING)
  391.                             .getComponent();
  392.                     Point pt = comp.getCenter();
  393.                     pt.translate(random(-2, 3), random(-2, 3));
  394.                     mouse.click(pt, false);
  395.                     sleep(500, 750);
  396.                     if (menu.doAction("Castle-wars")) {
  397.                         for (byte b2 = 0; b2 < 25; b2++)
  398.                             if (getMyPlayer().getAnimation() != DUELING_TELEPORT_ANIM)
  399.                                 sleep(100, 115);
  400.                             else {
  401.                                 Paint.runsMade++;
  402.                                 return;
  403.                             }
  404.                     }
  405.                 } else {
  406.                     log.severe("Somehow we are missing a ring :(");
  407.                     stopScript(true);
  408.                 }
  409.     }
  410.  
  411.     /**
  412.      * Determines if pouches need repairing by using the method defined in the
  413.      * Options class
  414.      *
  415.      * @return boolean should repair pouches
  416.      */
  417.     public boolean shouldRepairPouches() {
  418.         int degradeable = inventory.getCount(Pouch.ALL_DEGRADEABLE);
  419.         int degraded = inventory.getCount(Pouch.ALL_DEGRADED);
  420.         switch (Options.pouchRepair) {
  421.         case ONE:
  422.             return degraded > 0;
  423.         case TWO:
  424.             return degraded > 1 || degradeable < 2;
  425.         case ALL:
  426.             return degradeable == degraded;
  427.         case NEVER:
  428.             return false;
  429.         default:
  430.             return degraded > 1 || degradeable < 2;
  431.         }
  432.     }
  433.  
  434.     /**
  435.      * Tries to open the bank
  436.      *
  437.      * @return boolean did bank open
  438.      */
  439.     public boolean openBank() {
  440.         if (npcs.getNearest(BANKER_ID) != null) {
  441.             if (bank.isOpen())
  442.                 return true;
  443.             bank.open();
  444.             sleep(100, 250);
  445.         }
  446.  
  447.         return false;
  448.     }
  449.  
  450.     /**
  451.      * Deposit all non necessary items.
  452.      */
  453.     public void deposit_non_necessities() {
  454.         if (!openBank())
  455.             return;
  456.         if (inventory.getCount(Pouch.ALL) > 0)
  457.             bank.depositAllExcept(Pouch.ALL);
  458.         else
  459.             bank.depositAll();
  460.     }
  461.  
  462.     /**
  463.      * Tries to open the bank if the bank is not open, then tries to withdraw
  464.      * the item
  465.      *
  466.      * @param id
  467.      *            the item to withdraw
  468.      * @param amount
  469.      *            the amount to withdraw, 0 for all
  470.      * @return boolean successfully withdraw
  471.      */
  472.     public boolean withdrawItem(int id, int amount) {
  473.         if (!openBank())
  474.             return false;
  475.         boolean returns = bank.withdraw(id, amount);
  476.         sleep(500, 750);
  477.         return returns;
  478.     }
  479.  
  480.     /**
  481.      * Withdraws pure essence
  482.      *
  483.      * @return boolean did withdraw item
  484.      */
  485.     public boolean withdrawEss() {
  486.         return withdrawItem(PURE_ESS_ID, 0);
  487.     }
  488.  
  489.     /**
  490.      * Gets the count of items by the provided id(s) in both the bank and
  491.      * inventory. Includes stack sizes
  492.      *
  493.      * @param id
  494.      *            item ids to search
  495.      * @return int number of items in bank and inventory
  496.      */
  497.     public int getCountBankInv(int... id) {
  498.         int count = 0;
  499.         if (bank.isOpen())
  500.             count += bank.getCount(id);
  501.         count += inventory.getCount(true, id);
  502.         return count;
  503.     }
  504.  
  505.     /**
  506.      * Gets the loaded area or the world
  507.      *
  508.      * @return RSArea the local area
  509.      */
  510.     public RSArea getLocalArea() {
  511.         RSTile base = game.getMapBase();
  512.         int plane = game.getPlane();
  513.         int[][] flags = walking.getCollisionFlags(plane);
  514.         return new RSArea(base, new RSTile(base.getX() + flags.length - 1,
  515.                 base.getY() + flags.length - 1), plane);
  516.     }
  517.  
  518.     /**
  519.      * Checks if the given tile is loaded
  520.      *
  521.      * @param t
  522.      *            the RSTile to check
  523.      * @return boolean is loaded
  524.      */
  525.     public boolean isLocal(RSTile t) {
  526.         RSArea local = getLocalArea();
  527.         if (local != null)
  528.             return local.contains(t);
  529.         return false;
  530.     }
  531.  
  532.     /**
  533.      * Empties pouches using the method set in the Options class
  534.      */
  535.     public void emptyPouches() {
  536.         for (PouchType pType : pouches.keySet()) {
  537.             if (inventory.isFull())
  538.                 break;
  539.             if (inventory.containsOneOf(pType.ids)) {
  540.                 if (pouches.get(pType).equals(PouchState.Full)) {
  541.                     RSItem item = inventory.getItem(pType.ids);
  542.                     if (item != null) {
  543.                         int invCount = inventory.getCount();
  544.                         if (item.doAction("Empty"))
  545.                             switch (Options.pouchCheckSystem) {
  546.                             case SERVER_MSG:
  547.                                 for (int i = 0; i < POUCH_TRIES; i++) {
  548.                                     if (serverMsg != null)
  549.                                         if (serverMsg
  550.                                                 .contains("pouch has no ess")
  551.                                                 || serverMsg
  552.                                                         .contains("are no essences in your pouch")) {
  553.                                             pouches.put(pType, PouchState.Empty);
  554.                                             serverMsg = "";
  555.                                             sleep(100, 150);
  556.                                             break;
  557.                                         }
  558.                                     sleep(100, 150);
  559.                                 }
  560.                                 break;
  561.                             case INVENTORY_STATUS:
  562.                                 for (int i = 1; i <= POUCH_TRIES; i++)
  563.                                     if (inventory.getCount() == invCount
  564.                                             && i != POUCH_TRIES)
  565.                                         sleep(100, 150);
  566.                                     else {
  567.                                         if (!inventory.isFull())
  568.                                             pouches.put(pType, PouchState.Empty);
  569.                                         break;
  570.                                     }
  571.  
  572.                                 break;
  573.                             }
  574.                     }
  575.                 }
  576.             } else {
  577.                 pouches.remove(pType);
  578.             }
  579.         }
  580.     }
  581.  
  582.     /**
  583.      * Fills pouches using the method set in the Options class
  584.      */
  585.     public void fillPouches() {
  586.         for (PouchType pType : pouches.keySet()) {
  587.             if (!inventory.contains(PURE_ESS_ID))
  588.                 break;
  589.             if (inventory.containsOneOf(pType.ids)) {
  590.                 if (pouches.get(pType).equals(PouchState.Empty)) {
  591.                     RSItem item = inventory.getItem(pType.ids);
  592.                     if (item != null) {
  593.                         int invCount = inventory.getCount();
  594.                         if (item.doAction("Fill"))
  595.                             switch (Options.pouchCheckSystem) {
  596.                             case SERVER_MSG:
  597.                                 for (int i = 0; i < POUCH_TRIES; i++) {
  598.                                     if (serverMsg != null)
  599.                                         if (serverMsg.contains("pouch is ful")) {
  600.                                             pouches.put(pType, PouchState.Full);
  601.                                             serverMsg = "";
  602.                                             break;
  603.                                         }
  604.                                     sleep(80, 120);
  605.                                 }
  606.                                 break;
  607.                             case INVENTORY_STATUS:
  608.                                 for (int i = 1; i <= POUCH_TRIES; i++)
  609.                                     if (inventory.getCount() == invCount
  610.                                             && i != POUCH_TRIES)
  611.                                         sleep(100, 150);
  612.                                     else {
  613.                                         if (inventory.contains(PURE_ESS_ID))
  614.                                             pouches.put(pType, PouchState.Full);
  615.                                         break;
  616.                                     }
  617.  
  618.                                 break;
  619.                             }
  620.                     }
  621.                 }
  622.             } else {
  623.                 pouches.remove(pType);
  624.             }
  625.         }
  626.     }
  627.  
  628.     /**
  629.      * Rechecks pouches with the inventory's current contents. If a pouch in the
  630.      * list no longer exists it removes it, if there's a pouch in the inventory
  631.      * not in the list it adds it as an empty pouch
  632.      */
  633.     public void updatePouches() {
  634.         for (PouchType pouch : PouchType.values())
  635.             if (inventory.containsOneOf(pouch.ids)) {
  636.                 if (!pouches.containsKey(pouch))
  637.                     pouches.put(pouch, PouchState.Empty);
  638.             } else if (pouches.containsKey(pouch))
  639.                 pouches.remove(pouch);
  640.     }
  641.  
  642.     public boolean onStart() {
  643.         camera = new CameraControl(super.camera, calc, this);
  644.         karamjaMeshPath = new MeshPath(walking, calc, players, KARAMJA_MESH,
  645.                 Options.walkingDistance);
  646.         daemonheimMeshPath = new MeshPath(walking, calc, players,
  647.                 DAEMONHEIM_MESH, Options.walkingDistance);
  648.         modelMouse = new ModelMouse(tiles, calc, mouse, menu,
  649.                 Options.mouseSpeed);
  650.         // Set up the pouches:
  651.         updatePouches();
  652.         mouse.setSpeed(Options.mouseSpeed.randomLinearDistribution());
  653.         RSItem wep = equipment.getItem(Equipment.WEAPON);
  654.         if (wep != null)
  655.             Options.hasAirStaff = (wep.getName().toLowerCase()
  656.                     .contains("staff") && wep.getName().toLowerCase()
  657.                     .contains("air"));
  658.         RSItem ring = equipment.getItem(Equipment.RING);
  659.         if (ring == null
  660.                 || ring.getName() == null
  661.                 || !ring.getName().toLowerCase().contains("ring")
  662.                 || !(ring.getName().toLowerCase().contains("kinship") || ring
  663.                         .getName().toLowerCase().contains("dueling"))) {
  664.             log.severe("You must be weilding a ring of kinship or a ring of dueling");
  665.             return false;
  666.         }
  667.         Options.teleportSystem = OptionsClass.TeleportSystem.DAEMONHEIM;
  668.         if (ring.getName().toLowerCase().contains("dueling")
  669.                 && !ring.getName().toLowerCase().contains("(")) {
  670.             Options.teleportSystem = OptionsClass.TeleportSystem.CASTLE_WARS;
  671.             log.severe("Your ring of dueling must be charged!");
  672.             return false;
  673.         }
  674.         RSItem tiara = equipment.getItem(Equipment.HELMET);
  675.         if (tiara == null || !tiara.getName().toLowerCase().contains("nature")) {
  676.             log.severe("You must have a Nature Tiara equipped!");
  677.             return false;
  678.         }
  679.  
  680.          Paint = new PaintInterface(this, log, Skills.RUNECRAFTING);
  681.         new DataLoader(this);
  682.  
  683.         // optimal alt
  684.         camera.setPitch(random(0, 10));
  685.  
  686.         log.info("Script initiation successful!");
  687.         log.info("--Setup--");
  688.         log.info("Air staff: " + (Options.hasAirStaff ? "Yes" : "No"));
  689.         log.info("Walking range: " + Options.walkingDistance.toString());
  690.         log.info("Summoning recharge: " + Options.summoningRecharge.toString());
  691.         log.info("Pouch method: "
  692.                 + PaintFormatter.properCaseAll(Options.pouchCheckSystem.name()
  693.                         .replaceAll("_", " ")));
  694.         log.info("Pouch repair system: "
  695.                 + PaintFormatter.properCaseAll(Options.pouchRepair.name()
  696.                         .replaceAll("_", " ")));
  697.         log.info("Teleport System: "
  698.                 + PaintFormatter.properCaseAll(Options.teleportSystem.name()
  699.                         .replaceAll("_", " ")));
  700.         if (Options.teleportSystem
  701.                 .equals(OptionsClass.TeleportSystem.CASTLE_WARS))
  702.             log.info("Save 1 Charge Rings: "
  703.                     + (Options.save1ChargeRings ? "Yes" : "No"));
  704.         return true;
  705.     }
  706.  
  707.     public int loop() {
  708.         mouse.setSpeed(Options.mouseSpeed.randomLinearDistribution());
  709.         if (!camera.isRunning())
  710.             camera.start();
  711.         try {
  712.             currentState = getState();
  713.             switch (currentState) {
  714.             case NEAR_BANK:
  715.                 RSNPC banker = npcs.getNearest(Bank.BANKERS);
  716.                 RSObject chest = objects.getNearest(Bank.BANK_CHESTS);
  717.                 if (banker != null) {
  718.                     if (!banker.isOnScreen()) {
  719.                         RSTile loc = banker.getLocation();
  720.                         if (calc.tileOnMap(loc)) {
  721.                             walking.walkTileMM(loc);
  722.                             for (byte b = 0; b < 22; b++)
  723.                                 if (banker.isOnScreen()) {
  724.                                     sleep(500);
  725.                                     break;
  726.                                 } else
  727.                                     sleep(100, 115);
  728.                         }
  729.                     }
  730.                 } else if (chest != null) {
  731.                     if (!chest.isOnScreen()) {
  732.                         RSTile loc = chest.getLocation();
  733.                         if (calc.tileOnMap(loc)) {
  734.                             walking.walkTileMM(loc);
  735.                             for (byte b = 0; b < 22; b++)
  736.                                 if (chest.isOnScreen()) {
  737.                                     sleep(500);
  738.                                     break;
  739.                                 } else
  740.                                     sleep(100, 115);
  741.                         }
  742.                     }
  743.                 }
  744.                 break;
  745.             case BANK_DEPOSIT_STUFF:
  746.                 this.deposit_non_necessities();
  747.                 break;
  748.             case BANK_WITHDRAW_ESS:
  749.                 this.withdrawEss();
  750.                 break;
  751.             case BANK_WITHDRAW_GRAAHK:
  752.                 if (openBank())
  753.                     for (byte b = 0; b < 5 && inventory.contains(PURE_ESS_ID); b++) {
  754.                         bank.deposit(PURE_ESS_ID, 1);
  755.                         sleep(450, 750);
  756.                     }
  757.                 withdrawItem(SPIRIT_GRAAHK_POUCH_ID, 1);
  758.                 break;
  759.             case SUMMON_GRAAHK:
  760.                 RSItem p = inventory.getItem(SPIRIT_GRAAHK_POUCH_ID);
  761.                 if (p != null) {
  762.                     for (byte b = 0; b < 10 && bank.isOpen(); b++) {
  763.                         bank.close();
  764.                         sleep(500, 750);
  765.                     }
  766.                     if (summoning.getSummoningPoints() <= 0) {
  767.                         log.severe("Out of summoning points :'(");
  768.                         stopScript(true);
  769.                     } else if (!bank.isOpen())
  770.                         p.doAction("Summon");
  771.                 }
  772.                 break;
  773.             case CHARGE_SUMMONING:
  774.                 RSObject obby = objects.getNearest(OBELISK_OBJ_ID);
  775.                 if (calc.tileOnMap(obby.getLocation())) {
  776.                     if (calc.distanceTo(obby) > 5)
  777.                         walking.walkTileMM(obby.getLocation());
  778.                     camera.turnTo(obby);
  779.                     sleep(125, 175);
  780.                     modelMouse.doClick(obby);
  781.                     sleep(250, 500);
  782.                 }
  783.                 break;
  784.             case CRAFT_ESSENCE:
  785.                 RSObject alter = objects.getNearest(NATURE_ALTER_ID);
  786.                 if (!alter.isOnScreen() && !getMyPlayer().isMoving())
  787.                     walking.walkTileMM(alter.getArea().getNearestTile(
  788.                             getMyPlayer().getLocation()));
  789.  
  790.                 int invBefore = inventory.getCount(PURE_ESS_ID);
  791.                 int natsBefore = inventory.getCount(true, NATURE_RUNE_ID);
  792.                 if (!alter.isOnScreen())
  793.                     camera.turnTo(alter);
  794.                 modelMouse.doClick(alter);
  795.                 for (byte b = 0; b < 25
  796.                         && (getMyPlayer().getAnimation() == CRAFT_RUNE_ANIM || getMyPlayer()
  797.                                 .isMoving()); b++)
  798.                     sleep(100, 125);
  799.                 if ((inventory.getCount(PURE_ESS_ID) != invBefore && natsBefore != inventory
  800.                         .getCount(true, NATURE_RUNE_ID))) {
  801.                     Paint.essUsed += invBefore
  802.                             - inventory.getCount(PURE_ESS_ID);
  803.                     Paint.runesMade += inventory.getCount(true, NATURE_RUNE_ID)
  804.                             - natsBefore;
  805.                 }
  806.                 break;
  807.             case DISMISS_WRONG_FAMILIAR:
  808.                 this.summoning.doDismiss();
  809.                 sleep(500, 600);
  810.                 break;
  811.             case EMPTY_POUCH:
  812.                 emptyPouches();
  813.                 break;
  814.             case ENTER_PORTAL:
  815.                 RSObject obj = objects.getNearest(NATURE_PORTAL_ID);
  816.                 if (obj != null) {
  817.                     if (!obj.isOnScreen()) {
  818.                         walking.walkTileMM(obj.getArea().getNearestTile(
  819.                                 getMyPlayer().getLocation()));
  820.                         camera.turnTo(obj);
  821.                     }
  822.                     sleep(750);
  823.                     if (obj.isOnScreen()) {
  824.                         modelMouse.doClick(obj);
  825.                         for (byte b = 0; b < 10
  826.                                 && objects.getNearest(NATURE_ALTER_ID) == null; b++)
  827.                             sleep(80, 120);
  828.  
  829.                         karamjaMeshPath.clearPath();
  830.                     }
  831.                 }
  832.                 break;
  833.             case FILL_POUCH:
  834.                 fillPouches();
  835.                 break;
  836.             case NPC_CONTACT:
  837.                 npcContact.main();
  838.                 break;
  839.             case TELE_TO_BANK:
  840.                 if (Options.teleportSystem
  841.                         .equals(OptionsClass.TeleportSystem.DAEMONHEIM))
  842.                     this.teleportToDaemonheim();
  843.                 else
  844.                     this.teleportToCastleWars();
  845.                 break;
  846.             case TELE_TO_KARAMJA:
  847.                 this.teleportToKaramja();
  848.                 break;
  849.             case TRAVERSE_DAEMONHEIM:
  850.                 if (!daemonheimMeshPath.hasPath())
  851.                     daemonheimMeshPath.findPath(MeshPath.MeshPathType.FULL);
  852.                 daemonheimMeshPath.traverse();
  853.                 break;
  854.             case TRAVERSE_KARAMJA:
  855.                 if (!karamjaMeshPath.hasPath())
  856.                     karamjaMeshPath
  857.                             .findPath(MeshPath.MeshPathType.WEIGHT_CENTER);
  858.                 karamjaMeshPath.traverse();
  859.                 break;
  860.             case REMOVE_RING:
  861.                 if (bank.isOpen()
  862.                         || (interfaces.get(Bank.INTERFACE_EQUIPMENT) != null && interfaces
  863.                                 .get(Bank.INTERFACE_EQUIPMENT).isValid())) {
  864.                     /*
  865.                      * if (bank.getEquipmentItems().length == 0)
  866.                      * bank.openEquipment(); for (byte b = 0; b < 25 &&
  867.                      * bank.getEquipmentItems().length == 0; b++) sleep(100,
  868.                      * 125); RSItem ring =
  869.                      * bank.getEquipmentItem(bank.getEquipmentItemID("Ring"));
  870.                      * if (ring!=null) ring.doAction("Remove");
  871.                      */
  872.                     bank.close();
  873.                     sleep(1000, 1100);
  874.                 }
  875.                 if (equipment.getItem(Equipment.RING) != null)
  876.                     equipment.getItem(Equipment.RING).doAction("Remove");
  877.                 break;
  878.             case WITHDRAW_RING:
  879.                 openBank();
  880.                 boolean withdrawn = false;
  881.                 for (byte b = 2; b <= 8; b++)
  882.                     if (bank.getItemID("Ring of Dueling (" + b + ")") >= 0)
  883.                         if (withdrawItem(
  884.                                 bank.getItemID("Ring of Dueling (" + b + ")"),
  885.                                 1)) {
  886.                             withdrawn = true;
  887.                             break;
  888.                         }
  889.                 if (!withdrawn) {
  890.                     log.severe("Out of rings!");
  891.                     stopScript(false);
  892.                 }
  893.                 break;
  894.             case EQUIP_RING:
  895.                 for (byte b = 2; b <= 8; b++) {
  896.                     RSItem inv = inventory.getItem(inventory
  897.                             .getItemID("Ring of Dueling( " + b + ")"));
  898.                     if (inv != null)
  899.                         if (inv.doAction("Equip"))
  900.                             break;
  901.                 }
  902.                 break;
  903.             }
  904.         } catch (Exception e) {
  905.             logError(e);
  906.         }
  907.         if (walking.getEnergy() > 50 && !walking.isRunEnabled())
  908.             walking.setRun(true);
  909.         return random(100, 125);
  910.     }
  911.  
  912.     public void onFinish() {
  913.         env.saveScreenshot(true);
  914.         if (Paint != null)
  915.             saveProggie();
  916.     }
  917.  
  918.     /**
  919.      * Saves a progress report to the Screenshots directory
  920.      */
  921.     public void saveProggie() {
  922.         Paint.paintMenu.saveProggie(
  923.                 GlobalConfiguration.Paths.getScreenshotsDirectory(),
  924.                 getClass().getAnnotation(ScriptManifest.class).name()
  925.                         + "_"
  926.                         + PaintFormatter.getFormattedTime(
  927.                                 System.currentTimeMillis() - Paint.startTime,
  928.                                 "-", true), log);
  929.     }
  930.  
  931.     /**
  932.      * Logs an exception to the Logger and to the error cache
  933.      *
  934.      * @param e
  935.      *            as the Exception to log
  936.      */
  937.     public void logError(Exception e) {
  938.         String s = e.getMessage();
  939.         log.severe(s);
  940.         for (StackTraceElement ele : e.getStackTrace()) {
  941.             s += "\n" + ele.toString();
  942.             log.severe(ele.toString());
  943.         }
  944.         errorLog.add(s);
  945.     }
  946.  
  947.     public void onRepaint(Graphics g1) {
  948.         Graphics2D g = (Graphics2D) g1;
  949.         try {
  950.             // MeshPaths
  951.             karamjaMeshPath.paintAll(g);
  952.             daemonheimMeshPath.paintAll(g);
  953.  
  954.             // Mouse Crosshair
  955.             paintPouches(g);
  956.  
  957.             // main paint:
  958.             if (Paint != null)
  959.                 Paint.render(g);
  960.  
  961.         } catch (NullPointerException e) {
  962.             logError(e);
  963.         }
  964.     }
  965.  
  966.     /**
  967.      * Paints the pouches loaded into the list using red for empty and green for
  968.      * full. Only paints if inventory is visible.
  969.      *
  970.      * @param g
  971.      *            as Graphics
  972.      */
  973.     public void paintPouches(Graphics g) {
  974.         // Loop through pouches
  975.         for (PouchType pouch : pouches.keySet()) {
  976.             if (game.getCurrentTab() == Game.TAB_INVENTORY) {
  977.                 RSItem item = inventory.getItem(pouch.ids);
  978.                 if (item != null) {
  979.                     if (pouches.get(pouch).equals(PouchState.Empty))
  980.                         g.setColor(new Color(1f, 0f, 0f, 0.5f));
  981.                     else
  982.                         g.setColor(new Color(0f, 1f, 0f, 0.5f));
  983.  
  984.                     RSComponent c = item.getComponent();
  985.                     g.fillRect(c.getAbsoluteX(), c.getAbsoluteY(),
  986.                             c.getWidth(), c.getHeight());
  987.                     // Decay:
  988.                     for (int id : Pouch.ALL_DEGRADED)
  989.                         if (item.getID() == id) {
  990.                             g.setColor(Color.WHITE);
  991.                             g.setFont(Paint.style.FONT.deriveFont(4));
  992.                             g.drawString("Degraded", c.getAbsoluteX(),
  993.                                     c.getAbsoluteY() + c.getHeight());
  994.                             break;
  995.                         }
  996.                 }
  997.             }
  998.         }
  999.     }
  1000.  
  1001.     // Mouse Motion
  1002.     public void mouseDragged(MouseEvent arg0) {
  1003.         Paint.mouseLoc = arg0.getPoint();
  1004.     }
  1005.  
  1006.     public void mouseMoved(MouseEvent arg0) {
  1007.         Paint.mouseLoc = arg0.getPoint();
  1008.     }
  1009.  
  1010.     public void mouseClicked(MouseEvent arg0) {
  1011.         Paint.fireMouseEvent(arg0);
  1012.     }
  1013.  
  1014.     public void mouseEntered(MouseEvent arg0) {
  1015.  
  1016.     }
  1017.  
  1018.     public void mouseExited(MouseEvent arg0) {
  1019.  
  1020.     }
  1021.  
  1022.     public void mousePressed(MouseEvent arg0) {
  1023.  
  1024.     }
  1025.  
  1026.     public void mouseReleased(MouseEvent arg0) {
  1027.     }
  1028.  
  1029.     /**
  1030.      * A class used to represent the paint interface for this script
  1031.      *
  1032.      * @author Equinox_
  1033.      *
  1034.      */
  1035.     public class PaintInterface {
  1036.         // Constant
  1037.         public final int[] SKILL_INTERFACES = { Skills.INTERFACE_ATTACK,
  1038.                 Skills.INTERFACE_DEFENSE, Skills.INTERFACE_STRENGTH,
  1039.                 Skills.INTERFACE_CONSTITUTION, Skills.INTERFACE_RANGE,
  1040.                 Skills.INTERFACE_PRAYER, Skills.INTERFACE_MAGIC,
  1041.                 Skills.INTERFACE_COOKING, Skills.INTERFACE_WOODCUTTING,
  1042.                 Skills.INTERFACE_FLETCHING, Skills.INTERFACE_FISHING,
  1043.                 Skills.INTERFACE_FIREMAKING, Skills.INTERFACE_CRAFTING,
  1044.                 Skills.INTERFACE_SMITHING, Skills.INTERFACE_MINING,
  1045.                 Skills.INTERFACE_HERBLORE, Skills.INTERFACE_AGILITY,
  1046.                 Skills.INTERFACE_THIEVING, Skills.INTERFACE_SLAYER,
  1047.                 Skills.INTERFACE_FARMING, Skills.INTERFACE_RUNECRAFTING,
  1048.                 Skills.INTERFACE_HUNTER, Skills.INTERFACE_CONSTRUCTION,
  1049.                 Skills.INTERFACE_SUMMONING, Skills.INTERFACE_DUNGEONEERING };
  1050.  
  1051.         // Mouse
  1052.         public DrawMouse drawMouse = new DrawMouse();
  1053.  
  1054.         // Start data
  1055.         public int[] startExp = {};
  1056.         public long startTime = 0;
  1057.         public int[] startLevel = {};
  1058.  
  1059.         // Goal data
  1060.         public int[] goalExp;
  1061.         public int[] goalLevel;
  1062.  
  1063.         // Usage data
  1064.         public int runsMade = 0;
  1065.         public int runesMade = 0;
  1066.         public int essUsed = 0;
  1067.         public int graahksUsed = 0;
  1068.  
  1069.         // Prices
  1070.         public int essPrice;
  1071.  
  1072.         public FontRenderContext frc = new FontRenderContext(
  1073.                 new AffineTransform(), false, false);
  1074.  
  1075.         public PaintStyle style = new PaintStyle();
  1076.         public Point mouseLoc = new Point(0, 0);
  1077.  
  1078.         final EquinoxGraahkCrafter crafter;
  1079.         final Logger log;
  1080.         int[] SkillIDs = { -1 };
  1081.         public PaintMenu paintMenu;
  1082.  
  1083.         public int graahkPrice;
  1084.  
  1085.         public int natPrice;
  1086.  
  1087.         /**
  1088.          * Constructs a PaintInterface using the provided parent and logger
  1089.          * object
  1090.          *
  1091.          * @param crafter
  1092.          *            the parent crafter
  1093.          * @param log
  1094.          *            the Logger object
  1095.          */
  1096.         public PaintInterface(EquinoxGraahkCrafter crafter, Logger log) {
  1097.             this(crafter, log, -1);
  1098.         }
  1099.  
  1100.         public PaintInterface(EquinoxGraahkCrafter crafter, Logger log,
  1101.                 int... SkillIDs) {
  1102.             this.crafter = crafter;
  1103.             this.log = log;
  1104.             this.SkillIDs = SkillIDs;
  1105.             startTime = System.currentTimeMillis();
  1106.             startExp = new int[Skills.SKILL_NAMES.length];
  1107.             startLevel = new int[Skills.SKILL_NAMES.length];
  1108.             goalExp = new int[Skills.SKILL_NAMES.length];
  1109.             goalLevel = new int[Skills.SKILL_NAMES.length];
  1110.  
  1111.             for (int skill = 0; skill < Skills.SKILL_NAMES.length; skill++) {
  1112.                 startExp[skill] = -1;
  1113.                 startLevel[skill] = -1;
  1114.                 goalExp[skill] = -1;
  1115.                 goalLevel[skill] = -1;
  1116.             }
  1117.             init(SkillIDs);
  1118.         }
  1119.  
  1120.         public void init(int... skillsToAdd) {
  1121.             paintMenu = new PaintMenu(style, Anchor.DOWN_RIGHT);
  1122.             paintMenu.setFlowType(FlowType.HORIZONTAL);
  1123.             paintMenu.addMouseListener(new MenuMouseListener() {
  1124.                 public void MouseAction(MouseEvent e) {
  1125.                     if (e.getID() == MouseEvent.MOUSE_CLICKED)
  1126.                         paintMenu.toggleShowAll();
  1127.                 }
  1128.             });
  1129.             for (int SkillID : skillsToAdd) {
  1130.                 if (SkillID >= 0 && SkillID < Skills.SKILL_NAMES.length) {
  1131.                     startExp[SkillID] = skills.getCurrentExp(SkillID);
  1132.                     log.info("Current "
  1133.                             + PaintFormatter
  1134.                                     .properCaseAll(Skills.SKILL_NAMES[SkillID])
  1135.                             + " EXP: " + startExp[SkillID]);
  1136.                     startLevel[SkillID] = skills.getRealLevel(SkillID);
  1137.                     if (startLevel[SkillID] > (SkillID == Skills.DUNGEONEERING ? 120
  1138.                             : 99))
  1139.                         startLevel[SkillID] = SkillID == Skills.DUNGEONEERING ? 120
  1140.                                 : 99;
  1141.                     log.info("Current "
  1142.                             + PaintFormatter
  1143.                                     .properCaseAll(Skills.SKILL_NAMES[SkillID])
  1144.                             + " Level: " + startLevel[SkillID]);
  1145.                     setUpGoal(SkillID);
  1146.                     paintMenu.add(new PaintMenuSkillProgressBar(SkillID,
  1147.                             skills, startExp[SkillID], paintMenu));
  1148.                     if (goalExp[SkillID] > skills.getCurrentExp(SkillID))
  1149.                         paintMenu.add(new PaintMenuSkillProgressBar(paintMenu,
  1150.                                 SkillID, goalExp[SkillID], skills,
  1151.                                 startExp[SkillID]));
  1152.                 }
  1153.             }
  1154.  
  1155.             PaintMenuButton natures = new PaintMenuButton(paintMenu, "natures",
  1156.                     "natrune", new GetPopup() {
  1157.                         public PaintMenuPopUp getPopUp() {
  1158.                             long millis = System.currentTimeMillis()
  1159.                                     - crafter.Paint.startTime;
  1160.                             if (millis <= 0)
  1161.                                 millis = 1;
  1162.                             String runesPerEssence = "Runes per Ess: "
  1163.                                     + ((essUsed > 0) ? (new DecimalFormat(
  1164.                                             "#.##").format(((double) runesMade)
  1165.                                             / ((double) essUsed)))
  1166.                                             : "No essence used");
  1167.                             long natsPerHour = ((runesMade * 3600000l) / millis);
  1168.                             long profit = (runesMade * natPrice)
  1169.                                     - (essUsed * essPrice)
  1170.                                     - (graahksUsed - graahkPrice);
  1171.                             String[] lines = new String[9];
  1172.                             lines[0] = "Essence Used: "
  1173.                                     + PaintFormatter.getFormattedMoney(essUsed);
  1174.                             lines[1] = "Graahks Used: "
  1175.                                     + PaintFormatter
  1176.                                             .getFormattedMoney(graahksUsed);
  1177.                             lines[2] = "Runes Crafted: "
  1178.                                     + PaintFormatter
  1179.                                             .getFormattedMoney(runesMade);
  1180.                             lines[3] = "Runes/Hour: "
  1181.                                     + PaintFormatter
  1182.                                             .getFormattedMoney(natsPerHour);
  1183.                             lines[4] = "Profit: "
  1184.                                     + PaintFormatter.getFormattedMoney(profit)
  1185.                                     + " gp";
  1186.                             lines[5] = "Profit per hour: "
  1187.                                     + PaintFormatter
  1188.                                             .getFormattedMoney((profit * 3600000)
  1189.                                                     / millis) + " gp";
  1190.                             lines[6] = runesPerEssence;
  1191.                             lines[7] = "Runs made: " + runsMade;
  1192.                             lines[8] = "Runes per run: "
  1193.                                     + (runsMade > 0 ? (int) (runesMade / runsMade)
  1194.                                             : "No runs made");
  1195.                             return new PaintMenuPopUp(lines, "runes");
  1196.                         }
  1197.  
  1198.                         public String getName() {
  1199.                             return "runes";
  1200.                         }
  1201.                     });
  1202.             paintMenu.add(natures);
  1203.  
  1204.             PaintMenuButton home = new PaintMenuButton(paintMenu, "home",
  1205.                     "homeicon", new GetPopup() {
  1206.                         public PaintMenuPopUp getPopUp() {
  1207.                             long millis = System.currentTimeMillis()
  1208.                                     - crafter.Paint.startTime;
  1209.                             if (millis <= 0)
  1210.                                 millis = 1;
  1211.                             String[] lines = new String[5];
  1212.                             lines[0] = crafter.getClass()
  1213.                                     .getAnnotation(ScriptManifest.class).name();
  1214.                             lines[1] = "By: "
  1215.                                     + PaintFormatter
  1216.                                             .formatList(crafter
  1217.                                                     .getClass()
  1218.                                                     .getAnnotation(
  1219.                                                             ScriptManifest.class)
  1220.                                                     .authors());
  1221.                             lines[2] = "Version "
  1222.                                     + crafter
  1223.                                             .getClass()
  1224.                                             .getAnnotation(ScriptManifest.class)
  1225.                                             .version();
  1226.                             lines[3] = "Runtime: "
  1227.                                     + PaintFormatter.getFormattedTime(millis)
  1228.                                     + "\n";
  1229.                             lines[4] = PaintFormatter
  1230.                                     .properCaseAll(crafter.currentState.name()
  1231.                                             .replace("_", " "));
  1232.                             if (crafter.currentState.equals(State.NPC_CONTACT))
  1233.                                 lines[4] += " - "
  1234.                                         + PaintFormatter
  1235.                                                 .properCaseAll(npcContact.currentState
  1236.                                                         .name().replace("_",
  1237.                                                                 " "));
  1238.                             return new PaintMenuPopUp(lines, "home");
  1239.                         }
  1240.  
  1241.                         public String getName() {
  1242.                             return "home";
  1243.                         }
  1244.                     });
  1245.             paintMenu.add(home);
  1246.  
  1247.             PaintMenuButton tools = new PaintMenuButton(paintMenu, "tools",
  1248.                     "toolsicon", new GetPopup() {
  1249.                         public PaintMenuPopUp getPopUp() {
  1250.                             String[] lines = new String[2];
  1251.                             lines[0] = "Save proggie";
  1252.                             lines[1] = "Reload data";
  1253.  
  1254.                             PaintMenuPopUp popUp = new PaintMenuPopUp(lines,
  1255.                                     "tools");
  1256.                             popUp.addLineListener(new LineListener() {
  1257.                                 public void fireEvent(MouseEvent e, String line) {
  1258.                                     line = line.toLowerCase().trim();
  1259.                                     if (e.getID() == MouseEvent.MOUSE_CLICKED) {
  1260.                                         if (line.contains("save proggie"))
  1261.                                             crafter.saveProggie();
  1262.                                         else if (line.contains("reload data"))
  1263.                                             new DataLoader(crafter);
  1264.                                         else if (line.contains("set flow")) {
  1265.                                             if (line.contains("horizontal"))
  1266.                                                 paintMenu
  1267.                                                         .setFlowType(FlowType.HORIZONTAL);
  1268.                                             else
  1269.                                                 paintMenu
  1270.                                                         .setFlowType(FlowType.VERTICAL);
  1271.                                         } else if (line
  1272.                                                 .contains("set vertical")) {
  1273.                                             if (line.contains("top")) {
  1274.                                                 if (paintMenu.anchor.name()
  1275.                                                         .contains("LEFT"))
  1276.                                                     paintMenu.anchor = Anchor.UP_LEFT;
  1277.                                                 else
  1278.                                                     paintMenu.anchor = Anchor.UP_RIGHT;
  1279.                                             } else {
  1280.                                                 if (paintMenu.anchor.name()
  1281.                                                         .contains("LEFT"))
  1282.                                                     paintMenu.anchor = Anchor.DOWN_LEFT;
  1283.                                                 else
  1284.                                                     paintMenu.anchor = Anchor.DOWN_RIGHT;
  1285.                                             }
  1286.                                             paintMenu.compile();
  1287.                                         } else if (line
  1288.                                                 .contains("set horizontal")) {
  1289.                                             if (line.contains("left")) {
  1290.                                                 if (paintMenu.anchor.name()
  1291.                                                         .contains("UP"))
  1292.                                                     paintMenu.anchor = Anchor.UP_LEFT;
  1293.                                                 else
  1294.                                                     paintMenu.anchor = Anchor.DOWN_LEFT;
  1295.                                             } else {
  1296.                                                 if (paintMenu.anchor.name()
  1297.                                                         .contains("UP"))
  1298.                                                     paintMenu.anchor = Anchor.UP_RIGHT;
  1299.                                                 else
  1300.                                                     paintMenu.anchor = Anchor.DOWN_RIGHT;
  1301.                                             }
  1302.                                         }
  1303.                                         if (line.contains("set"))
  1304.                                             paintMenu.compile();
  1305.                                     }
  1306.                                 }
  1307.  
  1308.                                 public boolean hasAction(String line) {
  1309.                                     line = line.toLowerCase().trim();
  1310.                                     String[] lines = new String[2];
  1311.                                     lines[0] = "Save proggie";
  1312.                                     lines[1] = "Reload data";
  1313.                                     for (String s : lines)
  1314.                                         if (s.equalsIgnoreCase(line))
  1315.                                             return true;
  1316.                                     return false;
  1317.                                 }
  1318.                             });
  1319.                             return popUp;
  1320.                         }
  1321.  
  1322.                         public String getName() {
  1323.                             return "tools";
  1324.                         }
  1325.                     });
  1326.             paintMenu.add(tools);
  1327.  
  1328.             paintMenu.compile();
  1329.         }
  1330.  
  1331.         /**
  1332.          * Gets the current Goal for the setSkill, or sets all goal variables to
  1333.          * -1 if there is no goal
  1334.          */
  1335.         public void setUpGoal(int skillid) {
  1336.             try {
  1337.                 for (byte b = 0; b < 10; b++)
  1338.                     if (game.getCurrentTab() != Game.TAB_STATS) {
  1339.                         game.openTab(Game.TAB_STATS);
  1340.                         sleep(1000);
  1341.                     } else
  1342.                         break;
  1343.  
  1344.                 RSInterface stats = interfaces.get(320);
  1345.                 if (stats != null && stats.isValid()) {
  1346.                     RSComponent skill = stats
  1347.                             .getComponent(SKILL_INTERFACES[skillid]);
  1348.                     if (skill != null && skill.isValid()
  1349.                             && skill.getArea() != null) {
  1350.                         mouse.move((int) skill.getArea().getCenterX(),
  1351.                                 (int) skill.getArea().getCenterY());
  1352.                         sleep(500, 750);
  1353.                         // Now we see what the popup is doing
  1354.                         RSComponent pop = stats.getComponent(202);
  1355.                         if (pop != null && pop.isValid()) {
  1356.                             RSComponent def = pop.getComponent(5);
  1357.                             if (def != null && def.isValid()) {
  1358.                                 if (def.getText().trim()
  1359.                                         .equalsIgnoreCase("Target XP:")) {
  1360.                                     RSComponent goalXP = pop.getComponent(6);
  1361.                                     if (goalXP != null && goalXP.isValid()) {
  1362.                                         goalExp[skillid] = Integer
  1363.                                                 .valueOf(goalXP.getText()
  1364.                                                         .replace(",", "")
  1365.                                                         .trim());
  1366.                                         goalLevel[skillid] = -1;
  1367.                                     }
  1368.                                 } else {
  1369.                                     // Proceed
  1370.                                     RSComponent currExp = pop.getComponent(4);
  1371.                                     RSComponent goalRemainder = pop
  1372.                                             .getComponent(12);
  1373.                                     RSComponent goalLvl = pop.getComponent(10);
  1374.                                     if (currExp != null && currExp.isValid()
  1375.                                             && goalRemainder != null
  1376.                                             && goalRemainder.isValid()
  1377.                                             && goalLvl != null
  1378.                                             && goalLvl.isValid()) {
  1379.                                         goalExp[skillid] = Integer
  1380.                                                 .valueOf(currExp.getText()
  1381.                                                         .replace(",", "")
  1382.                                                         .trim())
  1383.                                                 + Integer.valueOf(goalRemainder
  1384.                                                         .getText()
  1385.                                                         .replace(",", "")
  1386.                                                         .trim());
  1387.                                         goalLevel[skillid] = Integer
  1388.                                                 .valueOf(goalLvl.getText()
  1389.                                                         .replace(",", "")
  1390.                                                         .trim());
  1391.                                     }
  1392.                                 }
  1393.                                 log.info("Target level for "
  1394.                                         + Skills.SKILL_NAMES[skillid]
  1395.                                         + ": "
  1396.                                         + (goalLevel[skillid] > 0 ? goalLevel[skillid]
  1397.                                                 : "None"));
  1398.                                 log.info("Target exp for "
  1399.                                         + Skills.SKILL_NAMES[skillid]
  1400.                                         + ": "
  1401.                                         + (goalExp[skillid] > 0 ? goalExp[skillid]
  1402.                                                 : "None"));
  1403.                                 return;
  1404.                             }
  1405.                         }
  1406.                     }
  1407.                 }
  1408.             } catch (Exception e) {
  1409.             }
  1410.             goalExp[skillid] = -1;
  1411.             goalLevel[skillid] = -1;
  1412.         }
  1413.  
  1414.         /**
  1415.          * Renders the current paint to the given graphics context.
  1416.          *
  1417.          * @param g
  1418.          *            as the Graphics2D object to render to
  1419.          */
  1420.         public void render(Graphics2D g) {
  1421.             paintMenu.render(g, mouseLoc);
  1422.             drawMouse.onRepaint(g);
  1423.         }
  1424.  
  1425.         /**
  1426.          * Fires a MouseEvent in the paintMenu
  1427.          *
  1428.          * @param e
  1429.          *            MouseEvent
  1430.          */
  1431.         public void fireMouseEvent(MouseEvent e) {
  1432.             if (paintMenu.getBoundsFull().contains(e.getPoint()))
  1433.                 paintMenu.fireMouseEvent(e);
  1434.         }
  1435.  
  1436.         public class DrawMouse {
  1437.             private MouseTrailing mouseTrailing = new MouseTrailing();
  1438.             private ArrayList<MouseInfo> clicks = new ArrayList<MouseInfo>();
  1439.             private long clickLifeTime = 1500;
  1440.  
  1441.             public void onRepaint(final Graphics render) {
  1442.                 if (mouse != null) {
  1443.                     Point Location = mouse.getLocation();
  1444.                     if (mouse.isPressed()) {
  1445.                         boolean found = false;
  1446.                         for (MouseInfo click : clicks)
  1447.                             if (click.getLocation().equals(Location)) {
  1448.                                 click.updateTime(System.currentTimeMillis());
  1449.                                 found = true;
  1450.                                 break;
  1451.                             }
  1452.                         if (!found)
  1453.                             clicks.add(new MouseInfo(
  1454.                                     System.currentTimeMillis(), Location));
  1455.                     }
  1456.                     mouseTrailing.draw((Graphics2D) render, Color.WHITE);
  1457.                     render.setColor(Color.GREEN);
  1458.                     render.drawLine(Location.x - 5, Location.y, Location.x + 5,
  1459.                             Location.y);
  1460.                     render.drawLine(Location.x, Location.y - 5, Location.x,
  1461.                             Location.y + 5);
  1462.                     for (MouseInfo click : clicks) {
  1463.                         float i = ((float) (click.getTime() + clickLifeTime - System
  1464.                                 .currentTimeMillis()) / (float) clickLifeTime) + 0.25f;
  1465.                         if (i > 0) {
  1466.                             render.setColor(new Color(1f, 0f, 0f, i > 1 ? 1f
  1467.                                     : i));
  1468.                             render.drawLine(click.getLocation().x - 5,
  1469.                                     click.getLocation().y,
  1470.                                     click.getLocation().x + 5,
  1471.                                     click.getLocation().y);
  1472.                             render.drawLine(click.getLocation().x,
  1473.                                     click.getLocation().y - 5,
  1474.                                     click.getLocation().x,
  1475.                                     click.getLocation().y + 5);
  1476.                         } else if (System.currentTimeMillis() - click.getTime() >= clickLifeTime)
  1477.                             clicks.remove(click);
  1478.  
  1479.                     }
  1480.  
  1481.                 }
  1482.             }
  1483.  
  1484.             public class MouseTrailing {
  1485.                 private int LifeTime = 1500;
  1486.                 private ArrayList<MouseInfo> locations = new ArrayList<MouseInfo>();
  1487.  
  1488.                 private void addLocation() {
  1489.                     Point m = mouse.getLocation();
  1490.                     MouseInfo mInfo = new MouseInfo(System.currentTimeMillis(),
  1491.                             m);
  1492.                     if (!locations.contains(mInfo))
  1493.                         locations.add(mInfo);
  1494.                 }
  1495.  
  1496.                 public void draw(Graphics2D g, Color c) {
  1497.                     addLocation();
  1498.                     for (int t = 1; t < locations.size(); t++) {
  1499.                         MouseInfo p1 = locations.get(t);
  1500.                         MouseInfo p2 = locations.get(t - 1);
  1501.                         float i = (float) (p1.getTime() + LifeTime - System
  1502.                                 .currentTimeMillis()) / (float) LifeTime;
  1503.                         i = i > 1 ? 1f : i;
  1504.                         if (i > 0) {
  1505.                             g.setColor(new Color(c.getRed() / 255f, c
  1506.                                     .getGreen() / 255f, c.getBlue() / 255f, (c
  1507.                                     .getAlpha() / 255f) * i));
  1508.                             g.drawLine(p1.getLocation().x, p1.getLocation().y,
  1509.                                     p2.getLocation().x, p2.getLocation().y);
  1510.                         }
  1511.                     }
  1512.                     if (locations.size() > 0
  1513.                             && System.currentTimeMillis() > locations.get(0)
  1514.                                     .getTime() + LifeTime)
  1515.                         locations.remove(0);
  1516.                 }
  1517.             }
  1518.  
  1519.             private class MouseInfo {
  1520.                 private long time;
  1521.                 private Point location;
  1522.  
  1523.                 public MouseInfo(long time, Point loc) {
  1524.                     this.time = time;
  1525.                     this.location = loc;
  1526.                 }
  1527.  
  1528.                 public long getTime() {
  1529.                     return time;
  1530.                 }
  1531.  
  1532.                 public Point getLocation() {
  1533.                     return location;
  1534.                 }
  1535.  
  1536.                 public void updateTime(long newTime) {
  1537.                     this.time = newTime;
  1538.                 }
  1539.  
  1540.                 public boolean equals(Object m) {
  1541.                     if (!(m instanceof MouseInfo))
  1542.                         return false;
  1543.                     MouseInfo cast = (MouseInfo) m;
  1544.                     return (cast.getLocation().equals(location));
  1545.                 }
  1546.             }
  1547.         }
  1548.  
  1549.     }
  1550.  
  1551.     /**
  1552.      * The different states the NPC contact process can be in
  1553.      *
  1554.      * @author Equinox_
  1555.      *
  1556.      */
  1557.     enum npcContactState {
  1558.         CAST, SCROLL, SELECT, WAIT, CONTINUE, MENU, WITHDRAW_ASTRAL, WITHDRAW_COSMIC, WITHDRAW_AIR, OUT_OF_RUNES
  1559.     }
  1560.  
  1561.     /**
  1562.      * A class to contact people using NPC contact
  1563.      *
  1564.      * @author Equinox_
  1565.      *
  1566.      */
  1567.     class NPCContact {
  1568.         public npcContactState currentState = npcContactState.WAIT;
  1569.         private final int MAIN_FRAME = 88;
  1570.         private final int SCROLL_BAR = 20;
  1571.         private final int SCROLL_BAR_DOWN = 0;
  1572.         private final int VISIBLE_NPCS_CONTAINER = 21;
  1573.         private final int NPCS_CONTAINER = 22;
  1574.         private final int NPC_CONTACT_ANIM = 4413;
  1575.  
  1576.         private final int NPC_TO_SELECT = 14;
  1577.         private final String[] MENU_ITEM = { "repair", "pouch" };
  1578.  
  1579.         /**
  1580.          * Gets the current npcContactState
  1581.          *
  1582.          * @return the current npcContactState
  1583.          */
  1584.         private npcContactState getState() {
  1585.             if (interfaces.get(MAIN_FRAME).isValid()) {
  1586.                 if (interfaces.get(MAIN_FRAME).getComponent(NPCS_CONTAINER)
  1587.                         .isValid()) {
  1588.                     Point npcPoint = interfaces.get(MAIN_FRAME)
  1589.                             .getComponent(NPCS_CONTAINER)
  1590.                             .getComponent(NPC_TO_SELECT).getCenter();
  1591.                     Rectangle visible = interfaces.get(MAIN_FRAME)
  1592.                             .getComponent(VISIBLE_NPCS_CONTAINER).getArea();
  1593.                     if (!visible.contains(npcPoint))
  1594.                         return npcContactState.SCROLL;
  1595.                     else
  1596.                         return npcContactState.SELECT;
  1597.                 }
  1598.             }
  1599.  
  1600.             if (interfaces.canContinue())
  1601.                 return npcContactState.CONTINUE;
  1602.  
  1603.             if (getComponent(MENU_ITEM) != null)
  1604.                 return npcContactState.MENU;
  1605.  
  1606.             if (!interfaces.canContinue()
  1607.                     && getMyPlayer().getAnimation() != NPC_CONTACT_ANIM
  1608.                     && getComponent(MENU_ITEM) == null
  1609.                     && getComponent("lease wai") == null) {
  1610.                 if (!inventory.contains(ASTRAL_RUNE_ID))
  1611.                     return npcContactState.WITHDRAW_ASTRAL;
  1612.                 if (!inventory.contains(COSMIC_RUNE_ID))
  1613.                     return npcContactState.WITHDRAW_COSMIC;
  1614.                 if (inventory.getCount(true, AIR_RUNE_ID) < 2
  1615.                         && !Options.hasAirStaff)
  1616.                     return npcContactState.WITHDRAW_AIR;
  1617.                 return npcContactState.CAST;
  1618.             }
  1619.             return npcContactState.WAIT;
  1620.         }
  1621.  
  1622.         /**
  1623.          * Finds a RSComponent with the specified string
  1624.          *
  1625.          * @param s
  1626.          *            The String to search
  1627.          * @return RSComponent with string, otherwise null
  1628.          */
  1629.         private RSComponent getComponent(String... s) {
  1630.             RSInterface[] valid = interfaces.getAll();
  1631.             for (RSInterface iface : valid) {
  1632.                 if (iface.getIndex() != 137) {
  1633.                     int len = iface.getChildCount();
  1634.                     for (int i = 0; i < len; i++) {
  1635.                         RSComponent child = iface.getComponent(i);
  1636.                         boolean b = (child.isValid()
  1637.                                 && child.getAbsoluteX() > 10 && child
  1638.                                 .getAbsoluteY() > 300);
  1639.                         for (String sub : s)
  1640.                             if (!child.getText().toLowerCase()
  1641.                                     .contains(sub.toLowerCase()))
  1642.                                 b = false;
  1643.                         if (b)
  1644.                             return child;
  1645.                     }
  1646.                 }
  1647.             }
  1648.             return null;
  1649.         }
  1650.  
  1651.         /**
  1652.          * Checks if the inventory or bank contains the specified item
  1653.          *
  1654.          * @param id
  1655.          *            the item id
  1656.          * @return <tt>true</tt> if the bank or inventory contains the item,
  1657.          *         otherwise, <tt>false</tt>
  1658.          */
  1659.         boolean invBank(int id) {
  1660.             return bank.getCount(id) > 0 || inventory.containsOneOf(id);
  1661.         }
  1662.  
  1663.         /**
  1664.          * Gets the count of a specified item in the inventory and bank
  1665.          *
  1666.          * @param int itemId
  1667.          * @return the item count
  1668.          */
  1669.         int invBankCount(int id) {
  1670.             return bank.getCount(id) + inventory.getCount(true, id);
  1671.         }
  1672.  
  1673.         /**
  1674.          * The main loop. Call this to do the next action for the contact
  1675.          * process.
  1676.          */
  1677.         public void main() {
  1678.             switch (currentState = getState()) {
  1679.             case WITHDRAW_ASTRAL:
  1680.                 withdrawItem(ASTRAL_RUNE_ID, 1);
  1681.                 if (!invBank(ASTRAL_RUNE_ID))
  1682.                     removePouches();
  1683.                 sleep(500, 650);
  1684.                 break;
  1685.             case WITHDRAW_COSMIC:
  1686.                 withdrawItem(COSMIC_RUNE_ID, 1);
  1687.                 if (!invBank(COSMIC_RUNE_ID))
  1688.                     removePouches();
  1689.                 sleep(500, 650);
  1690.                 break;
  1691.             case WITHDRAW_AIR:
  1692.                 if (!inventory.contains(AIR_RUNE_ID))
  1693.                     withdrawItem(AIR_RUNE_ID, 1);
  1694.                 withdrawItem(AIR_RUNE_ID, 1);
  1695.                 if (invBankCount(AIR_RUNE_ID) < 2)
  1696.                     removePouches();
  1697.                 sleep(500, 650);
  1698.                 break;
  1699.             case CAST:
  1700.                 for (byte b = 0; b < 10 && bank.isOpen(); b++) {
  1701.                     bank.close();
  1702.                     sleep(500, 750);
  1703.                 }
  1704.                 if (!bank.isOpen() && game.getCurrentTab() != Game.TAB_MAGIC)
  1705.                     game.openTab(Game.TAB_MAGIC);
  1706.  
  1707.                 sleep(500, 1250);
  1708.                 magic.castSpell(Magic.SPELL_NPC_CONTACT);
  1709.                 sleep(750, 1250);
  1710.                 break;
  1711.             case SCROLL:
  1712.                 // Scroll
  1713.                 RSComponent bar = interfaces.get(MAIN_FRAME)
  1714.                         .getComponent(SCROLL_BAR).getComponent(SCROLL_BAR_DOWN);
  1715.                 Point p = bar.getCenter();
  1716.                 p.y = bar.getAbsoluteY() + bar.getHeight() - 5;
  1717.                 mouse.click(p, true);
  1718.                 sleep(750, 1250);
  1719.                 break;
  1720.             case SELECT:
  1721.                 interfaces.get(MAIN_FRAME).getComponent(NPCS_CONTAINER)
  1722.                         .getComponent(NPC_TO_SELECT).doClick();
  1723.                 sleep(750, 1250);
  1724.                 break;
  1725.             case WAIT:
  1726.                 sleep(500, 600);
  1727.                 break;
  1728.             case CONTINUE:
  1729.                 if (interfaces.canContinue())
  1730.                     interfaces.clickContinue();
  1731.                 sleep(500, 650);
  1732.                 break;
  1733.             case MENU:
  1734.                 if (getComponent(MENU_ITEM) != null)
  1735.                     getComponent(MENU_ITEM).doClick();
  1736.                 break;
  1737.             }
  1738.             sleep(900, 1200);
  1739.         }
  1740.  
  1741.         public void removePouches() {
  1742.             if (!openBank())
  1743.                 return;
  1744.             // not enough runes, bank pouches:
  1745.             for (int id : Pouch.ALL_DEGRADED)
  1746.                 if (inventory.contains(id)) {
  1747.                     bank.deposit(id, 1);
  1748.                     sleep(500, 600);
  1749.                 }
  1750.             updatePouches();
  1751.         }
  1752.     }
  1753.  
  1754.     /**
  1755.      * Loads script data as the actual script runs. Useful for images that are
  1756.      * non-essential.
  1757.      *
  1758.      * @author Equinox_
  1759.      *
  1760.      */
  1761.     public static class DataLoader extends Thread {
  1762.         EquinoxGraahkCrafter crafter;
  1763.  
  1764.         public DataLoader(EquinoxGraahkCrafter crafter) {
  1765.             super("Data Loader");
  1766.             this.crafter = crafter;
  1767.             this.start();
  1768.         }
  1769.  
  1770.         // Item Info
  1771.         public void checkGE() {
  1772.             long before = System.currentTimeMillis();
  1773.             GEItem item = crafter.getGE().lookup(PURE_ESS_ID);
  1774.             if (item != null) {
  1775.                 crafter.Paint.essPrice = getItemPrice(item);
  1776.                 crafter.log("Price of " + item.getName() + ": "
  1777.                         + getItemPrice(item));
  1778.                 crafter.log("Loaded essence information in "
  1779.                         + PaintFormatter.getFormattedTime(System
  1780.                                 .currentTimeMillis() - before));
  1781.             }
  1782.             before = System.currentTimeMillis();
  1783.             item = crafter.getGE().lookup(NATURE_RUNE_ID);
  1784.             if (item != null) {
  1785.                 crafter.Paint.natPrice = getItemPrice(item);
  1786.                 crafter.log("Price of " + item.getName() + ": "
  1787.                         + getItemPrice(item));
  1788.                 crafter.log("Loaded rune information in "
  1789.                         + PaintFormatter.getFormattedTime(System
  1790.                                 .currentTimeMillis() - before));
  1791.             }
  1792.             before = System.currentTimeMillis();
  1793.             item = crafter.getGE().lookup(SPIRIT_GRAAHK_POUCH_ID);
  1794.             if (item != null) {
  1795.                 crafter.Paint.graahkPrice = getItemPrice(item);
  1796.                 crafter.log("Price of " + item.getName() + ": "
  1797.                         + getItemPrice(item));
  1798.                 crafter.log("Loaded graahk information in "
  1799.                         + PaintFormatter.getFormattedTime(System
  1800.                                 .currentTimeMillis() - before));
  1801.             }
  1802.         }
  1803.  
  1804.         public void run() {
  1805.             if (crafter.Paint != null) {
  1806.                 checkGE();
  1807.                 try {
  1808.                     if (JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(
  1809.                             null, "Would you like to load some images?",
  1810.                             "Equinox_ Data Loader", JOptionPane.YES_NO_OPTION))
  1811.                         throw new Exception();
  1812.  
  1813.                     long before = System.currentTimeMillis();
  1814.                     int width = (int) (30 - (crafter.Paint.style.roundCorner));
  1815.                     int height = (int) (30 - (crafter.Paint.style.roundCorner));
  1816.                     int hints = Image.SCALE_FAST;
  1817.                     crafter.Paint.paintMenu.putImage(
  1818.                             "overallSkill",
  1819.                             ImageIO.read(
  1820.                                     new URL(crafter.baseURL
  1821.                                             + "/images/overallSkill.png"))
  1822.                                     .getScaledInstance(width, height, hints));
  1823.                     crafter.log("Loaded overall skill image in "
  1824.                             + PaintFormatter.getFormattedTime(System
  1825.                                     .currentTimeMillis() - before));
  1826.  
  1827.                     before = System.currentTimeMillis();
  1828.                     crafter.Paint.paintMenu.putImage(
  1829.                             "homeIcon",
  1830.                             ImageIO.read(
  1831.                                     new URL(crafter.baseURL
  1832.                                             + "/images/houseIcon.png"))
  1833.                                     .getScaledInstance(width, height, hints));
  1834.                     crafter.log("Loaded home image in "
  1835.                             + PaintFormatter.getFormattedTime(System
  1836.                                     .currentTimeMillis() - before));
  1837.  
  1838.                     before = System.currentTimeMillis();
  1839.                     crafter.Paint.paintMenu.putImage(
  1840.                             "natrune",
  1841.                             ImageIO.read(
  1842.                                     new URL(crafter.baseURL
  1843.                                             + "/images/natRune.png"))
  1844.                                     .getScaledInstance(width, height, hints));
  1845.                     crafter.log("Loaded nature image in "
  1846.                             + PaintFormatter.getFormattedTime(System
  1847.                                     .currentTimeMillis() - before));
  1848.  
  1849.                     before = System.currentTimeMillis();
  1850.                     crafter.Paint.paintMenu.putImage(
  1851.                             "tools",
  1852.                             ImageIO.read(
  1853.                                     new URL(crafter.baseURL
  1854.                                             + "/images/tools.png"))
  1855.                                     .getScaledInstance(width, height, hints));
  1856.                     crafter.log("Loaded tools icon in "
  1857.                             + PaintFormatter.getFormattedTime(System
  1858.                                     .currentTimeMillis() - before));
  1859.                 } catch (Exception e) {
  1860.                     crafter.log("Failed to load images");
  1861.                 }
  1862.             } else {
  1863.                 crafter.log("Paint not initialized - didn't load data.");
  1864.             }
  1865.         }
  1866.     }
  1867.  
  1868.     /**
  1869.      * A class for reporting errors to the host. Threaded so the confirm dialog
  1870.      * doesn't freeze the script.
  1871.      *
  1872.      * @author Equinox_
  1873.      *
  1874.      */
  1875.     public static class ErrorReporter extends Thread {
  1876.         EquinoxGraahkCrafter crafter;
  1877.         String report = "";
  1878.  
  1879.         public ErrorReporter(EquinoxGraahkCrafter crafter) {
  1880.             super("Error reporter");
  1881.             this.crafter = crafter;
  1882.             this.start();
  1883.         }
  1884.  
  1885.         public void run() {
  1886.             if (JOptionPane.NO_OPTION == JOptionPane
  1887.                     .showConfirmDialog(
  1888.                             null,
  1889.                             "Would you like to submit an error report to http://equinox_scripts.web44.net?",
  1890.                             "Equinox_ Error Reporter",
  1891.                             JOptionPane.YES_NO_OPTION))
  1892.                 return;
  1893.  
  1894.             crafter.log("Submitting error report...");
  1895.             if (crafter.errorLog.size() <= 0) {
  1896.                 crafter.log("No errors to report!");
  1897.                 return;
  1898.             }
  1899.             for (String s : crafter.errorLog)
  1900.                 report += s + "\n--------------------";
  1901.             try {
  1902.                 URL url = new URL(crafter.baseURL + "/logError.php?errors="
  1903.                         + report);
  1904.                 url.openConnection().getContentLength();
  1905.                 crafter.errorLog.clear();
  1906.                 crafter.log("Sucessfully submitted error report.");
  1907.             } catch (Exception e) {
  1908.                 crafter.log("Failed to sumbit error report!");
  1909.             }
  1910.         }
  1911.     }
  1912.  
  1913.     /**
  1914.      * Gets the grandExchange singleton for reference by other classes
  1915.      *
  1916.      * @return GrandExchange reference
  1917.      */
  1918.     public GrandExchange getGE() {
  1919.         return grandExchange;
  1920.     }
  1921.  
  1922.     /**
  1923.      * Gets the item price based on class. Works with all versions of
  1924.      * grandExchange.class
  1925.      *
  1926.      * @param item
  1927.      *            the GEItem to get the price from
  1928.      * @return int the price
  1929.      *
  1930.      * @author Equinox_
  1931.      */
  1932.     public static int getItemPrice(GEItem item) {
  1933.         for (Method m : item.getClass().getMethods())
  1934.             if (m.getName().equals("getGuidePrice")
  1935.                     || m.getName().equals("getMinPrice"))
  1936.                 try {
  1937.                     return (Integer) (m.invoke(item));
  1938.                 } catch (Exception e) {
  1939.                 }
  1940.         return 0;
  1941.     }
  1942.    
  1943.    
  1944.     //Equinox Script Includes:
  1945.     /**
  1946.      * Used for anchoring the menu bar
  1947.      *
  1948.      * @author Equinox_
  1949.      *
  1950.      */
  1951.     public enum Anchor {
  1952.         UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT
  1953.     }
  1954.     /**
  1955.      * Threaded camera control, so the bot won't pause to turn the camera.
  1956.      *
  1957.      * @author Equinox_
  1958.      *
  1959.      */
  1960.     public static class CameraControl extends Thread {
  1961.         private final Calculations calc;
  1962.         private final Camera camera;
  1963.         private int cameraAngle = 0;
  1964.         private int cameraPitch = 0;
  1965.         private int movingRandomly = 0;
  1966.         private final Script parent;
  1967.         private final Range yawVariance = new Range(15,20);
  1968.         private final Range pitchVariance = new Range(5,10);
  1969.  
  1970.         public CameraControl(Camera camera, Calculations calc, Script parent) {
  1971.             super("Equinox Camera Control");
  1972.             cameraAngle = camera.getAngle();
  1973.             cameraPitch = camera.getPitch();
  1974.             movingRandomly = 0;
  1975.             this.calc = calc;
  1976.             this.camera = camera;
  1977.             this.parent = parent;
  1978.         }
  1979.  
  1980.         /**
  1981.          * Returns the current compass orientation in degrees, with North at 0,
  1982.          * increasing counter-clockwise to 360.
  1983.          *
  1984.          * @return The current camera angle in degrees.
  1985.          */
  1986.         public int getAngle() {
  1987.             // the client uses fixed point radians 0 - 2^14
  1988.             // degrees = yaw * 360 / 2^14 = yaw / 45.5111...
  1989.             return camera.getAngle();
  1990.         }
  1991.  
  1992.         /**
  1993.          * Returns the angle between the current camera angle and the given angle in
  1994.          * degrees.
  1995.          *
  1996.          * @param degrees
  1997.          *            The target angle.
  1998.          * @return The angle between the who angles in degrees.
  1999.          */
  2000.         public int getAngleTo(int degrees) {
  2001.             int ca = getAngle();
  2002.             if (ca < degrees) {
  2003.                 ca += 360;
  2004.             }
  2005.             int da = ca - degrees;
  2006.             if (da > 180) {
  2007.                 da -= 360;
  2008.             }
  2009.             return da;
  2010.         }
  2011.  
  2012.         /**
  2013.          * Returns the camera angle at which the camera would be facing a certain
  2014.          * character.
  2015.          *
  2016.          * @param n
  2017.          *            the RSCharacter
  2018.          * @return The angle
  2019.          */
  2020.         public int getCharacterAngle(RSCharacter n) {
  2021.             return getTileAngle(n.getLocation());
  2022.         }
  2023.  
  2024.         /**
  2025.          * Returns the camera angle at which the camera would be facing a certain
  2026.          * object.
  2027.          *
  2028.          * @param o
  2029.          *            The RSObject
  2030.          * @return The angle
  2031.          */
  2032.         public int getObjectAngle(RSObject o) {
  2033.             return getTileAngle(o.getLocation());
  2034.         }
  2035.  
  2036.         /**
  2037.          * Returns the current percentage of the maximum pitch of the camera in an
  2038.          * open area.
  2039.          *
  2040.          * @return The current camera altitude percentage.
  2041.          */
  2042.         public int getPitch() {
  2043.             return camera.getPitch();
  2044.         }
  2045.  
  2046.         /**
  2047.          * Returns the camera angle at which the camera would be facing a certain
  2048.          * tile.
  2049.          *
  2050.          * @param t
  2051.          *            The target tile
  2052.          * @return The angle in degrees
  2053.          */
  2054.         public int getTileAngle(RSTile t) {
  2055.             int a = (calc.angleToTile(t) - 90) % 360;
  2056.             return a < 0 ? a + 360 : a;
  2057.         }
  2058.  
  2059.         /**
  2060.          * Returns the current x position of the camera.
  2061.          *
  2062.          * @return The x position.
  2063.          */
  2064.         public int getX() {
  2065.             return camera.getX();
  2066.         }
  2067.  
  2068.         /**
  2069.          * Returns the current y position of the camera.
  2070.          *
  2071.          * @return The y position.
  2072.          */
  2073.         public int getY() {
  2074.             return camera.getY();
  2075.         }
  2076.  
  2077.         /**
  2078.          * Returns the current z position of the camera.
  2079.          *
  2080.          * @return The z position.
  2081.          */
  2082.         public int getZ() {
  2083.             return camera.getZ();
  2084.         }
  2085.  
  2086.         /**
  2087.          * Moves the camera in a random direction for a given time.
  2088.          *
  2089.          * @param timeOut
  2090.          *            The maximum time in milliseconds to move the camera for.
  2091.          */
  2092.         public void moveRandomly(int timeOut) {
  2093.             movingRandomly = timeOut;
  2094.         }
  2095.  
  2096.         /**
  2097.          * Get a random number in between min and max
  2098.          *
  2099.          * @return random int between inclusive minimum and exclusive maximum
  2100.          */
  2101.         public int random(int MIN, int MAX) {
  2102.             return (int) (Math.random() * (Math.max(MAX, MIN) - Math.min(MAX, MIN)))
  2103.                     + Math.min(MAX, MIN);
  2104.         }
  2105.  
  2106.         public boolean isRunning() {
  2107.             return this.isAlive();
  2108.         }
  2109.  
  2110.         public void run() {
  2111.             parent.log("Started camera control!");
  2112.             while (parent.isRunning()) {
  2113.                 if (parent.isActive()) {
  2114.                     if (movingRandomly > 0) {
  2115.                         camera.moveRandomly(movingRandomly);
  2116.                         movingRandomly = 0;
  2117.                     } else {
  2118.                         if (Math.abs(camera.getAngleTo(cameraAngle))>yawVariance.randomLinearDistribution())
  2119.                             camera.setAngle(cameraAngle);
  2120.                         if (Math.abs(camera.getPitch() - cameraPitch)>pitchVariance.randomLinearDistribution())
  2121.                             camera.setPitch(cameraPitch);
  2122.                     }
  2123.                 }
  2124.             }
  2125.             parent.log("Camera control stopped!");
  2126.         }
  2127.  
  2128.         /**
  2129.          * Rotates the camera to a specific angle in the closest direction.
  2130.          *
  2131.          * @param degrees
  2132.          *            The angle to rotate to.
  2133.          */
  2134.         public void setAngle(int degrees) {
  2135.             cameraAngle = degrees;
  2136.         }
  2137.  
  2138.         /**
  2139.          * Rotates the camera to the specified cardinal direction.
  2140.          *
  2141.          * @param direction
  2142.          *            The char direction to turn the map. char options are w,s,e,n
  2143.          *            and defaults to north if character is unrecognized.
  2144.          */
  2145.         public void setCompass(char direction) {
  2146.             switch (direction) {
  2147.             case 'n':
  2148.                 setAngle(359);
  2149.                 break;
  2150.             case 'w':
  2151.                 setAngle(89);
  2152.                 break;
  2153.             case 's':
  2154.                 setAngle(179);
  2155.                 break;
  2156.             case 'e':
  2157.                 setAngle(269);
  2158.                 break;
  2159.             default:
  2160.                 setAngle(359);
  2161.                 break;
  2162.             }
  2163.         }
  2164.  
  2165.         /**
  2166.          * Uses the compass component to set the camera to face north.
  2167.          * @deprecated Uses the mouse.  Frowned on here.
  2168.          */
  2169.         public void setNorth() {
  2170.             camera.setNorth();
  2171.         }
  2172.  
  2173.         /**
  2174.          * Sets the altitude to max or minimum.
  2175.          *
  2176.          * @param up
  2177.          *            True to go up. False to go down.
  2178.          * @return <tt>true</tt> if the altitude was changed.
  2179.          */
  2180.         public void setPitch(boolean up) {
  2181.             if (up) {
  2182.                 setPitch(100);
  2183.             } else {
  2184.                 setPitch(0);
  2185.             }
  2186.         }
  2187.  
  2188.         /**
  2189.          * Set the camera to a certain percentage of the maximum pitch. Don't rely
  2190.          * on the return value too much - it should return whether the camera was
  2191.          * successfully set, but it isn't very accurate near the very extremes of
  2192.          * the height.
  2193.          * <p/>
  2194.          * <p/>
  2195.          * This also depends on the maximum camera angle in a region, as it changes
  2196.          * depending on situation and surroundings. So in some areas, 68% might be
  2197.          * the maximum altitude. This method will do the best it can to switch the
  2198.          * camera altitude to what you want, but if it hits the maximum or stops
  2199.          * moving for any reason, it will return.
  2200.          * <p/>
  2201.          * <p/>
  2202.          * <p/>
  2203.          * Mess around a little to find the altitude percentage you like. In later
  2204.          * versions, there will be easier-to-work-with methods regarding altitude.
  2205.          *
  2206.          * @param percent
  2207.          *            The percentage of the maximum pitch to set the camera to.
  2208.          * @return true if the camera was successfully moved; otherwise false.
  2209.          */
  2210.         public void setPitch(int percent) {
  2211.             cameraPitch = percent;
  2212.         }
  2213.  
  2214.         /**
  2215.          * Turns to a RSCharacter (RSNPC or RSPlayer).
  2216.          *
  2217.          * @param c
  2218.          *            The RSCharacter to turn to.
  2219.          */
  2220.         public void turnTo(final RSCharacter c) {
  2221.             int angle = getCharacterAngle(c);
  2222.             setAngle(angle);
  2223.         }
  2224.  
  2225.         /**
  2226.          * Turns to within a few degrees of an RSCharacter (RSNPC or RSPlayer).
  2227.          *
  2228.          * @param c
  2229.          *            The RSCharacter to turn to.
  2230.          * @param dev
  2231.          *            The maximum difference in the angle.
  2232.          */
  2233.         public void turnTo(final RSCharacter c, final int dev) {
  2234.             int angle = getCharacterAngle(c);
  2235.             angle = random(angle - dev, angle + dev + 1);
  2236.             setAngle(angle);
  2237.         }
  2238.  
  2239.         /**
  2240.          * Turns to an RSObject.
  2241.          *
  2242.          * @param o
  2243.          *            The RSObject to turn to.
  2244.          */
  2245.         public void turnTo(final RSObject o) {
  2246.             int angle = getObjectAngle(o);
  2247.             setAngle(angle);
  2248.         }
  2249.  
  2250.         /**
  2251.          * Turns to within a few degrees of an RSObject.
  2252.          *
  2253.          * @param o
  2254.          *            The RSObject to turn to.
  2255.          * @param dev
  2256.          *            The maximum difference in the turn angle.
  2257.          */
  2258.         public void turnTo(final RSObject o, final int dev) {
  2259.             int angle = getObjectAngle(o);
  2260.             angle = random(angle - dev, angle + dev + 1);
  2261.             setAngle(angle);
  2262.         }
  2263.  
  2264.         /**
  2265.          * Turns to a specific RSTile.
  2266.          *
  2267.          * @param tile
  2268.          *            Tile to turn to.
  2269.          */
  2270.         public void turnTo(final RSTile tile) {
  2271.             int angle = getTileAngle(tile);
  2272.             setAngle(angle);
  2273.         }
  2274.  
  2275.         /**
  2276.          * Turns within a few degrees to a specific RSTile.
  2277.          *
  2278.          * @param tile
  2279.          *            Tile to turn to.
  2280.          * @param dev
  2281.          *            Maximum deviation from the angle to the tile.
  2282.          */
  2283.         public void turnTo(final RSTile tile, final int dev) {
  2284.             int angle = getTileAngle(tile);
  2285.             angle = random(angle - dev, angle + dev + 1);
  2286.             setAngle(angle);
  2287.         }
  2288.  
  2289.         /**
  2290.          * Turns to an RSCharacter (RSNPC or RSPlayer).
  2291.          *
  2292.          * @deprecated
  2293.          * @param c
  2294.          *            The RSCharacter to turn to.
  2295.          */
  2296.         public void turnToCharacter(RSCharacter c) {
  2297.             int angle = getCharacterAngle(c);
  2298.             setAngle(angle);
  2299.         }
  2300.  
  2301.         /**
  2302.          * Turns to within a few degrees of an RSCharacter (RSNPC or RSPlayer).
  2303.          *
  2304.          * @deprecated
  2305.          * @param c
  2306.          *            The RSCharacter to turn to.
  2307.          * @param dev
  2308.          *            The maximum difference in the angle.
  2309.          */
  2310.         public void turnToCharacter(RSCharacter c, int dev) {
  2311.             int angle = getCharacterAngle(c);
  2312.             angle = random(angle - dev, angle + dev + 1);
  2313.             setAngle(angle);
  2314.         }
  2315.  
  2316.         /**
  2317.          * Turns to an RSObject
  2318.          *
  2319.          * @deprecated
  2320.          * @param o
  2321.          *            The RSObject to turn to.
  2322.          */
  2323.         public void turnToObject(RSObject o) {
  2324.             int angle = getObjectAngle(o);
  2325.             setAngle(angle);
  2326.         }
  2327.  
  2328.         /**
  2329.          * Turns to within a few degrees of an RSObject.
  2330.          *
  2331.          * @deprecated
  2332.          * @param o
  2333.          *            The RSObject to turn to.
  2334.          * @param dev
  2335.          *            The maximum difference in the turn angle.
  2336.          */
  2337.         public void turnToObject(RSObject o, int dev) {
  2338.             int angle = getObjectAngle(o);
  2339.             angle = random(angle - dev, angle + dev + 1);
  2340.             setAngle(angle);
  2341.         }
  2342.  
  2343.         /**
  2344.          * Turns to a specific RSTile.
  2345.          *
  2346.          * @deprecated
  2347.          * @param tile
  2348.          *            Tile to turn to.
  2349.          */
  2350.         public void turnToTile(RSTile tile) {
  2351.             int angle = getTileAngle(tile);
  2352.             setAngle(angle);
  2353.         }
  2354.  
  2355.         /**
  2356.          * Turns within a few degrees to a specific RSTile.
  2357.          *
  2358.          * @deprecated
  2359.          * @param tile
  2360.          *            Tile to turn to.
  2361.          * @param dev
  2362.          *            Maximum deviation from the angle to the tile.
  2363.          */
  2364.         public void turnToTile(RSTile tile, int dev) {
  2365.             int angle = getTileAngle(tile);
  2366.             angle = random(angle - dev, angle + dev + 1);
  2367.             setAngle(angle);
  2368.         }
  2369.     }
  2370.     /**
  2371.      * Used for specifing the way a menu is layed out.
  2372.      *
  2373.      * @author Equinox_
  2374.      *
  2375.      */
  2376.     public enum FlowType {
  2377.         VERTICAL, HORIZONTAL
  2378.     }/**
  2379.      * Class representing the method for getting the current percentile for a
  2380.      * progress bar
  2381.      *
  2382.      * @author Equinox_
  2383.      *
  2384.      */
  2385.     public static abstract class GetPercent {
  2386.         public abstract double getPercent();
  2387.     }
  2388.  
  2389.  
  2390.     /**
  2391.      * Represents a class containing the method for getting the popup
  2392.      *
  2393.      * @author Equinox_
  2394.      *
  2395.      */
  2396.     public static abstract class GetPopup {
  2397.         public abstract PaintMenuPopUp getPopUp();
  2398.  
  2399.     public abstract String getName();
  2400.     }
  2401.  
  2402.  
  2403.     /**
  2404.      * A class for representing the validation of a PaintMenu component
  2405.      *
  2406.      * @author Equinox_
  2407.      *
  2408.      */
  2409.     public static abstract class IsValid {
  2410.         public abstract boolean isValid();
  2411.     }
  2412.     /**
  2413.      * Class representing the LineListeners in popups
  2414.      *
  2415.      * @author Equinox_
  2416.      *
  2417.      */
  2418.     public static abstract class LineListener {
  2419.         public abstract void fireEvent(MouseEvent e, String line);
  2420.  
  2421.         public abstract boolean hasAction(String line);
  2422.     }
  2423.     /**
  2424.      * Represents a modified MouseListener used in MenuEvents
  2425.      *
  2426.      * @author Equinox_
  2427.      *
  2428.      */
  2429.     public static abstract class MenuMouseListener {
  2430.         public abstract void MouseAction(MouseEvent e);
  2431.     }
  2432.     /**
  2433.      * A class that will take a specified mesh, generate a path through it, then
  2434.      * round the path off with Bezier curves.
  2435.      *
  2436.      * @author Equinox_
  2437.      *
  2438.      */
  2439.     public static class MeshPath {
  2440.         public static final Color BLOCK_PATH_COLOR = new Color(0f, 1f, 0f, 1f);
  2441.         public static final Color MESH_BORDER = new Color(0.75f, 0.75f, 0.75f,
  2442.                 0.75f);
  2443.         public static final Color MESH_FILL = new Color(0.5f, 0.5f, 0.5f, 0.75f);
  2444.         public static final Color SMOOTH_PATH_COLOR = new Color(0f, 0f, 1f, 1f);
  2445.         private List<RSTile> blockPath = new ArrayList<RSTile>();
  2446.         private final Calculations calc;
  2447.         private boolean end;
  2448.         private List<RSTile[]> mesh = new ArrayList<RSTile[]>();
  2449.         private final Players players;
  2450.         private List<RSTile> smoothPath = new ArrayList<RSTile>();
  2451.         private final Walking walking;
  2452.         private final Range walkingDistance;
  2453.  
  2454.         /**
  2455.          * Creates a mesh path with the provided walking methods, calculation
  2456.          * methods, player methods, and walking range.
  2457.          *
  2458.          * @param walking
  2459.          * @param calc
  2460.          * @param players
  2461.          */
  2462.         public MeshPath(Walking walking, Calculations calc, Players players) {
  2463.             this(walking, calc, players, new Range(7, 9));
  2464.         }
  2465.  
  2466.         /**
  2467.          * Creates a mesh path with the provided walking methods, calculation
  2468.          * methods, player methods, and walking range.
  2469.          *
  2470.          * @param walking
  2471.          * @param calc
  2472.          * @param players
  2473.          * @param walkingDistance
  2474.          */
  2475.         public MeshPath(Walking walking, Calculations calc, Players players,
  2476.                 Range walkingDistance) {
  2477.             this.walking = walking;
  2478.             this.calc = calc;
  2479.             this.players = players;
  2480.             this.walkingDistance = walkingDistance;
  2481.         }
  2482.  
  2483.         /**
  2484.          * Creates a mesh path with the provided walking methods, calculation
  2485.          * methods, player methods, and mesh.
  2486.          *
  2487.          * @param walking
  2488.          * @param calc
  2489.          * @param players
  2490.          * @param mesh
  2491.          */
  2492.         public MeshPath(Walking walking, Calculations calc, Players players,
  2493.                 RSTile[][] mesh) {
  2494.             this(walking, calc, players);
  2495.             setMesh(mesh);
  2496.         }
  2497.  
  2498.         /**
  2499.          * Creates a mesh path with the provided walking methods, calculation
  2500.          * methods, player methods, walking range, and mesh.
  2501.          *
  2502.          * @param walking
  2503.          * @param calc
  2504.          * @param players
  2505.          * @param walkingDistance
  2506.          * @param mesh
  2507.          */
  2508.         public MeshPath(Walking walking, Calculations calc, Players players,
  2509.                 RSTile[][] mesh, Range walkingDistance) {
  2510.             this(walking, calc, players, walkingDistance);
  2511.             setMesh(mesh);
  2512.         }
  2513.  
  2514.         /**
  2515.          * Adds a line to the mesh with the given tiles
  2516.          *
  2517.          * @param tiles
  2518.          *            the RSTiles making up the line
  2519.          */
  2520.         public void addLine(RSTile... tiles) {
  2521.             if (mesh.size() == 0) {
  2522.                 mesh.add(tiles);
  2523.                 return;
  2524.             }
  2525.             RSTile first = tiles[0];
  2526.             RSTile last = tiles[tiles.length - 1];
  2527.             RSTile[] oLine = mesh.get(mesh.size() - 1);
  2528.             RSTile oFirst = oLine[0];
  2529.             RSTile oLast = oLine[oLine.length - 1];
  2530.             double straight = calc.distanceBetween(first, oFirst)
  2531.                     + calc.distanceBetween(last, oLast);
  2532.             double crossed = calc.distanceBetween(first, oLast)
  2533.                     + calc.distanceBetween(last, oFirst);
  2534.             if (straight > crossed) {
  2535.                 mesh.add(tiles);
  2536.                 return;
  2537.             } else {
  2538.                 RSTile[] reversed = new RSTile[tiles.length];
  2539.                 for (int i = 0; i < tiles.length; i++)
  2540.                     reversed[i] = tiles[tiles.length - 1 - i];
  2541.                 mesh.add(reversed);
  2542.                 return;
  2543.             }
  2544.         }
  2545.  
  2546.         /**
  2547.          * Adds all the lines of a specified mesh to the current mesh.
  2548.          *
  2549.          * @param mesh
  2550.          *            as RSTile[][]
  2551.          */
  2552.         public void addMesh(RSTile[][] mesh) {
  2553.             for (RSTile[] line : mesh)
  2554.                 addLine(line);
  2555.         }
  2556.  
  2557.         /**
  2558.          * Clears the current path and all refined versions of it.
  2559.          */
  2560.         public void clearPath() {
  2561.             blockPath.clear();
  2562.             smoothPath.clear();
  2563.         }
  2564.  
  2565.         /**
  2566.          * Finds a path between the beginning and end of the mesh using the
  2567.          * WEIGHT_CENTER placement type, then sets the current path to the found
  2568.          * path
  2569.          */
  2570.         public void findPath() {
  2571.             findPath(MeshPathType.WEIGHT_CENTER);
  2572.         }
  2573.  
  2574.         /**
  2575.          * Finds a path between the beginning and end of the mesh using the provided
  2576.          * placement type, then sets the current path to the found path
  2577.          *
  2578.          * @param placement
  2579.          *            as MeshPathType
  2580.          */
  2581.         public void findPath(MeshPathType placement) {
  2582.             findPath(placement, 0, mesh.size() - 1);
  2583.         }
  2584.  
  2585.         /**
  2586.          * Finds a path between the start index and the end index using the provided
  2587.          * placement type, then sets the current path to the found path
  2588.          *
  2589.          * @param placement
  2590.          *            as MeshPathType
  2591.          * @param start
  2592.          *            index
  2593.          * @param end
  2594.          *            index
  2595.          */
  2596.         public void findPath(MeshPathType placement, int start, int end) {
  2597.             blockPath.clear();
  2598.             for (int i = start; i <= end; i++)
  2599.                 blockPath.add(getTileOnLine(placement, mesh.get(i)));
  2600.             smoothPath = smoothPath(blockPath);
  2601.         }
  2602.  
  2603.         /**
  2604.          * Gets the end tile of this path.
  2605.          *
  2606.          * @return The end <code>RSTile</code>.
  2607.          */
  2608.         public RSTile getEnd() {
  2609.             if (smoothPath.size() > 0)
  2610.                 return smoothPath.get(smoothPath.size() - 1);
  2611.             else
  2612.                 return null;
  2613.         }
  2614.  
  2615.         /**
  2616.          * Gets the next immediately available vertex in this path.
  2617.          *
  2618.          * @return The next walkable <code>RSTile</code>.
  2619.          */
  2620.         public RSTile getNext() {
  2621.             if (smoothPath.size() > 0)
  2622.                 for (int i = smoothPath.size() - 1; i >= 0; i--)
  2623.                     if (calc.tileOnMap(smoothPath.get(i)))
  2624.                         return smoothPath.get(i);
  2625.  
  2626.             return null;
  2627.         }
  2628.  
  2629.         /**
  2630.          * Gets the start tile of this path.
  2631.          *
  2632.          * @return The start <code>RSTile</code>.
  2633.          */
  2634.         public RSTile getStart() {
  2635.             if (smoothPath.size() > 0)
  2636.                 return smoothPath.get(0);
  2637.             else
  2638.                 return null;
  2639.         }
  2640.  
  2641.         /**
  2642.          * Gets the nearest tile in the mesh to the provided RSTile
  2643.          *
  2644.          * @param tile
  2645.          *            the RSTile to use
  2646.          * @return RSTile tile in mesh
  2647.          */
  2648.         public RSTile getTileInMesh(RSTile tile) {
  2649.             if (inMesh(tile))
  2650.                 return tile;
  2651.             double bestDist = Double.MAX_VALUE;
  2652.             RSTile nearest = null;
  2653.             if (mesh.size() > 1) {
  2654.                 for (int i = 1; i < mesh.size(); i++) {
  2655.                     RSTile[] lineA = mesh.get(i - 1);
  2656.                     RSTile[] lineB = mesh.get(i);
  2657.                     if (lineA != null)
  2658.                         for (RSTile t : lineA) {
  2659.                             double dist = calc.distanceBetween(t, tile);
  2660.                             if (dist < bestDist) {
  2661.                                 nearest = new RSTile(t.getX(), t.getY());
  2662.                                 bestDist = Double.valueOf(dist);
  2663.                             }
  2664.                         }
  2665.                     if (lineB != null)
  2666.                         for (RSTile t : lineB) {
  2667.                             double dist = calc.distanceBetween(t, tile);
  2668.                             if (dist < bestDist) {
  2669.                                 nearest = new RSTile(t.getX(), t.getY());
  2670.                                 bestDist = Double.valueOf(dist);
  2671.                             }
  2672.                         }
  2673.                 }
  2674.             }
  2675.             return nearest;
  2676.         }
  2677.  
  2678.         /**
  2679.          * Gets a tile on the provided line using the provided method
  2680.          *
  2681.          * @param placement
  2682.          *            as MeshPathType
  2683.          * @param line
  2684.          *            as RSTile...
  2685.          * @return RSTile tile on line
  2686.          */
  2687.         public RSTile getTileOnLine(MeshPathType placement, RSTile... line) {
  2688.             List<RSTile> tiles = getTilesOnLines(line);
  2689.             int index = 0;
  2690.             switch (placement) {
  2691.             case ALL_CENTER:
  2692.                 index = new Range((int) (tiles.size() * .25d),
  2693.                         (int) (tiles.size() * 0.75d)).randomLinearDistribution();
  2694.                 break;
  2695.             case FULL:
  2696.                 index = new Range(0, tiles.size()).randomLinearDistribution();
  2697.                 break;
  2698.             case WEIGHT_CENTER:
  2699.                 index = new Range(0, tiles.size()).randomNormalDistribution();
  2700.                 break;
  2701.             }
  2702.  
  2703.             if (index < 0)
  2704.                 index = 0;
  2705.             if (index >= tiles.size())
  2706.                 index = tiles.size() - 1;
  2707.  
  2708.             if (tiles.size() > 0)
  2709.                 return tiles.get(index);
  2710.             else
  2711.                 return null;
  2712.         }
  2713.  
  2714.         /**
  2715.          * Gets a tile on the provided line using the ALL_CENTER method
  2716.          *
  2717.          * @param line
  2718.          *            as RSTile...
  2719.          * @return RSTile tile on line
  2720.          */
  2721.         public RSTile getTileOnLine(RSTile... line) {
  2722.             return getTileOnLine(MeshPathType.WEIGHT_CENTER, line);
  2723.         }
  2724.  
  2725.         /**
  2726.          * Returns an array containing all of the vertices in this path.
  2727.          *
  2728.          * @return an array containing all of the vertices in this path.
  2729.          */
  2730.         public RSTile[] getTilePath() {
  2731.             RSTile[] tiles = new RSTile[smoothPath.size()];
  2732.             for (int i = 0; i < tiles.length; i++)
  2733.                 tiles[i] = new RSTile(smoothPath.get(i).getX(), smoothPath.get(i)
  2734.                         .getY());
  2735.             return tiles;
  2736.         }
  2737.  
  2738.         public List<RSTile> getTilesOnLine(RSTile a, RSTile b) {
  2739.             List<RSTile> sublineTiles = new ArrayList<RSTile>();
  2740.             int deltaX = b.getX() - a.getX();
  2741.             int deltaY = b.getY() - a.getY();
  2742.             double dist = calc.distanceBetween(a, b);
  2743.             if (dist == 0)
  2744.                 dist = 1;
  2745.             double eX = deltaX / dist, eY = deltaY / dist;
  2746.             for (int i = 0; i <= dist; i++)
  2747.                 sublineTiles.add(new RSTile(a.getX() + (int) (eX * i), a.getY()
  2748.                         + (int) (eY * i)));
  2749.             return sublineTiles;
  2750.         }
  2751.  
  2752.         /**
  2753.          * Gets all the tiles on the provided line
  2754.          *
  2755.          * @param line
  2756.          *            as RSTile...
  2757.          * @return List<RSTile> tiles on line
  2758.          */
  2759.         public List<RSTile> getTilesOnLines(RSTile... line) {
  2760.             List<RSTile> sublineTiles = new ArrayList<RSTile>();
  2761.             if (line.length == 1)
  2762.                 sublineTiles.add(line[0]);
  2763.             else
  2764.                 for (int i = 1; i < line.length; i++) {
  2765.                     RSTile a = line[i - 1];
  2766.                     RSTile b = line[i];
  2767.                     sublineTiles.addAll(getTilesOnLine(a, b));
  2768.                 }
  2769.             return sublineTiles;
  2770.         }
  2771.  
  2772.         /**
  2773.          * Checks if we have a path
  2774.          *
  2775.          * @return boolean has path
  2776.          */
  2777.         public boolean hasPath() {
  2778.             return smoothPath.size() > 0;
  2779.         }
  2780.  
  2781.         /**
  2782.          * Checks to see if the provided RSTile is inside the mesh
  2783.          *
  2784.          * @param tile
  2785.          *            the RSTile to check
  2786.          * @return boolean is in mesh
  2787.          */
  2788.         public boolean inMesh(RSTile tile) {
  2789.             if (mesh.size() > 1) {
  2790.                 for (int i = 1; i < mesh.size(); i++) {
  2791.                     RSTile[] lineA = mesh.get(i - 1);
  2792.                     RSTile[] lineB = mesh.get(i);
  2793.                     Polygon poly = new Polygon();
  2794.                     if (lineA != null)
  2795.                         for (RSTile t : lineA)
  2796.                             poly.addPoint(t.getX(), t.getY());
  2797.                     if (lineB != null)
  2798.                         for (RSTile t : lineB)
  2799.                             poly.addPoint(t.getX(), t.getY());
  2800.  
  2801.                     if (poly.npoints >= 2)
  2802.                         if (poly.contains(new Point(tile.getX(), tile.getY())))
  2803.                             return true;
  2804.                 }
  2805.             }
  2806.             return false;
  2807.         }
  2808.  
  2809.         /**
  2810.          * Checks whether or not this path can be traversed by the player. This will
  2811.          * be the case provided that the player near to one of its vertices, but not
  2812.          * already standing on the end vertex.
  2813.          *
  2814.          * @return <tt>true</tt> if the player can walk along this path; otherwise
  2815.          *         <tt>false</tt>.
  2816.          */
  2817.         public boolean isValid() {
  2818.             return smoothPath.size() > 0 && getNext() != null
  2819.                     && !players.getMyPlayer().getLocation().equals(getEnd());
  2820.         }
  2821.  
  2822.         /**
  2823.          * Gets the number of lines in the mesh
  2824.          *
  2825.          * @return int size
  2826.          */
  2827.         public int meshSize() {
  2828.             return mesh.size();
  2829.         }
  2830.  
  2831.         /**
  2832.          * Paints the mesh, and all versions of the current path to the minimap
  2833.          * using the provided graphics object
  2834.          *
  2835.          * @param g
  2836.          *            as Graphics2D
  2837.          */
  2838.         public void paintAll(Graphics2D g) {
  2839.             paintMesh(g);
  2840.             paintBlockPath(g);
  2841.             paintSmoothPath(g);
  2842.         }
  2843.  
  2844.         /**
  2845.          * Paints the current un-smoothed path to the minimap using the provided
  2846.          * graphics object
  2847.          *
  2848.          * @param g
  2849.          *            as Graphics2D
  2850.          */
  2851.         public void paintBlockPath(Graphics2D g) {
  2852.             if (blockPath.size() > 1) {
  2853.                 g.setColor(BLOCK_PATH_COLOR);
  2854.                 Point a = null;
  2855.                 for (RSTile t : blockPath) {
  2856.                     Point b = calc.tileToMinimap(t);
  2857.                     if (b.x > 0 && b.y > 0)
  2858.                         g.fillOval(b.x - 2, b.y - 2, 5, 5);
  2859.                     if (a != null && a.x > 0 && a.y > 0 && b.x > 0 && b.y > 0)
  2860.                         g.drawLine(a.x, a.y, b.x, b.y);
  2861.                     a = b;
  2862.                 }
  2863.             }
  2864.         }
  2865.  
  2866.         /**
  2867.          * Paints the mesh to the provided graphics context in the minimap
  2868.          *
  2869.          * @param g
  2870.          *            as Graphics2D
  2871.          */
  2872.         public void paintMesh(Graphics2D g) {
  2873.             if (mesh.size() > 1) {
  2874.                 Point[] oldLine = null;
  2875.                 for (int i = 0; i < mesh.size(); i++) {
  2876.                     RSTile[] lineB = mesh.get(i);
  2877.                     Point[] thisLine = new Point[lineB.length];
  2878.                     Polygon poly = new Polygon();
  2879.                     boolean onMap = true;
  2880.                     for (int tile = 0; tile < thisLine.length; tile++) {
  2881.                         Point p = calc.tileToMinimap(lineB[tile]);
  2882.                         thisLine[tile] = p;
  2883.                         poly.addPoint(p.x, p.y);
  2884.                         if (p.x == -1 || p.y == -1)
  2885.                             onMap = false;
  2886.                     }
  2887.                     if (oldLine != null) {
  2888.                         for (Point p : oldLine) {
  2889.                             poly.addPoint(p.x, p.y);
  2890.                             if (p.x == -1 || p.y == -1)
  2891.                                 onMap = false;
  2892.                         }
  2893.                         if (poly.npoints >= 2 && onMap) {
  2894.                             g.setColor(MESH_BORDER);
  2895.                             g.draw(poly);
  2896.                             g.setColor(MESH_FILL);
  2897.                             g.fill(poly);
  2898.                         }
  2899.                     }
  2900.                     oldLine = thisLine;
  2901.                 }
  2902.             }
  2903.         }
  2904.  
  2905.         /**
  2906.          * Paints the smoothed version of the current path to the minimap using the
  2907.          * provided graphics object
  2908.          *
  2909.          * @param g
  2910.          *            as Graphics2D
  2911.          */
  2912.         public void paintSmoothPath(Graphics2D g) {
  2913.             if (smoothPath.size() > 1) {
  2914.                 g.setColor(SMOOTH_PATH_COLOR);
  2915.                 Point a = null;
  2916.                 for (RSTile t : smoothPath) {
  2917.                     Point b = calc.tileToMinimap(t);
  2918.                     if (b.x > 0 && b.y > 0)
  2919.                         g.fillOval(b.x - 2, b.y - 2, 5, 5);
  2920.                     if (a != null && a.x > 0 && a.y > 0 && b.x > 0 && b.y > 0)
  2921.                         g.drawLine(a.x, a.y, b.x, b.y);
  2922.                     a = b;
  2923.                 }
  2924.             }
  2925.         }
  2926.  
  2927.         /**
  2928.          * Reverses this path.
  2929.          *
  2930.          * @return This path.
  2931.          */
  2932.         public MeshPath reverse() {
  2933.             MeshPath m = new MeshPath(walking, calc, players);
  2934.             for (int i = mesh.size() - 1; i >= 0; i--) {
  2935.                 m.addLine(blockPath.get(i));
  2936.             }
  2937.             return m;
  2938.         }
  2939.  
  2940.         /**
  2941.          * Sets the current mesh to the provided mesh
  2942.          *
  2943.          * @param mesh
  2944.          *            as RSTile[][]
  2945.          */
  2946.         public void setMesh(RSTile[][] mesh) {
  2947.             this.mesh.clear();
  2948.             addMesh(mesh);
  2949.         }
  2950.  
  2951.         /**
  2952.          * Smoothes the provided List of RSTiles using Bezier curves. Will return a
  2953.          * path with 4 times as many vertices
  2954.          *
  2955.          * @param path
  2956.          *            as List
  2957.          * @return List<RSTile> smoothed path
  2958.          */
  2959.         public List<RSTile> smoothPath(List<RSTile> path) {
  2960.             List<RSTile> smoothed = new ArrayList<RSTile>();
  2961.             if (path.size() >= 3) {
  2962.                 for (int i = 0; i < path.size(); i++) {
  2963.                     if (i + 2 < path.size()) {
  2964.                         RSTile tile1 = path.get(i);
  2965.                         RSTile tile2 = path.get(i + 1);
  2966.                         RSTile tile3 = path.get(i + 2);
  2967.                         if (i == 0)
  2968.                             smoothed.add(tile1);
  2969.                         double x2, y2;
  2970.                         double t = 0.5d;
  2971.                         // use Berstein polynomials
  2972.                         x2 = (tile1.getX() + t
  2973.                                 * (-tile1.getX() * 3 + t
  2974.                                         * (3 * tile1.getX() - tile1.getX() * t)))
  2975.                                 + t
  2976.                                 * (3 * tile2.getX() + t
  2977.                                         * (-6 * tile2.getX() + tile2.getX() * 3 * t))
  2978.                                 + t
  2979.                                 * t
  2980.                                 * (tile2.getX() * 3 - tile2.getX() * 3 * t)
  2981.                                 + tile3.getX() * t * t * t;
  2982.                         y2 = (tile1.getY() + t
  2983.                                 * (-tile1.getY() * 3 + t
  2984.                                         * (3 * tile1.getY() - tile1.getY() * t)))
  2985.                                 + t
  2986.                                 * (3 * tile2.getY() + t
  2987.                                         * (-6 * tile2.getY() + tile2.getY() * 3 * t))
  2988.                                 + t
  2989.                                 * t
  2990.                                 * (tile2.getY() * 3 - tile2.getY() * 3 * t)
  2991.                                 + tile3.getY() * t * t * t;
  2992.                         smoothed.add(getTileInMesh(new RSTile((int) x2, (int) y2)));
  2993.                     } else {
  2994.                         // Just add the vert.
  2995.                         smoothed.add(path.get(i));
  2996.                     }
  2997.                 }
  2998.             }
  2999.             return smoothed;
  3000.         }
  3001.  
  3002.         /**
  3003.          * Returns a string representation of this Mesh Path.
  3004.          *
  3005.          * @return String
  3006.          */
  3007.         public String toString() {
  3008.             return "{ Line count: " + mesh.size() + ", Path size: "
  3009.                     + (hasPath() ? smoothPath.size() : "No path") + " }";
  3010.         }
  3011.  
  3012.         /**
  3013.          * Takes a step along this path if appropriate. Specifies only
  3014.          * TraversalOption.SPACE_ACTIONS.
  3015.          *
  3016.          * @return <tt>true</tt> if this path is currently valid for the player;
  3017.          *         otherwise <tt>false</tt>.
  3018.          * @see #traverse(EnumSet)
  3019.          */
  3020.         public boolean traverse() {
  3021.             return this.traverse(EnumSet.of(TraversalOption.HANDLE_RUN,
  3022.                     TraversalOption.SPACE_ACTIONS));
  3023.         }
  3024.  
  3025.         /**
  3026.          * Takes a step along this path if appropriate. If the path cannot be
  3027.          * traversed due to the player being too far from its vertices or already at
  3028.          * the end vertex, <tt>false</tt> will be returned. In all other cases,
  3029.          * <tt>true</tt> will be returned, but an action will not necessarily be
  3030.          * performed (based on the given options).
  3031.          *
  3032.          * @param options
  3033.          *            Walking style options.
  3034.          * @return <tt>true</tt> if this path is currently valid for the player;
  3035.          *         otherwise <tt>false</tt>.
  3036.          */
  3037.         public boolean traverse(EnumSet<TraversalOption> options) {
  3038.             RSTile next = getNext();
  3039.             if (next == null) {
  3040.                 return false;
  3041.             }
  3042.             if (next.equals(getEnd())) {
  3043.                 if (calc.distanceTo(next) <= 1
  3044.                         || (end && players.getMyPlayer().isMoving())
  3045.                         || next.equals(walking.getDestination())) {
  3046.                     return false;
  3047.                 }
  3048.                 end = true;
  3049.             } else {
  3050.                 end = false;
  3051.             }
  3052.             if (options.contains(TraversalOption.HANDLE_RUN)
  3053.                     && !walking.isRunEnabled() && walking.getEnergy() > 50) {
  3054.                 walking.setRun(true);
  3055.             }
  3056.             if (options.contains(TraversalOption.SPACE_ACTIONS)) {
  3057.                 RSTile dest = walking.getDestination();
  3058.                 if (dest != null
  3059.                         && players.getMyPlayer().isMoving()
  3060.                         && calc.distanceTo(dest) > 5
  3061.                         && calc.distanceBetween(next, dest) < walkingDistance
  3062.                                 .randomLinearDistribution()) {
  3063.                     return true;
  3064.                 }
  3065.             }
  3066.             return walking.walkTileMM(next, 0, 0);
  3067.         }
  3068.  
  3069.         /**
  3070.          * Gets the nearest tile on the path by distance alone
  3071.          * @return RSTile
  3072.          */
  3073.         public RSTile getNearest() {
  3074.             int len = Integer.MAX_VALUE;
  3075.             RSTile best = null;
  3076.             if (hasPath())
  3077.                 for (RSTile t : smoothPath)
  3078.                     if (calc.distanceTo(t) < len) {
  3079.                         best = t;
  3080.                         len = calc.distanceTo(t);
  3081.                     }
  3082.             return best;
  3083.         }
  3084.  
  3085.         /**
  3086.          * Gets the nearest tile on the path by the length of a path to it.
  3087.          * @return RSTile
  3088.          */
  3089.         public RSTile getNearestByPath() {
  3090.             int len = Integer.MAX_VALUE;
  3091.             RSTile best = null;
  3092.             if (hasPath())
  3093.                 for (RSTile t : smoothPath) {
  3094.                     int pLen = calc.pathLengthTo(t, false);
  3095.                     if (pLen < len && pLen >= 0) {
  3096.                         best = t;
  3097.                         len = pLen;
  3098.                     }
  3099.                 }
  3100.             return best;
  3101.         }
  3102.  
  3103.         /**
  3104.          * Gets the nearest index on the path to the given tile
  3105.          * @param t RSTile
  3106.          * @return int
  3107.          */
  3108.         public int getNearestIndexTo(RSTile t) {
  3109.             int len = Integer.MAX_VALUE;
  3110.             int bestIndex = -1;
  3111.             if (hasPath())
  3112.                 for (int i = 0; i < smoothPath.size(); i++)
  3113.                     if (calc.distanceBetween(t, smoothPath.get(i)) < len) {
  3114.                         bestIndex = i;
  3115.                         len = calc.distanceTo(t);
  3116.                     }
  3117.             return bestIndex;
  3118.         }
  3119.  
  3120.         /**
  3121.          * Use this enum to define the type of path <br />
  3122.          * ALL_Center: All points are near the middle of the lines <br />
  3123.          * Full: Points can be anywhere on the line<br />
  3124.          * WEIGHT_CENTER: Weighted to center, possibility of being on sides, but
  3125.          * lower
  3126.          *
  3127.          * @author Equinox_
  3128.          *
  3129.          */
  3130.         public enum MeshPathType {
  3131.             ALL_CENTER, FULL, WEIGHT_CENTER
  3132.         }
  3133.     }
  3134.     /**
  3135.      * A class for clicking on objects via their model. Uses normally distributed
  3136.      * points inside bounding ellipses
  3137.      *
  3138.      * @author Equinox_
  3139.      *
  3140.      */
  3141.     public static class ModelMouse {
  3142.         // Sin & Cos Tables
  3143.         static Map<Integer, Double> cosTable;
  3144.         static Map<Integer, Double> sinTable;
  3145.  
  3146.         final Calculations calc;
  3147.         final Menu menu;
  3148.         final Mouse mouse;
  3149.         final Range mouseSpeed;
  3150.         final Tiles tiles;
  3151.  
  3152.         /**
  3153.          * Begin to cache sine and cosine values
  3154.          */
  3155.         public static void cacheTrig() {
  3156.             sinTable = new HashMap<Integer, Double>();
  3157.             cosTable = new HashMap<Integer, Double>();
  3158.         }
  3159.  
  3160.         /**
  3161.          * Gets the distance between two points.
  3162.          *
  3163.          * @see Point.distance(x1, y1, x2, y2)
  3164.          * @param a
  3165.          * @param b
  3166.          * @return double
  3167.          */
  3168.         public static double distanceBetween(Point a, Point b) {
  3169.             return Point.distance(a.x, a.y, b.x, b.y);
  3170.         }
  3171.  
  3172.         /**
  3173.          * Gets the absolute center of the array of points via it's bounding box.
  3174.          *
  3175.          * @param points
  3176.          * @return Point
  3177.          */
  3178.         public static Point getAbsoluteCenter(Point[] points) {
  3179.             Rectangle rect = getBoundingBox(points);
  3180.             return new Point((int) rect.getCenterX(), (int) rect.getCenterY());
  3181.         }
  3182.  
  3183.         /**
  3184.          * Gets the bounding box of the provided point array
  3185.          *
  3186.          * @param points
  3187.          * @return Rectangle
  3188.          */
  3189.         public static Rectangle getBoundingBox(Point[] points) {
  3190.             int min_x = Integer.MAX_VALUE;
  3191.             int min_y = Integer.MAX_VALUE;
  3192.             int max_x = Integer.MIN_VALUE;
  3193.             int max_y = Integer.MIN_VALUE;
  3194.             if (points != null) {
  3195.                 for (Point pt : points) {
  3196.                     if (pt.x < min_x)
  3197.                         min_x = pt.x;
  3198.                     if (pt.x > max_x)
  3199.                         max_x = pt.x;
  3200.                     if (pt.y < min_y)
  3201.                         min_y = pt.y;
  3202.                     if (pt.y > max_y)
  3203.                         max_y = pt.y;
  3204.                 }
  3205.                 return new Rectangle(min_x, min_y, max_x - min_x, max_y - min_y);
  3206.             }
  3207.             return null;
  3208.         }
  3209.  
  3210.         /**
  3211.          * Gets an approximate bounding ellipse for the specified point array. (May
  3212.          * not contain all points)
  3213.          *
  3214.          * @param points
  3215.          * @return Ellipse2D
  3216.          */
  3217.         public static Ellipse2D getBoundingEllipse(Point[] points) {
  3218.             Rectangle bound = getBoundingBox(points);
  3219.             return new Ellipse2D.Double(bound.x, bound.y, bound.width, bound.height);
  3220.         }
  3221.  
  3222.         /**
  3223.          * Gets the cosine of the given degrees, from the cache table if possible
  3224.          *
  3225.          * @param degrees
  3226.          * @return double
  3227.          */
  3228.         public static double getCos(int degrees) {
  3229.             if (cosTable != null && cosTable.containsKey(degrees))
  3230.                 return cosTable.get(degrees);
  3231.             else {
  3232.                 double cosVal = Math.cos(degrees);
  3233.                 if (cosTable != null)
  3234.                     cosTable.put(degrees, cosVal);
  3235.                 return cosVal;
  3236.             }
  3237.         }
  3238.  
  3239.         /**
  3240.          * Gets a random point on the provided ellipse, calculating a random angle
  3241.          * between 0 and 360
  3242.          *
  3243.          * @see getPointOnEllipse(Ellipse2D ellipse, int degrees)
  3244.          * @see randomLinearDistribution(int MIN, int MAX)
  3245.          * @param ellipse
  3246.          * @return Point
  3247.          */
  3248.         public static Point getPointOnEllipse(Ellipse2D ellipse) {
  3249.             return getPointOnEllipse(ellipse,
  3250.                     (int) randomLinearDistribution(0, 360));
  3251.         }
  3252.  
  3253.         /**
  3254.          * Gets a random point on the provided ellipse, on a ray originating from
  3255.          * the center and the provided angle, using a normally distributed length
  3256.          * between 0 and the ellipse's radius at the provided angle
  3257.          *
  3258.          * @see getPointOnEllipse(Ellipse2D ellipse, int degrees, double
  3259.          *      distFromCenter)
  3260.          * @see randomNormalDistribution(int MIN, int MAX)
  3261.          * @param ellipse
  3262.          * @param degrees
  3263.          * @return Point
  3264.          */
  3265.         public static Point getPointOnEllipse(Ellipse2D ellipse, int degrees) {
  3266.             return getPointOnEllipse(ellipse, degrees,
  3267.                     randomNormalDistribution(0, getRadiusAt(ellipse, degrees)));
  3268.         }
  3269.  
  3270.         /**
  3271.          * Gets a random point on the provided ellipse, on a ray originating from
  3272.          * the center and the provided angle, at the provided distance from the
  3273.          * center.
  3274.          *
  3275.          * @param ellipse
  3276.          * @param degrees
  3277.          * @param distFromCenter
  3278.          * @return Point
  3279.          */
  3280.         public static Point getPointOnEllipse(Ellipse2D ellipse, int degrees,
  3281.                 double distFromCenter) {
  3282.             while (degrees >= 360)
  3283.                 degrees -= 360;
  3284.             while (degrees < 0)
  3285.                 degrees += 360;
  3286.             double sinVal = getSin(degrees), cosVal = getCos(degrees);
  3287.             return new Point(
  3288.                     (int) (ellipse.getCenterX() + Math.round(distFromCenter
  3289.                             * cosVal)),
  3290.                     (int) (ellipse.getCenterY() + Math.round(distFromCenter
  3291.                             * sinVal)));
  3292.         }
  3293.  
  3294.         /**
  3295.          * Gets a random point on the model.
  3296.          *
  3297.          * @see getPointOnPolygon(Polygon p)
  3298.          * @see getPolygonOnModel(RSModel m)
  3299.          * @param model
  3300.          * @return Point
  3301.          */
  3302.         public static Point getPointOnModel(RSModel model) {
  3303.             if (model != null)
  3304.                 return getPointOnPolygon(getPolygonOnModel(model));
  3305.             return null;
  3306.         }
  3307.  
  3308.         /**
  3309.          * Gets a random point on the provided polygon using a bounding ellipse.
  3310.          *
  3311.          * @see getPointOnEllipse(Ellipse2D ellipse)
  3312.          * @see getPoints(Polygon p)
  3313.          * @see getBoundingEllipse(Point[] points)
  3314.          * @param p
  3315.          * @return Point
  3316.          */
  3317.         public static Point getPointOnPolygon(Polygon p) {
  3318.             Point result = null;
  3319.             Ellipse2D boundingEllipse = getBoundingEllipse(getPoints(p));
  3320.             for (byte b = 0; b < 100 && (result == null || !p.contains(result)); b++)
  3321.                 result = getPointOnEllipse(boundingEllipse);
  3322.             if (result != null)
  3323.                 return result;
  3324.             else
  3325.                 return new Point((int) p.getBounds().getCenterX(), (int) p
  3326.                         .getBounds().getCenterY());
  3327.         }
  3328.  
  3329.         /**
  3330.          * Gets all the points on the provided polygon.
  3331.          *
  3332.          * @param p
  3333.          * @return Point[]
  3334.          */
  3335.         public static Point[] getPoints(Polygon p) {
  3336.             Point[] result = new Point[p.npoints];
  3337.             for (int pIdx = 0; pIdx < p.npoints; pIdx++)
  3338.                 result[pIdx] = new Point(p.xpoints[pIdx], p.ypoints[pIdx]);
  3339.             return result;
  3340.         }
  3341.  
  3342.         /**
  3343.          * Gets the nearest polygon to the base point from the array of polygons
  3344.          *
  3345.          * @param polygons
  3346.          * @param base
  3347.          * @return Polygon
  3348.          */
  3349.         public static Polygon getPolygonNearestTo(Polygon[] polygons, Point base) {
  3350.             double bDist = Double.MAX_VALUE;
  3351.             Polygon bPoly = null;
  3352.             for (Polygon tri : polygons) {
  3353.                 double dist = Point.distance(tri.getBounds().getCenterX(), tri
  3354.                         .getBounds().getCenterY(), base.x, base.y);
  3355.                 if (dist < bDist) {
  3356.                     bDist = dist;
  3357.                     bPoly = tri;
  3358.                 }
  3359.             }
  3360.             return bPoly;
  3361.         }
  3362.  
  3363.         /**
  3364.          * Gets a random polygon on the model by first calculating its bounding
  3365.          * ellipse, finding a point on that ellipse using a normal distribution
  3366.          * method, then getting the nearest polygon on the model to that point
  3367.          *
  3368.          * @param m
  3369.          * @return Polygon
  3370.          */
  3371.         public static Polygon getPolygonOnModel(RSModel m) {
  3372.             if (m != null && m.getTriangles() != null)
  3373.                 return getPolygonNearestTo(m.getTriangles(),
  3374.                         getPointOnEllipse(getBoundingEllipse(m.getPoints())));
  3375.             return null;
  3376.         }
  3377.  
  3378.         /**
  3379.          * Gets the radius of the ellipse and the given angle.
  3380.          *
  3381.          * @param ellipse
  3382.          * @param degrees
  3383.          * @return double
  3384.          */
  3385.         public static double getRadiusAt(Ellipse2D ellipse, int degrees) {
  3386.             while (degrees >= 360)
  3387.                 degrees -= 360;
  3388.             while (degrees < 0)
  3389.                 degrees += 360;
  3390.             if (degrees > 180)
  3391.                 degrees = 360 - degrees;
  3392.             if (degrees > 90)
  3393.                 degrees = 180 - degrees;
  3394.             double weightX = degrees / 90d, weightY = Math.abs(1 - weightX);
  3395.             return (weightX * (ellipse.getWidth() / 2d) + weightY
  3396.                     * (ellipse.getHeight() / 2d));
  3397.         }
  3398.  
  3399.         /**
  3400.          * Gets the sine of the given degrees, from the cache table if possible
  3401.          *
  3402.          * @param degrees
  3403.          * @return double
  3404.          */
  3405.         public static double getSin(int degrees) {
  3406.             if (sinTable != null && sinTable.containsKey(degrees))
  3407.                 return sinTable.get(degrees);
  3408.             else {
  3409.                 double sinVal = Math.sin(degrees);
  3410.                 if (sinTable != null)
  3411.                     sinTable.put(degrees, sinVal);
  3412.                 return sinVal;
  3413.             }
  3414.         }
  3415.  
  3416.         /**
  3417.          * Returns a linearly distributed pseudorandom integer.
  3418.          *
  3419.          * @return Random integer min <= n < max.
  3420.          */
  3421.         public static double randomLinearDistribution(double min, double max) {
  3422.             return Math.min(min, max) + new Random().nextDouble()
  3423.                     * Math.abs(max - min);
  3424.         }
  3425.  
  3426.         /**
  3427.          * Returns a normally distributed pseudorandom integer about a mean centered
  3428.          * between min and max with a calculated standard deviation.
  3429.          *
  3430.          * @return Random integer min <= n < max from the normal distribution
  3431.          *         described by the parameters.
  3432.          */
  3433.         public static double randomNormalDistribution(double MIN, double MAX) {
  3434.             double mean = MIN + (MAX - MIN) / 2d;
  3435.             double rand;
  3436.             double sd = (MAX - MIN) / 3d;
  3437.             do {
  3438.                 rand = (new Random().nextGaussian() * sd + mean);
  3439.             } while (rand < MIN || rand >= MAX);
  3440.             return rand;
  3441.         }
  3442.  
  3443.         /**
  3444.          * Clear cache of sine and cosine values and no longer cache them
  3445.          */
  3446.         public static void unCacheTrig() {
  3447.             sinTable = null;
  3448.             cosTable = null;
  3449.         }
  3450.  
  3451.         static {
  3452.             cacheTrig();
  3453.         }
  3454.  
  3455.         /**
  3456.          * Creates a model mouse instance with the given Tile methods, Calculation
  3457.          * methods, Mouse methods, Menu methods, and mouse speed Range
  3458.          *
  3459.          * @param tiles
  3460.          * @param calc
  3461.          * @param mouse
  3462.          * @param menu
  3463.          * @param mouseSpeed
  3464.          * @see Range
  3465.          * @see Tiles
  3466.          * @see Calculations
  3467.          * @see Mouse
  3468.          * @see Menu
  3469.          */
  3470.         public ModelMouse(Tiles tiles, Calculations calc, Mouse mouse, Menu menu,
  3471.                 Range mouseSpeed) {
  3472.             this.mouse = mouse;
  3473.             this.menu = menu;
  3474.             this.mouseSpeed = mouseSpeed;
  3475.             this.calc = calc;
  3476.             this.tiles = tiles;
  3477.         }
  3478.  
  3479.         // Characters
  3480.         /**
  3481.          * Performs the specified action on the character.
  3482.          *
  3483.          * @param character
  3484.          *            RSCharacter
  3485.          * @param action
  3486.          *            the menu item to search and click
  3487.          * @return returns true if clicked, false if character does not contain the
  3488.          *         desired action
  3489.          */
  3490.         public boolean doAction(RSCharacter character, String action) {
  3491.             RSModel model = character.getModel();
  3492.             if (model != null) {
  3493.                 return doAction(model, action);
  3494.             }
  3495.             return tiles.doAction(character.getLocation(), action);
  3496.         }
  3497.  
  3498.         // Ground Items
  3499.         /**
  3500.          * Performs the specified action on the Ground Item.
  3501.          *
  3502.          * @param groundItem
  3503.          *            RSGroundItem
  3504.          * @param action
  3505.          *            the menu item to search and click
  3506.          * @return returns true if clicked, false if groundItem does not contain the
  3507.          *         desired action
  3508.          */
  3509.         public boolean doAction(RSGroundItem groundItem, String action) {
  3510.             RSModel model = groundItem.getModel();
  3511.             if (model != null) {
  3512.                 return doAction(model, action);
  3513.             }
  3514.             return tiles.doAction(groundItem.getLocation(), action);
  3515.         }
  3516.  
  3517.         // Models
  3518.         /**
  3519.          * Clicks the RSModel and clicks the menu action
  3520.          *
  3521.          * @param model
  3522.          *            The RSModel
  3523.          * @param action
  3524.          *            the action to be clicked in the menu
  3525.          * @return true if clicked, false if failed.
  3526.          */
  3527.         public boolean doAction(RSModel model, String action) {
  3528.             if (model != null && model.getPointOnScreen() != null) {
  3529.                 Point modelPoint = getPointOnModel(model);
  3530.                 if (modelPoint != null) {
  3531.                     mouse.setSpeed(mouseSpeed.randomLinearDistribution());
  3532.                     mouse.move(modelPoint);
  3533.                     return menu.doAction(action);
  3534.                 }
  3535.             }
  3536.             return false;
  3537.         }
  3538.  
  3539.         // Objects
  3540.         /**
  3541.          * Performs the specified action on the object.
  3542.          *
  3543.          * @param object
  3544.          *            RSObject
  3545.          * @param action
  3546.          *            the menu item to search and click
  3547.          * @return returns true if clicked, false if object does not contain the
  3548.          *         desired action
  3549.          */
  3550.         public boolean doAction(RSObject object, String action) {
  3551.  
  3552.             RSModel model = object.getModel();
  3553.             if (model != null) {
  3554.                 return doAction(model, action);
  3555.             }
  3556.             return tiles.doAction(object.getLocation(), action);
  3557.         }
  3558.  
  3559.         /**
  3560.          * Left-clicks the character.
  3561.          *
  3562.          * @param character
  3563.          *            The RSCharacter
  3564.          * @return <tt>true</tt> if clicked.
  3565.          */
  3566.         public boolean doClick(RSCharacter character) {
  3567.             return doClick(character, true);
  3568.         }
  3569.  
  3570.         /**
  3571.          * Clicks the character.
  3572.          *
  3573.          * @param character
  3574.          *            The RSCharacter
  3575.          * @param left
  3576.          *            <tt>true</tt> to left-click; <tt>false</tt> to right-click.
  3577.          * @return <tt>true</tt> if clicked.
  3578.          */
  3579.         public boolean doClick(RSCharacter character, boolean leftClick) {
  3580.             RSModel model = character.getModel();
  3581.             if (model != null) {
  3582.                 return doClick(model, leftClick);
  3583.             } else {
  3584.                 Point p = calc.tileToScreen(character.getLocation());
  3585.                 if (calc.pointOnScreen(p)) {
  3586.                     mouse.move(p);
  3587.                     if (calc.pointOnScreen(p)) {
  3588.                         mouse.click(leftClick);
  3589.                         return true;
  3590.                     } else {
  3591.                         p = calc.tileToScreen(character.getLocation());
  3592.                         if (calc.pointOnScreen(p)) {
  3593.                             mouse.move(p);
  3594.                             mouse.click(leftClick);
  3595.                             return true;
  3596.                         }
  3597.                     }
  3598.                 }
  3599.                 return false;
  3600.             }
  3601.         }
  3602.  
  3603.         /**
  3604.          * Left-clicks the Ground Item.
  3605.          *
  3606.          * @param groundItem
  3607.          *            The RSGroundItem
  3608.          * @return <tt>true</tt> if clicked.
  3609.          */
  3610.         public boolean doClick(RSGroundItem groundItem) {
  3611.             return doClick(groundItem, true);
  3612.         }
  3613.  
  3614.         /**
  3615.          * Clicks the Ground Item.
  3616.          *
  3617.          * @param groundItem
  3618.          *            The RSGroundItem
  3619.          * @param left
  3620.          *            <tt>true</tt> to left-click; <tt>false</tt> to right-click.
  3621.          * @return <tt>true</tt> if clicked.
  3622.          */
  3623.         public boolean doClick(RSGroundItem groundItem, boolean leftClick) {
  3624.             RSModel model = groundItem.getModel();
  3625.             if (model != null) {
  3626.                 return doClick(model, leftClick);
  3627.             } else {
  3628.                 Point p = calc.tileToScreen(groundItem.getLocation());
  3629.                 if (calc.pointOnScreen(p)) {
  3630.                     mouse.move(p);
  3631.                     if (calc.pointOnScreen(p)) {
  3632.                         mouse.click(leftClick);
  3633.                         return true;
  3634.                     } else {
  3635.                         p = calc.tileToScreen(groundItem.getLocation());
  3636.                         if (calc.pointOnScreen(p)) {
  3637.                             mouse.move(p);
  3638.                             mouse.click(leftClick);
  3639.                             return true;
  3640.                         }
  3641.                     }
  3642.                 }
  3643.                 return false;
  3644.             }
  3645.         }
  3646.  
  3647.         /**
  3648.          * Left clicks the RSModel.
  3649.          *
  3650.          * @param model
  3651.          *            the RSModel
  3652.          * @return true if clicked.
  3653.          */
  3654.         public boolean doClick(RSModel model) {
  3655.             return doClick(model, true);
  3656.         }
  3657.  
  3658.         /**
  3659.          * Clicks the RSModel.
  3660.          *
  3661.          * @param model
  3662.          *            the RSModel
  3663.          * @param leftClick
  3664.          *            if true it left clicks.
  3665.          * @return true if clicked.
  3666.          */
  3667.         public boolean doClick(RSModel model, boolean leftClick) {
  3668.             if (model != null && model.getPointOnScreen() != null) {
  3669.                 Point modelPoint = getPointOnModel(model);
  3670.                 if (modelPoint != null) {
  3671.                     mouse.setSpeed(mouseSpeed.randomLinearDistribution());
  3672.                     mouse.move(modelPoint);
  3673.                     mouse.click(leftClick);
  3674.                     return true;
  3675.                 }
  3676.             }
  3677.             return false;
  3678.         }
  3679.  
  3680.         /**
  3681.          * Left-clicks the object.
  3682.          *
  3683.          * @param object
  3684.          *            The RSObject
  3685.          * @return <tt>true</tt> if clicked.
  3686.          */
  3687.         public boolean doClick(RSObject object) {
  3688.             return doClick(object, true);
  3689.         }
  3690.  
  3691.         /**
  3692.          * Clicks the object.
  3693.          *
  3694.          * @param object
  3695.          *            The RSObject
  3696.          * @param left
  3697.          *            <tt>true</tt> to left-click; <tt>false</tt> to right-click.
  3698.          * @return <tt>true</tt> if clicked.
  3699.          */
  3700.         public boolean doClick(RSObject object, boolean leftClick) {
  3701.             RSModel model = object.getModel();
  3702.             if (model != null) {
  3703.                 return doClick(model, leftClick);
  3704.             } else {
  3705.                 Point p = calc.tileToScreen(object.getLocation());
  3706.                 if (calc.pointOnScreen(p)) {
  3707.                     mouse.move(p);
  3708.                     if (calc.pointOnScreen(p)) {
  3709.                         mouse.click(leftClick);
  3710.                         return true;
  3711.                     } else {
  3712.                         p = calc.tileToScreen(object.getLocation());
  3713.                         if (calc.pointOnScreen(p)) {
  3714.                             mouse.move(p);
  3715.                             mouse.click(leftClick);
  3716.                             return true;
  3717.                         }
  3718.                     }
  3719.                 }
  3720.                 return false;
  3721.             }
  3722.         }
  3723.  
  3724.         /**
  3725.          * Moves the mouse over the character.
  3726.          *
  3727.          * @param character
  3728.          *            The RSCharacter
  3729.          * @return <tt>true</tt> if the mouse was moved.
  3730.          */
  3731.         public boolean doHover(RSCharacter character) {
  3732.             RSModel model = character.getModel();
  3733.             if (model != null) {
  3734.                 return doHover(model);
  3735.             } else {
  3736.                 Point p = calc.tileToScreen(character.getLocation());
  3737.                 if (calc.pointOnScreen(p)) {
  3738.                     mouse.move(p);
  3739.                     return true;
  3740.                 }
  3741.             }
  3742.             return false;
  3743.         }
  3744.  
  3745.         /**
  3746.          * Moves the mouse over the Ground Item.
  3747.          *
  3748.          * @param groundItem
  3749.          *            The RSGroundItem
  3750.          * @return <tt>true</tt> if the mouse was moved.
  3751.          */
  3752.         public boolean doHover(RSGroundItem groundItem) {
  3753.             RSModel model = groundItem.getModel();
  3754.             if (model != null) {
  3755.                 return doHover(model);
  3756.             } else {
  3757.                 Point p = calc.tileToScreen(groundItem.getLocation());
  3758.                 if (calc.pointOnScreen(p)) {
  3759.                     mouse.move(p);
  3760.                     return true;
  3761.                 }
  3762.             }
  3763.             return false;
  3764.         }
  3765.  
  3766.         /**
  3767.          * Moves the mouse onto the RSModel.
  3768.          *
  3769.          * @param model
  3770.          *            The RSModel
  3771.          */
  3772.         public boolean doHover(RSModel model) {
  3773.             if (model != null && model.getPointOnScreen() != null) {
  3774.                 Point modelPoint = getPointOnModel(model);
  3775.                 if (modelPoint != null) {
  3776.                     mouse.setSpeed(mouseSpeed.randomLinearDistribution());
  3777.                     mouse.move(modelPoint);
  3778.                     return true;
  3779.                 }
  3780.             }
  3781.             return false;
  3782.         }
  3783.  
  3784.         /**
  3785.          * Moves the mouse over the object.
  3786.          *
  3787.          * @param object
  3788.          *            The RSObject
  3789.          * @return <tt>true</tt> if the mouse was moved.
  3790.          */
  3791.         public boolean doHover(RSObject object) {
  3792.             RSModel model = object.getModel();
  3793.             if (model != null) {
  3794.                 return doHover(model);
  3795.             } else {
  3796.                 Point p = calc.tileToScreen(object.getLocation());
  3797.                 if (calc.pointOnScreen(p)) {
  3798.                     mouse.move(p);
  3799.                     return true;
  3800.                 }
  3801.             }
  3802.             return false;
  3803.         }
  3804.     }
  3805.     /**
  3806.      * Used to format data into more conventional formats
  3807.      *
  3808.      * @author Equinox_
  3809.      *
  3810.      */
  3811.     public static class PaintFormatter {
  3812.         /**
  3813.          * Converts an array of strings into one string, using commas and 'and' <br>
  3814.          * <br>
  3815.          * Examples: <br>
  3816.          * {"option1"} returns "option1"<br>
  3817.          * {"option1","option2"} returns "option1 and option2"<br>
  3818.          * {"op1","op2","op3"} returns "op1, op2, and op3"<br>
  3819.          * <br>
  3820.          *
  3821.          * @param list
  3822.          *            The array of strings making up the list.
  3823.          * @return The properly formatted string
  3824.          */
  3825.         public static String formatList(String[] list) {
  3826.             String s = "";
  3827.             if (list != null)
  3828.                 if (list.length == 2) {
  3829.                     s = list[0] + " and " + list[1];
  3830.                 } else if (list.length > 2) {
  3831.                     for (int i = 0; i < list.length - 1; i++)
  3832.                         s += list[i] + ", ";
  3833.                     s += "and " + list[list.length - 1];
  3834.                 } else if (list.length == 1)
  3835.                     s = list[0];
  3836.             return s;
  3837.         }
  3838.  
  3839.         /**
  3840.          * Formats a specified amount into thousands, millions or billions, then
  3841.          * adds the proper suffix
  3842.          *
  3843.          * @param money
  3844.          *            the amount of money
  3845.          * @return the formatted string
  3846.          */
  3847.         public static String getFormattedMoney(long money) {
  3848.             /*
  3849.              * String suffix = ""; int t = 1; if (money >= 10000) { t *= 1000;
  3850.              * suffix = "K"; } if (money / t > 10000) { t *= 1000; suffix = "M"; }
  3851.              * if (money / t > 10000) { t *= 1000; suffix = "B"; } return
  3852.              * String.valueOf((money / t)) + suffix;
  3853.              */
  3854.             return new DecimalFormat("###,###,###,###,###,###").format(money);
  3855.         }
  3856.  
  3857.         /**
  3858.          * Converts the milliseconds to DD:HH:MM:SS with the days optional.
  3859.          *
  3860.          * @param timeMillis
  3861.          *            the number of milliseconds
  3862.          * @return the properly formatted time as a String
  3863.          */
  3864.         public static String getFormattedTime(final long timeMillis) {
  3865.             return getFormattedTime(timeMillis, ":", false);
  3866.         }
  3867.  
  3868.         /**
  3869.          * Formats time to DD:HH:MM:SS:MSMS with days and millis optional
  3870.          *
  3871.          * @param timeMillis
  3872.          *            long milliseconds of time
  3873.          * @param sep
  3874.          *            String seperation character
  3875.          * @param millisInc
  3876.          *            boolean include milliseconds
  3877.          * @return
  3878.          */
  3879.         public static String getFormattedTime(final long timeMillis, String sep,
  3880.                 boolean millisInc) {
  3881.             long millis = timeMillis;
  3882.             final long days = millis / (24 * 1000 * 60 * 60);
  3883.             millis -= days * (24 * 1000 * 60 * 60);
  3884.             final long hours = millis / (1000 * 60 * 60);
  3885.             millis -= hours * 1000 * 60 * 60;
  3886.             final long minutes = millis / (1000 * 60);
  3887.             millis -= minutes * 1000 * 60;
  3888.             final long _seconds = millis / 1000;
  3889.             millis -= _seconds * 1000;
  3890.             String dayString = String.valueOf(days);
  3891.             String hoursString = String.valueOf(hours);
  3892.             String minutesString = String.valueOf(minutes);
  3893.             String secondsString = String.valueOf(_seconds);
  3894.             if (hours < 10) {
  3895.                 hoursString = 0 + hoursString;
  3896.             }
  3897.             if (minutes < 10) {
  3898.                 minutesString = 0 + minutesString;
  3899.             }
  3900.             if (_seconds < 10) {
  3901.                 secondsString = 0 + secondsString;
  3902.             }
  3903.             String s = "";
  3904.             if (days > 0)
  3905.                 s = dayString + sep;
  3906.             s += hoursString + sep + minutesString + sep + secondsString;
  3907.             if (millisInc)
  3908.                 s += sep + String.valueOf(millis);
  3909.             return s;
  3910.         }
  3911.  
  3912.         /**
  3913.          * Converts the first letter of every word to uppercase, and the rest
  3914.          * lowercase.
  3915.          *
  3916.          * @param s
  3917.          *            The input String
  3918.          * @return the properly formatted string
  3919.          */
  3920.         public static String properCaseAll(String s) {
  3921.             String[] words = s.split(" ");
  3922.             String result = "";
  3923.             for (int i = 0; i < words.length; i++) {
  3924.                 String f = words[i].substring(0, 1).toUpperCase();
  3925.                 words[i] = f + words[i].substring(1).toLowerCase();
  3926.                 result += " " + words[i];
  3927.             }
  3928.             return result.substring(1);
  3929.         }
  3930.  
  3931.         /**
  3932.          * Capitalizes the first letter of every sentence, with every other letter
  3933.          * lowercase.
  3934.          *
  3935.          * @param s
  3936.          *            The input string
  3937.          * @return The properly formatted String
  3938.          */
  3939.         public static String properCaseFirst(String s) {
  3940.             String[] sentences = new String[] { s };
  3941.             if (s.contains("."))
  3942.                 sentences = s.split(".");
  3943.             String result = "";
  3944.             for (int i = 0; i < sentences.length; i++) {
  3945.                 String f = sentences[i].substring(0, 1).toUpperCase();
  3946.                 sentences[i] = f + sentences[i].substring(1).toLowerCase();
  3947.                 result += " " + sentences[i];
  3948.             }
  3949.             return result.substring(1);
  3950.         }
  3951.     }
  3952.     /**
  3953.      * Represents a PaintMenu
  3954.      *
  3955.      * @author Equinox_
  3956.      *
  3957.      */
  3958.     public static class PaintMenu {
  3959.         // Images
  3960.         public Map<String, Image> icons = new HashMap<String, Image>();
  3961.  
  3962.         public void putImage(String name, Image img) {
  3963.             icons.put(name.toLowerCase(), img);
  3964.         }
  3965.  
  3966.         public Image getImage(String name) {
  3967.             if (icons.containsKey(name.toLowerCase()))
  3968.                 return icons.get(name.toLowerCase());
  3969.             return null;
  3970.         }
  3971.  
  3972.         public long startTime;
  3973.         // End Images
  3974.  
  3975.         final int padding = 1;
  3976.         Point location;
  3977.         PaintStyle style;
  3978.         List<PaintMenuItem> components = new ArrayList<PaintMenuItem>();
  3979.         boolean[] validCache;
  3980.         public Anchor anchor = Anchor.UP_RIGHT;
  3981.         boolean showAllPopups;
  3982.         PaintMenuPopUp visiblePopup = null;
  3983.         public Point mouseLoc = new Point(-1, -1);
  3984.         List<MenuMouseListener> mouseListeners;
  3985.         LineListener defaultLineListener;
  3986.         private FlowType flowType = FlowType.HORIZONTAL;
  3987.         public Rectangle bounds = new Rectangle(4,4,512,334);
  3988.         public FontRenderContext frc = new FontRenderContext(new AffineTransform(),
  3989.                 false, false);
  3990.  
  3991.         int absX, absY, width, height;
  3992.  
  3993.         /**
  3994.          * Constructs a PaintMenu using the provided PaintInterface, Location,
  3995.          * Anchor, and the GameScreen as the bounds
  3996.          *
  3997.          * @param parent
  3998.          *            PaintInterface
  3999.          * @param location
  4000.          *            Point
  4001.          * @param anchor
  4002.          *            Anchor
  4003.          */
  4004.         public PaintMenu(PaintStyle paintStyle, Anchor anchor) {
  4005.             this.style = paintStyle;
  4006.             this.location = getLocation();
  4007.             this.anchor = anchor;
  4008.             startTime = System.currentTimeMillis();
  4009.         }
  4010.  
  4011.         public Point getLocation() {
  4012.             Point loc = bounds.getLocation();
  4013.             if (anchor.name().contains("DOWN"))
  4014.                 loc.y += bounds.getHeight();
  4015.             if (anchor.name().contains("RIGHT"))
  4016.                 loc.x += bounds.getWidth();
  4017.             return loc;
  4018.         }
  4019.  
  4020.         /**
  4021.          * Sets the FlowType and recompiles the menu
  4022.          *
  4023.          * @param flowType
  4024.          *            FlowType
  4025.          */
  4026.         public void setFlowType(FlowType flowType) {
  4027.             this.flowType = flowType;
  4028.             this.compile();
  4029.         }
  4030.  
  4031.         /**
  4032.          * Gets the FlowType
  4033.          *
  4034.          * @return FlowType
  4035.          */
  4036.         public FlowType getFlowType() {
  4037.             return flowType;
  4038.         }
  4039.  
  4040.         /**
  4041.          * Toggles the showAllPopups state
  4042.          * */
  4043.         public void toggleShowAll() {
  4044.             showAllPopups = !showAllPopups;
  4045.         }
  4046.  
  4047.         /**
  4048.          * Constructs a PaintMenu using the provided parent and location, assuming
  4049.          * Anchor.UP_LEFT
  4050.          *
  4051.          * @param parent
  4052.          *            PaintInterface
  4053.          * @param location
  4054.          *            Point
  4055.          */
  4056.         public PaintMenu(PaintStyle style) {
  4057.             this(style, Anchor.UP_LEFT);
  4058.         }
  4059.  
  4060.         /**
  4061.          * Adds the specified PaintMenu item
  4062.          *
  4063.          * @param i
  4064.          *            MenuItem
  4065.          */
  4066.         public void add(PaintMenuItem i) {
  4067.             i.setIndex(components.size());
  4068.             components.add(i);
  4069.             compile();
  4070.             validCache = new boolean[components.size()];
  4071.         }
  4072.  
  4073.         /**
  4074.          * Compiles this menu
  4075.          */
  4076.         public void compile() {
  4077.             location = getLocation();
  4078.             absX = getAbsX();
  4079.             absY = getAbsY();
  4080.             width = getWidth();
  4081.             height = getHeight();
  4082.             for (int i = 0; i < components.size(); i++)
  4083.                 if (components.get(i).isValid())
  4084.                     components.get(i).compile();
  4085.             absX = getAbsX();
  4086.             absY = getAbsY();
  4087.             width = getWidth();
  4088.             height = getHeight();
  4089.         }
  4090.  
  4091.         /**
  4092.          * Gets position x based on the menu's anchor
  4093.          *
  4094.          * @return int
  4095.          */
  4096.         public int getAbsX() {
  4097.             if (anchor.name().contains("LEFT"))
  4098.                 return location.x;
  4099.             else
  4100.                 return location.x - getWidth();
  4101.         }
  4102.  
  4103.         /**
  4104.          * Gets position y based on the menu's anchor
  4105.          *
  4106.          * @return int
  4107.          */
  4108.         public int getAbsY() {
  4109.             if (anchor.name().contains("UP"))
  4110.                 return location.y;
  4111.             else
  4112.                 return location.y - getHeight();
  4113.         }
  4114.  
  4115.         /**
  4116.          * Gets the maximum width of this PaintMenu using the menu's FlowType
  4117.          *
  4118.          * @return int
  4119.          */
  4120.         public int getWidth() {
  4121.             if (flowType.equals(FlowType.HORIZONTAL)) {
  4122.                 int width = padding * components.size();
  4123.                 for (PaintMenuItem m : components)
  4124.                     if (m.bounds != null && m.isValid())
  4125.                         width += m.bounds.width;
  4126.                 return width + padding;
  4127.             } else {
  4128.                 int max = Integer.MIN_VALUE;
  4129.                 for (PaintMenuItem m : components)
  4130.                     if (m.bounds != null && m.isValid())
  4131.                         if (max < m.bounds.width)
  4132.                             max = (int) m.bounds.width;
  4133.                 return max + (2 * padding);
  4134.             }
  4135.         }
  4136.  
  4137.         /**
  4138.          * Gets the maximum height of this PaintMenu based of FlowType
  4139.          *
  4140.          * @return int
  4141.          */
  4142.         public int getHeight() {
  4143.             if (flowType.equals(FlowType.HORIZONTAL)) {
  4144.                 int max = Integer.MIN_VALUE;
  4145.                 for (PaintMenuItem m : components)
  4146.                     if (m.bounds != null && m.isValid())
  4147.                         if (max < m.bounds.height)
  4148.                             max = (int) m.bounds.height;
  4149.                 return max + (2 * padding);
  4150.             } else {
  4151.                 int height = 0;
  4152.                 for (PaintMenuItem m : components)
  4153.                     if (m.bounds != null && m.isValid())
  4154.                         height += m.bounds.height + padding;
  4155.                 return height + padding;
  4156.             }
  4157.         }
  4158.  
  4159.         /**
  4160.          * Gets the current bounds of this menu's components, excluding popups, with
  4161.          * rounded corners if specified
  4162.          *
  4163.          * @param rounded
  4164.          *            boolean
  4165.          * @return RoundRectangle2D.Float
  4166.          */
  4167.         public RoundRectangle2D.Float getArea(boolean rounded) {
  4168.             float round = rounded ? style.roundCorner + 1 : 0;
  4169.             return new RoundRectangle2D.Float(absX, absY, width, height, round,
  4170.                     round);
  4171.         }
  4172.  
  4173.         /**
  4174.          * Gets the bounds of the PaintMenu and the popups.
  4175.          *
  4176.          * @return Rectangle
  4177.          */
  4178.         public Rectangle getBoundsFull() {
  4179.             RoundRectangle2D r1 = getArea(false);
  4180.             RoundRectangle2D r2 = visiblePopup != null ? visiblePopup.bkg
  4181.                     : getArea(false);
  4182.             int x = (int) Math.min(r1.getX(), r2.getX());
  4183.             int y = (int) Math.min(r1.getY(), r2.getY());
  4184.             int width = (int) Math.max(r1.getX() + r1.getWidth(),
  4185.                     r2.getX() + r2.getWidth())
  4186.                     - x;
  4187.             int height = (int) Math.max(r1.getX() + r1.getHeight(),
  4188.                     r2.getX() + r2.getHeight())
  4189.                     - y;
  4190.             return new Rectangle(x, y, width, height);
  4191.         }
  4192.  
  4193.         /**
  4194.          * Renders the PaintMenu to the specified Graphics object using the provided
  4195.          * mouse location for any popups
  4196.          *
  4197.          * @param g
  4198.          *            Graphics2D
  4199.          * @param mouseLoc
  4200.          *            Point
  4201.          */
  4202.         public void render(Graphics2D g, Point mouseLoc) {
  4203.             boolean compile = false;
  4204.             if (validCache == null || components.size() != validCache.length) {
  4205.                 validCache = new boolean[components.size()];
  4206.                 compile = true;
  4207.             }
  4208.             for (int i = 0; i < components.size(); i++) {
  4209.                 Boolean valid = components.get(i).isValid();
  4210.                 if (!valid.equals(validCache[i]))
  4211.                     compile = true;
  4212.                 validCache[i] = valid;
  4213.             }
  4214.             if (compile)
  4215.                 compile();
  4216.             g.setColor(style.bgColor);
  4217.             g.fill(getArea(true));
  4218.             PaintMenuPopUp overPopup = null;
  4219.             if (showAllPopups)
  4220.                 overPopup = getAllPopups(true, "tools");
  4221.             for (PaintMenuItem m : components) {
  4222.                 if (m.isValid()) {
  4223.                     m.render(g);
  4224.                     if (overPopup == null
  4225.                             && m.getMenuBounds().contains(mouseLoc)
  4226.                             || (m.getPopup.getName().equalsIgnoreCase(
  4227.                                     visiblePopup != null ? visiblePopup.name : "") && rectangleContainsApprox(
  4228.                                     visiblePopup.bkg, mouseLoc))) {
  4229.                         overPopup = m.getPopUp();
  4230.                     }
  4231.                 }
  4232.             }
  4233.             if (overPopup != null)
  4234.                 overPopup.render(g);
  4235.             visiblePopup = overPopup;
  4236.         }
  4237.  
  4238.         /**
  4239.          * Check if the specified point is within 1 point of the Rectangle
  4240.          *
  4241.          * @param r
  4242.          *            RoundRectangle2D
  4243.          * @param p
  4244.          *            Point
  4245.          * @return boolean contains
  4246.          */
  4247.         public boolean rectangleContainsApprox(RoundRectangle2D r, Point p) {
  4248.             for (int oX = -1; oX <= 1; oX++)
  4249.                 for (int oY = -1; oY <= 1; oY++)
  4250.                     if (r.contains(new Point(p.x + oX, p.y + oY)))
  4251.                         return true;
  4252.             return false;
  4253.         }
  4254.  
  4255.         /**
  4256.          * Gets all popups in frontToBack or backToFront order and excluding
  4257.          * specified names
  4258.          *
  4259.          * @param rightToLeft
  4260.          *            boolean
  4261.          * @param excludeItems
  4262.          *            String...
  4263.          * @return MenuPopUp
  4264.          */
  4265.         public PaintMenuPopUp getAllPopups(boolean backToFront,
  4266.                 String... excludeItems) {
  4267.             Map<String, String> lines = new HashMap<String, String>();
  4268.             if (components.size() > 0)
  4269.                 if (backToFront)
  4270.                     outer: for (int i = components.size() - 1; i >= 0; i--) {
  4271.                         if (!components.get(i).isValid())
  4272.                             continue outer;
  4273.                         for (String subS : excludeItems)
  4274.                             if (components.get(i).name.equalsIgnoreCase(subS))
  4275.                                 continue outer;
  4276.                         if (components.get(i).isValid()) {
  4277.                             PaintMenuPopUp sub = components.get(i).getPopUp();
  4278.                             if (sub != null)
  4279.                                 for (String line : sub.lines)
  4280.                                     lines.put(getLineTag(line).toLowerCase(), line);
  4281.                         }
  4282.                     }
  4283.                 else
  4284.                     outer: for (int i = 0; i < components.size(); i++) {
  4285.                         for (String subS : excludeItems)
  4286.                             if (components.get(i).name.equalsIgnoreCase(subS))
  4287.                                 continue outer;
  4288.                         PaintMenuPopUp sub = components.get(i).getPopUp();
  4289.                         if (sub != null)
  4290.                             for (String line : sub.lines)
  4291.                                 lines.put(getLineTag(line).toLowerCase(), line);
  4292.  
  4293.                     }
  4294.  
  4295.             String[] array = new String[lines.size()];
  4296.             PaintMenuPopUp p = new PaintMenuPopUp(lines.values().toArray(array),
  4297.                     "allpopups");
  4298.             p.setParent(components.get(components.size() - 1));
  4299.             return p;
  4300.         }
  4301.  
  4302.         /**
  4303.          * Gets the data tag of a line
  4304.          *
  4305.          * @param line
  4306.          *            the line String
  4307.          * @return the tag
  4308.          */
  4309.         public String getLineTag(String line) {
  4310.             if (line.contains(":"))
  4311.                 return line.split(":")[0];
  4312.             else
  4313.                 return line;
  4314.         }
  4315.  
  4316.         /**
  4317.          * Gets a proggie as a BufferedImage
  4318.          *
  4319.          * @return BufferedImage proggie
  4320.          */
  4321.         public BufferedImage getProggie() {
  4322.             String[] lines = getAllPopups(true, "tools").lines;
  4323.             Color color = style.FONT_COLOR;
  4324.             Font font = style.FONT;
  4325.             float line_height = font.getLineMetrics(lines[0], frc).getHeight();
  4326.             int width = 0;
  4327.             for (String line : lines)
  4328.                 if (font.getStringBounds(line, frc).getWidth() > width)
  4329.                     width = (int) font.getStringBounds(line, frc).getWidth();
  4330.  
  4331.             int height = (int) (lines.length * line_height);
  4332.  
  4333.             width += style.roundCorner + 2;
  4334.             height += style.roundCorner + 2;
  4335.  
  4336.             RoundRectangle2D.Float bkg = new RoundRectangle2D.Float(0, 0, width,
  4337.                     height, style.roundCorner > 0 ? style.roundCorner + 1 : 0,
  4338.                     style.roundCorner > 0 ? style.roundCorner + 1 : 0);
  4339.             RoundRectangle2D.Float fg = new RoundRectangle2D.Float(
  4340.                     (float) bkg.getX() + 1, (float) bkg.getY() + 1, width - 2,
  4341.                     height - 2, style.roundCorner, style.roundCorner);
  4342.             Rectangle txtBounds = new Rectangle(
  4343.                     (int) (fg.getX() + (style.roundCorner / 2)),
  4344.                     (int) (fg.getY() + (style.roundCorner / 2)), (int) (width
  4345.                             - style.roundCorner - 2), (int) (height
  4346.                             - style.roundCorner - 2));
  4347.             BufferedImage bImg = new BufferedImage(width, height,
  4348.                     BufferedImage.TYPE_INT_ARGB);
  4349.             Graphics2D g = (Graphics2D) bImg.getGraphics();
  4350.             g.setColor(style.bgColor);
  4351.             g.fill(bkg);
  4352.             g.setColor(style.fgColor);
  4353.             g.fill(fg);
  4354.             g.setFont(font);
  4355.             g.setColor(color);
  4356.             for (int i = 0; i < lines.length; i++)
  4357.                 g.drawString(lines[i], txtBounds.x, ((i + 1) * (line_height))
  4358.                         + txtBounds.y);
  4359.             return bImg;
  4360.         }
  4361.  
  4362.         /**
  4363.          * Saves a progress report to the specified dir, proggie name (without
  4364.          * extension), and logger.
  4365.          *
  4366.          * @param dir
  4367.          * @param proggieName
  4368.          * @param log
  4369.          */
  4370.         public void saveProggie(String dirPath, String proggieName, Logger log) {
  4371.             File dir = new File(dirPath);
  4372.             try {
  4373.                 if (!(dir.isDirectory() || dir.mkdirs()))
  4374.                     throw new Exception("Error ceating screenshots directory!");
  4375.                 File proggie = getFile(dir, proggieName, ".png");
  4376.                 ImageIO.write(this.getProggie(), "png", proggie);
  4377.                 log.info("Saved proggie!");
  4378.                 log.info(proggie.getAbsolutePath());
  4379.                 return;
  4380.             } catch (Exception e) {
  4381.                 log.severe("Failed to save proggie: " + e.getMessage());
  4382.             }
  4383.         }
  4384.  
  4385.         /**
  4386.          * Gets a file for writing to in the specified dir as File, and name & ext.
  4387.          * Appending (copy number) to the name if needed.
  4388.          *
  4389.          * @param dir
  4390.          * @param name
  4391.          * @param ext
  4392.          * @return
  4393.          */
  4394.         public File getFile(File dir, String name, String ext) {
  4395.             try {
  4396.                 int copy = 0;
  4397.                 while (new File(dir, name + (copy > 0 ? ("(" + copy + ")") : "")
  4398.                         + ext).exists())
  4399.                     copy++;
  4400.                 return new File(dir, name + (copy > 0 ? ("(" + copy + ")") : "")
  4401.                         + ext);
  4402.             } catch (Exception e) {
  4403.                 return new File(dir, name + ext);
  4404.             }
  4405.         }
  4406.  
  4407.         /**
  4408.          * Adds the specified mouse listener
  4409.          *
  4410.          * @param listen
  4411.          *            MouseListen
  4412.          */
  4413.         public void addMouseListener(MenuMouseListener listen) {
  4414.             if (mouseListeners == null)
  4415.                 mouseListeners = new ArrayList<MenuMouseListener>();
  4416.             mouseListeners.add(listen);
  4417.         }
  4418.  
  4419.         /**
  4420.          * Sets the default line listener
  4421.          *
  4422.          * @param listen
  4423.          *            LineListener
  4424.          */
  4425.         public void setDefaultLineListener(LineListener listen) {
  4426.             defaultLineListener = listen;
  4427.         }
  4428.  
  4429.         /**
  4430.          * Fires the MouseEvent in all containing children and this
  4431.          *
  4432.          * @param e
  4433.          *            MouseEvent
  4434.          */
  4435.         public void fireMouseEvent(MouseEvent e) {
  4436.             if (getArea(true).contains(e.getPoint()))
  4437.                 if (mouseListeners != null)
  4438.                     for (MenuMouseListener m : mouseListeners)
  4439.                         m.MouseAction(e);
  4440.             for (PaintMenuItem m : components)
  4441.                 if (m.bounds != null)
  4442.                     if (m.bounds.contains(e.getPoint()))
  4443.                         m.fireMouseEvent(e);
  4444.             if (visiblePopup != null)
  4445.                 visiblePopup.fireMouseEvent(e);
  4446.  
  4447.         }
  4448.     }
  4449.     /**
  4450.      * A class representing a mouse over button on the parent menu
  4451.      *
  4452.      * @author Equinox_
  4453.      *
  4454.      */
  4455.     public static class PaintMenuButton extends PaintMenuItem {
  4456.         final static int width = 30;
  4457.         final static int height = 30;
  4458.  
  4459.         String imageIcon;
  4460.  
  4461.         /**
  4462.          * Constructs a button using the specified name, parent, imageIcon, getPopup
  4463.          * method, and validation method.
  4464.          *
  4465.          * @param name
  4466.          *            String
  4467.          * @param parent
  4468.          *            Menu
  4469.          * @param imageIcon
  4470.          *            String
  4471.          * @param getPopUp
  4472.          *            GetPopup
  4473.          * @param validation
  4474.          *            IsValid
  4475.          */
  4476.         public PaintMenuButton(PaintMenu parent, String name, String imageIcon, GetPopup getPopUp,
  4477.                 IsValid validation) {
  4478.             super(parent, name, getPopUp, width, height, validation);
  4479.             this.imageIcon = imageIcon.toLowerCase();
  4480.         }
  4481.  
  4482.         /**
  4483.          * Constructs a button using the specified name, parent, imageIcon, and
  4484.          * validation method.
  4485.          *
  4486.          * @param name
  4487.          *            String
  4488.          * @param parent
  4489.          *            Menu
  4490.          * @param imageIcon
  4491.          *            String
  4492.          * @param validation
  4493.          *            IsValid
  4494.          */
  4495.         public PaintMenuButton(PaintMenu parent, String name, String imageIcon, IsValid validation) {
  4496.             this(parent, name, imageIcon, null, validation);
  4497.         }
  4498.  
  4499.         /**
  4500.          * Constructs a button using the specified name, parent, and validation
  4501.          * method.
  4502.          *
  4503.          * @param name
  4504.          *            String
  4505.          * @param parent
  4506.          *            Menu
  4507.          * @param validation
  4508.          *            IsValid
  4509.          */
  4510.         public PaintMenuButton(PaintMenu parent, String name, IsValid validation) {
  4511.             this(parent, name, "", null, validation);
  4512.         }
  4513.  
  4514.         /**
  4515.          * Constructs a button using the specified name, parent, imageIcon, and
  4516.          * getPopup method.
  4517.          *
  4518.          * @param name
  4519.          *            String
  4520.          * @param parent
  4521.          *            Menu
  4522.          * @param imageIcon
  4523.          *            String
  4524.          * @param getPopUp
  4525.          *            GetPopup
  4526.          */
  4527.         public PaintMenuButton(PaintMenu parent, String name, String imageIcon, GetPopup getPopUp) {
  4528.             super(parent, name, getPopUp, width, height, new IsValid() {
  4529.                 public boolean isValid() {
  4530.                     return true;
  4531.                 }
  4532.             });
  4533.             this.imageIcon = imageIcon.toLowerCase();
  4534.         }
  4535.  
  4536.         /**
  4537.          * Constructs a button using the specified name, parent, and imageIcon.
  4538.          *
  4539.          * @param name
  4540.          *            String
  4541.          * @param parent
  4542.          *            Menu
  4543.          * @param imageIcon
  4544.          *            String
  4545.          */
  4546.         public PaintMenuButton(PaintMenu parent, String name, String imageIcon) {
  4547.             this(parent, name, imageIcon, null, new IsValid() {
  4548.                 public boolean isValid() {
  4549.                     return true;
  4550.                 }
  4551.             });
  4552.         }
  4553.  
  4554.         /**
  4555.          * Constructs a button using the specified name and parent.
  4556.          *
  4557.          * @param name
  4558.          *            String
  4559.          * @param parent
  4560.          *            Menu
  4561.          */
  4562.         public PaintMenuButton(PaintMenu parent, String name) {
  4563.             this(parent, name, "", null, new IsValid() {
  4564.                 public boolean isValid() {
  4565.                     return true;
  4566.                 }
  4567.             });
  4568.         }
  4569.  
  4570.         /**
  4571.          * Renders this MenuButton to the given Graphics2D context
  4572.          */
  4573.         public void render(Graphics2D g) {
  4574.             // Compiling
  4575.             if (parent.icons.containsKey(imageIcon))
  4576.                 if (parent.icons.get(imageIcon) != null)
  4577.                     if (!parent.icons.containsKey("Button-" + imageIcon)) {
  4578.                         // Scale the image
  4579.                         int width = (int) (bounds.width - (parent.style.roundCorner));
  4580.                         int height = (int) (bounds.height - (parent.style.roundCorner));
  4581.                         int hints = Image.SCALE_FAST;
  4582.                         parent.icons.put(
  4583.                                 "Button-" + imageIcon,
  4584.                                 parent.icons.get(imageIcon).getScaledInstance(
  4585.                                         width, height, hints));
  4586.                     }
  4587.  
  4588.             g.setColor(parent.style.fgColor);
  4589.             g.fill(bounds);
  4590.             if (parent.icons.containsKey("Button-" + imageIcon))
  4591.                 if (parent.icons.get("Button-" + imageIcon) != null)
  4592.                     g.drawImage(parent.icons.get("Button-" + imageIcon),
  4593.                             (int) (bounds.x + (parent.style.roundCorner / 2)),
  4594.                             (int) (bounds.y + (parent.style.roundCorner / 2)), null);
  4595.         }
  4596.  
  4597.         boolean isValid() {
  4598.             return true;
  4599.         }
  4600.     }
  4601.     /**
  4602.      * An abstract class representing PaintMenuItems
  4603.      *
  4604.      * @author Equinox_
  4605.      *
  4606.      */
  4607.     public static abstract class PaintMenuItem {
  4608.         final GetPopup getPopup;
  4609.         public PaintMenu parent = null;
  4610.         RoundRectangle2D.Float bounds;
  4611.         int index;
  4612.         final int width;
  4613.         final int height;
  4614.         List<MenuMouseListener> mouseListeners;
  4615.         final String name;
  4616.         IsValid validation;
  4617.  
  4618.         /**
  4619.          * Constructs a PaintMenuItem with the given name, parent, getPopup method,
  4620.          * width, height, and validation method.
  4621.          *
  4622.          * @param parent
  4623.          * @param name
  4624.          *            String
  4625.          * @param parent
  4626.          *            Menu
  4627.          * @param getPopup
  4628.          *            GetPopup
  4629.          * @param width
  4630.          *            int
  4631.          * @param height
  4632.          *            int
  4633.          * @param validation
  4634.          *            IsValid
  4635.          */
  4636.         public PaintMenuItem(PaintMenu parent, String name, GetPopup getPopup,
  4637.                 int width, int height, IsValid validation) {
  4638.             this.name = name;
  4639.             this.height = height;
  4640.             this.width = width;
  4641.             this.getPopup = getPopup;
  4642.             this.parent = parent;
  4643.             this.bounds = new RoundRectangle2D.Float(0, 0, width, height,
  4644.                     parent.style.roundCorner, parent.style.roundCorner);
  4645.             this.validation = validation;
  4646.         }
  4647.  
  4648.         /**
  4649.          * Constructs a PaintMenuItem with the given name, parent, width, height,
  4650.          * and validation method.
  4651.          *
  4652.          * @param parent
  4653.          * @param name
  4654.          *            String
  4655.          * @param parent
  4656.          *            Menu
  4657.          * @param width
  4658.          *            int
  4659.          * @param height
  4660.          *            int
  4661.          * @param validation
  4662.          *            IsValid
  4663.          */
  4664.         public PaintMenuItem(PaintMenu parent, String name, int width, int height,
  4665.                 IsValid validation) {
  4666.             this(parent, name, null, width, height, validation);
  4667.         }
  4668.  
  4669.         /**
  4670.          * sets this item's index
  4671.          *
  4672.          * @param idx
  4673.          *            int
  4674.          */
  4675.         public void setIndex(int idx) {
  4676.             this.index = idx;
  4677.         }
  4678.  
  4679.         /**
  4680.          * renders this item to the given Graphics2D
  4681.          *
  4682.          * @param g
  4683.          *            Graphics2D
  4684.          */
  4685.         abstract void render(Graphics2D g);
  4686.  
  4687.         /**
  4688.          * Checks if this item is valid.
  4689.          *
  4690.          * @return boolean isValid
  4691.          */
  4692.         boolean isValid() {
  4693.             return validation.isValid();
  4694.         }
  4695.  
  4696.         /**
  4697.          * Compiles this item
  4698.          */
  4699.         public void compile() {
  4700.             if (parent != null) {
  4701.                 this.bounds = new RoundRectangle2D.Float(0, 0, width, height,
  4702.                         parent.style.roundCorner, parent.style.roundCorner);
  4703.                 bounds.x = getX();
  4704.                 bounds.y = getY();
  4705.             }
  4706.         }
  4707.  
  4708.         /**
  4709.          * Gets the offset from upper left of parent depending on parent's FlowType
  4710.          *
  4711.          * @return int offsetX
  4712.          */
  4713.         public int getOffX() {
  4714.             if (parent.getFlowType().equals(FlowType.HORIZONTAL)) {
  4715.                 int x = 1;
  4716.                 for (int i = 0; i < index; i++)
  4717.                     if (parent.components.get(i).isValid())
  4718.                         x += 1 + parent.components.get(i).bounds.width;
  4719.  
  4720.                 return x;
  4721.             }
  4722.             if (parent.getWidth() > bounds.width)
  4723.                 return (int) (parent.getWidth() / 2 - bounds.width / 2);
  4724.             return 1;
  4725.         }
  4726.  
  4727.         /**
  4728.          * Gets the bounds of this item in the menu, including extra space.
  4729.          *
  4730.          * @return Rectangle PaintMenu bounds
  4731.          */
  4732.         public Rectangle getMenuBounds() {
  4733.             if (parent.getFlowType().equals(FlowType.VERTICAL))
  4734.                 return new Rectangle(parent.absX, (int) bounds.y, parent.width,
  4735.                         (int) bounds.height);
  4736.             else
  4737.                 return new Rectangle((int) bounds.x, parent.absY,
  4738.                         (int) bounds.width, parent.height);
  4739.         }
  4740.  
  4741.         /**
  4742.          * Gets the offset from upper left of parent depending on parent's FlowType
  4743.          *
  4744.          * @return int offsetX
  4745.          */
  4746.         public int getOffY() {
  4747.             if (parent.getFlowType().equals(FlowType.VERTICAL)) {
  4748.                 int y = 1;
  4749.                 for (int i = 0; i < index; i++)
  4750.                     if (parent.components.get(i).isValid())
  4751.                         y += 1 + parent.components.get(i).bounds.height;
  4752.  
  4753.                 return y;
  4754.             }
  4755.             if (parent.getHeight() > bounds.height)
  4756.                 return (int) (parent.getHeight() / 2 - bounds.height / 2);
  4757.             return 1;
  4758.         }
  4759.  
  4760.         /**
  4761.          * Gets the absolute x
  4762.          *
  4763.          * @return int absolute x
  4764.          */
  4765.         public int getX() {
  4766.             int x = getOffX() + parent.absX;
  4767.             return x;
  4768.         }
  4769.  
  4770.         /**
  4771.          * Gets the absolute y
  4772.          *
  4773.          * @return int absolute y
  4774.          */
  4775.         public int getY() {
  4776.             int y = getOffY() + parent.absY;
  4777.             return y;
  4778.         }
  4779.  
  4780.         /**
  4781.          * Gets this item's popup
  4782.          *
  4783.          * @return MenuPopUp
  4784.          */
  4785.         public PaintMenuPopUp getPopUp() {
  4786.             if (getPopup != null) {
  4787.                 PaintMenuPopUp m = getPopup.getPopUp();
  4788.                 m.setParent(this);
  4789.                 if (parent.defaultLineListener != null)
  4790.                     m.addLineListener(parent.defaultLineListener);
  4791.                 return m;
  4792.             }
  4793.             return null;
  4794.         }
  4795.  
  4796.         /**
  4797.          * Adds a mouselistener
  4798.          *
  4799.          * @param listen
  4800.          *            MouseListen
  4801.          */
  4802.         public void addMouseListener(MenuMouseListener listen) {
  4803.             if (mouseListeners == null)
  4804.                 mouseListeners = new ArrayList<MenuMouseListener>();
  4805.             mouseListeners.add(listen);
  4806.         }
  4807.  
  4808.         /**
  4809.          * Sends the mouse event to all of this item's listeners
  4810.          *
  4811.          * @param e
  4812.          *            MouseEvent
  4813.          */
  4814.         public void fireMouseEvent(MouseEvent e) {
  4815.             if (mouseListeners != null && mouseListeners.size() > 0)
  4816.                 for (MenuMouseListener m : mouseListeners)
  4817.                     m.MouseAction(e);
  4818.         }
  4819.     }
  4820.     /**
  4821.      * A class representing the popups in the paint interface
  4822.      *
  4823.      * @author Equinox_
  4824.      *
  4825.      */
  4826.     public static class PaintMenuPopUp {
  4827.         private Font font;
  4828.         private Color color;
  4829.         private PaintStyle style;
  4830.         RoundRectangle2D bkg;
  4831.         public RoundRectangle2D fg;
  4832.         public Rectangle txtBounds;
  4833.         public final String name;
  4834.         public String[] lines;
  4835.         PaintMenuItem parent;
  4836.         int width;
  4837.         int height;
  4838.         List<LineListener> lineListeners;
  4839.  
  4840.         /**
  4841.          * Creates a popup using the provided array of strings making up the lines
  4842.          * and name
  4843.          *
  4844.          * @param lines
  4845.          *            as String[]
  4846.          * @param name
  4847.          *            as String
  4848.          */
  4849.         public PaintMenuPopUp(String[] lines, String name) {
  4850.             this.name = name;
  4851.             this.lines = lines;
  4852.         }
  4853.  
  4854.         /**
  4855.          * Set this popup's parent and compiles locations
  4856.          *
  4857.          * @param parent
  4858.          *            MenuItem
  4859.          */
  4860.         public void setParent(PaintMenuItem parent) {
  4861.             this.parent = parent;
  4862.             this.style = parent.parent.style;
  4863.             // Calc dimensions
  4864.             color = name.equals("tools") ? style.TOOLS_FONT_COLOR
  4865.                     : style.FONT_COLOR;
  4866.             font = name.equals("tools") ? style.TOOLS_FONT : style.FONT;
  4867.             float line_height = font.getLineMetrics(lines[0], parent.parent.frc).getHeight();
  4868.             width = 0;
  4869.             for (String line : lines)
  4870.                 if (font.getStringBounds(line, parent.parent.frc).getWidth() > width)
  4871.                     width = (int) font.getStringBounds(line, parent.parent.frc).getWidth();
  4872.  
  4873.             height = (int) (lines.length * line_height);
  4874.  
  4875.             width += style.roundCorner + 2;
  4876.             height += style.roundCorner + 2;
  4877.  
  4878.             int halfPadding = (parent.parent.padding / 2);
  4879.             int x = 0;
  4880.             int y = 0;
  4881.             if (parent.parent.getFlowType().equals(FlowType.HORIZONTAL)) {
  4882.                 if (parent.bounds.y - height - parent.parent.padding > 0) {
  4883.                     // Above
  4884.                     x = (int) (parent.bounds.x - width + parent.bounds.width + halfPadding);
  4885.                     if (x < 0)
  4886.                         x = (int) (parent.bounds.x - halfPadding);
  4887.                     y = (int) (parent.parent.absY - height);
  4888.                 } else {
  4889.                     // Below
  4890.                     x = (int) (parent.bounds.x - halfPadding);
  4891.                     if (x + width > parent.parent.bounds.width)
  4892.                         x = (int) (parent.bounds.x - halfPadding - width);
  4893.                     y = (int) (parent.parent.absY + parent.parent.getHeight());
  4894.                 }
  4895.             } else {
  4896.                 // Vertical
  4897.                 if (parent.parent.absX - width > 0) {
  4898.                     // Left
  4899.                     x = (int) (parent.parent.absX - width);
  4900.                     if (parent.bounds.y - height + halfPadding
  4901.                             + parent.bounds.height > 0)
  4902.                         y = (int) (parent.bounds.y - height + halfPadding + parent.bounds.height);
  4903.                     else
  4904.                         y = (int) (parent.bounds.y - halfPadding);
  4905.                 } else {
  4906.                     // Right
  4907.                     x = (int) (parent.parent.absX + parent.parent.getWidth());
  4908.                     if (parent.bounds.y - height + halfPadding
  4909.                             + parent.bounds.height > 0)
  4910.                         y = (int) (parent.bounds.y - height + halfPadding + parent.bounds.height);
  4911.                     else
  4912.                         y = (int) (parent.bounds.y - halfPadding);
  4913.                 }
  4914.             }
  4915.             bkg = new RoundRectangle2D.Float(x, y, width, height,
  4916.                     style.roundCorner > 0 ? style.roundCorner + 1 : 0,
  4917.                     style.roundCorner > 0 ? style.roundCorner + 1 : 0);
  4918.             fg = new RoundRectangle2D.Float((float) bkg.getX() + 1,
  4919.                     (float) bkg.getY() + 1, width - 2, height - 2,
  4920.                     style.roundCorner, style.roundCorner);
  4921.             txtBounds = new Rectangle((int) (fg.getX() + (style.roundCorner / 2)),
  4922.                     (int) (fg.getY() + (style.roundCorner / 2)), (int) (width
  4923.                             - style.roundCorner - 2), (int) (height
  4924.                             - style.roundCorner - 2));
  4925.         }
  4926.  
  4927.         /**
  4928.          * Paints this popup to using the provided Graphics2D in relation to it's
  4929.          * parent PaintInterface
  4930.          *
  4931.          * @param g
  4932.          *            as Graphics2D
  4933.          */
  4934.         public void render(Graphics2D g) {
  4935.             g.setColor(style.bgColor);
  4936.             g.fill(bkg);
  4937.             g.setColor(style.fgColor);
  4938.             g.fill(fg);
  4939.  
  4940.             float line_height = (txtBounds.height - 4) / lines.length;
  4941.  
  4942.             g.setFont(font);
  4943.             g.setColor(color);
  4944.             for (int i = 0; i < lines.length; i++) {
  4945.                 if (new Rectangle(txtBounds.x,
  4946.                         (int) ((i * (line_height)) + txtBounds.y), txtBounds.width,
  4947.                         (int) line_height).contains(parent.parent.mouseLoc)
  4948.                         && hasAction(lines[i])) {
  4949.                     g.setColor(style.highlightColor);
  4950.                     g.fill(new Rectangle(txtBounds.x,
  4951.                             (int) ((i * (line_height)) + 2 + txtBounds.y),
  4952.                             txtBounds.width, (int) line_height));
  4953.                 }
  4954.                 g.setColor(color);
  4955.                 g.drawString(lines[i], txtBounds.x, ((i + 1) * (line_height))
  4956.                         + txtBounds.y);
  4957.             }
  4958.         }
  4959.  
  4960.         boolean hasAction(String line) {
  4961.             if (lineListeners != null)
  4962.                 for (LineListener l : lineListeners)
  4963.                     if (l.hasAction(line))
  4964.                         return true;
  4965.             return false;
  4966.         }
  4967.  
  4968.         /**
  4969.          * Adds the specified line listener
  4970.          *
  4971.          * @param l
  4972.          *            LineListener
  4973.          */
  4974.         public void addLineListener(LineListener l) {
  4975.             if (lineListeners == null)
  4976.                 lineListeners = new ArrayList<LineListener>();
  4977.             this.lineListeners.add(l);
  4978.         }
  4979.  
  4980.         /**
  4981.          * fires the specified mouse event using this popup's line listeners
  4982.          *
  4983.          * @param e
  4984.          *            MouseEvent
  4985.          */
  4986.         public void fireMouseEvent(MouseEvent e) {
  4987.             if (!bkg.contains(e.getPoint()) || lineListeners == null
  4988.                     || lineListeners.size() == 0)
  4989.                 return;
  4990.  
  4991.             int line_height = (txtBounds.height - 4) / lines.length;
  4992.  
  4993.             for (int i = 0; i < lines.length; i++) {
  4994.                 Rectangle stringRect = new Rectangle(txtBounds.x,
  4995.                         (i * (line_height)) + txtBounds.y, txtBounds.width,
  4996.                         line_height);
  4997.                 if (stringRect.contains(e.getPoint())) {
  4998.                     for (LineListener lineListener : lineListeners)
  4999.                         lineListener.fireEvent(e, lines[i]);
  5000.                     return;
  5001.                 }
  5002.             }
  5003.         }
  5004.     }
  5005.     /**
  5006.      * A class representing a progress bar in a menu
  5007.      *
  5008.      * @author Equinox_
  5009.      *
  5010.      */
  5011.     public static class PaintMenuProgressBar extends PaintMenuItem {
  5012.         final static int width = 150;
  5013.         final static int height = 30;
  5014.  
  5015.         final GetPercent getPercent;
  5016.         RoundRectangle2D.Double percent;
  5017.         Point textLoc;
  5018.         String prefix;
  5019.  
  5020.         /**
  5021.          * Constructs a MenuProgress bar using the specified name, parent,
  5022.          * getPercent method, text prefix, getPopup method, and validation method.
  5023.          *
  5024.          * @param name
  5025.          *            String
  5026.          * @param parent
  5027.          *            Menu
  5028.          * @param getPercent
  5029.          *            GetPercent
  5030.          * @param prefix
  5031.          *            String
  5032.          * @param getPopup
  5033.          *            GetPopup
  5034.          * @param validation
  5035.          *            IsValid
  5036.          */
  5037.         public PaintMenuProgressBar(PaintMenu parent, String name,
  5038.                 GetPercent getPercent, String prefix, GetPopup getPopup,
  5039.                 IsValid validation) {
  5040.             super(parent, name, getPopup, width, height, validation);
  5041.             this.getPercent = getPercent;
  5042.             this.prefix = prefix;
  5043.             percent = new RoundRectangle2D.Double(bounds.x, bounds.y, 0,
  5044.                     bounds.height, bounds.arcwidth, bounds.archeight);
  5045.             compile();
  5046.         }
  5047.  
  5048.         /**
  5049.          * Renders this progress bar to the given Graphics context
  5050.          */
  5051.         public void render(Graphics2D g) {
  5052.             percent.width = (width / 100d) * getPercent.getPercent();
  5053.             g.setColor(parent.style.progressBarBG);
  5054.             g.fill(bounds);
  5055.             g.setColor(parent.style.progressBarFG);
  5056.             g.fill(percent);
  5057.             String perct = new DecimalFormat("00.00").format(getPercent
  5058.                     .getPercent());
  5059.             if (perct.length() < 2)
  5060.                 perct = "0" + perct;
  5061.             g.setFont(parent.style.FONT);
  5062.             g.setColor(parent.style.FONT_COLOR);
  5063.             g.drawString(prefix + ": " + perct + "%", textLoc.x, textLoc.y);
  5064.         }
  5065.  
  5066.         /**
  5067.          * Compiles this progress bar
  5068.          */
  5069.         public void compile() {
  5070.             super.compile();
  5071.             percent = new RoundRectangle2D.Double(bounds.x, bounds.y, 0,
  5072.                     bounds.height, bounds.arcwidth, bounds.archeight);
  5073.             textLoc = new Point();
  5074.             textLoc.x = (int) bounds.getCenterX();
  5075.             textLoc.y = (int) bounds.getCenterY();
  5076.             textLoc.y += parent.style.FONT.getLineMetrics(prefix + ": 00.00%",
  5077.                     parent.frc).getHeight() / 2f;
  5078.             textLoc.x -= parent.style.FONT.getStringBounds(prefix + ": 00.00%",
  5079.                     parent.frc).getWidth() / 2f;
  5080.         }
  5081.     }
  5082.     public static class PaintMenuSkillProgressBar extends PaintMenuProgressBar {
  5083.         /**
  5084.          * Constructs a Skill Based Progress Bar based on a Next-Level status
  5085.          *
  5086.          * @param parent
  5087.          *            Menu
  5088.          * @param SkillID
  5089.          *            int
  5090.          * @param skills
  5091.          *            Skills
  5092.          */
  5093.         public PaintMenuSkillProgressBar(final int SkillID,
  5094.                 final Skills skills, final int startExp, final PaintMenu parent) {
  5095.             super(parent, "nextlvl" + Skills.SKILL_NAMES[SkillID],
  5096.                     new GetPercent() {
  5097.                         public double getPercent() {
  5098.                             final int lvl = skills.getRealLevel(SkillID);
  5099.                             if (lvl >= (SkillID == Skills.DUNGEONEERING ? 120 : 99))
  5100.                                 return 100;
  5101.                             final double xpTotal = Skills.XP_TABLE[lvl + 1]
  5102.                                     - Skills.XP_TABLE[lvl];
  5103.                             if (xpTotal == 0)
  5104.                                 return 100;
  5105.                             final double xpDone = skills.getCurrentExp(SkillID)
  5106.                                     - Skills.XP_TABLE[lvl];
  5107.                             double result = xpDone / xpTotal;
  5108.                             if (result > 1)
  5109.                                 result = 1;
  5110.                             if (result < 0)
  5111.                                 result = 1;
  5112.                             return (result * 100d);
  5113.                         }
  5114.                     }, PaintFormatter.properCaseAll(Skills.SKILL_NAMES[SkillID])
  5115.                             + " NL", new GetPopup() {
  5116.                         public PaintMenuPopUp getPopUp() {
  5117.                             long millis = Math.abs(System.currentTimeMillis()
  5118.                                     - parent.startTime);
  5119.                             if (millis <= 0)
  5120.                                 millis = 1;
  5121.                             long expGained = skills.getCurrentExp(SkillID)
  5122.                                     - startExp;
  5123.                             long expPerHour = (expGained * 3600000l) / millis;
  5124.                             String TTL = "No Exp Gained";
  5125.                             if (expPerHour > 0)
  5126.                                 TTL = PaintFormatter
  5127.                                         .getFormattedTime((long) (((float) skills
  5128.                                                 .getExpToNextLevel(SkillID))
  5129.                                                 / ((float) expPerHour) * 3600000f));
  5130.                             String skillName = PaintFormatter
  5131.                                     .properCaseAll(Skills.SKILL_NAMES[SkillID]);
  5132.                             String[] lines = new String[4];
  5133.                             lines[0] = skillName + " Exp gained: "
  5134.                                     + PaintFormatter.getFormattedMoney(expGained);
  5135.                             lines[1] = skillName + " Exp per Hour: "
  5136.                                     + PaintFormatter.getFormattedMoney(expPerHour);
  5137.                             lines[2] = skillName
  5138.                                     + " Exp 'till level: "
  5139.                                     + PaintFormatter.getFormattedMoney(skills
  5140.                                             .getExpToNextLevel(SkillID));
  5141.                             lines[3] = "Time 'till " + skillName + " level: " + TTL;
  5142.                             return new PaintMenuPopUp(lines, Skills.SKILL_NAMES[SkillID]
  5143.                                     + "nextlevel");
  5144.                         }
  5145.  
  5146.                         public String getName() {
  5147.                             return Skills.SKILL_NAMES[SkillID] + "nextlevel";
  5148.                         }
  5149.                     }, new IsValid() {
  5150.                         public boolean isValid() {
  5151.                             final int lvl = skills.getRealLevel(SkillID);
  5152.                             if (lvl >= (SkillID == Skills.DUNGEONEERING ? 120 : 99))
  5153.                                 return false;
  5154.                             return skills.getCurrentExp(SkillID) > startExp;
  5155.                         }
  5156.                     });
  5157.         }
  5158.  
  5159.         /**
  5160.          * Constructs a Skill Based Progress Bar based on a set goal experience
  5161.          *
  5162.          * @param parent
  5163.          *            Menu
  5164.          * @param SkillID
  5165.          *            int
  5166.          * @param GoalEXP
  5167.          *            int
  5168.          * @param skills
  5169.          *            Skills
  5170.          */
  5171.         public PaintMenuSkillProgressBar(final PaintMenu parent, final int SkillID,
  5172.                 final int GoalEXP, final Skills skills, final int startExp) {
  5173.             super(parent, "goalbar" + Skills.SKILL_NAMES[SkillID],
  5174.                     new GetPercent() {
  5175.                         public double getPercent() {
  5176.                             double expTillGoalStart = GoalEXP - startExp;
  5177.                             double expGained = skills.getCurrentExp(SkillID)
  5178.                                     - startExp;
  5179.                             double result = expGained / expTillGoalStart;
  5180.                             if (result > 1)
  5181.                                 result = 1;
  5182.                             if (result < 0)
  5183.                                 result = 0;
  5184.                             return (result * 100d);
  5185.                         }
  5186.                     }, PaintFormatter.properCaseAll(Skills.SKILL_NAMES[SkillID])
  5187.                             + " Goal", new GetPopup() {
  5188.                         public PaintMenuPopUp getPopUp() {
  5189.                             long millis = Math.abs(System.currentTimeMillis()
  5190.                                     - parent.startTime);
  5191.                             if (millis <= 0)
  5192.                                 millis = 1;
  5193.                             long expGained = skills.getCurrentExp(SkillID)
  5194.                                     - startExp;
  5195.                             long expPerHour = (expGained * 3600000l) / millis;
  5196.                             int expTillGoal = (int) (GoalEXP - skills
  5197.                                     .getCurrentExp(SkillID));
  5198.                             String TTG = "";
  5199.                             int TTGunf = (int) ((expPerHour > 0) ? (((float) expTillGoal)
  5200.                                     / ((float) expPerHour) * 3600000f)
  5201.                                     : 0);
  5202.                             if (TTGunf != 0)
  5203.                                 TTG = PaintFormatter.getFormattedTime(TTGunf);
  5204.                             else
  5205.                                 TTG = "No exp gained";
  5206.                             String[] lines = new String[6];
  5207.                             String skillName = PaintFormatter
  5208.                                     .properCaseAll(Skills.SKILL_NAMES[SkillID]);
  5209.                             lines[0] = skillName + " Goal Level: "
  5210.                                     + Skills.getLevelAt(GoalEXP);
  5211.                             lines[1] = skillName + " Goal Exp: "
  5212.                                     + PaintFormatter.getFormattedMoney(GoalEXP);
  5213.                             lines[2] = skillName + " Exp gained: "
  5214.                                     + PaintFormatter.getFormattedMoney(expGained);
  5215.                             lines[3] = skillName + " Exp per Hour: "
  5216.                                     + PaintFormatter.getFormattedMoney(expPerHour);
  5217.                             lines[4] = "Exp 'till " + skillName + " goal: "
  5218.                                     + PaintFormatter.getFormattedMoney(expTillGoal);
  5219.                             lines[5] = "Time 'till " + skillName + " goal: " + TTG;
  5220.                             return new PaintMenuPopUp(lines, Skills.SKILL_NAMES[SkillID]
  5221.                                     + "goalpopup");
  5222.                         }
  5223.  
  5224.                         public String getName() {
  5225.                             return Skills.SKILL_NAMES[SkillID] + "goalpopup";
  5226.                         }
  5227.                     }, new IsValid() {
  5228.                         public boolean isValid() {
  5229.                             return skills.getCurrentExp(SkillID) > startExp;
  5230.                         }
  5231.                     });
  5232.         }
  5233.     }
  5234.     /**
  5235.      * Used to style the paint
  5236.      *
  5237.      * @author Equinox_
  5238.      *
  5239.      */
  5240.     public static class PaintStyle {
  5241.         public final Color FONT_COLOR = Color.BLACK;
  5242.         public final Font FONT = new Font("Arial", 1, 10)
  5243.                 .deriveFont(Font.PLAIN);
  5244.  
  5245.         public final Font TOOLS_FONT = new Font("Arial", 1, 14)
  5246.                 .deriveFont(Font.PLAIN);
  5247.         public final Color TOOLS_FONT_COLOR = Color.BLACK;
  5248.  
  5249.         public final float roundCorner = 7.5f;
  5250.         public final Color progressBarFG = new Color(0f, 1f, 0f, 1f);
  5251.         public final Color progressBarBG = new Color(.5f, .5f, .5f, 1f);
  5252.         public final Color bgColor = new Color(0f, 0f, 0f, 0.75f);
  5253.         final Color fgColor = new Color(.5f, .5f, .5f, 1f);
  5254.         final Color highlightColor = new Color(1f, 0f, 0f, 0.075f);
  5255.     }
  5256.     /**
  5257.      * A range class for randomized items
  5258.      *
  5259.      * @author Equinox_
  5260.      *
  5261.      */
  5262.     public static class Range {
  5263.         public int MAX;
  5264.         public int MIN;
  5265.  
  5266.         public Range() {
  5267.             MAX = 1;
  5268.             MIN = -1;
  5269.         }
  5270.  
  5271.         /**
  5272.          *
  5273.          * @param min
  5274.          *            The inclusive lower bound
  5275.          * @param max
  5276.          *            The inclusive upper bound
  5277.          */
  5278.         public Range(int min, int max) {
  5279.             MAX = Math.max(min, max) + 1;
  5280.             MIN = Math.min(min, max);
  5281.         }
  5282.  
  5283.         /**
  5284.          * Returns a linearly distributed pseudorandom integer.
  5285.          *
  5286.          * @return Random integer min <= n < max.
  5287.          */
  5288.         public int randomLinearDistribution() {
  5289.             return MIN + (MAX == MIN ? 0 : new Random().nextInt(MAX - MIN));
  5290.         }
  5291.  
  5292.         /**
  5293.          * Returns a normally distributed pseudorandom integer about a mean centered
  5294.          * between min and max with a calculated standard deviation.
  5295.          *
  5296.          * @return Random integer min <= n < max from the normal distribution
  5297.          *         described by the parameters.
  5298.          */
  5299.         public int randomNormalDistribution() {
  5300.             int mean = MIN + (MAX - MIN) / 2;
  5301.             int rand;
  5302.             int sd = (MAX - MIN) / 3;
  5303.             do {
  5304.                 rand = (int) (new Random().nextGaussian() * sd + mean);
  5305.             } while (rand < MIN || rand >= MAX);
  5306.             return rand;
  5307.         }
  5308.  
  5309.         /**
  5310.          * Returns a normally distributed pseudorandom integer about a mean centered
  5311.          * between min and max with a provided standard deviation.
  5312.          *
  5313.          * @param sd
  5314.          *            The standard deviation. A higher value will increase the
  5315.          *            probability of numbers further from the mean being returned.
  5316.          * @return Random integer min <= n < max from the normal distribution
  5317.          *         described by the parameters.
  5318.          */
  5319.         public int randomNormalDistribution(int sd) {
  5320.             int mean = MIN + (MAX - MIN) / 2;
  5321.             int rand;
  5322.             do {
  5323.                 rand = (int) (new Random().nextGaussian() * sd + mean);
  5324.             } while (rand < MIN || rand >= MAX);
  5325.             return rand;
  5326.         }
  5327.  
  5328.         public String toString() {
  5329.             return MIN + "-" + MAX;
  5330.         }
  5331.     }
  5332. }
Advertisement
Add Comment
Please, Sign In to add comment