From 87266346026c50f4d12088a48499c80c77462b4c Mon Sep 17 00:00:00 2001
From: Chris Kearney <chris@kearneymail.com>
Date: Wed, 3 Aug 2016 21:47:43 -0700
Subject: [PATCH] spellbooks

---
 .../com/comandante/creeper/ConfigureNpc.java  |  4 +-
 .../comandante/creeper/Items/ItemType.java    | 13 ++++-
 .../creeper/Items/ItemUseRegistry.java        |  4 ++
 .../use/LightningSpellBookUseAction.java      | 50 +++++++++++++++++++
 .../creeper/command/CastCommand.java          |  6 +--
 .../managers/NewUserRegistrationManager.java  |  2 +-
 .../com/comandante/creeper/player/Player.java | 43 ++++++++++++----
 .../creeper/player/PlayerMetadata.java        | 32 +++++++++++-
 .../creeper/spells/LightningSpell.java        |  2 +-
 .../com/comandante/creeper/spells/Spell.java  |  1 -
 ...egistry.java => SpellTriggerRegistry.java} |  5 +-
 11 files changed, 142 insertions(+), 20 deletions(-)
 create mode 100644 src/main/java/com/comandante/creeper/Items/use/LightningSpellBookUseAction.java
 rename src/main/java/com/comandante/creeper/spells/{SpellRegistry.java => SpellTriggerRegistry.java} (79%)

diff --git a/src/main/java/com/comandante/creeper/ConfigureNpc.java b/src/main/java/com/comandante/creeper/ConfigureNpc.java
index ea8febce..6d93d315 100755
--- a/src/main/java/com/comandante/creeper/ConfigureNpc.java
+++ b/src/main/java/com/comandante/creeper/ConfigureNpc.java
@@ -4,7 +4,6 @@ import com.comandante.creeper.Items.*;
 import com.comandante.creeper.entity.EntityManager;
 import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.merchant.*;
-import com.comandante.creeper.merchant.GrimulfWizard;
 import com.comandante.creeper.npc.Npc;
 import com.comandante.creeper.npc.NpcExporter;
 import com.comandante.creeper.spawner.ItemSpawner;
@@ -72,6 +71,7 @@ public class ConfigureNpc {
         blacksmithItems.put(4, new MerchantItemForSale(ItemType.IRON_HELMET, 500));
         blacksmithItems.put(5, new MerchantItemForSale(ItemType.IRON_CHEST_PLATE, 1500));
         blacksmithItems.put(6, new MerchantItemForSale(ItemType.IRON_LEGGINGS, 1100));
+        blacksmithItems.put(7, new MerchantItemForSale(ItemType.LIGHTNING_SPELLBOOKNG, 1100));
 
         Blacksmith blacksmith = new Blacksmith(gameManager, new Loot(18, 26, Sets.<ItemType>newHashSet()), blacksmithItems);
         gameManager.getRoomManager().addMerchant(66, blacksmith);
@@ -96,6 +96,6 @@ public class ConfigureNpc {
         gameManager.getForageManager().addForageToArea(Area.BLOODRIDGE2_ZONE, marijuanaForageBuilder);
         gameManager.getForageManager().addForageToArea(Area.BLOODRIDGE1_ZONE, marijuanaForageBuilder);
 
-        SpellRegistry.addSpell(new LightningSpell(gameManager));
+        SpellTriggerRegistry.addSpell(new LightningSpell(gameManager));
     }
 }
diff --git a/src/main/java/com/comandante/creeper/Items/ItemType.java b/src/main/java/com/comandante/creeper/Items/ItemType.java
index baf315a9..38d9d96e 100755
--- a/src/main/java/com/comandante/creeper/Items/ItemType.java
+++ b/src/main/java/com/comandante/creeper/Items/ItemType.java
@@ -145,7 +145,18 @@ public enum ItemType {
             60,
             true,
             Rarity.BASIC,
-            3000, Sets.<TimeTracker.TimeOfDay>newHashSet());
+            3000, Sets.<TimeTracker.TimeOfDay>newHashSet()),
+
+    LIGHTNING_SPELLBOOKNG(13, Arrays.asList("lightning book", "lightning spell book", "book", "spell book"),
+            "a " + Color.YELLOW + "lightning" + Color.RESET + " spell book." + Color.RESET,
+            "a " + Color.YELLOW + "lightning" + Color.RESET + " spell book." + Color.RESET,
+            "a " + Color.YELLOW + "lightning" + Color.RESET + " spell book." + Color.RESET,
+            true,
+            0,
+            60,
+            false,
+            Rarity.RARE,
+            3000, Sets.<TimeTracker.TimeOfDay>newHashSet()),;
 
             
     private final Integer itemTypeCode;
diff --git a/src/main/java/com/comandante/creeper/Items/ItemUseRegistry.java b/src/main/java/com/comandante/creeper/Items/ItemUseRegistry.java
index 6e9871be..c8f89507 100644
--- a/src/main/java/com/comandante/creeper/Items/ItemUseRegistry.java
+++ b/src/main/java/com/comandante/creeper/Items/ItemUseRegistry.java
@@ -2,6 +2,7 @@ package com.comandante.creeper.Items;
 
 import com.comandante.creeper.Items.use.DefaultApplyStatsAction;
 import com.comandante.creeper.Items.use.DirtyBombUseAction;
+import com.comandante.creeper.Items.use.LightningSpellBookUseAction;
 import com.comandante.creeper.Items.use.ResetAllEffectsUseAction;
 import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.player.Player;
@@ -38,6 +39,9 @@ public class ItemUseRegistry {
 
         //Marijuana
         addItemUseAction(new DefaultApplyStatsAction(ItemType.MARIJUANA, buildStats(500,500), Sets.<Effect>newHashSet()));
+
+        //Lightning Spellbook
+        addItemUseAction(new LightningSpellBookUseAction(ItemType.LIGHTNING_SPELLBOOKNG));
     }
 
     private static Stats buildStats(int health, int mana) {
diff --git a/src/main/java/com/comandante/creeper/Items/use/LightningSpellBookUseAction.java b/src/main/java/com/comandante/creeper/Items/use/LightningSpellBookUseAction.java
new file mode 100644
index 00000000..4d3c205c
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/Items/use/LightningSpellBookUseAction.java
@@ -0,0 +1,50 @@
+package com.comandante.creeper.Items.use;
+
+import com.comandante.creeper.Items.Item;
+import com.comandante.creeper.Items.ItemType;
+import com.comandante.creeper.Items.ItemUseAction;
+import com.comandante.creeper.managers.GameManager;
+import com.comandante.creeper.npc.Npc;
+import com.comandante.creeper.npc.NpcStatsChangeBuilder;
+import com.comandante.creeper.player.Player;
+import com.comandante.creeper.server.Color;
+import com.comandante.creeper.spells.Effect;
+import com.comandante.creeper.spells.LightningSpell;
+import com.comandante.creeper.stat.StatsBuilder;
+import com.comandante.creeper.world.Room;
+
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Set;
+
+public class LightningSpellBookUseAction implements ItemUseAction {
+
+    private final ItemType itemType;
+
+    public LightningSpellBookUseAction(ItemType itemType) {
+        this.itemType = itemType;
+    }
+
+    @Override
+    public Integer getItemTypeId() {
+        return itemType.getItemTypeCode();
+    }
+
+    @Override
+    public void executeAction(GameManager gameManager, Player player, Item item) {
+        gameManager.writeToPlayerCurrentRoom(player.getPlayerId(), player.getPlayerName() + " reads a leatherbound aging spell book and gains knowledge about lightning spells.");
+        player.addLearnedSpellByName(LightningSpell.NAME);
+    }
+
+    @Override
+    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+        player.removeInventoryId(item.getItemId());
+        gameManager.getEntityManager().removeItem(item);
+    }
+
+    @Override
+    public Set<Effect> getEffects() {
+        return null;
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/command/CastCommand.java b/src/main/java/com/comandante/creeper/command/CastCommand.java
index c8236f38..d2a68861 100644
--- a/src/main/java/com/comandante/creeper/command/CastCommand.java
+++ b/src/main/java/com/comandante/creeper/command/CastCommand.java
@@ -6,7 +6,7 @@ import com.comandante.creeper.npc.Npc;
 import com.comandante.creeper.player.CoolDownType;
 import com.comandante.creeper.player.Player;
 import com.comandante.creeper.spells.Spell;
-import com.comandante.creeper.spells.SpellRegistry;
+import com.comandante.creeper.spells.SpellTriggerRegistry;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Sets;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -42,8 +42,8 @@ public class CastCommand extends Command {
                 return;
             }
             String desiredSpellName = originalMessageParts.get(1);
-            Spell spell = SpellRegistry.getSpell(desiredSpellName);
-            if (spell == null) {
+            Spell spell = SpellTriggerRegistry.getSpell(desiredSpellName);
+            if (spell == null || !player.doesHaveSpellLearned(spell.getSpellName())) {
                 write("No spell found with the name: " + desiredSpellName + "\r\n");
                 return;
             }
diff --git a/src/main/java/com/comandante/creeper/managers/NewUserRegistrationManager.java b/src/main/java/com/comandante/creeper/managers/NewUserRegistrationManager.java
index b2ef99bb..5e9e3d2a 100644
--- a/src/main/java/com/comandante/creeper/managers/NewUserRegistrationManager.java
+++ b/src/main/java/com/comandante/creeper/managers/NewUserRegistrationManager.java
@@ -63,7 +63,7 @@ public class NewUserRegistrationManager {
             return;
         }
         session.setPassword(Optional.of(password));
-        PlayerMetadata playerMetadata = new PlayerMetadata(session.getUsername().get(), session.getPassword().get(), Main.createPlayerId(session.getUsername().get()), PlayerStats.DEFAULT_PLAYER.createStats(), 0, Sets.newHashSet(PlayerRole.MORTAL), new String[0], 0);
+        PlayerMetadata playerMetadata = new PlayerMetadata(session.getUsername().get(), session.getPassword().get(), Main.createPlayerId(session.getUsername().get()), PlayerStats.DEFAULT_PLAYER.createStats(), 0, Sets.newHashSet(PlayerRole.MORTAL), new String[0], 0, new String[0]);
         playerManager.savePlayerMetadata(playerMetadata);
         e.getChannel().write("User created.\r\n");
         session.setState(CreeperSession.State.newUserRegCompleted);
diff --git a/src/main/java/com/comandante/creeper/player/Player.java b/src/main/java/com/comandante/creeper/player/Player.java
index a3ded404..7fb1b00f 100755
--- a/src/main/java/com/comandante/creeper/player/Player.java
+++ b/src/main/java/com/comandante/creeper/player/Player.java
@@ -290,8 +290,8 @@ public class Player extends CreeperEntity {
         }
     }
 
-    public void resetEffects(){
-        synchronized (interner){
+    public void resetEffects() {
+        synchronized (interner) {
             PlayerMetadata playerMetadata = getPlayerMetadata();
             playerMetadata.resetEffects();
             gameManager.getPlayerManager().savePlayerMetadata(playerMetadata);
@@ -314,6 +314,31 @@ public class Player extends CreeperEntity {
         }
     }
 
+    public void addLearnedSpellByName(String spellName) {
+        synchronized (interner.intern(playerId)) {
+            PlayerMetadata playerMetadata = getPlayerMetadata();
+            playerMetadata.addLearnedSpellByName(spellName);
+            savePlayerMetadata(playerMetadata);
+        }
+    }
+
+    public boolean doesHaveSpellLearned(String spellName) {
+        PlayerMetadata playerMetadata = getPlayerMetadata();
+        if (playerMetadata.getLearnedSpells() == null || playerMetadata.getLearnedSpells().length == 0) {
+            return false;
+        }
+        List<String> learnedSpells = Arrays.asList(playerMetadata.getLearnedSpells());
+        return learnedSpells.contains(spellName);
+    }
+
+    public void removeLearnedSpellByName(String spellName) {
+        synchronized (interner.intern(playerId)) {
+            PlayerMetadata playerMetadata = getPlayerMetadata();
+            playerMetadata.removeLearnedSpellByName(spellName);
+            savePlayerMetadata(playerMetadata);
+        }
+    }
+
     public void addEquipmentId(String equipmentId) {
         synchronized (interner.intern(playerId)) {
             PlayerMetadata playerMetadata = getPlayerMetadata();
@@ -936,18 +961,18 @@ public class Player extends CreeperEntity {
             }
             if (damageToVictim > 0) {
                 if (randInt(0, 100) > 95) {
-                   final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + Color.YELLOW + "The " + npc.getColorName() + " was caught off guard by the attack!" + "+" + NumberFormat.getNumberInstance(Locale.US).format(damageToVictim) + Color.RESET + Color.BOLD_ON + Color.RED + " DAMAGE" + Color.RESET + " done to " + npc.getColorName();
-                   npcStatsChangeBuilder.setStats(new StatsBuilder().setCurrentHealth(-(damageToVictim*3)).createStats());
-                   npcStatsChangeBuilder.setDamageStrings(Arrays.asList(fightMsg)); 
+                    final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + Color.YELLOW + "The " + npc.getColorName() + " was caught off guard by the attack!" + "+" + NumberFormat.getNumberInstance(Locale.US).format(damageToVictim) + Color.RESET + Color.BOLD_ON + Color.RED + " DAMAGE" + Color.RESET + " done to " + npc.getColorName();
+                    npcStatsChangeBuilder.setStats(new StatsBuilder().setCurrentHealth(-(damageToVictim * 3)).createStats());
+                    npcStatsChangeBuilder.setDamageStrings(Collections.singletonList(fightMsg));
                 } else {
                     final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + Color.YELLOW + "+" + NumberFormat.getNumberInstance(Locale.US).format(damageToVictim) + Color.RESET + Color.BOLD_ON + Color.RED + " DAMAGE" + Color.RESET + " done to " + npc.getColorName();
                     npcStatsChangeBuilder.setStats(new StatsBuilder().setCurrentHealth(-damageToVictim).createStats());
-                    npcStatsChangeBuilder.setDamageStrings(Arrays.asList(fightMsg));
+                    npcStatsChangeBuilder.setDamageStrings(Collections.singletonList(fightMsg));
                 }
             } else {
                 final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + "You MISS " + npc.getName() + "!";
                 npcStatsChangeBuilder.setStats(new StatsBuilder().setCurrentHealth(-damageToVictim).createStats());
-                npcStatsChangeBuilder.setDamageStrings(Arrays.asList(fightMsg));
+                npcStatsChangeBuilder.setDamageStrings(Collections.singletonList(fightMsg));
             }
         }
         if (this.doesActiveFightExist(npc)) {
@@ -956,12 +981,12 @@ public class Player extends CreeperEntity {
             if (randInt(0, 100) < chanceToHitBack) {
                 final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + npc.getColorName() + Color.BOLD_ON + Color.RED + " DAMAGES" + Color.RESET + " you for " + Color.RED + "-" + NumberFormat.getNumberInstance(Locale.US).format(damageBack) + Color.RESET;
                 npcStatsChangeBuilder.setPlayerStatsChange(new StatsBuilder().setCurrentHealth(-damageBack).createStats());
-                npcStatsChangeBuilder.setPlayerDamageStrings(Arrays.asList(fightMsg));
+                npcStatsChangeBuilder.setPlayerDamageStrings(Collections.singletonList(fightMsg));
 
             } else {
                 final String fightMsg = Color.BOLD_ON + Color.RED + "[attack] " + Color.RESET + npc.getColorName() + Color.BOLD_ON + Color.CYAN + " MISSES" + Color.RESET + " you!";
                 npcStatsChangeBuilder.setPlayerStatsChange(new StatsBuilder().setCurrentHealth(0).createStats());
-                npcStatsChangeBuilder.setPlayerDamageStrings(Arrays.asList(fightMsg));
+                npcStatsChangeBuilder.setPlayerDamageStrings(Collections.singletonList(fightMsg));
             }
             npc.addNpcDamage(npcStatsChangeBuilder.createNpcStatsChange());
         }
diff --git a/src/main/java/com/comandante/creeper/player/PlayerMetadata.java b/src/main/java/com/comandante/creeper/player/PlayerMetadata.java
index 44004e8a..8ed7a3b1 100644
--- a/src/main/java/com/comandante/creeper/player/PlayerMetadata.java
+++ b/src/main/java/com/comandante/creeper/player/PlayerMetadata.java
@@ -24,8 +24,9 @@ public class PlayerMetadata implements Serializable {
     private List<String> effects;
     private boolean isMarkedForDelete;
     private Map<String, String> playerSettings;
+    private String[] learnedSpells;
 
-    public PlayerMetadata(String playerName, String password, String playerId, Stats stats, int gold, Set<PlayerRole> playerRoleSet, String[] playerEquipment, int goldInBank) {
+    public PlayerMetadata(String playerName, String password, String playerId, Stats stats, int gold, Set<PlayerRole> playerRoleSet, String[] playerEquipment, int goldInBank, String[] learnedSpells) {
         this.playerName = playerName;
         this.password = password;
         this.playerId = playerId;
@@ -34,6 +35,7 @@ public class PlayerMetadata implements Serializable {
         this.playerRoleSet = playerRoleSet;
         this.playerEquipment = playerEquipment;
         this.goldInBank = goldInBank;
+        this.learnedSpells = learnedSpells;
     }
 
     public PlayerMetadata(PlayerMetadata playerMetadata) {
@@ -61,6 +63,9 @@ public class PlayerMetadata implements Serializable {
         if (playerMetadata.playerSettings != null) {
             this.playerSettings = new HashMap<String, String>(playerMetadata.getPlayerSettings());
         }
+        if (playerMetadata.learnedSpells != null) {
+            this.learnedSpells = Arrays.copyOf(playerMetadata.learnedSpells, playerMetadata.learnedSpells.length);
+        }
         this.isMarkedForDelete = new Boolean(playerMetadata.isMarkedForDelete);
     }
 
@@ -101,6 +106,27 @@ public class PlayerMetadata implements Serializable {
         inventory.remove(itemId);
     }
 
+    protected void addLearnedSpellByName(String spellName) {
+        if (learnedSpells == null) {
+            learnedSpells = new String[0];
+        }
+        String[] result = Arrays.copyOf(learnedSpells, learnedSpells.length + 1);
+        result[learnedSpells.length] = spellName;
+        this.learnedSpells = result;
+    }
+
+    protected void removeLearnedSpellByName(String spellName) {
+        List<String> learnedSpellsKeep = new ArrayList<String>(Arrays.asList(learnedSpells));
+        learnedSpellsKeep.remove(spellName);
+        String[] newSpells = new String[learnedSpellsKeep.size()];
+        int i = 0;
+        for (String id : learnedSpellsKeep) {
+            newSpells[i] = id;
+            i++;
+        }
+        this.learnedSpells = newSpells;
+    }
+
     protected void addEquipmentEntityId(String equipmentItemId) {
         if (playerEquipment == null) {
             playerEquipment = new String[0];
@@ -161,6 +187,10 @@ public class PlayerMetadata implements Serializable {
         return goldInBank;
     }
 
+    public String[] getLearnedSpells() {
+        return learnedSpells;
+    }
+
     protected void setGold(long amt) {
         this.gold = amt;
     }
diff --git a/src/main/java/com/comandante/creeper/spells/LightningSpell.java b/src/main/java/com/comandante/creeper/spells/LightningSpell.java
index b9fc556b..c2f5af11 100644
--- a/src/main/java/com/comandante/creeper/spells/LightningSpell.java
+++ b/src/main/java/com/comandante/creeper/spells/LightningSpell.java
@@ -18,7 +18,7 @@ import static com.comandante.creeper.server.Color.BOLD_ON;
 
 public class LightningSpell extends Spell {
 
-    private final static String NAME = BOLD_ON + Color.YELLOW + "lightning" + Color.RESET + " bolt";
+    public final static String NAME = BOLD_ON + Color.YELLOW + "lightning" + Color.RESET + " bolt";
     private final static String DESCRIPTION = "A powerful bolt of lightning.";
     private final static Set<String> validTriggers = new HashSet<String>(Arrays.asList(new String[]
                     {"lightning", "lightning bolt", "l", NAME}
diff --git a/src/main/java/com/comandante/creeper/spells/Spell.java b/src/main/java/com/comandante/creeper/spells/Spell.java
index b89c3acd..f702e299 100644
--- a/src/main/java/com/comandante/creeper/spells/Spell.java
+++ b/src/main/java/com/comandante/creeper/spells/Spell.java
@@ -247,5 +247,4 @@ public abstract class Spell {
     interface SpellExecute {
         public void executeNpc(GameManager gameManager, Npc npc, Player player);
     }
-
 }
diff --git a/src/main/java/com/comandante/creeper/spells/SpellRegistry.java b/src/main/java/com/comandante/creeper/spells/SpellTriggerRegistry.java
similarity index 79%
rename from src/main/java/com/comandante/creeper/spells/SpellRegistry.java
rename to src/main/java/com/comandante/creeper/spells/SpellTriggerRegistry.java
index 1b4ad8b1..e393bc68 100644
--- a/src/main/java/com/comandante/creeper/spells/SpellRegistry.java
+++ b/src/main/java/com/comandante/creeper/spells/SpellTriggerRegistry.java
@@ -5,9 +5,11 @@ import com.google.common.collect.Maps;
 
 import java.util.Map;
 
-public class SpellRegistry {
+public class SpellTriggerRegistry {
 
     public static final Map<String, Spell> spellMap = Maps.newHashMap();
+    public static final Map<String, Spell> spellNameMap = Maps.newHashMap();
+
 
     public static void addSpell(Spell spell) {
         for (String trigger : spell.getValidTriggers()) {
@@ -18,4 +20,5 @@ public class SpellRegistry {
     public static Spell getSpell(String trigger) {
         return spellMap.get(trigger);
     }
+
 }
-- 
GitLab