- Joined
- Feb 15, 2016
- Messages
- 64
- Thread Author
- #1
Hey guys.
I've recently got into developing for RuneMate and read the beginner tutorials for it. I've had a fair bit of experience with Java before so most of it isn't that hard. The hard part is learning the API and working out how to code bots from there. The first bot I decided to make was a basic cow killer that kills cows in Burthorpe and banks their hides at Burthorpe bank. It is a very simple bot and isn't even that great in functionality but I want to try and improve it. The GUI is also basic but does its' job. I'm looking for any feedback from the developer community for improvements I can make or any general tips in relation to bot coding.
There's probably several bugs relating to this bot but I'll be testing it and improving it as I go but I wanted to get it up here first so that I can potentially get some feedback. My test account had already gotten banned from using this bot for a few hours which isn't surprising. I want to implement some antiban methods but I gotta figure out how to go about that first. I'll update this thread with more updated versions when I get to it. I'm trying to get this bot on the store as an open source bot as I will continually improve it until I am happy with it and then I will hopefully try and fulfill any bot requests that seem viable for a beginner bot developer. Anyways, here's my first bot. Be as harsh as you want, doesn't bother me as long as it's useful feedback. Thanks!
CowKiller.java
CowKiller.xml
CowKillerGui.java
CowKillerController.java
TaskKill.java
TaskBank.java
TaskPickup.java
TaskWalk.java
TaskDropJunk.java
TaskAntiban.java
TaskBuryBones.java
Updates
v1.0
- Created a simple antiban task
- Changed the traversal method from WebPath to RegionPath
- Minor changes
v1.0.1
- Added a new location: Lumbridge
- Added ability to bury bones
- Added a startup GUI (for multiple locations and bones support)
- Added more null checks (I think all are covered)
- Minor improvements
- Added to bot store!
What's Next?
- Refactor code to make the actions performed smoother
- Make the antiban better
- Improved GUI: better looking, ability to hide/show it, skill tracking in another tab of the GUI
- Change GUI from AWT to JFX
- Make player open gate at Lumbridge murder pen if the loot/cow is on the other side
Known Bugs
- Probably
I've recently got into developing for RuneMate and read the beginner tutorials for it. I've had a fair bit of experience with Java before so most of it isn't that hard. The hard part is learning the API and working out how to code bots from there. The first bot I decided to make was a basic cow killer that kills cows in Burthorpe and banks their hides at Burthorpe bank. It is a very simple bot and isn't even that great in functionality but I want to try and improve it. The GUI is also basic but does its' job. I'm looking for any feedback from the developer community for improvements I can make or any general tips in relation to bot coding.
There's probably several bugs relating to this bot but I'll be testing it and improving it as I go but I wanted to get it up here first so that I can potentially get some feedback. My test account had already gotten banned from using this bot for a few hours which isn't surprising. I want to implement some antiban methods but I gotta figure out how to go about that first. I'll update this thread with more updated versions when I get to it. I'm trying to get this bot on the store as an open source bot as I will continually improve it until I am happy with it and then I will hopefully try and fulfill any bot requests that seem viable for a beginner bot developer. Anyways, here's my first bot. Be as harsh as you want, doesn't bother me as long as it's useful feedback. Thanks!
CowKiller.java
JavaScript:
package com.smitty260.cowkiller;
import java.awt.Color;
import java.awt.Graphics2D;
import javafx.application.Platform;
import com.runemate.game.api.client.paint.PaintListener;
import com.runemate.game.api.hybrid.location.Area;
import com.runemate.game.api.hybrid.location.Coordinate;
import com.runemate.game.api.hybrid.net.GrandExchange;
import com.runemate.game.api.hybrid.util.StopWatch;
import com.runemate.game.api.script.framework.task.TaskScript;
import com.smitty260.cowkiller.tasks.TaskAntiban;
import com.smitty260.cowkiller.tasks.TaskBank;
import com.smitty260.cowkiller.tasks.TaskBuryBones;
import com.smitty260.cowkiller.tasks.TaskDropJunk;
import com.smitty260.cowkiller.tasks.TaskKill;
import com.smitty260.cowkiller.tasks.TaskPickup;
import com.smitty260.cowkiller.tasks.TaskWalk;
public class CowKiller extends TaskScript implements PaintListener {
public static Area bankArea;
public static Area cowArea;
public static Coordinate toBank;
public static Coordinate toCows;
public static String location = null;
public static boolean buryBones = false;
public static String status = "Loading..";
public static int kills = 0;
public static int cowhides = 0;
private int hidePrice = 0;
private final StopWatch runtime = new StopWatch();
@Override
public void onStart(String... args) {
Platform.runLater(() -> new CowKillerGui(this));
hidePrice = GrandExchange.lookup(1739).getPrice();
getEventDispatcher().addListener(this);
setLoopDelay(350, 1000);
}
public void initialise() {
if (location == "Burthorpe") {
bankArea = new Area.Rectangular(new Coordinate(2892, 3533), new Coordinate(2885, 3540));
cowArea = new Area.Rectangular(new Coordinate(2881, 3492), new Coordinate(2889, 3482));
toBank = new Coordinate(2889,3536);
toCows = new Coordinate(2884, 3487);
} else if (location == "Lumbridge") {
bankArea = new Area.Rectangular(new Coordinate(3215, 3261), new Coordinate(3211, 3253));
cowArea = new Area.Polygonal(new Coordinate(3265, 3255), new Coordinate(3251, 3255)
, new Coordinate(3251, 3272), new Coordinate(3241, 3284), new Coordinate(3241, 3298)
, new Coordinate(3267, 3298), new Coordinate(3264, 3289));
toBank = new Coordinate(3213, 3257);
toCows = new Coordinate(3258, 3266);
}
add(new TaskAntiban(), new TaskBuryBones(), new TaskPickup(), new TaskKill(),
new TaskDropJunk(), new TaskBank(), new TaskWalk());
TaskAntiban.antiban.start();
runtime.start();
}
@Override
public void onPause() {
runtime.stop();
}
@Override
public void onResume() {
runtime.start();
}
@Override
public void onPaint(Graphics2D g) {
Color transWhite = new Color(255, 255, 255, 150);
Color black = Color.BLACK;
g.setColor(transWhite);
g.fillRect(0, 0, 190, 200);
g.setColor(black);
g.drawString("Smitty260's Cow Killer v1.0.1", 10, 20);
g.drawString("Running: " + runtime.getRuntimeAsString(), 10, 40);
g.drawString("Status: " + status, 10, 60);
g.drawString("Cows Murdered: " + kills, 10, 80);
g.drawString("Cows Murdered p/hr: " + (int)(kills * 3600000D / runtime.getRuntime()), 10, 100);
g.drawString("Corpse Hides Looted: " + cowhides, 10, 120);
g.drawString("Corpse Hides Looted p/hr: " + (int)(cowhides * 3600000D / runtime.getRuntime()), 10, 140);
g.drawString("Profit: " + cowhides * hidePrice, 10, 160);
g.drawString("Profit p/hr: " + (int)(cowhides * hidePrice * 3600000D / runtime.getRuntime()), 10, 180);
}
}
CowKiller.xml
Code:
<manifest>
<main-class>com.smitty260.cowkiller.CowKiller</main-class>
<name>Cow Killer</name>
<tag-line>Kills cows and banks hides.</tag-line><!--Max of 50 chars-->
<description>A basic cow killer for Burthorpe and Lumbridge cows. Banks hides at a bank.</description><!--Max of 110 chars-->
<version>1.0.1</version>
<compatibility>
<game>RS3</game>
</compatibility>
<categories>
<category>COMBAT</category>
</categories>
<!--Required to publish on the bot store-->
<internal-id>CowKiller</internal-id>
<!--The rest are optional-->
<open-source>true</open-source>
<hidden>false</hidden> <!--If you need to hide it from the bot store for maintenance-->
<access>public</access>
<tags>
<tag>Cow</tag>
<tag>Kill</tag>
</tags>
<resources>
<resource>/com/smitty260/cowkiller/CowKiller.fxml</resource>
</resources>
</manifest>
CowKillerGui.java
JavaScript:
package com.smitty260.cowkiller;
import java.io.IOException;
import com.runemate.game.api.hybrid.util.Resources;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CowKillerGui extends Stage {
public CowKillerGui (CowKiller script) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setController(new CowKillerController(script, this));
final Parent root = loader.load(Resources.getAsStream("/com/smitty260/cowkiller/CowKiller.fxml"));
final Scene scene = new Scene(root);
setTitle("Cow Killer");
setScene(scene);
} catch (IOException e) {
e.printStackTrace();
}
show();
}
}
CowKillerController.java
JavaScript:
package com.smitty260.cowkiller;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
public class CowKillerController implements Initializable {
@FXML
private Button btnStart;
@FXML
private ComboBox<String> cmbLocation;
@FXML
private CheckBox chkBones;
@FXML
public void initialize(URL location, ResourceBundle resources) {
cmbLocation.getItems().addAll("Burthorpe", "Lumbridge");
cmbLocation.getSelectionModel().selectFirst();
btnStart.setOnAction(getbtnStartAction());
}
private final Stage stage;
private final CowKiller script;
public CowKillerController(CowKiller script, Stage stage) {
this.script = script;
this.stage = stage;
}
private EventHandler<ActionEvent> getbtnStartAction() {
return event -> {
CowKiller.buryBones = chkBones.isSelected();
CowKiller.location = cmbLocation.getSelectionModel().getSelectedItem();
script.initialise();
stage.hide();
};
}
}
TaskKill.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.entities.GroundItem;
import com.runemate.game.api.hybrid.entities.Npc;
import com.runemate.game.api.hybrid.local.Camera;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.region.GroundItems;
import com.runemate.game.api.hybrid.region.Npcs;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.hybrid.util.Filter;
import com.runemate.game.api.rs3.local.hud.interfaces.LootInventory;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskKill extends Task {
@Override
public void execute() {
Npc cow = Npcs.newQuery().names("Cow").actions("Attack").filter(new Filter<Npc>(){
@Override
public boolean accepts(Npc npc) {
return npc.getAnimationId() != 23566;
}
}).results().nearest();
CowKiller.status = "Murdering..";
if (cow != null) {
if (!cow.isVisible()) {
Camera.turnTo(cow);
}
}
if (cow != null){
if (cow.interact("Attack", "Cow")) {
Execution.delayUntil(()->!cow.isValid(), 15000);
CowKiller.kills += 1;
}
}
}
@Override
public boolean validate() {
return Players.getLocal().getAnimationId() == -1 && !Inventory.isFull()
&& !Npcs.newQuery().names("Cow").actions("Attack").results().isEmpty()
&& (GroundItems.newQuery().names("Cowhide").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).visible().results().isEmpty()
|| GroundItems.newQuery().names("Bones").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).visible().results().isEmpty() && CowKiller.buryBones)
&& CowKiller.cowArea.contains(Players.getLocal().getPosition())
&& Inventory.newQuery().names("Raw beef").results().isEmpty()
&& Inventory.newQuery().names("Bones").results().isEmpty()
&& !Players.getLocal().isMoving() && !LootInventory.isOpen();
}
}
TaskBank.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.entities.GameObject;
import com.runemate.game.api.hybrid.entities.Npc;
import com.runemate.game.api.hybrid.local.Camera;
import com.runemate.game.api.hybrid.local.hud.interfaces.Bank;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.region.GameObjects;
import com.runemate.game.api.hybrid.region.Npcs;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskBank extends Task {
@Override
public void execute() {
CowKiller.status = "Banking..";
if (Bank.isOpen()) {
if (Bank.depositInventory()) {
Execution.delay(1000, 2000);
Bank.close();
}
} else {
if (CowKiller.location == "Burthorpe") {
Npc bank = Npcs.getLoaded("Gnome Banker").nearest();
if (bank != null) {
if (!bank.isVisible()) {
Camera.turnTo(bank);
}
bank.interact("Bank", "Gnome Banker");
}
} else if (CowKiller.location == "Lumbridge") {
GameObject bank = GameObjects.getLoaded("Bank chest").nearest();
if (bank != null) {
if (!bank.isVisible()) {
Camera.turnTo(bank);
}
bank.interact("Use", "Bank chest");
}
}
Execution.delayUntil(()->Bank.isOpen(), 3000);
}
}
@Override
public boolean validate() {
return Inventory.isFull() && CowKiller.bankArea.contains(Players.getLocal().getPosition());
}
}
TaskPickup.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.entities.GroundItem;
import com.runemate.game.api.hybrid.local.Camera;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.region.GroundItems;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.hybrid.util.Filter;
import com.runemate.game.api.rs3.local.hud.interfaces.LootInventory;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskPickup extends Task {
@Override
public void execute() {
CowKiller.status = "Looting corpse..";
GroundItem item;
String itemName;
if (CowKiller.buryBones) {
item = GroundItems.newQuery().names("Cowhide", "Bones").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).results().nearest();
} else {
item = GroundItems.newQuery().names("Cowhide").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).results().nearest();
}
itemName = item.getDefinition().getName();
if (item != null) {
if (!item.isVisible()) {
Camera.turnTo(item);
}
}
if (LootInventory.isOpen()) {
if (CowKiller.buryBones) {
if (!LootInventory.newQuery().names("Cowhide", "Bones").results().isEmpty()) {
if (!LootInventory.newQuery().names("Cowhide").results().isEmpty()) {
if (LootInventory.take("Cowhide")) {
CowKiller.cowhides += 1;
}
}
if (!LootInventory.newQuery().names("Bones").results().isEmpty()) {
LootInventory.take("Bones");
}
if (LootInventory.newQuery().names("Cowhide", "Bones").results().isEmpty()) {
LootInventory.close();
}
} else {
LootInventory.close();
}
} else {
if (!LootInventory.newQuery().names("Cowhide").results().isEmpty()) {
if (LootInventory.take("Cowhide")) {
CowKiller.cowhides += 1;
}
if (LootInventory.newQuery().names("Cowhide").results().isEmpty()) {
LootInventory.close();
}
} else {
LootInventory.close();
}
}
}
if (item != null) {
if (item.interact("Take", itemName)) {
Execution.delayUntil(()->!item.isValid(), 5000);
if (item != null && !item.isValid() && itemName.matches("Cowhide")) {
CowKiller.cowhides += 1;
}
}
}
}
@Override
public boolean validate() {
return Players.getLocal().getTarget() == null
&& (!GroundItems.newQuery().names("Cowhide").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).visible().results().isEmpty()
|| !GroundItems.newQuery().names("Bones").filter(new Filter<GroundItem>() {
@Override
public boolean accepts(GroundItem item) {
return CowKiller.cowArea.contains(item.getPosition());
}
}).visible().results().isEmpty() && CowKiller.buryBones)
&& !Inventory.isFull()
&& CowKiller.cowArea.contains(Players.getLocal().getPosition())
&& !Players.getLocal().isMoving();
}
}
TaskWalk.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.entities.GameObject;
import com.runemate.game.api.hybrid.local.Camera;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.location.Coordinate;
import com.runemate.game.api.hybrid.location.navigation.Traversal;
import com.runemate.game.api.hybrid.location.navigation.web.WebPath;
import com.runemate.game.api.hybrid.region.GameObjects;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.hybrid.util.Filter;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskWalk extends Task {
private boolean needToBank;
private boolean needToReturn;
private WebPath walkToBank;
private WebPath walkToCows;
@Override
public void execute() {
if (needToBank) {
walkToBank = Traversal.getDefaultWeb().getPathBuilder().buildTo(CowKiller.toBank);
if (walkToBank != null) {
CowKiller.status = "Walking to bank..";
walkToBank.step(true);
}
} else if (needToReturn) {
walkToCows = Traversal.getDefaultWeb().getPathBuilder().buildTo(CowKiller.toCows);
if (walkToCows != null) {
CowKiller.status = "Walking to murder pen..";
if (openGate()) {
return;
}
walkToCows.step(true);
}
}
}
private boolean openGate() {
GameObject gate = GameObjects.newQuery().names("Gate").within(CowKiller.cowArea).filter(new Filter<GameObject>() {
@Override
public boolean accepts(GameObject gate) {
return gate != null && gate.getDefinition().getActions().contains("Open")
&& Players.getLocal().getPosition().distanceTo(gate) < 10;
}
}).results().nearest();
if (gate == null)
return false;
if (!gate.isVisible())
Camera.turnTo(gate);
return gate.interact("Open", "Gate");
}
@Override
public boolean validate() {
Coordinate pos = Players.getLocal().getPosition();
needToBank = Inventory.isFull() && !CowKiller.bankArea.contains(pos);
needToReturn = !Inventory.isFull() && !CowKiller.cowArea.contains(pos);
return needToBank || needToReturn;
}
}
TaskDropJunk.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.local.hud.interfaces.SpriteItem;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskDropJunk extends Task {
@Override
public void execute() {
SpriteItem junk;
CowKiller.status = "Dropping crap..";
if (!Inventory.newQuery().names("Raw beef").results().isEmpty()) {
junk = Inventory.newQuery().names("Raw beef").results().first();
if (junk != null)
junk.interact("Drop", "Raw beef");
} else {
junk = Inventory.newQuery().names("Bones").results().first();
if (junk != null)
junk.interact("Drop", "Bones");
}
Execution.delayUntil(()->!junk.isValid(), 2000);
}
@Override
public boolean validate() {
return !Inventory.newQuery().names("Raw beef").results().isEmpty() ||
!Inventory.newQuery().names("Bones").results().isEmpty()&&
Players.getLocal().getTarget() == null;
}
}
TaskAntiban.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.entities.GroundItem;
import com.runemate.game.api.hybrid.input.Mouse;
import com.runemate.game.api.hybrid.local.Camera;
import com.runemate.game.api.hybrid.local.hud.interfaces.InterfaceWindows;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.local.hud.interfaces.SpriteItem;
import com.runemate.game.api.hybrid.region.GroundItems;
import com.runemate.game.api.hybrid.util.StopWatch;
import com.runemate.game.api.hybrid.util.calculations.Random;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskAntiban extends Task {
public static StopWatch antiban = new StopWatch();
int interval = 0;
public TaskAntiban() {
interval = Random.nextInt(30, 60);
}
@Override
public void execute() {
CowKiller.status = "Antiban.. ing..";
Execution.delay(1000, 3000);
int action = Random.nextInt(1, 5);
switch (action) {
case 1:
int yaw = Random.nextInt(0, 360);
double pitch = Random.nextDouble(0.1, 0.6);
Camera.turnTo(yaw, pitch);
Execution.delay(1000, 3000);
break;
case 2:
int item = Random.nextInt(1, 3);
if (item == 1) {
SpriteItem randItem = Inventory.newQuery().results().random();
if (randItem != null) {
Mouse.move(randItem);
}
}
Execution.delay(1000, 3000);
if (item == 2) {
GroundItem randItem = GroundItems.newQuery().results().random();
if (randItem != null) {
Mouse.move(randItem);
}
}
Execution.delay(1000, 3000);
break;
case 3:
InterfaceWindows.getSkills().open();
Execution.delay(1000, 3000);
break;
case 4:
InterfaceWindows.getMagic().open();
Execution.delay(1000, 3000);
break;
}
if (!InterfaceWindows.getInventory().isOpen()) {
InterfaceWindows.getInventory().open();
}
antiban.reset();
interval = Random.nextInt(30, 60);
}
@Override
public boolean validate() {
return (interval * 1000) <= (int)antiban.getRuntime();
}
}
TaskBuryBones.java
JavaScript:
package com.smitty260.cowkiller.tasks;
import com.runemate.game.api.hybrid.local.hud.interfaces.Inventory;
import com.runemate.game.api.hybrid.local.hud.interfaces.SpriteItem;
import com.runemate.game.api.hybrid.region.Players;
import com.runemate.game.api.script.Execution;
import com.runemate.game.api.script.framework.task.Task;
import com.smitty260.cowkiller.CowKiller;
public class TaskBuryBones extends Task {
@Override
public void execute() {
SpriteItem bones = Inventory.newQuery().names("Bones").results().random();
CowKiller.status = "Burying corpse..";
if (bones != null) {
if (bones.interact("Bury", "Bones"))
Execution.delayUntil(()->!bones.isValid(), 3000);
}
}
@Override
public boolean validate() {
return Players.getLocal().getTarget() == null && CowKiller.buryBones
&& !Inventory.newQuery().names("Bones").results().isEmpty()
&& !Players.getLocal().isMoving();
}
}
Updates
v1.0
- Created a simple antiban task
- Changed the traversal method from WebPath to RegionPath
- Minor changes
v1.0.1
- Added a new location: Lumbridge
- Added ability to bury bones
- Added a startup GUI (for multiple locations and bones support)
- Added more null checks (I think all are covered)
- Minor improvements
- Added to bot store!
What's Next?
- Refactor code to make the actions performed smoother
- Make the antiban better
- Improved GUI: better looking, ability to hide/show it, skill tracking in another tab of the GUI
- Change GUI from AWT to JFX
- Make player open gate at Lumbridge murder pen if the loot/cow is on the other side
Known Bugs
- Probably
Last edited: