Repository reorganisation
Unified kotlin and java into just src/main Unified the rs09 and core packages Took all content out of the core package, and placed it into the new content package Reorganised all source code relating to content to be easier to find and explore
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,578 @@
|
||||
package core.game.bots;
|
||||
|
||||
import core.game.node.entity.impl.PulseType;
|
||||
import core.ServerConstants;
|
||||
import core.game.container.impl.EquipmentContainer;
|
||||
import core.game.interaction.DestinationFlag;
|
||||
import core.game.interaction.MovementPulse;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.PlayerDetails;
|
||||
import core.game.node.entity.player.link.appearance.Gender;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.map.Direction;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.RegionManager;
|
||||
import core.game.world.map.path.Pathfinder;
|
||||
import core.game.world.map.zone.impl.WildernessZone;
|
||||
import core.game.world.repository.Repository;
|
||||
import core.net.packet.context.MessageContext;
|
||||
import core.tools.RandomFunction;
|
||||
import core.tools.StringUtils;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import content.region.misc.tutisland.handlers.iface.CharacterDesign;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents an <b>A</b>rtificial <b>I</b>ntelligent <b>P</b>layer.
|
||||
*
|
||||
* @author Emperor
|
||||
*/
|
||||
public class AIPlayer extends Player {
|
||||
|
||||
/**
|
||||
* The current UID.
|
||||
*/
|
||||
private static int currentUID = 0x1;
|
||||
|
||||
private static final List<String> botNames = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* The active Artificial intelligent players mapping.
|
||||
*/
|
||||
private static final Map<Integer, AIPlayer> botMapping = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A line of data from namesandarmor.txt that will be used to generate the appearance
|
||||
* Data in format:
|
||||
* //name:cblevel:helmet:cape:neck:weapon:chest:shield:unknown:legs:unknown:gloves:boots:
|
||||
*/
|
||||
private static String OSRScopyLine;
|
||||
|
||||
/**
|
||||
* The AIP's UID.
|
||||
*/
|
||||
private final int uid;
|
||||
|
||||
/**
|
||||
* The start location of the AIP.
|
||||
*/
|
||||
private final Location startLocation;
|
||||
|
||||
/**
|
||||
* The username.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The player controlling this AIP.
|
||||
*/
|
||||
private Player controler;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new {@code AIPlayer} {@code Object}.
|
||||
*
|
||||
* @param l The location.
|
||||
*/
|
||||
static {
|
||||
loadNames("botnames.txt");
|
||||
}
|
||||
|
||||
public AIPlayer(Location l) {
|
||||
this(getRandomName(), l, null);
|
||||
}
|
||||
|
||||
public AIPlayer(String fileName, Location l) {
|
||||
this(retrieveRandomName(fileName), l, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private AIPlayer(String name, Location l, String ignored) {
|
||||
super(new PlayerDetails("/aip" + (currentUID + 1) + ":" + name));
|
||||
super.setLocation(startLocation = l);
|
||||
super.artificial = true;
|
||||
super.getDetails().setSession(ArtificialSession.getSingleton());
|
||||
Repository.getPlayers().add(this);
|
||||
this.username = StringUtils.formatDisplayName(name + (currentUID + 1));
|
||||
this.uid = currentUID++;
|
||||
this.updateRandomValues();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates bot stats/equipment/etc based on OSRScopyLine
|
||||
*/
|
||||
public void updateRandomValues() {
|
||||
this.getAppearance().setGender(RandomFunction.random(5) == 1 ? Gender.FEMALE : Gender.MALE);
|
||||
int setTo = RandomFunction.random(0,10);
|
||||
CharacterDesign.randomize(this,true);
|
||||
this.setDirection(Direction.values()[new Random().nextInt(Direction.values().length)]); //Random facing dir
|
||||
this.getSkills().updateCombatLevel();
|
||||
this.getAppearance().sync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
return;
|
||||
}
|
||||
|
||||
private void setLevels() {
|
||||
//Create realistic player stats
|
||||
int maxLevel = RandomFunction.random(1, Math.min(parseOSRS(1), 99));
|
||||
for (int i = 0; i < Skills.NUM_SKILLS; i++) {
|
||||
this.getSkills().setStaticLevel(i, RandomFunction.linearDecreaseRand(maxLevel));
|
||||
}
|
||||
int combatLevelsLeft = parseOSRS(1);
|
||||
int hitpoints = Math.max(RandomFunction.random(10, Math.min(maxLevel, combatLevelsLeft * 4)), 10);
|
||||
combatLevelsLeft -= 0.25 * hitpoints;
|
||||
int prayer = combatLevelsLeft > 0 ? RandomFunction.random(Math.min(maxLevel, combatLevelsLeft * 8)) : 1;
|
||||
combatLevelsLeft -= 0.125 * prayer;
|
||||
int defence = combatLevelsLeft > 0 ? RandomFunction.random(Math.min(maxLevel, combatLevelsLeft * 4)) : 1;
|
||||
combatLevelsLeft -= 0.25 * defence;
|
||||
|
||||
combatLevelsLeft = Math.min(combatLevelsLeft, 199);
|
||||
|
||||
int attack = combatLevelsLeft > 0 ? RandomFunction.normalRandDist(Math.min(maxLevel, combatLevelsLeft * 3)) : 1;
|
||||
int strength = combatLevelsLeft > 0 ? combatLevelsLeft * 3 - attack : 1;
|
||||
|
||||
this.getSkills().setStaticLevel(Skills.HITPOINTS, hitpoints);
|
||||
this.getSkills().setStaticLevel(Skills.PRAYER, prayer);
|
||||
this.getSkills().setStaticLevel(Skills.DEFENCE, defence);
|
||||
this.getSkills().setStaticLevel(Skills.ATTACK, attack);
|
||||
this.getSkills().setStaticLevel(Skills.STRENGTH, strength);
|
||||
this.getSkills().setStaticLevel(Skills.RANGE, combatLevelsLeft / 2);
|
||||
this.getSkills().setStaticLevel(Skills.MAGIC, combatLevelsLeft / 2);
|
||||
}
|
||||
|
||||
private void giveArmor() {
|
||||
//name:cblevel:helmet2:cape3:neck4:weapon5:chest6:shield7:unknown8:legs9:unknown10:gloves11:boots12:
|
||||
//sicriona:103:1163: 1023: 1725 :1333: 1127 :1201 :0: 1079 :0: 2922: 1061:0:
|
||||
equipIfExists(new Item(parseOSRS(2)), EquipmentContainer.SLOT_HAT);
|
||||
equipIfExists(new Item(parseOSRS(3)), EquipmentContainer.SLOT_CAPE);
|
||||
equipIfExists(new Item(parseOSRS(4)), EquipmentContainer.SLOT_AMULET);
|
||||
equipIfExists(new Item(parseOSRS(5)), EquipmentContainer.SLOT_WEAPON);
|
||||
equipIfExists(new Item(parseOSRS(6)), EquipmentContainer.SLOT_CHEST);
|
||||
equipIfExists(new Item(parseOSRS(7)), EquipmentContainer.SLOT_SHIELD);
|
||||
equipIfExists(new Item(parseOSRS(9)), EquipmentContainer.SLOT_LEGS);
|
||||
equipIfExists(new Item(parseOSRS(11)), EquipmentContainer.SLOT_HANDS);
|
||||
equipIfExists(new Item(parseOSRS(12)), EquipmentContainer.SLOT_FEET);
|
||||
}
|
||||
|
||||
private int parseOSRS(int index) {
|
||||
return Integer.parseInt(OSRScopyLine.split(":")[index]);
|
||||
}
|
||||
|
||||
private void equipIfExists(Item e, int slot) {
|
||||
if (e == null || e.getName().equalsIgnoreCase("null")) {
|
||||
return;
|
||||
}
|
||||
if (e.getId() != 0)
|
||||
getEquipment().replace(e, slot);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of bot names into memory
|
||||
*/
|
||||
public static void loadNames(String fileName){
|
||||
try {
|
||||
Scanner sc = new Scanner(new File(ServerConstants.BOT_DATA_PATH + fileName));
|
||||
while (sc.hasNextLine()) {
|
||||
botNames.add(sc.nextLine());
|
||||
}
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRandomName(){
|
||||
int index = (RandomFunction.random(botNames.size()));
|
||||
String name = botNames.get(index);
|
||||
botNames.remove(index);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bot content
|
||||
*/
|
||||
public static void updateRandomOSRScopyLine(String fileName) {
|
||||
Random rand = new Random();
|
||||
int n = 0;
|
||||
try {
|
||||
for (Scanner sc = new Scanner(new File(ServerConstants.BOT_DATA_PATH + fileName)); sc.hasNext(); ) {
|
||||
++n;
|
||||
String line = sc.nextLine();
|
||||
if (rand.nextInt(n) == 0) { //Chance of overwriting line is lower and lower
|
||||
if (line.length() < 3 || line.startsWith("#")) //probably an empty line
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OSRScopyLine = line;
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("Missing " + fileName);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String retrieveRandomName(String fileName) {
|
||||
do {
|
||||
updateRandomOSRScopyLine(fileName);
|
||||
} while (OSRScopyLine.startsWith("#") || OSRScopyLine.contains("_") || OSRScopyLine.contains(" ")); //Comment
|
||||
return OSRScopyLine.split(":")[0];
|
||||
}
|
||||
|
||||
private static String retrieveRandomName() {
|
||||
return retrieveRandomName("namesandarmor.txt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
getProperties().setSpawnLocation(startLocation);
|
||||
getInterfaceManager().openDefaultTabs();
|
||||
getSession().setObject(this);
|
||||
botMapping.put(uid, this);
|
||||
super.init();
|
||||
getSettings().setRunToggled(true);
|
||||
CharacterDesign.randomize(this, false);
|
||||
getPlayerFlags().setLastSceneGraph(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the following.
|
||||
*
|
||||
* @param e The entity to follow.
|
||||
*/
|
||||
public void follow(final Entity e) {
|
||||
getPulseManager().run(new MovementPulse(this, e, DestinationFlag.FOLLOW_ENTITY) {
|
||||
@Override
|
||||
public boolean pulse() {
|
||||
face(e);
|
||||
return false;
|
||||
}
|
||||
}, PulseType.STANDARD);
|
||||
}
|
||||
|
||||
public void randomWalkAroundPoint(Location point, int radius) {
|
||||
Pathfinder.find(this, point.transform(RandomFunction.random(radius, (radius * -1)), RandomFunction.random(radius, (radius * -1)), 0), true, Pathfinder.SMART).walk(this);
|
||||
}
|
||||
|
||||
public void randomWalk(int radiusX, int radiusY) {
|
||||
Pathfinder.find(this, this.getLocation().transform(RandomFunction.random(radiusX, (radiusX * -1)), RandomFunction.random(radiusY, (radiusY * -1)), 0), false, Pathfinder.SMART).walk(this);
|
||||
}
|
||||
|
||||
public void walkToPosSmart(int x, int y) {
|
||||
walkToPosSmart(new Location(x, y));
|
||||
}
|
||||
|
||||
public void walkToPosSmart(Location loc) {
|
||||
Pathfinder.find(this, loc, true, Pathfinder.SMART).walk(this);
|
||||
}
|
||||
|
||||
public void walkPos(int x, int y) {
|
||||
Pathfinder.find(this, new Location(x, y));
|
||||
}
|
||||
|
||||
public boolean checkVictimIsPlayer() {
|
||||
if (this.getProperties().getCombatPulse().getVictim() != null)
|
||||
if (this.getProperties().getCombatPulse().getVictim().isPlayer())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if(getSkullManager().isWilderness()) {
|
||||
getSkullManager().setLevel(WildernessZone.getWilderness(this));
|
||||
}
|
||||
if(getSkills().getLifepoints() <= 0){
|
||||
//deregister(this.uid);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Item getItemById(int id) {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
Item item = this.getInventory().get(i);
|
||||
if (item != null) {
|
||||
if (item.getId() == id)
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void handleIncomingChat(MessageContext ctx){
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<Node> getNodeInRange(int range, int entry) {
|
||||
int meX = this.getLocation().getX();
|
||||
int meY = this.getLocation().getY();
|
||||
ArrayList<Node> nodes = new ArrayList<Node>();
|
||||
for (NPC npc : RegionManager.getLocalNpcs(this, range)) {
|
||||
if (npc.getId() == entry)
|
||||
nodes.add(npc);
|
||||
}
|
||||
for (int x = 0; x < range; x++) {
|
||||
for (int y = 0; y < range - x; y++) {
|
||||
Node node = RegionManager.getObject(0, meX + x, meY + y);
|
||||
if (node != null)
|
||||
if (node.getId() == entry)
|
||||
nodes.add(node);
|
||||
Node node2 = RegionManager.getObject(0, meX + x, meY - y);
|
||||
if (node2 != null)
|
||||
if (node2.getId() == entry)
|
||||
nodes.add(node2);
|
||||
Node node3 = RegionManager.getObject(0, meX - x, meY + y);
|
||||
if (node3 != null)
|
||||
if (node3.getId() == entry)
|
||||
nodes.add(node3);
|
||||
Node node4 = RegionManager.getObject(0, meX - x, meY - y);
|
||||
if (node4 != null)
|
||||
if (node4.getId() == entry)
|
||||
nodes.add(node4);
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private ArrayList<Node> getNodeInRange(int range, List<Integer> entrys) {
|
||||
int meX = this.getLocation().getX();
|
||||
int meY = this.getLocation().getY();
|
||||
//int meX2 = this.getLocation().getX();
|
||||
//System.out.println("local " + meX + " real x? " + meX2 );
|
||||
ArrayList<Node> nodes = new ArrayList<Node>();
|
||||
for (NPC npc : RegionManager.getLocalNpcs(this, range)) {
|
||||
if (entrys.contains(npc.getId()))
|
||||
nodes.add(npc);
|
||||
}
|
||||
for (int x = 0; x < range; x++) {
|
||||
for (int y = 0; y < range - x; y++) {
|
||||
Node node = RegionManager.getObject(0, meX + x, meY + y);
|
||||
if (node != null)
|
||||
if (entrys.contains(node.getId()))
|
||||
nodes.add(node);
|
||||
Node node2 = RegionManager.getObject(0, meX + x, meY - y);
|
||||
if (node2 != null)
|
||||
if (entrys.contains(node2.getId()))
|
||||
nodes.add(node2);
|
||||
Node node3 = RegionManager.getObject(0, meX - x, meY + y);
|
||||
if (node3 != null)
|
||||
if (entrys.contains(node3.getId()))
|
||||
nodes.add(node3);
|
||||
Node node4 = RegionManager.getObject(0, meX - x, meY - y);
|
||||
if (node4 != null)
|
||||
if (entrys.contains(node4.getId()))
|
||||
nodes.add(node4);
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public Node getClosestNodeWithEntryAndDirection(int range, int entry, Direction direction) {
|
||||
ArrayList<Node> nodeList = getNodeInRange(range, entry);
|
||||
if (nodeList.isEmpty()) {
|
||||
//System.out.println("nodelist empty");
|
||||
return null;
|
||||
}
|
||||
Node node = getClosestNodeinNodeListWithDirection(nodeList, direction);
|
||||
return node;
|
||||
}
|
||||
|
||||
public Node getClosestNodeWithEntry(int range, int entry) {
|
||||
ArrayList<Node> nodeList = getNodeInRange(range, entry);
|
||||
if (nodeList.isEmpty()) {
|
||||
//System.out.println("nodelist empty");
|
||||
return null;
|
||||
}
|
||||
Node node = getClosestNodeinNodeList(nodeList);
|
||||
return node;
|
||||
}
|
||||
|
||||
public Node getClosestNodeWithEntry(int range, List<Integer> entrys) {
|
||||
ArrayList<Node> nodeList = getNodeInRange(range, entrys);
|
||||
if (nodeList.isEmpty()) {
|
||||
//System.out.println("nodelist empty");
|
||||
return null;
|
||||
}
|
||||
Node node = getClosestNodeinNodeList(nodeList);
|
||||
return node;
|
||||
}
|
||||
|
||||
public Node getClosesCreature(int radius) {
|
||||
int distance = radius + 1;
|
||||
Node npcReturn = null;
|
||||
for (NPC npc : RegionManager.getLocalNpcs(this, radius)) {
|
||||
double distanceToNpc = npc.getLocation().getDistance(this.getLocation());
|
||||
if ((distanceToNpc) < distance) {
|
||||
distance = (int) distanceToNpc;
|
||||
npcReturn = npc;
|
||||
}
|
||||
}
|
||||
return npcReturn;
|
||||
}
|
||||
|
||||
public Node getClosesCreature(int radius, int entry) {
|
||||
int distance = radius + 1;
|
||||
Node npcReturn = null;
|
||||
for (NPC npc : RegionManager.getLocalNpcs(this, radius)) {
|
||||
double distanceToNpc = npc.getLocation().getDistance(this.getLocation());
|
||||
if (npc.getId() == entry) {
|
||||
if ((distanceToNpc) < distance) {
|
||||
distance = (int) distanceToNpc;
|
||||
npcReturn = npc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return npcReturn;
|
||||
}
|
||||
|
||||
public Node getClosesCreature(int radius, ArrayList<Integer> entrys) {
|
||||
int distance = radius + 1;
|
||||
Node npcReturn = null;
|
||||
for (NPC npc : RegionManager.getLocalNpcs(this, radius)) {
|
||||
double distanceToNpc = npc.getLocation().getDistance(this.getLocation());
|
||||
if (entrys.contains(npc.getId())) {
|
||||
if ((distanceToNpc) < distance) {
|
||||
distance = (int) distanceToNpc;
|
||||
npcReturn = npc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return npcReturn;
|
||||
}
|
||||
|
||||
private Node getClosestNodeinNodeListWithDirection(ArrayList<Node> nodes, Direction direction) {
|
||||
if (nodes.isEmpty()) {
|
||||
//System.out.println("nodelist empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
double distance = 0;
|
||||
Node nodeReturn = null;
|
||||
for (Node node : nodes) {
|
||||
double nodeDistance = this.getLocation().getDistance(node.getLocation());
|
||||
if ((nodeReturn == null || nodeDistance < distance) && node.getDirection() == direction) {
|
||||
distance = nodeDistance;
|
||||
nodeReturn = node;
|
||||
}
|
||||
}
|
||||
return nodeReturn;
|
||||
}
|
||||
|
||||
private Node getClosestNodeinNodeList(ArrayList<Node> nodes) {
|
||||
if (nodes.isEmpty()) {
|
||||
//System.out.println("nodelist empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
double distance = 0;
|
||||
Node nodeReturn = null;
|
||||
for (Node node : nodes) {
|
||||
double nodeDistance = this.getLocation().getDistance(node.getLocation());
|
||||
if (nodeReturn == null || nodeDistance < distance) {
|
||||
distance = nodeDistance;
|
||||
nodeReturn = node;
|
||||
}
|
||||
}
|
||||
return nodeReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
botMapping.remove(uid);
|
||||
super.clear(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (getPlayerFlags().isUpdateSceneGraph()) {
|
||||
getPlayerFlags().setLastSceneGraph(getLocation());
|
||||
}
|
||||
super.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalizeDeath(Entity killer) {
|
||||
super.finalizeDeath(killer);
|
||||
fullRestore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UID.
|
||||
*
|
||||
* @return the UID.
|
||||
*/
|
||||
public int getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters an AIP.
|
||||
*
|
||||
* @param uid The player's UID.
|
||||
*/
|
||||
public static void deregister(int uid) {
|
||||
AIPlayer player = botMapping.get(uid);
|
||||
if (player != null) {
|
||||
player.clear();
|
||||
Repository.getPlayers().remove(player);
|
||||
return;
|
||||
}
|
||||
//SystemLogger.logErr(this.getClass(), "Could not deregister AIP#" + uid + ": UID not added to the mapping!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the AIP for the given UID.
|
||||
*
|
||||
* @param uid The UID.
|
||||
* @return The AIPlayer.
|
||||
*/
|
||||
public static AIPlayer get(int uid) {
|
||||
return botMapping.get(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the startLocation.
|
||||
*/
|
||||
public Location getStartLocation() {
|
||||
return startLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controler.
|
||||
*
|
||||
* @return The controler.
|
||||
*/
|
||||
public Player getControler() {
|
||||
return controler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controler.
|
||||
*
|
||||
* @param controler The controler to set.
|
||||
*/
|
||||
public void setControler(Player controler) {
|
||||
this.controler = controler;
|
||||
}
|
||||
|
||||
|
||||
public void interact(Node n) {
|
||||
// InteractionPacket.handleObjectInteraction(this, 0, n.getLocation(), n.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.ge.GrandExchangeOffer
|
||||
|
||||
/**
|
||||
* A repository for bots to make use of that can contain any info that might be useful to them.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class AIRepository {
|
||||
companion object {
|
||||
val groundItems = HashMap<Player,ArrayList<GroundItem>>()
|
||||
val GEOffers = HashMap<Player, GrandExchangeOffer>()
|
||||
|
||||
@JvmStatic
|
||||
val PulseRepository = HashMap<String, GeneralBotCreator.BotScriptPulse>() // Lowercase username, pulse
|
||||
|
||||
@JvmStatic
|
||||
fun addItem(item: GroundItem){
|
||||
if(groundItems[item.dropper] == null){
|
||||
val list = ArrayList<GroundItem>()
|
||||
list.add(item)
|
||||
groundItems[item.dropper] = list
|
||||
return
|
||||
}
|
||||
groundItems[item.dropper]!!.add(item)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getItems(player: Player): ArrayList<GroundItem>?{
|
||||
return groundItems[player]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun addOffer(player: Player, offer: GrandExchangeOffer){
|
||||
GEOffers[player] = offer
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getOffer(player: Player): GrandExchangeOffer? {
|
||||
return GEOffers[player]
|
||||
}
|
||||
|
||||
@JvmStatic fun clearAllBots() {
|
||||
PulseRepository.forEach { (_, it) ->
|
||||
it.stop();
|
||||
it.botScript.bot.clear();
|
||||
AIPlayer.deregister((it.botScript.bot as AIPlayer).uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package core.game.bots;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import core.net.IoSession;
|
||||
|
||||
/**
|
||||
* Represents an artificial networking session.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class ArtificialSession extends IoSession {
|
||||
|
||||
/**
|
||||
* The artificial session singleton.
|
||||
*/
|
||||
private static final ArtificialSession SINGLETON = new ArtificialSession();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ArtificialSession} {@code Object}.
|
||||
*/
|
||||
private ArtificialSession() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddress() {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object context, boolean instant) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queue(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton.
|
||||
*/
|
||||
public static ArtificialSession getSingleton() {
|
||||
return SINGLETON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.node.entity.player.link.appearance.Gender
|
||||
import core.game.node.entity.player.link.prayer.PrayerType
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Direction
|
||||
import core.game.world.map.Location
|
||||
import core.tools.RandomFunction
|
||||
import core.game.consumable.Consumable
|
||||
import content.data.consumables.Consumables
|
||||
import core.game.consumable.Food
|
||||
import content.data.consumables.effects.HealingEffect
|
||||
import core.game.node.entity.skill.Skills
|
||||
import java.util.*
|
||||
|
||||
class CombatBot(location: Location) : AIPlayer(location) {
|
||||
var tick = 0
|
||||
|
||||
override fun updateRandomValues() {
|
||||
appearance.gender = if (RandomFunction.random(5) == 1) Gender.FEMALE else Gender.MALE
|
||||
setDirection(Direction.values()[Random().nextInt(Direction.values().size)]) //Random facing dir
|
||||
skills.updateCombatLevel()
|
||||
appearance.sync()
|
||||
}
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
this.tick++
|
||||
|
||||
//Despawn
|
||||
if (skills.lifepoints == 0) {
|
||||
//CombatBotAssembler.produce(CombatBotAssembler.Type.RANGE, CombatBotAssembler.Tier.LOW,this.location)
|
||||
deregister(uid)
|
||||
}
|
||||
}
|
||||
|
||||
fun CheckPrayer(type: Array<PrayerType?>) {
|
||||
for (i in type.indices) prayer.toggle(type[i])
|
||||
}
|
||||
|
||||
fun eat(foodId: Int) {
|
||||
val foodItem = Item(foodId)
|
||||
if (skills.getStaticLevel(Skills.HITPOINTS) >= skills.lifepoints * 3 && inventory.containsItem(foodItem)) {
|
||||
this.lock(3)
|
||||
//this.animate(new Animation(829));
|
||||
val food = inventory.getItem(foodItem)
|
||||
var consumable: Consumable? = Consumables.getConsumableById(food.id)
|
||||
if (consumable == null) {
|
||||
consumable = Food(IntArray(food.id), HealingEffect(1))
|
||||
}
|
||||
consumable.consume(food, this)
|
||||
properties.combatPulse.delayNextAttack(3)
|
||||
}
|
||||
if (!checkVictimIsPlayer()) if (!inventory.contains(foodId, 1)) inventory.add(Item(foodId, 5)) //Add Food to Inventory
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Items
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Assembles combat-style bots, intended to replace PVMBotBuilder.
|
||||
* @author Ceikry
|
||||
*/
|
||||
class CombatBotAssembler {
|
||||
enum class Type{
|
||||
RANGE,
|
||||
MAGE,
|
||||
MELEE
|
||||
}
|
||||
enum class Tier{
|
||||
LOW,
|
||||
MED,
|
||||
HIGH,
|
||||
PURE
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a bot with the specified parameters.
|
||||
* @param type the type of bot to produce. Melee, Ranged, or Mage.
|
||||
* @param tier specifies the range of skills the bot can have, and thereby, the gear. LOW, MED, or HIGH.
|
||||
* @param location the location that the bot spawns and respawns at.
|
||||
* @return an AIPlayer with the specified values.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun produce(type: Type, tier: Tier, location: Location): AIPlayer? {
|
||||
return when (type) {
|
||||
Type.RANGE -> assembleRangedBot(tier, location)
|
||||
Type.MELEE -> assembleMeleeBot(tier,location)
|
||||
Type.MAGE -> assembleMeleeBot(tier,location)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a ranged bot with the specified values.
|
||||
* @param tier the tier to use which defines skill ranges and gear.
|
||||
* @param location the bot's spawn and respawn location
|
||||
* @param crossbow (optional) whether or not this bot should use a crossbow
|
||||
* @return a CombatBot with the specified values.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun assembleRangedBot(tier: Tier, location: Location, crossbow: Boolean? = null): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
|
||||
generateStats(bot, tier, Skills.RANGE, Skills.DEFENCE)
|
||||
gearRangedBot(bot, crossbow ?: Random().nextInt() % 2 == 0)
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a melee bot with the specified values.
|
||||
* @param tier the tier of the bot. Specifies levels and gear.
|
||||
* @param location the spawn and respawn location of the bot.
|
||||
* @return a CombatBot with the specified values.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun assembleMeleeBot(tier: Tier, location: Location): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
|
||||
generateStats(bot, tier, Skills.ATTACK, Skills.STRENGTH, Skills.DEFENCE)
|
||||
gearMeleeBot(bot)
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a melee bot with gear appropriate for killing dragons.
|
||||
* @param tier the tier of the bot, specifying levels and gear.
|
||||
* @param location the spawn and respawn location of the bot.
|
||||
* @return a CombatBot suited for killing dragons.
|
||||
* @author Kermit
|
||||
*/
|
||||
fun MeleeAdventurer(tier: Tier, location: Location): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
var max = 0
|
||||
val level = RandomFunction.random(25, 65).also {max = 99 }
|
||||
generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH, Skills.DEFENCE)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, level + 5)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, level + 5)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setLevel(Skills.ATTACK, level + 5)
|
||||
bot.skills.setLevel(Skills.STRENGTH, level + 5)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot, MELEE_HELMS)
|
||||
equipHighest(bot, MELEE_TOP)
|
||||
equipHighest(bot, MELEE_LEG)
|
||||
equipHighest(bot, MELEE_WEP)
|
||||
equipHighest(bot, MELEE_SHIELD)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NNECK)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NBOOTS)
|
||||
bot.equipment.refresh()
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a melee bot with gear appropriate for killing dragons.
|
||||
* @param tier the tier of the bot, specifying levels and gear.
|
||||
* @param location the spawn and respawn location of the bot.
|
||||
* @return a CombatBot suited for killing dragons.
|
||||
* @author Kermit
|
||||
*/
|
||||
fun RangeAdventurer(tier: Tier, location: Location): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
var max = 0
|
||||
val level = RandomFunction.random(35, 65).also {max = 75 }
|
||||
generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setStaticLevel(Skills.RANGE, level + 10)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setLevel(Skills.RANGE, level + 10)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot,RANGE_HELMS,65)
|
||||
equipHighest(bot,RANGE_TOPS,65)
|
||||
equipHighest(bot,RANGE_LEGS,65)
|
||||
equipHighest(bot,CROSSBOWS,50)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NRANGENECK)
|
||||
equipHighest(bot, NRANGESHIELD)
|
||||
equipHighest(bot, PCRANGE_BACK)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NRBOOTS)
|
||||
bot.equipment.add(Item(Items.BRONZE_BOLTS_877,100000),13,false,false)
|
||||
bot.equipment.refresh()
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a melee bot with gear appropriate for killing dragons.
|
||||
* @param tier the tier of the bot, specifying levels and gear.
|
||||
* @param location the spawn and respawn location of the bot.
|
||||
* @return a CombatBot suited for killing dragons.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun assembleMeleeDragonBot(tier: Tier, location: Location): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH, Skills.DEFENCE)
|
||||
equipHighest(bot, MELEE_HELMS, 50)
|
||||
equipHighest(bot, MELEE_TOP, 40)
|
||||
equipHighest(bot, MELEE_LEG, 40)
|
||||
equipHighest(bot, MELEE_WEP, 60)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NBOOTS)
|
||||
bot.equipment.refresh()
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a ranged bot with gear appropriate for killing dragons.
|
||||
* @param tier the tier of the bot, specifying levels and gear.
|
||||
* @param location the spawn and respawn location of the bot.
|
||||
* @return a CombatBot suited for killing dragons.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun assembleRangeDragonBot(tier: Tier, location: Location): CombatBot {
|
||||
val bot = CombatBot(location)
|
||||
bot.fullRestore()
|
||||
generateStats(bot,tier,Skills.RANGE,Skills.DEFENCE)
|
||||
equipHighest(bot,RANGE_HELMS,50)
|
||||
equipHighest(bot,RANGE_TOPS,50)
|
||||
equipHighest(bot,RANGE_LEGS,50)
|
||||
equipHighest(bot,CROSSBOWS,50)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NRBOOTS)
|
||||
bot.equipment.add(Item(Items.BRONZE_BOLTS_877,100000),13,false,false)
|
||||
bot.equipment.refresh()
|
||||
return bot
|
||||
}
|
||||
|
||||
/**
|
||||
* Gears a ranged bot with the best gear for its stats.
|
||||
* @param bot the bot to gear
|
||||
* @param crossbow (optional) whether or not this bot should use a crossbow. Default is false.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun gearRangedBot(bot: AIPlayer, crossbow: Boolean? = false) {
|
||||
equipHighest(bot, RANGE_HELMS)
|
||||
equipHighest(bot, RANGE_TOPS)
|
||||
equipHighest(bot, RANGE_LEGS)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NRBOOTS)
|
||||
if(crossbow == true) { equipHighest(bot,CROSSBOWS); equipHighest(bot,MELEE_SHIELD); bot.equipment.add(Item(Items.BRONZE_BOLTS_877,Integer.MAX_VALUE),13,false,false) }
|
||||
else {equipHighest(bot, BOWS); bot.equipment.add(Item(Items.BRONZE_ARROW_882,Integer.MAX_VALUE),13,false,false) }
|
||||
bot.equipment.refresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gears a melee bot with the best gear for its stats.
|
||||
* @param bot the bot to gear.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun gearMeleeBot(bot: AIPlayer){
|
||||
equipHighest(bot, MELEE_HELMS)
|
||||
equipHighest(bot, MELEE_LEG)
|
||||
equipHighest(bot, MELEE_SHIELD)
|
||||
equipHighest(bot, MELEE_TOP)
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NGLOVES)
|
||||
equipHighest(bot, NBOOTS)
|
||||
equipHighest(bot, MELEE_WEP)
|
||||
bot.equipment.refresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gears a Novice Pest control bot.
|
||||
* @author Sir Kermit & Ceikry
|
||||
*/
|
||||
fun gearPCnRangedBot(bot: AIPlayer, crossbow: Boolean? = false, vararg skills: Int) {
|
||||
var max = 0
|
||||
val level = RandomFunction.random(30, 70).also {max = 75 }
|
||||
bot.fullRestore()
|
||||
|
||||
bot.skills.setStaticLevel(Skills.RANGE, 50)
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, 50)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, level)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setLevel(Skills.RANGE, 50)
|
||||
bot.skills.setLevel(Skills.DEFENCE, 50)
|
||||
bot.skills.setLevel(Skills.ATTACK, level)
|
||||
bot.skills.setLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot, PCRANGE_HELMS)
|
||||
equipHighest(bot, PCRANGE_TOPS)
|
||||
equipHighest(bot, PCRANGE_BACK)
|
||||
equipHighest(bot, PCRANGE_LEGS)
|
||||
bot.equipment.refresh()
|
||||
equipHighest(bot, NECK)
|
||||
equipHighest(bot, GLOVES)
|
||||
equipHighest(bot, BOOTS)
|
||||
equipHighest(bot, RING_ARCH)
|
||||
bot.equipment.refresh()
|
||||
if(crossbow == true) { equipHighest(bot,PCCROSSBOWS); equipHighest(bot,MELEE_SHIELD); bot.equipment.add(Item(Items.BRONZE_BOLTS_877,Integer.MAX_VALUE),13,false,false) }
|
||||
else {equipHighest(bot, PCBOWS); bot.equipment.add(Item(Items.BRONZE_ARROW_882,Integer.MAX_VALUE),13,false,false) }
|
||||
bot.skills.setStaticLevel(Skills.RANGE, 99)
|
||||
bot.skills.setLevel(Skills.RANGE, 99)
|
||||
bot.equipment.refresh()
|
||||
}
|
||||
|
||||
fun gearPCnMeleeBot(bot: AIPlayer, vararg skills: Int){
|
||||
var max = 0
|
||||
val initial = RandomFunction.random(30, 75).also {max = 75 }
|
||||
var level = initial
|
||||
bot.fullRestore()
|
||||
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, level)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setStaticLevel(Skills.PRAYER, 70)
|
||||
bot.skills.setStaticLevel(Skills.RANGE, 10)
|
||||
bot.skills.setStaticLevel(Skills.MAGIC, 10)
|
||||
bot.skills.setLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setLevel(Skills.ATTACK, level)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setLevel(Skills.PRAYER, 70)
|
||||
bot.skills.setLevel(Skills.RANGE, 10)
|
||||
bot.skills.setLevel(Skills.MAGIC, 10)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot, PCMELEE_HELMS)
|
||||
equipHighest(bot, PCMELEE_LEG)
|
||||
equipHighest(bot, PCMELEE_SHIELD)
|
||||
equipHighest(bot, PCMELEE_TOP)
|
||||
equipHighest(bot, PCMELEE_WEP)
|
||||
bot.equipment.refresh()
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NECK)
|
||||
equipHighest(bot, GLOVES)
|
||||
equipHighest(bot, BOOTS)
|
||||
equipHighest(bot, RING_BERS)
|
||||
bot.equipment.refresh()
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, 70)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, 99)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, 99)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, 80)
|
||||
bot.skills.setLevel(Skills.DEFENCE, 70)
|
||||
bot.skills.setLevel(Skills.ATTACK, 99)
|
||||
bot.skills.setLevel(Skills.STRENGTH, 99)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, 80)
|
||||
bot.fullRestore()
|
||||
}
|
||||
/**
|
||||
* Gears a Intermediate Pest control bot.
|
||||
* @author Sir Kermit & Ceikry
|
||||
*/
|
||||
fun gearPCiRangedBot(bot: AIPlayer, crossbow: Boolean? = false, vararg skills: Int) {
|
||||
var max = 0
|
||||
val level = RandomFunction.random(50, 80).also {max=99 }
|
||||
bot.fullRestore()
|
||||
|
||||
bot.skills.setStaticLevel(Skills.RANGE, level)
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, 80)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, level)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, 70)
|
||||
bot.skills.setStaticLevel(Skills.SUMMONING, 99)
|
||||
bot.skills.setLevel(Skills.RANGE, level)
|
||||
bot.skills.setLevel(Skills.DEFENCE, 80)
|
||||
bot.skills.setLevel(Skills.ATTACK, level)
|
||||
bot.skills.setLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, 70)
|
||||
bot.skills.setLevel(Skills.SUMMONING, 99)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot, PCRANGE_HELMS)
|
||||
equipHighest(bot, PCRANGE_TOPS)
|
||||
equipHighest(bot, PCRANGE_BACK)
|
||||
equipHighest(bot, PCRANGE_LEGS)
|
||||
bot.equipment.refresh()
|
||||
equipHighest(bot, GLOVES)
|
||||
equipHighest(bot, NECK)
|
||||
equipHighest(bot, BOOTS)
|
||||
equipHighest(bot, RING_ARCH)
|
||||
bot.equipment.refresh()
|
||||
if(crossbow == true) { equipHighest(bot,PCCROSSBOWS); equipHighest(bot,MELEE_SHIELD); bot.equipment.add(Item(Items.BRONZE_BOLTS_877,Integer.MAX_VALUE),13,false,false) }
|
||||
else {equipHighest(bot, PCBOWS); bot.equipment.add(Item(Items.BRONZE_ARROW_882,Integer.MAX_VALUE),13,false,false) }
|
||||
bot.skills.setStaticLevel(Skills.RANGE, 99)
|
||||
bot.skills.setLevel(Skills.RANGE, 99)
|
||||
bot.equipment.refresh()
|
||||
}
|
||||
|
||||
fun gearPCiMeleeBot(bot: AIPlayer, vararg skills: Int){
|
||||
var max = 0
|
||||
val initial = RandomFunction.random(55, 95).also {max=95 }
|
||||
var level = initial
|
||||
bot.fullRestore()
|
||||
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, level)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setStaticLevel(Skills.PRAYER, 99)
|
||||
bot.skills.setStaticLevel(Skills.RANGE, level)
|
||||
bot.skills.setStaticLevel(Skills.MAGIC, level)
|
||||
bot.skills.setStaticLevel(Skills.SUMMONING, 99)
|
||||
bot.skills.setLevel(Skills.STRENGTH, level)
|
||||
bot.skills.setLevel(Skills.DEFENCE, level)
|
||||
bot.skills.setLevel(Skills.ATTACK, level)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, level)
|
||||
bot.skills.setLevel(Skills.PRAYER, 99)
|
||||
bot.skills.setLevel(Skills.RANGE, level)
|
||||
bot.skills.setLevel(Skills.MAGIC, level)
|
||||
bot.skills.setLevel(Skills.SUMMONING, 99)
|
||||
bot.skills.updateCombatLevel()
|
||||
equipHighest(bot, PCMELEE_HELMS)
|
||||
equipHighest(bot, PCMELEE_LEG)
|
||||
equipHighest(bot, PCMELEE_SHIELD)
|
||||
equipHighest(bot, PCMELEE_TOP)
|
||||
equipHighest(bot, PCMELEE_WEP)
|
||||
bot.equipment.refresh()
|
||||
equipHighest(bot, CAPE)
|
||||
equipHighest(bot, NECK)
|
||||
equipHighest(bot, GLOVES)
|
||||
equipHighest(bot, BOOTS)
|
||||
equipHighest(bot, RING_BERS)
|
||||
bot.equipment.refresh()
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE, 99)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK, 99)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH, 99)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS, 99)
|
||||
bot.skills.setLevel(Skills.DEFENCE, 99)
|
||||
bot.skills.setLevel(Skills.ATTACK, 99)
|
||||
bot.skills.setLevel(Skills.STRENGTH, 99)
|
||||
bot.skills.setLevel(Skills.HITPOINTS, 99)
|
||||
bot.fullRestore()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the stats for a bot with the given tier.
|
||||
* @param bot the bot to gear
|
||||
* @param tier the Tier of stats and gear. LOW, MED, HIGH, PURE.
|
||||
* @param skills the skills that we should generate.
|
||||
* @author Ceikry and Eli
|
||||
*/
|
||||
fun generateStats(bot: AIPlayer, tier: Tier, vararg skills: Int) {
|
||||
var totalXPAdd = 0.0
|
||||
var skillAmt = 0.0
|
||||
val variance = 0.5
|
||||
var max = 0
|
||||
val initial = when (tier) {
|
||||
Tier.LOW -> RandomFunction.random(33).also { max = 33 }
|
||||
Tier.MED -> RandomFunction.random(33, 66).also { max = 66 }
|
||||
Tier.HIGH -> RandomFunction.random(66, 99).also { max = 99 }
|
||||
Tier.PURE -> RandomFunction.random(90, 99).also {max = 99 }
|
||||
}
|
||||
for (skill in skills.indices) {
|
||||
val perc = RandomFunction.random(-variance,variance)
|
||||
var level = initial + (perc * 33).toInt()
|
||||
if(level < 1)
|
||||
level = 1
|
||||
if(level > max)
|
||||
level = max
|
||||
bot.skills.setLevel(skills[skill], level)
|
||||
bot.skills.setStaticLevel(skills[skill], level)
|
||||
totalXPAdd += bot.skills.getExperience(skills[skill])
|
||||
skillAmt++
|
||||
}
|
||||
when (tier){
|
||||
Tier.PURE -> {
|
||||
bot.skills.setStaticLevel(Skills.DEFENCE,10)
|
||||
bot.skills.setStaticLevel(Skills.STRENGTH,99)
|
||||
bot.skills.setStaticLevel(Skills.ATTACK,90)
|
||||
bot.skills.setStaticLevel(Skills.PRAYER,43)
|
||||
bot.skills.setStaticLevel(Skills.RANGE,1)
|
||||
bot.skills.setStaticLevel(Skills.MAGIC,1)
|
||||
}
|
||||
}
|
||||
|
||||
bot.skills.addExperience(Skills.HITPOINTS, (totalXPAdd / skillAmt) * 0.2)
|
||||
val new_hp = bot.skills.levelFromXP((totalXPAdd / skillAmt) * 0.2)
|
||||
bot.skills.setStaticLevel(Skills.HITPOINTS,10 + new_hp)
|
||||
bot.skills.updateCombatLevel()
|
||||
bot.fullRestore()
|
||||
}
|
||||
|
||||
/**
|
||||
* Equips the highest piece of gear from a set for the stats the bot has.
|
||||
* @param bot the bot to equip gear to.
|
||||
* @param set the IntArray set to check
|
||||
* @param levelcap (optional) the max level of gear to equip.
|
||||
* @author Ceikry
|
||||
*/
|
||||
private fun equipHighest(bot: AIPlayer, set: Array<Int>, levelcap: Int? = null) {
|
||||
val highestItems = ArrayList<Item>()
|
||||
var highest: Item? = null
|
||||
for (i in set.indices) {
|
||||
val item = Item(set[i])
|
||||
var canEquip = true
|
||||
(item.definition.handlers.getOrDefault("requirements",null) as HashMap<Int,Int>?)?.let { map ->
|
||||
levelcap?.let {levelcap ->
|
||||
map.map {
|
||||
if (bot.skills.getLevel(it.key) < it.value || it.value > levelcap)
|
||||
canEquip = false
|
||||
}
|
||||
} ?: map.map {
|
||||
if (bot.skills.getLevel(it.key) < it.value)
|
||||
canEquip = false
|
||||
}
|
||||
}
|
||||
if (canEquip) {
|
||||
if (highest == null) {
|
||||
highest = item
|
||||
highestItems.add(item)
|
||||
continue
|
||||
}
|
||||
if (item.lvlAvg() > highest.lvlAvg()) {
|
||||
highest = item
|
||||
highestItems.clear()
|
||||
highestItems.add(item)
|
||||
} else if(item.lvlAvg() == highest.lvlAvg()){
|
||||
highestItems.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
bot.equipment.add(highestItems.random(), highest!!.definition!!.handlers["equipment_slot"] as Int, false, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function for the Item class that gets the average of its level requirements.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun Item.lvlAvg(): Int {
|
||||
var total = 1
|
||||
var count = 1
|
||||
(definition.handlers.getOrDefault("requirements",null) as HashMap<Int,Int>?)?.let { map ->
|
||||
map.map {
|
||||
total += it.value
|
||||
count++
|
||||
}
|
||||
}
|
||||
return total / count
|
||||
}
|
||||
|
||||
|
||||
val RANGE_HELMS = arrayOf(1167,4732,3749)
|
||||
val RANGE_TOPS = arrayOf(1129,1131,1135,2499,2501,2503)
|
||||
val RANGE_LEGS = arrayOf(1095,1097,1099,2493,2495,2497)
|
||||
val BOWS = arrayOf(841,843,847,853)
|
||||
val CROSSBOWS = arrayOf(9185,9174,9177,9176,9179,9181,9183)
|
||||
|
||||
val PCRANGE_HELMS = arrayOf(1167,4732,3749,11718)
|
||||
val PCRANGE_TOPS = arrayOf(1129,1131,1135,2503,11720)
|
||||
val PCRANGE_LEGS = arrayOf(1095,1097,1099,2497,11722)
|
||||
val PCRANGE_BACK = arrayOf(1019,10498,10499)
|
||||
val PCBOWS = arrayOf(841,843,847,853)
|
||||
val PCCROSSBOWS = arrayOf(9185,9174,9177,9176,9179,9181,9183)
|
||||
|
||||
val MELEE_HELMS = arrayOf(1137,1139, 1141, 6621, 1143,1145,1147,1149,1151,1153, 6623, 1159,1163,1165,3748, 3751, 3753, 4716,4724, 4745, 4753)
|
||||
val MELEE_TOP = arrayOf(1101,1103,1105,1107,1109,1111,1113,2513,1115,1117,1119,1121,1123,1125,1127,4720,4728,4749,4749)
|
||||
val MELEE_LEG = arrayOf(1081,1083,1085,1087,1089,1091,1093,4759,1067,1069,1071,1073,1075,1077,1079,4722,4751,4722,4751)
|
||||
val MELEE_SHIELD = arrayOf(1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201)
|
||||
val MELEE_WEP = arrayOf(1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303,1305,1321,1323,1325,1327,1329,1331,1333,4587,4151,1363,1365,1367,1369,1371,1373,1375,1377)
|
||||
val NGLOVES = arrayOf(1059,2922,2912,2902,2932,2942,3799)
|
||||
val NBOOTS = arrayOf(4121,4123,4125,4127,4129,4131,1061,1837,2579,9005)
|
||||
val NRBOOTS = arrayOf(9006,626,628,630,632,634)
|
||||
val NNECK = arrayOf(1704,1725,1729,1731)
|
||||
val NRANGENECK = arrayOf(1478,1704)
|
||||
val NRANGESHIELD = arrayOf(1191,1193,1195,1197,1199,1201)
|
||||
|
||||
val PCMELEE_HELMS = arrayOf(1137,1139, 1141, 6621, 1143,1145,1147,1149,1151,1153, 6623, 1159,1163,1165,3748, 3751, 10828, 11335, 3753, 4716,4724, 4745, 4753, 3751)
|
||||
val PCMELEE_TOP = arrayOf(1101,1103,1105,1107,1109,1111,1113,2513,1115,1117,1119,1121,1123,1125,1127,4720,4728,4749,4749,11724,14479,2513)
|
||||
val PCMELEE_LEG = arrayOf(1081,1083,1085,1087,1089,1091,1093,4759,1067,1069,1071,1073,1075,1077,1079,4722,4751,4722,4751,11726,4087)
|
||||
val PCMELEE_SHIELD = arrayOf(1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,6524,13742,13740,13738,13736,13734)
|
||||
val PCMELEE_WEP = arrayOf(1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303,1305,1321,1323,1325,1327,1329,1331,1333,4587,4151,1363,1365,1367,1369,1371,1373,1375,1377,1434,5698)
|
||||
|
||||
val NECK = arrayOf(1704,6585)
|
||||
val CAPE = arrayOf(1019,1021,1023,6568,4315,4317,4319,4321,4323,4325,4327,4329,4331,4333,4335,4337,4339,4341,4343,4345,4347,4349,4351)
|
||||
val GLOVES = arrayOf(1059,7456,7457,7458,7459,7460,7461,7462)
|
||||
val BOOTS = arrayOf(1061,4131,11732,11728,4131)
|
||||
val RING_BERS = arrayOf(6737)
|
||||
val RING_ARCH = arrayOf(6733)
|
||||
|
||||
val RICH_MELEE_HELMS = arrayOf(2587,2595,2605,2613,2619,2627,2657,2665,2673,3486,1149,10828,4716,4724,4753)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.tools.RandomFunction
|
||||
import core.Server
|
||||
import content.global.bots.Idler
|
||||
|
||||
class GeneralBotCreator {
|
||||
//org/crandor/net/packet/in/InteractionPacket.java <<< This is a very useful class for learning to code bots
|
||||
constructor(botScript: Script, loc: Location?) {
|
||||
botScript.bot = AIPBuilder.create(loc)
|
||||
GameWorld.Pulser.submit(BotScriptPulse(botScript))
|
||||
}
|
||||
|
||||
constructor(botScript: Script, bot: AIPlayer?) {
|
||||
botScript.bot = bot
|
||||
GameWorld.Pulser.submit(BotScriptPulse(botScript).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it })
|
||||
}
|
||||
|
||||
constructor(botScript: Script, player: Player, isPlayer: Boolean){
|
||||
botScript.bot = player
|
||||
val pulse = BotScriptPulse(botScript,isPlayer)
|
||||
GameWorld.Pulser.submit(pulse)
|
||||
player.setAttribute("/save:not_on_highscores",true)
|
||||
player.setAttribute("botting:script",pulse)
|
||||
}
|
||||
|
||||
inner class BotScriptPulse(public val botScript: Script, val isPlayer: Boolean = false) : Pulse(1){
|
||||
var ticks = 0
|
||||
init {
|
||||
botScript.init(isPlayer)
|
||||
}
|
||||
var randomDelay = 0
|
||||
|
||||
override fun pulse(): Boolean {
|
||||
if(randomDelay > 0){
|
||||
randomDelay -= 1
|
||||
return false
|
||||
}
|
||||
if (!botScript.bot.pulseManager.hasPulseRunning()) {
|
||||
|
||||
/*if (ticks++ >= RandomFunction.random(90000,120000)) {
|
||||
AIPlayer.deregister(botScript.bot.uid)
|
||||
ticks = 0
|
||||
SystemLogger.log("Submitting transition pulse from ticks")
|
||||
GameWorld.Pulser.submit(TransitionPulse(botScript))
|
||||
return true
|
||||
}*/
|
||||
if(!botScript.running) return true //has to be separated this way or it double-submits the respawn pulse.
|
||||
|
||||
|
||||
val idleRoll = RandomFunction.random(50)
|
||||
if(idleRoll == 2 && botScript !is Idler){
|
||||
randomDelay += RandomFunction.random(2,20)
|
||||
return false
|
||||
}
|
||||
botScript.tick()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
ticks = Integer.MAX_VALUE - 20 //Sets the ticks as high as they can go (safely) and then runs pulse again
|
||||
pulse() //to trigger the transition pulse to be submitted.
|
||||
super.stop()
|
||||
if (Server.running) AIRepository.PulseRepository.remove(this.botScript.bot.username.lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
inner class TransitionPulse(val script: Script) : Pulse(RandomFunction.random(60,200)){
|
||||
override fun pulse(): Boolean {
|
||||
// This does not get called and should be removed
|
||||
GameWorld.Pulser.submit(BotScriptPulse(script.newInstance()).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it })
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package core.game.bots
|
||||
|
||||
annotation class PlayerCompatible
|
||||
@@ -0,0 +1,6 @@
|
||||
package core.game.bots
|
||||
|
||||
object PlayerScripts {
|
||||
class PlayerScript(val identifier: String, val description: Array<String>, val name: String, val clazz: Class<*>)
|
||||
val identifierMap = HashMap<String, PlayerScript>()
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package core.game.bots;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import core.game.consumable.Consumable;
|
||||
import content.data.consumables.Consumables;
|
||||
import core.game.consumable.Food;
|
||||
import content.data.consumables.effects.HealingEffect;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.prayer.PrayerType;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.RegionManager;
|
||||
import core.tools.RandomFunction;
|
||||
|
||||
public class PvMBots extends AIPlayer {
|
||||
|
||||
//Used so the bot doesn't spam actions
|
||||
private int tick = 0;
|
||||
|
||||
public PvMBots(Location l) {
|
||||
super(l);
|
||||
}
|
||||
|
||||
public PvMBots(String copyFromFile, Location l) {
|
||||
super(copyFromFile, l);
|
||||
}
|
||||
|
||||
|
||||
public List<Entity> FindTargets(Entity entity, int radius) {
|
||||
List<Entity> targets = new ArrayList<>(20);
|
||||
Object[] localNPCs = RegionManager.getLocalNpcs(entity,radius).toArray();
|
||||
int length = localNPCs.length;
|
||||
if(length > 5){length = 5;}
|
||||
for (int i = 0; i < length; i++) {
|
||||
NPC npc = (NPC) localNPCs[i];
|
||||
{
|
||||
if (checkValidTargets(npc))
|
||||
targets.add(npc);
|
||||
}
|
||||
}
|
||||
if (targets.size() == 0)
|
||||
return null;
|
||||
return targets;
|
||||
}
|
||||
|
||||
public boolean checkValidTargets(NPC target) {
|
||||
if (!target.isActive()) {
|
||||
return false;
|
||||
}
|
||||
if (!target.getProperties().isMultiZone() && target.inCombat()) {
|
||||
return false;
|
||||
}
|
||||
if (!target.getDefinition().hasAction("attack")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean AttackNpcsInRadius(int radius)
|
||||
{
|
||||
return AttackNpcsInRadius(this, radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attacks NPCs in radius of bot
|
||||
* @param bot
|
||||
* @param radius
|
||||
* @return true if bot will be fighting
|
||||
*/
|
||||
public boolean AttackNpcsInRadius(Player bot, int radius) {
|
||||
if (bot.inCombat())
|
||||
return true;
|
||||
List<Entity> creatures = FindTargets(bot, radius);
|
||||
if (creatures == null) {
|
||||
return false;
|
||||
}
|
||||
bot.attack(creatures.get(RandomFunction.getRandom((creatures.size() - 1))));
|
||||
if (!creatures.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
creatures = FindTargets(bot, radius);
|
||||
if (!creatures.isEmpty())
|
||||
{
|
||||
bot.attack(creatures.get(RandomFunction.getRandom((creatures.size() - 1))));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
this.tick++;
|
||||
|
||||
//Despawn
|
||||
if (this.getSkills().getLifepoints() <= 5){
|
||||
//TODO: Just respawn a new bot (not sure how you'd do that :L)
|
||||
// Maybe make all PvMBots know what to do if they aren't in right area? I.e. pest control bots teleport to PC
|
||||
//this.teleport(new Location(500, 500));
|
||||
//Despawning not being delayed causes 3 errors in the console
|
||||
this.getSkills().setLifepoints(20);
|
||||
}
|
||||
|
||||
//Npc Combat
|
||||
/*if (this.tick % 10 == 0) {
|
||||
if (!this.inCombat())
|
||||
AttackNpcsInRadius(this, 5);
|
||||
}*/
|
||||
|
||||
if (this.tick == 100) this.tick = 0;
|
||||
|
||||
//this.eat();
|
||||
//this.getPrayer().toggle()
|
||||
}
|
||||
|
||||
public void CheckPrayer(PrayerType type[]) {
|
||||
for (int i = 0; i < type.length; i++)
|
||||
this.getPrayer().toggle(type[i]);
|
||||
}
|
||||
|
||||
public void eat(int foodId) {
|
||||
Item foodItem = new Item(foodId);
|
||||
|
||||
if ((this.getSkills().getStaticLevel(Skills.HITPOINTS) >= this.getSkills().getLifepoints() * 3) && this.getInventory().containsItem(foodItem)) {
|
||||
this.lock(3);
|
||||
//this.animate(new Animation(829));
|
||||
Item food = this.getInventory().getItem(foodItem);
|
||||
|
||||
Consumable consumable = Consumables.getConsumableById(food.getId());
|
||||
|
||||
if (consumable == null) {
|
||||
consumable = new Food(new int[] {food.getId()}, new HealingEffect(1));
|
||||
}
|
||||
|
||||
consumable.consume(food, this);
|
||||
this.getProperties().getCombatPulse().delayNextAttack(3);
|
||||
}
|
||||
if (this.checkVictimIsPlayer() == false)
|
||||
if (!(this.getInventory().contains(foodId, 1)))
|
||||
this.getInventory().add(new Item(foodId, 5)); //Add Food to Inventory
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.world.map.Location
|
||||
import content.minigame.pestcontrol.bots.PestControlTestBot
|
||||
import content.minigame.pestcontrol.bots.PestControlTestBot2
|
||||
|
||||
// import sun.util.resources.CalendarData;
|
||||
class PvMBotsBuilder {
|
||||
|
||||
companion object {
|
||||
var botsSpawned = 0
|
||||
|
||||
fun create(l: Location?): PvMBots {
|
||||
botsSpawned++
|
||||
return PvMBots(l)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createPestControlTestBot2(l: Location?): PestControlTestBot2 {
|
||||
botsSpawned++
|
||||
return PestControlTestBot2(l!!)
|
||||
}
|
||||
@JvmStatic
|
||||
fun createPestControlTestBot(l: Location?): PestControlTestBot {
|
||||
botsSpawned++
|
||||
return PestControlTestBot(l!!)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package core.game.bots;
|
||||
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.item.Item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class Script {
|
||||
|
||||
public ScriptAPI scriptAPI;
|
||||
public ArrayList<Item> inventory = new ArrayList<>(20);
|
||||
public ArrayList<Item> equipment = new ArrayList<>(20);
|
||||
public Map<Integer, Integer> skills = new HashMap<>();
|
||||
|
||||
|
||||
public Player bot;
|
||||
|
||||
public boolean running = true;
|
||||
|
||||
public void init(boolean isPlayer)
|
||||
{
|
||||
//bot.init();
|
||||
scriptAPI = new ScriptAPI(bot);
|
||||
|
||||
if(!isPlayer) {
|
||||
for (Item i : equipment) {
|
||||
bot.getEquipment().add(i, true, false);
|
||||
}
|
||||
bot.getInventory().clear();
|
||||
for (Item i : inventory) {
|
||||
bot.getInventory().add(i);
|
||||
}
|
||||
for (Map.Entry<Integer, Integer> skill : skills.entrySet()) {
|
||||
setLevel(skill.getKey(), skill.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return bot.getName() + " is a " + this.getClass().getSimpleName() + " at location " + bot.getLocation().toString() + " Current pulse: " + bot.getPulseManager().getCurrent();
|
||||
}
|
||||
|
||||
public abstract void tick();
|
||||
|
||||
public void setLevel(int skill, int level) {
|
||||
bot.getSkills().setLevel(skill, level);
|
||||
bot.getSkills().setStaticLevel(skill, level);
|
||||
bot.getSkills().updateCombatLevel();
|
||||
bot.getAppearance().sync();
|
||||
}
|
||||
|
||||
// This does not get called and all implementations should be removed
|
||||
public abstract Script newInstance();
|
||||
}
|
||||
@@ -0,0 +1,827 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.api.*
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.game.component.Component
|
||||
import core.game.consumable.Consumable
|
||||
import content.data.consumables.Consumables
|
||||
import core.game.consumable.Food
|
||||
import content.data.consumables.effects.HealingEffect
|
||||
import core.game.interaction.DestinationFlag
|
||||
import core.game.interaction.MovementPulse
|
||||
import core.game.interaction.Option
|
||||
import core.game.node.Node
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.world.update.flag.context.ChatMessage
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import core.game.world.update.flag.player.ChatFlag
|
||||
import core.tools.RandomFunction
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
import core.ServerConstants.Companion.SERVER_GE_NAME
|
||||
import core.game.ge.GrandExchange
|
||||
import core.game.ge.GrandExchangeOffer
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListeners
|
||||
import core.tools.SystemLogger
|
||||
import core.game.system.config.ItemConfigParser
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.repository.Repository
|
||||
import core.tools.colorize
|
||||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class ScriptAPI(private val bot: Player) {
|
||||
val GRAPHICSUP = Graphics(1576)
|
||||
val ANIMATIONUP = Animation(8939)
|
||||
val GRAPHICSDOWN = Graphics(1577)
|
||||
val ANIMATIONDOWN = Animation(8941)
|
||||
|
||||
/**
|
||||
* Gets the distance between two nodes
|
||||
* @param n1 the first node
|
||||
* @param n2 the second node
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun distance(n1: Node, n2: Node): Double {
|
||||
return sqrt((n1.location.x - n2.location.x.toDouble()).pow(2.0) + (n2.location.y - n1.location.y.toDouble()).pow(2.0))
|
||||
}
|
||||
|
||||
fun interact(bot: Player, node: Node?, option: String){
|
||||
|
||||
if(node == null) return
|
||||
|
||||
val type = when(node){
|
||||
is Scenery -> IntType.SCENERY
|
||||
is NPC -> IntType.NPC
|
||||
is Item -> IntType.ITEM
|
||||
else -> null
|
||||
} ?: return
|
||||
val opt: Option? = node.interaction.options.filter {it != null && it.name.equals(option, true) }.firstOrNull()
|
||||
|
||||
if(opt == null){
|
||||
SystemLogger.logWarn(this::class.java, "Invalid option name provided: $option")
|
||||
return
|
||||
}
|
||||
|
||||
if(!InteractionListeners.run(node.id, type, option, bot, node)) node.interaction.handle(bot, opt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest node with name entityName
|
||||
* @param entityName the name of the node to look for
|
||||
* @return the nearest node with a matching name or null
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun getNearestNode(entityName: String): Node? {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (node in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) {
|
||||
if (node != null && node.name == entityName && distance(bot, node) < minDistance && !Pathfinder.find(bot, node).isMoveNear) {
|
||||
entity = node
|
||||
minDistance = distance(bot, node)
|
||||
}
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
fun sendChat(message: String) {
|
||||
bot.sendChat(message)
|
||||
bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest node with a name contained in the list of acceptable names
|
||||
* @param acceptedNames the list of accepted npc/object names
|
||||
* @return the nearest node with a matching name or null
|
||||
* @author Ceikry
|
||||
*/
|
||||
|
||||
fun getNearestNodeFromList(acceptedNames: List<String>, isObject: Boolean): Node? {
|
||||
if (isObject) {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) {
|
||||
for(e in objects) {
|
||||
val name = e?.name
|
||||
if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if(entity == null) null else entity as Scenery
|
||||
} else {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) {
|
||||
val name = e?.name
|
||||
if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
return entity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest node with matching id.
|
||||
* @param id the id to look for
|
||||
* @param object whether or not the node we are looking for is an object.
|
||||
* @return the closest node with matching id or null.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun getNearestNode(id: Int, `object`: Boolean): Node? {
|
||||
if (`object`) {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) {
|
||||
for(e in objects) {
|
||||
if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if(entity == null) null else entity as Scenery
|
||||
} else {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) {
|
||||
if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
return entity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest node with a matching name.
|
||||
* @param name the name to look for.
|
||||
* @param object whether or not the node we are looking for is an object.
|
||||
* @return the nearest matching node or null.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun getNearestNode(name: String, `object`: Boolean): Node? {
|
||||
if (`object`) {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) {
|
||||
for(e in objects) {
|
||||
if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if(entity == null) null else entity as Scenery
|
||||
} else {
|
||||
var entity: Node? = null
|
||||
var minDistance = Double.MAX_VALUE
|
||||
for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) {
|
||||
if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) {
|
||||
entity = e
|
||||
minDistance = distance(bot, e)
|
||||
}
|
||||
}
|
||||
return entity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest ground item with matching ID from the list in AIRepository.
|
||||
* @param id the id of the ground item we are looking for.
|
||||
* @return the nearest GroundItem with a matching id or null
|
||||
* @author Ceikry
|
||||
*/
|
||||
private fun getNearestGroundItem(id: Int): GroundItem? {
|
||||
var distance = 11.0
|
||||
var closest: GroundItem? = null
|
||||
if(AIRepository.getItems(bot) != null) {
|
||||
for (item in AIRepository.getItems(bot)!!.filter { it: GroundItem -> it.distance(bot.location) < 10 }) {
|
||||
if (item.id == id) {
|
||||
//distance = item.distance(bot.location)
|
||||
closest = item
|
||||
}
|
||||
}
|
||||
if (!GroundItemManager.getItems().contains(closest)){
|
||||
AIRepository.getItems(bot)?.remove(closest)
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
val items: ArrayList<GroundItem>? = bot.getAttribute("botting:drops",null)
|
||||
if(items != null){
|
||||
for(item in items.filter { it.distance(bot.location) < 10 }){
|
||||
if(item.id == id) return item.also { items.remove(item); bot.setAttribute("botting:drops",items) }
|
||||
}
|
||||
}
|
||||
}
|
||||
return closest
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the nearest ground item with a matching id if it exists.
|
||||
* @param id the id to look for
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun takeNearestGroundItem(id: Int) : Boolean{
|
||||
val item = getNearestGroundItem(id)
|
||||
if(item != null){
|
||||
item.interaction?.handle(bot, item.interaction[2])
|
||||
return true
|
||||
}
|
||||
else return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest GameObject to loc with matching objectId
|
||||
* @param loc the location we are checking around
|
||||
* @param objectId the id of the object we are looking for
|
||||
* @return the nearest matching object or null.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun getNearestGameObject(loc: Location, objectId: Int): Scenery? {
|
||||
var nearestObject: Scenery? = null
|
||||
val minDistance = Double.MAX_VALUE
|
||||
for (o in RegionManager.forId(loc.regionId).planes[0].objects) {
|
||||
for (obj in o) {
|
||||
if (obj != null) {
|
||||
if (distance(loc, obj) < minDistance && obj.id == objectId) {
|
||||
nearestObject = obj
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearestObject
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of NPCs that are attackable around the entity.
|
||||
* @param entity the entity to search around.
|
||||
* @param radius the radius around entity to search in.
|
||||
* @param name the name of the NPC to look for.
|
||||
* @return an Array of the nearest NPCs with matching name, or null.
|
||||
* @author Ceikry
|
||||
*/
|
||||
private fun findTargets(entity: Entity, radius: Int, name: String? = null): List<Entity>? {
|
||||
val targets: MutableList<Entity> = ArrayList()
|
||||
val localNPCs: Array<Any> = RegionManager.getLocalNpcs(entity, radius).toTypedArray()
|
||||
var length = localNPCs.size
|
||||
if (length > 5) {
|
||||
length = 5
|
||||
}
|
||||
for (i in 0 until length) {
|
||||
val npc = localNPCs[i] as NPC
|
||||
run { if (checkValidTargets(npc, name)) targets.add(npc) }
|
||||
}
|
||||
return if (targets.size == 0) null else targets
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given target is a valid target for a combat bot.
|
||||
* @param target the NPC that we are checking
|
||||
* @param name the expected name of the NPC.
|
||||
* @return true if the target is valid, false if not.
|
||||
* @author Ceikry
|
||||
*/
|
||||
private fun checkValidTargets(target: NPC, name: String?): Boolean {
|
||||
if (!target.isActive) {
|
||||
return false
|
||||
}
|
||||
if (!target.properties.isMultiZone && target.inCombat()) {
|
||||
return false
|
||||
}
|
||||
if (name != null){
|
||||
if(target.name != name)
|
||||
return false
|
||||
}
|
||||
return target.definition.hasAction("attack")
|
||||
}
|
||||
|
||||
/**
|
||||
* Attacks npcs in the given radius of the bot.
|
||||
* @param bot the bot that is attacking
|
||||
* @param radius the radius to attack in
|
||||
* @return true if successfully attacking an NPC within that radius, false if not.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun attackNpcsInRadius(bot: Player, radius: Int): Boolean {
|
||||
if (bot.inCombat()) return true
|
||||
var creatures: List<Entity>? = findTargets(bot, radius) ?: return false
|
||||
bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)])
|
||||
return if (creatures.isNotEmpty()) {
|
||||
true
|
||||
} else {
|
||||
creatures = findTargets(bot, radius)
|
||||
if (!creatures!!.isEmpty()) {
|
||||
bot.attack(creatures[RandomFunction.getRandom(creatures.size - 1)])
|
||||
return true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to iteratively walk a bot to a faraway location. Limited by doors and large obstacles like mountains.
|
||||
* @param loc the location to walk to.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun walkTo(loc: Location){
|
||||
if(!bot.walkingQueue.isMoving) {
|
||||
GlobalScope.launch {
|
||||
walkToIterator(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to iteratively walk an array of Locations to get over complex obstacles.
|
||||
* @param steps the array of locations to walk to.
|
||||
* @author dginovker
|
||||
*/
|
||||
fun walkArray(steps: Array<Location>){
|
||||
bot.pulseManager.run(object : Pulse(){
|
||||
var stepIndex = 0
|
||||
override fun pulse(): Boolean {
|
||||
// If the stepIndex is out of bounds, we're done
|
||||
if(stepIndex >= steps.size) return true
|
||||
// If we're near the last step, we're done
|
||||
if (bot.location.withinDistance(steps[steps.size - 1], 2)) {
|
||||
return true
|
||||
}
|
||||
// If we're not near the next step, walk to it
|
||||
if (!bot.location.withinDistance(steps[stepIndex], 5)) {
|
||||
walkTo(steps[stepIndex])
|
||||
return false
|
||||
}
|
||||
// If we're near the next step, increment the step index
|
||||
stepIndex++
|
||||
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loc the location to walk to.
|
||||
* @param radius tiles around the location the bot could walk to.
|
||||
* @author Kermit
|
||||
*/
|
||||
fun randomWalkTo(loc: Location, radius: Int) {
|
||||
if(!bot.walkingQueue.isMoving) {
|
||||
GlobalScope.launch {
|
||||
var newloc = loc.transform(RandomFunction.random(radius,-radius),
|
||||
RandomFunction.random(radius,-radius), 0)
|
||||
walkToIterator(newloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The iterator for long-distance walking. Limited by doors and large obstacles like mountains.
|
||||
* @param loc the location to find a path to.
|
||||
* @author Ceikry
|
||||
*/
|
||||
private fun walkToIterator(loc: Location){
|
||||
var diffX = loc.x - bot.location.x
|
||||
var diffY = loc.y - bot.location.y
|
||||
while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) {
|
||||
diffX /= 2
|
||||
diffY /= 2
|
||||
}
|
||||
GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) {
|
||||
override fun pulse(): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Attacks npcs in the given radius of the bot.
|
||||
* @param bot the bot that is attacking
|
||||
* @param radius the radius to attack in
|
||||
* @param name the name of the NPC to target.
|
||||
* @return true if successfully attacking an NPC within that radius, false if not.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun attackNpcInRadius(bot: Player, name: String, radius: Int): Boolean {
|
||||
if (bot.inCombat()) return true
|
||||
var creatures: List<Entity>? = findTargets(bot, radius, name) ?: return false
|
||||
bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)])
|
||||
return if (creatures.isNotEmpty()) {
|
||||
true
|
||||
} else {
|
||||
creatures = findTargets(bot, radius, name)
|
||||
if (!creatures!!.isEmpty()) {
|
||||
bot.attack(creatures.random())
|
||||
return true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function to find the distance between a location and a ground item.
|
||||
* @param loc the location to check the distance from.
|
||||
* @return a Double representing the distance.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun GroundItem.distance(loc: Location): Double{
|
||||
return location.getDistance(loc)
|
||||
}
|
||||
|
||||
/**
|
||||
* A function for teleporting the bot to the GE
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun teleportToGE() : Boolean{
|
||||
if (bot.isTeleBlocked) {
|
||||
return false
|
||||
}
|
||||
bot.lock()
|
||||
bot.visualize(ANIMATIONUP, GRAPHICSUP)
|
||||
bot.impactHandler.disabledTicks = 4
|
||||
val location = Location.create(3165, 3482, 0)
|
||||
bot.pulseManager.run(object : Pulse(4, bot) {
|
||||
override fun pulse(): Boolean {
|
||||
bot.unlock()
|
||||
bot.properties.teleportLocation = location
|
||||
bot.animator.reset()
|
||||
return true
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* A function for selling a given item on the GE.
|
||||
* @param id the ID of the item to sell on the GE. Pulls from the bot's bank.
|
||||
* @author Ceikry
|
||||
* @author Angle
|
||||
*/
|
||||
fun sellOnGE(id: Int){
|
||||
class toCounterPulse : MovementPulse(bot, Location.create(3165, 3487, 0)){
|
||||
override fun pulse(): Boolean {
|
||||
var actualId = id
|
||||
val itemAmt = bot.bank.getAmount(id)
|
||||
if (ItemDefinition.forId(id).noteId == id){
|
||||
actualId = Item(id).noteChange
|
||||
}
|
||||
val canSell = GrandExchange.addBotOffer(actualId, itemAmt)
|
||||
if (canSell && saleIsBigNews(actualId, itemAmt)) {
|
||||
SystemLogger.logAI(this::class.java, "Offered $itemAmt of $actualId on the GE.")
|
||||
Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.")
|
||||
}
|
||||
bot.bank.remove(Item(id, itemAmt))
|
||||
bot.bank.refresh()
|
||||
return true
|
||||
}
|
||||
}
|
||||
bot.pulseManager.run(toCounterPulse())
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun sellAllOnGe(){
|
||||
class toCounterPulseAll : MovementPulse(bot, Location.create(3165, 3487, 0)){
|
||||
override fun pulse(): Boolean {
|
||||
for(item in bot.bank.toArray()) {
|
||||
item ?: continue
|
||||
if (item.id == Items.LOBSTER_379) continue
|
||||
if (item.id == Items.SWORDFISH_373) continue
|
||||
if (item.id == Items.SHARK_385) continue
|
||||
if(!item.definition.isTradeable) {continue}
|
||||
val itemAmt = item.amount
|
||||
var actualId = item.id
|
||||
if (ItemDefinition.forId(actualId).noteId == actualId){
|
||||
actualId = Item(actualId).noteChange
|
||||
}
|
||||
val canSell = GrandExchange.addBotOffer(actualId, itemAmt)
|
||||
if (canSell && saleIsBigNews(actualId, itemAmt)) {
|
||||
Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.")
|
||||
}
|
||||
bot.bank.remove(item)
|
||||
bot.bank.refresh()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
bot.pulseManager.run(toCounterPulseAll())
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable.
|
||||
* @author Ceikry & Kermit
|
||||
*/
|
||||
fun sellAllOnGeAdv(){
|
||||
val ge: Scenery? = getNearestNode("Desk", true) as Scenery?
|
||||
class toCounterPulseAll : MovementPulse(bot, ge, DestinationFlag.OBJECT) {
|
||||
override fun pulse(): Boolean {
|
||||
for(item in bot.bank.toArray()) {
|
||||
item ?: continue
|
||||
if(!item.definition.isTradeable) {continue}
|
||||
val itemAmt = item.amount
|
||||
var actualId = item.id
|
||||
if (ItemDefinition.forId(actualId).noteId == actualId){
|
||||
actualId = Item(actualId).noteChange
|
||||
}
|
||||
val canSell = GrandExchange.addBotOffer(actualId, itemAmt)
|
||||
if (canSell && saleIsBigNews(actualId, itemAmt)) {
|
||||
when (actualId){
|
||||
1511 -> continue
|
||||
1513 -> continue
|
||||
1515 -> continue
|
||||
1517 -> continue
|
||||
1519 -> continue
|
||||
1521 -> continue
|
||||
else -> Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.")
|
||||
}
|
||||
}
|
||||
bot.bank.remove(item)
|
||||
bot.bank.refresh()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
bot.pulseManager.run(toCounterPulseAll())
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to determine whether or not to bother everyone on the server
|
||||
* with the big news that a bot is selling something to the GE, based on item value
|
||||
* @param itemID
|
||||
* @param amount
|
||||
* @author Gexja
|
||||
*/
|
||||
fun saleIsBigNews(itemID: Int, amount: Int): Boolean {
|
||||
return ItemDefinition.forId(itemID).getAlchemyValue(true) * amount >= 500
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to teleport a bot to the given location.
|
||||
* @param loc the location to teleport to
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun teleport(loc: Location) : Boolean {
|
||||
if (bot.isTeleBlocked) {
|
||||
return false
|
||||
}
|
||||
bot.lock()
|
||||
bot.visualize(ANIMATIONUP, GRAPHICSUP)
|
||||
bot.impactHandler.disabledTicks = 4
|
||||
val location = loc
|
||||
GameWorld.Pulser.submit(object : Pulse(4, bot) {
|
||||
override fun pulse(): Boolean {
|
||||
bot.unlock()
|
||||
bot.properties.teleportLocation = location
|
||||
bot.animator.reset()
|
||||
return true
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the given item out of the bot's inventory and places it into the bank. Banks all items with matching ID.
|
||||
* @param item the ID of the item to bank
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun bankItem(item: Int){
|
||||
class BankingPulse() : Pulse(20){
|
||||
override fun pulse(): Boolean {
|
||||
val logs = bot.inventory.getAmount(item)
|
||||
bot.inventory.remove(Item(item, logs))
|
||||
bot.bank.add(Item(item, logs))
|
||||
return true
|
||||
}
|
||||
}
|
||||
bot.pulseManager.run(BankingPulse())
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes every item out of the bots inventory and places it into the bank.
|
||||
* @param none
|
||||
* @author cfunnyman joe
|
||||
*/
|
||||
fun bankAll(){
|
||||
class BankingPulse() : Pulse(20){
|
||||
override fun pulse(): Boolean {
|
||||
for(item in bot.inventory.toArray()){
|
||||
var itemAmount = bot.inventory.getAmount(item)
|
||||
|
||||
bot.inventory.remove(item)
|
||||
bot.bank.add(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
bot.pulseManager.run(BankingPulse())
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that makes the bot eat the food item in their inventory if their HP is below 2/3.
|
||||
* @author Ceikry
|
||||
* @param foodId the ID of the food item to eat
|
||||
*/
|
||||
fun eat(foodId: Int) {
|
||||
val foodItem = Item(foodId)
|
||||
if (bot.skills.getStaticLevel(Skills.HITPOINTS) * RandomFunction.random(0.5, 0.75) >= bot.skills.lifepoints && bot.inventory.containsItem(foodItem)) {
|
||||
bot.lock(3)
|
||||
//this.animate(new Animation(829));
|
||||
val food = bot.inventory.getItem(foodItem)
|
||||
var consumable: Consumable? = Consumables.getConsumableById(foodId)
|
||||
if (consumable == null) {
|
||||
consumable = Food(intArrayOf(food.id), HealingEffect(1))
|
||||
}
|
||||
consumable.consume(food, bot)
|
||||
bot.properties.combatPulse.delayNextAttack(3)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as the eat function, except that it forces the bot to eat regardless of HP.
|
||||
* @author Ceikry
|
||||
* @param foodId the ID of the food item to eat.
|
||||
*/
|
||||
fun forceEat(foodId: Int) {
|
||||
val foodItem = Item(foodId)
|
||||
if (bot.inventory.containsItem(foodItem)) {
|
||||
bot.lock(3)
|
||||
//this.animate(new Animation(829));
|
||||
val food = bot.inventory.getItem(foodItem)
|
||||
var consumable: Consumable? = Consumables.getConsumableById(foodId)
|
||||
if (consumable == null) {
|
||||
consumable = Food(intArrayOf(foodId), HealingEffect(1))
|
||||
}
|
||||
consumable.consume(food, bot)
|
||||
bot.properties.combatPulse.delayNextAttack(3)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for buying items off the GE.
|
||||
* @param itemID the id of the item to buy off the GE.
|
||||
* @param amount the amount to buy.
|
||||
* @return true if item was successfully bought, false if not.
|
||||
*/
|
||||
fun buyFromGE(bot: Player, itemID: Int, amount: Int){
|
||||
GlobalScope.launch {
|
||||
val offer = GrandExchangeOffer()
|
||||
offer.itemID = itemID
|
||||
offer.sell = false
|
||||
offer.offeredValue = checkPriceOverrides(itemID) ?: ItemDefinition.forId(itemID).value
|
||||
offer.amount = amount
|
||||
offer.player = bot
|
||||
//GrandExchange.dispatch(bot, offer)
|
||||
AIRepository.addOffer(bot, offer)
|
||||
var bought: Boolean = false
|
||||
val latch = CountDownLatch(1)
|
||||
bot.pulseManager.run(object : Pulse(5) {
|
||||
override fun pulse(): Boolean {
|
||||
bought = offer.completedAmount == offer.amount
|
||||
latch.countDown()
|
||||
return true
|
||||
}
|
||||
})
|
||||
latch.await()
|
||||
if(bought){
|
||||
bot.bank.add(Item(offer.itemID, offer.completedAmount))
|
||||
bot.bank.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to allow a bot to withdraw items from its bank.
|
||||
* @param itemID the item to withdraw.
|
||||
* @param amount the amount to withdraw.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun withdraw(itemID: Int, amount: Int){
|
||||
var item: Item? = null
|
||||
if(bot.bank.containsItem(Item(itemID, amount))){
|
||||
item = Item(itemID, amount)
|
||||
} else {
|
||||
item = Item(itemID, bot.bank.getAmount(itemID))
|
||||
}
|
||||
if(item.amount == 0) return
|
||||
if(!bot.inventory.hasSpaceFor(item)){
|
||||
item.amount = bot.inventory.getMaximumAdd(item)
|
||||
}
|
||||
bot.bank.remove(item)
|
||||
bot.inventory.add(item)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to equip list of items and set stats
|
||||
* Useful for starting a bot up
|
||||
* @param items the list of items to equip
|
||||
* @author dginovker
|
||||
*/
|
||||
fun equipAndSetStats(items: List<Item>?){
|
||||
if (items == null) return
|
||||
for(item in items){
|
||||
equipAndSetStats(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to equip a single item and set stats
|
||||
* Useful for starting a bot up
|
||||
* @param item the item to equip
|
||||
* @author dginovker
|
||||
*/
|
||||
fun equipAndSetStats(item: Item){
|
||||
val configs = item.definition.handlers
|
||||
val slot = configs["equipment_slot"] ?: return
|
||||
bot.equipment.add(item, slot as Int,
|
||||
false,false)
|
||||
val reqs = configs["requirements"]
|
||||
if(reqs != null)
|
||||
for(req in configs["requirements"] as HashMap<Int,Int>)
|
||||
bot.skills.setStaticLevel(req.key, req.value)
|
||||
bot.skills.updateCombatLevel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to load appearance and equipment from JSON
|
||||
* Useful for starting a bot up
|
||||
* @param json the JSON object to load from (dumped via ::dumpappearance)
|
||||
* @author dginovker
|
||||
*/
|
||||
fun loadAppearanceAndEquipment(json: JSONObject?) {
|
||||
if (json == null) return
|
||||
bot.equipment.clear()
|
||||
bot.appearance.parse(json["appearance"] as JSONObject)
|
||||
val equipment = json["equipment"] as JSONArray
|
||||
bot.equipment.parse(equipment)
|
||||
bot.appearance.sync()
|
||||
for (i in 0 until bot.equipment.capacity()) {
|
||||
val item = bot.equipment.get(i)
|
||||
if (item != null) {
|
||||
equipAndSetStats(item)
|
||||
}
|
||||
}
|
||||
// Set all combat stats to a function of the highest combat stat(otherwise you end up with lopsided stats)
|
||||
val highestCombatSkill = bot.skills.getStaticLevel(bot.skills.highestCombatSkillId)
|
||||
for (i in 0 until 7) {
|
||||
bot.skills.setStaticLevel(i, max((highestCombatSkill * 0.75).toInt(), bot.skills.getStaticLevel(i)))
|
||||
}
|
||||
bot.skills.updateCombatLevel()
|
||||
}
|
||||
|
||||
fun getOverlay(): BottingOverlay {
|
||||
return BottingOverlay(bot)
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to check for price overrides.
|
||||
* @param id the id to check for overrides for.
|
||||
* @author Ceikry
|
||||
*/
|
||||
fun checkPriceOverrides(id: Int): Int?{
|
||||
return when(id){
|
||||
else -> itemDefinition(id).getConfiguration(ItemConfigParser.GE_PRICE)
|
||||
}
|
||||
}
|
||||
|
||||
class BottingOverlay(val player: Player){
|
||||
fun init(){
|
||||
player.interfaceManager.openOverlay(Component(195))
|
||||
player.packetDispatch.sendInterfaceConfig(195,5,true)
|
||||
}
|
||||
fun setTitle(title: String){
|
||||
player.packetDispatch.sendString(colorize("%B$title"),195,7)
|
||||
}
|
||||
fun setTaskLabel(label: String){
|
||||
player.packetDispatch.sendString(colorize("%B$label"),195,8)
|
||||
}
|
||||
fun setAmount(amount: Int){
|
||||
player.packetDispatch.sendString(colorize("%B$amount"),195,9)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package core.game.bots
|
||||
|
||||
annotation class ScriptDescription(vararg val value: String)
|
||||
@@ -0,0 +1,3 @@
|
||||
package core.game.bots
|
||||
|
||||
annotation class ScriptIdentifier(val value: String)
|
||||
@@ -0,0 +1,3 @@
|
||||
package core.game.bots
|
||||
|
||||
annotation class ScriptName(val value: String)
|
||||
@@ -0,0 +1,44 @@
|
||||
package core.game.bots
|
||||
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
|
||||
class SkillingBotAssembler {
|
||||
fun produce(type: Wealth, loc: Location): AIPlayer {
|
||||
return assembleBot(AIPlayer(loc),type)
|
||||
}
|
||||
|
||||
fun assembleBot(bot: AIPlayer, type: Wealth): AIPlayer {
|
||||
return when(type){
|
||||
Wealth.POOR -> equipSet(bot,POORSETS.random())
|
||||
Wealth.AVERAGE -> equipSet(bot,AVGSETS.random())
|
||||
Wealth.RICH -> equipSet(bot,RICHSETS.random())
|
||||
}
|
||||
}
|
||||
|
||||
fun equipSet(bot: AIPlayer, set: List<Int>): AIPlayer {
|
||||
for(i in set){
|
||||
val item = Item(i)
|
||||
val configs = item.definition.handlers
|
||||
val slot = configs["equipment_slot"] ?: continue
|
||||
bot.equipment.add(item, slot as Int,
|
||||
false,false)
|
||||
val reqs = configs["requirements"]
|
||||
if(reqs != null)
|
||||
for(req in configs["requirements"] as HashMap<Int,Int>)
|
||||
bot.skills.setStaticLevel(req.key, req.value)
|
||||
}
|
||||
bot.skills.updateCombatLevel()
|
||||
return bot
|
||||
}
|
||||
|
||||
enum class Wealth {
|
||||
POOR,
|
||||
AVERAGE,
|
||||
RICH
|
||||
}
|
||||
|
||||
val POORSETS = arrayOf(listOf(542,544), listOf(581), listOf(6654,6655,6656), listOf(6654,6656), listOf(636,646), listOf(638,648), listOf(), listOf(), listOf())
|
||||
val AVGSETS = arrayOf(listOf(2649,342,344), listOf(2651,542,544), listOf(6654,6655,6656), listOf(6139,6141), listOf(9923,9924,9925), listOf(10400,10402,2649), listOf(10404,10406), listOf(12971,12978))
|
||||
val RICHSETS = arrayOf(listOf(10330,10332,2649), listOf(12873,12880,1046), listOf(13858,13861,13864), listOf(13887,13893), listOf(3481,3483), listOf(2653,2655), listOf(2661,2663), listOf(2591,2593), listOf(14490,14492))
|
||||
}
|
||||
Reference in New Issue
Block a user