// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2026 // https://www.magicbane.com package engine.parsers.cache; import engine.gameManager.ConfigManager; import engine.mbEnums; import engine.parsers.objects.RuneTemplate; import org.json.JSONArray; import org.json.JSONObject; import org.pmw.tinylog.Logger; import java.io.File; import java.util.HashMap; // Loads RUNE templates from the client cache JSON (COBJECTS/RUNE/.json). // Not yet wired into server startup. public class RuneParser { private static final String runePath = ConfigManager.DEFAULT_DATA_DIR + "COBJECTS/RUNE/"; public static HashMap parseRuneFolder() { HashMap runeTemplates = new HashMap<>(); File[] runeFiles = CacheParserUtil.listJsonFiles(runePath); int templateId; JSONObject runeJson; for (File runeFile : runeFiles) { try { templateId = CacheParserUtil.templateIdFromFile(runeFile); runeJson = CacheParserUtil.readJsonFile(runeFile); runeTemplates.put(templateId, parseRuneEntry(templateId, runeJson)); } catch (Exception e) { Logger.error(runeFile.getName() + ": " + e); } } Logger.info("Loaded " + runeTemplates.size() + " of " + runeFiles.length + " rune templates"); return runeTemplates; } private static RuneTemplate parseRuneEntry(int templateId, JSONObject runeJson) { RuneTemplate rune = new RuneTemplate(); JSONObject raceReq; JSONObject classReq; JSONObject discReq; JSONArray itemFlags; String flag; mbEnums.ItemFlags itemFlag; Object subType; JSONObject speedJson; mbEnums.MovementType movementType; JSONObject groupJson; JSONArray skillGrantsJson; JSONObject skillGrantJson; RuneTemplate.SkillGrant skillGrant; JSONArray skillAdjJson; JSONObject skillAdjEntry; JSONArray naturalAttacksJson; JSONArray inventoryJson; JSONObject inventoryEntryJson; RuneTemplate.InventoryEntry inventoryEntry; JSONArray bodyPartsJson; JSONObject bodyPartJson; RuneTemplate.BodyPart bodyPart; rune.template_id = templateId; // obj_ rune.obj_name = runeJson.getString("obj_name"); rune.obj_pickable = runeJson.getBoolean("obj_pickable"); rune.obj_gravity = runeJson.getFloat("obj_gravity"); rune.obj_cull_distance = runeJson.getFloat("obj_cull_distance"); rune.obj_scale = CacheParserUtil.readVector3(runeJson.getJSONArray("obj_scale")); rune.obj_render_object = runeJson.getInt("obj_render_object"); rune.obj_double_fusion = runeJson.getBoolean("obj_double_fusion"); rune.obj_forward_vector = CacheParserUtil.readVector3(runeJson.getJSONArray("obj_forward_vector")); rune.obj_tracking_name = runeJson.getString("obj_tracking_name"); rune.obj_max_tracking_distance = runeJson.getFloat("obj_max_tracking_distance"); rune.obj_icon = runeJson.getInt("obj_icon"); rune.obj_gravity_f = runeJson.getFloat("obj_gravity_f"); rune.obj_sound_events.putAll(CacheParserUtil.readStringValueMap(runeJson.getJSONArray("obj_sound_events"), "sound_type", "sound_event")); rune.obj_arc_hardpoint_list = runeJson.getJSONArray("obj_arc_hardpoint_list"); rune.obj_sparse_data.putAll(CacheParserUtil.readSparseData(runeJson.getJSONObject("obj_sparse_data"))); rune.obj_render_object_low_detail = runeJson.getInt("obj_render_object_low_detail"); rune.obj_default_alignment = CacheParserUtil.readVector3(runeJson.getJSONArray("obj_default_alignment")); rune.obj_sound_table = CacheParserUtil.readLabel(runeJson, "obj_sound_table"); // combat_ rune.combat_health_current = runeJson.getFloat("combat_health_current"); rune.combat_health_full = runeJson.getFloat("combat_health_full"); rune.combat_attack_resist.putAll(CacheParserUtil.readEnumFloatMap(mbEnums.DamageType.class, runeJson.getJSONObject("combat_attack_resist"), templateId, "combat_attack_resist")); rune.combat_powers = runeJson.getJSONArray("combat_powers"); // item_ rune.item_type = CacheParserUtil.readEnum(mbEnums.ItemType.class, runeJson.getString("item_type"), templateId, "item_type"); rune.item_eq_slots_value = runeJson.getInt("item_eq_slots_value"); rune.item_eq_slots_type = runeJson.getBoolean("item_eq_slots_type"); rune.item_eq_slots_or.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, runeJson.getJSONArray("item_eq_slots_or"), templateId, "item_eq_slots_or")); rune.item_eq_slots_and.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, runeJson.getJSONArray("item_eq_slots_and"), templateId, "item_eq_slots_and")); rune.item_takeable = runeJson.getBoolean("item_takeable"); rune.item_value = runeJson.getInt("item_value"); rune.item_wt = runeJson.getInt("item_wt"); rune.item_passive_defense_mod = runeJson.getFloat("item_passive_defense_mod"); rune.item_base_name = runeJson.getString("item_base_name"); rune.item_dsc = runeJson.getString("item_dsc"); rune.item_render_object_female = runeJson.getInt("item_render_object_female"); rune.item_health_full = runeJson.getFloat("item_health_full"); rune.item_skill_used = CacheParserUtil.readLabel(runeJson, "item_skill_used"); rune.item_skill_mastery_used = CacheParserUtil.readLabel(runeJson, "item_skill_mastery_used"); rune.item_parry_anim_id = runeJson.getInt("item_parry_anim_id"); rune.item_offering_info.putAll(CacheParserUtil.readEnumValueMap(mbEnums.ShrineType.class, runeJson.getJSONArray("item_offering_info"), "offering_type", "offering_value", templateId, "item_offering_info")); // "None" is the client's empty marker, not a flag itemFlags = runeJson.getJSONArray("item_flags"); for (int i = 0; i < itemFlags.length(); i++) { flag = itemFlags.getString(i); if (flag.equals("None")) continue; itemFlag = CacheParserUtil.readEnum(mbEnums.ItemFlags.class, flag, templateId, "item_flags"); if (itemFlag != null) rune.item_flags.add(itemFlag); } rune.item_use_flags.addAll(CacheParserUtil.readEnumSet(mbEnums.ItemUseFlags.class, runeJson.getJSONArray("item_use_flags"), templateId, "item_use_flags")); rune.item_post_item_id = runeJson.getInt("item_post_item_id"); rune.item_initial_charges = runeJson.getInt("item_initial_charges"); rune.item_book_arcana = runeJson.getInt("item_book_arcana"); rune.item_skill_req.putAll(CacheParserUtil.readStringValueMap(runeJson.getJSONArray("item_skill_req"), "skill_type", "skill_level")); raceReq = runeJson.getJSONObject("item_race_req"); rune.item_race_req_restrict = raceReq.getBoolean("restrict"); rune.item_race_req.addAll(CacheParserUtil.readEnumSet(mbEnums.MonsterType.class, raceReq.getJSONArray("races"), templateId, "item_race_req")); classReq = runeJson.getJSONObject("item_class_req"); rune.item_class_req_restrict = classReq.getBoolean("restrict"); rune.item_class_req.addAll(CacheParserUtil.readEnumSet(mbEnums.ClassType.class, classReq.getJSONArray("classes"), templateId, "item_class_req")); discReq = runeJson.getJSONObject("item_disc_req"); rune.item_disc_req_restrict = discReq.getBoolean("restrict"); rune.item_disc_req.addAll(CacheParserUtil.readEnumSet(mbEnums.DisciplineType.class, discReq.getJSONArray("discs"), templateId, "item_disc_req")); rune.item_attr_req.putAll(CacheParserUtil.readEnumValueMap(mbEnums.AttributeType.class, runeJson.getJSONArray("item_attr_req"), "attr_type", "attr_value", templateId, "item_attr_req")); rune.item_level_req = runeJson.getInt("item_level_req"); rune.item_rank_req = runeJson.getInt("item_rank_req"); rune.item_sex_req = CacheParserUtil.readEnum(mbEnums.SexType.class, runeJson.getString("item_sex_req"), templateId, "item_sex_req"); rune.item_user_power_action.addAll(CacheParserUtil.readUserPowerActions(runeJson.getJSONArray("item_user_power_action"))); rune.item_power_grant.addAll(CacheParserUtil.readPowerGrants(runeJson.getJSONArray("item_power_grant"))); rune.item_power_action.putAll(CacheParserUtil.readPowerActions(runeJson.getJSONArray("item_power_action"))); rune.item_sheathable = runeJson.getBoolean("item_sheathable"); rune.item_has_stub = runeJson.getBoolean("item_has_stub"); rune.item_offering_adjustments.putAll(CacheParserUtil.readEnumValueMap(mbEnums.ShrineType.class, runeJson.getJSONArray("item_offering_adjustments"), "offering_type", "offering_value", templateId, "item_offering_adjustments")); rune.item_resource_costs.putAll(CacheParserUtil.readEnumValueMap(mbEnums.ResourceType.class, runeJson.getJSONArray("item_resource_costs"), "resource_type", "resource_value", templateId, "item_resource_costs")); rune.item_bane_rank = runeJson.getInt("item_bane_rank"); rune.item_ignore_saved_actions = runeJson.getBoolean("item_ignore_saved_actions"); // rune_ rune.rune_type = CacheParserUtil.readEnum(mbEnums.RuneType.class, runeJson.getString("rune_type"), templateId, "rune_type"); subType = runeJson.get("rune_sub_type"); if (subType instanceof String) { rune.rune_sub_type = (String) subType; } else { rune.rune_sub_type = ""; rune.rune_sub_type_hash = (int) runeJson.getLong("rune_sub_type"); } rune.rune_is_standard_character_creation = runeJson.getBoolean("rune_is_standard_character_creation"); rune.rune_creation_cost = runeJson.getInt("rune_creation_cost"); rune.rune_rank = runeJson.getInt("rune_rank"); rune.rune_pracs_per_level = runeJson.getInt("rune_pracs_per_level"); rune.rune_exp_req_to_level = runeJson.getFloat("rune_exp_req_to_level"); rune.rune_sex = CacheParserUtil.readEnum(mbEnums.SexType.class, runeJson.getString("rune_sex"), templateId, "rune_sex"); rune.rune_class_icon = runeJson.getInt("rune_class_icon"); rune.rune_health = runeJson.getInt("rune_health"); rune.rune_mana = runeJson.getInt("rune_mana"); rune.rune_stamina = runeJson.getInt("rune_stamina"); rune.rune_min_damage = runeJson.getFloat("rune_min_damage"); rune.rune_max_damage = runeJson.getFloat("rune_max_damage"); rune.rune_attack = runeJson.getInt("rune_attack"); rune.rune_defense = runeJson.getInt("rune_defense"); rune.rune_level = runeJson.getInt("rune_level"); speedJson = runeJson.getJSONObject("rune_speed"); for (String speedKey : speedJson.keySet()) { movementType = CacheParserUtil.readEnum(mbEnums.MovementType.class, speedKey, templateId, "rune_speed"); if (movementType != null) rune.rune_speed.put(movementType, speedJson.getFloat(speedKey)); } groupJson = runeJson.getJSONObject("rune_group"); rune.rune_group_type = groupJson.getInt("group_type"); rune.rune_group_is_faction = groupJson.getBoolean("group_is_faction"); rune.rune_group_is_guild = groupJson.getBoolean("group_is_guild"); rune.rune_dsc = runeJson.getString("rune_dsc"); rune.rune_fx_txt = runeJson.getString("rune_fx_txt"); rune.rune_group_tactics = runeJson.getLong("rune_group_tactics"); rune.rune_group_role_set = runeJson.getLong("rune_group_role_set"); rune.rune_enemy_monster_types.addAll(CacheParserUtil.readEnumSet(mbEnums.MonsterType.class, runeJson.getJSONArray("rune_enemy_monster_types"), templateId, "rune_enemy_monster_types")); rune.rune_groupee_monster_types.addAll(CacheParserUtil.readIntList(runeJson.getJSONArray("rune_groupee_monster_types"))); rune.rune_helper_monster_types.addAll(CacheParserUtil.readIntList(runeJson.getJSONArray("rune_helper_monster_types"))); rune.rune_not_enemy_monster_types.addAll(CacheParserUtil.readEnumSet(mbEnums.MonsterType.class, runeJson.getJSONArray("rune_not_enemy_monster_types"), templateId, "rune_not_enemy_monster_types")); rune.rune_enemy_gender = runeJson.getJSONArray("rune_enemy_gender"); skillGrantsJson = runeJson.getJSONArray("rune_skill_grant"); for (int i = 0; i < skillGrantsJson.length(); i++) { skillGrantJson = skillGrantsJson.getJSONObject(i); skillGrant = new RuneTemplate.SkillGrant(); skillGrant.skill_type = skillGrantJson.getString("skill_type"); skillGrant.skill_value = skillGrantJson.getInt("skill_value"); skillGrant.skill_granted_attrs = skillGrantJson.getJSONArray("skill_granted_attrs"); skillGrant.skill_granted_skills.addAll(CacheParserUtil.readIntArrays(skillGrantJson.getJSONArray("skill_granted_skills"))); skillGrant.skill_granted_powers = skillGrantJson.getJSONArray("skill_granted_powers"); skillGrant.skill_monster_types = skillGrantJson.getJSONArray("skill_monster_types"); rune.rune_skill_grant.add(skillGrant); } skillAdjJson = runeJson.getJSONArray("rune_skill_adj"); for (int i = 0; i < skillAdjJson.length(); i++) { skillAdjEntry = skillAdjJson.getJSONObject(i); rune.rune_skill_adj.put(skillAdjEntry.getString("skill_type"), CacheParserUtil.readIntArrays(skillAdjEntry.getJSONArray("skill_adjusts"))); } rune.rune_attr_adj.putAll(CacheParserUtil.readEnumValueMap(mbEnums.AttributeType.class, runeJson.getJSONArray("rune_attr_adj"), "attr_type", "attr_value", templateId, "rune_attr_adj")); rune.rune_max_attr_adj.putAll(CacheParserUtil.readEnumValueMap(mbEnums.AttributeType.class, runeJson.getJSONArray("rune_max_attr_adj"), "attr_type", "attr_value", templateId, "rune_max_attr_adj")); naturalAttacksJson = runeJson.getJSONArray("rune_naturalattacks"); for (int i = 0; i < naturalAttacksJson.length(); i++) rune.rune_naturalattacks.add(CacheParserUtil.readFloatArray(naturalAttacksJson.getJSONArray(i))); rune.rune_enchantment_type = runeJson.getJSONArray("rune_enchantment_type"); inventoryJson = runeJson.getJSONArray("rune_inventory_contents"); for (int i = 0; i < inventoryJson.length(); i++) { inventoryEntryJson = inventoryJson.getJSONObject(i); inventoryEntry = new RuneTemplate.InventoryEntry(); inventoryEntry.type = inventoryEntryJson.getString("type"); if (inventoryEntryJson.has("gold")) inventoryEntry.values = CacheParserUtil.readFloatArray(inventoryEntryJson.getJSONArray("gold")); else if (inventoryEntryJson.has("table")) inventoryEntry.values = CacheParserUtil.readFloatArray(inventoryEntryJson.getJSONArray("table")); else Logger.error(templateId + ": rune_inventory_contents entry of type " + inventoryEntry.type + " has no values"); rune.rune_inventory_contents.add(inventoryEntry); } rune.rune_renderable = runeJson.getBoolean("rune_renderable"); // Render fields are absent on some runes if (runeJson.has("rune_scale_factor")) rune.rune_scale_factor = CacheParserUtil.readVector3(runeJson.getJSONArray("rune_scale_factor")); if (runeJson.has("rune_skeleton")) rune.rune_skeleton = runeJson.getInt("rune_skeleton"); if (runeJson.has("rune_slope_hugger")) rune.rune_slope_hugger = runeJson.getBoolean("rune_slope_hugger"); if (runeJson.has("rune_can_fly")) rune.rune_can_fly = runeJson.getBoolean("rune_can_fly"); if (runeJson.has("rune_death_effect")) rune.rune_death_effect = runeJson.getInt("rune_death_effect"); if (runeJson.has("rune_tombstone_id")) rune.rune_tombstone_id = runeJson.getInt("rune_tombstone_id"); if (runeJson.has("rune_body_parts")) { bodyPartsJson = runeJson.getJSONArray("rune_body_parts"); for (int i = 0; i < bodyPartsJson.length(); i++) { bodyPartJson = bodyPartsJson.getJSONObject(i); bodyPart = new RuneTemplate.BodyPart(); bodyPart.body_part_render = bodyPartJson.getInt("body_part_render"); bodyPart.body_part_position = CacheParserUtil.readVector3(bodyPartJson.getJSONArray("body_part_position")); rune.rune_body_parts.add(bodyPart); } } if (runeJson.has("rune_hair")) rune.rune_hair.addAll(CacheParserUtil.readIntList(runeJson.getJSONArray("rune_hair"))); if (runeJson.has("rune_beard")) rune.rune_beard.addAll(CacheParserUtil.readIntList(runeJson.getJSONArray("rune_beard"))); rune.rune_natural_power_attack = runeJson.getInt("rune_natural_power_attack"); rune.rune_sparse_data.putAll(CacheParserUtil.readSparseData(runeJson.getJSONObject("rune_sparse_data"))); return rune; } }