From ffaf616b1be639c5a81f50e4f671b849c2493b55 Mon Sep 17 00:00:00 2001 From: fullwall Date: Fri, 28 Sep 2012 17:56:16 +0800 Subject: [PATCH] Fix some bugs with waypoints, add waypoint markers --- .../command/command/NPCCommands.java | 2 +- .../npc/ai/CitizensNavigator.java | 18 ++-- .../waypoint/EntityEnderCrystalMarker.java | 20 ++++ .../waypoint/LinearWaypointProvider.java | 96 +++++++++++++++++-- src/main/java/net/citizensnpcs/util/NMS.java | 32 +++++++ 5 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 src/main/java/net/citizensnpcs/trait/waypoint/EntityEnderCrystalMarker.java diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java index 92ffe161e..c9ceda303 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -417,7 +417,7 @@ public class NPCCommands { } @Command(aliases = { "npc" }, desc = "Show basic NPC information", max = 0) - public void npc(CommandContext args, CommandSender sender, NPC npc) { + public void npc(CommandContext args, CommandSender sender, final NPC npc) { Messaging.send(sender, StringHelper.wrapHeader(npc.getName())); Messaging.send(sender, " ID: " + npc.getId()); Messaging.send(sender, " Type: " + npc.getTrait(MobType.class).getType()); diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 741079310..a52cfcf8b 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -149,15 +149,13 @@ public class CitizensNavigator implements Navigator { private void stopNavigating(CancelReason reason) { if (!isNavigating()) return; - if (reason == CancelReason.STUCK) { + if (reason == CancelReason.STUCK && localParams.stuckAction() != null) { StuckAction action = localParams.stuckAction(); - if (action != null) { - boolean shouldContinue = action.run(npc, this); - if (shouldContinue) { - stationaryTicks = 0; - executing.clearCancelReason(); - return; - } + boolean shouldContinue = action.run(npc, this); + if (shouldContinue) { + stationaryTicks = 0; + executing.clearCancelReason(); + return; } } NavigationCancelEvent event = new NavigationCancelEvent(this, reason); @@ -188,9 +186,9 @@ public class CitizensNavigator implements Navigator { boolean finished = executing.update(); if (!finished) return; - if (executing.getCancelReason() != null) + if (executing.getCancelReason() != null) { stopNavigating(executing.getCancelReason()); - else { + } else { NavigationCompleteEvent event = new NavigationCompleteEvent(this); PathStrategy old = executing; Bukkit.getPluginManager().callEvent(event); diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/EntityEnderCrystalMarker.java b/src/main/java/net/citizensnpcs/trait/waypoint/EntityEnderCrystalMarker.java new file mode 100644 index 000000000..d3d965ae1 --- /dev/null +++ b/src/main/java/net/citizensnpcs/trait/waypoint/EntityEnderCrystalMarker.java @@ -0,0 +1,20 @@ +package net.citizensnpcs.trait.waypoint; + +import net.minecraft.server.DamageSource; +import net.minecraft.server.EntityEnderCrystal; +import net.minecraft.server.World; + +public class EntityEnderCrystalMarker extends EntityEnderCrystal { + public EntityEnderCrystalMarker(World world) { + super(world); + } + + @Override + public void h_() { + } + + @Override + public boolean damageEntity(DamageSource damagesource, int i) { + return false; + } +} diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index 26c9d66e0..4e1ae9698 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -2,6 +2,7 @@ package net.citizensnpcs.trait.waypoint; import java.util.Iterator; import java.util.List; +import java.util.Map; import javax.annotation.Nullable; @@ -16,20 +17,26 @@ import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; import net.citizensnpcs.util.Messaging; +import net.citizensnpcs.util.NMS; import net.citizensnpcs.util.StringHelper; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; +import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import com.google.common.base.Function; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; public class LinearWaypointProvider implements WaypointProvider { private LinearWaypointGoal currentGoal; @@ -41,19 +48,32 @@ public class LinearWaypointProvider implements WaypointProvider { return new Editor() { boolean editing = true; int editingSlot = waypoints.size() - 1; + Map waypointEntities = Maps.newHashMap(); + private boolean showPath; @Override public void begin() { - player.sendMessage(ChatColor.AQUA + "Entered the linear waypoint editor!"); + Messaging.send(player, ChatColor.AQUA + "Entered the linear waypoint editor!"); Messaging.send(player, "Left click to add a waypoint, right click to remove."); + Messaging.send(player, "Type toggle path to toggle showing entities at waypoints."); + } + + private void createWaypointMarker(Waypoint waypoint) { + Entity entity = spawnMarker(player.getWorld(), waypoint.getLocation().add(0, 1, 0)); + if (entity == null) + return; + waypointEntities.put(waypoint, entity); } @Override public void end() { if (!editing) return; - player.sendMessage(ChatColor.AQUA + "Exited the linear waypoint editor."); + Messaging.send(player, ChatColor.AQUA + "Exited the linear waypoint editor."); editing = false; + if (!showPath) + return; + destroyWaypointMarkers(); } private String formatLoc(Location location) { @@ -82,6 +102,44 @@ public class LinearWaypointProvider implements WaypointProvider { Editor.leave(player); } + @EventHandler(ignoreCancelled = true) + public void onPlayerChat(AsyncPlayerChatEvent event) { + if (!event.getPlayer().equals(player)) + return; + if (!event.getMessage().equalsIgnoreCase("toggle path")) + return; + event.setCancelled(true); + // we need to spawn entities, get back on the main thread. + Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() { + @Override + public void run() { + togglePath(); + } + }, 1); + } + + private void togglePath() { + showPath = !showPath; + if (showPath) { + createWaypointMarkers(); + Messaging.sendF(player, "%s waypoint markers.", StringHelper.wrap("Showing")); + } else { + destroyWaypointMarkers(); + Messaging.sendF(player, "%s showing waypoint markers.", StringHelper.wrap("Stopped")); + } + } + + private void createWaypointMarkers() { + for (Waypoint waypoint : waypoints) + createWaypointMarker(waypoint); + } + + private void destroyWaypointMarkers() { + for (Entity entity : waypointEntities.values()) + entity.remove(); + waypointEntities.clear(); + } + @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { if (!event.getPlayer().equals(player) || event.getAction() == Action.PHYSICAL) @@ -98,18 +156,20 @@ public class LinearWaypointProvider implements WaypointProvider { if (prev != null) { double distance = at.distanceSquared(prev); - double maxDistance = npc.getNavigator().getDefaultParameters().range(); - maxDistance = Math.pow(maxDistance, 2); + double maxDistance = Math.pow(npc.getNavigator().getDefaultParameters().range(), 2); if (distance > maxDistance) { Messaging.sendF(player, ChatColor.RED + "Previous waypoint is %s blocks away but the distance limit is %s.", - StringHelper.wrap(distance, ChatColor.RED), - StringHelper.wrap(maxDistance, ChatColor.RED)); + StringHelper.wrap(Math.sqrt(distance), ChatColor.RED), + StringHelper.wrap(Math.sqrt(maxDistance), ChatColor.RED)); return; } } - waypoints.add(Math.max(0, editingSlot), new Waypoint(at)); + Waypoint element = new Waypoint(at); + waypoints.add(Math.max(0, editingSlot), element); + if (showPath) + createWaypointMarker(element); editingSlot = Math.min(editingSlot + 1, waypoints.size()); Messaging.send( player, @@ -118,7 +178,9 @@ public class LinearWaypointProvider implements WaypointProvider { } else if (waypoints.size() > 0) { event.setCancelled(true); editingSlot = Math.min(0, Math.max(waypoints.size() - 1, editingSlot)); - waypoints.remove(editingSlot); + Waypoint waypoint = waypoints.remove(editingSlot); + if (showPath) + removeWaypointMarker(waypoint); editingSlot = Math.max(0, editingSlot - 1); Messaging.send(player, String.format( "Removed a waypoint (%d remaining) (%d)", waypoints.size(), @@ -151,6 +213,17 @@ public class LinearWaypointProvider implements WaypointProvider { + formatLoc(waypoints.get(editingSlot).getLocation()) + ")."); } + private void removeWaypointMarker(Waypoint waypoint) { + Entity entity = waypointEntities.remove(waypoint); + if (entity != null) + entity.remove(); + } + + private Entity spawnMarker(World world, Location at) { + return NMS.spawnCustomEntity(world, at, EntityEnderCrystalMarker.class, + EntityType.ENDER_CRYSTAL); + } + private static final int LARGEST_SLOT = 8; }; } @@ -224,7 +297,7 @@ public class LinearWaypointProvider implements WaypointProvider { @EventHandler public void onNavigationComplete(NavigationCompleteEvent event) { - if (currentDestination == null || !event.getNavigator().equals(getNavigator())) + if (selector == null || !event.getNavigator().equals(getNavigator())) return; selector.finish(); } @@ -243,6 +316,8 @@ public class LinearWaypointProvider implements WaypointProvider { @Override public void run(GoalSelector selector) { + if (!getNavigator().isNavigating()) + selector.finish(); } public void setPaused(boolean pause) { @@ -254,8 +329,9 @@ public class LinearWaypointProvider implements WaypointProvider { @Override public boolean shouldExecute(GoalSelector selector) { if (paused || currentDestination != null || !npc.isSpawned() || getNavigator().isNavigating() - || waypoints.size() == 0) + || waypoints.size() == 0) { return false; + } if (waypoints.size() == 1) { // avoid pathing to the same point and wasting memory. Location dest = npc.getBukkitEntity().getLocation(); diff --git a/src/main/java/net/citizensnpcs/util/NMS.java b/src/main/java/net/citizensnpcs/util/NMS.java index 8da9bde2d..f25a5b1fe 100644 --- a/src/main/java/net/citizensnpcs/util/NMS.java +++ b/src/main/java/net/citizensnpcs/util/NMS.java @@ -1,8 +1,10 @@ package net.citizensnpcs.util; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; import net.citizensnpcs.npc.CitizensNPC; import net.minecraft.server.ControllerLook; @@ -14,6 +16,7 @@ import net.minecraft.server.NetworkManager; import net.minecraft.server.PathfinderGoalSelector; import net.minecraft.server.World; +import org.bukkit.Location; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.entity.EntityType; @@ -28,6 +31,7 @@ public class NMS { } private static final float DEFAULT_SPEED = 0.4F; + private static final Map, Constructor> ENTITY_CONSTRUCTOR_CACHE = new WeakHashMap, Constructor>(); private static Map, Integer> ENTITY_CLASS_TO_INT; private static Map> ENTITY_INT_TO_CLASS; private static Field GOAL_FIELD; @@ -183,4 +187,32 @@ public class NMS { } catch (Exception e) { } } + + public static org.bukkit.entity.Entity spawnCustomEntity(org.bukkit.World world, Location at, + Class clazz, EntityType type) { + World handle = ((CraftWorld) world).getHandle(); + Entity entity = null; + try { + Constructor constructor = getCustomEntityConstructor(clazz, type); + entity = constructor.newInstance(handle); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + handle.addEntity(entity); + entity.setLocation(at.getX(), at.getY(), at.getZ(), at.getYaw(), at.getPitch()); + return entity.getBukkitEntity(); + } + + private static Constructor getCustomEntityConstructor(Class clazz, + EntityType type) throws SecurityException, NoSuchMethodException { + Constructor constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz); + if (constructor == null) { + constructor = clazz.getConstructor(World.class); + constructor.setAccessible(true); + ENTITY_CLASS_TO_INT.put(clazz, (int) type.getTypeId()); + ENTITY_CONSTRUCTOR_CACHE.put(clazz, constructor); + } + return constructor; + } }