From e1f6294a5e42c25e19d884289f1fd99e77e9ff9a Mon Sep 17 00:00:00 2001
From: Chris Kearney <chris@kearneymail.com>
Date: Sun, 14 May 2017 18:04:10 -0700
Subject: [PATCH] solid progresson templating the json files

---
 .../creeper/common/AttackMessage.java         |  26 ++++
 .../creeper/common/ColorizedTextTemplate.java |  79 ++++++++++++
 .../creeper/configuration/ConfigureNpc.java   |   2 +-
 .../java/com/comandante/creeper/npc/Npc.java  |  22 +++-
 .../comandante/creeper/npc/NpcBuilder.java    |  11 +-
 .../creeper/storage/ItemStorage.java          |  19 ++-
 .../creeper/storage/MerchantStorage.java      |  18 ++-
 .../creeper/storage/NpcMetadata.java          |  10 ++
 .../creeper/storage/NpcStorage.java           |  23 +++-
 .../common/ColorizedTextTemplateTest.java     | 113 ++++++++++++++++++
 world/items/basic_key.json                    |   4 +-
 world/items/beserker_baton.json               |   2 +-
 world/items/beserker_boots.json               |   2 +-
 world/items/beserker_bracers.json             |   2 +-
 world/items/beserker_chest.json               |   2 +-
 world/items/beserker_helm.json                |   2 +-
 world/items/beserker_shorts.json              |   2 +-
 world/items/biggers_skin_satchel.json         |   6 +-
 world/items/leather_satchel.json              |   6 +-
 world/items/lightning_spellbook.json          |   6 +-
 world/items/marijuana.json                    |  42 +++----
 world/items/purple_drank.json                 |   6 +-
 world/items/rad_claw_hoodie.json              |   4 +-
 world/items/rad_claw_pants.json               |   4 +-
 world/items/red_claw_beanie.json              |   4 +-
 world/items/small_health_potion.json          |   6 +-
 world/items/stick_of_justice.json             |   6 +-
 world/merchants/a_bank_of_lockers.json        |   2 +-
 world/merchants/biggers_the_blacksmith.json   |   2 +-
 world/merchants/jim_the_banker.json           |   2 +-
 world/merchants/lloyd_the_bartender.json      |   2 +-
 world/merchants/nigel_the_bartender.json      |   2 +-
 world/merchants/old_wise_man.json             |   4 +-
 world/merchants/willy_the_wizard.json         |   2 +-
 world/npcs/blood_wolf.json                    |   5 +-
 world/npcs/demon_cat.json                     |   7 +-
 world/npcs/red-eyed_bear.json                 |  10 +-
 world/npcs/swamp_bear.json                    |   8 +-
 world/npcs/swamp_berserker.json               |   7 +-
 world/npcs/tree_berserker.json                |   9 +-
 40 files changed, 386 insertions(+), 105 deletions(-)
 create mode 100644 src/main/java/com/comandante/creeper/common/AttackMessage.java
 create mode 100644 src/main/java/com/comandante/creeper/common/ColorizedTextTemplate.java
 create mode 100644 src/test/com/comandante/creeper/common/ColorizedTextTemplateTest.java

diff --git a/src/main/java/com/comandante/creeper/common/AttackMessage.java b/src/main/java/com/comandante/creeper/common/AttackMessage.java
new file mode 100644
index 00000000..867f112a
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/common/AttackMessage.java
@@ -0,0 +1,26 @@
+package com.comandante.creeper.common;
+
+
+public class AttackMessage {
+
+    private final Type type;
+    private final String attackMessage;
+
+    public AttackMessage(Type type, String attackMessage) {
+        this.type = type;
+        this.attackMessage = attackMessage;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public String getAttackMessage() {
+        return attackMessage;
+    }
+
+    public enum Type {
+        NORMAL,
+        CRITICAL
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/common/ColorizedTextTemplate.java b/src/main/java/com/comandante/creeper/common/ColorizedTextTemplate.java
new file mode 100644
index 00000000..4a5cd6d4
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/common/ColorizedTextTemplate.java
@@ -0,0 +1,79 @@
+package com.comandante.creeper.common;
+
+
+import com.comandante.creeper.server.player_communication.Color;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class ColorizedTextTemplate {
+
+    private final static Map<String, Set<String>> colors = new ImmutableMap.Builder<String, Set<String>>()
+            .put("c-yellow", Sets.newHashSet(Color.YELLOW))
+            .put("c-default", Sets.newHashSet(Color.DEFAULT))
+            .put("c-white", Sets.newHashSet(Color.WHITE))
+            .put("c-cyan", Sets.newHashSet(Color.CYAN))
+            .put("c-black", Sets.newHashSet(Color.BLACK))
+            .put("c-blue", Sets.newHashSet(Color.BLUE))
+            .put("c-magenta", Sets.newHashSet(Color.MAGENTA))
+            .put("c-red", Sets.newHashSet(Color.RED))
+            .put("c-green", Sets.newHashSet(Color.GREEN))
+            //Seems that there are two valid RESET sequences that I have to account for.
+            .put("c-reset", Sets.newHashSet(Color.RESET, "\u001b[0m"))
+            .put("c-bold-on", Sets.newHashSet(Color.BOLD_ON))
+            .put("c-bold-off", Sets.newHashSet(Color.BOLD_OFF))
+            .build();
+
+    private final static Map<String, Set<String>> defaults = new ImmutableMap.Builder<String, Set<String>>()
+            .putAll(colors)
+            .build();
+
+    public static String renderFromTemplateLanguage(String rawTemplateText) {
+        return renderFromTemplateLanguage(Maps.newHashMap(), rawTemplateText);
+    }
+
+    public static String renderFromTemplateLanguage(Map<String, String> variables, String rawTemplateText) {
+
+        for (Map.Entry<String, Set<String>> def : defaults.entrySet()) {
+            String key = def.getKey();
+            Optional<String> first = def.getValue().stream().findFirst();
+            if (first.isPresent()) {
+                rawTemplateText = rawTemplateText.replaceAll(Pattern.quote("@" + key + "@"), first.get());
+            }
+        }
+
+        for (String key : variables.keySet()) {
+            String renderedValue = variables.get(key);
+            rawTemplateText = rawTemplateText.replaceAll(Pattern.quote("@" + key + "@"), renderedValue);
+        }
+
+        return rawTemplateText;
+    }
+
+    public static String renderToTemplateLanguage(String raw) {
+        return renderToTemplateLanguage(Maps.newHashMap(), raw);
+    }
+
+    public static String renderToTemplateLanguage(Map<String, String> variables, String raw) {
+
+        for (Map.Entry<String, Set<String>> keyToValues : defaults.entrySet()) {
+            String templateKey = keyToValues.getKey();
+            for (String templateValue : keyToValues.getValue()) {
+                raw = raw.replaceAll(Pattern.quote(templateValue), "@" + templateKey + "@");
+            }
+        }
+
+        for (Map.Entry<String, String> next : variables.entrySet()) {
+            String templateKey = next.getKey();
+            raw = raw.replaceAll(Pattern.quote(next.getValue()), "@" + templateKey + "@");
+        }
+
+        return raw;
+    }
+
+}
diff --git a/src/main/java/com/comandante/creeper/configuration/ConfigureNpc.java b/src/main/java/com/comandante/creeper/configuration/ConfigureNpc.java
index 4460c42b..3c37af4d 100644
--- a/src/main/java/com/comandante/creeper/configuration/ConfigureNpc.java
+++ b/src/main/java/com/comandante/creeper/configuration/ConfigureNpc.java
@@ -35,7 +35,7 @@ public class ConfigureNpc {
 
         configureAllNpcs(gameManager);
 
-        List<ItemMetadata> allItemMetadata = gameManager.getItemStorage().getAllItemMetadata();
+        List<ItemMetadata> allItemMetadata = gameManager.getItemStorage().getItemMetadatas();
 
         for (ItemMetadata itemMetadata : allItemMetadata) {
             for (SpawnRule spawnRule : itemMetadata.getSpawnRules()) {
diff --git a/src/main/java/com/comandante/creeper/npc/Npc.java b/src/main/java/com/comandante/creeper/npc/Npc.java
index d6c52454..7b6a51c6 100644
--- a/src/main/java/com/comandante/creeper/npc/Npc.java
+++ b/src/main/java/com/comandante/creeper/npc/Npc.java
@@ -1,6 +1,7 @@
 package com.comandante.creeper.npc;
 
 
+import com.comandante.creeper.common.AttackMessage;
 import com.comandante.creeper.core_game.GameManager;
 import com.comandante.creeper.core_game.SentryManager;
 import com.comandante.creeper.entity.CreeperEntity;
@@ -62,9 +63,14 @@ public class Npc extends CreeperEntity {
     private int effectsTickBucket = 0;
     private Set<CoolDown> coolDowns = Sets.newHashSet();
     private final Experience experience = new Experience();
+    private final Set<AttackMessage> attackMessages;
 
 
-    protected Npc(GameManager gameManager, String name, String colorName, long lastPhraseTimestamp, Stats stats, String dieMessage, Temperament temperament, Set<Area> roamAreas, Set<String> validTriggers, Loot loot, Set<SpawnRule> spawnRules) {
+    public Set<AttackMessage> getAttackMessages() {
+        return attackMessages;
+    }
+
+    protected Npc(GameManager gameManager, String name, String colorName, long lastPhraseTimestamp, Stats stats, String dieMessage, Temperament temperament, Set<Area> roamAreas, Set<String> validTriggers, Loot loot, Set<SpawnRule> spawnRules, Set<AttackMessage> attackMessages) {
         this.gameManager = gameManager;
         this.name = name;
         this.colorName = colorName;
@@ -76,6 +82,8 @@ public class Npc extends CreeperEntity {
         this.loot = loot;
         this.spawnRules = spawnRules;
         this.temperament = temperament;
+        this.attackMessages = attackMessages;
+
     }
 
     @Override
@@ -439,4 +447,16 @@ public class Npc extends CreeperEntity {
         }
     }
 
+    public AttackMessage getRandomAttackMessage() {
+        int size = attackMessages.size();
+        int item = random.nextInt(size); // In real life, the Random object should be rather more shared than this
+        int i = 0;
+        for(AttackMessage attackMessage : attackMessages) {
+            if (i == item) {
+                return attackMessage;
+            }
+            i++;
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/com/comandante/creeper/npc/NpcBuilder.java b/src/main/java/com/comandante/creeper/npc/NpcBuilder.java
index d0e246eb..391568e4 100644
--- a/src/main/java/com/comandante/creeper/npc/NpcBuilder.java
+++ b/src/main/java/com/comandante/creeper/npc/NpcBuilder.java
@@ -1,5 +1,6 @@
 package com.comandante.creeper.npc;
 
+import com.comandante.creeper.common.AttackMessage;
 import com.comandante.creeper.core_game.GameManager;
 import com.comandante.creeper.items.Loot;
 import com.comandante.creeper.spawner.SpawnRule;
@@ -23,6 +24,7 @@ public class NpcBuilder {
     private Loot loot;
     private Set<SpawnRule> spawnRules;
     private Temperament temperament;
+    private Set<AttackMessage> attackMessages;
 
     public NpcBuilder() {
     }
@@ -39,6 +41,7 @@ public class NpcBuilder {
         this.spawnRules = npc.getSpawnRules();
         this.gameManager = npc.getGameManager();
         this.temperament = npc.getTemperament();
+        this.attackMessages = npc.getAttackMessages();
     }
 
     public NpcBuilder(NpcMetadata npcMetadata) {
@@ -51,6 +54,7 @@ public class NpcBuilder {
         this.loot = npcMetadata.getLoot();
         this.spawnRules = npcMetadata.getSpawnRules();
         this.temperament = npcMetadata.getTemperament();
+        this.attackMessages = npcMetadata.getAttackMessages();
     }
 
     public NpcBuilder setGameManager(GameManager gameManager) {
@@ -108,11 +112,16 @@ public class NpcBuilder {
         return this;
     }
 
+    public NpcBuilder setAttackMessages(Set<AttackMessage> attackMessages) {
+        this.attackMessages = attackMessages;
+        return this;
+    }
+
     public Npc createNpc() {
         checkNotNull(gameManager);
         if (loot.getLootGoldMin() > loot.getLootGoldMax()) {
             throw new RuntimeException("Invalid loot configuration.");
         }
-        return new Npc(gameManager, name, colorName, lastPhraseTimestamp, stats, dieMessage, temperament, roamAreas, validTriggers, loot, spawnRules);
+        return new Npc(gameManager, name, colorName, lastPhraseTimestamp, stats, dieMessage, temperament, roamAreas, validTriggers, loot, spawnRules, attackMessages);
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/comandante/creeper/storage/ItemStorage.java b/src/main/java/com/comandante/creeper/storage/ItemStorage.java
index 1ad24419..f19ddafa 100644
--- a/src/main/java/com/comandante/creeper/storage/ItemStorage.java
+++ b/src/main/java/com/comandante/creeper/storage/ItemStorage.java
@@ -1,11 +1,13 @@
 package com.comandante.creeper.storage;
 
+import com.comandante.creeper.common.ColorizedTextTemplate;
 import com.comandante.creeper.items.ItemMetadata;
 import org.apache.log4j.Logger;
 
 import java.io.IOException;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 public class ItemStorage {
 
@@ -18,14 +20,27 @@ public class ItemStorage {
 
     public ItemStorage(FilebasedJsonStorage filebasedJsonStorage) {
         this.filebasedJsonStorage = filebasedJsonStorage;
-        this.itemMetadatas = filebasedJsonStorage.readAllMetadatas(LOCAL_ITEM_DIRECTORY, true, new ItemMetadata());
+        this.itemMetadatas = getAllItemMetadata();
     }
 
-    public List<ItemMetadata> getAllItemMetadata() {
+    private List<ItemMetadata> getAllItemMetadata() {
+        return filebasedJsonStorage.readAllMetadatas(LOCAL_ITEM_DIRECTORY, true, new ItemMetadata()).stream()
+                .map(itemMetadata -> {
+                    itemMetadata.setItemDescription(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getItemDescription()));
+                    itemMetadata.setItemName(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getItemName()));
+                    itemMetadata.setRestingName(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getRestingName()));
+                    return itemMetadata;
+                }).collect(Collectors.toList());
+    }
+
+    public List<ItemMetadata> getItemMetadatas() {
         return itemMetadatas;
     }
 
     public void saveItemMetadata(ItemMetadata itemMetadata) throws IOException {
+        itemMetadata.setItemName(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getItemName()));
+        itemMetadata.setItemDescription(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getItemDescription()));
+        itemMetadata.setRestingName(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getRestingName()));
         filebasedJsonStorage.saveMetadata(itemMetadata.getInternalItemName(), LOCAL_ITEM_DIRECTORY, itemMetadata);
     }
 
diff --git a/src/main/java/com/comandante/creeper/storage/MerchantStorage.java b/src/main/java/com/comandante/creeper/storage/MerchantStorage.java
index 00e4db47..bd2835ab 100644
--- a/src/main/java/com/comandante/creeper/storage/MerchantStorage.java
+++ b/src/main/java/com/comandante/creeper/storage/MerchantStorage.java
@@ -1,5 +1,6 @@
 package com.comandante.creeper.storage;
 
+import com.comandante.creeper.common.ColorizedTextTemplate;
 import com.comandante.creeper.core_game.GameManager;
 import com.comandante.creeper.merchant.Merchant;
 import com.comandante.creeper.merchant.MerchantMetadata;
@@ -22,7 +23,20 @@ public class MerchantStorage {
     public MerchantStorage(GameManager gameManager, FilebasedJsonStorage filebasedJsonStorage) {
         this.gameManager = gameManager;
         this.filebasedJsonStorage = filebasedJsonStorage;
-        this.merchantMetadatas = filebasedJsonStorage.readAllMetadatas(LOCAL_MERCHANT_DIRECTORY, true, new MerchantMetadata());
+        this.merchantMetadatas = getAllMerchantMetadata();
+    }
+
+    private List<MerchantMetadata> getAllMerchantMetadata() {
+        return filebasedJsonStorage.readAllMetadatas(LOCAL_MERCHANT_DIRECTORY, true, new MerchantMetadata()).stream()
+                .map(merchantMetadata -> {
+                    merchantMetadata.setColorName(ColorizedTextTemplate.renderFromTemplateLanguage(merchantMetadata.getColorName()));
+                    merchantMetadata.setWelcomeMessage(ColorizedTextTemplate.renderFromTemplateLanguage(merchantMetadata.getWelcomeMessage()));
+                    return merchantMetadata;
+                }).collect(Collectors.toList());
+    }
+
+    public List<MerchantMetadata> getMerchantMetadatas() {
+        return merchantMetadatas;
     }
 
     public List<Merchant> getAllMerchants() {
@@ -56,6 +70,8 @@ public class MerchantStorage {
     }
 
     public void saveMerchantMetadata(MerchantMetadata merchantMetadata) throws IOException {
+        merchantMetadata.setColorName(ColorizedTextTemplate.renderToTemplateLanguage(merchantMetadata.getColorName()));
+        merchantMetadata.setWelcomeMessage(ColorizedTextTemplate.renderToTemplateLanguage(merchantMetadata.getWelcomeMessage()));
         filebasedJsonStorage.saveMetadata(merchantMetadata.getInternalName(), LOCAL_MERCHANT_DIRECTORY, merchantMetadata);
     }
 
diff --git a/src/main/java/com/comandante/creeper/storage/NpcMetadata.java b/src/main/java/com/comandante/creeper/storage/NpcMetadata.java
index 8694c9e8..8bb4ab67 100644
--- a/src/main/java/com/comandante/creeper/storage/NpcMetadata.java
+++ b/src/main/java/com/comandante/creeper/storage/NpcMetadata.java
@@ -1,5 +1,6 @@
 package com.comandante.creeper.storage;
 
+import com.comandante.creeper.common.AttackMessage;
 import com.comandante.creeper.items.Loot;
 import com.comandante.creeper.npc.Temperament;
 import com.comandante.creeper.spawner.SpawnRule;
@@ -19,10 +20,19 @@ public class NpcMetadata {
     private Set<String> validTriggers;
     private Set<SpawnRule> spawnRules;
     private Loot loot;
+    private Set<AttackMessage> attackMessages;
 
     public NpcMetadata() {
     }
 
+    public Set<AttackMessage> getAttackMessages() {
+        return attackMessages;
+    }
+
+    public void setAttackMessages(Set<AttackMessage> attackMessages) {
+        this.attackMessages = attackMessages;
+    }
+
     public String getName() {
         return name;
     }
diff --git a/src/main/java/com/comandante/creeper/storage/NpcStorage.java b/src/main/java/com/comandante/creeper/storage/NpcStorage.java
index 75c41a95..fae5e4c5 100644
--- a/src/main/java/com/comandante/creeper/storage/NpcStorage.java
+++ b/src/main/java/com/comandante/creeper/storage/NpcStorage.java
@@ -1,6 +1,7 @@
 package com.comandante.creeper.storage;
 
 
+import com.comandante.creeper.common.ColorizedTextTemplate;
 import com.comandante.creeper.core_game.GameManager;
 import com.comandante.creeper.npc.Npc;
 import com.comandante.creeper.npc.NpcBuilder;
@@ -16,24 +17,38 @@ public class NpcStorage {
     private final static String LOCAL_NPC_DIRECTORY = "world/npcs/";
     private static final Logger log = Logger.getLogger(NpcStorage.class);
     private final FilebasedJsonStorage filebasedJsonStorage;
+    private final List<NpcMetadata> npcMetadatas;
 
     public NpcStorage(GameManager gameManager, FilebasedJsonStorage filebasedJsonStorage) {
         this.gameManager = gameManager;
         this.filebasedJsonStorage = filebasedJsonStorage;
+        this.npcMetadatas = readAllNpcs();
     }
 
     public List<Npc> getAllNpcs() {
-        List<NpcMetadata> npcMetadata = readAlLNpcs();
+        List<NpcMetadata> npcMetadata = readAllNpcs();
         return npcMetadata.stream()
                 .map(metadata -> new NpcBuilder(metadata).setGameManager(gameManager).createNpc())
                 .collect(Collectors.toList());
     }
 
     public void saveNpcMetadata(NpcMetadata npcMetadata) throws IOException {
-       filebasedJsonStorage.saveMetadata(npcMetadata.getName(), LOCAL_NPC_DIRECTORY, npcMetadata);
+        npcMetadata.setDieMessage(ColorizedTextTemplate.renderToTemplateLanguage(npcMetadata.getDieMessage()));
+        npcMetadata.setColorName(ColorizedTextTemplate.renderToTemplateLanguage(npcMetadata.getColorName()));
+        filebasedJsonStorage.saveMetadata(npcMetadata.getName(), LOCAL_NPC_DIRECTORY, npcMetadata);
     }
 
-    private List<NpcMetadata> readAlLNpcs() {
-        return filebasedJsonStorage.readAllMetadatas(LOCAL_NPC_DIRECTORY, true, new NpcMetadata());
+    public List<NpcMetadata> getNpcMetadatas() {
+        return npcMetadatas;
+    }
+
+    private List<NpcMetadata> readAllNpcs() {
+        List<NpcMetadata> npcMetadatas = filebasedJsonStorage.readAllMetadatas(LOCAL_NPC_DIRECTORY, true, new NpcMetadata());
+
+        for (NpcMetadata npcMetadata: npcMetadatas) {
+            npcMetadata.setColorName(ColorizedTextTemplate.renderFromTemplateLanguage(npcMetadata.getColorName()));
+            npcMetadata.setDieMessage(ColorizedTextTemplate.renderFromTemplateLanguage(npcMetadata.getDieMessage()));
+        }
+        return npcMetadatas;
     }
 }
diff --git a/src/test/com/comandante/creeper/common/ColorizedTextTemplateTest.java b/src/test/com/comandante/creeper/common/ColorizedTextTemplateTest.java
new file mode 100644
index 00000000..62d336b0
--- /dev/null
+++ b/src/test/com/comandante/creeper/common/ColorizedTextTemplateTest.java
@@ -0,0 +1,113 @@
+package com.comandante.creeper.common;
+
+import com.comandante.creeper.items.ItemMetadata;
+import com.comandante.creeper.merchant.MerchantMetadata;
+import com.comandante.creeper.server.player_communication.Color;
+import com.comandante.creeper.storage.*;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+
+public class ColorizedTextTemplateTest {
+
+
+    @Test
+    public void testConversion() throws Exception {
+
+        final String fightMsg =
+                Color.BOLD_ON + Color.RED + "[attack] "
+                        + Color.RESET + Color.YELLOW + "The " + "worm wobbler" + " was caught off guard by the attack! " + "+" +
+                        NumberFormat.getNumberInstance(Locale.US).format(12) +
+                        Color.RESET + Color.BOLD_ON + Color.RED + " DAMAGE" + Color.RESET + " done to " + "worm wobbler";
+
+        String test = "@c-bold-on@" + "@c-red@" + "[attack] " + "@c-reset@" + "@c-yellow@" + "The " + "@npc-name@" + " was caught off guard by the attack! " + "+" + "@damage-done@" + "@c-reset@" + "@c-bold-on@" + "@c-red@" + " DAMAGE" + "@c-reset@" + " done to " + "@npc-name@";
+
+        HashMap<String, String> variableMap = Maps.newHashMap();
+
+        variableMap.put("npc-name", "worm wobbler");
+        variableMap.put("damage-done", String.valueOf(12));
+
+        String render = ColorizedTextTemplate.renderFromTemplateLanguage(variableMap, test);
+
+        Assert.assertTrue(render.equals(fightMsg));
+
+        String s = ColorizedTextTemplate.renderToTemplateLanguage(variableMap, render);
+
+        Assert.assertTrue(s.equals(test));
+
+    }
+
+    @Test
+    public void testConvertToTemplateLanguage() throws Exception {
+
+
+        Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+        NpcStorage npcStorage = new NpcStorage(null, new FilebasedJsonStorage(gson));
+
+
+        List<NpcMetadata> npcMetadata = npcStorage.getNpcMetadatas();
+
+        npcMetadata.forEach(new Consumer<NpcMetadata>() {
+            @Override
+            public void accept(NpcMetadata npcMetadata) {
+                System.out.println(npcMetadata.getColorName());
+                try {
+                    npcStorage.saveNpcMetadata(npcMetadata);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+            }
+        });
+
+        MerchantStorage merchantStorage = new MerchantStorage(null, new FilebasedJsonStorage(gson));
+        List<MerchantMetadata> merchantMetadatas = merchantStorage.getMerchantMetadatas();
+
+        merchantMetadatas.forEach(new Consumer<MerchantMetadata>() {
+            @Override
+            public void accept(MerchantMetadata merchantMetadata) {
+
+                System.out.println(merchantMetadata.getColorName());
+                try {
+                    merchantStorage.saveMerchantMetadata(merchantMetadata);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+
+        ItemStorage itemStorage = new ItemStorage(new FilebasedJsonStorage(gson));
+
+        itemStorage.getItemMetadatas().forEach(new Consumer<ItemMetadata>() {
+            @Override
+            public void accept(ItemMetadata itemMetadata) {
+                System.out.println(itemMetadata.getItemName());
+                try {
+                    itemStorage.saveItemMetadata(itemMetadata);
+                } catch (IOException e) {
+
+
+                }
+
+            }
+        });
+
+        String green = Color.GREEN;
+        String test = "\u001b[32m";
+
+
+        System.out.printf("u");
+    }
+
+}
\ No newline at end of file
diff --git a/world/items/basic_key.json b/world/items/basic_key.json
index 3f408dae..b9ee96c3 100644
--- a/world/items/basic_key.json
+++ b/world/items/basic_key.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "basic key",
-  "itemName": "a shiny \u001b[33mgold key\u001b[m",
+  "itemName": "a shiny @c-yellow@gold key@c-reset@",
   "itemDescription": "A basic key with nothing really remarkable other than its made of gold.",
-  "restingName": "a shiny \u001b[33mgold key\u001b[m catches your eye.",
+  "restingName": "a shiny @c-yellow@gold key@c-reset@ catches your eye.",
   "valueInGold": 10,
   "itemHalfLifeTicks": 60,
   "rarity": "BASIC",
diff --git a/world/items/beserker_baton.json b/world/items/beserker_baton.json
index 87fba918..2f82b15e 100644
--- a/world/items/beserker_baton.json
+++ b/world/items/beserker_baton.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker baton",
-  "itemName": "\u001b[36ma berserker baton\u001b[m",
+  "itemName": "@c-cyan@a berserker baton@c-reset@",
   "itemDescription": "a berserker baton",
   "restingName": "a berserker baton rests upon the ground.",
   "valueInGold": 100,
diff --git a/world/items/beserker_boots.json b/world/items/beserker_boots.json
index eb5f0bd7..2f4c6a4f 100644
--- a/world/items/beserker_boots.json
+++ b/world/items/beserker_boots.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker boots",
-  "itemName": "\u001b[36mberserker boots\u001b[m",
+  "itemName": "@c-cyan@berserker boots@c-reset@",
   "itemDescription": "a pair of berserker boots",
   "restingName": "a pair of berserker boots are here on the ground.",
   "valueInGold": 50,
diff --git a/world/items/beserker_bracers.json b/world/items/beserker_bracers.json
index 9cd91c21..155e4e9b 100644
--- a/world/items/beserker_bracers.json
+++ b/world/items/beserker_bracers.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker bracers",
-  "itemName": "\u001b[36mberserker bracers\u001b[m",
+  "itemName": "@c-cyan@berserker bracers@c-reset@",
   "itemDescription": "a pair of berserker bracers",
   "restingName": "a pair of berserker bracers are here on the ground.",
   "valueInGold": 40,
diff --git a/world/items/beserker_chest.json b/world/items/beserker_chest.json
index 813b8c50..71210122 100644
--- a/world/items/beserker_chest.json
+++ b/world/items/beserker_chest.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker chest",
-  "itemName": "\u001b[36mberserker chest\u001b[m",
+  "itemName": "@c-cyan@berserker chest@c-reset@",
   "itemDescription": "a berserker chest",
   "restingName": "a berserker chest is on the ground.",
   "valueInGold": 70,
diff --git a/world/items/beserker_helm.json b/world/items/beserker_helm.json
index 41167b08..51d8f188 100644
--- a/world/items/beserker_helm.json
+++ b/world/items/beserker_helm.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker helm",
-  "itemName": "\u001b[36mberserker helm\u001b[m",
+  "itemName": "@c-cyan@berserker helm@c-reset@",
   "itemDescription": "a berserker helm",
   "restingName": "a berserker helm is on the ground.",
   "valueInGold": 40,
diff --git a/world/items/beserker_shorts.json b/world/items/beserker_shorts.json
index f223c806..625e6296 100644
--- a/world/items/beserker_shorts.json
+++ b/world/items/beserker_shorts.json
@@ -1,6 +1,6 @@
 {
   "internalItemName": "beserker shorts",
-  "itemName": "\u001b[36mberserker shorts\u001b[m",
+  "itemName": "@c-cyan@berserker shorts@c-reset@",
   "itemDescription": "a pair of berserker shorts",
   "restingName": "a pair of berserker shorts are here on the ground.",
   "valueInGold": 80,
diff --git a/world/items/biggers_skin_satchel.json b/world/items/biggers_skin_satchel.json
index b458e768..cf3b9d2f 100644
--- a/world/items/biggers_skin_satchel.json
+++ b/world/items/biggers_skin_satchel.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "biggers skin satchel",
-  "itemName": "a \u001b[32mbiggers skin satchel\u001b[m",
-  "itemDescription": "a \u001b[32mbiggers skin satchel\u001b[m with room to store 100 items.",
-  "restingName": "a \u001b[32mbiggers skin satchel\u001b[m",
+  "itemName": "a @c-green@biggers skin satchel@c-reset@",
+  "itemDescription": "a @c-green@biggers skin satchel@c-reset@ with room to store 100 items.",
+  "restingName": "a @c-green@biggers skin satchel@c-reset@",
   "valueInGold": 3000,
   "itemHalfLifeTicks": 60,
   "rarity": "BASIC",
diff --git a/world/items/leather_satchel.json b/world/items/leather_satchel.json
index 6cc70f06..8edb0725 100644
--- a/world/items/leather_satchel.json
+++ b/world/items/leather_satchel.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "leather satchel",
-  "itemName": "a \u001b[32mleather satchel\u001b[m",
-  "itemDescription": "a \u001b[32mleather satchel\u001b[m (15 items)",
-  "restingName": "a \u001b[32mleather satchel\u001b[m",
+  "itemName": "a @c-green@leather satchel@c-reset@",
+  "itemDescription": "a @c-green@leather satchel@c-reset@ (15 items)",
+  "restingName": "a @c-green@leather satchel@c-reset@",
   "valueInGold": 800,
   "itemHalfLifeTicks": 60,
   "rarity": "BASIC",
diff --git a/world/items/lightning_spellbook.json b/world/items/lightning_spellbook.json
index b0d55c1f..97c65945 100644
--- a/world/items/lightning_spellbook.json
+++ b/world/items/lightning_spellbook.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "lightning spellbook",
-  "itemName": "a \u001b[33mlightning\u001b[m spell book.\u001b[m",
-  "itemDescription": "a \u001b[33mlightning\u001b[m spell book.\u001b[m",
-  "restingName": "a \u001b[33mlightning\u001b[m spell book.\u001b[m",
+  "itemName": "a @c-yellow@lightning@c-reset@ spell book.@c-reset@",
+  "itemDescription": "a @c-yellow@lightning@c-reset@ spell book.@c-reset@",
+  "restingName": "a @c-yellow@lightning@c-reset@ spell book.@c-reset@",
   "valueInGold": 3000,
   "itemHalfLifeTicks": 60,
   "rarity": "RARE",
diff --git a/world/items/marijuana.json b/world/items/marijuana.json
index 12353731..63051ff7 100644
--- a/world/items/marijuana.json
+++ b/world/items/marijuana.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "marijuana",
-  "itemName": "\u001b[32mmarijuana\u001b[m flowers\u001b[m",
-  "itemDescription": "some \u001b[32mmarijuana\u001b[m flowers\u001b[m",
-  "restingName": "some \u001b[32mmarijuana\u001b[m flowers\u001b[m are here on the ground.",
+  "itemName": "@c-green@marijuana@c-reset@ flowers@c-reset@",
+  "itemDescription": "some @c-green@marijuana@c-reset@ flowers@c-reset@",
+  "restingName": "some @c-green@marijuana@c-reset@ flowers@c-reset@ are here on the ground.",
   "valueInGold": 10,
   "itemHalfLifeTicks": 60,
   "rarity": "BASIC",
@@ -17,23 +17,6 @@
   "validTimeOfDays": [],
   "isDisposable": true,
   "maxUses": 0,
-  "forages": [
-    {
-      "minLevel": 0,
-      "pctOfSuccess": 40.0,
-      "minAmt": 1,
-      "maxAmt": 3,
-      "forageExperience": 4,
-      "coolDownTicks": 600,
-      "coolDownTicksLeft": 0,
-      "forageAreas": [
-        "BLOODRIDGE2_ZONE",
-        "BLOODRIDGE1_ZONE",
-        "WESTERN9_ZONE",
-        "NORTH3_ZONE"
-      ]
-    }
-  ],
   "itemApplyStats": {
     "strength": 0,
     "intelligence": 0,
@@ -53,5 +36,22 @@
     "foraging": 0,
     "inventorySize": 0,
     "maxEffects": 0
-  }
+  },
+  "forages": [
+    {
+      "minLevel": 0,
+      "pctOfSuccess": 40.0,
+      "minAmt": 1,
+      "maxAmt": 3,
+      "forageExperience": 4,
+      "coolDownTicks": 600,
+      "coolDownTicksLeft": 0,
+      "forageAreas": [
+        "BLOODRIDGE2_ZONE",
+        "BLOODRIDGE1_ZONE",
+        "WESTERN9_ZONE",
+        "NORTH3_ZONE"
+      ]
+    }
+  ]
 }
\ No newline at end of file
diff --git a/world/items/purple_drank.json b/world/items/purple_drank.json
index 81e4f82e..5bdbca71 100644
--- a/world/items/purple_drank.json
+++ b/world/items/purple_drank.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "purple drank",
-  "itemName": "a double cup of \u001b[35mpurple\u001b[m drank",
-  "itemDescription": "a tonic called \u001b[35mpurple\u001b[m drank that restores health\u001b[m",
-  "restingName": "a double cup of \u001b[35mpurple\u001b[m drank rests on the ground.",
+  "itemName": "a double cup of @c-magenta@purple@c-reset@ drank",
+  "itemDescription": "a tonic called @c-magenta@purple@c-reset@ drank that restores health@c-reset@",
+  "restingName": "a double cup of @c-magenta@purple@c-reset@ drank rests on the ground.",
   "valueInGold": 30,
   "itemHalfLifeTicks": 60,
   "rarity": "BASIC",
diff --git a/world/items/rad_claw_hoodie.json b/world/items/rad_claw_hoodie.json
index f2d64214..272cd922 100644
--- a/world/items/rad_claw_hoodie.json
+++ b/world/items/rad_claw_hoodie.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "rad claw hoodie",
-  "itemName": "\u001b[31mred-claw \u001b[mhoodie\u001b[m",
+  "itemName": "@c-red@red-claw @c-reset@hoodie@c-reset@",
   "itemDescription": "a red-claw hoodie",
-  "restingName": "a \u001b[31mred-claw \u001b[mhoodie is on the ground.",
+  "restingName": "a @c-red@red-claw @c-reset@hoodie is on the ground.",
   "valueInGold": 3500,
   "itemHalfLifeTicks": 60,
   "rarity": "LEGENDARY",
diff --git a/world/items/rad_claw_pants.json b/world/items/rad_claw_pants.json
index ee9f6b53..9e714c6c 100644
--- a/world/items/rad_claw_pants.json
+++ b/world/items/rad_claw_pants.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "rad claw pants",
-  "itemName": "\u001b[31mred-claw \u001b[mpants\u001b[m",
+  "itemName": "@c-red@red-claw @c-reset@pants@c-reset@",
   "itemDescription": "a red-claw pants",
-  "restingName": "a \u001b[31mred-claw \u001b[mpants is on the ground.",
+  "restingName": "a @c-red@red-claw @c-reset@pants is on the ground.",
   "valueInGold": 3500,
   "itemHalfLifeTicks": 60,
   "rarity": "LEGENDARY",
diff --git a/world/items/red_claw_beanie.json b/world/items/red_claw_beanie.json
index 6c8503bd..f9ea845e 100644
--- a/world/items/red_claw_beanie.json
+++ b/world/items/red_claw_beanie.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "red claw beanie",
-  "itemName": "\u001b[31mred-claw \u001b[mbeanie\u001b[m",
+  "itemName": "@c-red@red-claw @c-reset@beanie@c-reset@",
   "itemDescription": "a red-claw beanie",
-  "restingName": "a \u001b[31mred-claw \u001b[mbeanie is on the ground.",
+  "restingName": "a @c-red@red-claw @c-reset@beanie is on the ground.",
   "valueInGold": 3500,
   "itemHalfLifeTicks": 60,
   "rarity": "LEGENDARY",
diff --git a/world/items/small_health_potion.json b/world/items/small_health_potion.json
index 662f162b..2ebe1bad 100644
--- a/world/items/small_health_potion.json
+++ b/world/items/small_health_potion.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "small health potion",
-  "itemName": "a small vial of \u001b[31mhealth potion\u001b[m",
-  "itemDescription": "a small vial of \u001b[31mhealth potion\u001b[m that restores 50 health\u001b[m",
-  "restingName": "a small vial of \u001b[31mhealth potion\u001b[m rests on the ground.",
+  "itemName": "a small vial of @c-red@health potion@c-reset@",
+  "itemDescription": "a small vial of @c-red@health potion@c-reset@ that restores 50 health@c-reset@",
+  "restingName": "a small vial of @c-red@health potion@c-reset@ rests on the ground.",
   "valueInGold": 1,
   "itemHalfLifeTicks": 60,
   "rarity": "OFTEN",
diff --git a/world/items/stick_of_justice.json b/world/items/stick_of_justice.json
index 4a2e3230..fd41d908 100644
--- a/world/items/stick_of_justice.json
+++ b/world/items/stick_of_justice.json
@@ -1,8 +1,8 @@
 {
   "internalItemName": "stick of justice",
-  "itemName": "a \u001b[1m\u001b[35mstick\u001b[22m\u001b[32m of \u001b[1m\u001b[34mjustice\u001b[m",
-  "itemDescription": "a \u001b[1m\u001b[35mstick\u001b[22m\u001b[32m of \u001b[1m\u001b[34mjustice\u001b[m",
-  "restingName": "a \u001b[1m\u001b[35mstick\u001b[22m\u001b[32m of \u001b[1m\u001b[34mjustice\u001b[m",
+  "itemName": "a @c-bold-on@@c-magenta@stick@c-bold-off@@c-green@ of @c-bold-on@@c-blue@justice@c-reset@",
+  "itemDescription": "a @c-bold-on@@c-magenta@stick@c-bold-off@@c-green@ of @c-bold-on@@c-blue@justice@c-reset@",
+  "restingName": "a @c-bold-on@@c-magenta@stick@c-bold-off@@c-green@ of @c-bold-on@@c-blue@justice@c-reset@",
   "valueInGold": 3000,
   "itemHalfLifeTicks": 60,
   "rarity": "RARE",
diff --git a/world/merchants/a_bank_of_lockers.json b/world/merchants/a_bank_of_lockers.json
index 094a16ae..3c6bee85 100644
--- a/world/merchants/a_bank_of_lockers.json
+++ b/world/merchants/a_bank_of_lockers.json
@@ -4,7 +4,7 @@
     63
   ],
   "name": "a bank of lockers",
-  "colorName": "\u001b[1m\u001b[36ma bank of lockers\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@a bank of lockers@c-reset@",
   "validTriggers": [
     "a bank of lockers",
     "lockers",
diff --git a/world/merchants/biggers_the_blacksmith.json b/world/merchants/biggers_the_blacksmith.json
index afa9c10b..b73c00b5 100644
--- a/world/merchants/biggers_the_blacksmith.json
+++ b/world/merchants/biggers_the_blacksmith.json
@@ -5,7 +5,7 @@
     66
   ],
   "name": "biggers the blacksmith",
-  "colorName": "\u001b[1m\u001b[36mbiggers the blacksmith\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@biggers the blacksmith@c-reset@",
   "validTriggers": [
     "blacksmith",
     "biggers",
diff --git a/world/merchants/jim_the_banker.json b/world/merchants/jim_the_banker.json
index 868c0a55..6dcecda9 100644
--- a/world/merchants/jim_the_banker.json
+++ b/world/merchants/jim_the_banker.json
@@ -5,7 +5,7 @@
     209
   ],
   "name": "jim the banker",
-  "colorName": "\u001b[1m\u001b[36mjim the banker\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@jim the banker@c-reset@",
   "validTriggers": [
     "bank",
     "j",
diff --git a/world/merchants/lloyd_the_bartender.json b/world/merchants/lloyd_the_bartender.json
index 49660afd..0e8e3797 100644
--- a/world/merchants/lloyd_the_bartender.json
+++ b/world/merchants/lloyd_the_bartender.json
@@ -4,7 +4,7 @@
     64
   ],
   "name": "lloyd the bartender",
-  "colorName": "\u001b[1m\u001b[36mlloyd the bartender\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@lloyd the bartender@c-reset@",
   "validTriggers": [
     "lloyd the bartender",
     "bartender",
diff --git a/world/merchants/nigel_the_bartender.json b/world/merchants/nigel_the_bartender.json
index be98637d..675935c7 100644
--- a/world/merchants/nigel_the_bartender.json
+++ b/world/merchants/nigel_the_bartender.json
@@ -4,7 +4,7 @@
     377
   ],
   "name": "nigel the bartender",
-  "colorName": "\u001b[1m\u001b[36mnigel the bartender\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@nigel the bartender@c-reset@",
   "validTriggers": [
     "nigel the bartender",
     "bartender",
diff --git a/world/merchants/old_wise_man.json b/world/merchants/old_wise_man.json
index 08bd894b..857359e6 100644
--- a/world/merchants/old_wise_man.json
+++ b/world/merchants/old_wise_man.json
@@ -4,7 +4,7 @@
     2
   ],
   "name": "old wise man",
-  "colorName": "\u001b[1m\u001b[36mold wise man\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@old wise man@c-reset@",
   "validTriggers": [
     "old wise man",
     "wise",
@@ -13,6 +13,6 @@
     "man",
     "m"
   ],
-  "welcomeMessage": "The \u001b[1m\u001b[36mold wise man\u001b[m can assist you in choosing a character class.\r\n",
+  "welcomeMessage": "The @c-bold-on@@c-cyan@old wise man@c-reset@ can assist you in choosing a character class.\r\n",
   "merchantType": "PLAYERCLASS_SELECTOR"
 }
\ No newline at end of file
diff --git a/world/merchants/willy_the_wizard.json b/world/merchants/willy_the_wizard.json
index 8da84c20..dace79ff 100644
--- a/world/merchants/willy_the_wizard.json
+++ b/world/merchants/willy_the_wizard.json
@@ -4,7 +4,7 @@
     98
   ],
   "name": "willy the wizard",
-  "colorName": "\u001b[1m\u001b[36mwilly the wizard\u001b[m",
+  "colorName": "@c-bold-on@@c-cyan@willy the wizard@c-reset@",
   "validTriggers": [
     "willy the wizard",
     "willy",
diff --git a/world/npcs/blood_wolf.json b/world/npcs/blood_wolf.json
index 6544bf54..6687254e 100644
--- a/world/npcs/blood_wolf.json
+++ b/world/npcs/blood_wolf.json
@@ -1,6 +1,6 @@
 {
   "name": "blood wolf",
-  "colorName": "blood \u001b[1m\u001b[35mwolf\u001b[0m",
+  "colorName": "blood @c-bold-on@@c-magenta@wolf@c-reset@",
   "stats": {
     "strength": 10,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a blood \u001b[1m\u001b[35mwolf\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a blood @c-bold-on@@c-magenta@wolf@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "PASSIVE",
   "roamAreas": [
     "BLOODRIDGE1_ZONE",
@@ -50,7 +50,6 @@
     }
   ],
   "loot": {
-    "items": [],
     "lootGoldMax": 18,
     "lootGoldMin": 16
   }
diff --git a/world/npcs/demon_cat.json b/world/npcs/demon_cat.json
index 5b5578f6..0f6087dc 100644
--- a/world/npcs/demon_cat.json
+++ b/world/npcs/demon_cat.json
@@ -1,6 +1,6 @@
 {
   "name": "demon cat",
-  "colorName": "demon \u001b[1m\u001b[35mcat\u001b[0m",
+  "colorName": "demon @c-bold-on@@c-magenta@cat@c-reset@",
   "stats": {
     "strength": 9,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a demon \u001b[1m\u001b[35mcat\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a demon @c-bold-on@@c-magenta@cat@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "PASSIVE",
   "roamAreas": [
     "NEWBIE_ZONE"
@@ -42,9 +42,6 @@
     }
   ],
   "loot": {
-    "items": [
-      "small health potion"
-    ],
     "lootGoldMax": 3,
     "lootGoldMin": 1
   }
diff --git a/world/npcs/red-eyed_bear.json b/world/npcs/red-eyed_bear.json
index cbf9498e..3cee3298 100644
--- a/world/npcs/red-eyed_bear.json
+++ b/world/npcs/red-eyed_bear.json
@@ -1,6 +1,6 @@
 {
   "name": "red-eyed bear",
-  "colorName": "red-eyed \u001b[1m\u001b[35mbear\u001b[0m",
+  "colorName": "red-eyed @c-bold-on@@c-magenta@bear@c-reset@",
   "stats": {
     "strength": 14,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a red-eyed \u001b[1m\u001b[35mbear\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a red-eyed @c-bold-on@@c-magenta@bear@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "AGGRESSIVE",
   "roamAreas": [
     "TOFT1_ZONE",
@@ -52,12 +52,6 @@
     }
   ],
   "loot": {
-    "items": [
-      "berserker helm",
-      "red claw beanie",
-      "red claw pants",
-      "red claw hoodie"
-    ],
     "lootGoldMax": 24,
     "lootGoldMin": 18
   }
diff --git a/world/npcs/swamp_bear.json b/world/npcs/swamp_bear.json
index bb064a67..3ef0fb4e 100644
--- a/world/npcs/swamp_bear.json
+++ b/world/npcs/swamp_bear.json
@@ -1,6 +1,6 @@
 {
   "name": "swamp bear",
-  "colorName": "swamp \u001b[1m\u001b[35mbear\u001b[0m",
+  "colorName": "swamp @c-bold-on@@c-magenta@bear@c-reset@",
   "stats": {
     "strength": 13,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a swamp \u001b[1m\u001b[35mbear\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a swamp @c-bold-on@@c-magenta@bear@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "PASSIVE",
   "roamAreas": [
     "TOFT1_ZONE",
@@ -51,10 +51,6 @@
     }
   ],
   "loot": {
-    "items": [
-      "berserker bracers",
-      "berserker chest"
-    ],
     "lootGoldMax": 18,
     "lootGoldMin": 12
   }
diff --git a/world/npcs/swamp_berserker.json b/world/npcs/swamp_berserker.json
index 90ecbccd..43e6f9ac 100644
--- a/world/npcs/swamp_berserker.json
+++ b/world/npcs/swamp_berserker.json
@@ -1,6 +1,6 @@
 {
   "name": "swamp berserker",
-  "colorName": "swamp \u001b[1m\u001b[35mberserker\u001b[0m",
+  "colorName": "swamp @c-bold-on@@c-magenta@berserker@c-reset@",
   "stats": {
     "strength": 10,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a swamp \u001b[1m\u001b[35mberserker\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a swamp @c-bold-on@@c-magenta@berserker@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "PASSIVE",
   "roamAreas": [
     "NORTH1_ZONE"
@@ -42,9 +42,6 @@
     }
   ],
   "loot": {
-    "items": [
-      "berserker baton"
-    ],
     "lootGoldMax": 10,
     "lootGoldMin": 5
   }
diff --git a/world/npcs/tree_berserker.json b/world/npcs/tree_berserker.json
index b0cbf189..559e52d0 100644
--- a/world/npcs/tree_berserker.json
+++ b/world/npcs/tree_berserker.json
@@ -1,6 +1,6 @@
 {
   "name": "tree berserker",
-  "colorName": "tree \u001b[1m\u001b[35mberserker\u001b[0m",
+  "colorName": "tree @c-bold-on@@c-magenta@berserker@c-reset@",
   "stats": {
     "strength": 15,
     "intelligence": 0,
@@ -21,7 +21,7 @@
     "inventorySize": 0,
     "maxEffects": 0
   },
-  "dieMessage": "a tree \u001b[1m\u001b[35mberserker\u001b[0m breathes his last breath in a pool of \u001b[1m\u001b[31mblood\u001b[0m",
+  "dieMessage": "a tree @c-bold-on@@c-magenta@berserker@c-reset@ breathes his last breath in a pool of @c-bold-on@@c-red@blood@c-reset@",
   "temperament": "PASSIVE",
   "roamAreas": [
     "NORTH1_ZONE"
@@ -43,11 +43,6 @@
     }
   ],
   "loot": {
-    "items": [
-      "beserker boots",
-      "beserker shorts",
-      "small health potion"
-    ],
     "lootGoldMax": 14,
     "lootGoldMin": 8
   }
-- 
GitLab