diff --git a/pom.xml b/pom.xml index 82acff50e..82676af7b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 - 1.2.5-R1.2-SNAPSHOT + 1.2.5-R4.1-SNAPSHOT 2.0-SNAPSHOT Unknown diff --git a/src/main/java/net/citizensnpcs/Citizens.java b/src/main/java/net/citizensnpcs/Citizens.java index 69c4594de..60fd9ab3a 100644 --- a/src/main/java/net/citizensnpcs/Citizens.java +++ b/src/main/java/net/citizensnpcs/Citizens.java @@ -12,11 +12,10 @@ import net.citizensnpcs.api.event.CitizensReloadEvent; import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPCRegistry; -import net.citizensnpcs.api.npc.character.CharacterManager; import net.citizensnpcs.api.scripting.EventRegistrar; import net.citizensnpcs.api.scripting.ObjectProvider; import net.citizensnpcs.api.scripting.ScriptCompiler; -import net.citizensnpcs.api.trait.TraitManager; +import net.citizensnpcs.api.trait.TraitFactory; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DatabaseStorage; import net.citizensnpcs.api.util.NBTStorage; @@ -35,10 +34,9 @@ import net.citizensnpcs.command.exception.ServerCommandException; import net.citizensnpcs.command.exception.UnhandledCommandException; import net.citizensnpcs.command.exception.WrappedCommandException; import net.citizensnpcs.editor.Editor; -import net.citizensnpcs.npc.CitizensCharacterManager; import net.citizensnpcs.npc.CitizensNPC; import net.citizensnpcs.npc.CitizensNPCRegistry; -import net.citizensnpcs.npc.CitizensTraitManager; +import net.citizensnpcs.npc.CitizensTraitFactory; import net.citizensnpcs.npc.NPCSelector; import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.StringHelper; @@ -50,12 +48,12 @@ import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import com.google.common.collect.Iterables; public class Citizens extends JavaPlugin implements CitizensPlugin { - private final CitizensCharacterManager characterManager = new CitizensCharacterManager(); private final CommandManager commands = new CommandManager(); private boolean compatible; private Settings config; @@ -63,11 +61,32 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { private CitizensNPCRegistry npcRegistry; private Storage saves; // TODO: refactor this, it's used in too many places private NPCSelector selector; - private TraitManager traitManager; + private CitizensTraitFactory traitFactory; - @Override - public CharacterManager getCharacterManager() { - return characterManager; + private void despawnNPCs() { + Iterator itr = npcRegistry.iterator(); + while (itr.hasNext()) { + NPC npc = itr.next(); + itr.remove(); + npc.despawn(); + } + } + + private void enableSubPlugins() { + File root = new File(getDataFolder(), Setting.SUBPLUGIN_FOLDER.asString()); + if (!root.exists() || !root.isDirectory()) + return; + Plugin[] plugins = Bukkit.getPluginManager().loadPlugins(root); + // code beneath modified from CraftServer + for (Plugin plugin : plugins) { + try { + Messaging.logF("Loading %s", plugin.getDescription().getFullName()); + plugin.onLoad(); + } catch (Throwable ex) { + Messaging.severe(ex.getMessage() + " initializing " + plugin.getDescription().getFullName()); + ex.printStackTrace(); + } + } } public Iterable getCommands(String base) { @@ -89,8 +108,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { } @Override - public TraitManager getTraitManager() { - return traitManager; + public TraitFactory getTraitFactory() { + return traitFactory; } @Override @@ -170,7 +189,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { setupStorage(); npcRegistry = new CitizensNPCRegistry(saves); - traitManager = new CitizensTraitManager(this); + traitFactory = new CitizensTraitFactory(); selector = new NPCSelector(this); CitizensAPI.setImplementation(this); @@ -187,30 +206,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { public void run() { setupNPCs(); startMetrics(); - } - - private void startMetrics() { - new Thread() { - @Override - public void run() { - try { - Metrics metrics = new Metrics(Citizens.this); - if (metrics.isOptOut()) - return; - metrics.addCustomData(new Metrics.Plotter("Total NPCs") { - @Override - public int getValue() { - return Iterables.size(npcRegistry); - } - }); - characterManager.addPlotters(metrics); - metrics.start(); - Messaging.log("Metrics started."); - } catch (IOException e) { - Messaging.logF("Unable to start metrics: %s.", e.getMessage()); - } - } - }.start(); + enableSubPlugins(); } }) == -1) { Messaging.severe("Issue enabling plugin. Disabling."); @@ -251,15 +247,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { getServer().getPluginManager().callEvent(new CitizensReloadEvent()); } - private void despawnNPCs() { - Iterator itr = npcRegistry.iterator(); - while (itr.hasNext()) { - NPC npc = itr.next(); - itr.remove(); - npc.despawn(); - } - } - public void save() { for (NPC npc : npcRegistry) ((CitizensNPC) npc).save(saves.getKey("npc." + npc.getId())); @@ -287,7 +274,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { continue; } } - NPC npc = npcRegistry.createNPC(type, id, key.getString("name"), null); + NPC npc = npcRegistry.createNPC(type, id, key.getString("name")); ((CitizensNPC) npc).load(key); ++created; @@ -300,7 +287,11 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { private void setupScripting() { contextClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(getClassLoader()); - // workaround to fix scripts not loading plugin classes properly + // Workaround to fix scripts not loading plugin classes properly. + // The built in Sun Rhino Javascript engine uses the context classloader + // to search for class imports. Since the context classloader only has + // CraftBukkit classes, we replace it with a PluginClassLoader, which + // allows all plugin classes to be imported. } private void setupStorage() { @@ -324,6 +315,31 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { Messaging.logF("Save method set to %s.", saves.toString()); } + private void startMetrics() { + new Thread() { + @Override + public void run() { + try { + Metrics metrics = new Metrics(Citizens.this); + if (metrics.isOptOut()) + return; + metrics.addCustomData(new Metrics.Plotter("Total NPCs") { + @Override + public int getValue() { + return Iterables.size(npcRegistry); + } + }); + + traitFactory.addPlotters(metrics.createGraph("traits")); + metrics.start(); + Messaging.log("Metrics started."); + } catch (IOException e) { + Messaging.logF("Unable to start metrics: %s.", e.getMessage()); + } + } + }.start(); + } + private boolean suggestClosestModifier(CommandSender sender, String command, String modifier) { int minDist = Integer.MAX_VALUE; String closest = ""; diff --git a/src/main/java/net/citizensnpcs/EventListen.java b/src/main/java/net/citizensnpcs/EventListen.java index 7f970a4a4..92a5346b6 100644 --- a/src/main/java/net/citizensnpcs/EventListen.java +++ b/src/main/java/net/citizensnpcs/EventListen.java @@ -1,6 +1,5 @@ package net.citizensnpcs; -import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.event.NPCDamageByEntityEvent; import net.citizensnpcs.api.event.NPCDamageEvent; @@ -11,8 +10,6 @@ import net.citizensnpcs.api.npc.NPCRegistry; import net.citizensnpcs.editor.Editor; import net.citizensnpcs.npc.entity.EntityHumanNPC; import net.citizensnpcs.trait.CurrentLocation; -import net.citizensnpcs.trait.text.Text; -import net.citizensnpcs.util.Util; import org.bukkit.Bukkit; import org.bukkit.Chunk; @@ -52,7 +49,7 @@ public class EventListen implements Listener { if (!toRespawn.containsKey(coord)) return; for (int id : toRespawn.get(coord)) { - NPC npc = npcRegistry.getNPC(id); + NPC npc = npcRegistry.getById(id); npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); } toRespawn.removeAll(coord); @@ -96,11 +93,6 @@ public class EventListen implements Listener { // Call left-click event NPCLeftClickEvent leftClickEvent = new NPCLeftClickEvent(npc, damager); Bukkit.getPluginManager().callEvent(leftClickEvent); - if (leftClickEvent.isCancelled()) - return; - - if (npc.getCharacter() != null) - npc.getCharacter().onLeftClick(npc, damager); } else { Bukkit.getPluginManager().callEvent(new NPCDamageEvent(npc, event)); } @@ -125,15 +117,6 @@ public class EventListen implements Listener { // Call right-click event NPCRightClickEvent rightClickEvent = new NPCRightClickEvent(npc, player); Bukkit.getPluginManager().callEvent(rightClickEvent); - if (rightClickEvent.isCancelled()) - return; - // If the NPC isn't a close talker - // TODO: move this into text.class - if (Util.isSettingFulfilled(player, Setting.TALK_ITEM) && !npc.getTrait(Text.class).shouldTalkClose()) - npc.getTrait(Text.class).sendText(player); - - if (npc.getCharacter() != null) - npc.getCharacter().onRightClick(npc, player); } /* @@ -171,7 +154,7 @@ public class EventListen implements Listener { if (!event.getWorld().isChunkLoaded(chunk.first, chunk.second)) continue; for (int id : toRespawn.get(chunk)) { - NPC npc = npcRegistry.getNPC(id); + NPC npc = npcRegistry.getById(id); npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); } toRespawn.removeAll(chunk); diff --git a/src/main/java/net/citizensnpcs/Metrics.java b/src/main/java/net/citizensnpcs/Metrics.java index de58a4ad8..895675931 100644 --- a/src/main/java/net/citizensnpcs/Metrics.java +++ b/src/main/java/net/citizensnpcs/Metrics.java @@ -67,53 +67,6 @@ import org.bukkit.plugin.PluginDescriptionFile; */ public class Metrics { - /** - * The current revision number - */ - private final static int REVISION = 5; - - /** - * The base url of the metrics domain - */ - private static final String BASE_URL = "http://mcstats.org"; - - /** - * The url used to report a server's status - */ - private static final String REPORT_URL = "/report/%s"; - - /** - * The file where guid and opt out is stored in - */ - private static final String CONFIG_FILE = "plugins/PluginMetrics/config.yml"; - - /** - * The separator to use for custom data. This MUST NOT change unless you are - * hosting your own version of metrics and want to change it. - */ - private static final String CUSTOM_DATA_SEPARATOR = "~~"; - - /** - * Interval of time to ping (in minutes) - */ - private static final int PING_INTERVAL = 10; - - /** - * The plugin this metrics submits for - */ - private final Plugin plugin; - - /** - * All of the custom graphs to submit to metrics - */ - private final Set graphs = Collections.synchronizedSet(new HashSet()); - - /** - * The default graph, used for addCustomData when you don't want a specific - * graph - */ - private final Graph defaultGraph = new Graph("Default"); - /** * The plugin configuration file */ @@ -124,6 +77,17 @@ public class Metrics { */ private final File configurationFile; + /** + * The default graph, used for addCustomData when you don't want a specific + * graph + */ + private final Graph defaultGraph = new Graph("Default"); + + /** + * All of the custom graphs to submit to metrics + */ + private final Set graphs = Collections.synchronizedSet(new HashSet()); + /** * Unique server id */ @@ -134,6 +98,11 @@ public class Metrics { */ private final Object optOutLock = new Object(); + /** + * The plugin this metrics submits for + */ + private final Plugin plugin; + /** * Id of the scheduled task */ @@ -164,6 +133,23 @@ public class Metrics { guid = configuration.getString("guid"); } + /** + * Adds a custom data plotter to the default graph + * + * @param plotter + */ + public void addCustomData(final Plotter plotter) { + if (plotter == null) { + throw new IllegalArgumentException("Plotter cannot be null"); + } + + // Add the plotter to the graph o/ + defaultGraph.addPlotter(plotter); + + // Ensure the default graph is included in the submitted graphs + graphs.add(defaultGraph); + } + /** * Construct and create a Graph that can be used to separate specific * plotters to their own graphs on the metrics website. Plotters can be @@ -189,99 +175,27 @@ public class Metrics { } /** - * Adds a custom data plotter to the default graph + * Disables metrics for the server by setting "opt-out" to true in the + * config file and canceling the metrics task. * - * @param plotter + * @throws IOException */ - public void addCustomData(final Plotter plotter) { - if (plotter == null) { - throw new IllegalArgumentException("Plotter cannot be null"); - } - - // Add the plotter to the graph o/ - defaultGraph.addPlotter(plotter); - - // Ensure the default graph is included in the submitted graphs - graphs.add(defaultGraph); - } - - /** - * Start measuring statistics. This will immediately create an async - * repeating task as the plugin and send the initial data to the metrics - * backend, and then after that it will post in increments of PING_INTERVAL - * * 1200 ticks. - * - * @return True if statistics measuring is running, otherwise false. - */ - public boolean start() { + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the + // task. synchronized (optOutLock) { - // Did we opt out? - if (isOptOut()) { - return false; + // Check if the server owner has already set opt-out, if not, set + // it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); } - // Is metrics already running? - if (taskId >= 0) { - return true; + // Disable Task, if it is running + if (taskId > 0) { + this.plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; } - - // Begin hitting the server with glorious data - taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { - - private boolean firstPost = true; - - @Override - public void run() { - try { - // This has to be synchronized or it can collide with - // the disable method. - synchronized (optOutLock) { - // Disable Task, if it is running and the server - // owner decided to opt-out - if (isOptOut() && taskId > 0) { - plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; - } - } - - // We use the inverse of firstPost because if it is the - // first time we are posting, - // it is not a interval ping, so it evaluates to FALSE - // Each time thereafter it will evaluate to TRUE, i.e - // PING! - postPlugin(!firstPost); - - // After the first post we set firstPost to false - // Each post thereafter will be a ping - firstPost = false; - } catch (IOException e) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); - } - } - }, 0, PING_INTERVAL * 1200); - - return true; - } - } - - /** - * Has the server owner denied plugin metrics? - * - * @return - */ - public boolean isOptOut() { - synchronized (optOutLock) { - try { - // Reload the metrics file - configuration.load(CONFIG_FILE); - } catch (IOException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); - return true; - } catch (InvalidConfigurationException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); - return true; - } - return configuration.getBoolean("opt-out", false); } } @@ -310,27 +224,38 @@ public class Metrics { } /** - * Disables metrics for the server by setting "opt-out" to true in the - * config file and canceling the metrics task. + * Check if mineshafter is present. If it is, we need to bypass it to send + * POST requests * - * @throws IOException + * @return */ - public void disable() throws IOException { - // This has to be synchronized or it can collide with the check in the - // task. - synchronized (optOutLock) { - // Check if the server owner has already set opt-out, if not, set - // it. - if (!isOptOut()) { - configuration.set("opt-out", true); - configuration.save(configurationFile); - } + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } - // Disable Task, if it is running - if (taskId > 0) { - this.plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; + /** + * Has the server owner denied plugin metrics? + * + * @return + */ + public boolean isOptOut() { + synchronized (optOutLock) { + try { + // Reload the metrics file + configuration.load(CONFIG_FILE); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; } + return configuration.getBoolean("opt-out", false); } } @@ -444,51 +369,64 @@ public class Metrics { } /** - * Check if mineshafter is present. If it is, we need to bypass it to send - * POST requests + * Start measuring statistics. This will immediately create an async + * repeating task as the plugin and send the initial data to the metrics + * backend, and then after that it will post in increments of PING_INTERVAL + * * 1200 ticks. * - * @return + * @return True if statistics measuring is running, otherwise false. */ - private boolean isMineshafterPresent() { - try { - Class.forName("mineshafter.MineServer"); + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (taskId >= 0) { + return true; + } + + // Begin hitting the server with glorious data + taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { + + private boolean firstPost = true; + + @Override + public void run() { + try { + // This has to be synchronized or it can collide with + // the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server + // owner decided to opt-out + if (isOptOut() && taskId > 0) { + plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + } + } + + // We use the inverse of firstPost because if it is the + // first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e + // PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + }, 0, PING_INTERVAL * 1200); + return true; - } catch (Exception e) { - return false; } } - /** - *

- * Encode a key/value data pair to be used in a HTTP post request. This - * INCLUDES a & so the first key/value pair MUST be included manually, e.g: - *

- * - * StringBuffer data = new StringBuffer(); - * data.append(encode("guid")).append('=').append(encode(guid)); - * encodeDataPair(data, "version", description.getVersion()); - * - * - * @param buffer - * @param key - * @param value - * @return - */ - private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) - throws UnsupportedEncodingException { - buffer.append('&').append(encode(key)).append('=').append(encode(value)); - } - - /** - * Encode text as UTF-8 - * - * @param text - * @return - */ - private static String encode(final String text) throws UnsupportedEncodingException { - return URLEncoder.encode(text, "UTF-8"); - } - /** * Represents a custom graph on the website */ @@ -509,15 +447,6 @@ public class Metrics { this.name = name; } - /** - * Gets the graph's name - * - * @return - */ - public String getName() { - return name; - } - /** * Add a plotter to the graph, which will be used to plot entries * @@ -527,13 +456,23 @@ public class Metrics { plotters.add(plotter); } + @Override + public boolean equals(final Object object) { + if (!(object instanceof Graph)) { + return false; + } + + final Graph graph = (Graph) object; + return graph.name.equals(name); + } + /** - * Remove a plotter from the graph + * Gets the graph's name * - * @param plotter + * @return */ - public void removePlotter(final Plotter plotter) { - plotters.remove(plotter); + public String getName() { + return name; } /** @@ -550,14 +489,13 @@ public class Metrics { return name.hashCode(); } - @Override - public boolean equals(final Object object) { - if (!(object instanceof Graph)) { - return false; - } - - final Graph graph = (Graph) object; - return graph.name.equals(name); + /** + * Remove a plotter from the graph + * + * @param plotter + */ + public void removePlotter(final Plotter plotter) { + plotters.remove(plotter); } } @@ -588,12 +526,15 @@ public class Metrics { this.name = name; } - /** - * Get the current value for the plotted point - * - * @return - */ - public abstract int getValue(); + @Override + public boolean equals(final Object object) { + if (!(object instanceof Plotter)) { + return false; + } + + final Plotter plotter = (Plotter) object; + return plotter.name.equals(name) && plotter.getValue() == getValue(); + } /** * Get the column name for the plotted point @@ -605,26 +546,85 @@ public class Metrics { } /** - * Called after the website graphs have been updated + * Get the current value for the plotted point + * + * @return */ - public void reset() { - } + public abstract int getValue(); @Override public int hashCode() { return getColumnName().hashCode() + getValue(); } - @Override - public boolean equals(final Object object) { - if (!(object instanceof Plotter)) { - return false; - } - - final Plotter plotter = (Plotter) object; - return plotter.name.equals(name) && plotter.getValue() == getValue(); + /** + * Called after the website graphs have been updated + */ + public void reset() { } } + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://mcstats.org"; + + /** + * The file where guid and opt out is stored in + */ + private static final String CONFIG_FILE = "plugins/PluginMetrics/config.yml"; + + /** + * The separator to use for custom data. This MUST NOT change unless you are + * hosting your own version of metrics and want to change it. + */ + private static final String CUSTOM_DATA_SEPARATOR = "~~"; + + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 10; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/report/%s"; + + /** + * The current revision number + */ + private final static int REVISION = 5; + + /** + * Encode text as UTF-8 + * + * @param text + * @return + */ + private static String encode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + + /** + *

+ * Encode a key/value data pair to be used in a HTTP post request. This + * INCLUDES a & so the first key/value pair MUST be included manually, e.g: + *

+ * + * StringBuffer data = new StringBuffer(); + * data.append(encode("guid")).append('=').append(encode(guid)); + * encodeDataPair(data, "version", description.getVersion()); + * + * + * @param buffer + * @param key + * @param value + * @return + */ + private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) + throws UnsupportedEncodingException { + buffer.append('&').append(encode(key)).append('=').append(encode(value)); + } + } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/Settings.java b/src/main/java/net/citizensnpcs/Settings.java index 65f39f97a..bc26cd53e 100644 --- a/src/main/java/net/citizensnpcs/Settings.java +++ b/src/main/java/net/citizensnpcs/Settings.java @@ -23,9 +23,9 @@ public class Settings { for (Setting setting : Setting.values()) { if (!root.keyExists(setting.path)) { Messaging.logF("Writing default setting: '%s'", setting.path); - setting.set(root); + setting.setAtKey(root); } else - setting.load(root); + setting.loadFromKey(root); } save(); @@ -35,7 +35,7 @@ public class Settings { config.load(); for (Setting setting : Setting.values()) if (root.keyExists(setting.path)) - setting.load(root); + setting.loadFromKey(root); save(); } @@ -56,7 +56,7 @@ public class Settings { DEFAULT_TALK_CLOSE("npc.default.talk-close", false), DEFAULT_TEXT("npc.default.text.0", "Hi, I'm !") { @Override - public void load(DataKey root) { + public void loadFromKey(DataKey root) { List list = new ArrayList(); for (DataKey key : root.getRelative("npc.default.text").getSubKeys()) list.add(key.getString("")); @@ -69,6 +69,7 @@ public class Settings { SERVER_OWNS_NPCS("npc.server-ownership", false), STORAGE_FILE("storage.file", "saves.yml"), STORAGE_TYPE("storage.type", "yaml"), + SUBPLUGIN_FOLDER("subplugins.folder", "plugins"), TALK_CLOSE_MAXIMUM_COOLDOWN("npc.text.max-talk-cooldown", 60), TALK_CLOSE_MINIMUM_COOLDOWN("npc.text.min-talk-cooldown", 30), TALK_ITEM("npc.text.talk-item", "340"); @@ -109,12 +110,12 @@ public class Settings { return value.toString(); } - protected void set(DataKey root) { - root.setRaw(path, value); + protected void loadFromKey(DataKey root) { + value = root.getRaw(path); } - protected void load(DataKey root) { - value = root.getRaw(path); + protected void setAtKey(DataKey root) { + root.setRaw(path, value); } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/command/CommandManager.java b/src/main/java/net/citizensnpcs/command/CommandManager.java index 26e95359d..8a3696210 100644 --- a/src/main/java/net/citizensnpcs/command/CommandManager.java +++ b/src/main/java/net/citizensnpcs/command/CommandManager.java @@ -198,6 +198,11 @@ public class CommandManager { || commands.containsKey(command.toLowerCase() + " *"); } + // Returns whether a player has permission. + private boolean hasPermission(CommandSender sender, String perm) { + return sender.hasPermission("citizens." + perm); + } + // Returns whether a player has access to a command. private boolean hasPermission(Method method, CommandSender sender) { Command cmd = method.getAnnotation(Command.class); @@ -207,11 +212,6 @@ public class CommandManager { return false; } - // Returns whether a player has permission. - private boolean hasPermission(CommandSender sender, String perm) { - return sender.hasPermission("citizens." + perm); - } - /* * Register an class that contains commands (denoted by Command. If no * dependency injector is specified, then the methods of the class will be diff --git a/src/main/java/net/citizensnpcs/command/Requirements.java b/src/main/java/net/citizensnpcs/command/Requirements.java index b2afbfd9f..9b78ea1c6 100644 --- a/src/main/java/net/citizensnpcs/command/Requirements.java +++ b/src/main/java/net/citizensnpcs/command/Requirements.java @@ -8,11 +8,11 @@ import org.bukkit.entity.EntityType; @Retention(RetentionPolicy.RUNTIME) public @interface Requirements { + EntityType[] excludedTypes() default { EntityType.UNKNOWN }; + boolean ownership() default false; boolean selected() default false; EntityType[] types() default { EntityType.UNKNOWN }; - - EntityType[] excludedTypes() default { EntityType.UNKNOWN }; } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/command/command/HelpCommands.java b/src/main/java/net/citizensnpcs/command/command/HelpCommands.java index d73e0d434..88739d326 100644 --- a/src/main/java/net/citizensnpcs/command/command/HelpCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/HelpCommands.java @@ -59,24 +59,6 @@ public class HelpCommands { return lines; } - @Command( - aliases = { "script" }, - usage = "help (page)", - desc = "Script help menu", - modifiers = { "help" }, - min = 1, - max = 2, - permission = "script.help") - @Requirements - public void scriptHelp(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - int page = args.argsLength() == 2 ? args.getInteger(1) : 1; - Paginator paginator = new Paginator().header("Script Help"); - for (String line : getLines(sender, "script")) - paginator.addLine(line); - if (!paginator.sendPage(sender, page)) - throw new CommandException("The page '" + page + "' does not exist."); - } - @Command( aliases = { "npc" }, usage = "help (page)", @@ -94,4 +76,22 @@ public class HelpCommands { if (!paginator.sendPage(sender, page)) throw new CommandException("The page '" + page + "' does not exist."); } + + @Command( + aliases = { "script" }, + usage = "help (page)", + desc = "Script help menu", + modifiers = { "help" }, + min = 1, + max = 2, + permission = "script.help") + @Requirements + public void scriptHelp(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + int page = args.argsLength() == 2 ? args.getInteger(1) : 1; + Paginator paginator = new Paginator().header("Script Help"); + for (String line : getLines(sender, "script")) + paginator.addLine(line); + if (!paginator.sendPage(sender, page)) + throw new CommandException("The page '" + page + "' does not exist."); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java index 7eba59512..ca54d08d7 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -8,8 +8,7 @@ import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPCRegistry; -import net.citizensnpcs.api.npc.character.Character; -import net.citizensnpcs.api.npc.character.CharacterManager; +import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.trait.trait.Owner; import net.citizensnpcs.api.trait.trait.Spawned; @@ -26,15 +25,14 @@ import net.citizensnpcs.trait.CurrentLocation; import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.trait.Powered; import net.citizensnpcs.trait.VillagerProfession; -import net.citizensnpcs.trait.text.Text; import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.Paginator; import net.citizensnpcs.util.StringHelper; import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.Location; import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Ageable; import org.bukkit.entity.EntityType; @@ -46,7 +44,6 @@ import com.google.common.base.Splitter; @Requirements(selected = true, ownership = true) public class NPCCommands { - private final CharacterManager characterManager = CitizensAPI.getCharacterManager(); private final NPCRegistry npcRegistry; private final NPCSelector selector; @@ -64,8 +61,8 @@ public class NPCCommands { min = 1, max = 2, permission = "npc.age") - @Requirements(selected = true, ownership = true, types = { EntityType.CHICKEN, EntityType.COW, EntityType.OCELOT, - EntityType.PIG, EntityType.SHEEP, EntityType.VILLAGER, EntityType.WOLF }) + @Requirements(selected = true, ownership = true, types = { EntityType.CHICKEN, EntityType.COW, + EntityType.OCELOT, EntityType.PIG, EntityType.SHEEP, EntityType.VILLAGER, EntityType.WOLF }) public void age(CommandContext args, CommandSender sender, NPC npc) throws CommandException { Age trait = npc.getTrait(Age.class); @@ -93,8 +90,13 @@ public class NPCCommands { Messaging.send(sender, "Age " + (trait.toggle() ? "locked" : "unlocked") + "."); } - @Command(aliases = { "npc" }, usage = "behaviour [scripts]", desc = "Sets the behaviour of a NPC", modifiers = { - "behaviour", "ai" }, min = 2, max = -1) + @Command( + aliases = { "npc" }, + usage = "behaviour [scripts]", + desc = "Sets the behaviour of a NPC", + modifiers = { "behaviour", "ai" }, + min = 2, + max = -1) public void behaviour(CommandContext args, CommandSender sender, NPC npc) throws CommandException { Iterable files = Splitter.on(',').split(args.getJoinedStrings(1, ',')); npc.getTrait(Behaviour.class).addScripts(files); @@ -103,31 +105,19 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "character [character]", - desc = "Set the character of a NPC", - modifiers = { "character" }, - min = 2, - max = 2) - public void character(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - String name = args.getString(1).toLowerCase(); - Character character = characterManager.getCharacter(name); - - if (character == null) - throw new CommandException("The character '" + args.getString(1) + "' does not exist."); - if (npc.getCharacter() != null && npc.getCharacter().getName().equalsIgnoreCase(character.getName())) - throw new CommandException("The NPC already has the character '" + name + "'."); - if (!sender.hasPermission("citizens.npc.character." + character.getName()) - && !sender.hasPermission("citizens.npc.character.*") && !sender.hasPermission("citizens.admin")) - throw new NoPermissionsException(); - - EntityType type = EntityType.valueOf(npc.getTrait(MobType.class).getType()); - if (!character.getValidTypes().isEmpty() && !character.getValidTypes().contains(type)) { - Messaging.sendError(sender, "This NPC cannot be given the character '" + character.getName() + "'."); - return; + usage = "controllable", + desc = "Toggles whether the NPC can be ridden and controlled", + modifiers = { "controllable" }, + min = 1, + max = 1, + permission = "npc.controllable") + public void controllable(CommandContext args, CommandSender sender, NPC npc) { + boolean enabled = npc.getTrait(Controllable.class).toggle(); + if (enabled) { + Messaging.send(sender, StringHelper.wrap(npc.getName()) + " can now be controlled."); + } else { + Messaging.send(sender, StringHelper.wrap(npc.getName()) + " can no longer be controlled."); } - Messaging.send(sender, StringHelper.wrap(npc.getName() + "'s") + " character is now " + StringHelper.wrap(name) - + "."); - npc.setCharacter(character); } @Command( @@ -143,7 +133,8 @@ public class NPCCommands { public void create(CommandContext args, final Player player, NPC npc) { String name = args.getString(1); if (name.length() > 16) { - Messaging.sendError(player, "NPC names cannot be longer than 16 characters. The name has been shortened."); + Messaging.sendError(player, + "NPC names cannot be longer than 16 characters. The name has been shortened."); name = name.substring(0, 15); } EntityType type = EntityType.PLAYER; @@ -156,25 +147,8 @@ public class NPCCommands { } } npc = npcRegistry.createNPC(type, name); - String msg = ChatColor.GREEN + "You created " + StringHelper.wrap(npc.getName()); - if (args.hasValueFlag("char")) { - String character = args.getFlag("char").toLowerCase(); - if (characterManager.getCharacter(character) == null) { - Messaging.sendError(player, "'" + args.getFlag("char") + "' is not a valid character."); - return; - } else { - Character set = characterManager.getCharacter(character); - if (!set.getValidTypes().isEmpty() && !set.getValidTypes().contains(type)) { - Messaging.sendError(player, "The character '" + set.getName() + "' cannot be given the mob type '" - + type.name().toLowerCase() + "'."); - npc.remove(); - return; - } - npc.setCharacter(characterManager.getCharacter(character)); - msg += " with the character " + StringHelper.wrap(character); - } - } - msg += " at your location"; + String msg = ChatColor.GREEN + "You created " + StringHelper.wrap(npc.getName()) + + " at your location"; int age = 0; if (args.hasFlag('b')) { @@ -195,32 +169,36 @@ public class NPCCommands { msg += "."; // Initialize necessary traits - npc.addTrait(Owner.class); if (!Setting.SERVER_OWNS_NPCS.asBoolean()) npc.getTrait(Owner.class).setOwner(player.getName()); npc.getTrait(MobType.class).setType(type.toString()); - npc.addTrait(LookClose.class); - npc.addTrait(Text.class); npc.spawn(player.getLocation()); // Set age after entity spawns if (npc.getBukkitEntity() instanceof Ageable) npc.getTrait(Age.class).setAge(age); - selector.select(player, npc); Messaging.send(player, msg); } @Command( aliases = { "npc" }, - usage = "despawn", + usage = "despawn (id)", desc = "Despawn a NPC", modifiers = { "despawn" }, min = 1, - max = 1, + max = 2, permission = "npc.despawn") - public void despawn(CommandContext args, CommandSender sender, NPC npc) { + @Requirements + public void despawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + if (npc == null || args.argsLength() == 2) { + if (args.argsLength() < 2) + throw new CommandException("No NPC selected."); + npc = CitizensAPI.getNPCRegistry().getById(args.getInteger(2)); + if (npc == null) + throw new CommandException("No NPC found with that ID."); + } npc.getTrait(Spawned.class).setSpawned(false); npc.despawn(); Messaging.send(sender, ChatColor.GREEN + "You despawned " + StringHelper.wrap(npc.getName()) + "."); @@ -267,17 +245,6 @@ public class NPCCommands { npcs.add(add); } } - - if (args.hasValueFlag("char")) { - String character = args.getFlag("char"); - if (characterManager.getCharacter(character) == null) - throw new CommandException("'" + character + "' is not a valid character."); - - for (NPC add : npcRegistry.getNPCs(characterManager.getCharacter(character).getClass())) { - if (!npcs.contains(add)) - npcs.add(add); - } - } } Paginator paginator = new Paginator().header("NPCs"); @@ -308,26 +275,23 @@ public class NPCCommands { Messaging.send(sender, msg + " when a player is nearby."); } - @Command(aliases = { "npc" }, desc = "Show basic NPC information", max = 0) - public void npc(CommandContext args, CommandSender sender, NPC npc) { - Messaging.send(sender, StringHelper.wrapHeader(npc.getName())); - Messaging.send(sender, " ID: " + npc.getId()); - Messaging.send(sender, " Character: " - + (npc.getCharacter() != null ? npc.getCharacter().getName() : "None")); - Messaging.send(sender, " Type: " + npc.getTrait(MobType.class).getType()); - } - - @Command(aliases = { "npc" }, usage = "moveto", desc = "Teleports a NPC to a given location", modifiers = "moveto", - min = 1, max = 1, permission = "npc.moveto") + @Command( + aliases = { "npc" }, + usage = "moveto", + desc = "Teleports a NPC to a given location", + modifiers = "moveto", + min = 1, + max = 1, + permission = "npc.moveto") public void moveto(CommandContext args, CommandSender sender, NPC npc) throws CommandException { // Spawn the NPC if it isn't spawned to prevent NPEs if (!npc.isSpawned()) npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); Location current = npc.getBukkitEntity().getLocation(); Location to = current.clone(); - if (args.hasValueFlag("x")) + if (args.hasValueFlag("x")) to.setX(args.getFlagInteger("x")); - if (args.hasValueFlag("y")) + if (args.hasValueFlag("y")) to.setY(args.getFlagInteger("y")); if (args.hasValueFlag("z")) to.setZ(args.getFlagInteger("z")); @@ -335,16 +299,24 @@ public class NPCCommands { to.setYaw((float) args.getFlagDouble("yaw")); if (args.hasValueFlag("pitch")) to.setPitch((float) args.getFlagDouble("pitch")); - if (args.hasValueFlag("world")){ + if (args.hasValueFlag("world")) { World world = Bukkit.getWorld(args.getFlag("world")); if (world == null) throw new CommandException("Given world not found."); to.setWorld(world); } + npc.getBukkitEntity().teleport(to, TeleportCause.COMMAND); Messaging.send(sender, StringHelper.wrap(npc.getName()) + " was teleported to " + to + "."); } - + + @Command(aliases = { "npc" }, desc = "Show basic NPC information", max = 0) + public void npc(CommandContext args, CommandSender sender, NPC npc) { + Messaging.send(sender, StringHelper.wrapHeader(npc.getName())); + Messaging.send(sender, " ID: " + npc.getId()); + Messaging.send(sender, " Type: " + npc.getTrait(MobType.class).getType()); + } + @Command( aliases = { "npc" }, usage = "owner [name]", @@ -394,11 +366,10 @@ public class NPCCommands { public void profession(CommandContext args, CommandSender sender, NPC npc) throws CommandException { String profession = args.getString(1); try { - npc.getTrait(VillagerProfession.class).setProfession(Profession.valueOf(profession.toUpperCase())); - Messaging.send( - sender, - StringHelper.wrap(npc.getName()) + " is now the profession " - + StringHelper.wrap(profession.toUpperCase()) + "."); + npc.getTrait(VillagerProfession.class) + .setProfession(Profession.valueOf(profession.toUpperCase())); + Messaging.send(sender, StringHelper.wrap(npc.getName()) + " is now the profession " + + StringHelper.wrap(profession.toUpperCase()) + "."); } catch (IllegalArgumentException ex) { throw new CommandException("'" + profession + "' is not a valid profession."); } @@ -431,7 +402,7 @@ public class NPCCommands { throw new CommandException("You must be the owner of this NPC to execute that command."); if (!player.hasPermission("citizens.npc.remove") && !player.hasPermission("citizens.admin")) throw new NoPermissionsException(); - npc.remove(); + npc.destroy(); Messaging.send(player, "You permanently removed " + StringHelper.wrap(npc.getName()) + "."); } @@ -447,11 +418,13 @@ public class NPCCommands { String oldName = npc.getName(); String newName = args.getString(1); if (newName.length() > 16) { - Messaging.sendError(sender, "NPC names cannot be longer than 16 characters. The name has been shortened."); + Messaging.sendError(sender, + "NPC names cannot be longer than 16 characters. The name has been shortened."); newName = newName.substring(0, 15); } npc.setName(newName); - String msg = String.format("You renamed %s to %s.", StringHelper.wrap(oldName), StringHelper.wrap(newName)); + String msg = String.format("You renamed %s to %s.", StringHelper.wrap(oldName), + StringHelper.wrap(newName)); Messaging.send(sender, ChatColor.GREEN + msg); } @@ -465,7 +438,7 @@ public class NPCCommands { permission = "npc.select") @Requirements(ownership = true) public void select(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - NPC toSelect = npcRegistry.getNPC(args.getInteger(1)); + NPC toSelect = npcRegistry.getById(args.getInteger(1)); if (toSelect == null || !toSelect.getTrait(Spawned.class).shouldSpawn()) throw new CommandException("No NPC with the ID '" + args.getInteger(1) + "' is spawned."); if (npc != null && toSelect.getId() == npc.getId()) @@ -482,38 +455,24 @@ public class NPCCommands { min = 2, max = 2, permission = "npc.spawn") - @Requirements - public void spawn(CommandContext args, Player player, NPC npc) throws CommandException { - NPC respawn = npcRegistry.getNPC(args.getInteger(1)); + @Requirements(ownership = true) + public void spawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + NPC respawn = npcRegistry.getById(args.getInteger(1)); if (respawn == null) throw new CommandException("No NPC with the ID '" + args.getInteger(1) + "' exists."); - - if (!respawn.getTrait(Owner.class).isOwnedBy(player)) - throw new CommandException("You must be the owner of this NPC to execute that command."); - - if (respawn.spawn(player.getLocation())) { - selector.select(player, respawn); - Messaging.send(player, ChatColor.GREEN + "You respawned " + StringHelper.wrap(respawn.getName()) - + " at your location."); - } else + if (respawn.isSpawned()) throw new CommandException(respawn.getName() + " is already spawned at another location." + " Use '/npc tphere' to teleport the NPC to your location."); - } - @Command( - aliases = { "npc" }, - usage = "controllable", - desc = "Toggles whether the NPC can be ridden and controlled", - modifiers = { "controllable" }, - min = 1, - max = 1, - permission = "npc.controllable") - public void controllable(CommandContext args, CommandSender sender, NPC npc) { - boolean enabled = npc.getTrait(Controllable.class).toggle(); - if (enabled) { - Messaging.send(sender, StringHelper.wrap(npc.getName()) + " can now be controlled."); - } else { - Messaging.send(sender, StringHelper.wrap(npc.getName()) + " can no longer be controlled."); + Location location = respawn.getTrait(CurrentLocation.class).getLocation(); + if (location == null && sender instanceof Player) + location = ((Player) sender).getLocation(); + else + throw new CommandException("No stored location available - command must be used ingame."); + if (respawn.spawn(location)) { + selector.select(sender, respawn); + Messaging.send(sender, ChatColor.GREEN + "You respawned " + StringHelper.wrap(respawn.getName()) + + " at your location."); } } @@ -530,11 +489,12 @@ public class NPCCommands { if (!npc.isSpawned()) npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); player.teleport(npc.getBukkitEntity(), TeleportCause.COMMAND); - Messaging.send(player, ChatColor.GREEN + "You teleported to " + StringHelper.wrap(npc.getName()) + "."); + Messaging.send(player, ChatColor.GREEN + "You teleported to " + StringHelper.wrap(npc.getName()) + + "."); } - @Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = { "tphere", - "move" }, min = 1, max = 1, permission = "npc.tphere") + @Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = { + "tphere", "move" }, min = 1, max = 1, permission = "npc.tphere") public void tphere(CommandContext args, Player player, NPC npc) { // Spawn the NPC if it isn't spawned to prevent NPEs if (!npc.isSpawned()) @@ -542,4 +502,21 @@ public class NPCCommands { npc.getBukkitEntity().teleport(player, TeleportCause.COMMAND); Messaging.send(player, StringHelper.wrap(npc.getName()) + " was teleported to your location."); } + + @Command( + aliases = { "npc" }, + usage = "trait [trait name]", + desc = "Adds a trait to the NPC", + modifiers = { "trait" }, + min = 2, + max = 2, + permission = "npc.trait") + public void trait(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + Trait trait = CitizensAPI.getTraitFactory().getTrait(args.getString(1)); + if (trait == null) + throw new CommandException("Trait not found."); + npc.addTrait(trait); + Messaging.sendF(sender, ChatColor.GREEN + "Trait %s added successfully.", + StringHelper.wrap(trait.getName())); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/command/command/ScriptCommands.java b/src/main/java/net/citizensnpcs/command/command/ScriptCommands.java index 29c07ee6a..7c2bbb4fa 100644 --- a/src/main/java/net/citizensnpcs/command/command/ScriptCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/ScriptCommands.java @@ -37,6 +37,10 @@ public class ScriptCommands { if (!file.exists()) throw new CommandException("The file '" + args.getString(1) + "' doesn't exist!"); CitizensAPI.getScriptCompiler().compile(file).withCallback(new CompileCallback() { + @Override + public void onCompileTaskFinished() { + } + @Override public void onScriptCompiled(ScriptFactory script) { Script s = script.newInstance(); @@ -47,10 +51,6 @@ public class ScriptCommands { } Messaging.send(sender, "Done."); } - - @Override - public void onCompileTaskFinished() { - } }).begin(); sender.sendMessage("Compiling..."); } diff --git a/src/main/java/net/citizensnpcs/npc/CitizensCharacterManager.java b/src/main/java/net/citizensnpcs/npc/CitizensCharacterManager.java deleted file mode 100644 index eb77f9936..000000000 --- a/src/main/java/net/citizensnpcs/npc/CitizensCharacterManager.java +++ /dev/null @@ -1,47 +0,0 @@ -package net.citizensnpcs.npc; - -import java.util.HashMap; -import java.util.Map; - -import net.citizensnpcs.Metrics; -import net.citizensnpcs.Metrics.Graph; -import net.citizensnpcs.api.CitizensAPI; -import net.citizensnpcs.api.exception.CharacterException; -import net.citizensnpcs.api.npc.character.Character; -import net.citizensnpcs.api.npc.character.CharacterFactory; -import net.citizensnpcs.api.npc.character.CharacterManager; -import net.citizensnpcs.util.StringHelper; - -public class CitizensCharacterManager implements CharacterManager { - private final Map registered = new HashMap(); - - @Override - public Character getCharacter(String name) { - return registered.get(name); - } - - @Override - public void registerCharacter(CharacterFactory factory) { - try { - Character character = factory.create(); - registered.put(character.getName(), character); // TODO: this only - // allows singletons - // for characters. - } catch (CharacterException ex) { - ex.printStackTrace(); - } - } - - public void addPlotters(Metrics metrics) { - Graph graph = metrics.createGraph("Character Type Usage"); - for (final Character character : registered.values()) { - graph.addPlotter(new Metrics.Plotter(StringHelper.capitalize(character.getName())) { - @Override - public int getValue() { - return CitizensAPI.getNPCRegistry().getNPCs(character.getClass()).size(); - } - }); - } - - } -} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index dc16732c9..9e0c8c6e7 100644 --- a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -1,48 +1,31 @@ package net.citizensnpcs.npc; -import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.ai.Navigator; import net.citizensnpcs.api.event.NPCDespawnEvent; import net.citizensnpcs.api.event.NPCSpawnEvent; import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.npc.AbstractNPC; -import net.citizensnpcs.api.npc.character.Character; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.trait.Spawned; import net.citizensnpcs.api.util.DataKey; -import net.citizensnpcs.npc.ai.CitizensAI; +import net.citizensnpcs.npc.ai.CitizensNavigator; import net.citizensnpcs.trait.CurrentLocation; import net.citizensnpcs.util.Messaging; -import net.citizensnpcs.util.StringHelper; import net.minecraft.server.EntityLiving; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; +import org.bukkit.metadata.FixedMetadataValue; public abstract class CitizensNPC extends AbstractNPC { - private final CitizensAI ai = new CitizensAI(this); protected EntityLiving mcEntity; - private final CitizensTraitManager traitManager; + private final CitizensNavigator navigator = new CitizensNavigator(this); protected CitizensNPC(int id, String name) { super(id, name); - traitManager = (CitizensTraitManager) CitizensAPI.getTraitManager(); - // TODO: remove this dependency - } - - @Override - public void chat(Player player, String message) { - Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + message, this); - } - - @Override - public void chat(String message) { - for (Player player : Bukkit.getOnlinePlayers()) - chat(player, message); } protected abstract EntityLiving createHandle(Location loc); @@ -57,18 +40,13 @@ public abstract class CitizensNPC extends AbstractNPC { Bukkit.getPluginManager().callEvent(new NPCDespawnEvent(this)); boolean keepSelected = getTrait(Spawned.class).shouldSpawn(); if (!keepSelected) - removeMetadata("selectors", CitizensAPI.getPlugin()); + data().remove("selectors"); getBukkitEntity().remove(); mcEntity = null; return true; } - @Override - public CitizensAI getAI() { - return ai; - } - @Override public LivingEntity getBukkitEntity() { return (LivingEntity) getHandle().getBukkitEntity(); @@ -79,15 +57,13 @@ public abstract class CitizensNPC extends AbstractNPC { } @Override - public org.bukkit.inventory.Inventory getInventory() { - Inventory inventory = Bukkit.getServer().createInventory(this, 36, StringHelper.parseColors(getFullName())); - inventory.setContents(getTrait(net.citizensnpcs.api.trait.trait.Inventory.class).getContents()); - return inventory; + public Navigator getNavigator() { + return navigator; } @Override public Trait getTraitFor(Class clazz) { - return traitManager.getTrait(clazz, this); + return CitizensAPI.getTraitFactory().getTrait(clazz); } @Override @@ -96,30 +72,18 @@ public abstract class CitizensNPC extends AbstractNPC { } public void load(DataKey root) { - Character character = CitizensAPI.getCharacterManager().getCharacter(root.getString("character")); - - // Load the character if it exists - if (character != null) { - try { - character.load(root.getRelative("characters." + character.getName())); - } catch (NPCLoadException e) { - Messaging.severe(String.format("Unable to load character '%s': %s.", character.getName(), - e.getMessage())); - } - setCharacter(character); - } - // Load traits for (DataKey traitKey : root.getRelative("traits").getSubKeys()) { - Trait trait = traitManager.getTrait(traitKey.name(), this); + Trait trait = CitizensAPI.getTraitFactory().getTrait(traitKey.name()); if (trait == null) { - Messaging.severeF("Skipped missing trait '%s' while loading NPC ID: '%d'. Has the name changed?", + Messaging.severeF( + "Skipped missing trait '%s' while loading NPC ID: '%d'. Has the name changed?", traitKey.name(), getId()); continue; } addTrait(trait); try { - getTrait(trait.getClass()).load(traitKey); + trait.load(traitKey); } catch (NPCLoadException ex) { Messaging.logF("The trait '%s' failed to load for NPC ID: '%d'.", traitKey.name(), getId(), ex.getMessage()); @@ -134,32 +98,15 @@ public abstract class CitizensNPC extends AbstractNPC { } } - @Override - public void remove() { - super.remove(); - CitizensAPI.getNPCRegistry().deregister(this); - } - public void save(DataKey root) { root.setString("name", getFullName()); - // Save the character if it exists - if (getCharacter() != null) { - root.setString("character", getCharacter().getName()); - getCharacter().save(root.getRelative("characters." + getCharacter().getName())); - } - // Save all existing traits for (Trait trait : traits.values()) { trait.save(root.getRelative("traits." + trait.getName())); } } - @Override - public void setName(String name) { - super.setName(name); - } - @Override public boolean spawn(Location loc) { Validate.notNull(loc, "location cannot be null"); @@ -176,6 +123,8 @@ public abstract class CitizensNPC extends AbstractNPC { mcEntity.world.addEntity(mcEntity); mcEntity.world.players.remove(mcEntity); + getBukkitEntity().setMetadata(NPC_METADATA_MARKER, + new FixedMetadataValue(CitizensAPI.getPlugin(), true)); // Set the spawned state getTrait(CurrentLocation.class).setLocation(loc); @@ -183,7 +132,7 @@ public abstract class CitizensNPC extends AbstractNPC { // Modify NPC using traits after the entity has been created for (Trait trait : traits.values()) - trait.onNPCSpawn(); + trait.onSpawn(); return true; } @@ -191,10 +140,13 @@ public abstract class CitizensNPC extends AbstractNPC { public void update() { try { super.update(); - ai.update(); + if (isSpawned()) + navigator.update(); } catch (Exception ex) { Messaging.logF("Exception while updating %d: %s.", getId(), ex.getMessage()); ex.printStackTrace(); } } + + private static final String NPC_METADATA_MARKER = "NPC"; } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java b/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java index abd778c1c..a7ebc7b6b 100644 --- a/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java +++ b/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java @@ -1,17 +1,13 @@ package net.citizensnpcs.npc; -import java.util.ArrayList; -import java.util.Collection; import java.util.EnumMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPCRegistry; -import net.citizensnpcs.api.npc.character.Character; import net.citizensnpcs.api.util.Storage; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.npc.entity.CitizensBlazeNPC; import net.citizensnpcs.npc.entity.CitizensCaveSpiderNPC; import net.citizensnpcs.npc.entity.CitizensChickenNPC; @@ -82,67 +78,17 @@ public class CitizensNPCRegistry implements NPCRegistry { types.put(EntityType.ZOMBIE, CitizensZombieNPC.class); } - public NPC createNPC(EntityType type, int id, String name, Character character) { + public NPC createNPC(EntityType type, int id, String name) { CitizensNPC npc = getByType(type, id, name); if (npc == null) throw new IllegalStateException("Could not create NPC."); - if (character != null) - npc.setCharacter(character); npcs.put(npc.getId(), npc); return npc; } @Override public NPC createNPC(EntityType type, String name) { - return createNPC(type, name, null); - } - - @Override - public NPC createNPC(EntityType type, String name, Character character) { - return createNPC(type, generateUniqueId(), name, character); - } - - private int generateUniqueId() { - int count = 0; - while (getNPC(count++) != null) - ; // TODO: doesn't respect existing save data that might not have - // been loaded. This causes DBs with NPCs that weren't loaded to - // have conflicting primary keys. - return count - 1; - } - - @Override - public NPC getNPC(Entity entity) { - Validate.notNull(entity); - net.minecraft.server.Entity handle = ((CraftEntity) entity).getHandle(); - return handle instanceof NPCHandle ? ((NPCHandle) handle).getNPC() : null; - } - - @Override - public NPC getNPC(int id) { - if (id < 0) - throw new IllegalArgumentException("invalid id"); - return npcs.get(id); - } - - @Override - public Collection getNPCs(Class character) { - List npcs = new ArrayList(); - for (NPC npc : this) { - if (npc.getCharacter() != null && npc.getCharacter().getClass().equals(character)) - npcs.add(npc); - } - return npcs; - } - - @Override - public boolean isNPC(Entity entity) { - return getNPC(entity) != null; - } - - @Override - public Iterator iterator() { - return npcs.iterator(); + return createNPC(type, generateUniqueId(), name); } @Override @@ -163,6 +109,22 @@ public class CitizensNPCRegistry implements NPCRegistry { } } + private int generateUniqueId() { + int count = 0; + while (getById(count++) != null) + ; // TODO: doesn't respect existing save data that might not have + // been loaded. This causes DBs with NPCs that weren't loaded to + // have conflicting primary keys. + return count - 1; + } + + @Override + public NPC getById(int id) { + if (id < 0) + throw new IllegalArgumentException("invalid id"); + return npcs.get(id); + } + private CitizensNPC getByType(EntityType type, int id, String name) { Class npcClass = types.get(type); if (npcClass == null) @@ -173,4 +135,21 @@ public class CitizensNPCRegistry implements NPCRegistry { return null; } } + + @Override + public NPC getNPC(Entity entity) { + Validate.notNull(entity); + net.minecraft.server.Entity handle = ((CraftEntity) entity).getHandle(); + return handle instanceof NPCHolder ? ((NPCHolder) handle).getNPC() : null; + } + + @Override + public boolean isNPC(Entity entity) { + return getNPC(entity) != null; + } + + @Override + public Iterator iterator() { + return npcs.iterator(); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java new file mode 100644 index 000000000..dfc48bce5 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -0,0 +1,104 @@ +package net.citizensnpcs.npc; + +import java.util.Map; + +import net.citizensnpcs.Metrics; +import net.citizensnpcs.Metrics.Graph; +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.npc.NPC; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.trait.TraitInfo; +import net.citizensnpcs.api.trait.TraitFactory; +import net.citizensnpcs.api.trait.trait.Equipment; +import net.citizensnpcs.api.trait.trait.Inventory; +import net.citizensnpcs.api.trait.trait.MobType; +import net.citizensnpcs.api.trait.trait.Owner; +import net.citizensnpcs.api.trait.trait.Spawned; +import net.citizensnpcs.trait.Age; +import net.citizensnpcs.trait.Behaviour; +import net.citizensnpcs.trait.Controllable; +import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.trait.LookClose; +import net.citizensnpcs.trait.Powered; +import net.citizensnpcs.trait.Saddle; +import net.citizensnpcs.trait.Sheared; +import net.citizensnpcs.trait.VillagerProfession; +import net.citizensnpcs.trait.WoolColor; +import net.citizensnpcs.trait.text.Text; +import net.citizensnpcs.trait.waypoint.Waypoints; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; + +public class CitizensTraitFactory implements TraitFactory { + private final Map> registered = Maps.newHashMap(); + + // TODO: find a way to avoid naming conflicts + public CitizensTraitFactory() { + registerTrait(TraitInfo.create(Age.class).withName("age")); + registerTrait(TraitInfo.create(CurrentLocation.class).withName("location")); + registerTrait(TraitInfo.create(Equipment.class).withName("equipment")); + registerTrait(TraitInfo.create(Inventory.class).withName("inventory")); + registerTrait(TraitInfo.create(LookClose.class).withName("lookclose")); + registerTrait(TraitInfo.create(MobType.class).withName("type")); + registerTrait(TraitInfo.create(Owner.class).withName("owner")); + registerTrait(TraitInfo.create(Powered.class).withName("powered")); + registerTrait(TraitInfo.create(Saddle.class).withName("saddle")); + registerTrait(TraitInfo.create(Sheared.class).withName("sheared")); + registerTrait(TraitInfo.create(Spawned.class).withName("spawned")); + registerTrait(TraitInfo.create(Text.class).withName("text")); + registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession")); + registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints")); + registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor")); + registerTrait(TraitInfo.create(Controllable.class).withName("controllable")); + registerTrait(TraitInfo.create(Behaviour.class).withName("behaviour")); + } + + public void addPlotters(Graph graph) { + for (Map.Entry> entry : registered.entrySet()) { + final Class traitClass = entry.getValue(); + graph.addPlotter(new Metrics.Plotter(entry.getKey()) { + @Override + public int getValue() { + int numberUsingTrait = 0; + for (NPC npc : CitizensAPI.getNPCRegistry()) { + if (npc.hasTrait(traitClass)) + ++numberUsingTrait; + } + return numberUsingTrait; + } + }); + } + } + + private T create(Class trait) { + try { + return trait.newInstance(); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } + + @Override + public T getTrait(Class clazz) { + if (!registered.containsValue(clazz)) + return null; + return create(clazz); + } + + @Override + @SuppressWarnings("unchecked") + public T getTrait(String name) { + Class clazz = registered.get(name); + if (clazz == null) + return null; + return (T) create(clazz); + } + + @Override + public void registerTrait(TraitInfo info) { + Preconditions.checkNotNull(info, "info cannot be null"); + registered.put(info.getTraitName(), info.getTraitClass()); + } +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/CitizensTraitManager.java b/src/main/java/net/citizensnpcs/npc/CitizensTraitManager.java deleted file mode 100644 index a0ae78299..000000000 --- a/src/main/java/net/citizensnpcs/npc/CitizensTraitManager.java +++ /dev/null @@ -1,139 +0,0 @@ -package net.citizensnpcs.npc; - -import java.lang.reflect.Constructor; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import net.citizensnpcs.api.npc.NPC; -import net.citizensnpcs.api.trait.Trait; -import net.citizensnpcs.api.trait.TraitFactory; -import net.citizensnpcs.api.trait.TraitManager; -import net.citizensnpcs.api.trait.trait.Equipment; -import net.citizensnpcs.api.trait.trait.Inventory; -import net.citizensnpcs.api.trait.trait.MobType; -import net.citizensnpcs.api.trait.trait.Owner; -import net.citizensnpcs.api.trait.trait.Spawned; -import net.citizensnpcs.trait.Age; -import net.citizensnpcs.trait.Behaviour; -import net.citizensnpcs.trait.Controllable; -import net.citizensnpcs.trait.CurrentLocation; -import net.citizensnpcs.trait.LookClose; -import net.citizensnpcs.trait.Powered; -import net.citizensnpcs.trait.Saddle; -import net.citizensnpcs.trait.Sheared; -import net.citizensnpcs.trait.VillagerProfession; -import net.citizensnpcs.trait.WoolColor; -import net.citizensnpcs.trait.text.Text; -import net.citizensnpcs.trait.waypoint.Waypoints; - -import org.bukkit.plugin.Plugin; - -public class CitizensTraitManager implements TraitManager { - private final Map, Constructor> CACHED_CTORS = new HashMap, Constructor>(); - private final Map>> registered = new HashMap>>(); - - // TODO: handle Plugin-setting/names better and avoid cruft. also find a - // way to avoid naming conflicts - public CitizensTraitManager(Plugin plugin) { - registerTrait(new TraitFactory(Age.class).withName("age").withPlugin(plugin)); - registerTrait(new TraitFactory(CurrentLocation.class).withName("location").withPlugin(plugin)); - registerTrait(new TraitFactory(Equipment.class).withName("equipment").withPlugin(plugin)); - registerTrait(new TraitFactory(Inventory.class).withName("inventory").withPlugin(plugin)); - registerTrait(new TraitFactory(LookClose.class).withName("lookclose").withPlugin(plugin)); - registerTrait(new TraitFactory(MobType.class).withName("type").withPlugin(plugin)); - registerTrait(new TraitFactory(Owner.class).withName("owner").withPlugin(plugin)); - registerTrait(new TraitFactory(Powered.class).withName("powered").withPlugin(plugin)); - registerTrait(new TraitFactory(Saddle.class).withName("saddle").withPlugin(plugin)); - registerTrait(new TraitFactory(Sheared.class).withName("sheared").withPlugin(plugin)); - registerTrait(new TraitFactory(Spawned.class).withName("spawned").withPlugin(plugin)); - registerTrait(new TraitFactory(Text.class).withName("text").withPlugin(plugin)); - registerTrait(new TraitFactory(VillagerProfession.class).withName("profession").withPlugin(plugin)); - registerTrait(new TraitFactory(Waypoints.class).withName("waypoints").withPlugin(plugin)); - registerTrait(new TraitFactory(WoolColor.class).withName("woolcolor").withPlugin(plugin)); - registerTrait(new TraitFactory(Controllable.class).withName("controllable").withPlugin(plugin)); - registerTrait(new TraitFactory(Behaviour.class).withName("behaviour").withPlugin(plugin)); - } - - @SuppressWarnings("unchecked") - private T create(Class trait, NPC npc) { - Constructor constructor; - - if (!CACHED_CTORS.containsKey(trait)) { - try { - // TODO: replace this fixed constructor with a context class - // which can have extra environment variables. - constructor = trait.getConstructor(NPC.class); - if (constructor == null) - constructor = trait.getConstructor(CitizensNPC.class); - constructor.setAccessible(true); - } catch (Exception ex) { - constructor = null; - } - CACHED_CTORS.put(trait, constructor); - } else - constructor = CACHED_CTORS.get(trait); - - try { - if (constructor == null || npc == null) - return trait.newInstance(); - return (T) constructor.newInstance(npc); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - - @Override - public T getTrait(Class clazz) { - return getTrait(clazz, null); - } - - @SuppressWarnings("unchecked") - public T getTrait(Class clazz, NPC npc) { - for (Entry>> entry : registered.entrySet()) { - for (Entry> subEntry : entry.getValue().entrySet()) { - if (!subEntry.getValue().equals(clazz)) - continue; - Trait trait = create(subEntry.getValue(), npc); - if (trait == null) - return null; - trait.setPlugin(entry.getKey()); - trait.setName(subEntry.getKey()); - return (T) trait; - } - } - return null; - } - - @SuppressWarnings("unchecked") - @Override - public T getTrait(String name) { - for (Map> entry : registered.values()) { - if (!entry.containsKey(name)) - continue; - return (T) create(entry.get(name), null); - } - return null; - } - - @SuppressWarnings("unchecked") - public T getTrait(String name, NPC npc) { - for (Map> entry : registered.values()) { - Class clazz = entry.get(name); - if (clazz == null) - continue; - return (T) getTrait(clazz, npc); - } - return null; - } - - @Override - public void registerTrait(TraitFactory factory) { - Map> map = registered.get(factory.getTraitPlugin()); - if (map == null) - map = new HashMap>(); - map.put(factory.getTraitName(), factory.getTraitClass()); - registered.put(factory.getTraitPlugin(), map); - } -} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/NPCSelector.java b/src/main/java/net/citizensnpcs/npc/NPCSelector.java index 1b7e2989a..63fec7406 100644 --- a/src/main/java/net/citizensnpcs/npc/NPCSelector.java +++ b/src/main/java/net/citizensnpcs/npc/NPCSelector.java @@ -22,15 +22,48 @@ import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; +import com.google.common.collect.Lists; + public class NPCSelector implements Listener { - private final Plugin plugin; private int consoleSelectedNPC = -1; + private final Plugin plugin; public NPCSelector(Plugin plugin) { this.plugin = plugin; Bukkit.getPluginManager().registerEvents(this, plugin); } + public NPC getSelected(CommandSender sender) { + if (sender instanceof Player) { + List metadata = ((Player) sender).getMetadata("selected"); + if (metadata.size() == 0) + return null; + return CitizensAPI.getNPCRegistry().getById(metadata.get(0).asInt()); + } else { + if (consoleSelectedNPC == -1) + return null; + return CitizensAPI.getNPCRegistry().getById(consoleSelectedNPC); + } + } + + @EventHandler + public void onNPCRemove(NPCRemoveEvent event) { + NPC npc = event.getNPC(); + List selectors = npc.data().get("selectors"); + if (selectors == null) + return; + for (String value : selectors) { + if (value.equals("console")) { + consoleSelectedNPC = -1; + } else { + Player search = Bukkit.getPlayerExact(value); + if (search != null) + search.removeMetadata("selected", plugin); + } + } + npc.data().remove("selectors"); + } + @EventHandler public void onNPCRightClick(NPCRightClickEvent event) { Player player = event.getClicker(); @@ -48,51 +81,24 @@ public class NPCSelector implements Listener { } } - @EventHandler - public void onNPCRemove(NPCRemoveEvent event) { - NPC npc = event.getNPC(); - for (MetadataValue value : npc.getMetadata("selectors")) { - if (value.asString().equals("console")) { - consoleSelectedNPC = -1; - } else { - Player search = Bukkit.getPlayerExact(value.asString()); - if (search != null) - search.removeMetadata("selected", plugin); - } - } - npc.removeMetadata("selectors", plugin); - } - public void select(CommandSender sender, NPC npc) { // Remove existing selection if any + List selectors = npc.data().get("selectors", Lists.newArrayList()); if (sender instanceof Player) { Player player = (Player) sender; if (player.hasMetadata("selected")) player.removeMetadata("selected", plugin); player.setMetadata("selected", new FixedMetadataValue(plugin, npc.getId())); - npc.setMetadata("selectors", new FixedMetadataValue(plugin, player.getName())); + selectors.add(player.getName()); // Remove editor if the player has one Editor.leave(player); } else { consoleSelectedNPC = npc.getId(); - npc.setMetadata("selectors", new FixedMetadataValue(plugin, "console")); + selectors.add("console"); } Bukkit.getPluginManager().callEvent(new NPCSelectEvent(npc, sender)); } - - public NPC getSelected(CommandSender sender) { - if (sender instanceof Player) { - List metadata = ((Player) sender).getMetadata("selected"); - if (metadata.size() == 0) - return null; - return CitizensAPI.getNPCRegistry().getNPC(metadata.get(0).asInt()); - } else { - if (consoleSelectedNPC == -1) - return null; - return CitizensAPI.getNPCRegistry().getNPC(consoleSelectedNPC); - } - } } diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensAI.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensAI.java deleted file mode 100644 index 30ea5a881..000000000 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensAI.java +++ /dev/null @@ -1,236 +0,0 @@ -package net.citizensnpcs.npc.ai; - -import java.lang.ref.WeakReference; -import java.util.Iterator; -import java.util.List; - -import net.citizensnpcs.api.ai.AI; -import net.citizensnpcs.api.ai.Goal; -import net.citizensnpcs.api.ai.NavigationCallback; -import net.citizensnpcs.api.ai.NavigationCallback.CancelReason; -import net.citizensnpcs.npc.CitizensNPC; - -import org.bukkit.Location; -import org.bukkit.entity.LivingEntity; - -import com.google.common.collect.Lists; - -public class CitizensAI implements AI { - private final List> callbacks = Lists.newArrayList(); - private PathStrategy executing; - private final List executingGoals = Lists.newArrayList(); - private final List goals = Lists.newArrayList(); - private List toRemove = null; - private final CitizensNPC npc; - private boolean paused; - - public CitizensAI(CitizensNPC npc) { - this.npc = npc; - } - - @Override - public void addGoal(int priority, Goal goal) { - if (goals.contains(goal)) - return; - goals.add(new GoalEntry(priority, goal)); - } - - @Override - public void cancelDestination() { - if (executing == null) - return; - executing = null; - for (int i = 0; i < callbacks.size(); ++i) { - NavigationCallback next = callbacks.get(i).get(); - if (next == null || next.onCancel(this, CancelReason.CANCEL)) { - callbacks.remove(i); - } - } - } - - @Override - public boolean hasDestination() { - return executing != null; - } - - private boolean isGoalAllowable(GoalEntry test) { - for (int i = 0; i < goals.size(); ++i) { - GoalEntry item = goals.get(i); - if (item == test) - continue; - if (test.getPriority() >= item.getPriority()) { - if (!test.getGoal().isCompatibleWith(item.getGoal()) && executingGoals.contains(item)) { - return false; - } - } /*else if (executingGoals.contains(item) && !item.goal.requiresUpdates()) { - return false; - }*/ - } - - return true; - } - - public void pause() { - paused = true; - } - - @Override - public void registerNavigationCallback(NavigationCallback callback) { - if (!callbacks.contains(callback)) { - callbacks.add(new WeakReference(callback)); - callback.onAttach(this); - } - } - - @Override - public void removeGoal(Goal goal) { - if (toRemove == null) - toRemove = Lists.newArrayList(); - toRemove.add(goal); - } - - public void resume() { - paused = false; - } - - @Override - public void setDestination(Location destination) { - if (destination == null) - throw new IllegalArgumentException("destination cannot be null"); - if (!npc.isSpawned()) - throw new IllegalStateException("npc is not spawned"); - if (destination.getWorld() != npc.getBukkitEntity().getWorld()) - throw new IllegalArgumentException("location is not in the same world"); - - boolean replaced = executing != null; - executing = new MCNavigationStrategy(npc, destination); - - for (int i = 0; i < callbacks.size(); ++i) { - NavigationCallback next = callbacks.get(i).get(); - if (next == null || (replaced && next.onCancel(this, CancelReason.REPLACE)) || next.onBegin(this)) { - callbacks.remove(i); - } - } - } - - @Override - public void setTarget(LivingEntity target, boolean aggressive) { - if (target == null) - throw new IllegalArgumentException("target cannot be null"); - - boolean replaced = executing != null; - executing = new MCTargetStrategy(npc, target, aggressive); - - for (int i = 0; i < callbacks.size(); ++i) { - NavigationCallback next = callbacks.get(i).get(); - if (next == null || (replaced && next.onCancel(this, CancelReason.REPLACE)) || next.onBegin(this)) { - callbacks.remove(i); - } - } - } - - public void update() { - if (paused || !npc.isSpawned()) { - return; - } - - if (executing != null && executing.update()) { - executing = null; - for (int i = 0; i < callbacks.size(); ++i) { - NavigationCallback next = callbacks.get(i).get(); - if (next == null || next.onCompletion(this)) { - callbacks.remove(i); - } - } - } - removeGoals(); - for (int i = 0; i < goals.size(); ++i) { - GoalEntry entry = goals.get(i); - boolean executing = executingGoals.contains(entry); - - if (executing) { - if (!entry.getGoal().continueExecuting() || !isGoalAllowable(entry)) { - entry.getGoal().reset(); - executingGoals.remove(entry); - } - } else if (entry.getGoal().shouldExecute() && isGoalAllowable(entry)) { - entry.getGoal().start(); - executingGoals.add(entry); - } - } - - for (int i = 0; i < executingGoals.size(); ++i) { - executingGoals.get(i).getGoal().update(); - } - } - - private void removeGoals() { - if (toRemove == null) - return; - for (Goal goal : toRemove) { - Iterator itr = executingGoals.iterator(); - while (itr.hasNext()) { - GoalEntry entry = itr.next(); - if (entry.getGoal().equals(goal)) { - entry.getGoal().reset(); - itr.remove(); - } - } - itr = goals.iterator(); - while (itr.hasNext()) { - GoalEntry entry = itr.next(); - if (entry.getGoal().equals(goal)) - itr.remove(); - } - } - - toRemove = null; - } - - public static class GoalEntry implements Comparable { - private final Goal goal; - private final int priority; - - public GoalEntry(int priority, Goal goal) { - this.priority = priority; - this.goal = goal; - } - - @Override - public int compareTo(GoalEntry o) { - return o.priority > priority ? 1 : o.priority < priority ? -1 : 0; - } - - public Goal getGoal() { - return goal; - } - - public int getPriority() { - return priority; - } - - @Override - public int hashCode() { - return 31 + ((goal == null) ? 0 : goal.hashCode()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - GoalEntry other = (GoalEntry) obj; - if (goal == null) { - if (other.goal != null) { - return false; - } - } else if (!goal.equals(other.goal)) { - return false; - } - return true; - } - } -} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java new file mode 100644 index 000000000..e036ce185 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -0,0 +1,137 @@ +package net.citizensnpcs.npc.ai; + +import java.lang.reflect.Field; +import java.util.Map; + +import net.citizensnpcs.api.ai.EntityTarget; +import net.citizensnpcs.api.ai.Navigator; +import net.citizensnpcs.api.ai.TargetType; +import net.citizensnpcs.api.ai.event.NavigationBeginEvent; +import net.citizensnpcs.api.ai.event.NavigationCancelEvent; +import net.citizensnpcs.api.ai.event.NavigationReplaceEvent; +import net.citizensnpcs.npc.CitizensNPC; +import net.minecraft.server.EntityLiving; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import com.google.common.collect.Maps; + +public class CitizensNavigator implements Navigator { + private PathStrategy executing; + private final CitizensNPC npc; + private float speed; + + public CitizensNavigator(CitizensNPC npc) { + this.npc = npc; + this.speed = getSpeedFor(npc.getHandle()); + } + + @Override + public void cancelNavigation() { + if (executing != null) { + Bukkit.getPluginManager().callEvent(new NavigationCancelEvent(this)); + } + executing = null; + } + + @Override + public EntityTarget getEntityTarget() { + return executing instanceof EntityTarget ? (EntityTarget) executing : null; + } + + @Override + public float getSpeed() { + return speed; + } + + private float getSpeedFor(EntityLiving from) { + EntityType entityType = from.getBukkitEntity().getType(); + Float cached = MOVEMENT_SPEEDS.get(entityType); + if (cached != null) + return cached; + if (SPEED_FIELD == null) { + MOVEMENT_SPEEDS.put(entityType, DEFAULT_SPEED); + return DEFAULT_SPEED; + } + try { + float speed = SPEED_FIELD.getFloat(from); + MOVEMENT_SPEEDS.put(entityType, speed); + return speed; + } catch (IllegalAccessException ex) { + ex.printStackTrace(); + return DEFAULT_SPEED; + } + } + + @Override + public Location getTargetAsLocation() { + return executing.getTargetAsLocation(); + } + + @Override + public TargetType getTargetType() { + return executing.getTargetType(); + } + + @Override + public boolean isNavigating() { + return executing != null; + } + + @Override + public void setSpeed(float speed) { + this.speed = speed; + } + + @Override + public void setTarget(LivingEntity target, boolean aggressive) { + PathStrategy newStrategy = new MCTargetStrategy(npc, target, aggressive, speed); + switchStrategyTo(newStrategy); + } + + @Override + public void setTarget(Location target) { + PathStrategy newStrategy = new MCNavigationStrategy(npc, target, speed); + switchStrategyTo(newStrategy); + } + + private void switchStrategyTo(PathStrategy newStrategy) { + if (executing != null) + Bukkit.getPluginManager().callEvent(new NavigationReplaceEvent(this)); + + executing = newStrategy; + + Bukkit.getPluginManager().callEvent(new NavigationBeginEvent(this)); + } + + public void update() { + if (executing == null) + return; + boolean finished = executing.update(); + if (finished) { + Bukkit.getPluginManager().callEvent(new NavigationCompleteEvent(this)); + executing = null; + } + } + + private static final float DEFAULT_SPEED = 0.3F; + private static final Map MOVEMENT_SPEEDS = Maps.newEnumMap(EntityType.class); + private static Field SPEED_FIELD; + static { + MOVEMENT_SPEEDS.put(EntityType.IRON_GOLEM, 0.15F); + MOVEMENT_SPEEDS.put(EntityType.CHICKEN, 0.25F); + MOVEMENT_SPEEDS.put(EntityType.COW, 0.2F); + MOVEMENT_SPEEDS.put(EntityType.SHEEP, 0.25F); + MOVEMENT_SPEEDS.put(EntityType.VILLAGER, 0.3F); + MOVEMENT_SPEEDS.put(EntityType.SNOWMAN, 0.25F); + try { + SPEED_FIELD = EntityLiving.class.getDeclaredField("bb"); + SPEED_FIELD.setAccessible(true); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java index 214a490a1..3d4d5de46 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java @@ -1,82 +1,51 @@ package net.citizensnpcs.npc.ai; -import java.lang.reflect.Field; -import java.util.Map; - +import net.citizensnpcs.api.ai.TargetType; import net.citizensnpcs.npc.CitizensNPC; import net.minecraft.server.EntityLiving; import net.minecraft.server.Navigation; import org.bukkit.Location; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import com.google.common.collect.Maps; - public class MCNavigationStrategy implements PathStrategy { - private final EntityLiving entity; private final Navigation navigation; + private final Location target; - MCNavigationStrategy(final CitizensNPC npc, final Location dest) { - entity = npc.getHandle(); - if (npc.getBukkitEntity() instanceof Player) { + MCNavigationStrategy(final CitizensNPC npc, Location dest, float speed) { + this(npc.getHandle(), dest); + navigation.a(dest.getX(), dest.getY(), dest.getZ(), speed); + + } + + MCNavigationStrategy(EntityLiving entity, EntityLiving target, float speed) { + this(entity, target.getBukkitEntity().getLocation()); + navigation.a(target, speed); + } + + private MCNavigationStrategy(EntityLiving entity, Location target) { + this.target = target; + if (entity.getBukkitEntity() instanceof Player) { entity.onGround = true; // not sure of a better way around this - if onGround is false, then // navigation won't execute, and calling entity.move doesn't // entirely fix the problem. } navigation = entity.al(); - navigation.a(dest.getX(), dest.getY(), dest.getZ(), getSpeed(npc.getHandle())); - } - MCNavigationStrategy(EntityLiving entity, EntityLiving target) { - this.entity = entity; - if (entity.getBukkitEntity() instanceof Player) { - entity.onGround = true; // see above - } - navigation = entity.al(); - navigation.a(target, getSpeed(entity)); + @Override + public Location getTargetAsLocation() { + return target; } - private float getSpeed(EntityLiving from) { - Float cached = MOVEMENT_SPEEDS.get(from.getBukkitEntity().getType()); - if (cached != null) - return cached; - if (SPEED_FIELD == null) { - MOVEMENT_SPEEDS.put(from.getBukkitEntity().getType(), DEFAULT_SPEED); - return DEFAULT_SPEED; - } - try { - float speed = SPEED_FIELD.getFloat(from); - MOVEMENT_SPEEDS.put(from.getBukkitEntity().getType(), speed); - return speed; - } catch (IllegalAccessException ex) { - ex.printStackTrace(); - return DEFAULT_SPEED; - } + @Override + public TargetType getTargetType() { + return TargetType.LOCATION; } @Override public boolean update() { return navigation.e(); } - - private static final float DEFAULT_SPEED = 0.3F; - private static final Map MOVEMENT_SPEEDS = Maps.newEnumMap(EntityType.class); - private static Field SPEED_FIELD; - static { - MOVEMENT_SPEEDS.put(EntityType.IRON_GOLEM, 0.15F); - MOVEMENT_SPEEDS.put(EntityType.CHICKEN, 0.25F); - MOVEMENT_SPEEDS.put(EntityType.COW, 0.2F); - MOVEMENT_SPEEDS.put(EntityType.SHEEP, 0.25F); - MOVEMENT_SPEEDS.put(EntityType.VILLAGER, 0.3F); - MOVEMENT_SPEEDS.put(EntityType.SNOWMAN, 0.25F); - try { - SPEED_FIELD = EntityLiving.class.getDeclaredField("bb"); - SPEED_FIELD.setAccessible(true); - } catch (Exception ex) { - ex.printStackTrace(); - } - } } diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java index 9f6e0860e..e84446ce9 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java @@ -1,5 +1,7 @@ package net.citizensnpcs.npc.ai; +import net.citizensnpcs.api.ai.EntityTarget; +import net.citizensnpcs.api.ai.TargetType; import net.citizensnpcs.npc.CitizensNPC; import net.citizensnpcs.util.Util; import net.minecraft.server.EntityLiving; @@ -7,17 +9,20 @@ import net.minecraft.server.EntityMonster; import net.minecraft.server.EntityPlayer; import net.minecraft.server.Packet18ArmAnimation; +import org.bukkit.Location; import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.entity.LivingEntity; -public class MCTargetStrategy implements PathStrategy { +public class MCTargetStrategy implements PathStrategy, EntityTarget { private final boolean aggro; private final EntityLiving handle, target; + private final float speed; - public MCTargetStrategy(CitizensNPC handle, LivingEntity target, boolean aggro) { + public MCTargetStrategy(CitizensNPC handle, LivingEntity target, boolean aggro, float speed) { this.handle = handle.getHandle(); this.target = ((CraftLivingEntity) target).getHandle(); this.aggro = aggro; + this.speed = speed; } private boolean canAttack() { @@ -30,11 +35,31 @@ public class MCTargetStrategy implements PathStrategy { return handle.getBukkitEntity().getLocation().distanceSquared(target.getBukkitEntity().getLocation()); } + @Override + public LivingEntity getTarget() { + return (LivingEntity) target.getBukkitEntity(); + } + + @Override + public Location getTargetAsLocation() { + return getTarget().getLocation(); + } + + @Override + public TargetType getTargetType() { + return TargetType.ENTITY; + } + + @Override + public boolean isAggressive() { + return aggro; + } + @Override public boolean update() { if (target == null || target.dead) return true; - new MCNavigationStrategy(handle, target).update(); + new MCNavigationStrategy(handle, target, speed).update(); handle.getControllerLook().a(target, 10.0F, handle.D()); if (aggro && canAttack()) { if (handle instanceof EntityMonster) { @@ -43,8 +68,8 @@ public class MCTargetStrategy implements PathStrategy { } else if (handle instanceof EntityPlayer) { EntityPlayer humanHandle = (EntityPlayer) handle; humanHandle.attack(target); - Util.sendPacketNearby(handle.getBukkitEntity().getLocation(), new Packet18ArmAnimation(humanHandle, 1), - 64); + Util.sendPacketNearby(handle.getBukkitEntity().getLocation(), new Packet18ArmAnimation( + humanHandle, 1), 64); } } diff --git a/src/main/java/net/citizensnpcs/npc/ai/NPCHandle.java b/src/main/java/net/citizensnpcs/npc/ai/NPCHolder.java similarity index 77% rename from src/main/java/net/citizensnpcs/npc/ai/NPCHandle.java rename to src/main/java/net/citizensnpcs/npc/ai/NPCHolder.java index 183b1078a..63c1a4b72 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/NPCHandle.java +++ b/src/main/java/net/citizensnpcs/npc/ai/NPCHolder.java @@ -2,6 +2,6 @@ package net.citizensnpcs.npc.ai; import net.citizensnpcs.api.npc.NPC; -public interface NPCHandle { +public interface NPCHolder { public NPC getNPC(); } diff --git a/src/main/java/net/citizensnpcs/npc/ai/NavigationCompleteEvent.java b/src/main/java/net/citizensnpcs/npc/ai/NavigationCompleteEvent.java new file mode 100644 index 000000000..2deacc008 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/ai/NavigationCompleteEvent.java @@ -0,0 +1,23 @@ +package net.citizensnpcs.npc.ai; + +import net.citizensnpcs.api.ai.Navigator; +import net.citizensnpcs.api.ai.event.NavigationEvent; + +import org.bukkit.event.HandlerList; + +public class NavigationCompleteEvent extends NavigationEvent { + public NavigationCompleteEvent(Navigator navigator) { + super(navigator); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java index 3af3fbe6e..43ea83e69 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java @@ -1,6 +1,13 @@ package net.citizensnpcs.npc.ai; +import net.citizensnpcs.api.ai.TargetType; + +import org.bukkit.Location; + public interface PathStrategy { + Location getTargetAsLocation(); + + TargetType getTargetType(); boolean update(); } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensBlazeNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensBlazeNPC.java index 06aa9f2b9..8efab604e 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensBlazeNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensBlazeNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityBlaze; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensBlazeNPC extends CitizensMobNPC { return (Blaze) getHandle().getBukkitEntity(); } - public static class EntityBlazeNPC extends EntityBlaze implements NPCHandle { + public static class EntityBlazeNPC extends EntityBlaze implements NPCHolder { private final CitizensNPC npc; public EntityBlazeNPC(World world) { @@ -31,8 +31,16 @@ public class CitizensBlazeNPC extends CitizensMobNPC { public EntityBlazeNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensCaveSpiderNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensCaveSpiderNPC.java index 4176a7a33..b3fe519fb 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensCaveSpiderNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensCaveSpiderNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityCaveSpider; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,14 +21,27 @@ public class CitizensCaveSpiderNPC extends CitizensMobNPC { return (CaveSpider) getHandle().getBukkitEntity(); } - public static class EntityCaveSpiderNPC extends EntityCaveSpider implements NPCHandle { + public static class EntityCaveSpiderNPC extends EntityCaveSpider implements NPCHolder { private final CitizensNPC npc; public EntityCaveSpiderNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -36,10 +49,5 @@ public class CitizensCaveSpiderNPC extends CitizensMobNPC { super.z_(); npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensChickenNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensChickenNPC.java index f5b4fa2e4..c6108dee7 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensChickenNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensChickenNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityChicken; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensChickenNPC extends CitizensMobNPC { return (Chicken) getHandle().getBukkitEntity(); } - public static class EntityChickenNPC extends EntityChicken implements NPCHandle { + public static class EntityChickenNPC extends EntityChicken implements NPCHolder { private final CitizensNPC npc; public EntityChickenNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensChickenNPC extends CitizensMobNPC { public EntityChickenNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensChickenNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensCowNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensCowNPC.java index 4b0ee4309..bf2bed91b 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensCowNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensCowNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityCow; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensCowNPC extends CitizensMobNPC { return (Cow) getHandle().getBukkitEntity(); } - public static class EntityCowNPC extends EntityCow implements NPCHandle { + public static class EntityCowNPC extends EntityCow implements NPCHolder { private final CitizensNPC npc; public EntityCowNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensCowNPC extends CitizensMobNPC { public EntityCowNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensCowNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensCreeperNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensCreeperNPC.java index 79d83edb6..1c9c7b137 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensCreeperNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensCreeperNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityCreeper; import net.minecraft.server.EntityWeatherLighting; import net.minecraft.server.PathfinderGoalSelector; @@ -22,7 +22,7 @@ public class CitizensCreeperNPC extends CitizensMobNPC { return (Creeper) getHandle().getBukkitEntity(); } - public static class EntityCreeperNPC extends EntityCreeper implements NPCHandle { + public static class EntityCreeperNPC extends EntityCreeper implements NPCHolder { private final CitizensNPC npc; public EntityCreeperNPC(World world) { @@ -32,12 +32,27 @@ public class CitizensCreeperNPC extends CitizensMobNPC { public EntityCreeperNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } } @Override public void a(EntityWeatherLighting entityweatherlighting) { + if (npc == null) + super.a(entityweatherlighting); + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -46,10 +61,5 @@ public class CitizensCreeperNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensEnderDragonNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensEnderDragonNPC.java index 651b6a54a..0970dfb04 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensEnderDragonNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensEnderDragonNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityEnderDragon; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,23 +21,36 @@ public class CitizensEnderDragonNPC extends CitizensMobNPC { return (EnderDragon) getHandle().getBukkitEntity(); } - public static class EntityEnderDragonNPC extends EntityEnderDragon implements NPCHandle { + public static class EntityEnderDragonNPC extends EntityEnderDragon implements NPCHolder { private final CitizensNPC npc; public EntityEnderDragonNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override public void d_() { + if (npc == null) + super.d_(); } @Override public void e() { - npc.update(); + if (npc != null) + npc.update(); + else + super.e(); } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensEndermanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensEndermanNPC.java index 3d1344116..c85df1657 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensEndermanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensEndermanNPC.java @@ -5,7 +5,7 @@ import net.citizensnpcs.api.trait.trait.Equipment; import net.citizensnpcs.editor.Equipable; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.util.Messaging; import net.minecraft.server.EntityEnderman; import net.minecraft.server.PathfinderGoalSelector; @@ -59,7 +59,7 @@ public class CitizensEndermanNPC extends CitizensMobNPC implements Equipable { return (Enderman) getHandle().getBukkitEntity(); } - public static class EntityEndermanNPC extends EntityEnderman implements NPCHandle { + public static class EntityEndermanNPC extends EntityEnderman implements NPCHolder { private final CitizensNPC npc; public EntityEndermanNPC(World world) { @@ -69,12 +69,22 @@ public class CitizensEndermanNPC extends CitizensMobNPC implements Equipable { public EntityEndermanNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override public void d_() { + if (npc == null) + super.d_(); } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensGhastNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensGhastNPC.java index 27d074c62..6cf0c1f86 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensGhastNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensGhastNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityGhast; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensGhastNPC extends CitizensMobNPC { return (Ghast) getHandle().getBukkitEntity(); } - public static class EntityGhastNPC extends EntityGhast implements NPCHandle { + public static class EntityGhastNPC extends EntityGhast implements NPCHolder { private final CitizensNPC npc; public EntityGhastNPC(World world) { @@ -31,8 +31,16 @@ public class CitizensGhastNPC extends CitizensMobNPC { public EntityGhastNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensGiantNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensGiantNPC.java index 33d622fb0..80163f369 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensGiantNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensGiantNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityGiantZombie; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,14 +21,22 @@ public class CitizensGiantNPC extends CitizensMobNPC { return (Giant) getHandle().getBukkitEntity(); } - public static class EntityGiantNPC extends EntityGiantZombie implements NPCHandle { + public static class EntityGiantNPC extends EntityGiantZombie implements NPCHolder { private final CitizensNPC npc; public EntityGiantNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java index 0d59da5b1..bb0e01076 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java @@ -48,48 +48,50 @@ public class CitizensHumanNPC extends CitizensNPC implements Equipable { Material type = hand == null ? Material.AIR : hand.getType(); // First, determine the slot to edit switch (type) { - case PUMPKIN: - case JACK_O_LANTERN: - case LEATHER_HELMET: - case CHAINMAIL_HELMET: - case GOLD_HELMET: - case IRON_HELMET: - case DIAMOND_HELMET: - if (!equipper.isSneaking()) - slot = 1; - break; - case LEATHER_CHESTPLATE: - case CHAINMAIL_CHESTPLATE: - case GOLD_CHESTPLATE: - case IRON_CHESTPLATE: - case DIAMOND_CHESTPLATE: - if (!equipper.isSneaking()) - slot = 2; - break; - case LEATHER_LEGGINGS: - case CHAINMAIL_LEGGINGS: - case GOLD_LEGGINGS: - case IRON_LEGGINGS: - case DIAMOND_LEGGINGS: - if (!equipper.isSneaking()) - slot = 3; - break; - case LEATHER_BOOTS: - case CHAINMAIL_BOOTS: - case GOLD_BOOTS: - case IRON_BOOTS: - case DIAMOND_BOOTS: - if (!equipper.isSneaking()) - slot = 4; - break; - case AIR: - for (int i = 0; i < 5; i++) { - if (trait.get(i) != null && trait.get(i).getType() != Material.AIR) { - equipper.getWorld().dropItemNaturally(getBukkitEntity().getLocation(), trait.get(i)); - trait.set(i, null); + case PUMPKIN: + case JACK_O_LANTERN: + case LEATHER_HELMET: + case CHAINMAIL_HELMET: + case GOLD_HELMET: + case IRON_HELMET: + case DIAMOND_HELMET: + if (!equipper.isSneaking()) + slot = 1; + break; + case LEATHER_CHESTPLATE: + case CHAINMAIL_CHESTPLATE: + case GOLD_CHESTPLATE: + case IRON_CHESTPLATE: + case DIAMOND_CHESTPLATE: + if (!equipper.isSneaking()) + slot = 2; + break; + case LEATHER_LEGGINGS: + case CHAINMAIL_LEGGINGS: + case GOLD_LEGGINGS: + case IRON_LEGGINGS: + case DIAMOND_LEGGINGS: + if (!equipper.isSneaking()) + slot = 3; + break; + case LEATHER_BOOTS: + case CHAINMAIL_BOOTS: + case GOLD_BOOTS: + case IRON_BOOTS: + case DIAMOND_BOOTS: + if (!equipper.isSneaking()) + slot = 4; + break; + case AIR: + for (int i = 0; i < 5; i++) { + if (trait.get(i) != null && trait.get(i).getType() != Material.AIR) { + equipper.getWorld().dropItemNaturally(getBukkitEntity().getLocation(), trait.get(i)); + trait.set(i, null); + } } - } - Messaging.sendF(equipper, "%shad all of its items removed.", getName()); + Messaging.sendF(equipper, "%shad all of its items removed.", getName()); + default: + break; } // Drop any previous equipment on the ground if (trait.get(slot) != null && trait.get(slot).getType() != Material.AIR) diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensIronGolemNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensIronGolemNPC.java index 20d1b2d38..6f3f40002 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensIronGolemNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensIronGolemNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityIronGolem; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,25 +21,34 @@ public class CitizensIronGolemNPC extends CitizensMobNPC { return (IronGolem) getHandle().getBukkitEntity(); } - public static class EntityIronGolemNPC extends EntityIronGolem implements NPCHandle { + public static class EntityIronGolemNPC extends EntityIronGolem implements NPCHolder { private final CitizensNPC npc; public EntityIronGolemNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } } @Override - public void z_() { - super.z_(); - npc.update(); + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override public NPC getNPC() { return npc; } + + @Override + public void z_() { + super.z_(); + if (npc != null) + npc.update(); + } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java index 905c88245..e554bdf84 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityMagmaCube; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensMagmaCubeNPC extends CitizensMobNPC { return (MagmaCube) getHandle().getBukkitEntity(); } - public static class EntityMagmaCubeNPC extends EntityMagmaCube implements NPCHandle { + public static class EntityMagmaCubeNPC extends EntityMagmaCube implements NPCHolder { private final CitizensNPC npc; public EntityMagmaCubeNPC(World world) { @@ -31,9 +31,17 @@ public class CitizensMagmaCubeNPC extends CitizensMobNPC { public EntityMagmaCubeNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - setSize(3); - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + setSize(3); + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensMushroomCowNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensMushroomCowNPC.java index 7a3bfedd0..420a861bc 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensMushroomCowNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensMushroomCowNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityMushroomCow; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensMushroomCowNPC extends CitizensMobNPC { return (MushroomCow) getHandle().getBukkitEntity(); } - public static class EntityMushroomCowNPC extends EntityMushroomCow implements NPCHandle { + public static class EntityMushroomCowNPC extends EntityMushroomCow implements NPCHolder { private final CitizensNPC npc; public EntityMushroomCowNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensMushroomCowNPC extends CitizensMobNPC { public EntityMushroomCowNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensMushroomCowNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensOcelotNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensOcelotNPC.java index 9e692b93b..fd3d6148a 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensOcelotNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensOcelotNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityOcelot; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensOcelotNPC extends CitizensMobNPC { return (Ocelot) getHandle().getBukkitEntity(); } - public static class EntityOcelotNPC extends EntityOcelot implements NPCHandle { + public static class EntityOcelotNPC extends EntityOcelot implements NPCHolder { private final CitizensNPC npc; public EntityOcelotNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensOcelotNPC extends CitizensMobNPC { public EntityOcelotNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensOcelotNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensPigNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensPigNPC.java index bce59f966..936cba18f 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensPigNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensPigNPC.java @@ -4,7 +4,7 @@ import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.editor.Equipable; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.trait.Saddle; import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.StringHelper; @@ -48,7 +48,7 @@ public class CitizensPigNPC extends CitizensMobNPC implements Equipable { return (Pig) getHandle().getBukkitEntity(); } - public static class EntityPigNPC extends EntityPig implements NPCHandle { + public static class EntityPigNPC extends EntityPig implements NPCHolder { private final CitizensNPC npc; public EntityPigNPC(World world) { @@ -58,12 +58,27 @@ public class CitizensPigNPC extends CitizensMobNPC implements Equipable { public EntityPigNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } } @Override public void a(EntityWeatherLighting entityweatherlighting) { + if (npc == null) + super.a(entityweatherlighting); + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -72,10 +87,5 @@ public class CitizensPigNPC extends CitizensMobNPC implements Equipable { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensPigZombieNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensPigZombieNPC.java index e350a6857..c1303cec5 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensPigZombieNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensPigZombieNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityPigZombie; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensPigZombieNPC extends CitizensMobNPC { return (PigZombie) getHandle().getBukkitEntity(); } - public static class EntityPigZombieNPC extends EntityPigZombie implements NPCHandle { + public static class EntityPigZombieNPC extends EntityPigZombie implements NPCHolder { private final CitizensNPC npc; public EntityPigZombieNPC(World world) { @@ -31,8 +31,16 @@ public class CitizensPigZombieNPC extends CitizensMobNPC { public EntityPigZombieNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSheepNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSheepNPC.java index 218a5c110..1d5d18248 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSheepNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSheepNPC.java @@ -4,7 +4,7 @@ import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.editor.Equipable; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.trait.Sheared; import net.citizensnpcs.trait.WoolColor; import net.citizensnpcs.util.Messaging; @@ -49,7 +49,8 @@ public class CitizensSheepNPC extends CitizensMobNPC implements Equipable { equipper.setItemInHand(hand); } else { getTrait(WoolColor.class).setColor(DyeColor.WHITE); - Messaging.send(equipper, StringHelper.wrap(getName()) + " is now " + StringHelper.wrap("white") + "."); + Messaging.send(equipper, StringHelper.wrap(getName()) + " is now " + StringHelper.wrap("white") + + "."); } } @@ -58,7 +59,7 @@ public class CitizensSheepNPC extends CitizensMobNPC implements Equipable { return (Sheep) getHandle().getBukkitEntity(); } - public static class EntitySheepNPC extends EntitySheep implements NPCHandle { + public static class EntitySheepNPC extends EntitySheep implements NPCHolder { private final CitizensNPC npc; public EntitySheepNPC(World world) { @@ -68,8 +69,21 @@ public class CitizensSheepNPC extends CitizensMobNPC implements Equipable { public EntitySheepNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -78,10 +92,5 @@ public class CitizensSheepNPC extends CitizensMobNPC implements Equipable { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSilverfishNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSilverfishNPC.java index 692872fed..22b4c4f69 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSilverfishNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSilverfishNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySilverfish; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensSilverfishNPC extends CitizensMobNPC { return (Silverfish) getHandle().getBukkitEntity(); } - public static class EntitySilverfishNPC extends EntitySilverfish implements NPCHandle { + public static class EntitySilverfishNPC extends EntitySilverfish implements NPCHolder { private final CitizensNPC npc; public EntitySilverfishNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensSilverfishNPC extends CitizensMobNPC { public EntitySilverfishNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensSilverfishNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSkeletonNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSkeletonNPC.java index f4ae68741..999633fdd 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSkeletonNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSkeletonNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySkeleton; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensSkeletonNPC extends CitizensMobNPC { return (Skeleton) getHandle().getBukkitEntity(); } - public static class EntitySkeletonNPC extends EntitySkeleton implements NPCHandle { + public static class EntitySkeletonNPC extends EntitySkeleton implements NPCHolder { private final CitizensNPC npc; public EntitySkeletonNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensSkeletonNPC extends CitizensMobNPC { public EntitySkeletonNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensSkeletonNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSlimeNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSlimeNPC.java index 341470990..c753daec5 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSlimeNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSlimeNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySlime; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensSlimeNPC extends CitizensMobNPC { return (Slime) getHandle().getBukkitEntity(); } - public static class EntitySlimeNPC extends EntitySlime implements NPCHandle { + public static class EntitySlimeNPC extends EntitySlime implements NPCHolder { private final CitizensNPC npc; public EntitySlimeNPC(World world) { @@ -31,9 +31,22 @@ public class CitizensSlimeNPC extends CitizensMobNPC { public EntitySlimeNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - setSize(3); - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + setSize(3); + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -42,10 +55,5 @@ public class CitizensSlimeNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSnowmanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSnowmanNPC.java index 4cd639416..93d8d76a3 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSnowmanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSnowmanNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySnowman; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,14 +21,27 @@ public class CitizensSnowmanNPC extends CitizensMobNPC { return (Snowman) getHandle().getBukkitEntity(); } - public static class EntitySnowmanNPC extends EntitySnowman implements NPCHandle { + public static class EntitySnowmanNPC extends EntitySnowman implements NPCHolder { private final CitizensNPC npc; public EntitySnowmanNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -36,10 +49,5 @@ public class CitizensSnowmanNPC extends CitizensMobNPC { super.z_(); npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSpiderNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSpiderNPC.java index 50dd406eb..6edf1983a 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSpiderNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSpiderNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySpider; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensSpiderNPC extends CitizensMobNPC { return (Spider) getHandle().getBukkitEntity(); } - public static class EntitySpiderNPC extends EntitySpider implements NPCHandle { + public static class EntitySpiderNPC extends EntitySpider implements NPCHolder { private final CitizensNPC npc; public EntitySpiderNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensSpiderNPC extends CitizensMobNPC { public EntitySpiderNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensSpiderNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensSquidNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensSquidNPC.java index d7bba73dc..336545bc2 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensSquidNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensSquidNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntitySquid; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensSquidNPC extends CitizensMobNPC { return (Squid) getHandle().getBukkitEntity(); } - public static class EntitySquidNPC extends EntitySquid implements NPCHandle { + public static class EntitySquidNPC extends EntitySquid implements NPCHolder { private final CitizensNPC npc; public EntitySquidNPC(World world) { @@ -31,8 +31,16 @@ public class CitizensSquidNPC extends CitizensMobNPC { public EntitySquidNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. } @Override diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensVillagerNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensVillagerNPC.java index 62ab5a899..6f9f15dc9 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensVillagerNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensVillagerNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityVillager; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensVillagerNPC extends CitizensMobNPC { return (Villager) getHandle().getBukkitEntity(); } - public static class EntityVillagerNPC extends EntityVillager implements NPCHandle { + public static class EntityVillagerNPC extends EntityVillager implements NPCHolder { private final CitizensNPC npc; public EntityVillagerNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensVillagerNPC extends CitizensMobNPC { public EntityVillagerNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensVillagerNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensWolfNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensWolfNPC.java index f76ac35d1..09e6a3464 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensWolfNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensWolfNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityWolf; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensWolfNPC extends CitizensMobNPC { return (Wolf) getHandle().getBukkitEntity(); } - public static class EntityWolfNPC extends EntityWolf implements NPCHandle { + public static class EntityWolfNPC extends EntityWolf implements NPCHolder { private final CitizensNPC npc; public EntityWolfNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensWolfNPC extends CitizensMobNPC { public EntityWolfNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensWolfNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensZombieNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensZombieNPC.java index 630678b97..137e2b336 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensZombieNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensZombieNPC.java @@ -3,7 +3,7 @@ package net.citizensnpcs.npc.entity; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensMobNPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.minecraft.server.EntityZombie; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; @@ -21,7 +21,7 @@ public class CitizensZombieNPC extends CitizensMobNPC { return (Zombie) getHandle().getBukkitEntity(); } - public static class EntityZombieNPC extends EntityZombie implements NPCHandle { + public static class EntityZombieNPC extends EntityZombie implements NPCHolder { private final CitizensNPC npc; public EntityZombieNPC(World world) { @@ -31,8 +31,21 @@ public class CitizensZombieNPC extends CitizensMobNPC { public EntityZombieNPC(World world, NPC npc) { super(world); this.npc = (CitizensNPC) npc; - goalSelector = new PathfinderGoalSelector(); - targetSelector = new PathfinderGoalSelector(); + if (npc != null) { + goalSelector = new PathfinderGoalSelector(); + targetSelector = new PathfinderGoalSelector(); + } + } + + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + + @Override + public NPC getNPC() { + return npc; } @Override @@ -41,10 +54,5 @@ public class CitizensZombieNPC extends CitizensMobNPC { if (npc != null) npc.update(); } - - @Override - public NPC getNPC() { - return npc; - } } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java index 65bc5fea6..2dc95065e 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java @@ -4,7 +4,7 @@ import java.io.IOException; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.ai.NPCHandle; +import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.npc.network.NPCNetHandler; import net.citizensnpcs.npc.network.NPCNetworkManager; import net.citizensnpcs.npc.network.NPCSocket; @@ -16,7 +16,7 @@ import net.minecraft.server.NetHandler; import net.minecraft.server.NetworkManager; import net.minecraft.server.World; -public class EntityHumanNPC extends EntityPlayer implements NPCHandle { +public class EntityHumanNPC extends EntityPlayer implements NPCHolder { private CitizensNPC npc; public EntityHumanNPC(MinecraftServer minecraftServer, World world, String string, @@ -42,6 +42,12 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHandle { } } + @Override + public void b_(double x, double y, double z) { + // when another entity collides, b_ is called to push the NPC + // so we prevent b_ from doing anything. + } + @Override public void F_() { super.F_(); @@ -57,6 +63,11 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHandle { npc.update(); } + @Override + public NPC getNPC() { + return npc; + } + private void moveOnCurrentHeading() { getControllerMove().c(); getControllerLook().a(); @@ -76,9 +87,4 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHandle { a(aW, aX); X = yaw; // TODO: this looks jerky } - - @Override - public NPC getNPC() { - return npc; - } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/network/EmptyNetHandler.java b/src/main/java/net/citizensnpcs/npc/network/EmptyNetHandler.java new file mode 100644 index 000000000..8067a250d --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/network/EmptyNetHandler.java @@ -0,0 +1,84 @@ +package net.citizensnpcs.npc.network; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NetServerHandler; +import net.minecraft.server.NetworkManager; +import net.minecraft.server.Packet; +import net.minecraft.server.Packet102WindowClick; +import net.minecraft.server.Packet106Transaction; +import net.minecraft.server.Packet10Flying; +import net.minecraft.server.Packet130UpdateSign; +import net.minecraft.server.Packet14BlockDig; +import net.minecraft.server.Packet15Place; +import net.minecraft.server.Packet16BlockItemSwitch; +import net.minecraft.server.Packet255KickDisconnect; +import net.minecraft.server.Packet28EntityVelocity; +import net.minecraft.server.Packet3Chat; +import net.minecraft.server.Packet51MapChunk; + +public class EmptyNetHandler extends NetServerHandler { + public EmptyNetHandler(MinecraftServer minecraftServer, NetworkManager networkManager, EntityPlayer entityPlayer) { + super(minecraftServer, networkManager, entityPlayer); + } + + @Override + public void a() { + } + + @Override + public void a(Packet102WindowClick packet) { + } + + @Override + public void a(Packet106Transaction packet) { + } + + @Override + public void a(Packet10Flying packet) { + } + + @Override + public void a(Packet130UpdateSign packet) { + } + + @Override + public void a(Packet14BlockDig packet) { + } + + @Override + public void a(Packet15Place packet) { + } + + @Override + public void a(Packet16BlockItemSwitch packet) { + } + + @Override + public void a(Packet255KickDisconnect packet) { + } + + @Override + public void a(Packet28EntityVelocity packet) { + } + + @Override + public void a(Packet3Chat packet) { + } + + @Override + public void a(Packet51MapChunk packet) { + } + + @Override + public void a(String string, Object[] objects) { + } + + @Override + public void sendMessage(String string) { + } + + @Override + public void sendPacket(Packet packet) { + } +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/network/EmptyNetworkManager.java b/src/main/java/net/citizensnpcs/npc/network/EmptyNetworkManager.java new file mode 100644 index 000000000..daacf89f9 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/network/EmptyNetworkManager.java @@ -0,0 +1,52 @@ +package net.citizensnpcs.npc.network; + +import java.lang.reflect.Field; +import java.net.Socket; + +import net.minecraft.server.NetHandler; +import net.minecraft.server.NetworkManager; +import net.minecraft.server.Packet; + +public class EmptyNetworkManager extends NetworkManager { + + public EmptyNetworkManager(Socket socket, String string, NetHandler netHandler) { + super(socket, string, netHandler); + + try { + // the field above the 3 synchronized lists + Field f = NetworkManager.class.getDeclaredField("l"); + f.setAccessible(true); + f.set(this, false); + } catch (Exception e) { + } + } + + @Override + public void a() { + } + + @Override + public void a(NetHandler netHandler) { + } + + @Override + public void a(String s, Object... objects) { + } + + @Override + public void b() { + } + + @Override + public void d() { + } + + @Override + public int e() { + return 0; + } + + @Override + public void queue(Packet packet) { + } +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/network/EmptySocket.java b/src/main/java/net/citizensnpcs/npc/network/EmptySocket.java new file mode 100644 index 000000000..e68200db0 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/network/EmptySocket.java @@ -0,0 +1,20 @@ +package net.citizensnpcs.npc.network; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class EmptySocket extends Socket { + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(new byte[10]); + } + + @Override + public OutputStream getOutputStream() { + return new ByteArrayOutputStream(); + } +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/trait/Age.java b/src/main/java/net/citizensnpcs/trait/Age.java index a74c1092c..36930889b 100644 --- a/src/main/java/net/citizensnpcs/trait/Age.java +++ b/src/main/java/net/citizensnpcs/trait/Age.java @@ -1,20 +1,18 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.entity.Ageable; -public class Age extends Trait implements Runnable, Toggleable { +public class Age extends Trait implements Toggleable { private int age = 0; - private boolean locked = true; private boolean ageable = false; - private final NPC npc; + private boolean locked = true; - public Age(NPC npc) { - this.npc = npc; + public Age() { + super("age"); } @Override @@ -26,7 +24,7 @@ public class Age extends Trait implements Runnable, Toggleable { } @Override - public void onNPCSpawn() { + public void onSpawn() { if (npc instanceof Ageable) { Ageable entity = (Ageable) npc.getBukkitEntity(); entity.setAge(age); diff --git a/src/main/java/net/citizensnpcs/trait/Behaviour.java b/src/main/java/net/citizensnpcs/trait/Behaviour.java index 2c2e973dc..1f3759d86 100644 --- a/src/main/java/net/citizensnpcs/trait/Behaviour.java +++ b/src/main/java/net/citizensnpcs/trait/Behaviour.java @@ -8,7 +8,6 @@ import java.util.Map.Entry; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.ai.Goal; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.scripting.CompileCallback; import net.citizensnpcs.api.scripting.ScriptFactory; import net.citizensnpcs.api.trait.Trait; @@ -31,7 +30,6 @@ public class Behaviour extends Trait { return new File(rootFolder, arg0); } }; - private final NPC npc; private final File rootFolder = new File(CitizensAPI.getScriptFolder(), "behaviours"); private final List scripts = Lists.newArrayList(); { @@ -39,8 +37,8 @@ public class Behaviour extends Trait { rootFolder.mkdirs(); } - public Behaviour(NPC npc) { - this.npc = npc; + public Behaviour() { + super("behaviour"); } public void addScripts(Iterable scripts) { @@ -58,21 +56,21 @@ public class Behaviour extends Trait { addScripts(Splitter.on(",").split(scripts)); } - @Override - public void onNPCSpawn() { - for (Entry entry : addedGoals.entrySet()) { - npc.getAI().addGoal(entry.getValue(), entry.getKey()); - } - } - @Override public void onRemove() { removeGoals(); } + @Override + public void onSpawn() { + for (Entry entry : addedGoals.entrySet()) { + npc.getDefaultGoalController().addGoal(entry.getKey(), entry.getValue()); + } + } + private void removeGoals() { for (Goal entry : addedGoals.keySet()) { - npc.getAI().removeGoal(entry); + npc.getDefaultGoalController().removeGoal(entry); } } @@ -100,7 +98,7 @@ public class Behaviour extends Trait { if (!npc.isSpawned()) return; for (Entry entry : goals.goals.entrySet()) { - npc.getAI().addGoal(entry.getValue(), entry.getKey()); + npc.getDefaultGoalController().addGoal(entry.getKey(), entry.getValue()); } } @@ -113,7 +111,7 @@ public class Behaviour extends Trait { public static class Goals { private final Map goals = Maps.newHashMap(); - public void addGoal(int priority, Goal goal) { + public void addGoal(Goal goal, int priority) { Validate.notNull(goal); goals.put(goal, priority); } diff --git a/src/main/java/net/citizensnpcs/trait/Controllable.java b/src/main/java/net/citizensnpcs/trait/Controllable.java index b6a6eba86..11e77c988 100644 --- a/src/main/java/net/citizensnpcs/trait/Controllable.java +++ b/src/main/java/net/citizensnpcs/trait/Controllable.java @@ -5,30 +5,32 @@ import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; -import net.citizensnpcs.npc.CitizensNPC; import net.minecraft.server.EntityLiving; import net.minecraft.server.EntityPlayer; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; //TODO: reduce reliance on CitizensNPC -public class Controllable extends Trait implements Runnable, Listener, Toggleable { - private final CitizensNPC npc; +public class Controllable extends Trait implements Toggleable { private boolean enabled; public Controllable(NPC npc) { - this.npc = (CitizensNPC) npc; + super("controllable"); + } + + private EntityLiving getHandle() { + return ((CraftLivingEntity) npc.getBukkitEntity()).getHandle(); } private void jump() { - boolean allowed = npc.getHandle().onGround; + boolean allowed = getHandle().onGround; if (!allowed) return; - npc.getHandle().motY = JUMP_VELOCITY; + getHandle().motY = JUMP_VELOCITY; // TODO: make jumping work in liquid or make liquids float the npc } @@ -43,7 +45,7 @@ public class Controllable extends Trait implements Runnable, Listener, Toggleabl return; EntityPlayer handle = ((CraftPlayer) event.getPlayer()).getHandle(); Action performed = event.getAction(); - if (performed == Action.PHYSICAL || !handle.equals(npc.getHandle().passenger)) + if (performed == Action.PHYSICAL || !handle.equals(getHandle().passenger)) return; if (performed == Action.LEFT_CLICK_AIR || performed == Action.LEFT_CLICK_BLOCK) { jump(); @@ -55,20 +57,20 @@ public class Controllable extends Trait implements Runnable, Listener, Toggleabl if (!npc.isSpawned() || !event.getNPC().equals(npc)) return; EntityPlayer handle = ((CraftPlayer) event.getClicker()).getHandle(); - if (npc.getHandle().passenger != null) { - if (npc.getHandle().passenger == handle) { + if (getHandle().passenger != null) { + if (getHandle().passenger == handle) { event.getClicker().leaveVehicle(); } return; } - handle.setPassengerOf(npc.getHandle()); + handle.setPassengerOf(getHandle()); } @Override public void run() { - if (!npc.isSpawned() || npc.getHandle().passenger == null) + if (!npc.isSpawned() || getHandle().passenger == null) return; - EntityLiving handle = npc.getHandle(); + EntityLiving handle = getHandle(); boolean onGround = handle.onGround; handle.motX += handle.passenger.motX * (onGround ? GROUND_SPEED : AIR_SPEED); handle.motZ += handle.passenger.motZ * (onGround ? GROUND_SPEED : AIR_SPEED); @@ -79,12 +81,13 @@ public class Controllable extends Trait implements Runnable, Listener, Toggleabl key.setBoolean("enabled", enabled); } - private static final double GROUND_SPEED = 4; - private static final double AIR_SPEED = 1.5; - private static final double JUMP_VELOCITY = 0.6; - @Override public boolean toggle() { return (enabled = !enabled); } + + private static final double AIR_SPEED = 1.5; + private static final double GROUND_SPEED = 4; + + private static final double JUMP_VELOCITY = 0.6; } diff --git a/src/main/java/net/citizensnpcs/trait/CurrentLocation.java b/src/main/java/net/citizensnpcs/trait/CurrentLocation.java index c4a4eb91a..0cb55c0cd 100644 --- a/src/main/java/net/citizensnpcs/trait/CurrentLocation.java +++ b/src/main/java/net/citizensnpcs/trait/CurrentLocation.java @@ -1,29 +1,23 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.Bukkit; import org.bukkit.Location; -public class CurrentLocation extends Trait implements Runnable { +public class CurrentLocation extends Trait { private Location loc; - private final NPC npc; - public CurrentLocation(NPC npc) { - this.npc = npc; + public CurrentLocation() { + super("location"); } public Location getLocation() { return loc; } - public void setLocation(Location loc) { - this.loc = loc; - } - @Override public void load(DataKey key) throws NPCLoadException { if (Bukkit.getWorld(key.getString("world")) == null) @@ -56,6 +50,10 @@ public class CurrentLocation extends Trait implements Runnable { key.setDouble("pitch", loc.getPitch()); } + public void setLocation(Location loc) { + this.loc = loc; + } + @Override public String toString() { return "CurrentLocation{" + loc + "}"; diff --git a/src/main/java/net/citizensnpcs/trait/LookClose.java b/src/main/java/net/citizensnpcs/trait/LookClose.java index 2fbab0bfd..2d8de7572 100644 --- a/src/main/java/net/citizensnpcs/trait/LookClose.java +++ b/src/main/java/net/citizensnpcs/trait/LookClose.java @@ -6,7 +6,6 @@ import java.util.List; import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import net.minecraft.server.EntityLiving; @@ -16,13 +15,12 @@ import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -public class LookClose extends Trait implements Runnable, Toggleable { +public class LookClose extends Trait implements Toggleable { private boolean enabled = Setting.DEFAULT_LOOK_CLOSE.asBoolean(); private Player lookingAt; - private final NPC npc; - public LookClose(NPC npc) { - this.npc = npc; + public LookClose() { + super("lookclose"); } private void faceEntity(Entity from, Entity at) { @@ -49,23 +47,6 @@ public class LookClose extends Trait implements Runnable, Toggleable { handle.X = handle.yaw; } - @Override - public void load(DataKey key) throws NPCLoadException { - enabled = key.getBoolean(""); - } - - @Override - public void run() { - if (!enabled || npc.getAI().hasDestination()) - return; - if (hasInvalidTarget()) { - findNewTarget(); - } - if (lookingAt != null) { - faceEntity(npc.getBukkitEntity(), lookingAt); - } - } - private void findNewTarget() { List nearby = npc.getBukkitEntity().getNearbyEntities(2.5, 5, 2.5); Collections.sort(nearby, new Comparator() { @@ -96,6 +77,23 @@ public class LookClose extends Trait implements Runnable, Toggleable { return false; } + @Override + public void load(DataKey key) throws NPCLoadException { + enabled = key.getBoolean(""); + } + + @Override + public void run() { + if (!enabled || npc.getNavigator().isNavigating()) + return; + if (hasInvalidTarget()) { + findNewTarget(); + } + if (lookingAt != null) { + faceEntity(npc.getBukkitEntity(), lookingAt); + } + } + @Override public void save(DataKey key) { key.setBoolean("", enabled); diff --git a/src/main/java/net/citizensnpcs/trait/Powered.java b/src/main/java/net/citizensnpcs/trait/Powered.java index 582123751..5a976e668 100644 --- a/src/main/java/net/citizensnpcs/trait/Powered.java +++ b/src/main/java/net/citizensnpcs/trait/Powered.java @@ -1,18 +1,16 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.entity.Creeper; public class Powered extends Trait implements Toggleable { - private final NPC npc; private boolean powered; - public Powered(NPC npc) { - this.npc = npc; + public Powered() { + super("powered"); } @Override @@ -21,7 +19,7 @@ public class Powered extends Trait implements Toggleable { } @Override - public void onNPCSpawn() { + public void onSpawn() { if (npc.getBukkitEntity() instanceof Creeper) ((Creeper) npc.getBukkitEntity()).setPowered(powered); } diff --git a/src/main/java/net/citizensnpcs/trait/Saddle.java b/src/main/java/net/citizensnpcs/trait/Saddle.java index 6f5629c59..cb7053b82 100644 --- a/src/main/java/net/citizensnpcs/trait/Saddle.java +++ b/src/main/java/net/citizensnpcs/trait/Saddle.java @@ -2,22 +2,19 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.entity.Pig; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEntityEvent; -public class Saddle extends Trait implements Toggleable, Listener { - private final NPC npc; - private boolean saddle; +public class Saddle extends Trait implements Toggleable { private boolean pig; + private boolean saddle; - public Saddle(NPC npc) { - this.npc = npc; + public Saddle() { + super("saddle"); } @Override @@ -25,8 +22,14 @@ public class Saddle extends Trait implements Toggleable, Listener { saddle = key.getBoolean(""); } + @EventHandler + public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { + if (pig && npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getRightClicked()))) + event.setCancelled(true); + } + @Override - public void onNPCSpawn() { + public void onSpawn() { if (npc.getBukkitEntity() instanceof Pig) { ((Pig) npc.getBukkitEntity()).setSaddle(saddle); pig = true; @@ -34,12 +37,6 @@ public class Saddle extends Trait implements Toggleable, Listener { pig = false; } - @EventHandler - public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { - if (pig && npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getRightClicked()))) - event.setCancelled(true); - } - @Override public void save(DataKey key) { key.setBoolean("", saddle); diff --git a/src/main/java/net/citizensnpcs/trait/Sheared.java b/src/main/java/net/citizensnpcs/trait/Sheared.java index 26c4e6a8e..71b821d98 100644 --- a/src/main/java/net/citizensnpcs/trait/Sheared.java +++ b/src/main/java/net/citizensnpcs/trait/Sheared.java @@ -2,21 +2,18 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.entity.Sheep; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerShearEntityEvent; -public class Sheared extends Trait implements Toggleable, Listener { - private final NPC npc; +public class Sheared extends Trait implements Toggleable { private boolean sheared; - public Sheared(NPC npc) { - this.npc = npc; + public Sheared() { + super("sheared"); } @Override @@ -24,17 +21,17 @@ public class Sheared extends Trait implements Toggleable, Listener { sheared = key.getBoolean(""); } - @Override - public void onNPCSpawn() { - ((Sheep) npc.getBukkitEntity()).setSheared(sheared); - } - @EventHandler public void onPlayerShearEntityEvent(PlayerShearEntityEvent event) { if (npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getEntity()))) event.setCancelled(true); } + @Override + public void onSpawn() { + ((Sheep) npc.getBukkitEntity()).setSheared(sheared); + } + @Override public void save(DataKey key) { key.setBoolean("", sheared); diff --git a/src/main/java/net/citizensnpcs/trait/VillagerProfession.java b/src/main/java/net/citizensnpcs/trait/VillagerProfession.java index 786ea71dd..b89cab228 100644 --- a/src/main/java/net/citizensnpcs/trait/VillagerProfession.java +++ b/src/main/java/net/citizensnpcs/trait/VillagerProfession.java @@ -1,7 +1,6 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; @@ -9,11 +8,10 @@ import org.bukkit.entity.Villager; import org.bukkit.entity.Villager.Profession; public class VillagerProfession extends Trait { - private final NPC npc; private Profession profession = Profession.FARMER; - public VillagerProfession(NPC npc) { - this.npc = npc; + public VillagerProfession() { + super("profession"); } @Override @@ -26,7 +24,7 @@ public class VillagerProfession extends Trait { } @Override - public void onNPCSpawn() { + public void onSpawn() { if (npc.getBukkitEntity() instanceof Villager) ((Villager) npc.getBukkitEntity()).setProfession(profession); } diff --git a/src/main/java/net/citizensnpcs/trait/WoolColor.java b/src/main/java/net/citizensnpcs/trait/WoolColor.java index a604b3afe..3ffab5102 100644 --- a/src/main/java/net/citizensnpcs/trait/WoolColor.java +++ b/src/main/java/net/citizensnpcs/trait/WoolColor.java @@ -2,23 +2,20 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.DyeColor; import org.bukkit.entity.Sheep; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; import org.bukkit.event.entity.SheepDyeWoolEvent; -public class WoolColor extends Trait implements Listener { +public class WoolColor extends Trait { private DyeColor color = DyeColor.WHITE; - private final NPC npc; boolean sheep = false; - public WoolColor(NPC npc) { - this.npc = npc; + public WoolColor() { + super("woolcolor"); } @Override @@ -30,8 +27,14 @@ public class WoolColor extends Trait implements Listener { } } + @EventHandler + public void onSheepDyeWool(SheepDyeWoolEvent event) { + if (npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getEntity()))) + event.setCancelled(true); + } + @Override - public void onNPCSpawn() { + public void onSpawn() { if (npc.getBukkitEntity() instanceof Sheep) { ((Sheep) npc.getBukkitEntity()).setColor(color); sheep = true; @@ -39,12 +42,6 @@ public class WoolColor extends Trait implements Listener { sheep = false; } - @EventHandler - public void onSheepDyeWool(SheepDyeWoolEvent event) { - if (npc.equals(CitizensAPI.getNPCRegistry().getNPC(event.getEntity()))) - event.setCancelled(true); - } - @Override public void save(DataKey key) { key.setString("", color.name()); diff --git a/src/main/java/net/citizensnpcs/trait/text/Text.java b/src/main/java/net/citizensnpcs/trait/text/Text.java index 97ae9bf2e..135d68fd3 100644 --- a/src/main/java/net/citizensnpcs/trait/text/Text.java +++ b/src/main/java/net/citizensnpcs/trait/text/Text.java @@ -1,15 +1,16 @@ package net.citizensnpcs.trait.text; import java.util.ArrayList; -import java.util.Calendar; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.TimeUnit; import net.citizensnpcs.Settings.Setting; +import net.citizensnpcs.api.event.NPCRightClickEvent; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; @@ -17,6 +18,7 @@ import net.citizensnpcs.npc.CitizensNPC; import net.citizensnpcs.trait.Toggleable; import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.Paginator; +import net.citizensnpcs.util.Util; import net.minecraft.server.EntityHuman; import net.minecraft.server.EntityLiving; @@ -26,19 +28,20 @@ import org.bukkit.conversations.ConversationAbandonedEvent; import org.bukkit.conversations.ConversationAbandonedListener; import org.bukkit.conversations.ConversationFactory; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; -public class Text extends Trait implements Runnable, Toggleable, ConversationAbandonedListener { - private final Map cooldowns = new HashMap(); +public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener { + private final Map cooldowns = new HashMap(); private int currentIndex; - private final NPC npc; private final Plugin plugin; private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean(); private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean(); private final List text = new ArrayList(); - public Text(NPC npc) { - this.npc = npc; + public Text() { + super("text"); this.plugin = Bukkit.getPluginManager().getPlugin("Citizens"); } @@ -92,8 +95,16 @@ public class Text extends Trait implements Runnable, Toggleable, ConversationAba randomTalker = key.getBoolean("random-talker"); } + @EventHandler + public void onRightClick(NPCRightClickEvent event) { + if (!event.getNPC().equals(npc)) + return; + if (Util.isSettingFulfilled(event.getClicker(), Setting.TALK_ITEM) && !shouldTalkClose()) + sendText(event.getClicker()); + } + @Override - public void onNPCSpawn() { + public void onSpawn() { if (text.isEmpty()) populateDefaultText(); } @@ -116,17 +127,17 @@ public class Text extends Trait implements Runnable, Toggleable, ConversationAba Player player = (Player) search.getBukkitEntity(); // If the cooldown is not expired, do not send text if (cooldowns.get(player.getName()) != null) { - if (!Calendar.getInstance().after(cooldowns.get(player.getName()))) + if (!new Date().after(cooldowns.get(player.getName()))) return; cooldowns.remove(player.getName()); } if (sendText(player)) { // Add a cooldown if the text was successfully sent - Calendar wait = Calendar.getInstance(); - wait.add( - Calendar.SECOND, - (new Random().nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt()) + Setting.TALK_CLOSE_MINIMUM_COOLDOWN - .asInt())); + Date wait = new Date(); + int secondsDelta = new Random().nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt()) + + Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt(); + long millisecondsDelta = TimeUnit.MILLISECONDS.convert(secondsDelta, TimeUnit.SECONDS); + wait.setTime(wait.getTime() + millisecondsDelta); cooldowns.put(player.getName(), wait); } } @@ -162,7 +173,7 @@ public class Text extends Trait implements Runnable, Toggleable, ConversationAba currentIndex = 0; index = currentIndex++; } - npc.chat(player, text.get(index)); + Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + text.get(index), npc); return true; } diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/GenericWaypointCallback.java b/src/main/java/net/citizensnpcs/trait/waypoint/GenericWaypointCallback.java deleted file mode 100644 index dda6807a8..000000000 --- a/src/main/java/net/citizensnpcs/trait/waypoint/GenericWaypointCallback.java +++ /dev/null @@ -1,94 +0,0 @@ -package net.citizensnpcs.trait.waypoint; - -import java.util.Iterator; - -import net.citizensnpcs.api.ai.AI; -import net.citizensnpcs.api.ai.NavigationCallback; - -import org.bukkit.Location; - -public class GenericWaypointCallback extends NavigationCallback { - private AI ai; - private Location dest; - private boolean executing; - private Iterator itr; - private final Iterable provider; - - public GenericWaypointCallback(Iterable provider) { - this.provider = provider; - } - - private void ensureItr() { - if (itr == null || !itr.hasNext()) { - itr = provider.iterator(); - } - } - - @Override - public void onAttach(AI ai) { - this.ai = ai; - executing |= !ai.hasDestination(); - if (!executing) - return; - if (dest == null) { - ensureItr(); - if (itr.hasNext()) { - dest = itr.next().getLocation(); - } - } - if (dest != null) { - ai.setDestination(dest); - } - } - - boolean hackfix = false; - - @Override - public boolean onCancel(AI ai, CancelReason reason) { - if (hackfix) { - hackfix = false; - return false; - } - hackfix = false; - if (executing && reason == CancelReason.REPLACE) { - executing = false; - return false; - } - executing = true; - ensureItr(); - if (dest == null && itr.hasNext()) - dest = itr.next().getLocation(); - if (dest != null) { - hackfix = true; - ai.setDestination(dest); - hackfix = false; - } - return false; - } - - @Override - public boolean onCompletion(AI ai) { - if (executing) { // if we're executing, we need to get the next waypoint - ensureItr(); - dest = itr.hasNext() ? itr.next().getLocation() : null; - } else { - executing = true; - // we're free to return to our waypoints! - // if we had a destination, we will return to it. - } - if (dest != null) { - ai.setDestination(dest); - } - return false; - } - - public void onProviderChanged() { - itr = provider.iterator(); - if (ai == null) - return; - dest = itr.hasNext() ? itr.next().getLocation() : null; - if (dest != null) { - ai.setDestination(dest); - } - } -} diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index c675591d6..d786f77a8 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -3,7 +3,8 @@ package net.citizensnpcs.trait.waypoint; import java.util.Iterator; import java.util.List; -import net.citizensnpcs.api.ai.NavigationCallback; +import net.citizensnpcs.api.ai.Goal; +import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; import net.citizensnpcs.util.Messaging; @@ -21,7 +22,7 @@ import org.bukkit.event.player.PlayerItemHeldEvent; import com.google.common.collect.Lists; public class LinearWaypointProvider implements WaypointProvider, Iterable { - private final GenericWaypointCallback callback = new GenericWaypointCallback(this); + private WaypointGoal currentGoal; private final List waypoints = Lists.newArrayList(); @Override @@ -41,35 +42,37 @@ public class LinearWaypointProvider implements WaypointProvider, Iterable%d, %d, %d", location.getBlockX(), location.getBlockY(), - location.getBlockZ()); + return String.format("%d, %d, %d", location.getBlockX(), + location.getBlockY(), location.getBlockZ()); } @EventHandler - @SuppressWarnings("unused") public void onPlayerInteract(PlayerInteractEvent event) { if (!event.getPlayer().equals(player) || event.getAction() == Action.PHYSICAL) return; - if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_AIR) { + if (event.getAction() == Action.LEFT_CLICK_BLOCK + || event.getAction() == Action.LEFT_CLICK_AIR) { if (event.getClickedBlock() == null) return; Location at = event.getClickedBlock().getLocation(); waypoints.add(Math.max(0, editingSlot), new Waypoint(at)); editingSlot = Math.min(editingSlot + 1, waypoints.size()); - Messaging.send(player, String.format("Added a waypoint at (" + formatLoc(at) - + ") (%d, %d)", editingSlot + 1, waypoints.size())); + Messaging.send( + player, + String.format("Added a waypoint at (" + formatLoc(at) + + ") (%d, %d)", editingSlot + 1, waypoints.size())); } else if (waypoints.size() > 0) { editingSlot = Math.min(0, Math.max(waypoints.size() - 1, editingSlot)); waypoints.remove(editingSlot); editingSlot = Math.max(0, editingSlot - 1); - Messaging.send(player, String.format("Removed a waypoint (%d remaining) (%d)", - waypoints.size(), editingSlot + 1)); + Messaging.send(player, String.format( + "Removed a waypoint (%d remaining) (%d)", waypoints.size(), + editingSlot + 1)); } - callback.onProviderChanged(); + currentGoal.onProviderChanged(); } @EventHandler - @SuppressWarnings("unused") public void onPlayerItemHeldChange(PlayerItemHeldEvent event) { if (!event.getPlayer().equals(player) || waypoints.size() == 0) return; @@ -98,8 +101,10 @@ public class LinearWaypointProvider implements WaypointProvider, Iterable itr; + private final Navigator navigator; + private final Iterable provider; + private GoalSelector selector; + + public WaypointGoal(Iterable provider, Navigator navigator) { + this.provider = provider; + this.navigator = navigator; + } + + private void ensureItr() { + if (itr == null || !itr.hasNext()) { + itr = provider.iterator(); + } + } + + @EventHandler + public void onNavigationCancel(NavigationCancelEvent event) { + if (!event.getNavigator().equals(navigator) || currentDestination == null) + return; + if (currentDestination.equals(event.getNavigator().getTargetAsLocation())) + selector.finish(); + } + + public void onProviderChanged() { + itr = provider.iterator(); + if (currentDestination != null) + selector.finish(); + } + + @Override + public void reset() { + currentDestination = null; + selector = null; + } + + @Override + public void run() { + } + + @Override + public boolean shouldExecute(GoalSelector selector) { + ensureItr(); + boolean shouldExecute = itr.hasNext(); + if (shouldExecute) { + this.selector = selector; + currentDestination = itr.next().getLocation(); + navigator.setTarget(currentDestination); + } + return shouldExecute; + } +} diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/WaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/WaypointProvider.java index a2eed8e83..c45194bc6 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/WaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/WaypointProvider.java @@ -1,7 +1,7 @@ package net.citizensnpcs.trait.waypoint; -import net.citizensnpcs.api.ai.AI; -import net.citizensnpcs.api.ai.NavigationCallback; +import net.citizensnpcs.api.ai.Goal; +import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; @@ -19,12 +19,12 @@ public interface WaypointProvider { public Editor createEditor(Player player); /** - * Returns the {@link NavigationCallback} linked to this provider. This will - * be linked to the NPC's {@link AI}. * - * @return The callback in use + * @param npc + * The attached {@link NPC} the goal is for. + * @return The {@link Goal} to attach to the NPC. */ - public NavigationCallback getCallback(); + public Goal getGoal(NPC npc); /** * Loads from the specified {@link DataKey}. @@ -41,6 +41,4 @@ public interface WaypointProvider { * The key to save to */ public void save(DataKey key); - - public void onAttach(); } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/Waypoints.java b/src/main/java/net/citizensnpcs/trait/waypoint/Waypoints.java index c9c3c1faa..87ac7d354 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/Waypoints.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/Waypoints.java @@ -4,8 +4,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import net.citizensnpcs.api.ai.Goal; import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; @@ -13,13 +13,11 @@ import net.citizensnpcs.editor.Editor; import org.bukkit.entity.Player; public class Waypoints extends Trait { - private final NPC npc; private WaypointProvider provider = new LinearWaypointProvider(); private String providerName = "linear"; - public Waypoints(NPC npc) { - this.npc = npc; - npc.getAI().registerNavigationCallback(provider.getCallback()); + public Waypoints() { + super("waypoints"); } private WaypointProvider create(Class clazz) { @@ -32,11 +30,6 @@ public class Waypoints extends Trait { } } - @Override - public void onNPCSpawn() { - npc.getAI().registerNavigationCallback(provider.getCallback()); - } - public Editor getEditor(Player player) { return provider.createEditor(player); } @@ -56,6 +49,14 @@ public class Waypoints extends Trait { provider.load(key.getRelative(providerName)); } + @Override + public void onSpawn() { + if (provider != null) { + Goal goal = provider.getGoal(getNPC()); + getNPC().getDefaultGoalController().addGoal(goal, 1); + } + } + @Override public void save(DataKey key) { if (provider == null) diff --git a/src/main/java/net/citizensnpcs/util/Messaging.java b/src/main/java/net/citizensnpcs/util/Messaging.java index ec007a9ba..3ff8c1c05 100644 --- a/src/main/java/net/citizensnpcs/util/Messaging.java +++ b/src/main/java/net/citizensnpcs/util/Messaging.java @@ -22,6 +22,12 @@ public class Messaging { log(msg); } + private static String getFormatted(Object[] msg) { + String toFormat = msg[0].toString(); + Object[] args = msg.length > 1 ? Arrays.copyOfRange(msg, 1, msg.length) : new Object[] {}; + return String.format(toFormat, args); + } + public static void log(Level level, Object... msg) { Bukkit.getLogger().log(level, "[Citizens] " + SPACE.join(msg)); } @@ -30,12 +36,6 @@ public class Messaging { log(Level.INFO, msg); } - private static String getFormatted(Object[] msg) { - String toFormat = msg[0].toString(); - Object[] args = msg.length > 1 ? Arrays.copyOfRange(msg, 1, msg.length) : new Object[] {}; - return String.format(toFormat, args); - } - public static void logF(Object... msg) { log(getFormatted(msg)); } @@ -46,12 +46,6 @@ public class Messaging { sender.sendMessage(joined); } - public static void sendF(CommandSender sender, Object... msg) { - String joined = getFormatted(msg); - joined = StringHelper.parseColors(joined); - sender.sendMessage(joined); - } - public static void sendError(CommandSender sender, Object... msg) { send(sender, ChatColor.RED.toString() + SPACE.join(msg)); } @@ -60,6 +54,12 @@ public class Messaging { sendF(sender, ChatColor.RED.toString() + SPACE.join(msg)); } + public static void sendF(CommandSender sender, Object... msg) { + String joined = getFormatted(msg); + joined = StringHelper.parseColors(joined); + sender.sendMessage(joined); + } + public static void sendWithNPC(CommandSender sender, Object msg, NPC npc) { String send = msg.toString(); diff --git a/src/main/java/net/citizensnpcs/util/Util.java b/src/main/java/net/citizensnpcs/util/Util.java index 95e209f56..10ebf65a9 100644 --- a/src/main/java/net/citizensnpcs/util/Util.java +++ b/src/main/java/net/citizensnpcs/util/Util.java @@ -26,6 +26,8 @@ public class Util { private Util() { } + private static final Map, Class> primitiveClassMap = Maps.newHashMap(); + /** * Given a set of instantiation parameters, attempts to find a matching * constructor with the greatest number of matching class parameters and @@ -85,6 +87,17 @@ public class Util { return null; } + public static boolean isSettingFulfilled(Player player, Setting setting) { + String parts = setting.asString(); + if (parts.contains("*")) + return true; + for (String part : Splitter.on(',').split(parts)) { + if (Material.matchMaterial(part) == player.getItemInHand().getType()) { + return true; + } + } + return false; + } private static boolean searchInterfaces(Class class1, Class class2) { for (Class test : class1.getInterfaces()) if (test == class2) @@ -92,7 +105,31 @@ public class Util { return false; } - private static final Map, Class> primitiveClassMap = Maps.newHashMap(); + public static void sendPacketNearby(Location location, Packet packet, double radius) { + radius *= radius; + final World world = location.getWorld(); + for (Player ply : Bukkit.getServer().getOnlinePlayers()) { + if (ply == null || world != ply.getWorld()) { + continue; + } + if (location.distanceSquared(ply.getLocation()) > radius) { + continue; + } + ((CraftPlayer) ply).getHandle().netServerHandler.sendPacket(packet); + } + } + + public static void sendToOnline(Packet... packets) { + Validate.notNull(packets, "packets cannot be null"); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player == null || !player.isOnline()) + continue; + for (Packet packet : packets) { + ((CraftPlayer) player).getHandle().netServerHandler.sendPacket(packet); + } + } + } + static { primitiveClassMap.put(Boolean.class, boolean.class); primitiveClassMap.put(Byte.class, byte.class); @@ -111,41 +148,4 @@ public class Util { primitiveClassMap.put(float.class, Float.class); primitiveClassMap.put(double.class, Double.class); } - - public static boolean isSettingFulfilled(Player player, Setting setting) { - String parts = setting.asString(); - if (parts.contains("*")) - return true; - for (String part : Splitter.on(',').split(parts)) { - if (Material.matchMaterial(part) == player.getItemInHand().getType()) { - return true; - } - } - return false; - } - - public static void sendToOnline(Packet... packets) { - Validate.notNull(packets, "packets cannot be null"); - for (Player player : Bukkit.getOnlinePlayers()) { - if (player == null || !player.isOnline()) - continue; - for (Packet packet : packets) { - ((CraftPlayer) player).getHandle().netServerHandler.sendPacket(packet); - } - } - } - - public static void sendPacketNearby(Location location, Packet packet, double radius) { - radius *= radius; - final World world = location.getWorld(); - for (Player ply : Bukkit.getServer().getOnlinePlayers()) { - if (ply == null || world != ply.getWorld()) { - continue; - } - if (location.distanceSquared(ply.getLocation()) > radius) { - continue; - } - ((CraftPlayer) ply).getHandle().netServerHandler.sendPacket(packet); - } - } }