// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com

package engine.objects;

import engine.math.Vector3fImmutable;
import engine.mbEnums;
import org.json.JSONArray;
import org.json.JSONObject;
import org.pmw.tinylog.Logger;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;

public class ItemTemplate {

    // MB Dev Notes:
    // This class parses JSON data generated by mbEditor Pro 2.1.
    // Cache definitions are loaded for ItemType.ITEM, ItemType,DEED
    // and ItemType.RUNE.
    //
    // Exported JSON data is stored in db table static_item_templates
    // and can be easily mass updated using tooling available on the Wiki.
    //
    // Always read never write!
    // Use copies of fields to avoid side effects.

    // Global template lookup

    public static HashMap<Integer, ItemTemplate> templates = new HashMap<>();
    public int template_id;

    // Template Properties

    public String obj_name;
    public boolean obj_pickable;
    public Vector3fImmutable obj_scale;
    public Vector3fImmutable obj_forward_vector;
    public Vector3fImmutable obj_default_alignment;
    public int obj_render_object;
    public int obj_icon;
    public final HashMap<String, String> obj_sparse_data = new HashMap<>();
    public float combat_health_current;
    public float combat_health_full;
    public final HashMap<String, Float> combat_attack_resist = new HashMap<>();
    public mbEnums.ItemType item_type;
    public int item_eq_slots_value;
    public boolean item_eq_slots_type;
    public final EnumSet<mbEnums.EquipSlotType> item_eq_slots_or = EnumSet.noneOf(mbEnums.EquipSlotType.class);
    public final EnumSet<mbEnums.EquipSlotType> item_eq_slots_and = EnumSet.noneOf(mbEnums.EquipSlotType.class);
    public boolean item_takeable;
    public int item_value;
    public int item_wt;
    public float item_passive_defense_mod;
    public String item_base_name;
    public String item_dsc;
    public int item_render_object_female;
    public float item_health_full;
    public String item_skill_used = "";
    public String item_skill_mastery_used = "";
    public int item_parry_anim_id;
    public float item_bulk_factor;
    public final HashMap<mbEnums.ShrineType, Integer> item_offering_info = new HashMap<>();
    public int item_defense_rating;
    public float item_weapon_wepspeed;
    public float item_weapon_max_range;
    public int item_weapon_projectile_id;
    public float item_weapon_projectile_speed;
    public int item_weapon_combat_idle_anim;
    public final HashMap<mbEnums.DamageType, int[]> item_weapon_damage = new HashMap<>();
    public final ArrayList<int[]> weapon_attack_anim_right = new ArrayList<>();
    public final ArrayList<int[]> weapon_attack_anim_left = new ArrayList<>();
    public mbEnums.AttributeType item_primary_attr = mbEnums.AttributeType.None;
    public mbEnums.AttributeType item_secondary_attr = mbEnums.AttributeType.None;
    public final EnumSet<mbEnums.ItemFlags> item_flags = EnumSet.noneOf(mbEnums.ItemFlags.class);
    public final EnumSet<mbEnums.ItemUseFlags> item_use_flags = EnumSet.noneOf(mbEnums.ItemUseFlags.class);
    public int item_initial_charges;
    public final HashMap<String, Integer> item_skill_required = new HashMap<>();
    public final EnumSet<mbEnums.MonsterType> item_race_req = EnumSet.noneOf(mbEnums.MonsterType.class);
    public final EnumSet<mbEnums.MonsterType> item_race_res = EnumSet.noneOf(mbEnums.MonsterType.class);
    public final EnumSet<mbEnums.ClassType> item_class_req = EnumSet.noneOf(mbEnums.ClassType.class);
    public final EnumSet<mbEnums.ClassType> item_class_res = EnumSet.noneOf(mbEnums.ClassType.class);
    public final EnumSet<mbEnums.DisciplineType> item_disc_req = EnumSet.noneOf(mbEnums.DisciplineType.class);
    public final EnumSet<mbEnums.DisciplineType> item_disc_res = EnumSet.noneOf(mbEnums.DisciplineType.class);
    public int item_level_req;
    public mbEnums.SexType item_sex_req;
    public final HashMap<String, int[]> item_user_power_action = new HashMap<>();
    public final HashMap<String, Integer> item_power_grant = new HashMap<>();
    public final HashMap<String, int[]> item_power_action = new HashMap<>();
    public final HashMap<mbEnums.ResourceType, Integer> item_resource_cost = new HashMap<>();
    public int modTable;
    public int deed_type;
    public int deed_furniture_id;
    public int deed_target_id;
    public int deed_employment;
    public int deed_start_rank;
    public int deed_name_lookup;
    public boolean deed_indoors;
    public boolean deed_is_fortress;
    public float deed_namelookup_val;
    public boolean deed_custom_city;
    public int deed_structure_id;
    public final HashMap<mbEnums.AttributeType, Integer> rune_attr_adj = new HashMap<>();
    public final HashMap<mbEnums.AttributeType, Integer> rune_max_attr_adj = new HashMap<>();
    public final HashMap<String, Integer> rune_skill_grant = new HashMap<>();
    public final HashMap<String, Integer> skill_granted_skills = new HashMap<>();
    public final HashMap<String, Integer> power_granted_skills = new HashMap<>();
    public String rune_type;
    public String rune_sub_type;
    public boolean rune_is_standard_character_creation = false;
    public int rune_creation_cost;
    public int rune_rank;
    public int rune_pracs_per_level;
    public float rune_exp_req_to_level;
    public mbEnums.SexType rune_sex;
    public int rune_class_icon;
    public int rune_health;
    public int rune_mana;
    public int rune_stamina;
    public float rune_min_damage;
    public float rune_max_damage;
    public int rune_attack;
    public int rune_defense;
    public int rune_level;
    public final HashMap<mbEnums.MovementType, Float> rune_speed = new HashMap<>();
    public int rune_group_type;
    public boolean rune_group_is_faction = false;
    public boolean rune_group_is_guild = false;
    public String rune_dsc;
    public String rune_fx_txt;
    public final EnumSet<mbEnums.MobBehaviourType> rune_group_tactics = EnumSet.noneOf(mbEnums.MobBehaviourType.class);
    public final EnumSet<mbEnums.MobBehaviourType> rune_group_role_set = EnumSet.noneOf(mbEnums.MobBehaviourType.class);
    public boolean rune_renderable = false;
    public int rune_natural_power_attack;
    public final HashMap<String, String> rune_sparse_data = new HashMap<>();
    public final HashMap<String, int[]> rune_skill_adj = new HashMap<>();
    public final EnumSet<mbEnums.MonsterType> rune_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
    public final EnumSet<mbEnums.MonsterType> rune_not_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
    public final ArrayList<Integer> rune_groupee_monster_types = new ArrayList<>();
    public final ArrayList<Integer> rune_helper_monster_types = new ArrayList<>();
    public int item_bane_rank;

    public ItemTemplate(JSONObject jsonObject) {

        try {
            // Reading a String

            obj_name = jsonObject.getString("obj_name");

            // Reading a boolean

            obj_pickable = jsonObject.getBoolean("obj_pickable");

            // Reading floats from an array (note always check for empty arrays)

            JSONArray scaleData = jsonObject.getJSONArray("obj_scale");

            if (scaleData.isEmpty() == false)
                obj_scale = new Vector3fImmutable(scaleData.getFloat(0), scaleData.getFloat(1),
                        scaleData.getFloat(2));

            JSONArray forwardVector = jsonObject.getJSONArray("obj_forward_vector");

            if (forwardVector.isEmpty() == false)
                obj_forward_vector = new Vector3fImmutable(forwardVector.getFloat(0), forwardVector.getFloat(1),
                        forwardVector.getFloat(2));

            JSONArray defaultAlignment = jsonObject.getJSONArray("obj_default_alignment");

            if (defaultAlignment.isEmpty() == false)
                obj_default_alignment = new Vector3fImmutable(defaultAlignment.getFloat(0), defaultAlignment.getFloat(1),
                        defaultAlignment.getFloat(2));

            // Reading an integer value

            obj_render_object = jsonObject.getInt("obj_render_object");
            obj_icon = jsonObject.getInt("obj_icon");

            JSONObject obj_sparse_json = jsonObject.getJSONObject("obj_sparse_data");

            for (String sparseType : obj_sparse_json.keySet()) {
                Object sparseValue = obj_sparse_json.get(sparseType);
                obj_sparse_data.put(sparseType, sparseValue.toString());
            }

            // Banes are defined by their sparse data field

            if (obj_sparse_data.get("ACTIONRESPONSE") != null)
                if (obj_sparse_data.get("ACTIONRESPONSE").equals("4250517122"))
                    item_bane_rank = jsonObject.getInt("item_bane_rank");

            // Reading float values

            combat_health_current = jsonObject.getFloat("combat_health_current");
            combat_health_full = jsonObject.getFloat("combat_health_full");

            // Reading a hashmap of floats

            JSONObject resist_json = jsonObject.getJSONObject("combat_attack_resist");

            for (String resistType : resist_json.keySet()) {
                float resistValue = resist_json.getFloat(resistType);
                combat_attack_resist.put(resistType, resistValue);
            }

            // Parsing an enum

            item_type = mbEnums.ItemType.valueOf(jsonObject.getString("item_type"));
            item_eq_slots_value = jsonObject.getInt("item_eq_slots_value");
            item_eq_slots_type = jsonObject.getBoolean("item_eq_slots_type");

            // Parsing an enumset

            JSONArray eq_slots_or = jsonObject.getJSONArray("item_eq_slots_or");

            if (eq_slots_or.isEmpty() == false)
                for (Object o : eq_slots_or)
                    item_eq_slots_or.add(mbEnums.EquipSlotType.valueOf((String) o));

            JSONArray eq_slots_and = jsonObject.getJSONArray("item_eq_slots_and");

            if (eq_slots_and.isEmpty() == false)
                for (Object o : eq_slots_and)
                    item_eq_slots_and.add(mbEnums.EquipSlotType.valueOf((String) o));

            item_takeable = jsonObject.getBoolean("item_takeable");
            item_value = (jsonObject.getInt("item_value"));
            item_wt = jsonObject.getInt("item_wt");
            item_passive_defense_mod = jsonObject.getFloat("item_passive_defense_mod");
            item_base_name = jsonObject.getString("item_base_name");
            item_dsc = jsonObject.getString("item_dsc");
            item_render_object_female = jsonObject.getInt("item_render_object_female");
            item_health_full = jsonObject.getFloat("item_health_full");

            Object skill_used = jsonObject.get("item_skill_used");

            if (skill_used instanceof String) {
                if (skill_used.equals("Cloth") == false)
                    item_skill_used = (String) skill_used;
            }

            Object mastery_used = jsonObject.get("item_skill_mastery_used");

            if (mastery_used instanceof String)
                item_skill_mastery_used = (String) mastery_used;

            item_parry_anim_id = jsonObject.getInt("item_parry_anim_id");

            // Reading offering data

            JSONArray offering_data = jsonObject.getJSONArray("item_offering_info");

            for (Object entry : offering_data) {
                JSONObject offering_entry = (JSONObject) entry;
                String offering_type = offering_entry.getString("offering_type").replaceAll("-", "");
                int offering_value = offering_entry.getInt("offering_value");
                item_offering_info.put(mbEnums.ShrineType.valueOf(offering_type), offering_value);
            }

            // Fields only present for ARMOR

            if (item_type.equals(mbEnums.ItemType.ARMOR)) {
                item_bulk_factor = jsonObject.getFloat("item_bulk_factor");
                item_defense_rating = jsonObject.getInt("item_defense_rating");
            }

            // Fields only present for WEAPON

            if (item_type.equals(mbEnums.ItemType.WEAPON)) {

                JSONObject item_weapon = jsonObject.getJSONObject("item_weapon");
                item_weapon_wepspeed = item_weapon.getFloat("weapon_wepspeed");
                item_weapon_max_range = item_weapon.getFloat("weapon_max_range");
                item_weapon_projectile_id = item_weapon.getInt("weapon_projectile_id");
                item_weapon_projectile_speed = item_weapon.getFloat("weapon_projectile_speed");
                item_weapon_combat_idle_anim = item_weapon.getInt("weapon_combat_idle_anim");

                JSONArray weapon_damage = item_weapon.getJSONArray("weapon_damage");

                if (weapon_damage.isEmpty() == false)
                    for (Object o : weapon_damage) {
                        JSONObject damage_entry = (JSONObject) o;
                        mbEnums.DamageType damageType = mbEnums.DamageType.getDamageType(damage_entry.getString("damage_type"));
                        int min = damage_entry.getInt("damage_min");
                        int max = damage_entry.getInt("damage_max");
                        int[] minMax = {min, max};
                        item_weapon_damage.put(damageType, minMax);
                    }

                JSONArray attack_anim_right = item_weapon.getJSONArray("weapon_attack_anim_right");

                if (attack_anim_right.isEmpty() == false)
                    for (Object o : attack_anim_right) {
                        JSONArray animationEntry = (JSONArray) o;
                        int animation = animationEntry.getInt(0);
                        int duration = animationEntry.getInt(1);
                        weapon_attack_anim_right.add(new int[]{animation, duration});
                    }

                JSONArray attack_anim_left = item_weapon.getJSONArray("weapon_attack_anim_left");

                if (attack_anim_left.isEmpty() == false)
                    for (Object o : attack_anim_left) {
                        JSONArray animationEntry = (JSONArray) o;
                        int animation = animationEntry.getInt(0);
                        int duration = animationEntry.getInt(1);
                        weapon_attack_anim_left.add(new int[]{animation, duration});
                    }

                item_primary_attr = mbEnums.AttributeType.valueOf(jsonObject.getString("item_primary_attr"));
                item_secondary_attr = mbEnums.AttributeType.valueOf(jsonObject.getString("item_secondary_attr"));
            }

            JSONArray itemflags = jsonObject.getJSONArray("item_flags");

            if (itemflags.isEmpty() == false)
                for (Object o : itemflags) {
                    String flag = (String) o;
                    if (flag.equals("None") == false)
                        item_flags.add(mbEnums.ItemFlags.valueOf((String) o));
                }

            JSONArray itemUseflags = jsonObject.getJSONArray("item_use_flags");

            if (itemUseflags.isEmpty() == false)
                for (Object o : itemUseflags)
                    item_use_flags.add(mbEnums.ItemUseFlags.valueOf((String) o));

            item_initial_charges = jsonObject.getInt("item_initial_charges");

            JSONArray skill_required = jsonObject.getJSONArray("item_skill_req");

            if (skill_required.isEmpty() == false)
                for (Object o : skill_required) {
                    JSONObject skill_req = (JSONObject) o;
                    String skill_type = skill_req.getString("skill_type");
                    int skill_level = skill_req.getInt("skill_level");

                    if (skill_type.equals("Cloth") == false)
                        item_skill_required.put(skill_type, skill_level);
                }

            JSONObject race_required = jsonObject.getJSONObject("item_race_req");
            boolean restrict = race_required.getBoolean("restrict");
            JSONArray races = race_required.getJSONArray("races");

            if (races.isEmpty() == false)
                for (Object o : races) {
                    String race = (String) o;
                    race = race.replaceAll("\\s", "");
                    race = race.replaceAll(",", "");
                    race = race.replaceAll("-", "");

                    if (restrict)
                        item_race_res.add(mbEnums.MonsterType.valueOf(race));
                    else
                        item_race_req.add(mbEnums.MonsterType.valueOf(race));
                }

            JSONObject class_required = jsonObject.getJSONObject("item_class_req");
            restrict = class_required.getBoolean("restrict");
            JSONArray classes = class_required.getJSONArray("classes");

            if (classes.isEmpty() == false)
                for (Object o : classes) {
                    String classEntry = (String) o;
                    classEntry = classEntry.replaceAll("\\s", "");
                    classEntry = classEntry.replaceAll(",", "");

                    if (restrict)
                        item_class_res.add(mbEnums.ClassType.valueOf(classEntry));
                    else
                        item_class_req.add(mbEnums.ClassType.valueOf(classEntry));
                }

            JSONObject disc_required = jsonObject.getJSONObject("item_disc_req");
            restrict = disc_required.getBoolean("restrict");
            JSONArray discs = disc_required.getJSONArray("discs");

            if (discs.isEmpty() == false)
                for (Object o : discs) {
                    String disc = (String) o;
                    disc = disc.replaceAll("\\s", "");
                    disc = disc.replaceAll(",", "");

                    if (restrict)
                        item_disc_res.add(mbEnums.DisciplineType.valueOf(disc));
                    else
                        item_disc_req.add(mbEnums.DisciplineType.valueOf(disc));
                }

            item_level_req = jsonObject.getInt("item_level_req");
            item_sex_req = mbEnums.SexType.valueOf(jsonObject.getString("item_sex_req"));

            JSONArray userPowerActions = jsonObject.getJSONArray("item_user_power_action");

            if (userPowerActions.isEmpty() == false)
                for (Object o : userPowerActions) {
                    JSONObject powerActionEntry = (JSONObject) o;
                    String power = powerActionEntry.getString("power");
                    JSONArray args = powerActionEntry.getJSONArray("arguments");
                    int[] arguments = {args.getInt(0), args.getInt(1)};
                    item_user_power_action.put(power, arguments);
                }

            JSONArray powerGrantsArray = jsonObject.getJSONArray("item_power_grant");

            if (powerGrantsArray.isEmpty() == false) {

                for (Object grantArrayEntry : powerGrantsArray) {
                    JSONObject powerGrantEntry = (JSONObject) grantArrayEntry;
                    String power_type = powerGrantEntry.getString("power_type");
                    int power_value = powerGrantEntry.getInt("power_value");
                    item_power_grant.put(power_type, power_value);

                    JSONArray skill_granted_array = powerGrantEntry.getJSONArray("power_granted_skills");

                    for (Object skillGrantEntry : skill_granted_array) {
                        JSONArray skill_entry = (JSONArray) skillGrantEntry;
                        String skill_type = skill_entry.getString(0);
                        int skill_level = skill_entry.getInt(1);
                        power_granted_skills.put(skill_type, skill_level);
                    }
                }
            }

            JSONArray item_power_actions = jsonObject.getJSONArray("item_power_action");

            if (item_power_actions.isEmpty() == false)
                for (Object o : item_power_actions) {
                    JSONObject powerActionEntry = (JSONObject) o;
                    String power = powerActionEntry.getString("power_type");
                    JSONArray power_actions = powerActionEntry.getJSONArray("power_actions");
                    JSONObject argument_entry = power_actions.getJSONObject(0);
                    JSONArray power_args = argument_entry.getJSONArray("power_arguments");
                    int[] power_arguments = {power_args.getInt(0), power_args.getInt(1)};
                    item_power_action.put(power, power_arguments);
                }

            JSONArray resource_costs = jsonObject.getJSONArray("item_resource_costs");

            if (resource_costs.isEmpty() == false)
                for (Object o : resource_costs) {
                    JSONObject resource_entry = (JSONObject) o;
                    mbEnums.ResourceType resource_type = mbEnums.ResourceType.valueOf(resource_entry.getString("resource_type").toUpperCase());
                    int resource_value = resource_entry.getInt("resource_value");
                    item_resource_cost.put(resource_type, resource_value);
                }

            // Deed related fields

            if (item_type.equals(mbEnums.ItemType.DEED)) {

                deed_type = jsonObject.getInt("deed_type");
                deed_furniture_id = jsonObject.getInt("deed_furniture_id");
                deed_target_id = jsonObject.getInt("deed_target_id");
                deed_employment = jsonObject.getInt("deed_employment");
                deed_start_rank = jsonObject.getInt("deed_start_rank");
                deed_name_lookup = jsonObject.getInt("deed_name_lookup");
                deed_indoors = jsonObject.getBoolean("deed_indoors");
                deed_is_fortress = jsonObject.getBoolean("deed_is_fortress");
                deed_namelookup_val = jsonObject.getFloat("deed_namelookup_val");
                deed_custom_city = jsonObject.getBoolean("deed_custom_city");
                deed_structure_id = jsonObject.getInt("deed_structure_id");
            }

            if (item_type.equals(mbEnums.ItemType.RUNE)) {

                rune_type = jsonObject.getString("rune_type");

                Object subType = jsonObject.get("rune_sub_type");

                if (subType instanceof String)
                    rune_sub_type = (String) subType;

                rune_is_standard_character_creation = jsonObject.getBoolean("rune_is_standard_character_creation");
                rune_creation_cost = jsonObject.getInt("rune_creation_cost");
                rune_rank = jsonObject.getInt("rune_rank");
                rune_pracs_per_level = jsonObject.getInt("rune_pracs_per_level");
                rune_exp_req_to_level = jsonObject.getFloat("rune_exp_req_to_level");
                rune_sex = mbEnums.SexType.valueOf(jsonObject.getString("rune_sex"));
                rune_class_icon = jsonObject.getInt("rune_class_icon");
                rune_health = jsonObject.getInt("rune_health");
                rune_mana = jsonObject.getInt("rune_mana");
                rune_stamina = jsonObject.getInt("rune_stamina");
                rune_min_damage = jsonObject.getFloat("rune_min_damage");
                rune_max_damage = jsonObject.getFloat("rune_max_damage");
                rune_attack = jsonObject.getInt("rune_attack");
                rune_defense = jsonObject.getInt("rune_defense");
                rune_level = jsonObject.getInt("rune_level");

                JSONObject rune_speed_json = jsonObject.getJSONObject("rune_speed");

                for (String key : rune_speed_json.keySet()) {
                    mbEnums.MovementType movementType = mbEnums.MovementType.valueOf(key);
                    float speed = rune_speed_json.getFloat(key);
                    rune_speed.put(movementType, speed);
                }

                JSONObject rune_group = jsonObject.getJSONObject("rune_group");

                rune_group_type = rune_group.getInt("group_type");
                rune_group_is_faction = rune_group.getBoolean("group_is_faction");
                rune_group_is_guild = rune_group.getBoolean("group_is_guild");
                rune_dsc = jsonObject.getString("rune_dsc");
                rune_fx_txt = jsonObject.getString("rune_fx_txt");

                long group_tactics_bitvector = jsonObject.getLong("rune_group_tactics");

                rune_group_tactics.addAll(mbEnums.fromLong(group_tactics_bitvector, mbEnums.MobBehaviourType.class));

                long group_role_bitvector = jsonObject.getLong("rune_group_role_set");

                rune_group_role_set.addAll(mbEnums.fromLong(group_role_bitvector, mbEnums.MobBehaviourType.class));

                JSONArray enemy_types_json = jsonObject.getJSONArray("rune_enemy_monster_types");

                for (Object o : enemy_types_json) {
                    String enemy = (String) o;
                    enemy = enemy.replaceAll("-", "");
                    mbEnums.MonsterType monsterType = mbEnums.MonsterType.valueOf(enemy);
                    rune_enemy_monster_types.add(monsterType);
                }

                JSONArray not_enemy_types_json = jsonObject.getJSONArray("rune_not_enemy_monster_types");

                for (Object o : not_enemy_types_json) {
                    String notenemy = (String) o;
                    notenemy = notenemy.replaceAll("-", "");
                    mbEnums.MonsterType monsterType = mbEnums.MonsterType.valueOf(notenemy);
                    rune_not_enemy_monster_types.add(monsterType);
                }

                JSONArray groupee_types_json = jsonObject.getJSONArray("rune_groupee_monster_types");

                for (Object o : groupee_types_json) {
                    int groupeeId = (int) o;
                    rune_groupee_monster_types.add(groupeeId);
                }

                JSONArray helper_types_json = jsonObject.getJSONArray("rune_helper_monster_types");

                for (Object o : helper_types_json) {
                    int helperId = (int) o;
                    rune_helper_monster_types.add(helperId);
                }

                rune_renderable = jsonObject.getBoolean("rune_renderable");
                rune_natural_power_attack = jsonObject.getInt("rune_natural_power_attack");

                JSONObject rune_sparse_json = jsonObject.getJSONObject("rune_sparse_data");

                for (String sparseType : rune_sparse_json.keySet()) {
                    Object sparseValue = rune_sparse_json.get(sparseType);
                    rune_sparse_data.put(sparseType, sparseValue.toString());
                }

                JSONArray skill_adj_json = (JSONArray) jsonObject.get("rune_skill_adj");

                for (Object skillEntry : skill_adj_json) {
                    JSONObject skill = (JSONObject) skillEntry;
                    String typeString = skill.getString("skill_type");
                    JSONArray adjArray = skill.getJSONArray("skill_adjusts");

                    for (Object adjustList : adjArray) {
                        JSONArray adjustEntry = (JSONArray) adjustList;
                        int[] adjust = {adjustEntry.getInt(0), adjustEntry.getInt(1)};
                        rune_skill_adj.put(typeString, adjust);
                    }

                }
                JSONArray attr_adj_json = jsonObject.getJSONArray("rune_attr_adj");

                for (Object attributeEntry : attr_adj_json) {
                    JSONObject attribute = (JSONObject) attributeEntry;
                    String typeString = attribute.getString("attr_type");
                    mbEnums.AttributeType attributeType = mbEnums.AttributeType.valueOf(typeString);
                    int attributeValue = attribute.getInt("attr_value");
                    rune_attr_adj.put(attributeType, attributeValue);
                }

                JSONArray max_attr_adj_json = jsonObject.getJSONArray("rune_max_attr_adj");

                for (Object attributeEntry : max_attr_adj_json) {
                    JSONObject attribute = (JSONObject) attributeEntry;
                    String typeString = attribute.getString("attr_type");
                    mbEnums.AttributeType attributeType = mbEnums.AttributeType.valueOf(typeString);
                    int attributeValue = attribute.getInt("attr_value");
                    rune_max_attr_adj.put(attributeType, attributeValue);
                }

                JSONArray skill_grant_json = jsonObject.getJSONArray("rune_skill_grant");

                if (skill_grant_json.isEmpty() == false)
                    for (Object runeSkillEntry : skill_grant_json) {
                        JSONObject skill_granted = (JSONObject) runeSkillEntry;
                        String typeString = skill_granted.getString("skill_type");
                        int skill_level = skill_granted.getInt("skill_value");
                        rune_skill_grant.put(typeString, skill_level);

                        JSONArray skill_granted_array = skill_granted.getJSONArray("skill_granted_skills");

                        for (Object skillGrantEntry : skill_granted_array) {
                            JSONArray skill_entry = (JSONArray) skillGrantEntry;
                            typeString = skill_entry.getString(0);
                            skill_level = skill_entry.getInt(1);
                            skill_granted_skills.put(typeString, skill_level);
                        }
                    }
            }

        } catch (Exception e) {
            Logger.error(obj_name + ":" + e);
        }
    }

}
