From 4a484a4df0f4f2c1e5a56111eee3e0d9743ce8d0 Mon Sep 17 00:00:00 2001
From: Jeffrey Phillips Freeman <jeffrey.freeman@syncleus.com>
Date: Sun, 3 Sep 2017 14:44:05 -0400
Subject: [PATCH] fix: fixed using health potions.

---
 .../command/commands/MovementCommand.java     |   4 +-
 .../command/commands/UseCommand.java          |   4 +-
 .../commands/admin/GiveHealthCommand.java     |   8 +-
 .../commands/admin/TeleportCommand.java       |   4 +-
 .../configuration/ConfigureCommands.java      |   1 +
 .../syncleus/aethermud/core/GameManager.java  |   4 +-
 .../com/syncleus/aethermud/items/Effect.java  | 116 +++-------
 .../aethermud/items/EffectBuilder.java        |   5 +-
 .../syncleus/aethermud/items/EffectPojo.java  | 147 ++++++++++++
 .../aethermud/items/EffectsManager.java       |  18 +-
 .../com/syncleus/aethermud/items/Item.java    |   4 +-
 .../syncleus/aethermud/items/ItemBuilder.java |   4 +-
 .../aethermud/items/ItemMetadata.java         |   8 +-
 .../syncleus/aethermud/items/ItemPojo.java    |   8 +-
 .../aethermud/items/ItemUseAction.java        |   2 +-
 .../aethermud/items/ItemUseHandler.java       |   1 -
 .../items/use/DefaultApplyEffectsStats.java   |  10 +-
 .../items/use/DirtyBombUseAction.java         |   2 +-
 .../use/LightningSpellBookUseAction.java      |   4 +-
 .../items/use/ResetAllEffectsUseAction.java   |   2 +-
 .../items/use/StickOfJusticeUseAction.java    |   4 +-
 .../com/syncleus/aethermud/npc/NpcSpawn.java  |  12 +-
 .../com/syncleus/aethermud/player/Player.java |  22 +-
 .../aethermud/storage/graphdb/EffectData.java | 211 ++++++++++++++++++
 .../aethermud/storage/graphdb/ItemData.java   |  91 +++++++-
 .../aethermud/storage/graphdb/NpcData.java    |   2 +-
 .../aethermud/storage/graphdb/PlayerData.java |  16 +-
 27 files changed, 549 insertions(+), 165 deletions(-)
 create mode 100644 src/main/java/com/syncleus/aethermud/items/EffectPojo.java
 create mode 100644 src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java

diff --git a/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
index d8148a36..03e414a9 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
@@ -16,7 +16,7 @@
 package com.syncleus.aethermud.command.commands;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.storage.graphdb.PlayerData;
 import com.syncleus.aethermud.player.PlayerMovement;
@@ -68,7 +68,7 @@ public class MovementCommand extends Command {
             if (!playerMetadataOptional.isPresent()) {
                 return;
             }
-            for (Effect effect : playerMetadataOptional.get().getEffects()) {
+            for (EffectPojo effect : playerMetadataOptional.get().getEffects()) {
                 if (effect.isFrozenMovement()) {
                     MovementCommand.this.write("You are frozen and can not move.");
                     return;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
index 06361512..c3672635 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
@@ -61,7 +61,7 @@ public class UseCommand extends Command {
 
     public static class UseItemOn {
 
-        public static String ON_KEYWORD = "on";
+        public static String ON_KEYWORD = " on ";
 
         private final Optional<String> target;
         private final String item;
@@ -69,6 +69,8 @@ public class UseCommand extends Command {
         public UseItemOn(List<String> originalMessageParts) {
             originalMessageParts.remove(0);
             String fullCommand = Joiner.on(" ").join(originalMessageParts);
+            if(fullCommand.endsWith(" on"))
+                fullCommand = fullCommand.substring(0, fullCommand.length() - 3);
             item = getItem(fullCommand);
             target = getItemTarget(fullCommand);
         }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/GiveHealthCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/GiveHealthCommand.java
index e6a0ca79..7a254ed4 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/GiveHealthCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/GiveHealthCommand.java
@@ -43,10 +43,6 @@ public class GiveHealthCommand extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommand(ctx, e, () -> {
-            if (!player.getPlayerName().equals("fibs")) {
-                write("This attempt to cheat has been logged.");
-                return;
-            }
             if (originalMessageParts.size() > 2) {
                 String destinationPlayerName = originalMessageParts.get(1);
                 String amt = originalMessageParts.get(2);
@@ -59,8 +55,8 @@ public class GiveHealthCommand extends Command {
                     write("Player does not exist.");
                     return;
                 }
-                playerByUsername.incrementGold(Integer.parseInt(amt));
-                write("The amount of " + amt + " gold has been placed into " + destinationPlayerName + "'s inventory.");
+                playerByUsername.setCurrentHealth(playerByUsername.getCurrentHealth() + Integer.valueOf(amt));
+                write("The amount of " + amt + " health has been given to " + destinationPlayerName + ".");
             }
         });
     }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
index 40832bcf..029a7a25 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
@@ -18,7 +18,7 @@ package com.syncleus.aethermud.command.commands.admin;
 
 import com.syncleus.aethermud.command.commands.Command;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.player.*;
 import com.syncleus.aethermud.server.communication.Color;
 import com.syncleus.aethermud.storage.graphdb.PlayerData;
@@ -61,7 +61,7 @@ public class TeleportCommand extends Command {
                 return;
             }
             PlayerData playerData = playerMetadataOptional.get();
-            for (Effect effect : playerData.getEffects()) {
+            for (EffectPojo effect : playerData.getEffects()) {
                 if (effect.isFrozenMovement()) {
                     write("You are frozen and can not move.");
                     return;
diff --git a/src/main/java/com/syncleus/aethermud/configuration/ConfigureCommands.java b/src/main/java/com/syncleus/aethermud/configuration/ConfigureCommands.java
index fb7f2509..df8139fd 100644
--- a/src/main/java/com/syncleus/aethermud/configuration/ConfigureCommands.java
+++ b/src/main/java/com/syncleus/aethermud/configuration/ConfigureCommands.java
@@ -121,5 +121,6 @@ public class ConfigureCommands {
         commandRegistry.addCommand(new LoadItemCommand(gameManager));
         commandRegistry.addCommand(new LoadMerchantCommand(gameManager));
         commandRegistry.addCommand(new RestartCommand(gameManager));
+        commandRegistry.addCommand(new GiveHealthCommand(gameManager));
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/core/GameManager.java b/src/main/java/com/syncleus/aethermud/core/GameManager.java
index 143f1363..7f38d0c5 100644
--- a/src/main/java/com/syncleus/aethermud/core/GameManager.java
+++ b/src/main/java/com/syncleus/aethermud/core/GameManager.java
@@ -670,7 +670,7 @@ public class GameManager {
         return NumberFormat.getNumberInstance(Locale.US).format(longval);
     }
 
-    public String renderEffectsString(List<Effect> effects) {
+    public String renderEffectsString(List<EffectPojo> effects) {
         Table t = new Table(2, BorderStyle.CLASSIC_COMPATIBLE,
                 ShownBorders.NONE);
 
@@ -678,7 +678,7 @@ public class GameManager {
         // t.setColumnWidth(1, 10, 13);
 
         int i = 1;
-        for (Effect effect : effects) {
+        for (EffectPojo effect : effects) {
             int percent = 100 - (int) ((effect.getEffectApplications() * 100.0f) / effect.getMaxEffectApplications());
             t.addCell(drawProgressBar(percent));
             t.addCell(effect.getEffectName());
diff --git a/src/main/java/com/syncleus/aethermud/items/Effect.java b/src/main/java/com/syncleus/aethermud/items/Effect.java
index 4c9261d8..9cc59c54 100644
--- a/src/main/java/com/syncleus/aethermud/items/Effect.java
+++ b/src/main/java/com/syncleus/aethermud/items/Effect.java
@@ -15,88 +15,44 @@
  */
 package com.syncleus.aethermud.items;
 
-
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 import java.util.List;
 
-public class Effect {
-
-    private final String effectName;
-    private final String effectDescription;
-    private final List<String> effectApplyMessages;
-    private final Stats applyStatsOnTick;
-    private final Stats durationStats;
-    private final int maxEffectApplications;
-    private final boolean frozenMovement;
-    private int effectApplications;
-    private String playerId;
-
-    public Effect(String effectName, String effectDescription, List<String> effectApplyMessages, Stats applyStatsOnTick, Stats durationStats, int maxEffectApplications, boolean frozenMovement) {
-        this.effectName = effectName;
-        this.effectDescription = effectDescription;
-        this.effectApplyMessages = effectApplyMessages;
-        this.applyStatsOnTick = applyStatsOnTick;
-        this.durationStats = durationStats;
-        this.maxEffectApplications = maxEffectApplications;
-        this.frozenMovement = frozenMovement;
-        this.effectApplications = 0;
-    }
-
-    public Effect(Effect effect) {
-        this.effectName = effect.effectName;
-        this.effectDescription = effect.effectDescription;
-        this.effectApplyMessages = effect.effectApplyMessages;
-        this.applyStatsOnTick = effect.applyStatsOnTick;
-        this.durationStats = effect.durationStats;
-        this.maxEffectApplications = effect.maxEffectApplications;
-        this.frozenMovement = effect.frozenMovement;
-        this.effectApplications = effect.effectApplications;
-    }
-
-
-    public String getEffectName() {
-        return effectName;
-    }
-
-    public String getEffectDescription() {
-        return effectDescription;
-    }
-
-    public List<String> getEffectApplyMessages() {
-        return effectApplyMessages;
-    }
-
-    public Stats getApplyStatsOnTick() {
-        return applyStatsOnTick;
-    }
-
-    public int getMaxEffectApplications() {
-        return maxEffectApplications;
-    }
-
-    public boolean isFrozenMovement() {
-        return frozenMovement;
-    }
-
-    public int getEffectApplications() {
-        return effectApplications;
-    }
-
-    public void setEffectApplications(int effectApplications) {
-        this.effectApplications = effectApplications;
-    }
-
-    public Stats getDurationStats() {
-        return durationStats;
-    }
-
-    public String getPlayerId() {
-        return playerId;
-    }
-
-    public void setPlayerId(String playerId) {
-        this.playerId = playerId;
-    }
+public interface Effect {
+    String getEffectName();
+
+    String getEffectDescription();
+
+    List<String> getEffectApplyMessages();
+
+    Stats getApplyStatsOnTick();
+
+    int getMaxEffectApplications();
+
+    boolean isFrozenMovement();
+
+    int getEffectApplications();
+
+    void setEffectApplications(int effectApplications);
+
+    Stats getDurationStats();
+
+    String getPlayerId();
+
+    void setPlayerId(String playerId);
+
+    void setEffectName(String effectName);
+
+    void setEffectDescription(String effectDescription);
+
+    void setEffectApplyMessages(List<String> effectApplyMessages);
+
+    void setApplyStatsOnTick(Stats applyStatsOnTick);
+
+    void setDurationStats(Stats durationStats);
+
+    void setMaxEffectApplications(int maxEffectApplications);
+
+    void setFrozenMovement(boolean frozenMovement);
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java b/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
index 21641df3..ab0ed3e3 100644
--- a/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
@@ -16,7 +16,6 @@
 package com.syncleus.aethermud.items;
 
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 import java.util.List;
 
@@ -64,7 +63,7 @@ public class EffectBuilder {
         return this;
     }
 
-    public Effect createEffect() {
-        return new Effect(effectName, effectDescription, effectApplyMessages, applyStatsOnTick, durationStats, lifeSpanTicks, frozenMovement);
+    public EffectPojo createEffect() {
+        return new EffectPojo(effectName, effectDescription, effectApplyMessages, applyStatsOnTick, durationStats, lifeSpanTicks, frozenMovement);
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectPojo.java b/src/main/java/com/syncleus/aethermud/items/EffectPojo.java
new file mode 100644
index 00000000..4c4b3fb7
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/items/EffectPojo.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2017 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.syncleus.aethermud.items;
+
+
+import com.syncleus.aethermud.stats.Stats;
+
+import java.util.List;
+
+public class EffectPojo implements Effect {
+
+    private String effectName;
+    private String effectDescription;
+    private List<String> effectApplyMessages;
+    private Stats applyStatsOnTick;
+    private Stats durationStats;
+    private int maxEffectApplications;
+    private boolean frozenMovement;
+    private int effectApplications;
+    private String playerId;
+
+    public EffectPojo(String effectName, String effectDescription, List<String> effectApplyMessages, Stats applyStatsOnTick, Stats durationStats, int maxEffectApplications, boolean frozenMovement) {
+        this.effectName = effectName;
+        this.effectDescription = effectDescription;
+        this.effectApplyMessages = effectApplyMessages;
+        this.applyStatsOnTick = applyStatsOnTick;
+        this.durationStats = durationStats;
+        this.maxEffectApplications = maxEffectApplications;
+        this.frozenMovement = frozenMovement;
+        this.effectApplications = 0;
+    }
+
+    public EffectPojo(EffectPojo effect) {
+        this.effectName = effect.effectName;
+        this.effectDescription = effect.effectDescription;
+        this.effectApplyMessages = effect.effectApplyMessages;
+        this.applyStatsOnTick = effect.applyStatsOnTick;
+        this.durationStats = effect.durationStats;
+        this.maxEffectApplications = effect.maxEffectApplications;
+        this.frozenMovement = effect.frozenMovement;
+        this.effectApplications = effect.effectApplications;
+    }
+
+
+    @Override
+    public String getEffectName() {
+        return effectName;
+    }
+
+    @Override
+    public String getEffectDescription() {
+        return effectDescription;
+    }
+
+    @Override
+    public List<String> getEffectApplyMessages() {
+        return effectApplyMessages;
+    }
+
+    @Override
+    public Stats getApplyStatsOnTick() {
+        return applyStatsOnTick;
+    }
+
+    @Override
+    public int getMaxEffectApplications() {
+        return maxEffectApplications;
+    }
+
+    @Override
+    public boolean isFrozenMovement() {
+        return frozenMovement;
+    }
+
+    @Override
+    public int getEffectApplications() {
+        return effectApplications;
+    }
+
+    @Override
+    public void setEffectApplications(int effectApplications) {
+        this.effectApplications = effectApplications;
+    }
+
+    @Override
+    public Stats getDurationStats() {
+        return durationStats;
+    }
+
+    @Override
+    public String getPlayerId() {
+        return playerId;
+    }
+
+    @Override
+    public void setPlayerId(String playerId) {
+        this.playerId = playerId;
+    }
+
+    @Override
+    public void setEffectName(String effectName) {
+        this.effectName = effectName;
+    }
+
+    @Override
+    public void setEffectDescription(String effectDescription) {
+        this.effectDescription = effectDescription;
+    }
+
+    @Override
+    public void setEffectApplyMessages(List<String> effectApplyMessages) {
+        this.effectApplyMessages = effectApplyMessages;
+    }
+
+    @Override
+    public void setApplyStatsOnTick(Stats applyStatsOnTick) {
+        this.applyStatsOnTick = applyStatsOnTick;
+    }
+
+    @Override
+    public void setDurationStats(Stats durationStats) {
+        this.durationStats = durationStats;
+    }
+
+    @Override
+    public void setMaxEffectApplications(int maxEffectApplications) {
+        this.maxEffectApplications = maxEffectApplications;
+    }
+
+    @Override
+    public void setFrozenMovement(boolean frozenMovement) {
+        this.frozenMovement = frozenMovement;
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
index 1ab90072..799b3f86 100644
--- a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
@@ -43,10 +43,10 @@ public class EffectsManager {
         this.gameManager = gameManager;
     }
 
-    public void applyEffectsToNpcs(Player player, Set<NpcSpawn> npcSpawns, Set<Effect> effects) {
+    public void applyEffectsToNpcs(Player player, Set<NpcSpawn> npcSpawns, Set<EffectPojo> effects) {
         effects.forEach(effect ->
                 npcSpawns.forEach(npc -> {
-                    Effect nEffect = new Effect(effect);
+                    EffectPojo nEffect = new EffectPojo(effect);
                     nEffect.setPlayerId(player.getPlayerId());
                     if (effect.getDurationStats().getCurrentHealth() < 0) {
                         log.error("ERROR! Someone added an effect with a health modifier which won't work for various reasons.");
@@ -57,9 +57,9 @@ public class EffectsManager {
                 }));
     }
 
-    public void applyEffectsToPlayer(Player destinationPlayer, Player player, Set<Effect> effects) {
-        for (Effect effect : effects) {
-            Effect nEffect = new Effect(effect);
+    public void applyEffectsToPlayer(Player destinationPlayer, Player player, Set<EffectPojo> effects) {
+        for (EffectPojo effect : effects) {
+            EffectPojo nEffect = new EffectPojo(effect);
             nEffect.setPlayerId(player.getPlayerId());
             if (effect.getDurationStats().getCurrentHealth() < 0) {
                 log.error("ERROR! Someone added an effect with a health modifier which won't work for various reasons.");
@@ -76,7 +76,7 @@ public class EffectsManager {
         }
     }
 
-    public void application(Effect effect, Player player) {
+    public void application(EffectPojo effect, Player player) {
         // if there are effecst that modify player health, deal with it here, you can't rely on combine stats.
         Stats applyStatsOnTick = effect.getApplyStatsOnTick();
         if (effect.getApplyStatsOnTick() != null) {
@@ -95,7 +95,7 @@ public class EffectsManager {
         }
     }
 
-    public void application(Effect effect, NpcSpawn npcSpawn) {
+    public void application(EffectPojo effect, NpcSpawn npcSpawn) {
         Player player = gameManager.getPlayerManager().getPlayer(effect.getPlayerId());
         Stats applyStats = new StatsPojo(effect.getApplyStatsOnTick());
         // if there are effecst that modify npc health, deal with it here, you can't rely on combine stats.
@@ -113,13 +113,13 @@ public class EffectsManager {
         StatsHelper.combineStats(npcSpawn.getStats(), finalCombineWorthyStats);
     }
 
-    public void removeDurationStats(Effect effect, NpcSpawn npcSpawn) {
+    public void removeDurationStats(EffectPojo effect, NpcSpawn npcSpawn) {
         Stats newStats = new StatsPojo(effect.getDurationStats());
         StatsHelper.inverseStats(newStats);
         StatsHelper.combineStats(npcSpawn.getStats(), newStats);
     }
 
-    public void removeDurationStats(Effect effect, PlayerData playerData) {
+    public void removeDurationStats(EffectPojo effect, PlayerData playerData) {
         Stats newStats = new StatsPojo(effect.getDurationStats());
         StatsHelper.inverseStats(newStats);
         StatsHelper.combineStats(playerData.getStats(), newStats);
diff --git a/src/main/java/com/syncleus/aethermud/items/Item.java b/src/main/java/com/syncleus/aethermud/items/Item.java
index 7a146b20..3ea5f860 100644
--- a/src/main/java/com/syncleus/aethermud/items/Item.java
+++ b/src/main/java/com/syncleus/aethermud/items/Item.java
@@ -70,9 +70,9 @@ public interface Item {
 
     int getValueInGold();
 
-    void setEffects(Set<Effect> effects);
+    void setEffects(Set<EffectPojo> effects);
 
-    Set<Effect> getEffects();
+    Set<EffectPojo> getEffects();
 
     void setItemName(String itemName);
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
index e30123ad..013e931c 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
@@ -40,7 +40,7 @@ public class ItemBuilder {
     private Equipment equipment;
     private Rarity rarity;
     private int valueInGold;
-    private Set<Effect> effects;
+    private Set<EffectPojo> effects;
     private boolean hasBeenWithPlayer;
     private int maxUses;
     private boolean isDisposable;
@@ -160,7 +160,7 @@ public class ItemBuilder {
         return this;
     }
 
-    public ItemBuilder effects(Set<Effect> effects) {
+    public ItemBuilder effects(Set<EffectPojo> effects) {
         this.effects = effects;
         return this;
     }
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java b/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
index d2cea573..03fa5bcf 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
@@ -18,9 +18,7 @@ package com.syncleus.aethermud.items;
 
 import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.spawner.SpawnRule;
-import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.stats.StatsPojo;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 import com.google.common.collect.Sets;
 
 import java.util.List;
@@ -41,7 +39,7 @@ public class ItemMetadata {
     private int itemHalfLifeTicks;
     private Rarity rarity;
     private Equipment equipment;
-    private Set<Effect> effects;
+    private Set<EffectPojo> effects;
     private List<String> itemTriggers;
     private Set<TimeTracker.TimeOfDay> validTimeOfDays;
     private boolean isDisposable;
@@ -125,11 +123,11 @@ public class ItemMetadata {
         this.equipment = equipment;
     }
 
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return effects;
     }
 
-    public void setEffects(Set<Effect> effects) {
+    public void setEffects(Set<EffectPojo> effects) {
         this.effects = effects;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemPojo.java b/src/main/java/com/syncleus/aethermud/items/ItemPojo.java
index e6eb897a..5c5122ef 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemPojo.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemPojo.java
@@ -40,7 +40,7 @@ public class ItemPojo implements Serializable, Item {
     private Equipment equipment;
     private Rarity rarity;
     private int valueInGold;
-    private Set<Effect> effects;
+    private Set<EffectPojo> effects;
     private boolean hasBeenWithPlayer;
     private int maxUses;
     private boolean isDisposable;
@@ -49,7 +49,7 @@ public class ItemPojo implements Serializable, Item {
 
     public static final String CORPSE_INTENAL_NAME = "corpse";
 
-    protected ItemPojo(String itemName, String itemDescription, String internalItemName, List<String> itemTriggers, String restingName, String itemId, int numberOfUses, boolean isWithPlayer, Loot loot, int itemHalfLifeTicks, Equipment equipment, Rarity rarity, int valueInGold, Set<Effect> effects, boolean hasBeenWithPlayer, int maxUses, boolean isDisposable, Set<TimeTracker.TimeOfDay> validTimeOfDays, Stats itemApplyStats) {
+    protected ItemPojo(String itemName, String itemDescription, String internalItemName, List<String> itemTriggers, String restingName, String itemId, int numberOfUses, boolean isWithPlayer, Loot loot, int itemHalfLifeTicks, Equipment equipment, Rarity rarity, int valueInGold, Set<EffectPojo> effects, boolean hasBeenWithPlayer, int maxUses, boolean isDisposable, Set<TimeTracker.TimeOfDay> validTimeOfDays, Stats itemApplyStats) {
         this.itemName = itemName;
         this.itemDescription = itemDescription;
         this.internalItemName = internalItemName;
@@ -191,12 +191,12 @@ public class ItemPojo implements Serializable, Item {
     }
 
     @Override
-    public void setEffects(Set<Effect> effects) {
+    public void setEffects(Set<EffectPojo> effects) {
         this.effects = effects;
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return effects;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
index 0877b9d6..b412e882 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
@@ -28,6 +28,6 @@ public interface ItemUseAction {
 
     void postExecuteAction(GameManager gameManager, Player player, ItemPojo item);
 
-    Set<Effect> getEffects();
+    Set<EffectPojo> getEffects();
 
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java b/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
index 00d95a2f..73e07d73 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
@@ -65,4 +65,3 @@ public class ItemUseHandler {
         item.setNumberOfUses(item.getNumberOfUses() + 1);
     }
 }
-
diff --git a/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java b/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
index 5062ed72..81326e6c 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
@@ -27,7 +27,7 @@ import java.util.Set;
 public class DefaultApplyEffectsStats implements ItemUseAction {
 
     private final String internalItemName;
-    private final Set<Effect> effectSet;
+    private final Set<EffectPojo> effectSet;
     private final Stats itemApplyStats;
     private static final Logger log = Logger.getLogger(DefaultApplyEffectsStats.class);
 
@@ -73,16 +73,16 @@ public class DefaultApplyEffectsStats implements ItemUseAction {
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return effectSet;
     }
 
-    public static void processEffects(GameManager gameManager, Player player, Set<Effect> effects) {
+    public static void processEffects(GameManager gameManager, Player player, Set<EffectPojo> effects) {
         if (effects == null) {
             return;
         }
-        for (Effect effect : effects) {
-            Effect nEffect = new Effect(effect);
+        for (EffectPojo effect : effects) {
+            EffectPojo nEffect = new EffectPojo(effect);
             nEffect.setPlayerId(player.getPlayerId());
             boolean effectResult = player.addEffect(nEffect);
             if (effect.getDurationStats() != null) {
diff --git a/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
index f70f2e0c..f47cd911 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
@@ -87,7 +87,7 @@ public class DirtyBombUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return null;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
index 430a6f65..13362f63 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.items.use;
 
 import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.items.ItemUseAction;
@@ -61,7 +61,7 @@ public class LightningSpellBookUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return null;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
index 3530b2db..e703dd3c 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
@@ -55,7 +55,7 @@ public class ResetAllEffectsUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return null;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
index 45efbe1b..2824479b 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.items.use;
 
 import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.items.ItemUseAction;
@@ -64,7 +64,7 @@ public class StickOfJusticeUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<Effect> getEffects() {
+    public Set<EffectPojo> getEffects() {
         return null;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
index 098aeb9e..2f9d5bbf 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
@@ -21,7 +21,7 @@ import com.syncleus.aethermud.common.AetherMudMessage;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SentryManager;
 import com.syncleus.aethermud.entity.AetherMudEntity;
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.player.CoolDownPojo;
@@ -77,7 +77,7 @@ public class NpcSpawn extends AetherMudEntity {
     private final Random random = new Random();
     private long lastPhraseTimestamp;
     private Loot loot;
-    private List<Effect> effects = Lists.newArrayList();
+    private List<EffectPojo> effects = Lists.newArrayList();
     private int maxEffects = 4;
     private Map<String, Long> playerDamageMap = Maps.newHashMap();
     private Room currentRoom;
@@ -136,9 +136,9 @@ public class NpcSpawn extends AetherMudEntity {
                     if (effectsTickBucket == 5) {
 
                         // START Process NPC Effects
-                        Iterator<Effect> iterator = effects.iterator();
+                        Iterator<EffectPojo> iterator = effects.iterator();
                         while (iterator.hasNext()) {
-                            Effect effect = iterator.next();
+                            EffectPojo effect = iterator.next();
                             if (effect.getEffectApplications() >= effect.getMaxEffectApplications()) {
                                 Optional<Room> npcCurrentRoom = gameManager.getRoomManager().getNpcCurrentRoom(this);
                                 if (npcCurrentRoom.isPresent()) {
@@ -412,7 +412,7 @@ public class NpcSpawn extends AetherMudEntity {
         return spawnRules;
     }
 
-    public void addEffect(Effect effect) {
+    public void addEffect(EffectPojo effect) {
         synchronized (interner.intern(getEntityId())) {
             if (effects.size() >= maxEffects) {
             } else {
@@ -421,7 +421,7 @@ public class NpcSpawn extends AetherMudEntity {
         }
     }
 
-    public List<Effect> getEffects() {
+    public List<EffectPojo> getEffects() {
         return effects;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/player/Player.java b/src/main/java/com/syncleus/aethermud/player/Player.java
index c4589603..0c45fd11 100644
--- a/src/main/java/com/syncleus/aethermud/player/Player.java
+++ b/src/main/java/com/syncleus/aethermud/player/Player.java
@@ -167,8 +167,8 @@ public class Player extends AetherMudEntity {
                 return;
             }
             PlayerData playerData = playerMetadataOptional.get();
-            List<Effect> effectsToRemove = Lists.newArrayList();
-            for (Effect effect : playerData.getEffects()) {
+            List<EffectPojo> effectsToRemove = Lists.newArrayList();
+            for (EffectPojo effect : playerData.getEffects()) {
                 if (effect.getEffectApplications() >= effect.getMaxEffectApplications()) {
                     gameManager.getChannelUtils().write(playerId, Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + effect.getEffectName() + " has worn off.\r\n", true);
                     effectsToRemove.add(effect);
@@ -179,7 +179,7 @@ public class Player extends AetherMudEntity {
                 }
 
             }
-            for (Effect effect : effectsToRemove) {
+            for (EffectPojo effect : effectsToRemove) {
                 playerData.removeEffect(effect);
             }
             savePlayerMetadata();
@@ -335,13 +335,20 @@ public class Player extends AetherMudEntity {
         gameManager.getPlayerManager().persist();
     }
 
-    public long getCurrentHealth() {
+    public int getCurrentHealth() {
         synchronized (interner.intern(playerId)) {
             Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
             return playerMetadataOptional.map(playerMetadata -> playerMetadata.getStats().getCurrentHealth()).orElse(0);
         }
     }
 
+    public void setCurrentHealth(int health) {
+        synchronized (interner.intern(playerId)) {
+            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
+            playerMetadataOptional.ifPresent((p) -> p.getStats().setCurrentHealth(health));
+        }
+    }
+
     public void transferGoldToBank(int amt) {
         synchronized (interner.intern(playerId)) {
             Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
@@ -378,7 +385,7 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public boolean addEffect(Effect effect) {
+    public boolean addEffect(EffectPojo effect) {
         synchronized (interner.intern(playerId)) {
             Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
             if (!playerMetadataOptional.isPresent()) {
@@ -867,7 +874,6 @@ public class Player extends AetherMudEntity {
             for (String itemId : playerData.getInventory()) {
                 Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                 if (!itemOptional.isPresent()) {
-                    log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
                     continue;
                 }
                 ItemPojo itemEntity = itemOptional.get();
@@ -1169,7 +1175,7 @@ public class Player extends AetherMudEntity {
                 StatsHelper.combineStats(newStats, stats);
             }
             if (playerData.getEffects() != null) {
-                for (Effect effect : playerData.getEffects()) {
+                for (EffectPojo effect : playerData.getEffects()) {
                     StatsHelper.combineStats(newStats, effect.getDurationStats());
                 }
             }
@@ -1230,7 +1236,7 @@ public class Player extends AetherMudEntity {
             return "";
         }
         PlayerData playerData = playerMetadataOptional.get();
-        List<Effect> effects = playerData.getEffects();
+        List<EffectPojo> effects = playerData.getEffects();
         return gameManager.renderEffectsString(effects);
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java
new file mode 100644
index 00000000..0c705333
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java
@@ -0,0 +1,211 @@
+/**
+ * Copyright 2017 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.syncleus.aethermud.storage.graphdb;
+
+import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.stats.Stats;
+import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.Adjacency;
+import com.syncleus.ferma.annotations.Property;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class EffectData extends AbstractVertexFrame implements Effect {
+    @Override
+    @Property("EffectName")
+    public abstract String getEffectName();
+
+    @Override
+    @Property("EffectDescription")
+    public abstract String getEffectDescription();
+
+    @Override
+    @Property("EffectApplyMessages")
+    public abstract List<String> getEffectApplyMessages();
+
+    @Override
+    @Property("MaxEffectApplications")
+    public abstract int getMaxEffectApplications();
+
+    @Override
+    @Property("FrozenMovement")
+    public abstract boolean isFrozenMovement();
+
+    @Override
+    @Property("EffectApplications")
+    public abstract int getEffectApplications();
+
+    @Override
+    @Property("EffectApplications")
+    public abstract void setEffectApplications(int effectApplications);
+
+    @Override
+    @Property("PlayerId")
+    public abstract String getPlayerId();
+
+    @Override
+    @Property("PlayerId")
+    public abstract void setPlayerId(String playerId);
+
+    @Override
+    @Property("EffectName")
+    public abstract void setEffectName(String effectName);
+
+    @Override
+    @Property("EffectDescription")
+    public abstract void setEffectDescription(String effectDescription);
+
+    @Override
+    @Property("EffectApplyMessages")
+    public abstract void setEffectApplyMessages(List<String> effectApplyMessages);
+
+    @Override
+    @Property("MaxEffectApplications")
+    public abstract void setMaxEffectApplications(int maxEffectApplications);
+
+    @Override
+    @Property("FrozenMovement")
+    public abstract void setFrozenMovement(boolean frozenMovement);
+
+    @Adjacency(label = "ApplyStatsOnTick", direction = Direction.OUT)
+    public abstract <N extends StatsData> Iterator<? extends N> getAllApplyStatsOnTick(Class<? extends N> type);
+
+    @Override
+    public Stats getApplyStatsOnTick() {
+        Iterator<? extends StatsData> allStats = this.getAllApplyStatsOnTick(StatsData.class);
+        if( allStats.hasNext() )
+            return allStats.next();
+        else
+            return null;
+    }
+
+    @Adjacency(label = "ApplyStatsOnTick", direction = Direction.OUT)
+    public abstract StatsData addApplyStatsOnTick(StatsData stats);
+
+    @Adjacency(label = "ApplyStatsOnTick", direction = Direction.OUT)
+    public abstract void removeApplyStatsOnTick(StatsData stats);
+
+    @Override
+    public void setApplyStatsOnTick(Stats stats) {
+        Iterator<? extends StatsData> existingAll = this.getAllApplyStatsOnTick(StatsData.class);
+        if( existingAll != null ) {
+            while( existingAll.hasNext() ) {
+                StatsData existing = existingAll.next();
+                this.removeApplyStatsOnTick(existing);
+                existing.remove();
+            }
+
+        }
+
+        if( stats == null ) {
+            this.addApplyStatsOnTick(this.createStats());
+            return;
+        }
+
+        StatsData statsData;
+        if( stats instanceof StatsData ) {
+            this.addApplyStatsOnTick((StatsData) stats);
+        }
+        else {
+            StatsData createdData = this.createStats();
+            try {
+                PropertyUtils.copyProperties(createdData, stats);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalStateException("Could not copy properties");
+            }
+            this.addApplyStatsOnTick(createdData);
+        }
+    }
+
+    @Adjacency(label = "DurationStats", direction = Direction.OUT)
+    public abstract <N extends StatsData> Iterator<? extends N> getAllDurationStats(Class<? extends N> type);
+
+    @Override
+    public Stats getDurationStats() {
+        Iterator<? extends StatsData> allStats = this.getAllDurationStats(StatsData.class);
+        if( allStats.hasNext() )
+            return allStats.next();
+        else
+            return null;
+    }
+
+    @Adjacency(label = "DurationStats", direction = Direction.OUT)
+    public abstract StatsData addDurationStats(StatsData stats);
+
+    @Adjacency(label = "DurationStats", direction = Direction.OUT)
+    public abstract void removeDurationStats(StatsData stats);
+
+    @Override
+    public void setDurationStats(Stats stats) {
+        Iterator<? extends StatsData> existingAll = this.getAllDurationStats(StatsData.class);
+        if( existingAll != null ) {
+            while( existingAll.hasNext() ) {
+                StatsData existing = existingAll.next();
+                this.removeDurationStats(existing);
+                existing.remove();
+            }
+
+        }
+
+        if( stats == null ) {
+            this.addApplyStatsOnTick(this.createStats());
+            return;
+        }
+
+        StatsData statsData;
+        if( stats instanceof StatsData ) {
+            this.addDurationStats((StatsData) stats);
+        }
+        else {
+            StatsData createdData = this.createStats();
+            try {
+                PropertyUtils.copyProperties(createdData, stats);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalStateException("Could not copy properties");
+            }
+            this.addDurationStats(createdData);
+        }
+    }
+
+    private StatsData createStats() {
+        if( this.getDurationStats() != null )
+            throw new IllegalStateException("Already has stats, can't create another");
+        final StatsData stats = this.getGraph().addFramedVertex(StatsData.class);
+        stats.setAgile(0);
+        stats.setAim(0);
+        stats.setArmorRating(0);
+        stats.setCurrentHealth(0);
+        stats.setCurrentMana(0);
+        stats.setExperience(0);
+        stats.setForaging(0);
+        stats.setIntelligence(0);
+        stats.setInventorySize(0);
+        stats.setMaxEffects(0);
+        stats.setMaxHealth(0);
+        stats.setMaxMana(0);
+        stats.setMeleeSkill(0);
+        stats.setNumberOfWeaponRolls(0);
+        stats.setStrength(0);
+        stats.setWeaponRatingMax(0);
+        stats.setWeaponRatingMin(0);
+        stats.setWillpower(0);
+        return stats;
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java
index 00e64d87..5366297b 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java
@@ -19,21 +19,17 @@ import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.items.*;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.Property;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.tinkerpop.gremlin.structure.Direction;
 
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
 public abstract class ItemData extends AbstractVertexFrame implements Item {
-    @Override
-    @Property("Stats")
-    public abstract Stats getItemApplyStats();
-
-    @Override
-    public void setItemApplyStats(Stats itemApplyStats) {
-        this.traverse((v) -> v.property("Stats", itemApplyStats));
-    }
-
     @Override
     @Property("ValidTimeOfDays")
     public abstract List<TimeTracker.TimeOfDay> getValidTimeOfDays();
@@ -132,11 +128,11 @@ public abstract class ItemData extends AbstractVertexFrame implements Item {
 
     @Override
     @Property("Effects")
-    public abstract void setEffects(Set<Effect> effects);
+    public abstract void setEffects(Set<EffectPojo> effects);
 
     @Override
     @Property("Effects")
-    public abstract Set<Effect> getEffects();
+    public abstract Set<EffectPojo> getEffects();
 
     @Override
     @Property("ItemName")
@@ -182,4 +178,77 @@ public abstract class ItemData extends AbstractVertexFrame implements Item {
     @Override
     @Property("HasBeenWithPlayer")
     public abstract boolean isHasBeenWithPlayer();
+
+    @Adjacency(label = "Stats", direction = Direction.OUT)
+    public abstract <N extends StatsData> Iterator<? extends N> getAllItemApplyStats(Class<? extends N> type);
+
+    public Stats getItemApplyStats() {
+        Iterator<? extends StatsData> allStats = this.getAllItemApplyStats(StatsData.class);
+        if( allStats.hasNext() )
+            return allStats.next();
+        else
+            return null;
+    }
+
+    @Adjacency(label = "Stats", direction = Direction.OUT)
+    public abstract StatsData addStats(StatsData stats);
+
+    @Adjacency(label = "Stats", direction = Direction.OUT)
+    public abstract void removeStats(StatsData stats);
+
+    public void setItemApplyStats(Stats stats) {
+        Iterator<? extends StatsData> existingAll = this.getAllItemApplyStats(StatsData.class);
+        if( existingAll != null ) {
+            while( existingAll.hasNext() ) {
+                StatsData existing = existingAll.next();
+                this.removeStats(existing);
+                existing.remove();
+            }
+
+        }
+
+        if( stats == null ) {
+            this.createItemApplyStats();
+            return;
+        }
+
+        StatsData statsData;
+        if( stats instanceof StatsData ) {
+            this.addStats((StatsData) stats);
+        }
+        else {
+            try {
+                PropertyUtils.copyProperties(this.createItemApplyStats(), stats);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalStateException("Could not copy properties");
+            }
+        }
+    }
+
+    public StatsData createItemApplyStats() {
+        if( this.getItemApplyStats() != null )
+            throw new IllegalStateException("Already has stats, can't create another");
+        final StatsData stats = this.getGraph().addFramedVertex(StatsData.class);
+        stats.setAgile(0);
+        stats.setAim(0);
+        stats.setArmorRating(0);
+        stats.setCurrentHealth(0);
+        stats.setCurrentMana(0);
+        stats.setExperience(0);
+        stats.setForaging(0);
+        stats.setIntelligence(0);
+        stats.setInventorySize(0);
+        stats.setMaxEffects(0);
+        stats.setMaxHealth(0);
+        stats.setMaxMana(0);
+        stats.setMeleeSkill(0);
+        stats.setNumberOfWeaponRolls(0);
+        stats.setStrength(0);
+        stats.setWeaponRatingMax(0);
+        stats.setWeaponRatingMin(0);
+        stats.setWillpower(0);
+        this.addStats(stats);
+        return stats;
+    }
+
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java
index dc8a1023..ce6ba2c8 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java
@@ -178,7 +178,7 @@ public abstract class NpcData extends AbstractVertexFrame implements Npc {
         stats.setWeaponRatingMax(0);
         stats.setWeaponRatingMin(0);
         stats.setWillpower(0);
-        this.setStats(stats);
+        this.addStats(stats);
         return stats;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java
index 165c1b83..b81cf640 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java
@@ -16,7 +16,7 @@
 package com.syncleus.aethermud.storage.graphdb;
 
 
-import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.player.*;
 import com.google.common.collect.Sets;
 import com.syncleus.ferma.AbstractVertexFrame;
@@ -97,10 +97,10 @@ public abstract class PlayerData extends AbstractVertexFrame {
     public abstract void setPlayerEquipment(List<String> playerEquipment);
 
     @Property("effects")
-    public abstract List<Effect> getEffects();
+    public abstract List<EffectPojo> getEffects();
 
     @Property("effects")
-    public abstract void setEffects(List<Effect> effects);
+    public abstract void setEffects(List<EffectPojo> effects);
 
     @Property("markedForDelete")
     public abstract boolean isMarkedForDelete();
@@ -272,7 +272,7 @@ public abstract class PlayerData extends AbstractVertexFrame {
         stats.setWeaponRatingMax(0);
         stats.setWeaponRatingMin(0);
         stats.setWillpower(0);
-        this.setStats(stats);
+        this.addStats(stats);
         return stats;
     }
 
@@ -353,8 +353,8 @@ public abstract class PlayerData extends AbstractVertexFrame {
         this.setPlayerEquipment(playerEquipment);
     }
 
-    public void addEffect(Effect effect) {
-        List<Effect> effects = this.getEffects();
+    public void addEffect(EffectPojo effect) {
+        List<EffectPojo> effects = this.getEffects();
         if (effects == null) {
             effects = new ArrayList<>();
         }
@@ -362,8 +362,8 @@ public abstract class PlayerData extends AbstractVertexFrame {
         this.setEffects(effects);
     }
 
-    public void removeEffect(Effect effect) {
-        List<Effect> effects = this.getEffects();
+    public void removeEffect(EffectPojo effect) {
+        List<EffectPojo> effects = this.getEffects();
         effects.remove(effect);
         this.setEffects(effects);
     }
-- 
GitLab