mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2024-11-24 03:25:13 +01:00
Merge Speech branch into master for testing. Please backup your current saves and config before using this build.
This commit is contained in:
parent
81d00792d1
commit
b8878eda5d
@ -1,351 +1,363 @@
|
||||
package net.citizensnpcs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.CitizensPlugin;
|
||||
import net.citizensnpcs.api.event.CitizensDisableEvent;
|
||||
import net.citizensnpcs.api.event.CitizensEnableEvent;
|
||||
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.scripting.EventRegistrar;
|
||||
import net.citizensnpcs.api.scripting.ObjectProvider;
|
||||
import net.citizensnpcs.api.scripting.ScriptCompiler;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.trait.TraitFactory;
|
||||
import net.citizensnpcs.command.CommandContext;
|
||||
import net.citizensnpcs.command.CommandManager;
|
||||
import net.citizensnpcs.command.CommandManager.CommandInfo;
|
||||
import net.citizensnpcs.command.Injector;
|
||||
import net.citizensnpcs.command.RequirementsProcessor;
|
||||
import net.citizensnpcs.command.command.AdminCommands;
|
||||
import net.citizensnpcs.command.command.EditorCommands;
|
||||
import net.citizensnpcs.command.command.HelpCommands;
|
||||
import net.citizensnpcs.command.command.NPCCommands;
|
||||
import net.citizensnpcs.command.command.ScriptCommands;
|
||||
import net.citizensnpcs.command.command.TemplateCommands;
|
||||
import net.citizensnpcs.command.command.TraitCommands;
|
||||
import net.citizensnpcs.command.command.WaypointCommands;
|
||||
import net.citizensnpcs.editor.Editor;
|
||||
import net.citizensnpcs.npc.CitizensNPCRegistry;
|
||||
import net.citizensnpcs.npc.CitizensTraitFactory;
|
||||
import net.citizensnpcs.npc.NPCSelector;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.citizensnpcs.util.StringHelper;
|
||||
import net.citizensnpcs.util.Util;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.InputSupplier;
|
||||
|
||||
public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
private final CommandManager commands = new CommandManager();
|
||||
private boolean compatible;
|
||||
private Settings config;
|
||||
private CitizensNPCRegistry npcRegistry;
|
||||
private NPCDataStore saves;
|
||||
private NPCSelector selector;
|
||||
private CitizensTraitFactory traitFactory;
|
||||
|
||||
private void despawnNPCs() {
|
||||
Iterator<NPC> itr = npcRegistry.iterator();
|
||||
while (itr.hasNext()) {
|
||||
NPC npc = itr.next();
|
||||
try {
|
||||
npc.despawn();
|
||||
for (Trait trait : npc.getTraits())
|
||||
trait.onRemove();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
// ensure that all entities are despawned
|
||||
}
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
getDataFolder().mkdirs();
|
||||
final InputStream dllResource = getResource("path/to/dll");
|
||||
try {
|
||||
Files.copy(new InputSupplier<InputStream>() {
|
||||
@Override
|
||||
public InputStream getInput() throws IOException {
|
||||
return dllResource;
|
||||
}
|
||||
}, new File(getDataFolder(), "name.dll"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// code here
|
||||
new File(getDataFolder(), "name.dll").delete();
|
||||
}
|
||||
|
||||
private void enableSubPlugins() {
|
||||
File root = new File(getDataFolder(), Setting.SUBPLUGIN_FOLDER.asString());
|
||||
if (!root.exists() || !root.isDirectory())
|
||||
return;
|
||||
File[] files = root.listFiles();
|
||||
for (File file : files) {
|
||||
Plugin plugin;
|
||||
try {
|
||||
plugin = Bukkit.getPluginManager().loadPlugin(file);
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
if (plugin == null)
|
||||
continue;
|
||||
// code beneath modified from CraftServer
|
||||
try {
|
||||
Messaging.logTr(Messages.LOADING_SUB_PLUGIN, plugin.getDescription().getFullName());
|
||||
plugin.onLoad();
|
||||
} catch (Throwable ex) {
|
||||
Messaging.severeTr(Messages.ERROR_INITALISING_SUB_PLUGIN, ex.getMessage(), plugin
|
||||
.getDescription().getFullName());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
NMS.loadPlugins();
|
||||
}
|
||||
|
||||
public CommandInfo getCommandInfo(String rootCommand, String modifier) {
|
||||
return commands.getCommand(rootCommand, modifier);
|
||||
}
|
||||
|
||||
public Iterable<CommandInfo> getCommands(String base) {
|
||||
return commands.getCommands(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NPCRegistry getNPCRegistry() {
|
||||
return npcRegistry;
|
||||
}
|
||||
|
||||
public NPCSelector getNPCSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getScriptFolder() {
|
||||
return new File(getDataFolder(), "scripts");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraitFactory getTraitFactory() {
|
||||
return traitFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String cmdName, String[] args) {
|
||||
String modifier = args.length > 0 ? args[0] : "";
|
||||
if (!commands.hasCommand(command, modifier) && !modifier.isEmpty()) {
|
||||
return suggestClosestModifier(sender, command.getName(), modifier);
|
||||
}
|
||||
|
||||
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
|
||||
// changing everything)
|
||||
|
||||
Object[] methodArgs = { sender, npc };
|
||||
return commands.executeSafe(command, args, sender, methodArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
Bukkit.getPluginManager().callEvent(new CitizensDisableEvent());
|
||||
Editor.leaveAll();
|
||||
CitizensAPI.shutdown();
|
||||
|
||||
// Don't bother with this part if MC versions are not compatible
|
||||
if (compatible) {
|
||||
saves.storeAll(npcRegistry);
|
||||
saves.saveToDiskImmediate();
|
||||
despawnNPCs();
|
||||
npcRegistry = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
CitizensAPI.setImplementation(this);
|
||||
// Disable if the server is not using the compatible Minecraft version
|
||||
String mcVersion = Util.getMinecraftVersion();
|
||||
compatible = mcVersion.startsWith(COMPATIBLE_MC_VERSION);
|
||||
if (!compatible) {
|
||||
Messaging.severeTr(Messages.CITIZENS_INCOMPATIBLE, getDescription().getVersion(), mcVersion);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
config = new Settings(getDataFolder());
|
||||
registerScriptHelpers();
|
||||
|
||||
saves = NPCDataStore.create(getDataFolder());
|
||||
if (saves == null) {
|
||||
Messaging.severeTr(Messages.FAILED_LOAD_SAVES);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
npcRegistry = new CitizensNPCRegistry(saves);
|
||||
traitFactory = new CitizensTraitFactory();
|
||||
selector = new NPCSelector(this);
|
||||
|
||||
getServer().getPluginManager().registerEvents(new EventListen(), this);
|
||||
|
||||
if (Setting.NPC_COST.asDouble() > 0)
|
||||
setupEconomy();
|
||||
|
||||
registerCommands();
|
||||
enableSubPlugins();
|
||||
|
||||
// Setup NPCs after all plugins have been enabled (allows for multiworld
|
||||
// support and for NPCs to properly register external settings)
|
||||
if (getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
saves.loadInto(npcRegistry);
|
||||
startMetrics();
|
||||
scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
|
||||
Bukkit.getPluginManager().callEvent(new CitizensEnableEvent());
|
||||
}
|
||||
}, 1) == -1) {
|
||||
Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImplementationChanged() {
|
||||
Messaging.severeTr(Messages.CITIZENS_IMPLEMENTATION_DISABLED);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
public void registerCommandClass(Class<?> clazz) {
|
||||
try {
|
||||
commands.register(clazz);
|
||||
} catch (Throwable ex) {
|
||||
Messaging.logTr(Messages.CITIZENS_INVALID_COMMAND_CLASS);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
commands.setInjector(new Injector(this));
|
||||
commands.registerAnnotationProcessor(new RequirementsProcessor());
|
||||
|
||||
// Register command classes
|
||||
commands.register(AdminCommands.class);
|
||||
commands.register(EditorCommands.class);
|
||||
commands.register(HelpCommands.class);
|
||||
commands.register(NPCCommands.class);
|
||||
commands.register(ScriptCommands.class);
|
||||
commands.register(TemplateCommands.class);
|
||||
commands.register(TraitCommands.class);
|
||||
commands.register(WaypointCommands.class);
|
||||
}
|
||||
|
||||
private void registerScriptHelpers() {
|
||||
ScriptCompiler compiler = CitizensAPI.getScriptCompiler();
|
||||
compiler.registerGlobalContextProvider(new EventRegistrar(this));
|
||||
compiler.registerGlobalContextProvider(new ObjectProvider("plugin", this));
|
||||
}
|
||||
|
||||
public void reload() throws NPCLoadException {
|
||||
Editor.leaveAll();
|
||||
config.reload();
|
||||
despawnNPCs();
|
||||
saves.loadInto(npcRegistry);
|
||||
|
||||
getServer().getPluginManager().callEvent(new CitizensReloadEvent());
|
||||
}
|
||||
|
||||
private void scheduleSaveTask(int delay) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
storeNPCs();
|
||||
saves.saveToDisk();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupEconomy() {
|
||||
try {
|
||||
RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration(
|
||||
Economy.class);
|
||||
if (provider != null && provider.getProvider() != null) {
|
||||
Economy economy = provider.getProvider();
|
||||
Bukkit.getPluginManager().registerEvents(new PaymentListener(economy), this);
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
Messaging.logTr(Messages.ERROR_LOADING_ECONOMY);
|
||||
}
|
||||
}
|
||||
|
||||
private void startMetrics() {
|
||||
try {
|
||||
Metrics metrics = new Metrics(Citizens.this);
|
||||
if (metrics.isOptOut())
|
||||
return;
|
||||
metrics.addCustomData(new Metrics.Plotter("Total NPCs") {
|
||||
@Override
|
||||
public int getValue() {
|
||||
if (npcRegistry == null)
|
||||
return 0;
|
||||
return Iterables.size(npcRegistry);
|
||||
}
|
||||
});
|
||||
|
||||
traitFactory.addPlotters(metrics.createGraph("traits"));
|
||||
saves.addPlotters(metrics.createGraph("Storage type"));
|
||||
metrics.start();
|
||||
} catch (IOException e) {
|
||||
Messaging.logTr(Messages.METRICS_ERROR_NOTIFICATION, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void storeNPCs() {
|
||||
if (saves == null)
|
||||
return;
|
||||
for (NPC npc : npcRegistry)
|
||||
saves.store(npc);
|
||||
}
|
||||
|
||||
public void storeNPCs(CommandContext args) {
|
||||
storeNPCs();
|
||||
boolean async = args.hasFlag('a');
|
||||
if (async)
|
||||
saves.saveToDisk();
|
||||
else
|
||||
saves.saveToDiskImmediate();
|
||||
}
|
||||
|
||||
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";
|
||||
package net.citizensnpcs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.CitizensPlugin;
|
||||
import net.citizensnpcs.api.ai.speech.SpeechFactory;
|
||||
import net.citizensnpcs.api.event.CitizensDisableEvent;
|
||||
import net.citizensnpcs.api.event.CitizensEnableEvent;
|
||||
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.scripting.EventRegistrar;
|
||||
import net.citizensnpcs.api.scripting.ObjectProvider;
|
||||
import net.citizensnpcs.api.scripting.ScriptCompiler;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.trait.TraitFactory;
|
||||
import net.citizensnpcs.command.CommandContext;
|
||||
import net.citizensnpcs.command.CommandManager;
|
||||
import net.citizensnpcs.command.CommandManager.CommandInfo;
|
||||
import net.citizensnpcs.command.Injector;
|
||||
import net.citizensnpcs.command.RequirementsProcessor;
|
||||
import net.citizensnpcs.command.command.AdminCommands;
|
||||
import net.citizensnpcs.command.command.EditorCommands;
|
||||
import net.citizensnpcs.command.command.HelpCommands;
|
||||
import net.citizensnpcs.command.command.NPCCommands;
|
||||
import net.citizensnpcs.command.command.ScriptCommands;
|
||||
import net.citizensnpcs.command.command.TemplateCommands;
|
||||
import net.citizensnpcs.command.command.TraitCommands;
|
||||
import net.citizensnpcs.command.command.WaypointCommands;
|
||||
import net.citizensnpcs.editor.Editor;
|
||||
import net.citizensnpcs.npc.CitizensNPCRegistry;
|
||||
import net.citizensnpcs.npc.CitizensTraitFactory;
|
||||
import net.citizensnpcs.npc.NPCSelector;
|
||||
import net.citizensnpcs.npc.ai.speech.Chat;
|
||||
import net.citizensnpcs.npc.ai.speech.CitizensSpeechFactory;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.citizensnpcs.util.StringHelper;
|
||||
import net.citizensnpcs.util.Util;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.InputSupplier;
|
||||
|
||||
public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
private final CommandManager commands = new CommandManager();
|
||||
private boolean compatible;
|
||||
private Settings config;
|
||||
private CitizensNPCRegistry npcRegistry;
|
||||
private NPCDataStore saves;
|
||||
private NPCSelector selector;
|
||||
private CitizensTraitFactory traitFactory;
|
||||
private CitizensSpeechFactory speechFactory;
|
||||
|
||||
private void despawnNPCs() {
|
||||
Iterator<NPC> itr = npcRegistry.iterator();
|
||||
while (itr.hasNext()) {
|
||||
NPC npc = itr.next();
|
||||
try {
|
||||
npc.despawn();
|
||||
for (Trait trait : npc.getTraits())
|
||||
trait.onRemove();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
// ensure that all entities are despawned
|
||||
}
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
getDataFolder().mkdirs();
|
||||
final InputStream dllResource = getResource("path/to/dll");
|
||||
try {
|
||||
Files.copy(new InputSupplier<InputStream>() {
|
||||
@Override
|
||||
public InputStream getInput() throws IOException {
|
||||
return dllResource;
|
||||
}
|
||||
}, new File(getDataFolder(), "name.dll"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// code here
|
||||
new File(getDataFolder(), "name.dll").delete();
|
||||
}
|
||||
|
||||
private void enableSubPlugins() {
|
||||
File root = new File(getDataFolder(), Setting.SUBPLUGIN_FOLDER.asString());
|
||||
if (!root.exists() || !root.isDirectory())
|
||||
return;
|
||||
File[] files = root.listFiles();
|
||||
for (File file : files) {
|
||||
Plugin plugin;
|
||||
try {
|
||||
plugin = Bukkit.getPluginManager().loadPlugin(file);
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
if (plugin == null)
|
||||
continue;
|
||||
// code beneath modified from CraftServer
|
||||
try {
|
||||
Messaging.logTr(Messages.LOADING_SUB_PLUGIN, plugin.getDescription().getFullName());
|
||||
plugin.onLoad();
|
||||
} catch (Throwable ex) {
|
||||
Messaging.severeTr(Messages.ERROR_INITALISING_SUB_PLUGIN, ex.getMessage(), plugin
|
||||
.getDescription().getFullName());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
NMS.loadPlugins();
|
||||
}
|
||||
|
||||
public CommandInfo getCommandInfo(String rootCommand, String modifier) {
|
||||
return commands.getCommand(rootCommand, modifier);
|
||||
}
|
||||
|
||||
public Iterable<CommandInfo> getCommands(String base) {
|
||||
return commands.getCommands(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NPCRegistry getNPCRegistry() {
|
||||
return npcRegistry;
|
||||
}
|
||||
|
||||
public NPCSelector getNPCSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getScriptFolder() {
|
||||
return new File(getDataFolder(), "scripts");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraitFactory getTraitFactory() {
|
||||
return traitFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeechFactory getSpeechFactory() {
|
||||
return speechFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String cmdName, String[] args) {
|
||||
String modifier = args.length > 0 ? args[0] : "";
|
||||
if (!commands.hasCommand(command, modifier) && !modifier.isEmpty()) {
|
||||
return suggestClosestModifier(sender, command.getName(), modifier);
|
||||
}
|
||||
|
||||
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
|
||||
// changing everything)
|
||||
|
||||
Object[] methodArgs = { sender, npc };
|
||||
return commands.executeSafe(command, args, sender, methodArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
Bukkit.getPluginManager().callEvent(new CitizensDisableEvent());
|
||||
Editor.leaveAll();
|
||||
CitizensAPI.shutdown();
|
||||
|
||||
// Don't bother with this part if MC versions are not compatible
|
||||
if (compatible) {
|
||||
saves.storeAll(npcRegistry);
|
||||
saves.saveToDiskImmediate();
|
||||
despawnNPCs();
|
||||
npcRegistry = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
CitizensAPI.setImplementation(this);
|
||||
// Disable if the server is not using the compatible Minecraft version
|
||||
String mcVersion = Util.getMinecraftVersion();
|
||||
compatible = mcVersion.startsWith(COMPATIBLE_MC_VERSION);
|
||||
if (!compatible) {
|
||||
Messaging.severeTr(Messages.CITIZENS_INCOMPATIBLE, getDescription().getVersion(), mcVersion);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
config = new Settings(getDataFolder());
|
||||
registerScriptHelpers();
|
||||
|
||||
saves = NPCDataStore.create(getDataFolder());
|
||||
if (saves == null) {
|
||||
Messaging.severeTr(Messages.FAILED_LOAD_SAVES);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
npcRegistry = new CitizensNPCRegistry(saves);
|
||||
traitFactory = new CitizensTraitFactory();
|
||||
selector = new NPCSelector(this);
|
||||
speechFactory = new CitizensSpeechFactory();
|
||||
speechFactory.register(Chat.class, "chat");
|
||||
|
||||
getServer().getPluginManager().registerEvents(new EventListen(), this);
|
||||
|
||||
if (Setting.NPC_COST.asDouble() > 0)
|
||||
setupEconomy();
|
||||
|
||||
registerCommands();
|
||||
enableSubPlugins();
|
||||
|
||||
// Setup NPCs after all plugins have been enabled (allows for multiworld
|
||||
// support and for NPCs to properly register external settings)
|
||||
if (getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
saves.loadInto(npcRegistry);
|
||||
startMetrics();
|
||||
scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
|
||||
Bukkit.getPluginManager().callEvent(new CitizensEnableEvent());
|
||||
}
|
||||
}, 1) == -1) {
|
||||
Messaging.severeTr(Messages.LOAD_TASK_NOT_SCHEDULED);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImplementationChanged() {
|
||||
Messaging.severeTr(Messages.CITIZENS_IMPLEMENTATION_DISABLED);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
public void registerCommandClass(Class<?> clazz) {
|
||||
try {
|
||||
commands.register(clazz);
|
||||
} catch (Throwable ex) {
|
||||
Messaging.logTr(Messages.CITIZENS_INVALID_COMMAND_CLASS);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
commands.setInjector(new Injector(this));
|
||||
commands.registerAnnotationProcessor(new RequirementsProcessor());
|
||||
|
||||
// Register command classes
|
||||
commands.register(AdminCommands.class);
|
||||
commands.register(EditorCommands.class);
|
||||
commands.register(HelpCommands.class);
|
||||
commands.register(NPCCommands.class);
|
||||
commands.register(ScriptCommands.class);
|
||||
commands.register(TemplateCommands.class);
|
||||
commands.register(TraitCommands.class);
|
||||
commands.register(WaypointCommands.class);
|
||||
}
|
||||
|
||||
private void registerScriptHelpers() {
|
||||
ScriptCompiler compiler = CitizensAPI.getScriptCompiler();
|
||||
compiler.registerGlobalContextProvider(new EventRegistrar(this));
|
||||
compiler.registerGlobalContextProvider(new ObjectProvider("plugin", this));
|
||||
}
|
||||
|
||||
public void reload() throws NPCLoadException {
|
||||
Editor.leaveAll();
|
||||
config.reload();
|
||||
despawnNPCs();
|
||||
saves.loadInto(npcRegistry);
|
||||
|
||||
getServer().getPluginManager().callEvent(new CitizensReloadEvent());
|
||||
}
|
||||
|
||||
private void scheduleSaveTask(int delay) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
storeNPCs();
|
||||
saves.saveToDisk();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupEconomy() {
|
||||
try {
|
||||
RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration(
|
||||
Economy.class);
|
||||
if (provider != null && provider.getProvider() != null) {
|
||||
Economy economy = provider.getProvider();
|
||||
Bukkit.getPluginManager().registerEvents(new PaymentListener(economy), this);
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
Messaging.logTr(Messages.ERROR_LOADING_ECONOMY);
|
||||
}
|
||||
}
|
||||
|
||||
private void startMetrics() {
|
||||
try {
|
||||
Metrics metrics = new Metrics(Citizens.this);
|
||||
if (metrics.isOptOut())
|
||||
return;
|
||||
metrics.addCustomData(new Metrics.Plotter("Total NPCs") {
|
||||
@Override
|
||||
public int getValue() {
|
||||
if (npcRegistry == null)
|
||||
return 0;
|
||||
return Iterables.size(npcRegistry);
|
||||
}
|
||||
});
|
||||
|
||||
traitFactory.addPlotters(metrics.createGraph("traits"));
|
||||
saves.addPlotters(metrics.createGraph("Storage type"));
|
||||
metrics.start();
|
||||
} catch (IOException e) {
|
||||
Messaging.logTr(Messages.METRICS_ERROR_NOTIFICATION, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void storeNPCs() {
|
||||
if (saves == null)
|
||||
return;
|
||||
for (NPC npc : npcRegistry)
|
||||
saves.store(npc);
|
||||
}
|
||||
|
||||
public void storeNPCs(CommandContext args) {
|
||||
storeNPCs();
|
||||
boolean async = args.hasFlag('a');
|
||||
if (async)
|
||||
saves.saveToDisk();
|
||||
else
|
||||
saves.saveToDiskImmediate();
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
}
|
@ -1,142 +1,149 @@
|
||||
package net.citizensnpcs;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.api.util.Storage;
|
||||
import net.citizensnpcs.api.util.YamlStorage;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class Settings {
|
||||
private final Storage config;
|
||||
private final DataKey root;
|
||||
|
||||
public Settings(File folder) {
|
||||
config = new YamlStorage(new File(folder, "config.yml"), "Citizens Configuration");
|
||||
root = config.getKey("");
|
||||
|
||||
config.load();
|
||||
for (Setting setting : Setting.values()) {
|
||||
if (!root.keyExists(setting.path)) {
|
||||
setting.setAtKey(root);
|
||||
} else
|
||||
setting.loadFromKey(root);
|
||||
}
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
config.load();
|
||||
for (Setting setting : Setting.values())
|
||||
if (root.keyExists(setting.path))
|
||||
setting.loadFromKey(root);
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
config.save();
|
||||
}
|
||||
|
||||
public enum Setting {
|
||||
CHAT_PREFIX("npc.chat.prefix", "[<npc>]: "),
|
||||
DATABASE_DRIVER("storage.database.driver", ""),
|
||||
DATABASE_PASSWORD("storage.database.password", ""),
|
||||
DATABASE_URL("storage.database.url", ""),
|
||||
DATABASE_USERNAME("storage.database.username", ""),
|
||||
DEBUG_MODE("general.debug-mode", false),
|
||||
DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false),
|
||||
DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5),
|
||||
DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10),
|
||||
DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F),
|
||||
DEFAULT_RANDOM_TALKER("npc.default.random-talker", true),
|
||||
DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false),
|
||||
DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1),
|
||||
DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false),
|
||||
DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5),
|
||||
DEFAULT_TEXT("npc.default.text.0", "Hi, I'm <npc>!") {
|
||||
@Override
|
||||
public void loadFromKey(DataKey root) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (DataKey key : root.getRelative("npc.default.text").getSubKeys())
|
||||
list.add(key.getString(""));
|
||||
value = list;
|
||||
}
|
||||
},
|
||||
HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "<e>"),
|
||||
KEEP_CHUNKS_LOADED("npc.chunks.always-keep-loaded", false),
|
||||
LOCALE("general.translation.locale", ""),
|
||||
MAX_NPC_LIMIT_CHECKS("npc.limits.max-permission-checks", 100),
|
||||
MAX_SPEED("npc.limits.max-speed", 100),
|
||||
MESSAGE_COLOUR("general.color-scheme.message", "<a>"),
|
||||
NPC_COST("economy.npc.cost", 100D),
|
||||
QUICK_SELECT("npc.selection.quick-select", false),
|
||||
REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true),
|
||||
SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60),
|
||||
SELECTION_ITEM("npc.selection.item", "280"),
|
||||
SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"),
|
||||
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"),
|
||||
USE_NEW_PATHFINDER("npc.pathfinding.use-new-finder", false);
|
||||
|
||||
protected String path;
|
||||
protected Object value;
|
||||
|
||||
Setting(String path, Object value) {
|
||||
this.path = path;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
public double asDouble() {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
public float asFloat() {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
if (value instanceof String) {
|
||||
return Integer.parseInt(value.toString());
|
||||
}
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> asList() {
|
||||
if (!(value instanceof List)) {
|
||||
value = Lists.newArrayList(value);
|
||||
}
|
||||
return (List<String>) value;
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
protected void loadFromKey(DataKey root) {
|
||||
value = root.getRaw(path);
|
||||
}
|
||||
|
||||
protected void setAtKey(DataKey root) {
|
||||
root.setRaw(path, value);
|
||||
}
|
||||
}
|
||||
package net.citizensnpcs;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.api.util.Storage;
|
||||
import net.citizensnpcs.api.util.YamlStorage;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class Settings {
|
||||
private final Storage config;
|
||||
private final DataKey root;
|
||||
|
||||
public Settings(File folder) {
|
||||
config = new YamlStorage(new File(folder, "config.yml"), "Citizens Configuration");
|
||||
root = config.getKey("");
|
||||
|
||||
config.load();
|
||||
for (Setting setting : Setting.values()) {
|
||||
if (!root.keyExists(setting.path)) {
|
||||
setting.setAtKey(root);
|
||||
} else
|
||||
setting.loadFromKey(root);
|
||||
}
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
config.load();
|
||||
for (Setting setting : Setting.values())
|
||||
if (root.keyExists(setting.path))
|
||||
setting.loadFromKey(root);
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
config.save();
|
||||
}
|
||||
|
||||
public enum Setting {
|
||||
CHAT_FORMAT("npc.chat.format.no-targets", "[<npc>]: <text>"),
|
||||
CHAT_FORMAT_TO_TARGET("npc.chat.format.to-target", "[<npc>] -> You: <text>"),
|
||||
CHAT_FORMAT_TO_BYSTANDERS("npc.chat.format.with-target-to-bystanders", "[<npc>] -> [<target>]: <text>"),
|
||||
CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS("npc.chat.format.with-targets-to-bystanders", "[<npc>] -> [<targets>]: <text>"),
|
||||
CHAT_RANGE("npc.chat.options.range", 5),
|
||||
CHAT_BYSTANDERS_HEAR_TARGETED_CHAT("npc.chat.options.bystanders-hear-targeted-chat", true),
|
||||
CHAT_MAX_NUMBER_OF_TARGETS("npc.chat.options.max-number-of-targets-to-show", 2),
|
||||
CHAT_MULTIPLE_TARGETS_FORMAT("npc.chat.options.multiple-targets-format", "<target>|, <target>| & <target>| & others"),
|
||||
DATABASE_DRIVER("storage.database.driver", ""),
|
||||
DATABASE_PASSWORD("storage.database.password", ""),
|
||||
DATABASE_URL("storage.database.url", ""),
|
||||
DATABASE_USERNAME("storage.database.username", ""),
|
||||
DEBUG_MODE("general.debug-mode", false),
|
||||
DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false),
|
||||
DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5),
|
||||
DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10),
|
||||
DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F),
|
||||
DEFAULT_RANDOM_TALKER("npc.default.random-talker", true),
|
||||
DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false),
|
||||
DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1),
|
||||
DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false),
|
||||
DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5),
|
||||
DEFAULT_TEXT("npc.default.text.0", "Hi, I'm <npc>!") {
|
||||
@Override
|
||||
public void loadFromKey(DataKey root) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (DataKey key : root.getRelative("npc.default.text").getSubKeys())
|
||||
list.add(key.getString(""));
|
||||
value = list;
|
||||
}
|
||||
},
|
||||
HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "<e>"),
|
||||
KEEP_CHUNKS_LOADED("npc.chunks.always-keep-loaded", false),
|
||||
LOCALE("general.translation.locale", ""),
|
||||
MAX_NPC_LIMIT_CHECKS("npc.limits.max-permission-checks", 100),
|
||||
MAX_SPEED("npc.limits.max-speed", 100),
|
||||
MESSAGE_COLOUR("general.color-scheme.message", "<a>"),
|
||||
NPC_COST("economy.npc.cost", 100D),
|
||||
QUICK_SELECT("npc.selection.quick-select", false),
|
||||
REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true),
|
||||
SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60),
|
||||
SELECTION_ITEM("npc.selection.item", "280"),
|
||||
SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"),
|
||||
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"),
|
||||
USE_NEW_PATHFINDER("npc.pathfinding.use-new-finder", false);
|
||||
|
||||
protected String path;
|
||||
protected Object value;
|
||||
|
||||
Setting(String path, Object value) {
|
||||
this.path = path;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
public double asDouble() {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
public float asFloat() {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
if (value instanceof String) {
|
||||
return Integer.parseInt(value.toString());
|
||||
}
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> asList() {
|
||||
if (!(value instanceof List)) {
|
||||
value = Lists.newArrayList(value);
|
||||
}
|
||||
return (List<String>) value;
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
protected void loadFromKey(DataKey root) {
|
||||
value = root.getRaw(path);
|
||||
}
|
||||
|
||||
protected void setAtKey(DataKey root) {
|
||||
root.setRaw(path, value);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||
import net.citizensnpcs.Citizens;
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.ai.speech.SpeechContext;
|
||||
import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
|
||||
import net.citizensnpcs.api.npc.NPC;
|
||||
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.Owner;
|
||||
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.MemoryDataKey;
|
||||
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(
|
||||
aliases = { "npc" },
|
||||
usage = "speed [speed]",
|
||||
|
@ -1,246 +1,247 @@
|
||||
package net.citizensnpcs.npc;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.citizensnpcs.EventListen;
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.ai.Navigator;
|
||||
import net.citizensnpcs.api.event.DespawnReason;
|
||||
import net.citizensnpcs.api.event.NPCDespawnEvent;
|
||||
import net.citizensnpcs.api.event.NPCSpawnEvent;
|
||||
import net.citizensnpcs.api.npc.AbstractNPC;
|
||||
import net.citizensnpcs.api.persistence.PersistenceLoader;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.trait.trait.Spawned;
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.npc.ai.CitizensNavigator;
|
||||
import net.citizensnpcs.trait.CurrentLocation;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.citizensnpcs.util.Util;
|
||||
import net.minecraft.server.v1_4_6.EntityLiving;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class CitizensNPC extends AbstractNPC {
|
||||
private EntityController entityController;
|
||||
private final CitizensNavigator navigator = new CitizensNavigator(this);
|
||||
private final List<String> removedTraits = Lists.newArrayList();
|
||||
|
||||
public CitizensNPC(int id, String name, EntityController entityController) {
|
||||
super(id, name);
|
||||
Preconditions.checkNotNull(entityController);
|
||||
this.entityController = entityController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean despawn(DespawnReason reason) {
|
||||
if (!isSpawned())
|
||||
return false;
|
||||
|
||||
NPCDespawnEvent event = new NPCDespawnEvent(this, reason);
|
||||
if (reason == DespawnReason.CHUNK_UNLOAD)
|
||||
event.setCancelled(Setting.KEEP_CHUNKS_LOADED.asBoolean());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
getBukkitEntity().getLocation().getChunk();
|
||||
// ensure that we are in a loaded chunk.
|
||||
return false;
|
||||
}
|
||||
boolean keepSelected = getTrait(Spawned.class).shouldSpawn();
|
||||
if (!keepSelected)
|
||||
data().remove("selectors");
|
||||
for (Trait trait : traits.values())
|
||||
trait.onDespawn();
|
||||
entityController.remove();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getBukkitEntity() {
|
||||
return entityController.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public EntityLiving getHandle() {
|
||||
return ((CraftLivingEntity) getBukkitEntity()).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigator getNavigator() {
|
||||
return navigator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpawned() {
|
||||
return getBukkitEntity() != null;
|
||||
}
|
||||
|
||||
public void load(final DataKey root) {
|
||||
metadata.loadFrom(root.getRelative("metadata"));
|
||||
// Load traits
|
||||
|
||||
String traitNames = root.getString("traitnames");
|
||||
Iterable<DataKey> keys = traitNames.isEmpty() ? root.getRelative("traits").getSubKeys() : Iterables
|
||||
.transform(Splitter.on(',').split(traitNames), new Function<String, DataKey>() {
|
||||
@Override
|
||||
public DataKey apply(@Nullable String input) {
|
||||
return root.getRelative("traits." + input);
|
||||
}
|
||||
});
|
||||
for (DataKey traitKey : keys) {
|
||||
if (traitKey.keyExists("enabled") && !traitKey.getBoolean("enabled"))
|
||||
continue;
|
||||
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitKey.name());
|
||||
Trait trait;
|
||||
if (hasTrait(clazz)) {
|
||||
trait = getTrait(clazz);
|
||||
} else {
|
||||
trait = CitizensAPI.getTraitFactory().getTrait(clazz);
|
||||
if (trait == null) {
|
||||
Messaging.severeTr(Messages.SKIPPING_BROKEN_TRAIT, traitKey.name(), getId());
|
||||
continue;
|
||||
}
|
||||
addTrait(trait);
|
||||
}
|
||||
loadTrait(trait, traitKey);
|
||||
}
|
||||
|
||||
// Spawn the NPC
|
||||
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
|
||||
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
|
||||
spawn(spawnLocation.getLocation());
|
||||
|
||||
navigator.load(root.getRelative("navigator"));
|
||||
}
|
||||
|
||||
private void loadTrait(Trait trait, DataKey traitKey) {
|
||||
try {
|
||||
trait.load(traitKey);
|
||||
PersistenceLoader.load(trait, traitKey);
|
||||
} catch (Throwable ex) {
|
||||
Messaging.logTr(Messages.TRAIT_LOAD_FAILED, traitKey.name(), getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrait(Class<? extends Trait> clazz) {
|
||||
Trait present = traits.get(clazz);
|
||||
if (present != null)
|
||||
removedTraits.add(present.getName());
|
||||
super.removeTrait(clazz);
|
||||
}
|
||||
|
||||
private void removeTraitData(DataKey root) {
|
||||
for (String name : removedTraits) {
|
||||
root.removeKey("traits." + name);
|
||||
}
|
||||
removedTraits.clear();
|
||||
}
|
||||
|
||||
public void save(DataKey root) {
|
||||
root.setString("name", getFullName());
|
||||
|
||||
metadata.saveTo(root.getRelative("metadata"));
|
||||
navigator.save(root.getRelative("navigator"));
|
||||
|
||||
// Save all existing traits
|
||||
StringBuilder traitNames = new StringBuilder();
|
||||
for (Trait trait : traits.values()) {
|
||||
DataKey traitKey = root.getRelative("traits." + trait.getName());
|
||||
trait.save(traitKey);
|
||||
PersistenceLoader.save(trait, traitKey);
|
||||
removedTraits.remove(trait.getName());
|
||||
traitNames.append(trait.getName() + ",");
|
||||
}
|
||||
if (traitNames.length() > 0) {
|
||||
root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1));
|
||||
}
|
||||
removeTraitData(root);
|
||||
}
|
||||
|
||||
public void setEntityController(EntityController newController) {
|
||||
Preconditions.checkNotNull(newController);
|
||||
boolean wasSpawned = isSpawned();
|
||||
Location prev = null;
|
||||
if (wasSpawned) {
|
||||
prev = getBukkitEntity().getLocation();
|
||||
despawn();
|
||||
}
|
||||
entityController = newController;
|
||||
if (wasSpawned)
|
||||
spawn(prev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawn(Location at) {
|
||||
Preconditions.checkNotNull(at, "location cannot be null");
|
||||
if (isSpawned())
|
||||
return false;
|
||||
|
||||
entityController.spawn(at, this);
|
||||
EntityLiving mcEntity = getHandle();
|
||||
boolean couldSpawn = !Util.isLoaded(at) ? false : mcEntity.world.addEntity(mcEntity,
|
||||
SpawnReason.CUSTOM);
|
||||
if (!couldSpawn) {
|
||||
// we need to wait for a chunk load before trying to spawn
|
||||
mcEntity = null;
|
||||
EventListen.addForRespawn(at, getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
NPCSpawnEvent spawnEvent = new NPCSpawnEvent(this, at);
|
||||
Bukkit.getPluginManager().callEvent(spawnEvent);
|
||||
if (spawnEvent.isCancelled()) {
|
||||
mcEntity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
NMS.setHeadYaw(mcEntity, at.getYaw());
|
||||
getBukkitEntity().setMetadata(NPC_METADATA_MARKER,
|
||||
new FixedMetadataValue(CitizensAPI.getPlugin(), true));
|
||||
|
||||
// Set the spawned state
|
||||
getTrait(CurrentLocation.class).setLocation(at);
|
||||
getTrait(Spawned.class).setSpawned(true);
|
||||
|
||||
navigator.onSpawn();
|
||||
// Modify NPC using traits after the entity has been created
|
||||
for (Trait trait : traits.values())
|
||||
trait.onSpawn();
|
||||
getBukkitEntity().setRemoveWhenFarAway(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
try {
|
||||
super.update();
|
||||
if (isSpawned()) {
|
||||
NMS.trySwim(getBukkitEntity());
|
||||
navigator.run();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Messaging.logTr(Messages.EXCEPTION_UPDATING_NPC, getId(), ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String NPC_METADATA_MARKER = "NPC";
|
||||
package net.citizensnpcs.npc;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.citizensnpcs.EventListen;
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.ai.Navigator;
|
||||
import net.citizensnpcs.api.event.DespawnReason;
|
||||
import net.citizensnpcs.api.event.NPCDespawnEvent;
|
||||
import net.citizensnpcs.api.event.NPCSpawnEvent;
|
||||
import net.citizensnpcs.api.npc.AbstractNPC;
|
||||
import net.citizensnpcs.api.persistence.PersistenceLoader;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.trait.trait.Spawned;
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.npc.ai.CitizensNavigator;
|
||||
import net.citizensnpcs.trait.CurrentLocation;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.citizensnpcs.util.Util;
|
||||
import net.minecraft.server.v1_4_6.EntityLiving;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class CitizensNPC extends AbstractNPC {
|
||||
private EntityController entityController;
|
||||
private final CitizensNavigator navigator = new CitizensNavigator(this);
|
||||
private final List<String> removedTraits = Lists.newArrayList();
|
||||
|
||||
public CitizensNPC(int id, String name, EntityController entityController) {
|
||||
super(id, name);
|
||||
Preconditions.checkNotNull(entityController);
|
||||
this.entityController = entityController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean despawn(DespawnReason reason) {
|
||||
if (!isSpawned())
|
||||
return false;
|
||||
|
||||
NPCDespawnEvent event = new NPCDespawnEvent(this, reason);
|
||||
if (reason == DespawnReason.CHUNK_UNLOAD)
|
||||
event.setCancelled(Setting.KEEP_CHUNKS_LOADED.asBoolean());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
getBukkitEntity().getLocation().getChunk();
|
||||
// ensure that we are in a loaded chunk.
|
||||
return false;
|
||||
}
|
||||
boolean keepSelected = getTrait(Spawned.class).shouldSpawn();
|
||||
if (!keepSelected)
|
||||
data().remove("selectors");
|
||||
for (Trait trait : traits.values())
|
||||
trait.onDespawn();
|
||||
entityController.remove();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getBukkitEntity() {
|
||||
return entityController.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public EntityLiving getHandle() {
|
||||
return ((CraftLivingEntity) getBukkitEntity()).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigator getNavigator() {
|
||||
return navigator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpawned() {
|
||||
return getBukkitEntity() != null;
|
||||
}
|
||||
|
||||
public void load(final DataKey root) {
|
||||
metadata.loadFrom(root.getRelative("metadata"));
|
||||
// Load traits
|
||||
|
||||
String traitNames = root.getString("traitnames");
|
||||
Iterable<DataKey> keys = traitNames.isEmpty() ? root.getRelative("traits").getSubKeys() : Iterables
|
||||
.transform(Splitter.on(',').split(traitNames), new Function<String, DataKey>() {
|
||||
@Override
|
||||
public DataKey apply(@Nullable String input) {
|
||||
return root.getRelative("traits." + input);
|
||||
}
|
||||
});
|
||||
for (DataKey traitKey : keys) {
|
||||
if (traitKey.keyExists("enabled") && !traitKey.getBoolean("enabled"))
|
||||
continue;
|
||||
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitKey.name());
|
||||
Trait trait;
|
||||
if (hasTrait(clazz)) {
|
||||
trait = getTrait(clazz);
|
||||
} else {
|
||||
trait = CitizensAPI.getTraitFactory().getTrait(clazz);
|
||||
if (trait == null) {
|
||||
Messaging.severeTr(Messages.SKIPPING_BROKEN_TRAIT, traitKey.name(), getId());
|
||||
continue;
|
||||
}
|
||||
addTrait(trait);
|
||||
}
|
||||
loadTrait(trait, traitKey);
|
||||
}
|
||||
|
||||
// Spawn the NPC
|
||||
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
|
||||
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
|
||||
spawn(spawnLocation.getLocation());
|
||||
|
||||
navigator.load(root.getRelative("navigator"));
|
||||
}
|
||||
|
||||
private void loadTrait(Trait trait, DataKey traitKey) {
|
||||
try {
|
||||
trait.load(traitKey);
|
||||
PersistenceLoader.load(trait, traitKey);
|
||||
} catch (Throwable ex) {
|
||||
Messaging.logTr(Messages.TRAIT_LOAD_FAILED, traitKey.name(), getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrait(Class<? extends Trait> clazz) {
|
||||
Trait present = traits.get(clazz);
|
||||
if (present != null)
|
||||
removedTraits.add(present.getName());
|
||||
super.removeTrait(clazz);
|
||||
}
|
||||
|
||||
private void removeTraitData(DataKey root) {
|
||||
for (String name : removedTraits) {
|
||||
root.removeKey("traits." + name);
|
||||
}
|
||||
removedTraits.clear();
|
||||
}
|
||||
|
||||
public void save(DataKey root) {
|
||||
root.setString("name", getFullName());
|
||||
|
||||
metadata.saveTo(root.getRelative("metadata"));
|
||||
navigator.save(root.getRelative("navigator"));
|
||||
|
||||
// Save all existing traits
|
||||
StringBuilder traitNames = new StringBuilder();
|
||||
for (Trait trait : traits.values()) {
|
||||
DataKey traitKey = root.getRelative("traits." + trait.getName());
|
||||
trait.save(traitKey);
|
||||
PersistenceLoader.save(trait, traitKey);
|
||||
removedTraits.remove(trait.getName());
|
||||
traitNames.append(trait.getName() + ",");
|
||||
}
|
||||
if (traitNames.length() > 0) {
|
||||
root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1));
|
||||
}
|
||||
removeTraitData(root);
|
||||
}
|
||||
|
||||
public void setEntityController(EntityController newController) {
|
||||
Preconditions.checkNotNull(newController);
|
||||
boolean wasSpawned = isSpawned();
|
||||
Location prev = null;
|
||||
if (wasSpawned) {
|
||||
prev = getBukkitEntity().getLocation();
|
||||
despawn();
|
||||
}
|
||||
entityController = newController;
|
||||
if (wasSpawned)
|
||||
spawn(prev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawn(Location at) {
|
||||
Preconditions.checkNotNull(at, "location cannot be null");
|
||||
if (isSpawned())
|
||||
return false;
|
||||
|
||||
entityController.spawn(at, this);
|
||||
EntityLiving mcEntity = getHandle();
|
||||
boolean couldSpawn = !Util.isLoaded(at) ? false : mcEntity.world.addEntity(mcEntity,
|
||||
SpawnReason.CUSTOM);
|
||||
if (!couldSpawn) {
|
||||
// we need to wait for a chunk load before trying to spawn
|
||||
mcEntity = null;
|
||||
EventListen.addForRespawn(at, getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
NPCSpawnEvent spawnEvent = new NPCSpawnEvent(this, at);
|
||||
Bukkit.getPluginManager().callEvent(spawnEvent);
|
||||
if (spawnEvent.isCancelled()) {
|
||||
mcEntity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
NMS.setHeadYaw(mcEntity, at.getYaw());
|
||||
getBukkitEntity().setMetadata(NPC_METADATA_MARKER,
|
||||
new FixedMetadataValue(CitizensAPI.getPlugin(), true));
|
||||
|
||||
// Set the spawned state
|
||||
getTrait(CurrentLocation.class).setLocation(at);
|
||||
getTrait(Spawned.class).setSpawned(true);
|
||||
|
||||
navigator.onSpawn();
|
||||
// Modify NPC using traits after the entity has been created
|
||||
for (Trait trait : traits.values())
|
||||
trait.onSpawn();
|
||||
getBukkitEntity().setRemoveWhenFarAway(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
try {
|
||||
super.update();
|
||||
if (isSpawned()) {
|
||||
NMS.trySwim(getBukkitEntity());
|
||||
navigator.run();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Messaging.logTr(Messages.EXCEPTION_UPDATING_NPC, getId(), ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String NPC_METADATA_MARKER = "NPC";
|
||||
|
||||
}
|
@ -1,136 +1,138 @@
|
||||
package net.citizensnpcs.npc;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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.TraitFactory;
|
||||
import net.citizensnpcs.api.trait.TraitInfo;
|
||||
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.Anchors;
|
||||
import net.citizensnpcs.trait.Behaviour;
|
||||
import net.citizensnpcs.trait.Controllable;
|
||||
import net.citizensnpcs.trait.CurrentLocation;
|
||||
import net.citizensnpcs.trait.Gravity;
|
||||
import net.citizensnpcs.trait.LookClose;
|
||||
import net.citizensnpcs.trait.NPCSkeletonType;
|
||||
import net.citizensnpcs.trait.Poses;
|
||||
import net.citizensnpcs.trait.Powered;
|
||||
import net.citizensnpcs.trait.Saddle;
|
||||
import net.citizensnpcs.trait.Sheared;
|
||||
import net.citizensnpcs.trait.SlimeSize;
|
||||
import net.citizensnpcs.trait.VillagerProfession;
|
||||
import net.citizensnpcs.trait.WoolColor;
|
||||
import net.citizensnpcs.trait.ZombieModifier;
|
||||
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.collect.Sets;
|
||||
|
||||
public class CitizensTraitFactory implements TraitFactory {
|
||||
private final Map<String, Class<? extends Trait>> registered = Maps.newHashMap();
|
||||
|
||||
public CitizensTraitFactory() {
|
||||
registerTrait(TraitInfo.create(Age.class).withName("age"));
|
||||
registerTrait(TraitInfo.create(Anchors.class).withName("anchors"));
|
||||
registerTrait(TraitInfo.create(Behaviour.class).withName("behaviour"));
|
||||
registerTrait(TraitInfo.create(Controllable.class).withName("controllable"));
|
||||
registerTrait(TraitInfo.create(Equipment.class).withName("equipment"));
|
||||
registerTrait(TraitInfo.create(Gravity.class).withName("gravity"));
|
||||
registerTrait(TraitInfo.create(Inventory.class).withName("inventory"));
|
||||
registerTrait(TraitInfo.create(CurrentLocation.class).withName("location"));
|
||||
registerTrait(TraitInfo.create(LookClose.class).withName("lookclose"));
|
||||
registerTrait(TraitInfo.create(Owner.class).withName("owner"));
|
||||
registerTrait(TraitInfo.create(Poses.class).withName("poses"));
|
||||
registerTrait(TraitInfo.create(Powered.class).withName("powered"));
|
||||
registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession"));
|
||||
registerTrait(TraitInfo.create(Saddle.class).withName("saddle"));
|
||||
registerTrait(TraitInfo.create(Sheared.class).withName("sheared"));
|
||||
registerTrait(TraitInfo.create(NPCSkeletonType.class).withName("skeletontype"));
|
||||
registerTrait(TraitInfo.create(SlimeSize.class).withName("slimesize"));
|
||||
registerTrait(TraitInfo.create(Spawned.class).withName("spawned"));
|
||||
registerTrait(TraitInfo.create(Text.class).withName("text"));
|
||||
registerTrait(TraitInfo.create(MobType.class).withName("type"));
|
||||
registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints"));
|
||||
registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor"));
|
||||
registerTrait(TraitInfo.create(ZombieModifier.class).withName("zombiemodifier"));
|
||||
|
||||
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()))
|
||||
continue;
|
||||
final Class<? extends Trait> 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 extends Trait> T create(Class<T> trait) {
|
||||
try {
|
||||
return trait.newInstance();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Trait> T getTrait(Class<T> clazz) {
|
||||
if (!registered.containsValue(clazz))
|
||||
return null;
|
||||
return create(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Trait> T getTrait(String name) {
|
||||
Class<? extends Trait> clazz = registered.get(name);
|
||||
if (clazz == null)
|
||||
return null;
|
||||
return (T) create(clazz);
|
||||
}
|
||||
|
||||
@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 void registerTrait(TraitInfo info) {
|
||||
Preconditions.checkNotNull(info, "info cannot be null");
|
||||
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();
|
||||
package net.citizensnpcs.npc;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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.TraitFactory;
|
||||
import net.citizensnpcs.api.trait.TraitInfo;
|
||||
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.api.trait.trait.Speech;
|
||||
import net.citizensnpcs.trait.Age;
|
||||
import net.citizensnpcs.trait.Anchors;
|
||||
import net.citizensnpcs.trait.Behaviour;
|
||||
import net.citizensnpcs.trait.Controllable;
|
||||
import net.citizensnpcs.trait.CurrentLocation;
|
||||
import net.citizensnpcs.trait.Gravity;
|
||||
import net.citizensnpcs.trait.LookClose;
|
||||
import net.citizensnpcs.trait.NPCSkeletonType;
|
||||
import net.citizensnpcs.trait.Poses;
|
||||
import net.citizensnpcs.trait.Powered;
|
||||
import net.citizensnpcs.trait.Saddle;
|
||||
import net.citizensnpcs.trait.Sheared;
|
||||
import net.citizensnpcs.trait.SlimeSize;
|
||||
import net.citizensnpcs.trait.VillagerProfession;
|
||||
import net.citizensnpcs.trait.WoolColor;
|
||||
import net.citizensnpcs.trait.ZombieModifier;
|
||||
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.collect.Sets;
|
||||
|
||||
public class CitizensTraitFactory implements TraitFactory {
|
||||
private final Map<String, Class<? extends Trait>> registered = Maps.newHashMap();
|
||||
|
||||
public CitizensTraitFactory() {
|
||||
registerTrait(TraitInfo.create(Age.class).withName("age"));
|
||||
registerTrait(TraitInfo.create(Anchors.class).withName("anchors"));
|
||||
registerTrait(TraitInfo.create(Behaviour.class).withName("behaviour"));
|
||||
registerTrait(TraitInfo.create(Controllable.class).withName("controllable"));
|
||||
registerTrait(TraitInfo.create(Equipment.class).withName("equipment"));
|
||||
registerTrait(TraitInfo.create(Gravity.class).withName("gravity"));
|
||||
registerTrait(TraitInfo.create(Inventory.class).withName("inventory"));
|
||||
registerTrait(TraitInfo.create(CurrentLocation.class).withName("location"));
|
||||
registerTrait(TraitInfo.create(LookClose.class).withName("lookclose"));
|
||||
registerTrait(TraitInfo.create(Owner.class).withName("owner"));
|
||||
registerTrait(TraitInfo.create(Poses.class).withName("poses"));
|
||||
registerTrait(TraitInfo.create(Powered.class).withName("powered"));
|
||||
registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession"));
|
||||
registerTrait(TraitInfo.create(Saddle.class).withName("saddle"));
|
||||
registerTrait(TraitInfo.create(Sheared.class).withName("sheared"));
|
||||
registerTrait(TraitInfo.create(NPCSkeletonType.class).withName("skeletontype"));
|
||||
registerTrait(TraitInfo.create(SlimeSize.class).withName("slimesize"));
|
||||
registerTrait(TraitInfo.create(Spawned.class).withName("spawned"));
|
||||
registerTrait(TraitInfo.create(Speech.class).withName("speech"));
|
||||
registerTrait(TraitInfo.create(Text.class).withName("text"));
|
||||
registerTrait(TraitInfo.create(MobType.class).withName("type"));
|
||||
registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints"));
|
||||
registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor"));
|
||||
registerTrait(TraitInfo.create(ZombieModifier.class).withName("zombiemodifier"));
|
||||
|
||||
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()))
|
||||
continue;
|
||||
final Class<? extends Trait> 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 extends Trait> T create(Class<T> trait) {
|
||||
try {
|
||||
return trait.newInstance();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Trait> T getTrait(Class<T> clazz) {
|
||||
if (!registered.containsValue(clazz))
|
||||
return null;
|
||||
return create(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Trait> T getTrait(String name) {
|
||||
Class<? extends Trait> clazz = registered.get(name);
|
||||
if (clazz == null)
|
||||
return null;
|
||||
return (T) create(clazz);
|
||||
}
|
||||
|
||||
@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 void registerTrait(TraitInfo info) {
|
||||
Preconditions.checkNotNull(info, "info cannot be null");
|
||||
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();
|
||||
}
|
117
src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java
Normal file
117
src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -1,232 +1,237 @@
|
||||
package net.citizensnpcs.trait.text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.CitizensAPI;
|
||||
import net.citizensnpcs.api.event.NPCRightClickEvent;
|
||||
import net.citizensnpcs.api.exception.NPCLoadException;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.editor.Editor;
|
||||
import net.citizensnpcs.trait.Toggleable;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.Paginator;
|
||||
import net.citizensnpcs.util.Util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.conversations.Conversation;
|
||||
import org.bukkit.conversations.ConversationAbandonedEvent;
|
||||
import org.bukkit.conversations.ConversationAbandonedListener;
|
||||
import org.bukkit.conversations.ConversationFactory;
|
||||
import org.bukkit.entity.Entity;
|
||||
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, Listener, ConversationAbandonedListener {
|
||||
private final Map<String, Date> cooldowns = new HashMap<String, Date>();
|
||||
private int currentIndex;
|
||||
private String itemInHandPattern = "default";
|
||||
private final Plugin plugin;
|
||||
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
|
||||
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
|
||||
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
|
||||
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
|
||||
private final List<String> text = new ArrayList<String>();
|
||||
|
||||
public Text() {
|
||||
super("text");
|
||||
this.plugin = CitizensAPI.getPlugin();
|
||||
}
|
||||
|
||||
void add(String string) {
|
||||
text.add(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conversationAbandoned(ConversationAbandonedEvent event) {
|
||||
Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text");
|
||||
}
|
||||
|
||||
void edit(int index, String newText) {
|
||||
text.set(index, newText);
|
||||
}
|
||||
|
||||
public Editor getEditor(final Player player) {
|
||||
final Conversation conversation = new ConversationFactory(plugin)
|
||||
.addConversationAbandonedListener(this).withLocalEcho(false).withEscapeSequence("/npc text")
|
||||
.withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextStartPrompt(this))
|
||||
.buildConversation(player);
|
||||
return new Editor() {
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN);
|
||||
conversation.begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
Messaging.sendTr(player, Messages.TEXT_EDITOR_END);
|
||||
conversation.abandon();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
boolean hasIndex(int index) {
|
||||
return index >= 0 && text.size() > index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(DataKey key) throws NPCLoadException {
|
||||
text.clear();
|
||||
// TODO: legacy, remove later
|
||||
for (DataKey sub : key.getIntegerSubKeys())
|
||||
text.add(sub.getString(""));
|
||||
for (DataKey sub : key.getRelative("text").getIntegerSubKeys())
|
||||
text.add(sub.getString(""));
|
||||
if (text.isEmpty())
|
||||
populateDefaultText();
|
||||
|
||||
talkClose = key.getBoolean("talk-close", talkClose);
|
||||
realisticLooker = key.getBoolean("realistic-looking", realisticLooker);
|
||||
randomTalker = key.getBoolean("random-talker", randomTalker);
|
||||
range = key.getDouble("range", range);
|
||||
itemInHandPattern = key.getString("talkitem", itemInHandPattern);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onRightClick(NPCRightClickEvent event) {
|
||||
if (!event.getNPC().equals(npc))
|
||||
return;
|
||||
String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString()
|
||||
: itemInHandPattern;
|
||||
if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose())
|
||||
sendText(event.getClicker());
|
||||
}
|
||||
|
||||
private void populateDefaultText() {
|
||||
text.addAll(Setting.DEFAULT_TEXT.asList());
|
||||
}
|
||||
|
||||
void remove(int index) {
|
||||
text.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!talkClose || !npc.isSpawned())
|
||||
return;
|
||||
List<Entity> nearby = npc.getBukkitEntity().getNearbyEntities(range, range, range);
|
||||
for (Entity search : nearby) {
|
||||
if (!(search instanceof Player))
|
||||
continue;
|
||||
Player player = (Player) search;
|
||||
// If the cooldown is not expired, do not send text
|
||||
Date cooldown = cooldowns.get(player.getName());
|
||||
if (cooldown != null) {
|
||||
if (!new Date().after(cooldown))
|
||||
return;
|
||||
cooldowns.remove(player.getName());
|
||||
}
|
||||
if (!sendText(player))
|
||||
return;
|
||||
// Add a cooldown if the text was successfully sent
|
||||
Date wait = new Date();
|
||||
int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt())
|
||||
+ Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt();
|
||||
if (secondsDelta <= 0)
|
||||
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);
|
||||
key.setBoolean("realistic-looking", realisticLooker);
|
||||
key.setDouble("range", range);
|
||||
key.setString("talkitem", itemInHandPattern);
|
||||
// TODO: legacy, remove later
|
||||
for (int i = 0; i < 100; i++)
|
||||
key.removeKey(String.valueOf(i));
|
||||
key.removeKey("text");
|
||||
for (int i = 0; i < text.size(); i++)
|
||||
key.setString("text." + String.valueOf(i), text.get(i));
|
||||
}
|
||||
|
||||
boolean sendPage(Player player, int page) {
|
||||
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));
|
||||
|
||||
return paginator.sendPage(player, page);
|
||||
}
|
||||
|
||||
private boolean sendText(Player player) {
|
||||
if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk"))
|
||||
return false;
|
||||
if (text.size() == 0)
|
||||
return false;
|
||||
|
||||
int index = 0;
|
||||
if (randomTalker)
|
||||
index = new Random().nextInt(text.size());
|
||||
else {
|
||||
if (currentIndex > text.size() - 1)
|
||||
currentIndex = 0;
|
||||
index = currentIndex++;
|
||||
}
|
||||
Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + text.get(index), npc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setItemInHandPattern(String pattern) {
|
||||
itemInHandPattern = pattern;
|
||||
}
|
||||
|
||||
void setRange(double range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
boolean shouldTalkClose() {
|
||||
return talkClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggle() {
|
||||
return (talkClose = !talkClose);
|
||||
}
|
||||
|
||||
boolean toggleRandomTalker() {
|
||||
return (randomTalker = !randomTalker);
|
||||
}
|
||||
|
||||
boolean toggleRealisticLooking() {
|
||||
return (realisticLooker = !realisticLooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Text{talk-close=" + talkClose + ",text=");
|
||||
for (String line : text)
|
||||
builder.append(line + ",");
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static Random RANDOM = Util.getFastRandom();
|
||||
package net.citizensnpcs.trait.text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.CitizensAPI;
|
||||
import net.citizensnpcs.api.ai.speech.SpeechContext;
|
||||
import net.citizensnpcs.api.ai.speech.Talkable;
|
||||
import net.citizensnpcs.api.event.NPCRightClickEvent;
|
||||
import net.citizensnpcs.api.exception.NPCLoadException;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.editor.Editor;
|
||||
import net.citizensnpcs.npc.ai.speech.TalkableEntity;
|
||||
import net.citizensnpcs.trait.Toggleable;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.Messaging;
|
||||
import net.citizensnpcs.util.Paginator;
|
||||
import net.citizensnpcs.util.Util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.conversations.Conversation;
|
||||
import org.bukkit.conversations.ConversationAbandonedEvent;
|
||||
import org.bukkit.conversations.ConversationAbandonedListener;
|
||||
import org.bukkit.conversations.ConversationFactory;
|
||||
import org.bukkit.entity.Entity;
|
||||
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, Listener, ConversationAbandonedListener {
|
||||
private final Map<String, Date> cooldowns = new HashMap<String, Date>();
|
||||
private int currentIndex;
|
||||
private String itemInHandPattern = "default";
|
||||
private final Plugin plugin;
|
||||
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
|
||||
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
|
||||
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
|
||||
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
|
||||
private final List<String> text = new ArrayList<String>();
|
||||
|
||||
public Text() {
|
||||
super("text");
|
||||
this.plugin = CitizensAPI.getPlugin();
|
||||
}
|
||||
|
||||
void add(String string) {
|
||||
text.add(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conversationAbandoned(ConversationAbandonedEvent event) {
|
||||
Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text");
|
||||
}
|
||||
|
||||
void edit(int index, String newText) {
|
||||
text.set(index, newText);
|
||||
}
|
||||
|
||||
public Editor getEditor(final Player player) {
|
||||
final Conversation conversation = new ConversationFactory(plugin)
|
||||
.addConversationAbandonedListener(this).withLocalEcho(false).withEscapeSequence("/npc text")
|
||||
.withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextStartPrompt(this))
|
||||
.buildConversation(player);
|
||||
return new Editor() {
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN);
|
||||
conversation.begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
Messaging.sendTr(player, Messages.TEXT_EDITOR_END);
|
||||
conversation.abandon();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
boolean hasIndex(int index) {
|
||||
return index >= 0 && text.size() > index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(DataKey key) throws NPCLoadException {
|
||||
text.clear();
|
||||
// TODO: legacy, remove later
|
||||
for (DataKey sub : key.getIntegerSubKeys())
|
||||
text.add(sub.getString(""));
|
||||
for (DataKey sub : key.getRelative("text").getIntegerSubKeys())
|
||||
text.add(sub.getString(""));
|
||||
if (text.isEmpty())
|
||||
populateDefaultText();
|
||||
|
||||
talkClose = key.getBoolean("talk-close", talkClose);
|
||||
realisticLooker = key.getBoolean("realistic-looking", realisticLooker);
|
||||
randomTalker = key.getBoolean("random-talker", randomTalker);
|
||||
range = key.getDouble("range", range);
|
||||
itemInHandPattern = key.getString("talkitem", itemInHandPattern);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onRightClick(NPCRightClickEvent event) {
|
||||
if (!event.getNPC().equals(npc))
|
||||
return;
|
||||
String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString()
|
||||
: itemInHandPattern;
|
||||
if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose())
|
||||
sendText(event.getClicker());
|
||||
}
|
||||
|
||||
private void populateDefaultText() {
|
||||
text.addAll(Setting.DEFAULT_TEXT.asList());
|
||||
}
|
||||
|
||||
void remove(int index) {
|
||||
text.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!talkClose || !npc.isSpawned())
|
||||
return;
|
||||
List<Entity> nearby = npc.getBukkitEntity().getNearbyEntities(range, range, range);
|
||||
for (Entity search : nearby) {
|
||||
if (!(search instanceof Player))
|
||||
continue;
|
||||
Player player = (Player) search;
|
||||
// If the cooldown is not expired, do not send text
|
||||
Date cooldown = cooldowns.get(player.getName());
|
||||
if (cooldown != null) {
|
||||
if (!new Date().after(cooldown))
|
||||
return;
|
||||
cooldowns.remove(player.getName());
|
||||
}
|
||||
if (!sendText(player))
|
||||
return;
|
||||
// Add a cooldown if the text was successfully sent
|
||||
Date wait = new Date();
|
||||
int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt())
|
||||
+ Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt();
|
||||
if (secondsDelta <= 0)
|
||||
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);
|
||||
key.setBoolean("realistic-looking", realisticLooker);
|
||||
key.setDouble("range", range);
|
||||
key.setString("talkitem", itemInHandPattern);
|
||||
// TODO: legacy, remove later
|
||||
for (int i = 0; i < 100; i++)
|
||||
key.removeKey(String.valueOf(i));
|
||||
key.removeKey("text");
|
||||
for (int i = 0; i < text.size(); i++)
|
||||
key.setString("text." + String.valueOf(i), text.get(i));
|
||||
}
|
||||
|
||||
boolean sendPage(Player player, int page) {
|
||||
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));
|
||||
|
||||
return paginator.sendPage(player, page);
|
||||
}
|
||||
|
||||
private boolean sendText(Player player) {
|
||||
if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk"))
|
||||
return false;
|
||||
if (text.size() == 0)
|
||||
return false;
|
||||
|
||||
int index = 0;
|
||||
if (randomTalker)
|
||||
index = new Random().nextInt(text.size());
|
||||
else {
|
||||
if (currentIndex > text.size() - 1)
|
||||
currentIndex = 0;
|
||||
index = currentIndex++;
|
||||
}
|
||||
|
||||
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
|
||||
// Messaging.sendWithNPC(player, Setting.CHAT_PREFIX.asString() + text.get(index), npc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setItemInHandPattern(String pattern) {
|
||||
itemInHandPattern = pattern;
|
||||
}
|
||||
|
||||
void setRange(double range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
boolean shouldTalkClose() {
|
||||
return talkClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggle() {
|
||||
return (talkClose = !talkClose);
|
||||
}
|
||||
|
||||
boolean toggleRandomTalker() {
|
||||
return (randomTalker = !randomTalker);
|
||||
}
|
||||
|
||||
boolean toggleRealisticLooking() {
|
||||
return (realisticLooker = !realisticLooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Text{talk-close=" + talkClose + ",text=");
|
||||
for (String line : text)
|
||||
builder.append(line + ",");
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static Random RANDOM = Util.getFastRandom();
|
||||
}
|
Loading…
Reference in New Issue
Block a user