diff --git a/README.md b/README.md index f63f673..a6a3ebc 100644 --- a/README.md +++ b/README.md @@ -15,36 +15,42 @@ $ mvn install Note: `/destination purge` and `/portal purge` require [specific keywords defined below these commands](#definitions). -| Command | Description | -|-------------------------------------------------------------|--------------------------------------------------------------------| -|`/destination change ` | Sets the location of a destination to the user's current position. | -|`/destination clear ` | Removes the destination of a portal. | -|`/destination create ` | Creates a new destination at the user's current position. | -|`/destination info ` | Displays information about the current destination. | -|`/destination list` | Displays a list of all the existing destinations. | -|`/destination purge <'invalid'/'no-portals'/'both'>` | Purges any unused and / or invalid destinations. | -|`/destination remove ` | Removes a destination. | -|`/destination set ` | Sets the destination of a portal. | -|`/destination sethere ` | Sets the destination of the portal to the user's current location. | -|`/destination teleport ` | Teleports the user to the provided destination. | -|`/portal create ` | Creates a permissionless portal using the selected area. | -|`/portal debug` | Displays information about the portal being entered (no teleport). | -|`/portal info ` | Displays information about a portal. | -|`/portal list` | Lists all existing portals. | -|`/portal permission whimc-portals.entry.` | Sets or removes portal permissions. | -|`/portal purge <'invalid'/'no-destination'/'both'>` | Purges any unused and / or invalid portals. | -|`/portal refill ` | Regenerates the filler of a portal. | -|`/portal remove ` | Removes a portal. | -|`/portal reshape ` | Reshape a portal to your current selection. | -|`/portal setfiller ` | Sets the filler of a portal. | -|`/portal teleport ` | Teleports the user to the provided portal. | -|`/portal tool` | Gives user the portal selector tool. | +| Command | Description | +|--------------------------------------------------------------|--------------------------------------------------------------------| +| `/destination change ` | Sets the location of a destination to the user's current position. | +| `/destination clear ` | Removes the destination of a portal. | +| `/destination create ` | Creates a new destination at the user's current position. | +| `/destination info ` | Displays information about the current destination. | +| `/destination list` | Displays a list of all the existing destinations. | +| `/destination purge <'invalid'/'no-portals'/'both'>` | Purges any unused and / or invalid destinations. | +| `/destination remove ` | Removes a destination. | +| `/destination set ` | Sets the destination of a portal. | +| `/destination sethere ` | Sets the destination of the portal to the user's current location. | +| `/destination teleport ` | Teleports the user to the provided destination. | +| `/portal create ` | Creates a permissionless portal using the selected area. | +| `/portal debug` | Displays information about the portal being entered (no teleport). | +| `/portal info ` | Displays information about a portal. | +| `/portal list` | Lists all existing portals. | +| `/portal permission whimc-portals.entry.` | Sets or removes portal permissions. | +| `/portal purge <'invalid'/'no-destination'/'both'>` | Purges any unused and / or invalid portals. | +| `/portal refill ` | Regenerates the filler of a portal. | +| `/portal remove ` | Removes a portal. | +| `/portal reshape ` | Reshape a portal to your current selection. | +| `/portal setfiller ` | Sets the filler of a portal. | +| `/portal teleport ` | Teleports the user to the provided portal. | +| `/portal tool` | Gives user the portal selector tool. | +| `/portal allowcitizens ` | Allows Citizens NPCs to use the specified portal. | ### Definitions - `invalid` means that the destination/portal is in a non-existent world (used in `/destination purge` and `/portal purge`) - `no-portals` means that the destination has no linked portals (used in `/destination purge`) - `no-destination` means that the portal has no linked destinations (used in `/portal purge`) +### Notes About Citizens Integration +- The pathing waypoint must be set 1 block behind the portal so that a Citizen steps on the portal block +- Citizens will swim up water filled portals +- Citizens will not go through lava filled portals + ### How to Create a Portal To set up a new portal you'll want to do `/portal tool` to get the portal selection tool. Then left click to select your first position and right click to select your second. These two blocks will be the bounding corners of a cuboid [like WorldEdit](https://worldedit.enginehub.org/en/latest/usage/regions/selections/). Then do: diff --git a/pom.xml b/pom.xml index 81da15a..a48785f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 edu.whimc WHIMC-Portals - 2.8.3 + 2.9.0 WHIMC Portals Create portals for player teleportation @@ -14,6 +14,12 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + everything + https://repo.citizensnpcs.co/ + @@ -24,6 +30,15 @@ 1.14.4-R0.1-SNAPSHOT provided + + + + net.citizensnpcs + citizens-main + 2.0.29-SNAPSHOT + jar + provided + diff --git a/src/main/java/edu/whimc/portals/Main.java b/src/main/java/edu/whimc/portals/Main.java index d1f9e58..770d187 100644 --- a/src/main/java/edu/whimc/portals/Main.java +++ b/src/main/java/edu/whimc/portals/Main.java @@ -1,5 +1,7 @@ package edu.whimc.portals; +import edu.whimc.portals.listeners.*; +import net.citizensnpcs.api.CitizensPlugin; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -10,10 +12,6 @@ import edu.whimc.portals.commands.destination.DestinationCommand; import edu.whimc.portals.commands.portal.PortalCommand; -import edu.whimc.portals.listeners.PortalBlockChangeListener; -import edu.whimc.portals.listeners.PortalDamageListener; -import edu.whimc.portals.listeners.PortalEnterListener; -import edu.whimc.portals.listeners.ToolSelectListener; import edu.whimc.portals.utils.LocationSaver; import edu.whimc.portals.utils.MyConfig; import edu.whimc.portals.utils.MyConfigManager; @@ -32,11 +30,15 @@ public class Main extends JavaPlugin { /** The instance of the location saver. */ private LocationSaver locationSaver; + /** Instance variable storing whether Citizens Plugin is enabled*/ + private boolean citizensEnabled; + @Override public void onEnable() { manager = new MyConfigManager(this); portalData = manager.getNewConfig("portalData.yml"); locationSaver = new LocationSaver(this); + citizensEnabled = Bukkit.getPluginManager().isPluginEnabled("Citizens"); Permission parent = new Permission(PERM_PREFIX + ".*"); Bukkit.getPluginManager().addPermission(parent); @@ -59,6 +61,9 @@ public LocationSaver getLocationSaver() { return locationSaver; } + /** @return Status of Citizens Plugin */ + public boolean getPluginStatus() { return citizensEnabled;} + /** * Registers event listeners and sub-commands. */ @@ -69,6 +74,11 @@ private void registerStuff() { pm.registerEvents(new PortalBlockChangeListener(), this); pm.registerEvents(new PortalDamageListener(), this); + // check if Citizens is enabled + if (citizensEnabled) { + pm.registerEvents(new PortalEnterCitizensListener(), this); + } + PortalCommand pc = new PortalCommand(this); getCommand("portal").setExecutor(pc); getCommand("portal").setTabCompleter(pc); @@ -117,7 +127,9 @@ private void initializeConfig() { String fillerName = portalData.getString("Portals." + key + ".filler", ""); Material filler = Material.matchMaterial(fillerName); - Portal.loadPortal(this, key, permission, portalWorldName, pos1, pos2, dest, filler); + boolean allowCitizens = portalData.getBoolean("Portals." + key + ".allowcitizens"); + + Portal.loadPortal(this, key, permission, portalWorldName, pos1, pos2, dest, filler, allowCitizens); } } } diff --git a/src/main/java/edu/whimc/portals/Portal.java b/src/main/java/edu/whimc/portals/Portal.java index 826bf86..a92e79f 100644 --- a/src/main/java/edu/whimc/portals/Portal.java +++ b/src/main/java/edu/whimc/portals/Portal.java @@ -62,6 +62,9 @@ public class Portal { /** If the portal is valid. */ private boolean valid = true; + /** If the portal allows citizens to use it. */ + private boolean allowCitizens; + /** * Creates a Portal. * @@ -74,7 +77,7 @@ public class Portal { * @return The new Portal. */ public static Portal createPortal(Main plugin, String name, String permission, World world, Vector pos1, Vector pos2) { - return new Portal(plugin, name, permission, world.getName(), pos1, pos2, null, defaultFiller, true); + return new Portal(plugin, name, permission, world.getName(), pos1, pos2, null, defaultFiller, false, true); } /** @@ -90,8 +93,8 @@ public static Portal createPortal(Main plugin, String name, String permission, W * @param filler The filler materials of the portal. * @return The loaded portal. */ - public static Portal loadPortal(Main plugin, String name, String permission, String worldName, Vector pos1, Vector pos2, Destination destination, Material filler) { - return new Portal(plugin, name, permission, worldName, pos1, pos2, destination, filler, false); + public static Portal loadPortal(Main plugin, String name, String permission, String worldName, Vector pos1, Vector pos2, Destination destination, Material filler, boolean allowCitizens) { + return new Portal(plugin, name, permission, worldName, pos1, pos2, destination, filler, allowCitizens, false); } /** @@ -107,7 +110,7 @@ public static Portal loadPortal(Main plugin, String name, String permission, Str * @param filler The filler materials of the portal. * @param isNew If the portal is a newly created one. */ - private Portal(Main plugin, String name, String permission, String worldName, Vector pos1, Vector pos2, Destination destination, Material filler, boolean isNew){ + private Portal(Main plugin, String name, String permission, String worldName, Vector pos1, Vector pos2, Destination destination, Material filler, boolean allowCitizens, boolean isNew){ this.plugin = plugin; this.name = name; this.worldName = worldName; @@ -120,6 +123,7 @@ private Portal(Main plugin, String name, String permission, String worldName, Ve } else { this.filler = filler; } + this.allowCitizens = allowCitizens; if(isNew) { setConfig("world", worldName); @@ -129,6 +133,7 @@ private Portal(Main plugin, String name, String permission, String worldName, Ve setConfig("filler", this.filler.toString()); setConfig("permission", permission); setConfig("destination", destination == null ? Destination.NONE : name); + setConfig("allowcitizens", this.allowCitizens); saveConfig(); } portals.add(this); @@ -655,4 +660,19 @@ public String toString() { return "&f&o" + this.name; } + /** + * Sets if Citizens NPCs are allowed to use the portal. + * + * @param allowCitizens Allow Citizens NPCs to use the portal. + */ + public void setAllowCitizens(boolean allowCitizens) { + this.allowCitizens = allowCitizens; + setConfig("allowcitizens", this.allowCitizens); + saveConfig(); + } + + /** If Citizens NPCs are allowed to use the portal. */ + public boolean getAllowCitizens() { + return allowCitizens; + } } diff --git a/src/main/java/edu/whimc/portals/commands/portal/PortalAllowCitizens.java b/src/main/java/edu/whimc/portals/commands/portal/PortalAllowCitizens.java new file mode 100644 index 0000000..27202c1 --- /dev/null +++ b/src/main/java/edu/whimc/portals/commands/portal/PortalAllowCitizens.java @@ -0,0 +1,75 @@ +package edu.whimc.portals.commands.portal; + +import edu.whimc.portals.Main; +import edu.whimc.portals.Portal; +import edu.whimc.portals.commands.AbstractSubCommand; +import edu.whimc.portals.utils.Messenger; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.Material; + +import java.util.List; + +/** + * The command to allow Citizens NPCs to use a portal. + * Command: "/portal allowcitizens" + */ +public class PortalAllowCitizens extends AbstractSubCommand { + /** + * Constructs a PortalAllowCitizens command with the given arguments. + * + * @param plugin the instance of the plugin. + * @param baseCommand the base command keyword. + * @param subCommand the sub command keyword. + */ + public PortalAllowCitizens(Main plugin, String baseCommand, String subCommand) { + super(plugin, baseCommand, subCommand); + super.description("Toggles Citizens NPCs' ability to travel through the portal."); + super.arguments("portal"); + } + + @Override + protected boolean onCommand(CommandSender sender, String[] args) { + + // notify if Citizens plugin is not loaded and abort command execution + if(!plugin.getPluginStatus()) { + Messenger.msg(sender, "Citizens plugin is not loaded"); + return true; + } + + // notify if portal does not exist and abort command execution + Portal portal = Portal.getPortal(args[0]); + if (portal == null) { + Messenger.msg(sender, Messenger.ReplaceMessage.PORTAL_DOES_NOT_EXIST, args[0]); + return true; + } + + // notify if portal is invalid, suggest deletion, and abort command execution + if (!portal.isValid()) { + Messenger.msg(sender, Messenger.ReplaceMessage.PORTAL_INVALID, portal.getName()); + Messenger.msg(sender, Messenger.ReplaceMessage.SUGGEST_DELETE, "/portal remove " + portal.getName()); + return true; + } + + portal.setAllowCitizens(!portal.getAllowCitizens()); + Messenger.msg(sender, portal.getName() + " allows citizens: " + portal.getAllowCitizens()); + + // Notify consequences of water/lava filler + if (portal.getFiller() == Material.WATER) { + Messenger.msg(sender, Messenger.ReplaceMessage.WATER_FILLER); + } + if (portal.getFiller() == Material.LAVA) { + Messenger.msg(sender, Messenger.ReplaceMessage.LAVA_FILLER); + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected List onTabComplete(CommandSender sender, String[] args) { + return Portal.getTabCompletedPortals(args[0]); + } +} diff --git a/src/main/java/edu/whimc/portals/commands/portal/PortalCommand.java b/src/main/java/edu/whimc/portals/commands/portal/PortalCommand.java index 5a37cf5..eade3df 100644 --- a/src/main/java/edu/whimc/portals/commands/portal/PortalCommand.java +++ b/src/main/java/edu/whimc/portals/commands/portal/PortalCommand.java @@ -49,6 +49,7 @@ public PortalCommand(Main plugin) { subCommands.put("setfiller", new PortalSetFiller(plugin, "portal", "setfiller")); subCommands.put("teleport", new PortalTeleport(plugin, "portal", "teleport")); subCommands.put("tool", new PortalTool(plugin, "portal", "tool")); + subCommands.put("allowcitizens", new PortalAllowCitizens(plugin, "portal", "allowcitizens")); } /** @@ -57,7 +58,7 @@ public PortalCommand(Main plugin) { @Override public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { // send command usages to user if no arguments provided - if (args.length == 0){ + if (args.length == 0) { sendCommands(sender); return true; } diff --git a/src/main/java/edu/whimc/portals/commands/portal/PortalSetFiller.java b/src/main/java/edu/whimc/portals/commands/portal/PortalSetFiller.java index f937284..6036db3 100644 --- a/src/main/java/edu/whimc/portals/commands/portal/PortalSetFiller.java +++ b/src/main/java/edu/whimc/portals/commands/portal/PortalSetFiller.java @@ -67,6 +67,17 @@ protected boolean onCommand(CommandSender sender, String[] args) { // set the filler block of the portal portal.setFiller(mat); Messenger.msg(sender, ReplaceMessage.PORTAL_FILLER_SET, portal.getName(), mat.toString()); + + // if portal allows citizens, notify consequences of water/lava filler + if (portal.getAllowCitizens()) { + if (portal.getFiller() == Material.WATER) { + Messenger.msg(sender, ReplaceMessage.WATER_FILLER); + } + if (portal.getFiller() == Material.LAVA) { + Messenger.msg(sender, ReplaceMessage.LAVA_FILLER); + } + } + return true; } diff --git a/src/main/java/edu/whimc/portals/listeners/PortalEnterCitizensListener.java b/src/main/java/edu/whimc/portals/listeners/PortalEnterCitizensListener.java new file mode 100644 index 0000000..a6e9195 --- /dev/null +++ b/src/main/java/edu/whimc/portals/listeners/PortalEnterCitizensListener.java @@ -0,0 +1,46 @@ +package edu.whimc.portals.listeners; + +import edu.whimc.portals.Destination; +import edu.whimc.portals.Portal; +import net.citizensnpcs.api.ai.event.NavigationCompleteEvent; +import net.citizensnpcs.api.npc.NPC; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; + +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerTeleportEvent; + +/** + * Listens for events where the portal is entered by a Citizen. + */ +public class PortalEnterCitizensListener implements Listener { + /** + * Teleport Citizen if its navigation ends in a portal. + * + * @param event The Citizens NavigationCompleteEvent + */ + + public void onCitizenEndNavigation(NavigationCompleteEvent event) { + // get NPC information + NPC npc = event.getNPC(); + Location loc = npc.getStoredLocation(); + + // TODO: add console logging to errors + + // check valid portal + Portal portal = Portal.getPortal(loc); + if (portal == null) { return; } + if (!portal.hasDestination()) { return; } + + // check valid destination + Destination dest = portal.getDestination(); + if (!dest.isValid()) { return; } + + // Do nothing if portal does not allow citizens + if (!portal.getAllowCitizens()) { return; } + + // teleport citizen + npc.teleport(dest.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + } +} diff --git a/src/main/java/edu/whimc/portals/utils/Messenger.java b/src/main/java/edu/whimc/portals/utils/Messenger.java index e259c48..85b5286 100644 --- a/src/main/java/edu/whimc/portals/utils/Messenger.java +++ b/src/main/java/edu/whimc/portals/utils/Messenger.java @@ -241,7 +241,9 @@ public enum ReplaceMessage { INVALID_FILLER(prefix + "&c'&4%s&c' is an invalid filler type!&r\n &7(Valid fillers: %s)"), SUGGEST_DELETE(" (&7You may want to delete it with \"&o%s&7\")"), MISSING_ARGUMENTS(prefix + "&cMissing argument(s): %s"), - UNKNOWN_ARGUMENT(prefix + "&cUnknown argument: &4%s"); + UNKNOWN_ARGUMENT(prefix + "&cUnknown argument: &4%s"), + WATER_FILLER(prefix + "Citizens will swim up water filled portals"), + LAVA_FILLER(prefix + "Citizens will not go through lava filled portals"); /* The message text. */ private String message; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a8fcf3e..7d22b7d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,7 +5,7 @@ author: Jack Henhapl api-version: 1.14 main: edu.whimc.portals.Main load: POSTWORLD -softdepend: [Multiverse-Core] +softdepend: [Multiverse-Core, Citizens] commands: portal: description: Root command for WHIMC Portals