public static void CreateItems()
    {
        items = new Dictionary <string, Item>();

        //use this line to load from a json file
        //ItemMap map = Utils.LoadJsonFromPath<ItemMap>("data/item_data");

        /*
         *      {"id":"3:wooden_sword",                                              "type":"WEAPON",         "stats" : ["atk=2", "crit_chance=1","crit_bonus=10"],                                                           "spawn_rate":"15",        "min_spawn_lvl":"5",          "max_spawn_lvl":"0",          "area":"CAVERNS",                                         "attached_skill_ids":"",                                                                        "season":"ALL",                  "slots": [],                           "flags":"",                                               "explicit_rarity":"",                   "explicit_value":0},
         *      {"id":"4:buckler",                                                   "type":"SHIELD",         "stats" : ["hp=2"],                                                                                             "spawn_rate":"15",        "min_spawn_lvl":"5",          "max_spawn_lvl":"0",          "area":"CAVERNS",                                         "attached_skill_ids":"",                                                                        "season":"ALL",                  "slots": [],                           "flags":"",                                               "explicit_rarity":"",                   "explicit_value":0},
         *      {"id":"5:leather_armor",                                             "type":"ARMOR",          "stats" : ["hp=1"],                                                                                             "spawn_rate":"20",        "min_spawn_lvl":"5",          "max_spawn_lvl":"0",          "area":"CAVERNS",                                         "attached_skill_ids":"",                                                                        "season":"ALL",                  "slots": [],                           "flags":"",                                               "explicit_rarity":"",                   "explicit_value":0},
         *
         */

        //instead, create the data manually
        var woodenSwordData = new ItemRow()
        {
            id = "3:" + ItemId.WOODEN_SWORD, type = "WEAPON", stats = new string[] { "atk=2", "crit_chance=1", "crit_bonus=10" }, spawn_rate = 15, min_spawn_lvl = 5, max_spawn_lvl = 0, area = "CAVERNS", attached_skill_ids = "", slots = new string[] { }, flags = ""
        };
        var woodenShieldData = new ItemRow()
        {
            id = "4:" + ItemId.BUCKLER, type = "SHIELD", stats = new string[] { "hp=2" }, spawn_rate = 15, min_spawn_lvl = 5, max_spawn_lvl = 0, area = "CAVERNS", attached_skill_ids = "", slots = new string[] { }, flags = ""
        };
        var leatherArmorData = new ItemRow()
        {
            id = "5:" + ItemId.LEATHER_ARMOR, type = "ARMOR", stats = new string[] { "hp=1" }, spawn_rate = 20, min_spawn_lvl = 5, max_spawn_lvl = 0, area = "CAVERNS", attached_skill_ids = "", slots = new string[] { }, flags = ""
        };
        var peltData = new ItemRow()
        {
            id = "6:" + ItemId.PELT, type = "RESOURCE", stats = new string[] { }, spawn_rate = 0, min_spawn_lvl = 0, max_spawn_lvl = 0, area = "CAVERNS", attached_skill_ids = "", slots = new string[] { }, flags = ""
        };

        ItemMap map = new ItemMap();

        map.items = new ItemRow[] { woodenSwordData, woodenShieldData, leatherArmorData, peltData };

        ItemRow row;
        Item    tempItem;

        string[] idPieces;
        string   id;

        for (int i = 0; i < map.items.Length; i++)
        {
            row = map.items[i];

            //item ids are provied in a number:name format. the name is only present in the data file for readability, so we need to discard it
            idPieces = row.id.Split(':');
            id       = idPieces[0];

            if (!ItemId.IsValid(id))
            {
                throw new Exception("Item id '" + row.id + "' does not exist");
            }

            tempItem = new Item();
            tempItem.Init();

            tempItem.SetId(id);
            tempItem.DeriveBaseAndStorageId();
            ItemType parsedType = (ItemType)Enum.Parse(typeof(ItemType), row.type);
            tempItem.SetType(parsedType);

            Stats.ParseArrayIntoStatObject(row.stats, tempItem.baseStats);

            AdventureArea parsedAreas = Utils.ParseStringToFlagEnum <AdventureArea>(row.area, '|');
            tempItem.SetSpawnInfo(row.spawn_rate, row.min_spawn_lvl, row.max_spawn_lvl, parsedAreas);

            if (string.IsNullOrEmpty(row.explicit_rarity))
            {
                tempItem.DeriveRarity();
            }
            else
            {
                tempItem._rarity = (Rarity)Enum.Parse(typeof(Rarity), row.explicit_rarity);
            }

            char[] separators = new char[',']; //must define a separator array so that we can pass in the stringsplit option param
            tempItem.attachedSkillIds = row.attached_skill_ids.Split(separators, StringSplitOptions.RemoveEmptyEntries);
            string curId;
            for (int j = 0; j < tempItem.attachedSkillIds.Length; j++)
            {
                curId = tempItem.attachedSkillIds[j];
                if (!SkillId.IsValid(curId))
                {
                    throw new Exception(curId + " is not a valid skill id to attach to an item");
                }
            }

            int numSlots = row.slots.Length;
            tempItem.slotPositions = new Vector2[numSlots];
            string[] coords;
            for (int j = 0; j < numSlots; j++)
            {
                coords = row.slots[j].Split(',');
                tempItem.slotPositions[j] = new Vector2(float.Parse(coords[0]), float.Parse(coords[1]));
            }

            tempItem.flags = Utils.ParseStringToFlagEnum <ItemFlags>(row.flags, ',');

            if (row.explicit_value == 0)
            {
                tempItem.CalculateValue();
            }
            else
            {
                tempItem.value = row.explicit_value;
            }


            items.Add(tempItem.id, tempItem);
        }

        //record the number of un-leveled items
        numBasicItemsInGame = items.Count;

        //create leveled items
        itemLevelBoosts = new Stats();
        itemLevelBoosts.Set(Stat.hp, 3);
        itemLevelBoosts.Set(Stat.mp, 4);
        itemLevelBoosts.Set(Stat.atk, 2);
        itemLevelBoosts.Set(Stat.crit_chance, 1);
        itemLevelBoosts.Set(Stat.crit_bonus, 1);
        itemLevelBoosts.Set(Stat.dmg_reduction, 1);
        itemLevelBoosts.Set(Stat.evade, 1);
        itemLevelBoosts.Set(Stat.gold_find, 3);
        itemLevelBoosts.Set(Stat.item_find, 2);
        itemLevelBoosts.Set(Stat.magic_boost, 2);
        itemLevelBoosts.Set(Stat.phys_boost, 2);
        itemLevelBoosts.Set(Stat.hp_boost, 1);
        itemLevelBoosts.Set(Stat.mp_boost, 1);
        itemLevelBoosts.Set(Stat.atk_boost, 1);
        itemLevelBoosts.Set(Stat.dmg_reflection, 2);
        itemLevelBoosts.Set(Stat.mastery_xp_boost, 2);

        itemTypesThatLevelUp = ItemType.WEAPON | ItemType.ARMOR | ItemType.SHIELD;
        List <Item> existingItems = items.Values.ToList();
        Item        newItem;
        int         spawnRange = 100;

        for (int i = 0; i < existingItems.Count; i++)
        {
            tempItem = existingItems[i];

            if (tempItem.IsTypeThatLevelsUp())
            {
                //TODO manually exclude some stuff from leveling up
                if (tempItem.id == ItemId.APHOTIC_BLADE || tempItem.id == ItemId.MOP || tempItem.id == ItemId.IRON_SKILLET)
                {
                    continue;
                }

                //force all items to have a max spawn level of min + 100
                tempItem._spawnMaxLevel = tempItem.spawnMinLevel + spawnRange;

                //right now just levels 2 to the "max"
                for (int j = 2; j <= GameContext.MAX_ITEM_LEVEL_WITHOUT_ENHANCING; j++)
                {
                    newItem = tempItem.GetCopy();
                    newItem.BoostItemToLevel(j);
                    items.Add(newItem.id, newItem);

                    //once item is level 5 or higher it can ALWAYS spawn, otherwise limit it
                    if (j > 4)
                    {
                        newItem._spawnMaxLevel = 0;
                    }
                    else
                    {
                        newItem._spawnMaxLevel = newItem.spawnMinLevel + spawnRange;
                    }
                }
            }
        }

        //copy ALL items into a parallel list for convenience
        itemCollectionList = items.Values.ToList();

        //create specialized lists
        foodItemIds = new List <string>()
        {
            ItemId.APPLE, ItemId.BACON, ItemId.CARROT, ItemId.CARAMEL, ItemId.EGGPLANT, ItemId.MUSHROOM, ItemId.RADISH
        };



        //build this dict to make status effect prevention checks much faster
        statusEffectToPreventionFlagMap = new Dictionary <StatusEffectType, ItemFlags>();
        statusEffectToPreventionFlagMap[StatusEffectType.BURN]       = ItemFlags.PREVENTS_BURN;
        statusEffectToPreventionFlagMap[StatusEffectType.STONE]      = ItemFlags.PREVENTS_STONE;
        statusEffectToPreventionFlagMap[StatusEffectType.SPEED_DOWN] = ItemFlags.PREVENTS_SPEED_DOWN;
        statusEffectToPreventionFlagMap[StatusEffectType.SLEEP]      = ItemFlags.PREVENTS_SLEEP;
    }
    public static void CreateEnemyCharacters()
    {
        enemyCollection = new Dictionary <string, EnemyCharacterData>();

        //use this line to load from a json file
        //EnemyCharacterMap map = Utils.LoadJsonFromPath<EnemyCharacterMap>(inPath);

        /*
         *     {"id":"1:rat",                                                  "area": "CAVERNS|JAIL",     "rank":"NORM",        "size": "MD",               "base_stats" : ["hp=2",     "atk=1",      "spd=default"],                "spawn_rate": 1,        "area_spawn_range": "0-50",       "drop_rate":"3",                "drops": "6",                                                                   "fps":4,        "frames":3,           "shadow_size": 7,      "shadow_offset":0,         "fly_height": 0,        "fly_speed": 0,          "projectile_spawn_offset":"0,0",          "fade_amt":0,              "flags": "CANT_STRIKE",                                                      "variable_target_preference":1,     "skills":"CLAW"},
         *     {"id":"11:tunneler",                                          "area": "CAVERNS",          "rank":"MBOS",        "size": "LG",               "base_stats" : ["hp=8",     "atk=2",      "spd=default"],                "spawn_rate": 1,        "area_spawn_range": "0-50",       "drop_rate":"1",                "drops": "3",                                                                   "fps":4,        "frames":3,           "shadow_size": 0,      "shadow_offset":0,         "fly_height": 0,        "fly_speed": 0,          "projectile_spawn_offset":"0,0",          "fade_amt":0,              "flags": "IN_GROUND",                                                        "variable_target_preference":1,     "skills":""},
         *     {"id":"13:dragon",                                          "area": "CAVERNS",          "rank":"BOSS",        "size": "XL",               "base_stats" : ["hp=28",    "atk=3",      "spd=default"],                "spawn_rate": 1,        "area_spawn_range": "0-50",       "drop_rate":"50",               "drops": "55",                                                                  "fps":4,        "frames":3,           "shadow_size": 23,     "shadow_offset":0,         "fly_height": 30,       "fly_speed": 10,         "projectile_spawn_offset":"0,0",          "fade_amt":0,              "flags": "",                                                                 "variable_target_preference":1,     "skills":"METEOR"},
         */

        //instead, create the data manually
        var ratData = new EnemyCharacterRow()
        {
            id = "1:" + EnemyId.RAT, area = "CAVERNS|JAIL", rank = "NORM", size = "MD", base_stats = new string[] { "hp=2", "atk=1", "spd=default" }, spawn_rate = 1, area_spawn_range = "0-50", drop_rate = "3", drops = "6", flags = "", variable_target_preference = 1, skills = ""
        };
        var tunnelerData = new EnemyCharacterRow()
        {
            id = "11:" + EnemyId.TUNNELER, area = "CAVERNS", rank = "MBOS", size = "MD", base_stats = new string[] { "hp=8", "atk=2", "spd=default" }, spawn_rate = 1, area_spawn_range = "0-50", drop_rate = "1", drops = "", flags = "IN_GROUND", variable_target_preference = 1, skills = ""
        };
        var dragonData = new EnemyCharacterRow()
        {
            id = "13:" + EnemyId.DRAGON, area = "CAVERNS", rank = "BOSS", size = "MD", base_stats = new string[] { "hp=28", "atk=3", "spd=default" }, spawn_rate = 1, area_spawn_range = "0-50", drop_rate = "50", drops = "", flags = "", variable_target_preference = 1, skills = "METEOR"
        };

        EnemyCharacterMap map = new EnemyCharacterMap();

        map.enemy_characters = new EnemyCharacterRow[] { ratData, tunnelerData, dragonData };

        EnemyCharacterRow  row;
        EnemyCharacterData tempCharacter;

        string[] idPieces;

        for (int i = 0; i < map.enemy_characters.Length; i++)
        {
            row           = map.enemy_characters[i];
            tempCharacter = new EnemyCharacterData();
            tempCharacter.Init();

            idPieces = row.id.Split(':');
            int storageId = int.Parse(idPieces[0]);

            tempCharacter.SetId(idPieces[1]);
            tempCharacter.SetStorageId(storageId);
            tempCharacter.SetSpecies(Species.CREATURE);
            tempCharacter.SetBaseFaction(Faction.ENEMY);

            Stats.ParseArrayIntoStatObject(row.base_stats, tempCharacter.baseStats);

            tempCharacter.spawnAreas = Utils.ParseStringToFlagEnum <AdventureArea>(row.area, '|');

            EnemyRank parsedRank = (EnemyRank)Enum.Parse(typeof(EnemyRank), row.rank);
            EnemySize parsedSize = (EnemySize)Enum.Parse(typeof(EnemySize), row.size);
            tempCharacter.SetRankAndSize(parsedRank, parsedSize);

            int      spawnRate = row.spawn_rate;
            string[] rangeInfo = row.area_spawn_range.Split('-');
            if (rangeInfo.Length != 2)
            {
                throw new Exception("Spawn range for enemy " + row.id + " is not exactly two elements");
            }
            int minSpawnRange = int.Parse(rangeInfo[0]);
            int maxSpawnRange = int.Parse(rangeInfo[1]);
            tempCharacter.SetSpawnValues(spawnRate, minSpawnRange, maxSpawnRange);

            string[] drops = row.drops == "" ? new string[] { } : row.drops.Split('|');
            for (int j = 0; j < drops.Length; j++)
            {
                if (!ItemId.IsValid(drops[j]))
                {
                    throw new Exception("Item drop id '" + drops[j] + "' is not valid");
                }
            }

            int dropRate = 9;
            if (row.drop_rate != "default")
            {
                dropRate = int.Parse(row.drop_rate);
            }
            tempCharacter.SetItemDrops(dropRate, drops);

            tempCharacter.SetVisualValues(row.fps, row.frames, row.shadow_size, row.shadow_offset, row.fade_amt);
            tempCharacter.SetFlightValue(row.fly_height, row.fly_speed);

            CharacterFlags flags = Utils.ParseStringToFlagEnum <CharacterFlags>(row.flags, ',');
            flags |= CharacterFlags.CANT_DEFEND;
            tempCharacter.SetFlags(flags);
            tempCharacter.SetVariableTargetPreference(row.variable_target_preference);

            if (row.skills.Length > 0)
            {
                tempCharacter.SetBaseSkills(row.skills.Split(','));
            }

            //was the cant strike flag set? only allow this if the enemy has at least one other skill that costs 0 mp
            if (tempCharacter.FlagIsSet(CharacterFlags.CANT_STRIKE))
            {
                if (tempCharacter.baseSkills[0].mpCost != 0)
                {
                    throw new Exception("0 mp Default skill not provided for enemy " + row.id + " (cant_strike flag set)");
                }
            }

            enemyCollection.Add(tempCharacter.id, tempCharacter);
            characterCollection.Add(tempCharacter.id, tempCharacter);
        }

        enemyCollectionList = enemyCollection.Values.ToList();

        if (enemyCollection.Count > GameContext.MAX_NUM_ENEMIES_SUPPPORTED)
        {
            throw new Exception("Loaded " + enemyCollection.Count + " enemy types but game only allows a max of " + GameContext.MAX_NUM_ENEMIES_SUPPPORTED);
        }
    }