diff --git a/src/main/java/net/citizensnpcs/EventListen.java b/src/main/java/net/citizensnpcs/EventListen.java index 7f22de6d1..930bf4904 100644 --- a/src/main/java/net/citizensnpcs/EventListen.java +++ b/src/main/java/net/citizensnpcs/EventListen.java @@ -62,7 +62,7 @@ public class EventListen implements Listener { private final ListMultimap toRespawn = ArrayListMultimap.create(); public EventListen() { - instance = this; + instance = this; // TODO: remove singleton } /* diff --git a/src/main/java/net/citizensnpcs/Settings.java b/src/main/java/net/citizensnpcs/Settings.java index 0b7afbb4d..4377ca9ad 100644 --- a/src/main/java/net/citizensnpcs/Settings.java +++ b/src/main/java/net/citizensnpcs/Settings.java @@ -85,7 +85,8 @@ public class Settings { 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"); + TALK_ITEM("npc.text.talk-item", "340"), + USE_NEW_PATHFINDER("npc.pathfinding.use-new-finder", false); protected String path; protected Object value; diff --git a/src/main/java/net/citizensnpcs/command/CommandManager.java b/src/main/java/net/citizensnpcs/command/CommandManager.java index 32c4b9a5c..8208af94e 100644 --- a/src/main/java/net/citizensnpcs/command/CommandManager.java +++ b/src/main/java/net/citizensnpcs/command/CommandManager.java @@ -223,14 +223,12 @@ public class CommandManager { // Requirements if (cmdRequirements.selected()) { - boolean canRedefineSelected = context.hasValueFlag("id") - && sender.hasPermission("npc.select"); + boolean canRedefineSelected = context.hasValueFlag("id") && sender.hasPermission("npc.select"); String error = Messaging.tr(Messages.COMMAND_MUST_HAVE_SELECTED); if (canRedefineSelected) { npc = CitizensAPI.getNPCRegistry().getById(context.getFlagInteger("id")); if (npc == null) - error += ' ' + Messaging.tr(Messages.COMMAND_ID_NOT_FOUND, - context.getFlagInteger("id")); + error += ' ' + Messaging.tr(Messages.COMMAND_ID_NOT_FOUND, context.getFlagInteger("id")); } if (npc == null) throw new RequirementMissingException(error); @@ -249,8 +247,7 @@ public class CommandManager { } if (npc != null) { - Set types = Sets.newEnumSet(Arrays.asList(cmdRequirements.types()), - EntityType.class); + Set types = Sets.newEnumSet(Arrays.asList(cmdRequirements.types()), EntityType.class); if (types.contains(EntityType.UNKNOWN)) types = EnumSet.allOf(EntityType.class); types.removeAll(Sets.newHashSet(cmdRequirements.excludedTypes())); diff --git a/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java new file mode 100644 index 000000000..5ec321f3a --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java @@ -0,0 +1,65 @@ +package net.citizensnpcs.npc.ai; + +import net.citizensnpcs.api.ai.NavigatorParameters; +import net.citizensnpcs.api.ai.TargetType; +import net.citizensnpcs.api.ai.event.CancelReason; +import net.citizensnpcs.api.astar.AStarMachine; +import net.citizensnpcs.api.astar.pathfinder.ChunkBlockSource; +import net.citizensnpcs.api.astar.pathfinder.Path; +import net.citizensnpcs.api.astar.pathfinder.VectorGoal; +import net.citizensnpcs.api.astar.pathfinder.VectorNode; +import net.citizensnpcs.npc.CitizensNPC; +import net.citizensnpcs.util.NMS; + +import org.bukkit.Location; +import org.bukkit.util.Vector; + +public class AStarNavigationStrategy extends AbstractPathStrategy { + private final Location dest; + private final CitizensNPC npc; + private final NavigatorParameters params; + private Path plan; + private Vector vector; + + AStarNavigationStrategy(CitizensNPC npc, Location dest, NavigatorParameters params) { + super(TargetType.LOCATION); + this.params = params; + this.dest = dest; + this.npc = npc; + Location location = npc.getBukkitEntity().getLocation(); + plan = (Path) ASTAR.runFully(new VectorGoal(dest), new VectorNode(location, new ChunkBlockSource( + location, params.range()), params.examiners()), 10000); + if (plan == null || plan.isComplete()) + setCancelReason(CancelReason.STUCK); + else + vector = plan.getCurrentVector(); + } + + @Override + public Location getTargetAsLocation() { + return dest; + } + + @Override + public void stop() { + plan = null; + } + + @Override + public boolean update() { + if (getCancelReason() != null) + return true; + if (plan.isComplete()) + return true; + if (NMS.distanceSquared(npc.getHandle(), vector) <= params.distanceMargin()) { + plan.update(npc); + if (plan.isComplete()) + return true; + vector = plan.getCurrentVector(); + } + npc.getHandle().getControllerMove().a(vector.getX(), vector.getY(), vector.getZ(), params.speed()); + return false; + } + + private static final AStarMachine ASTAR = AStarMachine.createWithDefaultStorage(); +} diff --git a/src/main/java/net/citizensnpcs/npc/ai/AbstractPathStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/AbstractPathStrategy.java new file mode 100644 index 000000000..378017260 --- /dev/null +++ b/src/main/java/net/citizensnpcs/npc/ai/AbstractPathStrategy.java @@ -0,0 +1,32 @@ +package net.citizensnpcs.npc.ai; + +import net.citizensnpcs.api.ai.TargetType; +import net.citizensnpcs.api.ai.event.CancelReason; + +public abstract class AbstractPathStrategy implements PathStrategy { + private CancelReason cancelReason; + private final TargetType type; + + protected AbstractPathStrategy(TargetType type) { + this.type = type; + } + + @Override + public void clearCancelReason() { + cancelReason = null; + } + + @Override + public CancelReason getCancelReason() { + return cancelReason; + } + + @Override + public TargetType getTargetType() { + return type; + } + + protected void setCancelReason(CancelReason reason) { + cancelReason = reason; + } +} diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 30c791b4f..db5a02104 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -12,6 +12,7 @@ import net.citizensnpcs.api.ai.event.NavigationBeginEvent; import net.citizensnpcs.api.ai.event.NavigationCancelEvent; import net.citizensnpcs.api.ai.event.NavigationCompleteEvent; import net.citizensnpcs.api.ai.event.NavigationReplaceEvent; +import net.citizensnpcs.api.astar.pathfinder.MinecraftBlockExaminer; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.npc.CitizensNPC; @@ -26,7 +27,7 @@ public class CitizensNavigator implements Navigator { private final NavigatorParameters defaultParams = new NavigatorParameters() .baseSpeed(UNINITIALISED_SPEED).range(Setting.DEFAULT_PATHFINDING_RANGE.asFloat()) .stationaryTicks(Setting.DEFAULT_STATIONARY_TICKS.asInt()) - .stuckAction(TeleportStuckAction.INSTANCE); + .stuckAction(TeleportStuckAction.INSTANCE).examiner(new MinecraftBlockExaminer()); private PathStrategy executing; private int lastX, lastY, lastZ; private NavigatorParameters localParams = defaultParams; @@ -136,7 +137,11 @@ public class CitizensNavigator implements Navigator { return; } localParams = defaultParams.clone(); - PathStrategy newStrategy = new MCNavigationStrategy(npc, target, localParams); + PathStrategy newStrategy; + if (Setting.USE_NEW_PATHFINDER.asBoolean()) + newStrategy = new AStarNavigationStrategy(npc, target, localParams); + else + newStrategy = new MCNavigationStrategy(npc, target, localParams); switchStrategyTo(newStrategy); } diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java index 2efa5493f..3c4ab6299 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java @@ -9,13 +9,13 @@ import net.minecraft.server.Navigation; import org.bukkit.Location; -public class MCNavigationStrategy implements PathStrategy { - private CancelReason cancelReason; +public class MCNavigationStrategy extends AbstractPathStrategy { private final Navigation navigation; private final NavigatorParameters parameters; private final Location target; MCNavigationStrategy(final CitizensNPC npc, Location dest, NavigatorParameters params) { + super(TargetType.LOCATION); this.target = dest; this.parameters = params; if (npc.getHandle() instanceof EntityPlayer) { @@ -28,17 +28,7 @@ public class MCNavigationStrategy implements PathStrategy { navigation.a(parameters.avoidWater()); navigation.a(dest.getX(), dest.getY(), dest.getZ(), parameters.speed()); if (navigation.f()) - cancelReason = CancelReason.STUCK; - } - - @Override - public void clearCancelReason() { - cancelReason = null; - } - - @Override - public CancelReason getCancelReason() { - return cancelReason; + setCancelReason(CancelReason.STUCK); } @Override @@ -58,7 +48,7 @@ public class MCNavigationStrategy implements PathStrategy { @Override public boolean update() { - if (cancelReason != null) + if (getCancelReason() != null) return true; navigation.a(parameters.avoidWater()); navigation.a(parameters.speed()); diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java index f2ba38314..919e5a4de 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensMagmaCubeNPC.java @@ -45,7 +45,6 @@ public class CitizensMagmaCubeNPC extends CitizensMobNPC { } } - @Override public void bl() { super.bl(); diff --git a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java index 2a638ad47..f888a8680 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java @@ -131,8 +131,9 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder { NMS.updateSenses(this); Navigation navigation = getNavigation(); - if (!navigation.f()) { - navigation.e(); + if (npc.getNavigator().isNavigating()) { + if (!navigation.f()) + navigation.e(); moveOnCurrentHeading(); } else if (motX != 0 || motZ != 0 || motY != 0) e(0, 0); // is this necessary? it does controllable but sometimes diff --git a/src/main/java/net/citizensnpcs/trait/CurrentLocation.java b/src/main/java/net/citizensnpcs/trait/CurrentLocation.java index 290d1b35b..f3b1a2dd6 100644 --- a/src/main/java/net/citizensnpcs/trait/CurrentLocation.java +++ b/src/main/java/net/citizensnpcs/trait/CurrentLocation.java @@ -14,7 +14,7 @@ public class CurrentLocation extends Trait { } public Location getLocation() { - if (loc.getWorld() == null) + if (loc != null && loc.getWorld() == null) return null; return loc; } diff --git a/src/main/java/net/citizensnpcs/trait/Gravity.java b/src/main/java/net/citizensnpcs/trait/Gravity.java index fd31b47ee..72a25b735 100644 --- a/src/main/java/net/citizensnpcs/trait/Gravity.java +++ b/src/main/java/net/citizensnpcs/trait/Gravity.java @@ -14,9 +14,9 @@ public class Gravity extends Trait implements Toggleable { } public void gravitate(boolean gravitate) { - enabled = gravitate; + enabled = gravitate; } - + @Override public void run() { if (!npc.isSpawned() || !enabled) diff --git a/src/main/java/net/citizensnpcs/trait/LookClose.java b/src/main/java/net/citizensnpcs/trait/LookClose.java index fcef4ab9a..e27adfa9f 100644 --- a/src/main/java/net/citizensnpcs/trait/LookClose.java +++ b/src/main/java/net/citizensnpcs/trait/LookClose.java @@ -118,7 +118,7 @@ public class LookClose extends Trait implements Toggleable, CommandConfigurable enabled = !enabled; return enabled; } - + @Override public String toString() { return "LookClose{" + enabled + "}"; diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index e704b0c81..7151f099b 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -107,6 +107,7 @@ public class LinearWaypointProvider implements WaypointProvider { private void clearWaypoints() { editingSlot = 0; waypoints.clear(); + onWaypointsModified(); destroyWaypointMarkers(); Messaging.sendTr(player, Messages.LINEAR_WAYPOINT_EDITOR_WAYPOINTS_CLEARED); } @@ -185,7 +186,8 @@ public class LinearWaypointProvider implements WaypointProvider { public void onPlayerChat(AsyncPlayerChatEvent event) { if (!event.getPlayer().equals(player)) return; - if (event.getMessage().equalsIgnoreCase("triggers")) { + String message = event.getMessage(); + if (message.equalsIgnoreCase("triggers")) { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { @Override public void run() { @@ -193,8 +195,7 @@ public class LinearWaypointProvider implements WaypointProvider { } }); return; - } - if (event.getMessage().equalsIgnoreCase("clear")) { + } else if (message.equalsIgnoreCase("clear")) { Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { @Override public void run() { @@ -202,17 +203,17 @@ public class LinearWaypointProvider implements WaypointProvider { } }); return; - } - if (!event.getMessage().equalsIgnoreCase("toggle path")) + } else if (message.equalsIgnoreCase("toggle path")) { + event.setCancelled(true); + Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { + @Override + public void run() { + // we need to spawn entities on the main thread. + togglePath(); + } + }); return; - event.setCancelled(true); - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { - @Override - public void run() { - // we need to spawn entities, get back on the main thread. - togglePath(); - } - }); + } } @EventHandler(ignoreCancelled = true) @@ -256,7 +257,7 @@ public class LinearWaypointProvider implements WaypointProvider { Messaging.sendTr(player, Messages.LINEAR_WAYPOINT_EDITOR_REMOVED_WAYPOINT, waypoints.size(), editingSlot + 1); } - currentGoal.onProviderChanged(); + onWaypointsModified(); } @EventHandler(ignoreCancelled = true) @@ -291,6 +292,11 @@ public class LinearWaypointProvider implements WaypointProvider { formatLoc(waypoints.get(editingSlot).getLocation())); } + private void onWaypointsModified() { + if (currentGoal != null) + currentGoal.onProviderChanged(); + } + private void removeWaypointMarker(Waypoint waypoint) { Entity entity = waypointMarkers.remove(waypoint); if (entity != null) @@ -380,7 +386,7 @@ public class LinearWaypointProvider implements WaypointProvider { public void setPaused(boolean pause) { if (pause && currentDestination != null) selector.finish(); - this.paused = pause; + paused = pause; } @Override diff --git a/src/main/java/net/citizensnpcs/util/Anchor.java b/src/main/java/net/citizensnpcs/util/Anchor.java index 0ffb5b7da..19e2039dc 100644 --- a/src/main/java/net/citizensnpcs/util/Anchor.java +++ b/src/main/java/net/citizensnpcs/util/Anchor.java @@ -9,49 +9,49 @@ import org.bukkit.Location; */ public class Anchor { - private final Location location; - private final String name; + private final Location location; + private final String name; - public Anchor(String name, Location location) { - this.location = location; - this.name = name; - } - - @Override - public boolean equals(Object object) { - if (object == null) return false; - if (object == this) return true; - if (object.getClass() != getClass()) - return false; - - Anchor op = (Anchor) object; - return new EqualsBuilder(). - append(name, op.getName()). - isEquals(); - } - - public Location getLocation() { - return location; - } - - public String getName() { - return name; - } - - @Override - public int hashCode() { - return new HashCodeBuilder(13, 21). - append(name). - toHashCode(); + public Anchor(String name, Location location) { + this.location = location; + this.name = name; } - public String stringValue() { - return name + ";" + location.getWorld().getName() + ";" + location.getX() + ";" + location.getY() + ";" + location.getZ(); - } - - @Override - public String toString() { - return "Name: " + name + " World: " + location.getWorld().getName() + " Location: " + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); - } + @Override + public boolean equals(Object object) { + if (object == null) + return false; + if (object == this) + return true; + if (object.getClass() != getClass()) + return false; + + Anchor op = (Anchor) object; + return new EqualsBuilder().append(name, op.getName()).isEquals(); + } + + public Location getLocation() { + return location; + } + + public String getName() { + return name; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(13, 21).append(name).toHashCode(); + } + + public String stringValue() { + return name + ";" + location.getWorld().getName() + ";" + location.getX() + ";" + location.getY() + + ";" + location.getZ(); + } + + @Override + public String toString() { + return "Name: " + name + " World: " + location.getWorld().getName() + " Location: " + + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/util/NMS.java b/src/main/java/net/citizensnpcs/util/NMS.java index b63cf2abb..cbc59c2ad 100644 --- a/src/main/java/net/citizensnpcs/util/NMS.java +++ b/src/main/java/net/citizensnpcs/util/NMS.java @@ -33,6 +33,7 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.material.Stairs; import org.bukkit.material.Step; +import org.bukkit.util.Vector; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -120,6 +121,15 @@ public class NMS { } } + public static double distance(EntityLiving handle, Vector vector) { + return Math.sqrt(distanceSquared(handle, vector)); + } + + public static double distanceSquared(EntityLiving handle, Vector vector) { + return Math.pow(handle.locX - vector.getX(), 2) + Math.pow(handle.locY - vector.getY(), 2) + + Math.pow(handle.locZ - vector.getZ(), 2); + } + private static Constructor getCustomEntityConstructor(Class clazz, EntityType type) throws SecurityException, NoSuchMethodException { Constructor constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz); diff --git a/src/main/java/net/citizensnpcs/util/Pose.java b/src/main/java/net/citizensnpcs/util/Pose.java index c077786d6..af7c75a98 100644 --- a/src/main/java/net/citizensnpcs/util/Pose.java +++ b/src/main/java/net/citizensnpcs/util/Pose.java @@ -8,55 +8,53 @@ import org.apache.commons.lang.builder.HashCodeBuilder; */ public class Pose { - private final String name; - private final float pitch; - private final float yaw; + private final String name; + private final float pitch; + private final float yaw; - public Pose(String name, float pitch, float yaw) { - this.yaw = yaw; - this.pitch = pitch; - this.name = name; - } - - @Override - public boolean equals(Object object) { - if (object == null) return false; - if (object == this) return true; - if (object.getClass() != getClass()) - return false; - - Pose op = (Pose) object; - return new EqualsBuilder(). - append(name, op.getName()). - isEquals(); - } - - public String getName() { - return name; - } - - public float getPitch() { - return pitch; - } - - public float getYaw() { - return yaw; - } - - @Override - public int hashCode() { - return new HashCodeBuilder(13, 21). - append(name). - toHashCode(); + public Pose(String name, float pitch, float yaw) { + this.yaw = yaw; + this.pitch = pitch; + this.name = name; } - public String stringValue() { - return name + ";" + pitch + ";" + yaw; - } - - @Override - public String toString() { - return "Name: " + name + " Pitch: " + pitch + " Yaw: " + yaw; - } + @Override + public boolean equals(Object object) { + if (object == null) + return false; + if (object == this) + return true; + if (object.getClass() != getClass()) + return false; + + Pose op = (Pose) object; + return new EqualsBuilder().append(name, op.getName()).isEquals(); + } + + public String getName() { + return name; + } + + public float getPitch() { + return pitch; + } + + public float getYaw() { + return yaw; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(13, 21).append(name).toHashCode(); + } + + public String stringValue() { + return name + ";" + pitch + ";" + yaw; + } + + @Override + public String toString() { + return "Name: " + name + " Pitch: " + pitch + " Yaw: " + yaw; + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/util/StringHelper.java b/src/main/java/net/citizensnpcs/util/StringHelper.java index 4046ffbe2..b723041fb 100644 --- a/src/main/java/net/citizensnpcs/util/StringHelper.java +++ b/src/main/java/net/citizensnpcs/util/StringHelper.java @@ -84,6 +84,7 @@ public class StringHelper { String highlight = Setting.HIGHLIGHT_COLOUR.asString(); return highlight + "=====[ " + string.toString() + highlight + " ]====="; } + static { String colors = ""; for (ChatColor color : ChatColor.values())