diff --git a/src/main/java/net/citizensnpcs/api/ai/NavigatorParameters.java b/src/main/java/net/citizensnpcs/api/ai/NavigatorParameters.java index e93aa5d3..99be1275 100644 --- a/src/main/java/net/citizensnpcs/api/ai/NavigatorParameters.java +++ b/src/main/java/net/citizensnpcs/api/ai/NavigatorParameters.java @@ -32,6 +32,9 @@ public NavigatorParameters addSingleUseCallback(NavigatorCallback callback) { return this; } + public void addSingleUseCallback(Object callback) { + } + /** * @return The {@link AttackStrategy} currently in use (may be null) */ diff --git a/src/main/java/net/citizensnpcs/api/ai/goals/MoveToGoal.java b/src/main/java/net/citizensnpcs/api/ai/goals/MoveToGoal.java new file mode 100644 index 00000000..2c9caff1 --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/ai/goals/MoveToGoal.java @@ -0,0 +1,54 @@ +package net.citizensnpcs.api.ai.goals; + +import javax.annotation.Nullable; + +import net.citizensnpcs.api.ai.event.CancelReason; +import net.citizensnpcs.api.ai.event.NavigatorCallback; +import net.citizensnpcs.api.ai.tree.BehaviorGoalAdapter; +import net.citizensnpcs.api.ai.tree.BehaviorStatus; +import net.citizensnpcs.api.npc.NPC; + +import org.bukkit.Location; + +public class MoveToGoal extends BehaviorGoalAdapter { + private boolean finished; + private final NPC npc; + private CancelReason reason; + private final Location target; + + public MoveToGoal(NPC npc, Location target) { + this.npc = npc; + this.target = target; + } + + @Override + public void reset() { + npc.getNavigator().cancelNavigation(); + reason = null; + finished = false; + } + + @Override + public BehaviorStatus run() { + if (finished) { + return reason == null ? BehaviorStatus.SUCCESS : BehaviorStatus.FAILURE; + } + return BehaviorStatus.RUNNING; + } + + @Override + public boolean shouldExecute() { + boolean executing = !npc.getNavigator().isNavigating() && target != null; + if (executing) { + npc.getNavigator().setTarget(target); + npc.getNavigator().getLocalParameters().addSingleUseCallback(new NavigatorCallback() { + @Override + public void onCompletion(@Nullable CancelReason cancelReason) { + finished = true; + reason = cancelReason; + } + }); + } + return executing; + } +} diff --git a/src/main/java/net/citizensnpcs/api/ai/goals/TargetNearbyEntityGoal.java b/src/main/java/net/citizensnpcs/api/ai/goals/TargetNearbyEntityGoal.java new file mode 100644 index 00000000..422cf83a --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/ai/goals/TargetNearbyEntityGoal.java @@ -0,0 +1,109 @@ +package net.citizensnpcs.api.ai.goals; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.Set; + +import javax.annotation.Nullable; + +import net.citizensnpcs.api.ai.event.CancelReason; +import net.citizensnpcs.api.ai.event.NavigatorCallback; +import net.citizensnpcs.api.ai.tree.BehaviorGoalAdapter; +import net.citizensnpcs.api.ai.tree.BehaviorStatus; +import net.citizensnpcs.api.npc.NPC; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; + +public class TargetNearbyEntityGoal extends BehaviorGoalAdapter { + private final boolean aggressive; + private boolean finished; + private final NPC npc; + private final double radius; + private CancelReason reason; + private Entity target; + private final Set targets; + + private TargetNearbyEntityGoal(NPC npc, Set targets, boolean aggressive, double radius) { + this.npc = npc; + this.targets = targets; + this.aggressive = aggressive; + this.radius = radius; + } + + @Override + public void reset() { + npc.getNavigator().cancelNavigation(); + target = null; + finished = false; + reason = null; + } + + @Override + public BehaviorStatus run() { + if (finished) { + return reason == null ? BehaviorStatus.SUCCESS : BehaviorStatus.FAILURE; + } + return BehaviorStatus.RUNNING; + } + + @Override + public boolean shouldExecute() { + if (targets.size() == 0 || !npc.isSpawned()) + return false; + Collection nearby = npc.getBukkitEntity().getNearbyEntities(radius, radius, radius); + this.target = null; + for (Entity entity : nearby) { + if (targets.contains(entity.getType())) { + target = entity; + break; + } + } + if (target != null) { + npc.getNavigator().setTarget(target, aggressive); + npc.getNavigator().getLocalParameters().addSingleUseCallback(new NavigatorCallback() { + @Override + public void onCompletion(@Nullable CancelReason cancelReason) { + reason = cancelReason; + finished = true; + } + }); + return true; + } + return false; + } + + public static class Builder { + private boolean aggressive; + private final NPC npc; + private double radius = 10D; + private Set targetTypes = EnumSet.noneOf(EntityType.class); + + public Builder(NPC npc) { + this.npc = npc; + } + + public Builder aggressive(boolean aggressive) { + this.aggressive = aggressive; + return this; + } + + public TargetNearbyEntityGoal build() { + return new TargetNearbyEntityGoal(npc, targetTypes, aggressive, radius); + } + + public Builder radius(double radius) { + this.radius = radius; + return this; + } + + public Builder targets(Set targetTypes) { + this.targetTypes = targetTypes; + return this; + } + } + + public static Builder builder(NPC npc) { + return new Builder(npc); + } +} diff --git a/src/main/java/net/citizensnpcs/api/astar/pathfinder/MinecraftBlockExaminer.java b/src/main/java/net/citizensnpcs/api/astar/pathfinder/MinecraftBlockExaminer.java index 7df8fbd2..e510e154 100644 --- a/src/main/java/net/citizensnpcs/api/astar/pathfinder/MinecraftBlockExaminer.java +++ b/src/main/java/net/citizensnpcs/api/astar/pathfinder/MinecraftBlockExaminer.java @@ -4,17 +4,11 @@ import java.util.Set; import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; public class MinecraftBlockExaminer implements BlockExaminer { - private boolean canStandIn(Material mat) { - return PASSABLE.contains(mat); - } - - private boolean canStandOn(Material mat) { - return !UNWALKABLE.contains(mat) && !PASSABLE.contains(mat); - } - private boolean contains(Material[] search, Material... find) { for (Material haystack : search) { for (Material needle : find) @@ -59,6 +53,7 @@ public boolean isPassable(BlockSource source, PathPoint point) { } private static final Vector DOWN = new Vector(0, -1, 0); + private static final Set PASSABLE = EnumSet.of(Material.AIR, Material.DEAD_BUSH, Material.DETECTOR_RAIL, Material.DIODE, Material.DIODE_BLOCK_OFF, Material.DIODE_BLOCK_ON, Material.FENCE_GATE, Material.ITEM_FRAME, Material.LADDER, Material.LEVER, Material.LONG_GRASS, Material.MELON_STEM, @@ -68,7 +63,22 @@ public boolean isPassable(BlockSource source, PathPoint point) { Material.STONE_BUTTON, Material.SUGAR_CANE_BLOCK, Material.TRIPWIRE, Material.VINE, Material.WALL_SIGN, Material.WHEAT, Material.WATER, Material.WEB, Material.WOOD_BUTTON, Material.WOODEN_DOOR, Material.STATIONARY_WATER); + private static final Set UNWALKABLE = EnumSet.of(Material.AIR, Material.LAVA, Material.STATIONARY_LAVA, Material.CACTUS); private static final Vector UP = new Vector(0, 1, 0); + + public static boolean canStandIn(Material mat) { + return PASSABLE.contains(mat); + } + + public static boolean canStandOn(Block block) { + Block up = block.getRelative(BlockFace.UP); + return canStandOn(block.getType()) && canStandIn(up.getType()) + && canStandIn(up.getRelative(BlockFace.UP).getType()); + } + + public static boolean canStandOn(Material mat) { + return !UNWALKABLE.contains(mat) && !PASSABLE.contains(mat); + } } diff --git a/src/main/java/net/citizensnpcs/api/npc/NPC.java b/src/main/java/net/citizensnpcs/api/npc/NPC.java index b0aaeaad..f5111b73 100644 --- a/src/main/java/net/citizensnpcs/api/npc/NPC.java +++ b/src/main/java/net/citizensnpcs/api/npc/NPC.java @@ -63,7 +63,8 @@ public interface NPC extends Agent { public void destroy(); /** - * Gets the Bukkit entity associated with this NPC. + * Gets the Bukkit entity associated with this NPC. This may be + * null if {@link #isSpawned()} is false. * * @return Entity associated with this NPC */ diff --git a/src/main/java/net/citizensnpcs/api/util/FileStorage.java b/src/main/java/net/citizensnpcs/api/util/FileStorage.java new file mode 100644 index 00000000..cbe7a6cc --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/util/FileStorage.java @@ -0,0 +1,7 @@ +package net.citizensnpcs.api.util; + +import java.io.File; + +public interface FileStorage extends Storage { + File getFile(); +} diff --git a/src/main/java/net/citizensnpcs/api/util/Messaging.java b/src/main/java/net/citizensnpcs/api/util/Messaging.java index 0ea58b9d..ac2c0ebb 100644 --- a/src/main/java/net/citizensnpcs/api/util/Messaging.java +++ b/src/main/java/net/citizensnpcs/api/util/Messaging.java @@ -80,6 +80,11 @@ public static void sendErrorTr(CommandSender sender, String key, Object... msg) } private static void sendMessageTo(CommandSender sender, String rawMessage) { + if (sender instanceof Player) { + Player player = (Player) sender; + rawMessage = rawMessage.replace("", player.getName()); + rawMessage = rawMessage.replace("", player.getWorld().getName()); + } rawMessage = Colorizer.parseColors(rawMessage); for (String message : CHAT_NEWLINE_SPLITTER.split(rawMessage)) { sender.sendMessage(prettify(message)); @@ -92,16 +97,9 @@ public static void sendTr(CommandSender sender, String key, Object... msg) { public static void sendWithNPC(CommandSender sender, Object msg, NPC npc) { String send = msg.toString(); - - if (sender instanceof Player) { - Player player = (Player) sender; - send = send.replace("", player.getName()); - send = send.replace("", player.getWorld().getName()); - } send = send.replace("", npc.getTrait(Owner.class).getOwner()); send = send.replace("", npc.getName()); send = send.replace("", Integer.toString(npc.getId())); - send(sender, send); } diff --git a/src/main/java/net/citizensnpcs/api/util/NBTStorage.java b/src/main/java/net/citizensnpcs/api/util/NBTStorage.java index 88e67f00..85cf6d2b 100644 --- a/src/main/java/net/citizensnpcs/api/util/NBTStorage.java +++ b/src/main/java/net/citizensnpcs/api/util/NBTStorage.java @@ -33,7 +33,7 @@ import com.google.common.io.Closeables; import com.google.common.io.Files; -public class NBTStorage implements Storage { +public class NBTStorage implements FileStorage { private final File file; private final String name; private final Map root = Maps.newHashMap(); @@ -59,6 +59,11 @@ private void create() { } } + @Override + public File getFile() { + return file; + } + @Override public DataKey getKey(String root) { return new NBTKey(root); diff --git a/src/main/java/net/citizensnpcs/api/util/YamlStorage.java b/src/main/java/net/citizensnpcs/api/util/YamlStorage.java index 89eb3562..fad7d5a8 100644 --- a/src/main/java/net/citizensnpcs/api/util/YamlStorage.java +++ b/src/main/java/net/citizensnpcs/api/util/YamlStorage.java @@ -15,7 +15,7 @@ import com.google.common.io.Files; -public class YamlStorage implements Storage { +public class YamlStorage implements FileStorage { private final FileConfiguration config; private final File file; @@ -44,6 +44,7 @@ private void create() { } } + @Override public File getFile() { return file; }