From e441abf8394e79b9dd9d515b2fcb5a19fd551748 Mon Sep 17 00:00:00 2001
From: fullwall <fullwall25@gmail.com>
Date: Thu, 16 Aug 2012 00:06:13 +0800
Subject: [PATCH] Vault support

---
 pom.xml                                       | 13 ++++-
 src/main/java/net/citizensnpcs/Citizens.java  | 29 ++++++++++-
 .../java/net/citizensnpcs/EventListen.java    |  1 +
 .../java/net/citizensnpcs/NPCPayListener.java | 35 +++++++++++++
 src/main/java/net/citizensnpcs/Settings.java  |  2 +
 .../command/command/NPCCommands.java          | 12 ++++-
 .../citizensnpcs/npc/ai/MCTargetStrategy.java | 14 +++---
 .../net/citizensnpcs/npc/ai/PathStrategy.java |  4 +-
 src/main/java/net/citizensnpcs/trait/Age.java | 10 ++--
 .../net/citizensnpcs/trait/Behaviour.java     | 50 +++++++++----------
 src/main/resources/plugin.yml                 |  1 +
 11 files changed, 129 insertions(+), 42 deletions(-)
 create mode 100644 src/main/java/net/citizensnpcs/NPCPayListener.java

diff --git a/pom.xml b/pom.xml
index ba4180708..22d67b7e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<craftbukkit.version>1.3.1-R0.1-SNAPSHOT</craftbukkit.version>
 		<citizensapi.version>2.0-SNAPSHOT</citizensapi.version>
+		<vault.version>1.2.18-SNAPSHOT</vault.version>
 		<build.number>Unknown</build.number>
 	</properties>
 
@@ -21,14 +22,24 @@
 			<id>bukkit-repo</id>
 			<url>http://repo.bukkit.org/content/groups/public/</url>
 		</repository>
-
 		<repository>
 			<id>everything</id>
 			<url>http://repo.citizensnpcs.net</url>
 		</repository>
+        <repository>
+            <id>vault-repo</id>
+            <url>http://ci.herocraftonline.com/plugin/repository/everything</url>
+        </repository>
 	</repositories>
 
 	<dependencies>
+        <dependency>
+            <groupId>net.milkbowl.vault</groupId>
+            <artifactId>Vault</artifactId>
+            <version>${vault.version}</version>
+            <type>jar</type>
+            <scope>compile</scope>
+        </dependency>
 		<dependency>
 			<groupId>org.bukkit</groupId>
 			<artifactId>craftbukkit</artifactId>
diff --git a/src/main/java/net/citizensnpcs/Citizens.java b/src/main/java/net/citizensnpcs/Citizens.java
index 8922a5624..0d7e831f4 100644
--- a/src/main/java/net/citizensnpcs/Citizens.java
+++ b/src/main/java/net/citizensnpcs/Citizens.java
@@ -41,6 +41,7 @@ import net.citizensnpcs.npc.CitizensTraitFactory;
 import net.citizensnpcs.npc.NPCSelector;
 import net.citizensnpcs.util.Messaging;
 import net.citizensnpcs.util.StringHelper;
+import net.milkbowl.vault.economy.Economy;
 
 import org.bukkit.Bukkit;
 import org.bukkit.ChatColor;
@@ -50,6 +51,7 @@ import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.entity.EntityType;
 import org.bukkit.entity.Player;
 import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.RegisteredServiceProvider;
 import org.bukkit.plugin.java.JavaPlugin;
 
 import com.google.common.collect.Iterables;
@@ -197,6 +199,16 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
         CitizensAPI.setImplementation(this);
 
         getServer().getPluginManager().registerEvents(new EventListen(), this);
+        if (Setting.NPC_COST.asDouble() > 0) {
+            try {
+                RegisteredServiceProvider<Economy> provider = Bukkit.getServicesManager().getRegistration(
+                        Economy.class);
+                Economy economy = provider.getProvider();
+                Bukkit.getPluginManager().registerEvents(new NPCPayListener(economy), this);
+            } catch (NoClassDefFoundError e) {
+                Messaging.log("Unable to use economy handling. Has Vault been enabled?");
+            }
+        }
 
         registerCommands();
 
@@ -210,6 +222,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
                 setupNPCs();
                 startMetrics();
                 enableSubPlugins();
+                scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
             }
         }) == -1) {
             Messaging.severe("NPC load task couldn't be scheduled - disabling...");
@@ -255,7 +268,21 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
         for (NPC npc : npcRegistry)
             ((CitizensNPC) npc).save(saves.getKey("npc." + npc.getId()));
 
-        saves.save();
+        new Thread() {
+            @Override
+            public void run() {
+                saves.save();
+            }
+        }.start();
+    }
+
+    private void scheduleSaveTask(int delay) {
+        Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
+            @Override
+            public void run() {
+                save();
+            }
+        });
     }
 
     // TODO: refactor
diff --git a/src/main/java/net/citizensnpcs/EventListen.java b/src/main/java/net/citizensnpcs/EventListen.java
index 5a83cd0f5..f61781b88 100644
--- a/src/main/java/net/citizensnpcs/EventListen.java
+++ b/src/main/java/net/citizensnpcs/EventListen.java
@@ -121,6 +121,7 @@ public class EventListen implements Listener {
     /*
      * Player events
      */
+
     @EventHandler(ignoreCancelled = true)
     public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
         EntityPlayer handle = ((CraftPlayer) event.getPlayer()).getHandle();
diff --git a/src/main/java/net/citizensnpcs/NPCPayListener.java b/src/main/java/net/citizensnpcs/NPCPayListener.java
new file mode 100644
index 000000000..89c5fb3ce
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/NPCPayListener.java
@@ -0,0 +1,35 @@
+package net.citizensnpcs;
+
+import net.citizensnpcs.Settings.Setting;
+import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
+import net.milkbowl.vault.economy.Economy;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+import com.google.common.base.Preconditions;
+
+public class NPCPayListener implements Listener {
+    private final Economy provider;
+
+    public NPCPayListener(Economy provider) {
+        Preconditions.checkNotNull(provider, "provider cannot be null");
+        this.provider = provider;
+    }
+
+    @EventHandler(ignoreCancelled = true)
+    public void onPlayerCreateNPC(PlayerCreateNPCEvent event) {
+        String name = event.getCreator().getName();
+        boolean hasAccount = provider.hasAccount(name);
+        if (!hasAccount)
+            return;
+        double cost = Setting.NPC_COST.asDouble();
+        boolean hasEnough = provider.has(name, cost);
+        if (!hasEnough) {
+            event.setCancelled(true);
+            event.setCancelReason(String.format("Need at least %s.", provider.format(cost)));
+            return;
+        }
+        provider.bankWithdraw(name, cost);
+    }
+}
diff --git a/src/main/java/net/citizensnpcs/Settings.java b/src/main/java/net/citizensnpcs/Settings.java
index ed6bbf5c6..cc9ccac3e 100644
--- a/src/main/java/net/citizensnpcs/Settings.java
+++ b/src/main/java/net/citizensnpcs/Settings.java
@@ -67,7 +67,9 @@ public class Settings {
                 value = list;
             }
         },
+        NPC_COST("economy.npc.cost", 100D),
         QUICK_SELECT("npc.selection.quick-select", false),
+        SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60),
         SELECTION_ITEM("npc.selection.item", "280"),
         SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"),
         SERVER_OWNS_NPCS("npc.server-ownership", false),
diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java
index ba1c4ad46..cc776d146 100644
--- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java
+++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java
@@ -6,6 +6,7 @@ import java.util.List;
 import net.citizensnpcs.Citizens;
 import net.citizensnpcs.Settings.Setting;
 import net.citizensnpcs.api.CitizensAPI;
+import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
 import net.citizensnpcs.api.npc.NPC;
 import net.citizensnpcs.api.npc.NPCRegistry;
 import net.citizensnpcs.api.trait.Trait;
@@ -165,7 +166,7 @@ public class NPCCommands {
             min = 2,
             permission = "npc.create")
     @Requirements
-    public void create(CommandContext args, final Player player, NPC npc) {
+    public void create(CommandContext args, final Player player, NPC npc) throws CommandException {
         String name = StringHelper.parseColors(args.getJoinedStrings(1));
         if (name.length() > 16) {
             Messaging.sendError(player,
@@ -224,6 +225,15 @@ public class NPCCommands {
 
         npc.spawn(player.getLocation());
 
+        PlayerCreateNPCEvent event = new PlayerCreateNPCEvent(player, npc);
+        if (event.isCancelled()) {
+            npc.destroy();
+            String reason = "Couldn't create NPC.";
+            if (!event.getCancelReason().isEmpty())
+                reason += " Reason: " + event.getCancelReason();
+            throw new CommandException(reason);
+        }
+
         // Set age after entity spawns
         if (npc.getBukkitEntity() instanceof Ageable)
             npc.getTrait(Age.class).setAge(age);
diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java
index 777a0868c..c4e1269d9 100644
--- a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java
+++ b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java
@@ -18,8 +18,8 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
     private final boolean aggro;
     private int attackTicks;
     private final EntityLiving handle, target;
-    private final float speed;
     private final Navigation navigation;
+    private final float speed;
 
     public MCTargetStrategy(CitizensNPC handle, LivingEntity target, boolean aggro, float speed) {
         this.handle = handle.getHandle();
@@ -59,6 +59,11 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
         return aggro;
     }
 
+    @Override
+    public void setSpeed(float speed) {
+        navigation.a(speed);
+    }
+
     @Override
     public boolean update() {
         if (target == null || target.dead)
@@ -82,12 +87,7 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget {
 
         return false;
     }
-
     private static final int ATTACK_DELAY_TICKS = 20;
-    private static final double ATTACK_DISTANCE = 1.75 * 1.75;
 
-    @Override
-    public void setSpeed(float speed) {
-        navigation.a(speed);
-    }
+    private static final double ATTACK_DISTANCE = 1.75 * 1.75;
 }
\ No newline at end of file
diff --git a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java
index 556b79832..7c1eb29bf 100644
--- a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java
+++ b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java
@@ -9,7 +9,7 @@ public interface PathStrategy {
 
     TargetType getTargetType();
 
-    boolean update();
-
     void setSpeed(float speed);
+
+    boolean update();
 }
\ No newline at end of file
diff --git a/src/main/java/net/citizensnpcs/trait/Age.java b/src/main/java/net/citizensnpcs/trait/Age.java
index 53ddf62f4..92789c29b 100644
--- a/src/main/java/net/citizensnpcs/trait/Age.java
+++ b/src/main/java/net/citizensnpcs/trait/Age.java
@@ -18,6 +18,11 @@ public class Age extends Trait implements Toggleable {
         super("age");
     }
 
+    public void describe(CommandSender sender) {
+        Messaging.sendF(sender, "%s's age is %s and %s locked.", StringHelper.wrap(npc.getName()),
+                StringHelper.wrap(age), StringHelper.wrap(locked ? "is" : "isn't"));
+    }
+
     @Override
     public void load(DataKey key) throws NPCLoadException {
         if (npc.isSpawned() && !(npc.getBukkitEntity() instanceof Ageable))
@@ -67,9 +72,4 @@ public class Age extends Trait implements Toggleable {
     public String toString() {
         return "Age{age=" + age + ",locked=" + locked + "}";
     }
-
-    public void describe(CommandSender sender) {
-        Messaging.sendF(sender, "%s's age is %s and %s locked.", StringHelper.wrap(npc.getName()),
-                StringHelper.wrap(age), StringHelper.wrap(locked ? "is" : "isn't"));
-    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/citizensnpcs/trait/Behaviour.java b/src/main/java/net/citizensnpcs/trait/Behaviour.java
index 29d3eecf3..841dc7c09 100644
--- a/src/main/java/net/citizensnpcs/trait/Behaviour.java
+++ b/src/main/java/net/citizensnpcs/trait/Behaviour.java
@@ -74,6 +74,24 @@ public class Behaviour extends Trait {
         }
     }
 
+    public void removeScripts(Iterable<String> files) {
+        Iterable<File> transformed = Iterables.transform(files, fileConverterFunction);
+        boolean isSpawned = npc.isSpawned();
+        for (File file : transformed) {
+            if (isSpawned) {
+                Iterator<BehaviourGoalEntry> itr = addedGoals.iterator();
+                while (itr.hasNext()) {
+                    BehaviourGoalEntry entry = itr.next();
+                    if (file.equals(entry.file)) {
+                        itr.remove();
+                        npc.getDefaultGoalController().removeGoal(entry.getGoal());
+                    }
+                }
+            }
+            scripts.remove(file);
+        }
+    }
+
     private void reset() {
         removeGoals();
         scripts.clear();
@@ -85,18 +103,9 @@ public class Behaviour extends Trait {
         key.setString("scripts", Joiner.on(",").join(scripts));
     }
 
-    private static class BehaviourGoalEntry extends SimpleGoalEntry {
-        private final File file;
-
-        private BehaviourGoalEntry(Goal goal, int priority, File file) {
-            super(goal, priority);
-            this.file = file;
-        }
-    }
-
     public class BehaviourCallback implements CompileCallback {
-        private final List<BehaviourGoalEntry> goals = Lists.newArrayList();
         private File fileInUse;
+        private final List<BehaviourGoalEntry> goals = Lists.newArrayList();
 
         public void addGoal(int priority, Goal goal) {
             Validate.notNull(goal);
@@ -124,21 +133,12 @@ public class Behaviour extends Trait {
         }
     }
 
-    public void removeScripts(Iterable<String> files) {
-        Iterable<File> transformed = Iterables.transform(files, fileConverterFunction);
-        boolean isSpawned = npc.isSpawned();
-        for (File file : transformed) {
-            if (isSpawned) {
-                Iterator<BehaviourGoalEntry> itr = addedGoals.iterator();
-                while (itr.hasNext()) {
-                    BehaviourGoalEntry entry = itr.next();
-                    if (file.equals(entry.file)) {
-                        itr.remove();
-                        npc.getDefaultGoalController().removeGoal(entry.getGoal());
-                    }
-                }
-            }
-            scripts.remove(file);
+    private static class BehaviourGoalEntry extends SimpleGoalEntry {
+        private final File file;
+
+        private BehaviourGoalEntry(Goal goal, int priority, File file) {
+            super(goal, priority);
+            this.file = file;
         }
     }
 }
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index c242cbd1f..3c401f28b 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,5 +1,6 @@
 name: Citizens
 authors: [aPunch, fullwall]
+softdepend: [Vault]
 version: 2.0
 main: net.citizensnpcs.Citizens
 website: http://www.citizensnpcs.net