forked from 2009Scape/Server
608 lines
No EOL
20 KiB
Java
608 lines
No EOL
20 KiB
Java
package rs09.game.ai;
|
|
|
|
import rs09.ServerConstants;
|
|
import core.game.container.impl.EquipmentContainer;
|
|
import core.game.interaction.DestinationFlag;
|
|
import core.game.interaction.MovementPulse;
|
|
import core.game.interaction.Option;
|
|
import core.game.interaction.OptionHandler;
|
|
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 rs09.game.world.repository.Repository;
|
|
import core.net.packet.context.MessageContext;
|
|
import core.net.packet.in.InteractionPacket;
|
|
import core.plugin.Plugin;
|
|
import core.tools.RandomFunction;
|
|
import core.tools.StringUtils;
|
|
import core.game.content.dialogue.DialoguePlugin;
|
|
import core.game.node.entity.skill.Skills;
|
|
import core.game.content.quest.tutorials.tutorialisland.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<>();
|
|
|
|
/**
|
|
* The aip control dialogue.
|
|
*/
|
|
private static final AIPControlDialogue CONTROL_DIAL = new AIPControlDialogue();
|
|
|
|
/**
|
|
* 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.next());
|
|
}
|
|
} 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);
|
|
getInteraction().set(new Option("Control", 7).setHandler(new OptionHandler() {
|
|
@Override
|
|
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean handle(Player p, Node node, String option) {
|
|
DialoguePlugin dial = CONTROL_DIAL.newInstance(p);
|
|
if (dial != null && dial.open(AIPlayer.this)) {
|
|
p.getDialogueInterpreter().setDialogue(dial);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isWalk() {
|
|
return 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;
|
|
}
|
|
}, "movement");
|
|
}
|
|
|
|
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("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());
|
|
}
|
|
} |