diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
index c151ddee4a92512471e06cd27dfd44ea1a4e5c45..2348e1519b983ac0b4448212ac6158e3dd6ba753 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
@@ -100,7 +100,7 @@ public class LoadNpcCommand extends Command {
                 NpcData npcData = storage.newNpcData();
                 try {
                     PropertyUtils.copyProperties(npcData, npc);
-                    PropertyUtils.copyProperties(npcData.createStats(), npc.getStats());
+                    PropertyUtils.copyProperties(npcData.createStatsData(), npc.getStats());
                     PropertyUtils.copyProperties(npcData.createLootData(), npc.getLoot());
                 } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException ex) {
                     throw new IllegalStateException("Could not copy properties for stats", ex);
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
index 0dae32f788e02af9b3e09088de7530158eb1e989..be2f7bf63ce3afdaafb063aee22fb381c7cee797 100644
--- a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
@@ -21,7 +21,6 @@ import com.syncleus.aethermud.npc.NpcStatsChange;
 import com.syncleus.aethermud.npc.NpcStatsChangeBuilder;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import com.syncleus.aethermud.stats.StatsBuilder;
@@ -97,7 +96,7 @@ public class EffectsManager {
 
     public void application(Effect effect, NpcSpawn npcSpawn) {
         Player player = gameManager.getPlayerManager().getPlayer(effect.getPlayerId());
-        Stats applyStats = new StatsPojo(effect.getApplyStatsOnTick());
+        Stats applyStats = new Stats(effect.getApplyStatsOnTick());
         // if there are effecst that modify npc health, deal with it here, you can't rely on combine stats.
         if (effect.getApplyStatsOnTick().getCurrentHealth() < 0) {
             String s = Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + npcSpawn.getColorName() + " is affected by " + effect.getEffectDescription() + " " + Color.RED + applyStats.getCurrentHealth() + Color.RESET + Color.CYAN + Color.RESET;
@@ -114,14 +113,8 @@ public class EffectsManager {
     }
 
     public void removeDurationStats(Effect effect, NpcSpawn npcSpawn) {
-        Stats newStats = new StatsPojo(effect.getDurationStats());
+        Stats newStats = new Stats(effect.getDurationStats());
         StatsHelper.inverseStats(newStats);
         StatsHelper.combineStats(npcSpawn.getStats(), newStats);
     }
-
-    public void removeDurationStats(Effect 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/Equipment.java b/src/main/java/com/syncleus/aethermud/items/Equipment.java
index 095e1050a0e2c57025be8750a050c41a5de2af17..062c2c459d478ab240bde5330bfa7b015e39b0b7 100644
--- a/src/main/java/com/syncleus/aethermud/items/Equipment.java
+++ b/src/main/java/com/syncleus/aethermud/items/Equipment.java
@@ -16,14 +16,14 @@
 package com.syncleus.aethermud.items;
 
 
-import com.syncleus.aethermud.stats.StatsPojo;
+import com.syncleus.aethermud.stats.Stats;
 
 public class Equipment {
 
     private final EquipmentSlotType equipmentSlotType;
-    private final StatsPojo statsIncreaseWhileEquipped;
+    private final Stats statsIncreaseWhileEquipped;
 
-    public Equipment(EquipmentSlotType equipmentSlotType, StatsPojo statsIncreaseWhileEquipped) {
+    public Equipment(EquipmentSlotType equipmentSlotType, Stats statsIncreaseWhileEquipped) {
         this.equipmentSlotType = equipmentSlotType;
         this.statsIncreaseWhileEquipped = statsIncreaseWhileEquipped;
     }
@@ -37,7 +37,7 @@ public class Equipment {
         return equipmentSlotType;
     }
 
-    public StatsPojo getStats() {
+    public Stats getStats() {
         return statsIncreaseWhileEquipped;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
index 25d723672bf0833167633139a50f39ba0bf44ad8..f7b44291ca97083e311b7b5c97f079ac4a36c911 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
@@ -186,7 +186,7 @@ public class ItemBuilder {
         return this;
     }
 
-    public ItemBuilder itemApplyStats(StatsData itemApplyStats) {
+    public ItemBuilder itemApplyStats(Stats itemApplyStats) {
         this.itemApplyStats = itemApplyStats;
         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 a151770694d9165b36e3d224bc9788cd79a1e78d..375e2b298c13290c597ae720260f9377193a9432 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
@@ -18,7 +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.StatsPojo;
+import com.syncleus.aethermud.stats.Stats;
 import com.google.common.collect.Sets;
 
 import java.util.List;
@@ -45,7 +45,7 @@ public class ItemMetadata {
     private boolean isDisposable;
     private int maxUses;
     private Set<SpawnRule> spawnRules;
-    private StatsPojo itemApplyStats;
+    private Stats itemApplyStats;
     private Set<Forage> forages;
 
     public Set<Forage> getForages() {
@@ -59,11 +59,11 @@ public class ItemMetadata {
         this.forages = forages;
     }
 
-    public StatsPojo getItemApplyStats() {
+    public Stats getItemApplyStats() {
         return itemApplyStats;
     }
 
-    public void setItemApplyStats(StatsPojo itemApplyStats) {
+    public void setItemApplyStats(Stats itemApplyStats) {
         this.itemApplyStats = itemApplyStats;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/Loot.java b/src/main/java/com/syncleus/aethermud/items/Loot.java
index 85a4dbf5216a17dc0b241daa27b7c89840849303..3c07efa919be89272bde037b0000aaef84d7c7bc 100644
--- a/src/main/java/com/syncleus/aethermud/items/Loot.java
+++ b/src/main/java/com/syncleus/aethermud/items/Loot.java
@@ -65,6 +65,6 @@ public class Loot {
     }
 
     public void setInternalItemNames(List<String> internalItemNames) {
-        this.internalItemNames = internalItemNames;
+        this.internalItemNames = Lists.newArrayList(internalItemNames);
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/npc/Npc.java b/src/main/java/com/syncleus/aethermud/npc/Npc.java
index faa3242e64d7bb97bdbf6ff7b684b1580cb15eb1..642d41ed5b9a7f4d81cad79024a9a88211608dd8 100644
--- a/src/main/java/com/syncleus/aethermud/npc/Npc.java
+++ b/src/main/java/com/syncleus/aethermud/npc/Npc.java
@@ -19,7 +19,6 @@ import com.syncleus.aethermud.common.AetherMudMessage;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.aethermud.world.model.Area;
 
 import java.util.List;
@@ -28,7 +27,7 @@ public class Npc {
 
     private String name;
     private String colorName;
-    private StatsPojo stats;
+    private Stats stats;
     private String dieMessage;
     private Temperament temperament;
     private List<Area> roamAreas;
@@ -100,9 +99,9 @@ public class Npc {
     }
 
     public void setStats(Stats stats) {
-        if( ! (stats instanceof StatsPojo) )
+        if( ! (stats instanceof Stats) )
             throw new IllegalStateException("not a pojo");
-        this.stats = (StatsPojo) stats;
+        this.stats = (Stats) stats;
     }
 
     public String getDieMessage() {
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java b/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
index 25a8b90b486e39ac7f38fa67878e8ad251f35a7a..e9c1513eaa216d26f853554d5e4b6b59f4fbce87 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
@@ -21,7 +21,6 @@ import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.aethermud.storage.graphdb.model.StatsData;
 import com.syncleus.aethermud.storage.graphdb.model.NpcData;
 import com.syncleus.aethermud.world.model.Area;
@@ -62,7 +61,7 @@ public class NpcBuilder {
         this.name = npcSpawn.getName();
         this.colorName = npcSpawn.getColorName();
         this.lastPhraseTimestamp = npcSpawn.getLastPhraseTimestamp();
-        this.stats = new StatsPojo(npcSpawn.getStats());
+        this.stats = new Stats(npcSpawn.getStats());
         this.dieMessage = npcSpawn.getDieMessage();
         this.roamAreas = npcSpawn.getRoamAreas();
         this.validTriggers = npcSpawn.getValidTriggers();
@@ -76,22 +75,22 @@ public class NpcBuilder {
         this.idleMessages = npcSpawn.getIdleMessages();
     }
 
-    public NpcBuilder(NpcData npcData) {
-        this.name = npcData.getName();
-        this.colorName = npcData.getColorName();
-        this.stats = new StatsPojo(npcData.getStats());
-        this.dieMessage = npcData.getDieMessage();
-        this.roamAreas = new HashSet<>(npcData.getRoamAreas());
-        this.validTriggers = Sets.newHashSet(npcData.getValidTriggers());
-        this.spawnRules = npcData.getSpawnRulesData().stream().map((d) -> new SpawnRule(d.getArea(), d.getSpawnIntervalTicks(), d.getMaxInstances(), d.getMaxPerRoom(), d.getRandomChance())).collect(Collectors.toSet());
-        this.temperament = npcData.getTemperament();
-        this.attackMessages = Sets.newHashSet(npcData.getAttackMessages());
-        this.criticalAttackMessages = Sets.newHashSet(npcData.getCriticalAttackMessages());
-        this.battleMessages = Sets.newHashSet(npcData.getBattleMessages());
-        this.idleMessages = Sets.newHashSet(npcData.getIdleMessages());
+    public NpcBuilder(Npc npc) {
+        this.name = npc.getName();
+        this.colorName = npc.getColorName();
+        this.stats = new Stats(npc.getStats());
+        this.dieMessage = npc.getDieMessage();
+        this.roamAreas = new HashSet<>(npc.getRoamAreas());
+        this.validTriggers = Sets.newHashSet(npc.getValidTriggers());
+        this.spawnRules = Sets.newHashSet(npc.getSpawnRules());
+        this.temperament = npc.getTemperament();
+        this.attackMessages = Sets.newHashSet(npc.getAttackMessages());
+        this.criticalAttackMessages = Sets.newHashSet(npc.getCriticalAttackMessages());
+        this.battleMessages = Sets.newHashSet(npc.getBattleMessages());
+        this.idleMessages = Sets.newHashSet(npc.getIdleMessages());
         this.loot = new Loot();
         try {
-            PropertyUtils.copyProperties(this.loot, npcData.getLootData());
+            PropertyUtils.copyProperties(this.loot, npc.getLoot());
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("Could not copy properties");
         }
@@ -117,7 +116,7 @@ public class NpcBuilder {
         return this;
     }
 
-    public NpcBuilder setStats(StatsData stats) {
+    public NpcBuilder setStats(Stats stats) {
         this.stats = stats;
         return this;
     }
diff --git a/src/main/java/com/syncleus/aethermud/player/Player.java b/src/main/java/com/syncleus/aethermud/player/Player.java
index 213cba313ae8d1225e722281c731033b79e0fda3..6bdbb9a57f1562d0dfba2567ad3aca6a8f1026e9 100644
--- a/src/main/java/com/syncleus/aethermud/player/Player.java
+++ b/src/main/java/com/syncleus/aethermud/player/Player.java
@@ -1259,7 +1259,7 @@ public class Player extends AetherMudEntity {
         return this.transactRead(playerData -> new HashSet<PlayerRole>(playerData.getPlayerRoles()));
     }
 
-    public StatsPojo getStats() {
+    public Stats getStats() {
         return this.transactRead(playerData -> StatsData.copyStats(playerData.getStats()));
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/stats/Stats.java b/src/main/java/com/syncleus/aethermud/stats/Stats.java
index 938d3a2ef15e2e72ad9748059686c0b47b988682..1761ab00dfb2ce502ac37ede8fcbfc24dd96d140 100644
--- a/src/main/java/com/syncleus/aethermud/stats/Stats.java
+++ b/src/main/java/com/syncleus/aethermud/stats/Stats.java
@@ -17,80 +17,235 @@ package com.syncleus.aethermud.stats;
 
 import static java.lang.StrictMath.sqrt;
 
-public interface Stats {
-    Integer getIntelligence();
+public class Stats {
+    private Integer strength;
+    private Integer intelligence;
+    private Integer willpower;
+    private Integer aim;
+    private Integer agile;
+    private Integer armorRating;
+    private Integer meleeSkill;
+    private Integer currentHealth;
+    private Integer maxHealth;
+    private Integer weaponRatingMax;
+    private Integer weaponRatingMin;
+    private Integer numberOfWeaponRolls;
+    private Integer experience;
+    private Integer currentMana;
+    private Integer maxMana;
+    private Integer foraging;
+    private Integer inventorySize;
+    private Integer maxEffects;
+
+    public Stats() {
+
+    }
+
+    public Stats(Stats stats) {
+        this.strength = ( stats == null ? 0 : stats.getStrength());
+        this.intelligence = ( stats == null ? 0 : stats.getIntelligence());
+        this.willpower = ( stats == null ? 0 : stats.getWillpower());
+        this.aim = ( stats == null ? 0 : stats.getAim());
+        this.agile = ( stats == null ? 0 : stats.getAgile());
+        this.armorRating = ( stats == null ? 0 : stats.getArmorRating());
+        this.meleeSkill = ( stats == null ? 0 : stats.getMeleeSkill());
+        this.currentHealth = ( stats == null ? 0 : stats.getCurrentHealth());
+        this.maxHealth = ( stats == null ? 0 : stats.getMaxHealth());
+        this.weaponRatingMax = ( stats == null ? 0 : stats.getWeaponRatingMax());
+        this.weaponRatingMin = ( stats == null ? 0 : stats.getWeaponRatingMin());
+        this.numberOfWeaponRolls = ( stats == null ? 0 : stats.getNumberOfWeaponRolls());
+        this.experience = ( stats == null ? 0 : stats.getExperience());
+        this.currentMana = ( stats == null ? 0 : stats.getCurrentMana());
+        this.foraging = ( stats == null ? 0 : stats.getForaging());
+        this.maxMana = ( stats == null ? 0 : stats.getMaxMana());
+        this.inventorySize = ( stats == null ? 0 : stats.getInventorySize());
+        this.maxEffects = ( stats == null ? 0 : stats.getMaxEffects());
+    }
+
+    public Stats(Integer strength,
+                 Integer intelligence,
+                 Integer willpower,
+                 Integer aim,
+                 Integer agile,
+                 Integer armorRating,
+                 Integer meleeSkill,
+                 Integer currentHealth,
+                 Integer maxHealth,
+                 Integer weaponRatingMax,
+                 Integer weaponRatingMin,
+                 Integer numberOfWeaponRolls,
+                 Integer experience,
+                 Integer currentMana,
+                 Integer maxMana,
+                 Integer foraging,
+                 Integer inventorySize,
+                 Integer maxEffects) {
+        this.strength = strength;
+        this.intelligence = intelligence;
+        this.willpower = willpower;
+        this.aim = aim;
+        this.agile = agile;
+        this.armorRating = armorRating;
+        this.meleeSkill = meleeSkill;
+        this.currentHealth = currentHealth;
+        this.maxHealth = maxHealth;
+        this.weaponRatingMax = weaponRatingMax;
+        this.weaponRatingMin = weaponRatingMin;
+        this.numberOfWeaponRolls = numberOfWeaponRolls;
+        this.experience = experience;
+        this.currentMana = currentMana;
+        this.maxMana = maxMana;
+        this.foraging = foraging;
+        this.inventorySize = inventorySize;
+        this.maxEffects = maxEffects;
+    }
 
-    void setIntelligence(Integer Integerelligence);
 
-    Integer getMaxEffects();
+    public Integer getIntelligence() {
+        return intelligence;
+    }
+
+    public void setIntelligence(Integer intelligence) {
+        this.intelligence = intelligence;
+    }
+
+    public Integer getMaxEffects() {
+        return maxEffects;
+    }
 
-    void setMaxEffects(Integer maxEffects);
+    public void setMaxEffects(Integer maxEffects) {
+        this.maxEffects = maxEffects;
+    }
 
-    Integer getExperience();
+    public Integer getExperience() {
+        return experience;
+    }
 
-    void setExperience(Integer experience);
+    public void setExperience(Integer experience) {
+        this.experience = experience;
+    }
 
-    Integer getStrength();
+    public Integer getStrength() {
+        return strength;
+    }
 
-    void setStrength(Integer strength);
+    public void setStrength(Integer strength) {
+        this.strength = strength;
+    }
 
-    Integer getWillpower();
+    public Integer getWillpower() {
+        return willpower;
+    }
 
-    void setWillpower(Integer willpower);
+    public void setWillpower(Integer willpower) {
+        this.willpower = willpower;
+    }
 
-    Integer getAim();
+    public Integer getAim() {
+        return aim;
+    }
 
-    void setAim(Integer aim);
+    public void setAim(Integer aim) {
+        this.aim = aim;
+    }
 
-    Integer getAgile();
+    public Integer getAgile() {
+        return agile;
+    }
 
-    void setAgile(Integer agile);
+    public void setAgile(Integer agile) {
+        this.agile = agile;
+    }
 
-    Integer getArmorRating();
+    public Integer getArmorRating() {
+        return armorRating;
+    }
 
-    void setArmorRating(Integer armorRating);
+    public void setArmorRating(Integer armorRating) {
+        this.armorRating = armorRating;
+    }
 
-    Integer getMeleeSkill();
+    public Integer getMeleeSkill() {
+        return meleeSkill;
+    }
 
-    void setMeleeSkill(Integer meleSkill);
+    public void setMeleeSkill(Integer meleSkill) {
+        this.meleeSkill = meleSkill;
+    }
 
-    Integer getCurrentHealth();
+    public Integer getCurrentHealth() {
+        return currentHealth;
+    }
 
-    void setCurrentHealth(Integer currentHealth);
+    public void setCurrentHealth(Integer currentHealth) {
+        this.currentHealth = currentHealth;
+    }
 
-    Integer getMaxHealth();
+    public Integer getMaxHealth() {
+        return maxHealth;
+    }
 
-    void setMaxHealth(Integer maxHealth);
+    public void setMaxHealth(Integer maxHealth) {
+        this.maxHealth = maxHealth;
+    }
 
-    Integer getWeaponRatingMax();
+    public Integer getWeaponRatingMax() {
+        return weaponRatingMax;
+    }
 
-    void setWeaponRatingMax(Integer weaponRatingMax);
+    public void setWeaponRatingMax(Integer weaponRatingMax) {
+        this.weaponRatingMax = weaponRatingMax;
+    }
 
-    Integer getWeaponRatingMin();
+    public Integer getWeaponRatingMin() {
+        return weaponRatingMin;
+    }
 
-    void setWeaponRatingMin(Integer weaponRatingMin);
+    public void setWeaponRatingMin(Integer weaponRatingMin) {
+        this.weaponRatingMin = weaponRatingMin;
+    }
 
-    Integer getNumberOfWeaponRolls();
+    public Integer getNumberOfWeaponRolls() {
+        return numberOfWeaponRolls;
+    }
 
-    void setNumberOfWeaponRolls(Integer numberOfWeaponRolls);
+    public void setNumberOfWeaponRolls(Integer numberOfWeaponRolls) {
+        this.numberOfWeaponRolls = numberOfWeaponRolls;
+    }
 
-    Integer getCurrentMana();
+    public Integer getCurrentMana() {
+        return currentMana;
+    }
 
-    Integer getMaxMana();
+    public Integer getMaxMana() {
+        return maxMana;
+    }
 
-    void setCurrentMana(Integer currentMana);
+    public void setCurrentMana(Integer currentMana) {
+        this.currentMana = currentMana;
+    }
 
-    void setMaxMana(Integer maxMana);
+    public void setMaxMana(Integer maxMana) {
+        this.maxMana = maxMana;
+    }
 
-    Integer getForaging();
+    public Integer getForaging() {
+        return foraging;
+    }
 
-    void setForaging(Integer foraging);
+    public void setForaging(Integer foraging) {
+        this.foraging = foraging;
+    }
 
-    Integer getInventorySize();
+    public Integer getInventorySize() {
+        return inventorySize;
+    }
 
-    void setInventorySize(Integer inventorySize);
+    public void setInventorySize(Integer inventorySize) {
+        this.inventorySize = inventorySize;
+    }
 
-    default Integer getLevel() {
+    public Integer getLevel() {
         double v = 0.02 * sqrt(getExperience());
         return Double.valueOf(Math.floor(v)).intValue();
     }
diff --git a/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java b/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
index 7ec8007c06296a3e102f1c06a7d5fc4895cf8510..9c3818a0c78b1560df1b943fbbff9b2b8c843c46 100644
--- a/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
@@ -150,6 +150,6 @@ public class StatsBuilder {
     }
 
     public Stats createStats() {
-        return new StatsPojo(strength, intelligence, willpower, aim, agile, armorRating, meleSkill, currentHealth, maxHealth, weaponRatingMax, weaponRatingMin, numberOfWeaponRolls, experience, currentMana, maxMana, foraging, inventorySize, maxEffects);
+        return new Stats(strength, intelligence, willpower, aim, agile, armorRating, meleSkill, currentHealth, maxHealth, weaponRatingMax, weaponRatingMin, numberOfWeaponRolls, experience, currentMana, maxMana, foraging, inventorySize, maxEffects);
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java b/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java
deleted file mode 100644
index 0de25e4b483a1baf2de502ac1d456f16077ddce5..0000000000000000000000000000000000000000
--- a/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/**
- * 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.stats;
-
-public class StatsPojo implements Stats {
-    private Integer strength;
-    private Integer intelligence;
-    private Integer willpower;
-    private Integer aim;
-    private Integer agile;
-    private Integer armorRating;
-    private Integer meleeSkill;
-    private Integer currentHealth;
-    private Integer maxHealth;
-    private Integer weaponRatingMax;
-    private Integer weaponRatingMin;
-    private Integer numberOfWeaponRolls;
-    private Integer experience;
-    private Integer currentMana;
-    private Integer maxMana;
-    private Integer foraging;
-    private Integer inventorySize;
-    private Integer maxEffects;
-
-    public StatsPojo() {
-
-    }
-
-    public StatsPojo(Stats stats) {
-        this.strength = ( stats == null ? 0 : stats.getStrength());
-        this.intelligence = ( stats == null ? 0 : stats.getIntelligence());
-        this.willpower = ( stats == null ? 0 : stats.getWillpower());
-        this.aim = ( stats == null ? 0 : stats.getAim());
-        this.agile = ( stats == null ? 0 : stats.getAgile());
-        this.armorRating = ( stats == null ? 0 : stats.getArmorRating());
-        this.meleeSkill = ( stats == null ? 0 : stats.getMeleeSkill());
-        this.currentHealth = ( stats == null ? 0 : stats.getCurrentHealth());
-        this.maxHealth = ( stats == null ? 0 : stats.getMaxHealth());
-        this.weaponRatingMax = ( stats == null ? 0 : stats.getWeaponRatingMax());
-        this.weaponRatingMin = ( stats == null ? 0 : stats.getWeaponRatingMin());
-        this.numberOfWeaponRolls = ( stats == null ? 0 : stats.getNumberOfWeaponRolls());
-        this.experience = ( stats == null ? 0 : stats.getExperience());
-        this.currentMana = ( stats == null ? 0 : stats.getCurrentMana());
-        this.foraging = ( stats == null ? 0 : stats.getForaging());
-        this.maxMana = ( stats == null ? 0 : stats.getMaxMana());
-        this.inventorySize = ( stats == null ? 0 : stats.getInventorySize());
-        this.maxEffects = ( stats == null ? 0 : stats.getMaxEffects());
-    }
-
-    public StatsPojo(Integer strength,
-                     Integer intelligence,
-                     Integer willpower,
-                     Integer aim,
-                     Integer agile,
-                     Integer armorRating,
-                     Integer meleeSkill,
-                     Integer currentHealth,
-                     Integer maxHealth,
-                     Integer weaponRatingMax,
-                     Integer weaponRatingMin,
-                     Integer numberOfWeaponRolls,
-                     Integer experience,
-                     Integer currentMana,
-                     Integer maxMana,
-                     Integer foraging,
-                     Integer inventorySize,
-                     Integer maxEffects) {
-        this.strength = strength;
-        this.intelligence = intelligence;
-        this.willpower = willpower;
-        this.aim = aim;
-        this.agile = agile;
-        this.armorRating = armorRating;
-        this.meleeSkill = meleeSkill;
-        this.currentHealth = currentHealth;
-        this.maxHealth = maxHealth;
-        this.weaponRatingMax = weaponRatingMax;
-        this.weaponRatingMin = weaponRatingMin;
-        this.numberOfWeaponRolls = numberOfWeaponRolls;
-        this.experience = experience;
-        this.currentMana = currentMana;
-        this.maxMana = maxMana;
-        this.foraging = foraging;
-        this.inventorySize = inventorySize;
-        this.maxEffects = maxEffects;
-    }
-
-
-    public Integer getIntelligence() {
-        return intelligence;
-    }
-
-    public void setIntelligence(Integer intelligence) {
-        this.intelligence = intelligence;
-    }
-
-    public Integer getMaxEffects() {
-        return maxEffects;
-    }
-
-    public void setMaxEffects(Integer maxEffects) {
-        this.maxEffects = maxEffects;
-    }
-
-    public Integer getExperience() {
-        return experience;
-    }
-
-    public void setExperience(Integer experience) {
-        this.experience = experience;
-    }
-
-    public Integer getStrength() {
-        return strength;
-    }
-
-    public void setStrength(Integer strength) {
-        this.strength = strength;
-    }
-
-    public Integer getWillpower() {
-        return willpower;
-    }
-
-    public void setWillpower(Integer willpower) {
-        this.willpower = willpower;
-    }
-
-    public Integer getAim() {
-        return aim;
-    }
-
-    public void setAim(Integer aim) {
-        this.aim = aim;
-    }
-
-    public Integer getAgile() {
-        return agile;
-    }
-
-    public void setAgile(Integer agile) {
-        this.agile = agile;
-    }
-
-    public Integer getArmorRating() {
-        return armorRating;
-    }
-
-    public void setArmorRating(Integer armorRating) {
-        this.armorRating = armorRating;
-    }
-
-    public Integer getMeleeSkill() {
-        return meleeSkill;
-    }
-
-    public void setMeleeSkill(Integer meleSkill) {
-        this.meleeSkill = meleSkill;
-    }
-
-    public Integer getCurrentHealth() {
-        return currentHealth;
-    }
-
-    public void setCurrentHealth(Integer currentHealth) {
-        this.currentHealth = currentHealth;
-    }
-
-    public Integer getMaxHealth() {
-        return maxHealth;
-    }
-
-    public void setMaxHealth(Integer maxHealth) {
-        this.maxHealth = maxHealth;
-    }
-
-    public Integer getWeaponRatingMax() {
-        return weaponRatingMax;
-    }
-
-    public void setWeaponRatingMax(Integer weaponRatingMax) {
-        this.weaponRatingMax = weaponRatingMax;
-    }
-
-    public Integer getWeaponRatingMin() {
-        return weaponRatingMin;
-    }
-
-    public void setWeaponRatingMin(Integer weaponRatingMin) {
-        this.weaponRatingMin = weaponRatingMin;
-    }
-
-    public Integer getNumberOfWeaponRolls() {
-        return numberOfWeaponRolls;
-    }
-
-    public void setNumberOfWeaponRolls(Integer numberOfWeaponRolls) {
-        this.numberOfWeaponRolls = numberOfWeaponRolls;
-    }
-
-    public Integer getCurrentMana() {
-        return currentMana;
-    }
-
-    public Integer getMaxMana() {
-        return maxMana;
-    }
-
-    public void setCurrentMana(Integer currentMana) {
-        this.currentMana = currentMana;
-    }
-
-    public void setMaxMana(Integer maxMana) {
-        this.maxMana = maxMana;
-    }
-
-    public Integer getForaging() {
-        return foraging;
-    }
-
-    public void setForaging(Integer foraging) {
-        this.foraging = foraging;
-    }
-
-    public Integer getInventorySize() {
-        return inventorySize;
-    }
-
-    public void setInventorySize(Integer inventorySize) {
-        this.inventorySize = inventorySize;
-    }
-}
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
index 3a7931e1cec0000928d0417b38a2b6d68fe9d0bc..fad669dd4dd723617d95ca7d8bbdf323c0ffba52 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
@@ -87,7 +87,7 @@ public class BasicPlayerLevelStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        StatsPojo baseStats = player.getStats();
+        Stats baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
index d3f6a90a8983a3bd59bd9eecd0dcfd2f010c107b..c6940897fa13c09f82b7ceb17b4d10068fe1fb4c 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
@@ -87,7 +87,7 @@ public class RangerStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        StatsPojo baseStats = player.getStats();
+        Stats baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
index 2022d4ffc674ceac7219baf4e8e5d07ff62c623c..8b7014341a96d2a4565dd932b48f22d6d1afed39 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
@@ -87,7 +87,7 @@ public class ShamanStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        StatsPojo baseStats = player.getStats();
+        Stats baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
index a230aa137d2234f1a425ba1ca2737faa6e455ca9..f84ef7a928746fb44f704677e9e53cf65296d109 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
@@ -87,7 +87,7 @@ public class WarriorStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        StatsPojo baseStats = player.getStats();
+        Stats baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
index 48406ba41b4165d2df4cf946d62c20868c0fc892..038d5d66140eaca4d1ce26b786236bf044d3ebbf 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
@@ -88,7 +88,7 @@ public class WizardStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        StatsPojo baseStats = player.getStats();
+        Stats baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
index c1c62ae20e752262c927b32a92001291d79d0860..b9ff5871334b05606a79d4bbe364efd614056cdc 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
@@ -88,9 +88,9 @@ public class GraphDbAetherMudStorage implements AetherMudStorage {
     }
 
     public List<? extends NpcSpawn> getAllNpcs(GameManager gameManager) {
-        List<? extends NpcData> npcData = this.getNpcDatas();
-        return npcData.stream()
-            .map(metadata -> new NpcBuilder(metadata).setGameManager(gameManager).createNpc())
+        List<? extends NpcData> npcDatas = this.getNpcDatas();
+        return npcDatas.stream()
+            .map(npcData -> new NpcBuilder(NpcData.copyNpc(npcData)).setGameManager(gameManager).createNpc())
             .collect(Collectors.toList());
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
index 0c6ddc8b697416198c86fb199123c497c135e322..c7c281ebd88a79a5d4bc741eae62c71db028c4b4 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
@@ -17,7 +17,6 @@ package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
@@ -79,7 +78,7 @@ public abstract class EffectData extends AbstractInterceptingVertexFrame {
     public Stats getApplyStatsOnTick() {
         Iterator<? extends StatsData> allStats = this.getAllApplyStatsOnTick(StatsData.class);
         if (allStats.hasNext())
-            return allStats.next();
+            return StatsData.copyStats(allStats.next());
         else
             return null;
     }
@@ -100,27 +99,23 @@ public abstract class EffectData extends AbstractInterceptingVertexFrame {
             }
         }
         if (stats == null) {
-            this.addApplyStatsOnTick(this.createStats());
+            this.addApplyStatsOnTick(this.createOrphanStats());
             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);
+
+        StatsData createdData = this.createOrphanStats();
+        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);
 
-    public Stats getDurationStats() {
+    public StatsData getDurationStats() {
         Iterator<? extends StatsData> allStats = this.getAllDurationStats(StatsData.class);
         if (allStats.hasNext())
             return allStats.next();
@@ -144,24 +139,20 @@ public abstract class EffectData extends AbstractInterceptingVertexFrame {
             }
         }
         if (stats == null) {
-            this.addApplyStatsOnTick(this.createStats());
+            this.addApplyStatsOnTick(this.createOrphanStats());
             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);
+
+        StatsData createdData = this.createOrphanStats();
+        try {
+            PropertyUtils.copyProperties(createdData, stats);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
         }
+        this.addDurationStats(createdData);
     }
 
-    private StatsData createStats() {
+    private StatsData createOrphanStats() {
         if (this.getDurationStats() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final StatsData stats = this.getGraph().addFramedVertex(StatsData.class);
@@ -200,10 +191,10 @@ public abstract class EffectData extends AbstractInterceptingVertexFrame {
         Effect retVal = new Effect();
         try {
             PropertyUtils.copyProperties(retVal, src);
-            StatsPojo durationStats = new StatsPojo();
+            Stats durationStats = new Stats();
             PropertyUtils.copyProperties(durationStats, src.getDurationStats());
             retVal.setDurationStats(durationStats);
-            StatsPojo applyStats = new StatsPojo();
+            Stats applyStats = new Stats();
             PropertyUtils.copyProperties(applyStats, src.getApplyStatsOnTick());
             retVal.setApplyStatsOnTick(applyStats);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
index 73cf50c44b6ec290e1655b52dbdfbaae796f772d..09f481ffe5307baf731da2e22c1b55de5e2b003f 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
@@ -230,7 +230,7 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame implement
     public Stats getItemApplyStats() {
         Iterator<? extends StatsData> allStats = this.getAllItemApplyStats(StatsData.class);
         if( allStats.hasNext() )
-            return allStats.next();
+            return StatsData.copyStats(allStats.next());
         else
             return null;
     }
@@ -257,15 +257,10 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame implement
             return;
         }
 
-        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");
-            }
+        try {
+            PropertyUtils.copyProperties(this.createItemApplyStats(), stats);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
         }
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
index 419c6c2e5cbcdf253c2a65413c4b325572ef65b0..ff663b5e6a95385a97d0dfb9a5b6ad4295274cfe 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
@@ -15,11 +15,16 @@
  */
 package com.syncleus.aethermud.storage.graphdb.model;
 
+import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.items.Loot;
+import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
 import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
+import org.apache.commons.beanutils.PropertyUtils;
 
+import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
 @GraphElement
@@ -41,4 +46,22 @@ public abstract class LootData extends AbstractInterceptingVertexFrame {
 
     @Property("GoldMin")
     public abstract void setLootGoldMin(int lootGoldMin);
+
+    public static void copyLoot(LootData dest, Loot src) {
+        try {
+            PropertyUtils.copyProperties(dest, src);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+    }
+
+    public static Loot copyLoot(LootData src) {
+        Loot retVal = new Loot();
+        try {
+            PropertyUtils.copyProperties(retVal, src);;
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+        return retVal;
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
index e1274dfdf3a83d1b60c360406ab0dc73e4e48e18..2d0c077ea66513d866e3c32af589250581850809 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
@@ -18,12 +18,13 @@ package com.syncleus.aethermud.storage.graphdb.model;
 import com.google.common.collect.Lists;
 import com.syncleus.aethermud.common.AetherMudMessage;
 import com.syncleus.aethermud.common.ColorizedTextTemplate;
+import com.syncleus.aethermud.items.Effect;
+import com.syncleus.aethermud.npc.Npc;
 import com.syncleus.aethermud.npc.Temperament;
+import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.storage.graphdb.DataUtils;
 import com.syncleus.aethermud.world.model.Area;
-import com.syncleus.ferma.AbstractVertexFrame;
-import com.syncleus.ferma.ElementFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
@@ -32,13 +33,10 @@ import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
 
 @GraphElement
 public abstract class NpcData extends AbstractInterceptingVertexFrame {
@@ -162,10 +160,10 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     }
 
     @Adjacency(label = "stats", direction = Direction.OUT)
-    public abstract <N extends StatsData> Iterator<? extends N> getAllStats(Class<? extends N> type);
+    public abstract <N extends StatsData> Iterator<? extends N> getAllStatsData(Class<? extends N> type);
 
-    public StatsData getStats() {
-        Iterator<? extends StatsData> allStats = this.getAllStats(StatsData.class);
+    public StatsData getStatsData() {
+        Iterator<? extends StatsData> allStats = this.getAllStatsData(StatsData.class);
         if( allStats.hasNext() )
             return allStats.next();
         else
@@ -173,17 +171,17 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     }
 
     @Adjacency(label = "stats", direction = Direction.OUT)
-    public abstract StatsData addStats(StatsData stats);
+    public abstract StatsData addStatsData(StatsData stats);
 
     @Adjacency(label = "stats", direction = Direction.OUT)
-    public abstract void removeStats(StatsData stats);
+    public abstract void removeStatsData(StatsData stats);
 
-    public void setStats(StatsData stats) {
-        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getAllStats(StatsData.class), statsData -> this.addStats(statsData), () -> this.createStats() );
+    public void setStatsData(StatsData stats) {
+        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getAllStatsData(StatsData.class), statsData -> this.addStatsData(statsData), () -> this.createStatsData() );
     }
 
-    public StatsData createStats() {
-        if( this.getStats() != null )
+    public StatsData createStatsData() {
+        if( this.getStatsData() != null )
             throw new IllegalStateException("Already has stats, can't create another");
         final StatsData stats = this.getGraph().addFramedVertex(StatsData.class);
         stats.setAgile(0);
@@ -204,9 +202,37 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
         stats.setWeaponRatingMax(0);
         stats.setWeaponRatingMin(0);
         stats.setWillpower(0);
-        this.addStats(stats);
+        this.addStatsData(stats);
         return stats;
     }
+
+    public static void copyNpc(NpcData dest, Npc src) {
+        try {
+            PropertyUtils.copyProperties(dest, src);
+            LootData.copyLoot(dest.createLootData(), src.getLoot());
+            for(SpawnRule spawnRule : src.getSpawnRules())
+                SpawnRuleData.copySpawnRule(dest.createSpawnRuleData(), spawnRule);
+            StatsData.copyStats(dest.createStatsData(), src.getStats());
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+    }
+
+    public static Npc copyNpc(NpcData src) {
+        Npc retVal = new Npc();
+        try {
+            PropertyUtils.copyProperties(retVal, src);
+            retVal.setLoot(LootData.copyLoot(src.getLootData()));
+            List<SpawnRule> rules = new ArrayList<>();
+            for(SpawnRuleData spawnRuleData : src.getSpawnRulesData())
+                rules.add(SpawnRuleData.copySpawnRule(spawnRuleData));
+            retVal.setSpawnRules(Collections.unmodifiableList(rules));
+            retVal.setStats(StatsData.copyStats(src.getStatsData()));
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+        return retVal;
+    }
 }
 
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
index 5465f88a1ac3642d4308a93b90f8648e7411b949..62dda8fa87f94b8fce757528637acf3e63d2919a 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
@@ -15,11 +15,16 @@
  */
 package com.syncleus.aethermud.storage.graphdb.model;
 
+import com.syncleus.aethermud.items.Loot;
+import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
 import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
+import org.apache.commons.beanutils.PropertyUtils;
+
+import java.lang.reflect.InvocationTargetException;
 
 @GraphElement
 public abstract class SpawnRuleData extends AbstractInterceptingVertexFrame {
@@ -52,4 +57,22 @@ public abstract class SpawnRuleData extends AbstractInterceptingVertexFrame {
 
     @Property("MaxPerRoom")
     public abstract void setMaxPerRoom(int maxPerRoom);
+
+    public static void copySpawnRule(SpawnRuleData dest, SpawnRule src) {
+        try {
+            PropertyUtils.copyProperties(dest, src);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+    }
+
+    public static SpawnRule copySpawnRule(SpawnRuleData src) {
+        SpawnRule retVal = new SpawnRule();
+        try {
+            PropertyUtils.copyProperties(retVal, src);;
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+        return retVal;
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
index b7c4b33f6ca6bbd757b14acb4372ff668fbe2df8..4bc3bfc14f196ec147d57019b6c74a3212469630 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
@@ -16,8 +16,6 @@
 package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.stats.StatsPojo;
-import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
 import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
@@ -25,153 +23,119 @@ import org.apache.commons.beanutils.PropertyUtils;
 
 import java.lang.reflect.InvocationTargetException;
 
+import static java.lang.StrictMath.sqrt;
+
 @GraphElement
-public abstract class StatsData extends AbstractInterceptingVertexFrame implements Stats {
-    @Override
+public abstract class StatsData extends AbstractInterceptingVertexFrame {
     @Property("intelligence")
     public abstract Integer getIntelligence();
 
-    @Override
     @Property("intelligence")
     public abstract void setIntelligence(Integer intelligence);
 
-    @Override
     @Property("maxEffects")
     public abstract Integer getMaxEffects();
 
-    @Override
     @Property("maxEffects")
     public abstract void setMaxEffects(Integer maxEffects);
 
-    @Override
     @Property("experience")
     public abstract Integer getExperience();
 
-    @Override
     @Property("experience")
     public abstract void setExperience(Integer experience);
 
-    @Override
     @Property("strength")
     public abstract Integer getStrength();
 
-    @Override
     @Property("strength")
     public abstract void setStrength(Integer strength);
 
-    @Override
     @Property("willpower")
     public abstract Integer getWillpower();
 
-    @Override
     @Property("willpower")
     public abstract void setWillpower(Integer willpower);
 
-    @Override
     @Property("aim")
     public abstract Integer getAim();
 
-    @Override
     @Property("aim")
     public abstract void setAim(Integer aim);
 
-    @Override
     @Property("agile")
     public abstract Integer getAgile();
 
-    @Override
     @Property("agile")
     public abstract void setAgile(Integer agile);
 
-    @Override
     @Property("armorRating")
     public abstract Integer getArmorRating();
 
-    @Override
     @Property("armorRating")
     public abstract void setArmorRating(Integer armorRating);
 
-    @Override
     @Property("meleSkill")
     public abstract Integer getMeleeSkill();
 
-    @Override
     @Property("meleSkill")
     public abstract void setMeleeSkill(Integer meleSkill);
 
-    @Override
     @Property("currentHealth")
     public abstract Integer getCurrentHealth();
 
-    @Override
     @Property("currentHealth")
     public abstract void setCurrentHealth(Integer currentHealth);
 
-    @Override
     @Property("maxHealth")
     public abstract Integer getMaxHealth();
 
-    @Override
     @Property("maxHealth")
     public abstract void setMaxHealth(Integer maxHealth);
 
-    @Override
     @Property("weaponRatingMax")
     public abstract Integer getWeaponRatingMax();
 
-    @Override
     @Property("weaponRatingMax")
     public abstract void setWeaponRatingMax(Integer weaponRatingMax);
 
-    @Override
     @Property("weaponRatingMin")
     public abstract Integer getWeaponRatingMin();
 
-    @Override
     @Property("weaponRatingMin")
     public abstract void setWeaponRatingMin(Integer weaponRatingMin);
 
-    @Override
     @Property("numberOfWeaponRolls")
     public abstract Integer getNumberOfWeaponRolls();
 
-    @Override
     @Property("numberOfWeaponRolls")
     public abstract void setNumberOfWeaponRolls(Integer numberOfWeaponRolls);
 
-    @Override
     @Property("currentMana")
     public abstract Integer getCurrentMana();
 
-    @Override
     @Property("maxMana")
     public abstract Integer getMaxMana();
 
-    @Override
     @Property("currentMana")
     public abstract void setCurrentMana(Integer currentMana);
 
-    @Override
     @Property("maxMana")
     public abstract void setMaxMana(Integer maxMana);
 
-    @Override
     @Property("foraging")
     public abstract Integer getForaging();
 
-    @Override
     @Property("foraging")
     public abstract void setForaging(Integer foraging);
 
-    @Override
     @Property("inventorySize")
     public abstract Integer getInventorySize();
 
-    @Override
     @Property("inventorySize")
     public abstract void setInventorySize(Integer inventorySize);
 
-    public static void copyStats(StatsData dest, StatsPojo src) {
+    public static void copyStats(StatsData dest, Stats src) {
         try {
             PropertyUtils.copyProperties(dest, src);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -179,8 +143,8 @@ public abstract class StatsData extends AbstractInterceptingVertexFrame implemen
         }
     }
 
-    public static StatsPojo copyStats(Stats src) {
-        StatsPojo retVal = new StatsPojo();
+    public static Stats copyStats(StatsData src) {
+        Stats retVal = new Stats();
         try {
             PropertyUtils.copyProperties(retVal, src);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -188,4 +152,9 @@ public abstract class StatsData extends AbstractInterceptingVertexFrame implemen
         }
         return retVal;
     }
+
+    public Integer getLevel() {
+        double v = 0.02 * sqrt(getExperience());
+        return Double.valueOf(Math.floor(v)).intValue();
+    }
 }