/* Copyright 2012 Jan Ove Saltvedt This file is part of KBot. KBot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KBot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KBot. If not, see <http://www.gnu.org/licenses/>. */ package com.kbotpro.scriptsystem.fetch; import com.kbotpro.scriptsystem.various.KTimer; import com.kbotpro.scriptsystem.various.ModuleConnector; import com.kbotpro.scriptsystem.input.jobs.MouseHoverJob; import com.kbotpro.scriptsystem.input.jobs.MouseJob; import com.kbotpro.scriptsystem.input.callbacks.MouseMoveListener; import com.kbotpro.scriptsystem.interfaces.MouseTarget; import com.kbotpro.bot.BotEnvironment; import com.kbotpro.hooks.MenuElementNode; import com.kbotpro.hooks.NodeList; import com.kbotpro.hooks.Node; import java.awt.*; import java.util.*; import java.util.List; import java.util.regex.Pattern; /** * Class that takes care of the interaction with the menu system of Runescape. */ public class Menu extends ModuleConnector { private static final Pattern pattern = Pattern.compile("\\<.+?\\>"); public Menu(BotEnvironment botEnv) { super(botEnv); } /** * Gets the menu's top left position on screen. * @return Point */ public Point getPosition(){ int menuX = getClient().getMenuX(); int menuY = getClient().getMenuY(); return new Point(menuX, menuY); } /** * Gets the on screen boundings of the menu. * @return Rectangle */ public Rectangle getBounds(){ Point pos = getPosition(); // Client::menuHeight is not precise! int height = (getItemCount())*16+22; return new Rectangle(pos.x, pos.y, getClient().getMenuWidth(), height); } /** * Gets the current menu actions. * @return String[] array contains formatted menu strings. */ public String[] getMenuItemsFormatted(){ List<String> actions = new ArrayList<String>(); NodeList menuNodeList = getClient().getMenuNodeList(); if(menuNodeList == null){ return new String[0]; } Node headNode = menuNodeList.getHeadNode(); if(headNode == null || headNode.getNextNode() == null){ return new String[0]; } Node currentNode = headNode.getNextNode(); while(currentNode != null && currentNode != headNode){ if(currentNode instanceof MenuElementNode){ MenuElementNode menuElementNode = (MenuElementNode) currentNode; actions.add(menuElementNode.getAction()+" "+menuElementNode.getOption()); } currentNode = currentNode.getNextNode(); } if(actions.size() == 0){ return new String[0]; } else{ return actions.toArray(new String[1]); } } /** * Gets the current menu actions. * @return String[] array of menu items. */ public String[] getMenuItems(){ String[] in = getMenuItemsFormatted(); String[] out = new String[in.length]; for(int i = 0; i < in.length; i++){ out[i] = removeFormatting(in[i]); } return out; } /** * Checks if the menu is currently opened * @return */ public boolean isOpen(){ return getClient().isMenuOpen(); } /** * Gets the current menu item count. * @return */ public int getItemCount(){ return getClient().getMenuOptionsCount(); } /** * Removes the runescape string formatting from the parsed string * @param in * @return */ public String removeFormatting(String in) { if(in == null) return "null"; return pattern.matcher(in).replaceAll(""); } /** * Does a click on the menu item with the given index. * * @param index the index of the menu item * @return false if it dis not succeed. (Normally if the menu is not open) */ public boolean atMenuItem(int index){ if(!isOpen()){ return false; } Rectangle menuBounds = getBounds(); final Rectangle boxBounds = new Rectangle(menuBounds.x+4, menuBounds.y+20+index*16, menuBounds.width-8, 15); final int targetX = random(boxBounds.x+3, boxBounds.x+boxBounds.width-6); final int targetY = random(boxBounds.y+3, boxBounds.y+boxBounds.height-6); MouseTarget target = new MouseTarget() { public Point get() { return new Point(targetX, targetY); } public boolean isOver(int posX, int posY) { return boxBounds.contains(posX, posY); } }; final boolean[] succeeded = new boolean[]{false}; MouseHoverJob mouseHoverJob = botEnv.mouse.createMouseHoverJob(new MouseMoveListener() { int count = 0; public void onMouseOverTarget(MouseJob mouseJob) { count++; if(count > random(6, 50)){ MouseHoverJob job = (MouseHoverJob) mouseJob; job.doMouseClick(true); succeeded[0] = true; job.stop(); } } public void onFinished(MouseJob mouseJob) { } }, target, new KTimer(3000)); mouseHoverJob.start(); mouseHoverJob.join(); sleep(3); return succeeded[0]; } /** * @param actionContains case insensitive string that specifies the action to be performed, * @return succeeded */ public boolean atMenu(String actionContains){ int idx = getMenuIndex(actionContains); for(int iterator = 0; iterator < 16; iterator++){ if(idx != -1){ break; } idx = getMenuIndex(actionContains); sleep(random(10, 30)); } if(idx == -1){ while(isOpen()){ botEnv.mouse.moveMouseRandomly(750); try { Thread.sleep(random(100,500)); } catch (InterruptedException ie) { } if(idx != -1) atMenuItem(idx); } return false; // Could not find item } if(!isOpen()){ if(idx == 0){ // First menu item is the same as top text so a left click will do. botEnv.mouse.clickMouse(true); return true; } else{ // Open menu botEnv.mouse.clickMouse(false); sleep(40, 70); } } for(int i = 0; i < 20; i++){ if(isOpen()) break; sleep(random(10, 20)); } if(!isOpen()) return false; // Could not open menu idx = getMenuIndex(actionContains); return idx != -1 && atMenuItem(idx); } /** * Searches from top... * * @param actionContains * @return -1 if not found */ public int getMenuIndex(String actionContains) { try { actionContains = actionContains.toLowerCase(); String[] actions = getMenuItems(); for (int i = 0; i < actions.length; i++) { if (actions[i].toLowerCase().contains(actionContains)) { return i; } } return -1; } catch (NullPointerException ignored) { return -1; } } /** * @param actionContains - Actions name to check the menu for. * @return - Whether the menu contains the specified String. */ public boolean contains(String actionContains) { for (String s : getMenuItems()) { if (s.toLowerCase().contains(actionContains.toLowerCase())) { return true; } } return false; } }