public override void CopyPrototypeValuesToInstance(CharacterData inInstance)
    {
        base.CopyPrototypeValuesToInstance(inInstance);

        EnemyCharacterData ecd = (EnemyCharacterData)inInstance;

        ecd.SetStorageId(storageId);
        ecd.SetRankAndSize(rank, size);
        ecd.SetSpawnValues(spawnRate, areaSpawnRange.min, areaSpawnRange.max);
        ecd.SetItemDrops(dropRate, itemDrops);
        ecd.SetVisualValues(fps, frames, shadowSize, shadowOffset, fadeAmt);
        ecd.SetFlightValue(flyHeight, flySpeed);
        ecd.SetProjectileOffset(projectileSpawnOffset);
        ecd.SetVariableTargetPreference(variableTargetPreference);

        ecd.spawnAreas = spawnAreas;
    }
    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);
        }
    }