Merge Speech branch into master for testing. Please backup your current saves and config before using this build.

This commit is contained in:
Jeremy Schroeder 2012-12-26 15:49:53 -05:00
parent 3dd8d9242b
commit 17bb7a9cd6
9 changed files with 1457 additions and 1102 deletions

View File

@ -1,351 +1,363 @@
package net.citizensnpcs; package net.citizensnpcs;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Iterator; import java.util.Iterator;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.CitizensPlugin; import net.citizensnpcs.api.CitizensPlugin;
import net.citizensnpcs.api.event.CitizensDisableEvent; import net.citizensnpcs.api.ai.speech.SpeechFactory;
import net.citizensnpcs.api.event.CitizensEnableEvent; import net.citizensnpcs.api.event.CitizensDisableEvent;
import net.citizensnpcs.api.event.CitizensReloadEvent; import net.citizensnpcs.api.event.CitizensEnableEvent;
import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.event.CitizensReloadEvent;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.exception.NPCLoadException;
import net.citizensnpcs.api.npc.NPCRegistry; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.scripting.EventRegistrar; import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.api.scripting.ObjectProvider; import net.citizensnpcs.api.scripting.EventRegistrar;
import net.citizensnpcs.api.scripting.ScriptCompiler; import net.citizensnpcs.api.scripting.ObjectProvider;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.scripting.ScriptCompiler;
import net.citizensnpcs.api.trait.TraitFactory; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.command.CommandContext; import net.citizensnpcs.api.trait.TraitFactory;
import net.citizensnpcs.command.CommandManager; import net.citizensnpcs.command.CommandContext;
import net.citizensnpcs.command.CommandManager.CommandInfo; import net.citizensnpcs.command.CommandManager;
import net.citizensnpcs.command.Injector; import net.citizensnpcs.command.CommandManager.CommandInfo;
import net.citizensnpcs.command.RequirementsProcessor; import net.citizensnpcs.command.Injector;
import net.citizensnpcs.command.command.AdminCommands; import net.citizensnpcs.command.RequirementsProcessor;
import net.citizensnpcs.command.command.EditorCommands; import net.citizensnpcs.command.command.AdminCommands;
import net.citizensnpcs.command.command.HelpCommands; import net.citizensnpcs.command.command.EditorCommands;
import net.citizensnpcs.command.command.NPCCommands; import net.citizensnpcs.command.command.HelpCommands;
import net.citizensnpcs.command.command.ScriptCommands; import net.citizensnpcs.command.command.NPCCommands;
import net.citizensnpcs.command.command.TemplateCommands; import net.citizensnpcs.command.command.ScriptCommands;
import net.citizensnpcs.command.command.TraitCommands; import net.citizensnpcs.command.command.TemplateCommands;
import net.citizensnpcs.command.command.WaypointCommands; import net.citizensnpcs.command.command.TraitCommands;
import net.citizensnpcs.editor.Editor; import net.citizensnpcs.command.command.WaypointCommands;
import net.citizensnpcs.npc.CitizensNPCRegistry; import net.citizensnpcs.editor.Editor;
import net.citizensnpcs.npc.CitizensTraitFactory; import net.citizensnpcs.npc.CitizensNPCRegistry;
import net.citizensnpcs.npc.NPCSelector; import net.citizensnpcs.npc.CitizensTraitFactory;
import net.citizensnpcs.util.Messages; import net.citizensnpcs.npc.NPCSelector;
import net.citizensnpcs.util.Messaging; import net.citizensnpcs.npc.ai.speech.Chat;
import net.citizensnpcs.util.NMS; import net.citizensnpcs.npc.ai.speech.CitizensSpeechFactory;
import net.citizensnpcs.util.StringHelper; import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Util; import net.citizensnpcs.util.Messaging;
import net.milkbowl.vault.economy.Economy; import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.StringHelper;
import org.bukkit.Bukkit; import net.citizensnpcs.util.Util;
import org.bukkit.ChatColor; import net.milkbowl.vault.economy.Economy;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin; import org.bukkit.ChatColor;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.command.Command;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import com.google.common.collect.Iterables; import org.bukkit.plugin.RegisteredServiceProvider;
import com.google.common.io.Files; import org.bukkit.plugin.java.JavaPlugin;
import com.google.common.io.InputSupplier;
import com.google.common.collect.Iterables;
public class Citizens extends JavaPlugin implements CitizensPlugin { import com.google.common.io.Files;
private final CommandManager commands = new CommandManager(); import com.google.common.io.InputSupplier;
private boolean compatible;
private Settings config; public class Citizens extends JavaPlugin implements CitizensPlugin {
private CitizensNPCRegistry npcRegistry; private final CommandManager commands = new CommandManager();
private NPCDataStore saves; private boolean compatible;
private NPCSelector selector; private Settings config;
private CitizensTraitFactory traitFactory; private CitizensNPCRegistry npcRegistry;
private NPCDataStore saves;
private void despawnNPCs() { private NPCSelector selector;
Iterator<NPC> itr = npcRegistry.iterator(); private CitizensTraitFactory traitFactory;
while (itr.hasNext()) { private CitizensSpeechFactory speechFactory;
NPC npc = itr.next();
try { private void despawnNPCs() {
npc.despawn(); Iterator<NPC> itr = npcRegistry.iterator();
for (Trait trait : npc.getTraits()) while (itr.hasNext()) {
trait.onRemove(); NPC npc = itr.next();
} catch (Throwable e) { try {
e.printStackTrace(); npc.despawn();
// ensure that all entities are despawned for (Trait trait : npc.getTraits())
} trait.onRemove();
itr.remove(); } catch (Throwable e) {
} e.printStackTrace();
} // ensure that all entities are despawned
}
public void test() { itr.remove();
getDataFolder().mkdirs(); }
final InputStream dllResource = getResource("path/to/dll"); }
try {
Files.copy(new InputSupplier<InputStream>() { public void test() {
@Override getDataFolder().mkdirs();
public InputStream getInput() throws IOException { final InputStream dllResource = getResource("path/to/dll");
return dllResource; try {
} Files.copy(new InputSupplier<InputStream>() {
}, new File(getDataFolder(), "name.dll")); @Override
} catch (IOException e) { public InputStream getInput() throws IOException {
e.printStackTrace(); return dllResource;
} }
// code here }, new File(getDataFolder(), "name.dll"));
new File(getDataFolder(), "name.dll").delete(); } catch (IOException e) {
} e.printStackTrace();
}
private void enableSubPlugins() { // code here
File root = new File(getDataFolder(), Setting.SUBPLUGIN_FOLDER.asString()); new File(getDataFolder(), "name.dll").delete();
if (!root.exists() || !root.isDirectory()) }
return;
File[] files = root.listFiles(); private void enableSubPlugins() {
for (File file : files) { File root = new File(getDataFolder(), Setting.SUBPLUGIN_FOLDER.asString());
Plugin plugin; if (!root.exists() || !root.isDirectory())
try { return;
plugin = Bukkit.getPluginManager().loadPlugin(file); File[] files = root.listFiles();
} catch (Exception e) { for (File file : files) {
continue; Plugin plugin;
} try {
if (plugin == null) plugin = Bukkit.getPluginManager().loadPlugin(file);
continue; } catch (Exception e) {
// code beneath modified from CraftServer continue;
try { }
Messaging.logTr(Messages.LOADING_SUB_PLUGIN, plugin.getDescription().getFullName()); if (plugin == null)
plugin.onLoad(); continue;
} catch (Throwable ex) { // code beneath modified from CraftServer
Messaging.severeTr(Messages.ERROR_INITALISING_SUB_PLUGIN, ex.getMessage(), plugin try {
.getDescription().getFullName()); Messaging.logTr(Messages.LOADING_SUB_PLUGIN, plugin.getDescription().getFullName());
ex.printStackTrace(); plugin.onLoad();
} } catch (Throwable ex) {
} Messaging.severeTr(Messages.ERROR_INITALISING_SUB_PLUGIN, ex.getMessage(), plugin
NMS.loadPlugins(); .getDescription().getFullName());
} ex.printStackTrace();
}
public CommandInfo getCommandInfo(String rootCommand, String modifier) { }
return commands.getCommand(rootCommand, modifier); NMS.loadPlugins();
} }
public Iterable<CommandInfo> getCommands(String base) { public CommandInfo getCommandInfo(String rootCommand, String modifier) {
return commands.getCommands(base); return commands.getCommand(rootCommand, modifier);
} }
@Override public Iterable<CommandInfo> getCommands(String base) {
public NPCRegistry getNPCRegistry() { return commands.getCommands(base);
return npcRegistry; }
}
@Override
public NPCSelector getNPCSelector() { public NPCRegistry getNPCRegistry() {
return selector; return npcRegistry;
} }
@Override public NPCSelector getNPCSelector() {
public File getScriptFolder() { return selector;
return new File(getDataFolder(), "scripts"); }
}
@Override
@Override public File getScriptFolder() {
public TraitFactory getTraitFactory() { return new File(getDataFolder(), "scripts");
return traitFactory; }
}
@Override
@Override public TraitFactory getTraitFactory() {
public boolean onCommand(CommandSender sender, Command command, String cmdName, String[] args) { return traitFactory;
String modifier = args.length > 0 ? args[0] : ""; }
if (!commands.hasCommand(command, modifier) && !modifier.isEmpty()) {
return suggestClosestModifier(sender, command.getName(), modifier); @Override
} public SpeechFactory getSpeechFactory() {
return speechFactory;
NPC npc = selector == null ? null : selector.getSelected(sender); }
// TODO: change the args supplied to a context style system for
// flexibility (ie. adding more context in the future without @Override
// changing everything) public boolean onCommand(CommandSender sender, Command command, String cmdName, String[] args) {
String modifier = args.length > 0 ? args[0] : "";
Object[] methodArgs = { sender, npc }; if (!commands.hasCommand(command, modifier) && !modifier.isEmpty()) {
return commands.executeSafe(command, args, sender, methodArgs); return suggestClosestModifier(sender, command.getName(), modifier);
} }
@Override NPC npc = selector == null ? null : selector.getSelected(sender);
public void onDisable() { // TODO: change the args supplied to a context style system for
Bukkit.getPluginManager().callEvent(new CitizensDisableEvent()); // flexibility (ie. adding more context in the future without
Editor.leaveAll(); // changing everything)
CitizensAPI.shutdown();
Object[] methodArgs = { sender, npc };
// Don't bother with this part if MC versions are not compatible return commands.executeSafe(command, args, sender, methodArgs);
if (compatible) { }
saves.storeAll(npcRegistry);
saves.saveToDiskImmediate(); @Override
despawnNPCs(); public void onDisable() {
npcRegistry = null; Bukkit.getPluginManager().callEvent(new CitizensDisableEvent());
} Editor.leaveAll();
} CitizensAPI.shutdown();
@Override // Don't bother with this part if MC versions are not compatible
public void onEnable() { if (compatible) {
CitizensAPI.setImplementation(this); saves.storeAll(npcRegistry);
// Disable if the server is not using the compatible Minecraft version saves.saveToDiskImmediate();
String mcVersion = Util.getMinecraftVersion(); despawnNPCs();
compatible = mcVersion.startsWith(COMPATIBLE_MC_VERSION); npcRegistry = null;
if (!compatible) { }
Messaging.severeTr(Messages.CITIZENS_INCOMPATIBLE, getDescription().getVersion(), mcVersion); }
getServer().getPluginManager().disablePlugin(this);
return; @Override
} public void onEnable() {
config = new Settings(getDataFolder()); CitizensAPI.setImplementation(this);
registerScriptHelpers(); // Disable if the server is not using the compatible Minecraft version
String mcVersion = Util.getMinecraftVersion();
saves = NPCDataStore.create(getDataFolder()); compatible = mcVersion.startsWith(COMPATIBLE_MC_VERSION);
if (saves == null) { if (!compatible) {
Messaging.severeTr(Messages.FAILED_LOAD_SAVES); Messaging.severeTr(Messages.CITIZENS_INCOMPATIBLE, getDescription().getVersion(), mcVersion);
getServer().getPluginManager().disablePlugin(this); getServer().getPluginManager().disablePlugin(this);
return; return;
} }
config = new Settings(getDataFolder());
npcRegistry = new CitizensNPCRegistry(saves); registerScriptHelpers();
traitFactory = new CitizensTraitFactory();
selector = new NPCSelector(this); saves = NPCDataStore.create(getDataFolder());
if (saves == null) {
getServer().getPluginManager().registerEvents(new EventListen(), this); Messaging.severeTr(Messages.FAILED_LOAD_SAVES);
getServer().getPluginManager().disablePlugin(this);
if (Setting.NPC_COST.asDouble() > 0) return;
setupEconomy(); }
registerCommands(); npcRegistry = new CitizensNPCRegistry(saves);
enableSubPlugins(); traitFactory = new CitizensTraitFactory();
selector = new NPCSelector(this);
// Setup NPCs after all plugins have been enabled (allows for multiworld speechFactory = new CitizensSpeechFactory();
// support and for NPCs to properly register external settings) speechFactory.register(Chat.class, "chat");
if (getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override getServer().getPluginManager().registerEvents(new EventListen(), this);
public void run() {
saves.loadInto(npcRegistry); if (Setting.NPC_COST.asDouble() > 0)
startMetrics(); setupEconomy();
scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
Bukkit.getPluginManager().callEvent(new CitizensEnableEvent()); registerCommands();
} enableSubPlugins();
}, 1) == -1) {
Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED); // Setup NPCs after all plugins have been enabled (allows for multiworld
getServer().getPluginManager().disablePlugin(this); // support and for NPCs to properly register external settings)
} if (getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
} @Override
public void run() {
@Override saves.loadInto(npcRegistry);
public void onImplementationChanged() { startMetrics();
Messaging.severeTr(Messages.CITIZENS_IMPLEMENTATION_DISABLED); scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().callEvent(new CitizensEnableEvent());
} }
}, 1) == -1) {
public void registerCommandClass(Class<?> clazz) { Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED);
try { getServer().getPluginManager().disablePlugin(this);
commands.register(clazz); }
} catch (Throwable ex) { }
Messaging.logTr(Messages.CITIZENS_INVALID_COMMAND_CLASS);
ex.printStackTrace(); @Override
} public void onImplementationChanged() {
} Messaging.severeTr(Messages.CITIZENS_IMPLEMENTATION_DISABLED);
Bukkit.getPluginManager().disablePlugin(this);
private void registerCommands() { }
commands.setInjector(new Injector(this));
commands.registerAnnotationProcessor(new RequirementsProcessor()); public void registerCommandClass(Class<?> clazz) {
try {
// Register command classes commands.register(clazz);
commands.register(AdminCommands.class); } catch (Throwable ex) {
commands.register(EditorCommands.class); Messaging.logTr(Messages.CITIZENS_INVALID_COMMAND_CLASS);
commands.register(HelpCommands.class); ex.printStackTrace();
commands.register(NPCCommands.class); }
commands.register(ScriptCommands.class); }
commands.register(TemplateCommands.class);
commands.register(TraitCommands.class); private void registerCommands() {
commands.register(WaypointCommands.class); commands.setInjector(new Injector(this));
} commands.registerAnnotationProcessor(new RequirementsProcessor());
private void registerScriptHelpers() { // Register command classes
ScriptCompiler compiler = CitizensAPI.getScriptCompiler(); commands.register(AdminCommands.class);
compiler.registerGlobalContextProvider(new EventRegistrar(this)); commands.register(EditorCommands.class);
compiler.registerGlobalContextProvider(new ObjectProvider("plugin", this)); commands.register(HelpCommands.class);
} commands.register(NPCCommands.class);
commands.register(ScriptCommands.class);
public void reload() throws NPCLoadException { commands.register(TemplateCommands.class);
Editor.leaveAll(); commands.register(TraitCommands.class);
config.reload(); commands.register(WaypointCommands.class);
despawnNPCs(); }
saves.loadInto(npcRegistry);
private void registerScriptHelpers() {
getServer().getPluginManager().callEvent(new CitizensReloadEvent()); ScriptCompiler compiler = CitizensAPI.getScriptCompiler();
} compiler.registerGlobalContextProvider(new EventRegistrar(this));
compiler.registerGlobalContextProvider(new ObjectProvider("plugin", this));
private void scheduleSaveTask(int delay) { }
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override public void reload() throws NPCLoadException {
public void run() { Editor.leaveAll();
storeNPCs(); config.reload();
saves.saveToDisk(); despawnNPCs();
} saves.loadInto(npcRegistry);
});
} getServer().getPluginManager().callEvent(new CitizensReloadEvent());
}
private void setupEconomy() {
try { private void scheduleSaveTask(int delay) {
RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration( Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
Economy.class); @Override
if (provider != null && provider.getProvider() != null) { public void run() {
Economy economy = provider.getProvider(); storeNPCs();
Bukkit.getPluginManager().registerEvents(new PaymentListener(economy), this); saves.saveToDisk();
} }
} catch (NoClassDefFoundError e) { });
Messaging.logTr(Messages.ERROR_LOADING_ECONOMY); }
}
} private void setupEconomy() {
try {
private void startMetrics() { RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration(
try { Economy.class);
Metrics metrics = new Metrics(Citizens.this); if (provider != null && provider.getProvider() != null) {
if (metrics.isOptOut()) Economy economy = provider.getProvider();
return; Bukkit.getPluginManager().registerEvents(new PaymentListener(economy), this);
metrics.addCustomData(new Metrics.Plotter("Total NPCs") { }
@Override } catch (NoClassDefFoundError e) {
public int getValue() { Messaging.logTr(Messages.ERROR_LOADING_ECONOMY);
if (npcRegistry == null) }
return 0; }
return Iterables.size(npcRegistry);
} private void startMetrics() {
}); try {
Metrics metrics = new Metrics(Citizens.this);
traitFactory.addPlotters(metrics.createGraph("traits")); if (metrics.isOptOut())
saves.addPlotters(metrics.createGraph("Storage type")); return;
metrics.start(); metrics.addCustomData(new Metrics.Plotter("Total NPCs") {
} catch (IOException e) { @Override
Messaging.logTr(Messages.METRICS_ERROR_NOTIFICATION, e.getMessage()); public int getValue() {
} if (npcRegistry == null)
} return 0;
return Iterables.size(npcRegistry);
public void storeNPCs() { }
if (saves == null) });
return;
for (NPC npc : npcRegistry) traitFactory.addPlotters(metrics.createGraph("traits"));
saves.store(npc); saves.addPlotters(metrics.createGraph("Storage type"));
} metrics.start();
} catch (IOException e) {
public void storeNPCs(CommandContext args) { Messaging.logTr(Messages.METRICS_ERROR_NOTIFICATION, e.getMessage());
storeNPCs(); }
boolean async = args.hasFlag('a'); }
if (async)
saves.saveToDisk(); public void storeNPCs() {
else if (saves == null)
saves.saveToDiskImmediate(); return;
} for (NPC npc : npcRegistry)
saves.store(npc);
private boolean suggestClosestModifier(CommandSender sender, String command, String modifier) { }
String closest = commands.getClosestCommandModifier(command, modifier);
if (!closest.isEmpty()) { public void storeNPCs(CommandContext args) {
sender.sendMessage(ChatColor.GRAY + Messaging.tr(Messages.UNKNOWN_COMMAND)); storeNPCs();
sender.sendMessage(StringHelper.wrap(" /") + command + " " + StringHelper.wrap(closest)); boolean async = args.hasFlag('a');
return true; if (async)
} saves.saveToDisk();
return false; else
} saves.saveToDiskImmediate();
}
private static final String COMPATIBLE_MC_VERSION = "1.4.6";
private boolean suggestClosestModifier(CommandSender sender, String command, String modifier) {
String closest = commands.getClosestCommandModifier(command, modifier);
if (!closest.isEmpty()) {
sender.sendMessage(ChatColor.GRAY + Messaging.tr(Messages.UNKNOWN_COMMAND));
sender.sendMessage(StringHelper.wrap(" /") + command + " " + StringHelper.wrap(closest));
return true;
}
return false;
}
private static final String COMPATIBLE_MC_VERSION = "1.4.6";
} }

View File

@ -1,142 +1,149 @@
package net.citizensnpcs; package net.citizensnpcs;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.Storage; import net.citizensnpcs.api.util.Storage;
import net.citizensnpcs.api.util.YamlStorage; import net.citizensnpcs.api.util.YamlStorage;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class Settings { public class Settings {
private final Storage config; private final Storage config;
private final DataKey root; private final DataKey root;
public Settings(File folder) { public Settings(File folder) {
config = new YamlStorage(new File(folder, "config.yml"), "Citizens Configuration"); config = new YamlStorage(new File(folder, "config.yml"), "Citizens Configuration");
root = config.getKey(""); root = config.getKey("");
config.load(); config.load();
for (Setting setting : Setting.values()) { for (Setting setting : Setting.values()) {
if (!root.keyExists(setting.path)) { if (!root.keyExists(setting.path)) {
setting.setAtKey(root); setting.setAtKey(root);
} else } else
setting.loadFromKey(root); setting.loadFromKey(root);
} }
save(); save();
} }
public void reload() { public void reload() {
config.load(); config.load();
for (Setting setting : Setting.values()) for (Setting setting : Setting.values())
if (root.keyExists(setting.path)) if (root.keyExists(setting.path))
setting.loadFromKey(root); setting.loadFromKey(root);
save(); save();
} }
public void save() { public void save() {
config.save(); config.save();
} }
public enum Setting { public enum Setting {
CHAT_PREFIX("npc.chat.prefix", "[<npc>]: "), CHAT_FORMAT("npc.chat.format.no-targets", "[<npc>]: <text>"),
DATABASE_DRIVER("storage.database.driver", ""), CHAT_FORMAT_TO_TARGET("npc.chat.format.to-target", "[<npc>] -> You: <text>"),
DATABASE_PASSWORD("storage.database.password", ""), CHAT_FORMAT_TO_BYSTANDERS("npc.chat.format.with-target-to-bystanders", "[<npc>] -> [<target>]: <text>"),
DATABASE_URL("storage.database.url", ""), CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS("npc.chat.format.with-targets-to-bystanders", "[<npc>] -> [<targets>]: <text>"),
DATABASE_USERNAME("storage.database.username", ""), CHAT_RANGE("npc.chat.options.range", 5),
DEBUG_MODE("general.debug-mode", false), CHAT_BYSTANDERS_HEAR_TARGETED_CHAT("npc.chat.options.bystanders-hear-targeted-chat", true),
DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false), CHAT_MAX_NUMBER_OF_TARGETS("npc.chat.options.max-number-of-targets-to-show", 2),
DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5), CHAT_MULTIPLE_TARGETS_FORMAT("npc.chat.options.multiple-targets-format", "<target>|, <target>| & <target>| & others"),
DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10), DATABASE_DRIVER("storage.database.driver", ""),
DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F), DATABASE_PASSWORD("storage.database.password", ""),
DEFAULT_RANDOM_TALKER("npc.default.random-talker", true), DATABASE_URL("storage.database.url", ""),
DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false), DATABASE_USERNAME("storage.database.username", ""),
DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1), DEBUG_MODE("general.debug-mode", false),
DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false), DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false),
DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5), DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5),
DEFAULT_TEXT("npc.default.text.0", "Hi, I'm <npc>!") { DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10),
@Override DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F),
public void loadFromKey(DataKey root) { DEFAULT_RANDOM_TALKER("npc.default.random-talker", true),
List<String> list = new ArrayList<String>(); DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false),
for (DataKey key : root.getRelative("npc.default.text").getSubKeys()) DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1),
list.add(key.getString("")); DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false),
value = list; DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5),
} DEFAULT_TEXT("npc.default.text.0", "Hi, I'm <npc>!") {
}, @Override
HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "<e>"), public void loadFromKey(DataKey root) {
KEEP_CHUNKS_LOADED("npc.chunks.always-keep-loaded", false), List<String> list = new ArrayList<String>();
LOCALE("general.translation.locale", ""), for (DataKey key : root.getRelative("npc.default.text").getSubKeys())
MAX_NPC_LIMIT_CHECKS("npc.limits.max-permission-checks", 100), list.add(key.getString(""));
MAX_SPEED("npc.limits.max-speed", 100), value = list;
MESSAGE_COLOUR("general.color-scheme.message", "<a>"), }
NPC_COST("economy.npc.cost", 100D), },
QUICK_SELECT("npc.selection.quick-select", false), HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "<e>"),
REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true), KEEP_CHUNKS_LOADED("npc.chunks.always-keep-loaded", false),
SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60), LOCALE("general.translation.locale", ""),
SELECTION_ITEM("npc.selection.item", "280"), MAX_NPC_LIMIT_CHECKS("npc.limits.max-permission-checks", 100),
SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"), MAX_SPEED("npc.limits.max-speed", 100),
SERVER_OWNS_NPCS("npc.server-ownership", false), MESSAGE_COLOUR("general.color-scheme.message", "<a>"),
STORAGE_FILE("storage.file", "saves.yml"), NPC_COST("economy.npc.cost", 100D),
STORAGE_TYPE("storage.type", "yaml"), QUICK_SELECT("npc.selection.quick-select", false),
SUBPLUGIN_FOLDER("subplugins.folder", "plugins"), REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true),
TALK_CLOSE_MAXIMUM_COOLDOWN("npc.text.max-talk-cooldown", 60), SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60),
TALK_CLOSE_MINIMUM_COOLDOWN("npc.text.min-talk-cooldown", 30), SELECTION_ITEM("npc.selection.item", "280"),
TALK_ITEM("npc.text.talk-item", "340"), SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"),
USE_NEW_PATHFINDER("npc.pathfinding.use-new-finder", false); SERVER_OWNS_NPCS("npc.server-ownership", false),
STORAGE_FILE("storage.file", "saves.yml"),
protected String path; STORAGE_TYPE("storage.type", "yaml"),
protected Object value; SUBPLUGIN_FOLDER("subplugins.folder", "plugins"),
TALK_CLOSE_MAXIMUM_COOLDOWN("npc.text.max-talk-cooldown", 60),
Setting(String path, Object value) { TALK_CLOSE_MINIMUM_COOLDOWN("npc.text.min-talk-cooldown", 30),
this.path = path; TALK_ITEM("npc.text.talk-item", "340"),
this.value = value; USE_NEW_PATHFINDER("npc.pathfinding.use-new-finder", false);
}
protected String path;
public boolean asBoolean() { protected Object value;
return (Boolean) value;
} Setting(String path, Object value) {
this.path = path;
public double asDouble() { this.value = value;
return ((Number) value).doubleValue(); }
}
public boolean asBoolean() {
public float asFloat() { return (Boolean) value;
return ((Number) value).floatValue(); }
}
public double asDouble() {
public int asInt() { return ((Number) value).doubleValue();
if (value instanceof String) { }
return Integer.parseInt(value.toString());
} public float asFloat() {
return ((Number) value).intValue(); return ((Number) value).floatValue();
} }
@SuppressWarnings("unchecked") public int asInt() {
public List<String> asList() { if (value instanceof String) {
if (!(value instanceof List)) { return Integer.parseInt(value.toString());
value = Lists.newArrayList(value); }
} return ((Number) value).intValue();
return (List<String>) value; }
}
@SuppressWarnings("unchecked")
public long asLong() { public List<String> asList() {
return ((Number) value).longValue(); if (!(value instanceof List)) {
} value = Lists.newArrayList(value);
}
public String asString() { return (List<String>) value;
return value.toString(); }
}
public long asLong() {
protected void loadFromKey(DataKey root) { return ((Number) value).longValue();
value = root.getRaw(path); }
}
public String asString() {
protected void setAtKey(DataKey root) { return value.toString();
root.setRaw(path, value); }
}
} protected void loadFromKey(DataKey root) {
value = root.getRaw(path);
}
protected void setAtKey(DataKey root) {
root.setRaw(path, value);
}
}
} }

View File

@ -8,6 +8,7 @@ import java.util.List;
import net.citizensnpcs.Citizens; import net.citizensnpcs.Citizens;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.event.PlayerCreateNPCEvent; import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry; import net.citizensnpcs.api.npc.NPCRegistry;
@ -15,6 +16,7 @@ import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.trait.trait.Owner; import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.trait.trait.Spawned; import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.api.trait.trait.Speech;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.MemoryDataKey; import net.citizensnpcs.api.util.MemoryDataKey;
import net.citizensnpcs.command.Command; import net.citizensnpcs.command.Command;
@ -959,6 +961,45 @@ public class NPCCommands {
} }
} }
@Command(
aliases = { "npc" },
usage = "speak message to speak --target npcid|player_name --type vocal_type",
desc = "Uses the NPCs SpeechController to talk",
modifiers = { "speak" },
min = 2,
permission = "npc.speak")
public void speak(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
String type = npc.getTrait(Speech.class).getDefaultVocalChord();
String message = StringHelper.parseColors(args.getJoinedStrings(1));
if (message.length() <= 0) {
Messaging.send(sender, "Default Vocal Chord for " + npc.getName() + ": " + npc.getTrait(Speech.class).getDefaultVocalChord());
return;
}
SpeechContext context = new SpeechContext(message);
if (args.hasValueFlag("target")) {
if (args.getFlag("target").matches("\\d+")) {
NPC target = CitizensAPI.getNPCRegistry().getById(Integer.valueOf(args.getFlag("target")));
if ( target != null)
context.addRecipient(target.getBukkitEntity());
} else {
Player player = Bukkit.getPlayer(args.getFlag("target"));
if (player != null)
context.addRecipient(player);
}
}
if (args.hasValueFlag("type")) {
if (CitizensAPI.getSpeechFactory().isRegistered(args.getFlag("type")))
type = args.getFlag("type");
}
npc.getDefaultSpeechController().speak(context, type);
}
@Command( @Command(
aliases = { "npc" }, aliases = { "npc" },
usage = "speed [speed]", usage = "speed [speed]",

View File

@ -1,246 +1,247 @@
package net.citizensnpcs.npc; package net.citizensnpcs.npc;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.citizensnpcs.EventListen; import net.citizensnpcs.EventListen;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.Navigator; import net.citizensnpcs.api.ai.Navigator;
import net.citizensnpcs.api.event.DespawnReason; import net.citizensnpcs.api.event.DespawnReason;
import net.citizensnpcs.api.event.NPCDespawnEvent; import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCSpawnEvent; import net.citizensnpcs.api.event.NPCSpawnEvent;
import net.citizensnpcs.api.npc.AbstractNPC; import net.citizensnpcs.api.npc.AbstractNPC;
import net.citizensnpcs.api.persistence.PersistenceLoader; import net.citizensnpcs.api.persistence.PersistenceLoader;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.trait.Spawned; import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.npc.ai.CitizensNavigator; import net.citizensnpcs.npc.ai.CitizensNavigator;
import net.citizensnpcs.trait.CurrentLocation; import net.citizensnpcs.trait.CurrentLocation;
import net.citizensnpcs.util.Messages; import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.Messaging;
import net.citizensnpcs.util.NMS; import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util; import net.citizensnpcs.util.Util;
import net.minecraft.server.v1_4_6.EntityLiving; import net.minecraft.server.v1_4_6.EntityLiving;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_4_6.entity.CraftLivingEntity; import org.bukkit.craftbukkit.v1_4_6.entity.CraftLivingEntity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class CitizensNPC extends AbstractNPC { public class CitizensNPC extends AbstractNPC {
private EntityController entityController; private EntityController entityController;
private final CitizensNavigator navigator = new CitizensNavigator(this); private final CitizensNavigator navigator = new CitizensNavigator(this);
private final List<String> removedTraits = Lists.newArrayList(); private final List<String> removedTraits = Lists.newArrayList();
public CitizensNPC(int id, String name, EntityController entityController) { public CitizensNPC(int id, String name, EntityController entityController) {
super(id, name); super(id, name);
Preconditions.checkNotNull(entityController); Preconditions.checkNotNull(entityController);
this.entityController = entityController; this.entityController = entityController;
} }
@Override @Override
public boolean despawn(DespawnReason reason) { public boolean despawn(DespawnReason reason) {
if (!isSpawned()) if (!isSpawned())
return false; return false;
NPCDespawnEvent event = new NPCDespawnEvent(this, reason); NPCDespawnEvent event = new NPCDespawnEvent(this, reason);
if (reason == DespawnReason.CHUNK_UNLOAD) if (reason == DespawnReason.CHUNK_UNLOAD)
event.setCancelled(Setting.KEEP_CHUNKS_LOADED.asBoolean()); event.setCancelled(Setting.KEEP_CHUNKS_LOADED.asBoolean());
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) { if (event.isCancelled()) {
getBukkitEntity().getLocation().getChunk(); getBukkitEntity().getLocation().getChunk();
// ensure that we are in a loaded chunk. // ensure that we are in a loaded chunk.
return false; return false;
} }
boolean keepSelected = getTrait(Spawned.class).shouldSpawn(); boolean keepSelected = getTrait(Spawned.class).shouldSpawn();
if (!keepSelected) if (!keepSelected)
data().remove("selectors"); data().remove("selectors");
for (Trait trait : traits.values()) for (Trait trait : traits.values())
trait.onDespawn(); trait.onDespawn();
entityController.remove(); entityController.remove();
return true; return true;
} }
@Override @Override
public LivingEntity getBukkitEntity() { public LivingEntity getBukkitEntity() {
return entityController.getBukkitEntity(); return entityController.getBukkitEntity();
} }
@Deprecated @Deprecated
public EntityLiving getHandle() { public EntityLiving getHandle() {
return ((CraftLivingEntity) getBukkitEntity()).getHandle(); return ((CraftLivingEntity) getBukkitEntity()).getHandle();
} }
@Override @Override
public Navigator getNavigator() { public Navigator getNavigator() {
return navigator; return navigator;
} }
@Override @Override
public boolean isSpawned() { public boolean isSpawned() {
return getBukkitEntity() != null; return getBukkitEntity() != null;
} }
public void load(final DataKey root) { public void load(final DataKey root) {
metadata.loadFrom(root.getRelative("metadata")); metadata.loadFrom(root.getRelative("metadata"));
// Load traits // Load traits
String traitNames = root.getString("traitnames"); String traitNames = root.getString("traitnames");
Iterable<DataKey> keys = traitNames.isEmpty() ? root.getRelative("traits").getSubKeys() : Iterables Iterable<DataKey> keys = traitNames.isEmpty() ? root.getRelative("traits").getSubKeys() : Iterables
.transform(Splitter.on(',').split(traitNames), new Function<String, DataKey>() { .transform(Splitter.on(',').split(traitNames), new Function<String, DataKey>() {
@Override @Override
public DataKey apply(@Nullable String input) { public DataKey apply(@Nullable String input) {
return root.getRelative("traits." + input); return root.getRelative("traits." + input);
} }
}); });
for (DataKey traitKey : keys) { for (DataKey traitKey : keys) {
if (traitKey.keyExists("enabled") && !traitKey.getBoolean("enabled")) if (traitKey.keyExists("enabled") && !traitKey.getBoolean("enabled"))
continue; continue;
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitKey.name()); Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitKey.name());
Trait trait; Trait trait;
if (hasTrait(clazz)) { if (hasTrait(clazz)) {
trait = getTrait(clazz); trait = getTrait(clazz);
} else { } else {
trait = CitizensAPI.getTraitFactory().getTrait(clazz); trait = CitizensAPI.getTraitFactory().getTrait(clazz);
if (trait == null) { if (trait == null) {
Messaging.severeTr(Messages.SKIPPING_BROKEN_TRAIT, traitKey.name(), getId()); Messaging.severeTr(Messages.SKIPPING_BROKEN_TRAIT, traitKey.name(), getId());
continue; continue;
} }
addTrait(trait); addTrait(trait);
} }
loadTrait(trait, traitKey); loadTrait(trait, traitKey);
} }
// Spawn the NPC // Spawn the NPC
CurrentLocation spawnLocation = getTrait(CurrentLocation.class); CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null) if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
spawn(spawnLocation.getLocation()); spawn(spawnLocation.getLocation());
navigator.load(root.getRelative("navigator")); navigator.load(root.getRelative("navigator"));
} }
private void loadTrait(Trait trait, DataKey traitKey) { private void loadTrait(Trait trait, DataKey traitKey) {
try { try {
trait.load(traitKey); trait.load(traitKey);
PersistenceLoader.load(trait, traitKey); PersistenceLoader.load(trait, traitKey);
} catch (Throwable ex) { } catch (Throwable ex) {
Messaging.logTr(Messages.TRAIT_LOAD_FAILED, traitKey.name(), getId()); Messaging.logTr(Messages.TRAIT_LOAD_FAILED, traitKey.name(), getId());
} }
} }
@Override @Override
public void removeTrait(Class<? extends Trait> clazz) { public void removeTrait(Class<? extends Trait> clazz) {
Trait present = traits.get(clazz); Trait present = traits.get(clazz);
if (present != null) if (present != null)
removedTraits.add(present.getName()); removedTraits.add(present.getName());
super.removeTrait(clazz); super.removeTrait(clazz);
} }
private void removeTraitData(DataKey root) { private void removeTraitData(DataKey root) {
for (String name : removedTraits) { for (String name : removedTraits) {
root.removeKey("traits." + name); root.removeKey("traits." + name);
} }
removedTraits.clear(); removedTraits.clear();
} }
public void save(DataKey root) { public void save(DataKey root) {
root.setString("name", getFullName()); root.setString("name", getFullName());
metadata.saveTo(root.getRelative("metadata")); metadata.saveTo(root.getRelative("metadata"));
navigator.save(root.getRelative("navigator")); navigator.save(root.getRelative("navigator"));
// Save all existing traits // Save all existing traits
StringBuilder traitNames = new StringBuilder(); StringBuilder traitNames = new StringBuilder();
for (Trait trait : traits.values()) { for (Trait trait : traits.values()) {
DataKey traitKey = root.getRelative("traits." + trait.getName()); DataKey traitKey = root.getRelative("traits." + trait.getName());
trait.save(traitKey); trait.save(traitKey);
PersistenceLoader.save(trait, traitKey); PersistenceLoader.save(trait, traitKey);
removedTraits.remove(trait.getName()); removedTraits.remove(trait.getName());
traitNames.append(trait.getName() + ","); traitNames.append(trait.getName() + ",");
} }
if (traitNames.length() > 0) { if (traitNames.length() > 0) {
root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1)); root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1));
} }
removeTraitData(root); removeTraitData(root);
} }
public void setEntityController(EntityController newController) { public void setEntityController(EntityController newController) {
Preconditions.checkNotNull(newController); Preconditions.checkNotNull(newController);
boolean wasSpawned = isSpawned(); boolean wasSpawned = isSpawned();
Location prev = null; Location prev = null;
if (wasSpawned) { if (wasSpawned) {
prev = getBukkitEntity().getLocation(); prev = getBukkitEntity().getLocation();
despawn(); despawn();
} }
entityController = newController; entityController = newController;
if (wasSpawned) if (wasSpawned)
spawn(prev); spawn(prev);
} }
@Override @Override
public boolean spawn(Location at) { public boolean spawn(Location at) {
Preconditions.checkNotNull(at, "location cannot be null"); Preconditions.checkNotNull(at, "location cannot be null");
if (isSpawned()) if (isSpawned())
return false; return false;
entityController.spawn(at, this); entityController.spawn(at, this);
EntityLiving mcEntity = getHandle(); EntityLiving mcEntity = getHandle();
boolean couldSpawn = !Util.isLoaded(at) ? false : mcEntity.world.addEntity(mcEntity, boolean couldSpawn = !Util.isLoaded(at) ? false : mcEntity.world.addEntity(mcEntity,
SpawnReason.CUSTOM); SpawnReason.CUSTOM);
if (!couldSpawn) { if (!couldSpawn) {
// we need to wait for a chunk load before trying to spawn // we need to wait for a chunk load before trying to spawn
mcEntity = null; mcEntity = null;
EventListen.addForRespawn(at, getId()); EventListen.addForRespawn(at, getId());
return true; return true;
} }
NPCSpawnEvent spawnEvent = new NPCSpawnEvent(this, at); NPCSpawnEvent spawnEvent = new NPCSpawnEvent(this, at);
Bukkit.getPluginManager().callEvent(spawnEvent); Bukkit.getPluginManager().callEvent(spawnEvent);
if (spawnEvent.isCancelled()) { if (spawnEvent.isCancelled()) {
mcEntity = null; mcEntity = null;
return false; return false;
} }
NMS.setHeadYaw(mcEntity, at.getYaw()); NMS.setHeadYaw(mcEntity, at.getYaw());
getBukkitEntity().setMetadata(NPC_METADATA_MARKER, getBukkitEntity().setMetadata(NPC_METADATA_MARKER,
new FixedMetadataValue(CitizensAPI.getPlugin(), true)); new FixedMetadataValue(CitizensAPI.getPlugin(), true));
// Set the spawned state // Set the spawned state
getTrait(CurrentLocation.class).setLocation(at); getTrait(CurrentLocation.class).setLocation(at);
getTrait(Spawned.class).setSpawned(true); getTrait(Spawned.class).setSpawned(true);
navigator.onSpawn(); navigator.onSpawn();
// Modify NPC using traits after the entity has been created // Modify NPC using traits after the entity has been created
for (Trait trait : traits.values()) for (Trait trait : traits.values())
trait.onSpawn(); trait.onSpawn();
getBukkitEntity().setRemoveWhenFarAway(false); getBukkitEntity().setRemoveWhenFarAway(false);
return true; return true;
} }
@Override @Override
public void update() { public void update() {
try { try {
super.update(); super.update();
if (isSpawned()) { if (isSpawned()) {
NMS.trySwim(getBukkitEntity()); NMS.trySwim(getBukkitEntity());
navigator.run(); navigator.run();
} }
} catch (Exception ex) { } catch (Exception ex) {
Messaging.logTr(Messages.EXCEPTION_UPDATING_NPC, getId(), ex.getMessage()); Messaging.logTr(Messages.EXCEPTION_UPDATING_NPC, getId(), ex.getMessage());
ex.printStackTrace(); ex.printStackTrace();
} }
} }
private static final String NPC_METADATA_MARKER = "NPC"; private static final String NPC_METADATA_MARKER = "NPC";
} }

View File

@ -1,136 +1,138 @@
package net.citizensnpcs.npc; package net.citizensnpcs.npc;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.citizensnpcs.Metrics; import net.citizensnpcs.Metrics;
import net.citizensnpcs.Metrics.Graph; import net.citizensnpcs.Metrics.Graph;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitFactory; import net.citizensnpcs.api.trait.TraitFactory;
import net.citizensnpcs.api.trait.TraitInfo; import net.citizensnpcs.api.trait.TraitInfo;
import net.citizensnpcs.api.trait.trait.Equipment; import net.citizensnpcs.api.trait.trait.Equipment;
import net.citizensnpcs.api.trait.trait.Inventory; import net.citizensnpcs.api.trait.trait.Inventory;
import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.trait.trait.Owner; import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.trait.trait.Spawned; import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.trait.Age; import net.citizensnpcs.api.trait.trait.Speech;
import net.citizensnpcs.trait.Anchors; import net.citizensnpcs.trait.Age;
import net.citizensnpcs.trait.Behaviour; import net.citizensnpcs.trait.Anchors;
import net.citizensnpcs.trait.Controllable; import net.citizensnpcs.trait.Behaviour;
import net.citizensnpcs.trait.CurrentLocation; import net.citizensnpcs.trait.Controllable;
import net.citizensnpcs.trait.Gravity; import net.citizensnpcs.trait.CurrentLocation;
import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.trait.Gravity;
import net.citizensnpcs.trait.NPCSkeletonType; import net.citizensnpcs.trait.LookClose;
import net.citizensnpcs.trait.Poses; import net.citizensnpcs.trait.NPCSkeletonType;
import net.citizensnpcs.trait.Powered; import net.citizensnpcs.trait.Poses;
import net.citizensnpcs.trait.Saddle; import net.citizensnpcs.trait.Powered;
import net.citizensnpcs.trait.Sheared; import net.citizensnpcs.trait.Saddle;
import net.citizensnpcs.trait.SlimeSize; import net.citizensnpcs.trait.Sheared;
import net.citizensnpcs.trait.VillagerProfession; import net.citizensnpcs.trait.SlimeSize;
import net.citizensnpcs.trait.WoolColor; import net.citizensnpcs.trait.VillagerProfession;
import net.citizensnpcs.trait.ZombieModifier; import net.citizensnpcs.trait.WoolColor;
import net.citizensnpcs.trait.text.Text; import net.citizensnpcs.trait.ZombieModifier;
import net.citizensnpcs.trait.waypoint.Waypoints; import net.citizensnpcs.trait.text.Text;
import net.citizensnpcs.trait.waypoint.Waypoints;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps; import com.google.common.base.Preconditions;
import com.google.common.collect.Sets; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class CitizensTraitFactory implements TraitFactory {
private final Map<String, Class<? extends Trait>> registered = Maps.newHashMap(); public class CitizensTraitFactory implements TraitFactory {
private final Map<String, Class<? extends Trait>> registered = Maps.newHashMap();
public CitizensTraitFactory() {
registerTrait(TraitInfo.create(Age.class).withName("age")); public CitizensTraitFactory() {
registerTrait(TraitInfo.create(Anchors.class).withName("anchors")); registerTrait(TraitInfo.create(Age.class).withName("age"));
registerTrait(TraitInfo.create(Behaviour.class).withName("behaviour")); registerTrait(TraitInfo.create(Anchors.class).withName("anchors"));
registerTrait(TraitInfo.create(Controllable.class).withName("controllable")); registerTrait(TraitInfo.create(Behaviour.class).withName("behaviour"));
registerTrait(TraitInfo.create(Equipment.class).withName("equipment")); registerTrait(TraitInfo.create(Controllable.class).withName("controllable"));
registerTrait(TraitInfo.create(Gravity.class).withName("gravity")); registerTrait(TraitInfo.create(Equipment.class).withName("equipment"));
registerTrait(TraitInfo.create(Inventory.class).withName("inventory")); registerTrait(TraitInfo.create(Gravity.class).withName("gravity"));
registerTrait(TraitInfo.create(CurrentLocation.class).withName("location")); registerTrait(TraitInfo.create(Inventory.class).withName("inventory"));
registerTrait(TraitInfo.create(LookClose.class).withName("lookclose")); registerTrait(TraitInfo.create(CurrentLocation.class).withName("location"));
registerTrait(TraitInfo.create(Owner.class).withName("owner")); registerTrait(TraitInfo.create(LookClose.class).withName("lookclose"));
registerTrait(TraitInfo.create(Poses.class).withName("poses")); registerTrait(TraitInfo.create(Owner.class).withName("owner"));
registerTrait(TraitInfo.create(Powered.class).withName("powered")); registerTrait(TraitInfo.create(Poses.class).withName("poses"));
registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession")); registerTrait(TraitInfo.create(Powered.class).withName("powered"));
registerTrait(TraitInfo.create(Saddle.class).withName("saddle")); registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession"));
registerTrait(TraitInfo.create(Sheared.class).withName("sheared")); registerTrait(TraitInfo.create(Saddle.class).withName("saddle"));
registerTrait(TraitInfo.create(NPCSkeletonType.class).withName("skeletontype")); registerTrait(TraitInfo.create(Sheared.class).withName("sheared"));
registerTrait(TraitInfo.create(SlimeSize.class).withName("slimesize")); registerTrait(TraitInfo.create(NPCSkeletonType.class).withName("skeletontype"));
registerTrait(TraitInfo.create(Spawned.class).withName("spawned")); registerTrait(TraitInfo.create(SlimeSize.class).withName("slimesize"));
registerTrait(TraitInfo.create(Text.class).withName("text")); registerTrait(TraitInfo.create(Spawned.class).withName("spawned"));
registerTrait(TraitInfo.create(MobType.class).withName("type")); registerTrait(TraitInfo.create(Speech.class).withName("speech"));
registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints")); registerTrait(TraitInfo.create(Text.class).withName("text"));
registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor")); registerTrait(TraitInfo.create(MobType.class).withName("type"));
registerTrait(TraitInfo.create(ZombieModifier.class).withName("zombiemodifier")); registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints"));
registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor"));
for (String trait : registered.keySet()) registerTrait(TraitInfo.create(ZombieModifier.class).withName("zombiemodifier"));
INTERNAL_TRAITS.add(trait);
} for (String trait : registered.keySet())
INTERNAL_TRAITS.add(trait);
public void addPlotters(Graph graph) { }
for (Map.Entry<String, Class<? extends Trait>> entry : registered.entrySet()) {
if (INTERNAL_TRAITS.contains(entry.getKey())) public void addPlotters(Graph graph) {
continue; for (Map.Entry<String, Class<? extends Trait>> entry : registered.entrySet()) {
final Class<? extends Trait> traitClass = entry.getValue(); if (INTERNAL_TRAITS.contains(entry.getKey()))
graph.addPlotter(new Metrics.Plotter(entry.getKey()) { continue;
@Override final Class<? extends Trait> traitClass = entry.getValue();
public int getValue() { graph.addPlotter(new Metrics.Plotter(entry.getKey()) {
int numberUsingTrait = 0; @Override
for (NPC npc : CitizensAPI.getNPCRegistry()) { public int getValue() {
if (npc.hasTrait(traitClass)) int numberUsingTrait = 0;
++numberUsingTrait; for (NPC npc : CitizensAPI.getNPCRegistry()) {
} if (npc.hasTrait(traitClass))
return numberUsingTrait; ++numberUsingTrait;
} }
}); return numberUsingTrait;
} }
} });
}
private <T extends Trait> T create(Class<T> trait) { }
try {
return trait.newInstance(); private <T extends Trait> T create(Class<T> trait) {
} catch (Exception ex) { try {
ex.printStackTrace(); return trait.newInstance();
return null; } catch (Exception ex) {
} ex.printStackTrace();
} return null;
}
@Override }
public <T extends Trait> T getTrait(Class<T> clazz) {
if (!registered.containsValue(clazz)) @Override
return null; public <T extends Trait> T getTrait(Class<T> clazz) {
return create(clazz); if (!registered.containsValue(clazz))
} return null;
return create(clazz);
@Override }
@SuppressWarnings("unchecked")
public <T extends Trait> T getTrait(String name) { @Override
Class<? extends Trait> clazz = registered.get(name); @SuppressWarnings("unchecked")
if (clazz == null) public <T extends Trait> T getTrait(String name) {
return null; Class<? extends Trait> clazz = registered.get(name);
return (T) create(clazz); if (clazz == null)
} return null;
return (T) create(clazz);
@Override }
public Class<? extends Trait> getTraitClass(String name) {
return registered.get(name.toLowerCase()); @Override
} public Class<? extends Trait> getTraitClass(String name) {
return registered.get(name.toLowerCase());
@Override }
public boolean isInternalTrait(Trait trait) {
return INTERNAL_TRAITS.contains(trait.getName()); @Override
} public boolean isInternalTrait(Trait trait) {
return INTERNAL_TRAITS.contains(trait.getName());
@Override }
public void registerTrait(TraitInfo info) {
Preconditions.checkNotNull(info, "info cannot be null"); @Override
if (registered.containsKey(info)) public void registerTrait(TraitInfo info) {
throw new IllegalArgumentException("trait name already registered"); Preconditions.checkNotNull(info, "info cannot be null");
registered.put(info.getTraitName(), info.getTraitClass()); if (registered.containsKey(info))
} throw new IllegalArgumentException("trait name already registered");
registered.put(info.getTraitName(), info.getTraitClass());
private static final Set<String> INTERNAL_TRAITS = Sets.newHashSet(); }
private static final Set<String> INTERNAL_TRAITS = Sets.newHashSet();
} }

View File

@ -0,0 +1,117 @@
package net.citizensnpcs.npc.ai.speech;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.ai.speech.VocalChord;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.util.Messaging;
public class Chat implements VocalChord {
public final String VOCAL_CHORD_NAME = "chat";
@Override
public String getName() {
return VOCAL_CHORD_NAME;
}
@Override
public void talk(SpeechContext context) {
// Check valid talker
if (context.getTalker() == null) return;
NPC npc = CitizensAPI.getNPCRegistry().getNPC(context.getTalker().getEntity());
if (npc == null) return;
// If no recipients, chat to the world with CHAT_FORMAT and CHAT_RANGE settings
if (!context.hasRecipients()) {
String text = Setting.CHAT_FORMAT.asString().replace("<npc>", npc.getName()).replace("<text>", context.getMessage());
talkToBystanders(npc, text, context);
return;
}
// Assumed recipients at this point
else if (context.size() <= 1) { // One recipient
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<npc>", npc.getName()).replace("<text>", context.getMessage());
String targetName = "";
// For each recipient
for (Talkable entity : context) {
entity.talkTo(context, text, this);
targetName = entity.getName();
}
// Check if bystanders hear targeted chat
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean()) return;
// Format message with config setting and send to bystanders
String bystanderText = Setting.CHAT_FORMAT_TO_BYSTANDERS.asString().replace("<npc>", npc.getName()).replace("<target>", targetName).replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
return;
}
else { // Multiple recipients
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<npc>", npc.getName()).replace("<text>", context.getMessage());
List<String> targetNames = Collections.emptyList();
// Talk to each recipient
for (Talkable entity : context) {
entity.talkTo(context, text, this);
targetNames.add(entity.getName());
}
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean()) return;
String targets = "";
int max = Setting.CHAT_MAX_NUMBER_OF_TARGETS.asInt();
String[] format = Setting.CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS.asString().split("\\|");
if (format.length != 4) Messaging.log(Level.WARNING, "npc.chat.format.with-target-to-bystanders invalid!");
if (max == 1) {
targets = format[0].replace("<npc>", targetNames.get(0)) + format[3];
}
else if (max == 2 || targetNames.size() == 2) {
if (targetNames.size() == 2)
targets = format[0].replace("<npc>", targetNames.get(0)) + format[2].replace("<npc>", targetNames.get(1));
else
targets = format[0].replace("<npc>", targetNames.get(0)) + format[1].replace("<npc>", targetNames.get(1)) + format[3];
}
else if (max >= 3) {
targets = format[0].replace("<npc>", targetNames.get(0));
int x = 1;
for (x = 1; x < max - 1; x++) {
if (targetNames.size() - 1 == x) break;
targets = targets + format[1].replace("<npc>", targetNames.get(x));
}
if (targetNames.size() == max)
targets = targets + format[2].replace("<npc>", targetNames.get(x));
else targets = targets + format[3];
}
String bystanderText = Setting.CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS.asString().replace("<npc>", npc.getName()).replace("<targets>", targets).replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
}
}
private void talkToBystanders(NPC npc, String text, SpeechContext context) {
// Get list of nearby entities
List<Entity> bystanderEntities = npc.getBukkitEntity().getNearbyEntities(Setting.CHAT_RANGE.asDouble(), Setting.CHAT_RANGE.asDouble(), Setting.CHAT_RANGE.asDouble());
for (Entity bystander : bystanderEntities)
// Continue if a LivingEntity, which is compatible with TalkableEntity
if (bystander instanceof LivingEntity) {
// Exclude targeted recipients
if (context.hasRecipients()) {
for (Talkable target : context)
if (target.getEntity() == bystander) continue;
else new TalkableEntity((LivingEntity) bystander).talkNear(context, text, this);
} else
// Found a nearby LivingEntity, make it Talkable and talkNear it
new TalkableEntity((LivingEntity) bystander).talkNear(context, text, this);
}
}
}

View File

@ -0,0 +1,79 @@
package net.citizensnpcs.npc.ai.speech;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.entity.LivingEntity;
import com.google.common.base.Preconditions;
import net.citizensnpcs.api.ai.speech.SpeechFactory;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.VocalChord;
public class CitizensSpeechFactory implements SpeechFactory {
Map<String, Class<? extends VocalChord>> registered = new HashMap<String, Class <? extends VocalChord>>();
@Override
public VocalChord getVocalChord(Class<? extends VocalChord> clazz) {
// Return a new instance of the VocalChord specified
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@Override
public VocalChord getVocalChord(String name) {
// Check if VocalChord name is a registered type
if (isRegistered(name))
// Return a new instance of the VocalChord specified
try {
return registered.get(name.toLowerCase()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@Override
public String getVocalChordName(Class<? extends VocalChord> clazz) {
// Get the name of a VocalChord class that has been registered
for (Entry<String, Class<? extends VocalChord>> vocalChord : registered.entrySet())
if (vocalChord.getValue() == clazz) return vocalChord.getKey();
return null;
}
@Override
public boolean isRegistered(String name) {
if (registered.containsKey(name.toLowerCase())) return true;
else return false;
}
@Override
public void register(Class<? extends VocalChord> clazz, String name) {
Preconditions.checkNotNull(name, "info cannot be null");
Preconditions.checkNotNull(clazz, "vocalchord cannot be null");
if (registered.containsKey(name.toLowerCase()))
throw new IllegalArgumentException("vocalchord name already registered");
registered.put(name.toLowerCase(), clazz);
}
@Override
public Talkable newTalkableEntity(LivingEntity entity) {
if (entity == null) return null;
return new TalkableEntity(entity);
}
}

View File

@ -0,0 +1,91 @@
package net.citizensnpcs.npc.ai.speech;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.VocalChord;
import net.citizensnpcs.api.ai.speech.event.SpeechBystanderEvent;
import net.citizensnpcs.api.ai.speech.event.SpeechTargetedEvent;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.util.Messaging;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
public class TalkableEntity implements Talkable {
LivingEntity entity;
public TalkableEntity(LivingEntity entity) {
this.entity = entity;
}
public TalkableEntity(NPC npc) {
entity = npc.getBukkitEntity();
}
public TalkableEntity(Player player) {
entity = (LivingEntity) player;
}
/**
* Used to compare a LivingEntity to this TalkableEntity
*
* @return
* 0 if the Entities are the same, 1 if they are not, -1 if
* the object compared is not a valid LivingEntity
*/
@Override
public int compareTo(Object o) {
// If not living entity, return -1
if (!(o instanceof LivingEntity)) return -1;
// If NPC and matches, return 0
else if (CitizensAPI.getNPCRegistry().isNPC((LivingEntity) o)
&& CitizensAPI.getNPCRegistry().isNPC((LivingEntity) entity)
&& CitizensAPI.getNPCRegistry().getNPC((LivingEntity) o).getId() ==
CitizensAPI.getNPCRegistry().getNPC((LivingEntity) entity).getId())
return 0;
else if ((LivingEntity) o == entity) return 0;
// Not a match, return 1
else return 1;
}
@Override
public LivingEntity getEntity() {
return entity;
}
@Override
public String getName() {
if (CitizensAPI.getNPCRegistry().isNPC(entity))
return CitizensAPI.getNPCRegistry().getNPC(entity).getName();
else if (entity instanceof Player)
return ((Player) entity).getName();
else
return entity.getType().name().replace("_", " ");
}
private void talk(String message) {
if (entity instanceof Player
&& !CitizensAPI.getNPCRegistry().isNPC(entity))
Messaging.send((Player) entity, message);
}
@Override
public void talkNear(SpeechContext context, String text, VocalChord vocalChord) {
SpeechBystanderEvent event = new SpeechBystanderEvent(this, context, text, vocalChord);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) return;
else talk(event.getMessage());
}
@Override
public void talkTo(SpeechContext context, String text, VocalChord vocalChord) {
SpeechTargetedEvent event = new SpeechTargetedEvent(this, context, text, vocalChord);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) return;
else talk(event.getMessage());
}
}

View File

@ -1,232 +1,237 @@
package net.citizensnpcs.trait.text; package net.citizensnpcs.trait.text;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.event.NPCRightClickEvent; import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.exception.NPCLoadException;
import net.citizensnpcs.editor.Editor; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.trait.Toggleable; import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.util.Messages; import net.citizensnpcs.editor.Editor;
import net.citizensnpcs.util.Messaging; import net.citizensnpcs.npc.ai.speech.TalkableEntity;
import net.citizensnpcs.util.Paginator; import net.citizensnpcs.trait.Toggleable;
import net.citizensnpcs.util.Util; import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Messaging;
import org.bukkit.Bukkit; import net.citizensnpcs.util.Paginator;
import org.bukkit.conversations.Conversation; import net.citizensnpcs.util.Util;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ConversationAbandonedListener; import org.bukkit.Bukkit;
import org.bukkit.conversations.ConversationFactory; import org.bukkit.conversations.Conversation;
import org.bukkit.entity.Entity; import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.entity.Player; import org.bukkit.conversations.ConversationAbandonedListener;
import org.bukkit.event.EventHandler; import org.bukkit.conversations.ConversationFactory;
import org.bukkit.event.Listener; import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener { import org.bukkit.event.Listener;
private final Map<String, Date> cooldowns = new HashMap<String, Date>(); import org.bukkit.plugin.Plugin;
private int currentIndex;
private String itemInHandPattern = "default"; public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener {
private final Plugin plugin; private final Map<String, Date> cooldowns = new HashMap<String, Date>();
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean(); private int currentIndex;
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble(); private String itemInHandPattern = "default";
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean(); private final Plugin plugin;
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean(); private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
private final List<String> text = new ArrayList<String>(); private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
public Text() { private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
super("text"); private final List<String> text = new ArrayList<String>();
this.plugin = CitizensAPI.getPlugin();
} public Text() {
super("text");
void add(String string) { this.plugin = CitizensAPI.getPlugin();
text.add(string); }
}
void add(String string) {
@Override text.add(string);
public void conversationAbandoned(ConversationAbandonedEvent event) { }
Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text");
} @Override
public void conversationAbandoned(ConversationAbandonedEvent event) {
void edit(int index, String newText) { Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text");
text.set(index, newText); }
}
void edit(int index, String newText) {
public Editor getEditor(final Player player) { text.set(index, newText);
final Conversation conversation = new ConversationFactory(plugin) }
.addConversationAbandonedListener(this).withLocalEcho(false).withEscapeSequence("/npc text")
.withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextStartPrompt(this)) public Editor getEditor(final Player player) {
.buildConversation(player); final Conversation conversation = new ConversationFactory(plugin)
return new Editor() { .addConversationAbandonedListener(this).withLocalEcho(false).withEscapeSequence("/npc text")
.withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextStartPrompt(this))
@Override .buildConversation(player);
public void begin() { return new Editor() {
Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN);
conversation.begin(); @Override
} public void begin() {
Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN);
@Override conversation.begin();
public void end() { }
Messaging.sendTr(player, Messages.TEXT_EDITOR_END);
conversation.abandon(); @Override
} public void end() {
}; Messaging.sendTr(player, Messages.TEXT_EDITOR_END);
} conversation.abandon();
}
boolean hasIndex(int index) { };
return index >= 0 && text.size() > index; }
}
boolean hasIndex(int index) {
@Override return index >= 0 && text.size() > index;
public void load(DataKey key) throws NPCLoadException { }
text.clear();
// TODO: legacy, remove later @Override
for (DataKey sub : key.getIntegerSubKeys()) public void load(DataKey key) throws NPCLoadException {
text.add(sub.getString("")); text.clear();
for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) // TODO: legacy, remove later
text.add(sub.getString("")); for (DataKey sub : key.getIntegerSubKeys())
if (text.isEmpty()) text.add(sub.getString(""));
populateDefaultText(); for (DataKey sub : key.getRelative("text").getIntegerSubKeys())
text.add(sub.getString(""));
talkClose = key.getBoolean("talk-close", talkClose); if (text.isEmpty())
realisticLooker = key.getBoolean("realistic-looking", realisticLooker); populateDefaultText();
randomTalker = key.getBoolean("random-talker", randomTalker);
range = key.getDouble("range", range); talkClose = key.getBoolean("talk-close", talkClose);
itemInHandPattern = key.getString("talkitem", itemInHandPattern); realisticLooker = key.getBoolean("realistic-looking", realisticLooker);
} randomTalker = key.getBoolean("random-talker", randomTalker);
range = key.getDouble("range", range);
@EventHandler itemInHandPattern = key.getString("talkitem", itemInHandPattern);
public void onRightClick(NPCRightClickEvent event) { }
if (!event.getNPC().equals(npc))
return; @EventHandler
String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString() public void onRightClick(NPCRightClickEvent event) {
: itemInHandPattern; if (!event.getNPC().equals(npc))
if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose()) return;
sendText(event.getClicker()); String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString()
} : itemInHandPattern;
if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose())
private void populateDefaultText() { sendText(event.getClicker());
text.addAll(Setting.DEFAULT_TEXT.asList()); }
}
private void populateDefaultText() {
void remove(int index) { text.addAll(Setting.DEFAULT_TEXT.asList());
text.remove(index); }
}
void remove(int index) {
@Override text.remove(index);
public void run() { }
if (!talkClose || !npc.isSpawned())
return; @Override
List<Entity> nearby = npc.getBukkitEntity().getNearbyEntities(range, range, range); public void run() {
for (Entity search : nearby) { if (!talkClose || !npc.isSpawned())
if (!(search instanceof Player)) return;
continue; List<Entity> nearby = npc.getBukkitEntity().getNearbyEntities(range, range, range);
Player player = (Player) search; for (Entity search : nearby) {
// If the cooldown is not expired, do not send text if (!(search instanceof Player))
Date cooldown = cooldowns.get(player.getName()); continue;
if (cooldown != null) { Player player = (Player) search;
if (!new Date().after(cooldown)) // If the cooldown is not expired, do not send text
return; Date cooldown = cooldowns.get(player.getName());
cooldowns.remove(player.getName()); if (cooldown != null) {
} if (!new Date().after(cooldown))
if (!sendText(player)) return;
return; cooldowns.remove(player.getName());
// Add a cooldown if the text was successfully sent }
Date wait = new Date(); if (!sendText(player))
int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt()) return;
+ Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt(); // Add a cooldown if the text was successfully sent
if (secondsDelta <= 0) Date wait = new Date();
return; int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt())
long millisecondsDelta = TimeUnit.MILLISECONDS.convert(secondsDelta, TimeUnit.SECONDS); + Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt();
wait.setTime(wait.getTime() + millisecondsDelta); if (secondsDelta <= 0)
cooldowns.put(player.getName(), wait); return;
} long millisecondsDelta = TimeUnit.MILLISECONDS.convert(secondsDelta, TimeUnit.SECONDS);
} wait.setTime(wait.getTime() + millisecondsDelta);
cooldowns.put(player.getName(), wait);
@Override }
public void save(DataKey key) { }
key.setBoolean("talk-close", talkClose);
key.setBoolean("random-talker", randomTalker); @Override
key.setBoolean("realistic-looking", realisticLooker); public void save(DataKey key) {
key.setDouble("range", range); key.setBoolean("talk-close", talkClose);
key.setString("talkitem", itemInHandPattern); key.setBoolean("random-talker", randomTalker);
// TODO: legacy, remove later key.setBoolean("realistic-looking", realisticLooker);
for (int i = 0; i < 100; i++) key.setDouble("range", range);
key.removeKey(String.valueOf(i)); key.setString("talkitem", itemInHandPattern);
key.removeKey("text"); // TODO: legacy, remove later
for (int i = 0; i < text.size(); i++) for (int i = 0; i < 100; i++)
key.setString("text." + String.valueOf(i), text.get(i)); key.removeKey(String.valueOf(i));
} key.removeKey("text");
for (int i = 0; i < text.size(); i++)
boolean sendPage(Player player, int page) { key.setString("text." + String.valueOf(i), text.get(i));
Paginator paginator = new Paginator().header(npc.getName() + "'s Text Entries"); }
for (int i = 0; i < text.size(); i++)
paginator.addLine("<a>" + i + " <7>- <e>" + text.get(i)); boolean sendPage(Player player, int page) {
Paginator paginator = new Paginator().header(npc.getName() + "'s Text Entries");
return paginator.sendPage(player, page); for (int i = 0; i < text.size(); i++)
} paginator.addLine("<a>" + i + " <7>- <e>" + text.get(i));
private boolean sendText(Player player) { return paginator.sendPage(player, page);
if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk")) }
return false;
if (text.size() == 0) private boolean sendText(Player player) {
return false; if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk"))
return false;
int index = 0; if (text.size() == 0)
if (randomTalker) return false;
index = new Random().nextInt(text.size());
else { int index = 0;
if (currentIndex > text.size() - 1) if (randomTalker)
currentIndex = 0; index = new Random().nextInt(text.size());
index = currentIndex++; else {
} if (currentIndex > text.size() - 1)
Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + text.get(index), npc); currentIndex = 0;
return true; index = currentIndex++;
} }
void setItemInHandPattern(String pattern) { npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
itemInHandPattern = pattern; // Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + text.get(index), npc);
} return true;
}
void setRange(double range) {
this.range = range; void setItemInHandPattern(String pattern) {
} itemInHandPattern = pattern;
}
boolean shouldTalkClose() {
return talkClose; void setRange(double range) {
} this.range = range;
}
@Override
public boolean toggle() { boolean shouldTalkClose() {
return (talkClose = !talkClose); return talkClose;
} }
boolean toggleRandomTalker() { @Override
return (randomTalker = !randomTalker); public boolean toggle() {
} return (talkClose = !talkClose);
}
boolean toggleRealisticLooking() {
return (realisticLooker = !realisticLooker); boolean toggleRandomTalker() {
} return (randomTalker = !randomTalker);
}
@Override
public String toString() { boolean toggleRealisticLooking() {
StringBuilder builder = new StringBuilder(); return (realisticLooker = !realisticLooker);
builder.append("Text{talk-close=" + talkClose + ",text="); }
for (String line : text)
builder.append(line + ","); @Override
builder.append("}"); public String toString() {
return builder.toString(); StringBuilder builder = new StringBuilder();
} builder.append("Text{talk-close=" + talkClose + ",text=");
for (String line : text)
private static Random RANDOM = Util.getFastRandom(); builder.append(line + ",");
builder.append("}");
return builder.toString();
}
private static Random RANDOM = Util.getFastRandom();
} }