/*
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.wrappers;
import com.kbotpro.bot.BotEnvironment;
import com.kbotpro.scriptsystem.interfaces.Targetable;
import com.kbotpro.scriptsystem.interfaces.MouseTarget;
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.various.KTimer;
import java.awt.*;
/**
* IComponent is very much the same as a Component in awt in Java.
* It can be a window or just a part of a window.
*
* Usually many IComponenents is grouped together to create a Runescape interface.
*/
public class IComponent extends Wrapper implements Targetable{
private com.kbotpro.hooks.IComponent rIComponent;
private IComponent parrent;
private Interface parrentInterface;
/**
* Constructor for IComponent
* @param botEnv botEnvironment to connect to the rest of the bot
* @param rIComponent Reflection wrapper for IComponent
* @param parrent the parrent IComponent or null if no parrent.
*/
public IComponent(BotEnvironment botEnv, com.kbotpro.hooks.IComponent rIComponent, IComponent parrent) {
super(botEnv);
this.rIComponent = rIComponent;
this.parrent = parrent;
}
/**
* Gets the ID of the contained element.
* This can for example be the ID of a bank item or the object ID of a spinning object.
* @return Returns the ID of the contained element.
*/
public int getElementID(){
return rIComponent.getElementID();
}
/**
* This gets the contained elements stack size.
* This can for example be the stack size of the a bank item.
* @return Return an integer containing the stack size of the contained element.
*/
public int getElementStackSize(){
return rIComponent.getElementStackSize();
}
/**
* This gets the contained elements name.
* For example the name of an item in the bank
* @return Returns a String containing the name or null if this IComponent does not have a contained element name
*/
public String getElementName(){
return rIComponent.getElementName();
}
/**
* Checks if the element is visible, for example a bank item is visible on your current tab.
* @return Return true if is visible.
*/
public boolean isElementVisible() {
return !rIComponent.isElementVisible();
}
/**
* Gets the upper left screen position of the interface.
* @return Return Point containing screen position.
*/
public Point getUpperLeftScreenPos(){
int x = rIComponent.getX();
int y = rIComponent.getY();
int indexArray = rIComponent.getVisibleArrayIndex();
if(indexArray != -1){
Rectangle[] bounds = getClient().getInterfaceBounds();
if(bounds != null && bounds[indexArray] != null){
return new Point(x+bounds[indexArray].x, y+bounds[indexArray].y);
}
}
if(parrent != null){
Point point = parrent.getUpperLeftScreenPos();
point.x += x;
point.y += y;
return point;
}
return new Point(x, y);
}
/**
* Gets the screen boundings of the interface.
* @return Returns a Rectangle containing screen position and bounds.
*/
public Rectangle getBounds(){
int x = rIComponent.getX();
int y = rIComponent.getY();
x += rIComponent.getMasterX();
y += rIComponent.getMasterY();
/*
Bad solution
int indexArray = rIComponent.getVisibleArrayIndex();
if(indexArray != -1){
Rectangle[] bounds = getClient().getInterfaceBounds();
if(bounds != null && bounds[indexArray] != null){
return new Rectangle(x+bounds[indexArray].x, y+bounds[indexArray].y, getWidth(), getHeight());
}
}
if(parrent != null){
Point point = parrent.getUpperLeftScreenPos();
return new Rectangle(point.x+x, point.y+y, getWidth(), getHeight());
}*/
return new Rectangle(x, y, getWidth(), getHeight());
}
/**
* Basically the same as bounds but a bit smaller so it does not include the edges.
* @return
*/
public Rectangle getClickableArea(){
Rectangle bounds = getBounds();
return new Rectangle(bounds.x+3, bounds.y+3, bounds.width-6, bounds.height-6);
}
/**
* Gets the width of the IComponent.
* @return Returns an integer with the width of the IComponent
*/
public int getWidth(){
return rIComponent.getWidth();
}
/**
* Gets the height of the IComponent
* @return Returns an integer with the height of the IComponent
*/
public int getHeight(){
return rIComponent.getHeight();
}
/**
* Returns the screen posituon of the center of the IComponent.
* @return Returns a Point containing the center of the IComponent
*/
public Point getScreenPos(){
Rectangle bounds = getBounds();
return new Point((int)bounds.getCenterX(), (int)bounds.getCenterY());
}
/**
* Gets the menu actions for the IComponent
* @return Returns a String array or null if no actions.
*/
public String[] getActions(){
return rIComponent.getActions();
}
/**
* Gets the text of the IComponent
* @return Returns a String or null if no text.
*/
public String getText(){
return rIComponent.getText();
}
/**
* Gets the texture ID of the IComponent
* @return int containtaining the texture ID.
*/
public int getTextureID(){
return rIComponent.getTextureID();
}
/**
* Gets the text color of the IComponent
* @return int.
*/
public int getTextColor(){
return rIComponent.getTextColor();
}
/**
* Gets the children of the IComponent
* @return Return an array of IComponents or an empty array if no children
*/
public IComponent[] getChildren(){
com.kbotpro.hooks.IComponent[] rChildren = rIComponent.getChildren();
if(rChildren == null){
return new IComponent[0];
}
IComponent[] out = new IComponent[rChildren.length];
for(int i = 0; i < rChildren.length; i++){
if(rChildren[i] == null){
continue;
}
out[i] = new IComponent(botEnv, rChildren[i], this);
out[i].parrentInterface = parrentInterface;
}
return out;
}
/**
* Checks if the IComponents has child components.
* @return
*/
public boolean hasChildren(){
com.kbotpro.hooks.IComponent[] rChildren = rIComponent.getChildren();
return rChildren != null && rChildren.length != 0;
}
/**
* Gets the parrent interface for the IComponent
* @return
*/
public Interface getParrentInterface() {
return parrentInterface;
}
/**
* Used internally by the bot to update the parrent interface
* @param parrentInterface
*/
void setParrentInterface(Interface parrentInterface) {
this.parrentInterface = parrentInterface;
}
/**
* Indicates whether some other object is "equal to" this one.
* <p/>
* The <code>equals</code> method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
* <code>x</code>, <code>x.equals(x)</code> should return
* <code>true</code>.
* <li>It is <i>symmetric</i>: for any non-null reference values
* <code>x</code> and <code>y</code>, <code>x.equals(y)</code>
* should return <code>true</code> if and only if
* <code>y.equals(x)</code> returns <code>true</code>.
* <li>It is <i>transitive</i>: for any non-null reference values
* <code>x</code>, <code>y</code>, and <code>z</code>, if
* <code>x.equals(y)</code> returns <code>true</code> and
* <code>y.equals(z)</code> returns <code>true</code>, then
* <code>x.equals(z)</code> should return <code>true</code>.
* <li>It is <i>consistent</i>: for any non-null reference values
* <code>x</code> and <code>y</code>, multiple invocations of
* <tt>x.equals(y)</tt> consistently return <code>true</code>
* or consistently return <code>false</code>, provided no
* information used in <code>equals</code> comparisons on the
* objects is modified.
* <li>For any non-null reference value <code>x</code>,
* <code>x.equals(null)</code> should return <code>false</code>.
* </ul>
* <p/>
* The <tt>equals</tt> method for class <code>Object</code> implements
* the most discriminating possible equivalence relation on objects;
* that is, for any non-null reference values <code>x</code> and
* <code>y</code>, this method returns <code>true</code> if and only
* if <code>x</code> and <code>y</code> refer to the same object
* (<code>x == y</code> has the value <code>true</code>).
* <p/>
* Note that it is generally necessary to override the <tt>hashCode</tt>
* method whenever this method is overridden, so as to maintain the
* general contract for the <tt>hashCode</tt> method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> if this object is the same as the obj
* argument; <code>false</code> otherwise.
* @see #hashCode()
* @see java.util.Hashtable
*/
@Override
public boolean equals(Object obj) {
if(obj == null || !(obj instanceof IComponent))
return super.equals(obj);
IComponent iComponent = (IComponent) obj;
return iComponent.rIComponent == rIComponent;
}
/**
* Checks if the interface is valid.
* @return
*/
public boolean isValid(){
return parrentInterface!= null && parrentInterface.isValid();
}
/**
* Checks if the interface is visible.
* NOTE: This method is not working perfectly at the moment. It broke in #569 and returns true if the interface has been viewed once.
* @return
*/
public boolean isVisible(){
/* int index = rIComponent.getVisibleArrayIndex();
if(index != -1){
return true;//getClient().getVisibleIComponents()[index]; // Somethign weird happened to this field after the 569 update
}
else if(parrent != null){
return parrent.isValid();
} */
return botEnv.client.getLoopCycle() < rIComponent.getLoopCycleStatus()+20;
}
/**
* Gets its position relative to the parrents position
* @return
*/
public int getRelativeX() {
return rIComponent.getX();
}
/**
* Gets its position relative to the parrents position
* @return
*/
public int getRelativeY() {
return rIComponent.getY();
}
/**
* Gets the model ID
* @return
*/
public int getModelID(){
return rIComponent.getModelID();
}
/**
* Gets the model type
* @return
*/
public int getModelType(){
return rIComponent.getModelType();
}
/**
* Gets the model zoom
* @return
*/
public int getModelZoom(){
return rIComponent.getModelZoom();
}
/**
* Gets the center of the screen area of this object
* @return
*/
public Point getCenter() {
Rectangle bounds = getBounds();
return new Point((int)bounds.getCenterX(), (int)bounds.getCenterY());
}
/**
* Internal method used for debugging. This should not be used by scripters
* @return
*/
public int getInternalVisibleArrayIndex() {
return rIComponent.getVisibleArrayIndex();
}
/**
* Get target
*
* @return
*/
public MouseTarget getTarget() {
return new MouseTarget() {
Rectangle bounds = getClickableArea();
Point p = new Point(random(bounds.x+3, bounds.x+bounds.width-6), random(bounds.y+3, bounds.y+bounds.height-6));
public Point get() {
return p;
}
public boolean isOver(int posX, int posY) {
return bounds.contains(posX, posY);
}
};
}
/**
* Moves the mouse to the object and clicks at the given action
* NOTE: Do not use this method while you have a mouse job active!
* @param actionContains A string that the action contains. Case ignored
* @return Boolean, true if succeeded, false if not.
*/
public boolean doAction(final String actionContains) {
if (getBounds().contains(botEnv.mouse.getMousePos())) {
return botEnv.menu.atMenu(actionContains);
}
final boolean[] ret = new boolean[]{false};
MouseHoverJob mouseHoverJob = botEnv.mouse.createMouseHoverJob(new MouseMoveListener() {
private int count = 0;
public void onMouseOverTarget(MouseJob mouseJob) {
MouseHoverJob mouseHoverJob = (MouseHoverJob) mouseJob;
count++;
if (count > random(5, 100)) {
mouseHoverJob.stop();
ret[0] = botEnv.menu.atMenu(actionContains);
}
}
public void onFinished(MouseJob mouseJob) {
}
}, this, new KTimer(5000));
mouseHoverJob.start();
mouseHoverJob.join();
return ret[0];
}
public boolean doClick() {
if (getBounds().contains(botEnv.mouse.getMousePos())) {
botEnv.mouse.clickMouse(true);
return true;
}
final boolean[] ret = new boolean[]{false};
MouseHoverJob mouseHoverJob = botEnv.mouse.createMouseHoverJob(new MouseMoveListener() {
private int count = 0;
public void onMouseOverTarget(MouseJob mouseJob) {
MouseHoverJob mouseHoverJob = (MouseHoverJob) mouseJob;
count++;
if (count > random(5, 100)) {
mouseHoverJob.doMouseClick(true);
ret[0] = true;
mouseHoverJob.stop();
}
}
public void onFinished(MouseJob mouseJob) {
}
}, this, new KTimer(5000));
mouseHoverJob.start();
mouseHoverJob.join();
return ret[0];
}
/**
* Gets the x axis screen pos
* @return
*/
public int getAbsoluteX() {
return rIComponent.getX()+rIComponent.getMasterX();
}
/**
* Gets the y axis screen pos
* @return
*/
public int getAbsoluteY() {
return rIComponent.getY()+rIComponent.getMasterY();
}
public Object getClientObject() {
return rIComponent;
}
/**
* Returns a random point inside of the component
* @return
*/
public java.awt.Point getRandomPointInside() {
return new Point(
random(getAbsoluteX(), getAbsoluteX()+(int)getBounds().getX()),
random(getAbsoluteY(), getAbsoluteY()+(int)getBounds().getY())
);
}
/**
* textContainsIgnoreCase(String)
* @return boolean
*/
public boolean textContainsIgnoreCase(String s) {
String p = this.getText();
return (p.toLowerCase().contains(s.toLowerCase()));
}
}