diff --git a/src/main/java/net/citizensnpcs/Citizens.java b/src/main/java/net/citizensnpcs/Citizens.java index 391294410..794909a86 100644 --- a/src/main/java/net/citizensnpcs/Citizens.java +++ b/src/main/java/net/citizensnpcs/Citizens.java @@ -174,6 +174,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { tearDownScripting(); // Don't bother with this part if MC versions are not compatible if (compatible) { + saves.storeAll(npcRegistry); saves.saveToDiskImmediate(); despawnNPCs(); npcRegistry = null; diff --git a/src/main/java/net/citizensnpcs/NPCDataStore.java b/src/main/java/net/citizensnpcs/NPCDataStore.java index 8130e22da..078e66996 100644 --- a/src/main/java/net/citizensnpcs/NPCDataStore.java +++ b/src/main/java/net/citizensnpcs/NPCDataStore.java @@ -5,6 +5,7 @@ import java.sql.SQLException; import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.npc.NPC; +import net.citizensnpcs.api.npc.NPCRegistry; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DatabaseStorage; import net.citizensnpcs.api.util.NBTStorage; @@ -28,7 +29,7 @@ public class NPCDataStore { for (DataKey key : root.getKey("npc").getIntegerSubKeys()) { int id = Integer.parseInt(key.name()); if (!key.keyExists("name")) { - Messaging.logF("Could not find a name for the NPC with ID '%s'.", id); + Messaging.logF("Could not find a name for ID '%s'.", id); continue; } String unparsedEntityType = key.getString("traits.type", "PLAYER"); @@ -73,6 +74,11 @@ public class NPCDataStore { ((CitizensNPC) npc).save(root.getKey("npc." + npc.getId())); } + public void storeAll(NPCRegistry registry) { + for (NPC npc : registry) + store(npc); + } + public static NPCDataStore create(File folder) { Storage saves = null; String type = Setting.STORAGE_TYPE.asString(); diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 34b50b661..6c82f49b9 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -22,8 +22,8 @@ import org.bukkit.Location; import org.bukkit.entity.LivingEntity; public class CitizensNavigator implements Navigator { - private final NavigatorParameters defaultParams = new NavigatorParameters().speed(UNINITIALISED_SPEED) - .range(Setting.DEFAULT_PATHFINDING_RANGE.asFloat()) + private final NavigatorParameters defaultParams = new NavigatorParameters() + .baseSpeed(UNINITIALISED_SPEED).range(Setting.DEFAULT_PATHFINDING_RANGE.asFloat()) .stationaryTicks(Setting.DEFAULT_STATIONARY_TICKS.asInt()); private PathStrategy executing; private int lastX, lastY, lastZ; @@ -79,7 +79,7 @@ public class CitizensNavigator implements Navigator { } public void load(DataKey root) { - defaultParams.speed((float) root.getDouble("speed", UNINITIALISED_SPEED)); + defaultParams.baseSpeed((float) root.getDouble("speed", UNINITIALISED_SPEED)); defaultParams.range((float) root.getDouble("pathfindingrange", Setting.DEFAULT_PATHFINDING_RANGE.asFloat())); defaultParams @@ -90,8 +90,8 @@ public class CitizensNavigator implements Navigator { } public void onSpawn() { - if (defaultParams.speed() == UNINITIALISED_SPEED) - defaultParams.speed(NMS.getSpeedFor(npc.getHandle())); + if (defaultParams.baseSpeed() == UNINITIALISED_SPEED) + defaultParams.baseSpeed(NMS.getSpeedFor(npc.getHandle())); updatePathfindingRange(); if (!updatedAvoidWater) { boolean defaultAvoidWater = npc.getHandle().getNavigation().a(); diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java index 6cf596d8f..8fcba133f 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java @@ -84,6 +84,10 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget { cancelReason = CancelReason.TARGET_DIED; return true; } + if (target.world != handle.world) { + cancelReason = CancelReason.TARGET_MOVED_WORLD; + return true; + } if (cancelReason != null) return true; navigation.a(target, parameters.speed()); diff --git a/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java index cf9bbad7d..1fa05fdf8 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/CitizensHumanNPC.java @@ -35,7 +35,7 @@ public class CitizensHumanNPC extends CitizensNPC implements Equipable { // set the head yaw in another tick - if done immediately, // minecraft will not update it. } - }, 3); + }, 5); handle.getBukkitEntity().setSleepingIgnored(true); return handle; } diff --git a/src/main/java/net/citizensnpcs/trait/Controllable.java b/src/main/java/net/citizensnpcs/trait/Controllable.java index 53cb8a7b6..b812edf6b 100644 --- a/src/main/java/net/citizensnpcs/trait/Controllable.java +++ b/src/main/java/net/citizensnpcs/trait/Controllable.java @@ -114,6 +114,7 @@ public class Controllable extends Trait implements Toggleable { return; controller.run((Player) getHandle().passenger.getBukkitEntity()); } + @Override public void save(DataKey key) { key.setBoolean("enabled", enabled); @@ -176,10 +177,12 @@ public class Controllable extends Trait implements Toggleable { return; getHandle().motY = JUMP_VELOCITY; } + @Override public void leftClick(PlayerInteractEvent event) { jump(); } + @Override public void rightClick(PlayerInteractEvent event) { } @@ -200,9 +203,7 @@ public class Controllable extends Trait implements Toggleable { } private static final float AIR_SPEED = 1.5F; - private static final float GROUND_SPEED = 4F; - private static final float JUMP_VELOCITY = 0.6F; } diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java index fadd88cae..0fecf2370 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/LinearWaypointProvider.java @@ -39,6 +39,7 @@ public class LinearWaypointProvider implements WaypointProvider { @Override public Editor createEditor(final Player player) { return new Editor() { + boolean editing = true; int editingSlot = waypoints.size() - 1; @Override @@ -49,7 +50,10 @@ public class LinearWaypointProvider implements WaypointProvider { @Override public void end() { + if (!editing) + return; player.sendMessage(ChatColor.AQUA + "Exited the linear waypoint editor."); + editing = false; } private String formatLoc(Location location) { @@ -69,13 +73,13 @@ public class LinearWaypointProvider implements WaypointProvider { @EventHandler public void onNPCDespawn(NPCDespawnEvent event) { if (event.getNPC().equals(npc)) - end(); + Editor.leave(player); } @EventHandler public void onNPCRemove(NPCRemoveEvent event) { if (event.getNPC().equals(npc)) - end(); + Editor.leave(player); } @EventHandler diff --git a/src/main/java/net/citizensnpcs/trait/waypoint/WanderingWaypointProvider.java b/src/main/java/net/citizensnpcs/trait/waypoint/WanderingWaypointProvider.java index 1cfe1ae79..721d119c3 100644 --- a/src/main/java/net/citizensnpcs/trait/waypoint/WanderingWaypointProvider.java +++ b/src/main/java/net/citizensnpcs/trait/waypoint/WanderingWaypointProvider.java @@ -1,14 +1,26 @@ package net.citizensnpcs.trait.waypoint; +import java.util.Random; + +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.ai.Goal; +import net.citizensnpcs.api.ai.GoalSelector; +import net.citizensnpcs.api.ai.event.NavigationCompleteEvent; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.editor.Editor; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; public class WanderingWaypointProvider implements WaypointProvider { + private WanderGoal currentGoal; private NPC npc; private boolean paused; + private int xrange, yrange; @Override public Editor createEditor(Player player) { @@ -34,25 +46,81 @@ public class WanderingWaypointProvider implements WaypointProvider { @Override public void load(DataKey key) { + xrange = key.getInt("xrange", DEFAULT_XRANGE); + yrange = key.getInt("yrange", DEFAULT_YRANGE); } - @Override public void onSpawn(NPC npc) { this.npc = npc; - /* if (currentGoal == null) { - currentGoal = new WaypointGoal(this, npc.getNavigator()); + currentGoal = new WanderGoal(); CitizensAPI.registerEvents(currentGoal); } - npc.getDefaultGoalController().addGoal(currentGoal, 1);TODO*/ + npc.getDefaultGoalController().addGoal(currentGoal, 1); } @Override public void save(DataKey key) { + key.setInt("xrange", xrange); + key.setInt("yrange", yrange); } @Override public void setPaused(boolean paused) { // TODO } + + private class WanderGoal implements Goal { + private final Random random = new Random(); + private GoalSelector selector; + + private Location findRandomPosition() { + Location base = npc.getBukkitEntity().getLocation(); + Location found = null; + int range = 50; + int yrange = 3; + for (int i = 0; i < 10; i++) { + int x = base.getBlockX() + random.nextInt(2 * range) - range; + int y = base.getBlockY() + random.nextInt(2 * yrange) - yrange; + int z = base.getBlockZ() + random.nextInt(2 * range) - range; + Block block = base.getWorld().getBlockAt(x, y, z); + if (block.isEmpty() && block.getRelative(BlockFace.DOWN).isEmpty()) { + found = block.getLocation(); + break; + } + } + return found; + } + + @EventHandler + public void onFinish(NavigationCompleteEvent event) { + if (selector != null) + selector.finish(); + } + + @Override + public void reset() { + selector = null; + } + + @Override + public void run(GoalSelector selector) { + } + + @Override + public boolean shouldExecute(GoalSelector selector) { + if (!npc.isSpawned() || npc.getNavigator().isNavigating()) + return false; + Location dest = findRandomPosition(); + if (dest == null) + return false; + npc.getNavigator().setTarget(dest); + this.selector = selector; + return true; + } + } + + private static final int DEFAULT_XRANGE = 3; + + private static final int DEFAULT_YRANGE = 25; }