/* * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package simpleserver.bot; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; import simpleserver.Coordinate; import simpleserver.Player; import simpleserver.Position; import simpleserver.Server; import simpleserver.config.xml.Event; import simpleserver.nbt.PlayerFile; public class NpcBot extends Bot { private Position position; private PlayerFile dat; private String purename; private HashMap<Integer, DroppedItem> dropped = new HashMap<Integer, DroppedItem>(); private String ev = null; private Timer timer = new Timer(); public NpcBot(String name, String e, Server s, Coordinate coordinate) throws IOException { this(name, e, s, new Position(coordinate)); } public NpcBot(String name, String e, Server s, Position position) throws IOException { super(s, "npc" + name); purename = name; ev = e; this.position = position; prepare(); } protected void prepare() throws IOException { dat = new PlayerFile(name, server); dat.setPosition(position); dat.save(); } @Override protected void handlePacket(byte packetId) throws IOException { switch (packetId) { case 0x08: // respawn super.handlePacket(packetId); if (dead) { respawnEvent(); } break; case 0x15: // pickup spawn addDroppedItem(); break; case 0x16: // collect item collectItem(); break; case 0x1d: // destroy entity destroyEntity(); break; default: super.handlePacket(packetId); } } private void collectItem() throws IOException { int what = in.readInt(); int who = in.readInt(); // System.out.println("Collected " + what + " Collector:" + who + // " real player id:" + playerEntityId); // DEBUG if (who != playerEntityId) { return; } DroppedItem i = dropped.get(what); if (i != null) { itemCollectEvent(i); } } private void addDroppedItem() throws IOException { int eid = in.readInt(); short id = in.readShort(); byte count = in.readByte(); short dat = in.readShort(); int x = in.readInt(); int y = in.readInt(); int z = in.readInt(); in.readByte(); // rotation in.readByte(); // pitch in.readByte(); // roll // System.out.println("Dropped item " + eid + " itemid:" + id + " amount:" + // count); // DEBUG dropped.put(eid, new DroppedItem(eid, id, count, dat, (double) x / 32, (double) y / 32, (double) z / 32)); } public void destroyEntity() throws IOException { byte destroyCount = in.readByte(); if (destroyCount > 0) { for (int i = 0; i < destroyCount; i++) { int eid = in.readInt(); dropped.remove(eid); } } } private Player nearestPlayer() { Player[] players = server.playerList.getArray(); Player nearest = null; double bestdistance = 255; for (Player p : players) { // fugly as hell :D double distx = Math.abs(p.x() - position.x()); double disty = Math.abs(p.y() - position.y()); double distz = Math.abs(p.z() - position.z()); double distxy = Math.sqrt(Math.pow(distx, 2) + Math.pow(disty, 2)); double distxyz = Math.sqrt(Math.pow(distxy, 2) + Math.pow(distz, 2)); if (distxyz < bestdistance) { bestdistance = distxyz; nearest = p; } } return nearest; } private void itemCollectEvent(DroppedItem i) { if (i == null || ev == null) { return; } Event e = server.eventhost.findEvent(ev); if (e == null) { System.out.println("NPC " + purename + ": Event " + ev + " not found!"); return; } Player nearest = nearestPlayer(); ArrayList<String> args = new ArrayList<String>(); args.add(purename); // NPC name args.add("collect"); // NPC trigger // collect trigger arguments args.add(String.valueOf(i.id)); // item id args.add(String.valueOf(i.count)); // item count args.add(String.valueOf(i.data)); // meta data server.eventhost.execute(e, nearest, true, args); } private void loginEvent() { Player nearest = nearestPlayer(); Event e = server.eventhost.findEvent(ev); if (e == null) { System.out.println("NPC " + purename + ": Event " + ev + " not found!"); return; } ArrayList<String> args = new ArrayList<String>(); args.add(purename); // NPC name args.add("login"); // NPC trigger server.eventhost.execute(e, nearest, true, args); } private void logoutEvent() { Player nearest = nearestPlayer(); Event e = server.eventhost.findEvent(ev); if (e == null) { System.out.println("NPC " + purename + ": Event " + ev + " not found!"); return; } ArrayList<String> args = new ArrayList<String>(); args.add(purename); // NPC name args.add("logout"); // NPC trigger server.eventhost.execute(e, nearest, true, args); } private void respawnEvent() { try { Thread.sleep(1000); } catch (Exception e) { } Player nearest = nearestPlayer(); Event e = server.eventhost.findEvent(ev); if (e == null) { System.out.println("NPC " + purename + ": Event " + ev + " not found!"); return; } ArrayList<String> args = new ArrayList<String>(); args.add(purename); // NPC name args.add("respawn"); // NPC trigger server.eventhost.execute(e, nearest, true, args); } @Override protected void ready() throws IOException { super.ready(); server.runCommand("gamemode", "1 " + name); // creative mode timer.schedule(new GhostWalk(), 0, 1000); // send movement updates loginEvent(); } @Override protected void die() { logoutEvent(); server.eventhost.npcs.remove(purename); timer.cancel(); super.die(); } @Override boolean ninja() { return false; } @SuppressWarnings("unused") private class DroppedItem { public int eid; public short id; public byte count; public short data; public double x; public double y; public double z; public DroppedItem(int eid, short id, byte count, short data, double x, double y, double z) { this.eid = eid; this.id = id; this.count = count; this.data = data; this.x = x; this.y = y; this.z = z; } } private final class GhostWalk extends TimerTask { @Override public void run() { try { if (!dead) { position.updateLook(0, 0); writeLock.lock(); walk(0.1); sendPosition(); walk(-0.1); sendPosition(); writeLock.unlock(); } } catch (IOException e) { error("GhostWalk failed"); } } } }