Created
July 14, 2012 06:52
-
-
Save battleguard/3109764 to your computer and use it in GitHub Desktop.
Alchs all the items you select. No messing with ids. Automatically sets up alch spot for you!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.powerbot.core.event.events.MessageEvent; | |
import org.powerbot.core.event.listeners.MessageListener; | |
import org.powerbot.core.event.listeners.PaintListener; | |
import org.powerbot.core.script.ActiveScript; | |
import org.powerbot.core.script.job.Task; | |
import org.powerbot.core.script.job.state.Node; | |
import org.powerbot.game.api.Manifest; | |
import org.powerbot.game.api.methods.Game; | |
import org.powerbot.game.api.methods.Tabs; | |
import org.powerbot.game.api.methods.Widgets; | |
import org.powerbot.game.api.methods.input.Mouse; | |
import org.powerbot.game.api.methods.tab.Inventory; | |
import org.powerbot.game.api.methods.tab.Skills; | |
import org.powerbot.game.api.methods.widget.Camera; | |
import org.powerbot.game.api.methods.widget.Lobby; | |
import org.powerbot.game.api.util.Filter; | |
import org.powerbot.game.api.util.Random; | |
import org.powerbot.game.api.util.SkillData; | |
import org.powerbot.game.api.util.Time; | |
import org.powerbot.game.api.util.Timer; | |
import org.powerbot.game.api.wrappers.node.Item; | |
import org.powerbot.game.api.wrappers.widget.WidgetChild; | |
import javax.imageio.ImageIO; | |
import javax.swing.*; | |
import java.awt.*; | |
import java.awt.event.*; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.net.URL; | |
import java.text.DecimalFormat; | |
import java.text.NumberFormat; | |
import java.util.ArrayList; | |
@Manifest(name = "The Best Alcher v3.2", authors = { "Battleguard, Logicail" }, description= "Magic: Alchs all the items you select. No messing with ids. Automatically sets up alch spot for you!", version=3.2, website="http://www.powerbot.org/community/topic/733464-the-best-alcher-alchs-all-selected-items-no-ids-required-sets-up-items-for-you/") | |
public class TheBestAlcher extends ActiveScript implements PaintListener, MessageListener, MouseListener { | |
private static Timer runTime; | |
private static Timer lastAlch = new Timer(0); | |
private static Rectangle ALCH_REC, ITEM_SPOT_REC, HIGH_ALCH_SPELL_REC; | |
private static Point ALCH_PNT; | |
private static int START_XP = 0, START_LVL = 0; | |
private static final ArrayList<Item> goodItems = new ArrayList<Item>(); | |
private static AlchGui gui; | |
private static boolean runPaint = false, highalch; | |
private static int tries = 0; | |
private String version; | |
@Override | |
public void onStart() { | |
version = "v" + this.getClass().getAnnotation(Manifest.class).version(); | |
System.setProperty("http.agent", ""); | |
getAlchList(); | |
getContainer().submit(new ImageLoader()); | |
getContainer().submit(new PriceLoader()); | |
gui = new AlchGui(); | |
}; | |
private void startScript() { | |
jobs = new Node[] { new Restart(), new Alch(), new Cam() }; | |
runPaint = true; | |
runTime = new Timer(0); | |
} | |
private Node[] jobs; | |
@Override | |
public int loop() { | |
if(jobs == null) return 50; | |
for (Node curJob : jobs) { | |
if(curJob.activate()) { | |
curJob.execute(); | |
break; | |
} | |
} | |
return 50; | |
} | |
private static final Filter<Item> alchFilter = new Filter<Item>() { | |
public boolean accept(Item item) { | |
if (!item.getName().contains(" rune") && item.getStackSize() > 10) { | |
goodItems.add(item); | |
} | |
return false; | |
} | |
}; | |
private void getAlchList() { | |
Tabs.INVENTORY.open(); | |
Inventory.getItems(alchFilter); | |
itemCheckbox = new JCheckBox[goodItems.size()]; | |
itemImages = new ImageIcon[goodItems.size()]; | |
itemProfits = new JLabel[goodItems.size()]; | |
for (int i = 0; i < goodItems.size(); i++) { | |
itemCheckbox[i] = new JCheckBox(goodItems.get(i).getName()); | |
} | |
} | |
private class PriceLoader extends Task { | |
@Override | |
public void execute() { | |
int naturePrice; | |
try { | |
naturePrice = Integer.parseInt(lookup(561, false)[1]); | |
System.out.println("Nature Price: " + naturePrice); | |
} catch (Exception e) { | |
System.out.println("Failed to load nature rune price"); | |
return; | |
} | |
for (int i = 0; i < itemImages.length; i++) { | |
try { | |
System.out.println("Nature rune price: " + naturePrice); | |
final String[] info = lookup(goodItems.get(i).getId(), false); | |
System.out.println("Alch: " + info[2] + " GE: " + info[1]); | |
final int alch = Integer.parseInt(info[2]) - Integer.parseInt(info[1]); | |
System.out.println("Alch - buyprice: " + alch); | |
final int profit = alch - naturePrice; | |
itemProfits[i] = new JLabel("" + profit + "gp"); | |
itemProfits[i].setForeground(profit > 0 ? Color.green : Color.red); | |
} catch (Exception e) { | |
System.out.println("Failed to get alch profit on: " + goodItems.get(i).getName()); | |
continue; | |
} | |
gui.draw(); | |
} | |
} | |
} | |
private class ImageLoader extends Task { | |
@Override | |
public void execute() { | |
for (int i = 0; i < itemImages.length; i++) { | |
itemImages[i] = getItemImage(goodItems.get(i).getId()); | |
gui.draw(); | |
} | |
} | |
} | |
private class Restart extends Node { | |
@Override | |
public boolean activate() { | |
return Game.isLoggedIn() && Game.getClientState() == Game.INDEX_MAP_LOADED && !Lobby.isOpen() | |
&& (!lastAlch.isRunning() || (Tabs.INVENTORY.isOpen() && Inventory.getCount(goodItems.get(0).getId()) == 0)); | |
} | |
@Override | |
public void execute() { | |
try { | |
System.out.println("resetting alch"); | |
if (getOverlap()) { | |
Tabs.MAGIC.open(); | |
Mouse.move(ALCH_PNT.x, ALCH_PNT.y); | |
lastAlch = new Timer(5000); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
System.out.println("showing error message"); | |
if(!ACTIVE) { | |
new ErrorMessageGUI(e); | |
} | |
} | |
} | |
} | |
private int getBestSlot() throws Exception { | |
Dimension bestRec = new Dimension(0, 0); | |
final WidgetChild inv = Inventory.getWidget(false); | |
int idx = 0; | |
for (int i = 0; i < 28; i++) { | |
final Rectangle rec = inv.getChild(i).getBoundingRectangle().intersection(HIGH_ALCH_SPELL_REC); | |
if (!rec.isEmpty() && rec.width * rec.height > bestRec.width * bestRec.height) { | |
bestRec = rec.getSize(); | |
idx = i; | |
} | |
} | |
return idx; | |
} | |
private boolean checkInventory = false; | |
private boolean isWieldable(final Item curItem) throws Exception { | |
if (curItem == null) return false; | |
final String[] actions = curItem.getWidgetChild().getActions(); | |
if (actions != null) { | |
for (String action : actions) { | |
if (action != null && action.startsWith("Wield")) return true; | |
} | |
} | |
return false; | |
} | |
private void checkEquipped() throws Exception { | |
Task.sleep(500); | |
Tabs.EQUIPMENT.open(); | |
Task.sleep(1000); | |
final WidgetChild arrow = Widgets.get(387, 37); | |
if(arrow != null && arrow.getChildId() == goodItems.get(0).getId()) { | |
arrow.click(true); | |
Task.sleep(1000); | |
Tabs.INVENTORY.open(); | |
return; | |
} | |
} | |
private boolean getOverlap() throws Exception { | |
if(goodItems.isEmpty()) { | |
stop(); | |
return false; | |
} | |
if (tries >= 5) { | |
goodItems.remove(0); | |
tries = 0; | |
return false; | |
} | |
Tabs.INVENTORY.open(); | |
Task.sleep(500); | |
final Item curItem = Inventory.getItem(goodItems.get(0).getId()); | |
if(curItem == null) { | |
if(checkInventory) { | |
checkEquipped(); | |
} | |
tries++; | |
return false; | |
} | |
checkInventory = isWieldable(curItem); | |
ITEM_SPOT_REC = curItem.getWidgetChild().getBoundingRectangle(); | |
Tabs.MAGIC.open(); | |
Task.sleep(500); | |
HIGH_ALCH_SPELL_REC = Widgets.get(192, highalch ? 59 : 38).getBoundingRectangle(); | |
ALCH_REC = ITEM_SPOT_REC.intersection(HIGH_ALCH_SPELL_REC); | |
if (ALCH_REC.isEmpty()) { | |
Tabs.INVENTORY.open(); | |
final Rectangle rec = Inventory.getWidget(false).getChild(getBestSlot()).getBoundingRectangle(); | |
curItem.getWidgetChild().interact("Use"); | |
Task.sleep(500, 1000); | |
Mouse.move((int) ITEM_SPOT_REC.getCenterX(), (int) ITEM_SPOT_REC.getCenterY()); | |
Task.sleep(500, 1000); | |
Mouse.drag((int) rec.getCenterX(), (int) rec.getCenterY()); | |
Task.sleep(500, 1000); | |
tries++; | |
return false; | |
} | |
ALCH_PNT = new Point((int) ALCH_REC.getCenterX(), (int) ALCH_REC.getCenterY()); | |
tries = 0; | |
return true; | |
} | |
public class Alch extends Node { | |
public Point getNextClickSpot() throws Exception { | |
int x = Mouse.getX(); | |
int y = Mouse.getY(); | |
x -= (x > (int) ALCH_REC.getCenterX() ? 10 : 5); | |
y -= (y > (int) ALCH_REC.getCenterY() ? 10 : 5); | |
return new Point(Random.nextGaussian(x, x + 15, 1), Random.nextGaussian(y, y + 15, 1)); | |
} | |
@Override | |
public boolean activate() { | |
return Game.isLoggedIn() && !Lobby.isOpen() && Tabs.MAGIC.isOpen(); | |
} | |
@Override | |
public void execute() { | |
try { | |
if (antiBan) { | |
Task.sleep(100, 450); | |
final Point p = getNextClickSpot(); | |
if (ALCH_REC.contains(p)) { | |
Mouse.move(p); | |
} | |
} | |
if (!ALCH_REC.contains(Mouse.getLocation())) { | |
Mouse.hop(ALCH_PNT.x, ALCH_PNT.y); | |
} | |
Mouse.click(true); | |
Task.sleep(60, 120); | |
Mouse.click(true); | |
lastAlch.reset(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
if(!ACTIVE) { | |
System.out.println("showing error message"); | |
new ErrorMessageGUI(e); | |
} | |
} | |
} | |
} | |
private class Cam extends Node { | |
final Timer timer = new Timer(Random.nextInt(60, 180) * 1000); | |
@Override | |
public boolean activate() { | |
return Game.isLoggedIn() && !Lobby.isOpen() && !timer.isRunning(); | |
} | |
@Override | |
public void execute() { | |
Camera.setPitch(Random.nextInt(10, 90)); | |
Camera.setAngle(Random.nextInt(340, 380) % 360); | |
timer.setEndIn(Random.nextInt(60, 180) * 1000); | |
} | |
} | |
private final static NumberFormat k = new DecimalFormat("###,###,###"); | |
private final static Color OPAQUE_BLACK = new Color(0, 0, 0, 160); | |
private final static Color OPAQUE_BLACK2 = new Color(0, 0, 0, 180); | |
private final static Color GREEN = new Color(85, 110, 36, 180); | |
private static final Color BLUE = new Color(0, 0, 255, 180); | |
SkillData skillData; | |
@Override | |
public void onRepaint(Graphics g) { | |
if (!runPaint) { | |
return; | |
} | |
if (START_LVL == 0) { | |
if (Game.isLoggedIn()) { | |
START_XP = Skills.getExperience(Skills.MAGIC); | |
START_LVL = Skills.getLevel(Skills.MAGIC); | |
highalch = START_LVL > 54; | |
} | |
} | |
if (ITEM_SPOT_REC != null && HIGH_ALCH_SPELL_REC != null && ALCH_REC != null) { | |
g.setColor(BLUE); | |
g.drawRect(ITEM_SPOT_REC.x, ITEM_SPOT_REC.y, ITEM_SPOT_REC.width, ITEM_SPOT_REC.height); | |
g.drawRect(HIGH_ALCH_SPELL_REC.x, HIGH_ALCH_SPELL_REC.y, HIGH_ALCH_SPELL_REC.width, HIGH_ALCH_SPELL_REC.height); | |
g.setColor(GREEN); | |
g.fill3DRect(ALCH_REC.x, ALCH_REC.y, ALCH_REC.width, ALCH_REC.height, true); | |
} | |
final int CUR_XP = Skills.getExperience(Skills.MAGIC); | |
final int CUR_XP_GAIN = CUR_XP - START_XP; | |
final double XP_PER_HOUR = (long) (CUR_XP_GAIN * (3600000.0 / runTime.getElapsed())); | |
final int CUR_LVL = Skills.getLevel(Skills.MAGIC); | |
final int ALCHS = CUR_XP_GAIN / (highalch ? 65 : 31); | |
final double ALCHS_PER_HOUR = (long) (ALCHS * (3600000.0 / runTime.getElapsed())); | |
final double Percentage = (double) (CUR_XP - Skills.XP_TABLE[CUR_LVL]) / (double) (Skills.XP_TABLE[CUR_LVL + 1] - Skills.XP_TABLE[CUR_LVL]); | |
final long TTL = CUR_XP_GAIN > 0 ? ((runTime.getElapsed() * (Skills.XP_TABLE[CUR_LVL + 1] - CUR_XP)) / CUR_XP_GAIN) : 0; | |
if (!highalch && CUR_LVL > 54) { | |
highalch = true; | |
lastAlch.setEndIn(0); | |
} | |
int yComp = 345, xComp = 20; | |
g.setColor(OPAQUE_BLACK); | |
g.fill3DRect(0, yComp, 300, 45, true); | |
g.setFont(new Font("Gayatri", 0, 12)); | |
final String l1 = version + " Run Time: " + runTime.toElapsedString() + " Alchs : " + k.format(ALCHS) + " - " + k.format(ALCHS_PER_HOUR) + "/H"; | |
shadowText(g, l1, xComp, yComp += 18); | |
g.setColor(Color.BLACK); | |
g.draw3DRect(xComp - 5, yComp + 5, 270, 17, true); | |
g.setColor(OPAQUE_BLACK2); | |
g.fill3DRect(xComp - 5, yComp + 5, 270, 17, true); | |
g.setColor(BLUE); | |
g.fillRect(xComp - 4, yComp + 6, (int) (268 * Percentage), 15); | |
final String l2 = "Magic (" + CUR_LVL + ")" + (CUR_LVL == START_LVL ? "" : "+" + (CUR_LVL - START_LVL)) | |
+ " - " + formatter(CUR_XP_GAIN) + " - " + formatter((int) XP_PER_HOUR) + "/H - TTL: " + Time.format(TTL); | |
shadowText(g, l2, xComp, yComp += 18); | |
drawButton(g); | |
drawMouse(g); | |
} | |
private void drawMouse(Graphics g) { | |
g.setColor(Mouse.isPressed() ? Color.RED : Color.GREEN); | |
final Point m = Mouse.getLocation(); | |
g.drawLine(m.x - 5, m.y + 5, m.x + 5, m.y - 5); | |
g.drawLine(m.x - 5, m.y - 5, m.x + 5, m.y + 5); | |
} | |
private final static Rectangle REC = new Rectangle(372, 395, 125, 15); | |
private boolean antiBan = false; | |
private void drawButton(Graphics g) { | |
g.setColor(Color.BLACK); | |
g.drawRect(REC.x - 1, REC.y - 1, REC.width + 1, REC.height + 1); | |
g.setColor(antiBan ? new Color(85, 110, 36, 180) : new Color(153, 0, 36, 180)); | |
g.fillRect(REC.x, REC.y, REC.width, REC.height); | |
g.setColor(Color.WHITE); | |
shadowText(g, "Antiban: " + (antiBan ? "Enabled" : "Disabled"), REC.x + 3, REC.y + 13); | |
} | |
private void shadowText(Graphics g, final String line, final int x, final int y) { | |
g.setColor(Color.BLACK); | |
g.drawString(line, x + 1, y + 1); | |
g.setColor(Color.WHITE); | |
g.drawString(line, x, y); | |
} | |
private String formatter(final int num) { | |
return num / 1000 + "." + (num % 1000) / 100 + "K"; | |
} | |
private ImageIcon getItemImage(final int ITEM_ID) { | |
try { | |
return new ImageIcon(ImageIO.read(new URL("http://www.tip.it/runescape/styles/images/gec/items/" + ITEM_ID + ".gif"))); | |
} catch (IOException e) { | |
try { | |
return new ImageIcon(ImageIO.read(new URL("http://www.tip.it/runescape/styles/images/gec/items/" + (ITEM_ID - 1) + ".gif"))); | |
} catch (IOException f) { | |
System.out.println("Could not find item Image with ID: " + ITEM_ID); | |
return null; | |
} | |
} | |
} | |
private static JCheckBox[] itemCheckbox; | |
private static ImageIcon[] itemImages; | |
private static JLabel[] itemProfits; | |
public class AlchGui extends JFrame { | |
private static final long serialVersionUID = 1L; | |
final JButton startButton = new JButton("Start"); | |
final JLabel instructionLabel = new JLabel("Please check all items you wish to alch", JLabel.CENTER); | |
final JCheckBox antiBanCheckBox = new JCheckBox("Use Antiban"); | |
final int height = 32; | |
public AlchGui() { | |
super("The Best Alcher"); | |
addWindowListener(new WindowAdapter() { | |
public void windowClosing(WindowEvent e) { | |
dispose(); | |
} | |
}); | |
setLocationRelativeTo(getOwner()); | |
draw(); | |
startButton.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
for (int i = itemCheckbox.length - 1; i >= 0; i--) { | |
if (!itemCheckbox[i].isSelected()) goodItems.remove(i); | |
} | |
antiBan = antiBanCheckBox.isSelected(); | |
startScript(); | |
dispose(); | |
} | |
}); | |
} | |
private void draw() { | |
if (runPaint) return; | |
getContentPane().removeAll(); | |
JPanel bottumPane = new JPanel(); | |
bottumPane.setLayout(new GridLayout(1, 2)); | |
bottumPane.setPreferredSize(new Dimension(240, height)); | |
bottumPane.add(antiBanCheckBox); | |
bottumPane.add(startButton); | |
JPanel bottumPane2 = new JPanel(); | |
bottumPane2.setLayout(new GridLayout(1, 1)); | |
bottumPane2.setPreferredSize(new Dimension(240, height)); | |
bottumPane2.add(instructionLabel); | |
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); | |
JPanel leftPane = new JPanel(); | |
leftPane.setLayout(new GridLayout(itemCheckbox.length, 1)); | |
leftPane.setPreferredSize(new Dimension(60, height * (itemCheckbox.length))); | |
for (ImageIcon curImage : itemImages) { | |
leftPane.add(new JLabel(curImage)); | |
} | |
JPanel rightPane = new JPanel(); | |
rightPane.setLayout(new GridLayout(itemCheckbox.length, 1)); | |
rightPane.setPreferredSize(new Dimension(140, height * (itemCheckbox.length))); | |
for (JCheckBox curCheckBox : itemCheckbox) { | |
rightPane.add(curCheckBox); | |
} | |
JPanel profitPane = new JPanel(); | |
profitPane.setLayout(new GridLayout(itemProfits.length, 1)); | |
profitPane.setPreferredSize(new Dimension(40, height * (itemProfits.length))); | |
for (JLabel curItemProfits : itemProfits) { | |
profitPane.add(curItemProfits == null ? new JLabel() : curItemProfits); | |
} | |
JPanel centerPane = new JPanel(); | |
centerPane.setLayout(new BoxLayout(centerPane, BoxLayout.X_AXIS)); | |
centerPane.add(leftPane); | |
centerPane.add(rightPane); | |
centerPane.add(profitPane); | |
getContentPane().add(centerPane); | |
getContentPane().add(bottumPane2); | |
getContentPane().add(bottumPane); | |
pack(); | |
setVisible(true); | |
} | |
} | |
private static boolean ACTIVE = false; | |
public class ErrorMessageGUI extends JFrame { | |
private static final long serialVersionUID = 1L; | |
public ErrorMessageGUI(Exception e) { | |
super("Error Message"); | |
addWindowListener(new WindowAdapter() { | |
public void windowClosing(WindowEvent e) { | |
ACTIVE = false; | |
dispose(); | |
} | |
}); | |
setLocationRelativeTo(getOwner()); | |
JTextArea textField = new JTextArea(); | |
textField.append("Current Item: " | |
+ (goodItems.isEmpty() ? "No More Items" : goodItems.get(0).getName())); | |
textField.append("\nError Message \n"); | |
textField.append(e.getMessage() + "\n"); | |
final StackTraceElement[] elements = e.getStackTrace(); | |
for(int i = 0; i < 5 && i < elements.length; i++) { | |
textField.append(elements[i].toString() + "\n"); | |
} | |
add(textField); | |
pack(); | |
setVisible(true); | |
ACTIVE = true; | |
} | |
} | |
/** | |
* Looks up grand exchange information and returns a string array with the following contents | |
* <br>String[0] = item name | |
* <br>String[1] = item price | |
* <br>String[2] = item Alch Price | |
* | |
* @param itemID for the item being looked up on the grand exchange | |
* @param secondRun used for recursive search of noted item ids | |
* @return : a string array of grand exchange information on the item id provided | |
*/ | |
private static String[] lookup(final int itemID, final boolean secondRun) { | |
String[] info = {"0", "0", "0"}; | |
try { | |
final URL url = new URL("http://www.tip.it/runescape/index.php?gec&itemid=" + itemID); | |
final BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); | |
String input; | |
while ((input = br.readLine()) != null) { | |
if (input.startsWith(" </table><p>Invalid item number")) { | |
return secondRun ? info : lookup(itemID - 1, true); | |
} else if (input.startsWith("<h2>")) { | |
info[0] = input.substring(4, input.length() - 5); | |
} else if (input.startsWith("<tr><td colspan=\"4\"><b>Current Market Price: </b>")) { | |
info[1] = input.substring(49, input.lastIndexOf("gp")).replaceAll(",", ""); | |
return info; | |
} else if (input.contains("View Item DB")) { | |
input = input.substring(input.indexOf("id=") + 3, input.indexOf(" title") - 1); | |
info[2] = lookupAlchPrice(input); | |
} | |
} | |
} catch (final Exception ignored) { | |
} | |
return info; | |
} | |
/** | |
* Looks up grand exchange information and returns a string array with the following contents | |
* | |
* @param tipItID the tip it id for the item to find the alch price for | |
* @return : the alch price of the item if not found returns Error | |
*/ | |
private static String lookupAlchPrice(final String tipItID) { | |
try { | |
final URL url = new URL("http://www.tip.it/runescape/?rs2item_id=" + tipItID); | |
final BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); | |
String input; | |
while ((input = br.readLine()) != null) { | |
if (input.equals(" <td class=\"header\" width=\"25%\">High Alchemy</td>")) { | |
br.readLine(); | |
input = br.readLine().replaceAll("\t", ""); | |
return input.substring(0, input.indexOf(" gp")); | |
} | |
} | |
} catch (final Exception ignored) { | |
} | |
return "Error"; | |
} | |
@Override | |
public void mouseClicked(MouseEvent e) { | |
if (REC.contains(e.getPoint())) { | |
antiBan ^= true; | |
} | |
} | |
@Override | |
public void mouseEntered(MouseEvent arg0) { | |
} | |
@Override | |
public void mouseExited(MouseEvent arg0) { | |
} | |
@Override | |
public void mousePressed(MouseEvent arg0) { | |
} | |
@Override | |
public void mouseReleased(MouseEvent arg0) { | |
} | |
@Override | |
public void messageReceived(MessageEvent e) { | |
if (e.getId() == 0 && e.getMessage().equals("You do not have enough nature runes to cast this spell.")) { | |
System.out.println("You are out of nature runes"); | |
stop(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment