// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2026 // https://www.magicbane.com package engine.parsers.objects; import engine.math.Vector3fImmutable; import engine.mbEnums; import org.json.JSONArray; import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; // Rune template as defined by the client's cache data // (MBDATA/data/COBJECTS/RUNE). Field names match the JSON keys. // // Covers every rune the client knows about: player races, classes, // disciplines, talents and mob/pet/npc runes alike. // // Values are kept as the client defines them: no req/res splitting, // no defaults substituted. Strings with a matching enum in mbEnums // are stored as that enum; everything else stays a String. // // Read only once loaded. public class RuneTemplate { public int template_id; // obj_ public String obj_name; public boolean obj_pickable; public float obj_gravity; public float obj_cull_distance; public Vector3fImmutable obj_scale; public int obj_render_object; public boolean obj_double_fusion; public Vector3fImmutable obj_forward_vector; public String obj_tracking_name; public float obj_max_tracking_distance; public int obj_icon; public float obj_gravity_f; public final LinkedHashMap obj_sound_events = new LinkedHashMap<>(); public JSONArray obj_arc_hardpoint_list; public final HashMap obj_sparse_data = new HashMap<>(); public int obj_render_object_low_detail; public Vector3fImmutable obj_default_alignment; public String obj_sound_table; // combat_ public float combat_health_current; public float combat_health_full; public final EnumMap combat_attack_resist = new EnumMap<>(mbEnums.DamageType.class); public JSONArray combat_powers; // item_ public mbEnums.ItemType item_type; public int item_eq_slots_value; public boolean item_eq_slots_type; public final EnumSet item_eq_slots_or = EnumSet.noneOf(mbEnums.EquipSlotType.class); public final EnumSet 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 final EnumMap item_offering_info = new EnumMap<>(mbEnums.ShrineType.class); public final EnumSet item_flags = EnumSet.noneOf(mbEnums.ItemFlags.class); public final EnumSet item_use_flags = EnumSet.noneOf(mbEnums.ItemUseFlags.class); public int item_post_item_id; public int item_initial_charges; public int item_book_arcana; public final LinkedHashMap item_skill_req = new LinkedHashMap<>(); public boolean item_race_req_restrict; public final EnumSet item_race_req = EnumSet.noneOf(mbEnums.MonsterType.class); public boolean item_class_req_restrict; public final EnumSet item_class_req = EnumSet.noneOf(mbEnums.ClassType.class); public boolean item_disc_req_restrict; public final EnumSet item_disc_req = EnumSet.noneOf(mbEnums.DisciplineType.class); public final EnumMap item_attr_req = new EnumMap<>(mbEnums.AttributeType.class); public int item_level_req; public int item_rank_req; public mbEnums.SexType item_sex_req; public final ArrayList item_user_power_action = new ArrayList<>(); public final ArrayList item_power_grant = new ArrayList<>(); public final LinkedHashMap> item_power_action = new LinkedHashMap<>(); public boolean item_sheathable; public boolean item_has_stub; public final EnumMap item_offering_adjustments = new EnumMap<>(mbEnums.ShrineType.class); public final EnumMap item_resource_costs = new EnumMap<>(mbEnums.ResourceType.class); public int item_bane_rank; public boolean item_ignore_saved_actions; // rune_ public mbEnums.RuneType rune_type; public String rune_sub_type; // A handful of runes carry a number instead of a name in rune_sub_type. // These look like string hashes the cache converter could not resolve. // Stored here (wrapped to int) with rune_sub_type left empty. public int rune_sub_type_hash; public boolean rune_is_standard_character_creation; 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 EnumMap rune_speed = new EnumMap<>(mbEnums.MovementType.class); public int rune_group_type; public boolean rune_group_is_faction; public boolean rune_group_is_guild; public String rune_dsc; public String rune_fx_txt; // Bit vectors. The existing ItemTemplate decodes these as MobBehaviourType. public long rune_group_tactics; public long rune_group_role_set; public final EnumSet rune_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class); public final ArrayList rune_groupee_monster_types = new ArrayList<>(); public final ArrayList rune_helper_monster_types = new ArrayList<>(); public final EnumSet rune_not_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class); // Empty in every RUNE file; shape unknown. public JSONArray rune_enemy_gender; public final ArrayList rune_skill_grant = new ArrayList<>(); // skill_type -> list of [value, value] pairs, in file order. public final LinkedHashMap> rune_skill_adj = new LinkedHashMap<>(); public final EnumMap rune_attr_adj = new EnumMap<>(mbEnums.AttributeType.class); public final EnumMap rune_max_attr_adj = new EnumMap<>(mbEnums.AttributeType.class); // One float[10] per natural attack. Same "attack info" struct as a weapon // (see MBDATA/docs/client-map.md): [0] attack speed, [1] range, [2] unknown, // [3] equip slot (1 = RHELD, 2 = LHELD), [4] projectile template id, // [5] projectile speed, [6] swing anim, [7] damage type, [8] min damage, // [9] max damage public final ArrayList rune_naturalattacks = new ArrayList<>(); // Empty in every RUNE file; shape unknown. public JSONArray rune_enchantment_type; public final ArrayList rune_inventory_contents = new ArrayList<>(); public boolean rune_renderable; // The following fields are only present when the rune is renderable // (missing from 315 of 2695 files). They keep their defaults otherwise. public Vector3fImmutable rune_scale_factor; public int rune_skeleton; public boolean rune_slope_hugger; public boolean rune_can_fly; public int rune_death_effect; public int rune_tombstone_id; public final ArrayList rune_body_parts = new ArrayList<>(); public final ArrayList rune_hair = new ArrayList<>(); public final ArrayList rune_beard = new ArrayList<>(); public int rune_natural_power_attack; public final HashMap rune_sparse_data = new HashMap<>(); // One entry of rune_skill_grant. // skill_granted_skills entries look like [hash, value] pairs; not confirmed in the client. // skill_granted_attrs, skill_granted_powers and skill_monster_types are empty // in every RUNE file, so they are kept as raw JSON. public static class SkillGrant { public String skill_type; public int skill_value; public JSONArray skill_granted_attrs; public final ArrayList skill_granted_skills = new ArrayList<>(); public JSONArray skill_granted_powers; public JSONArray skill_monster_types; } // One entry of rune_body_parts. public static class BodyPart { public int body_part_render; public Vector3fImmutable body_part_position; } // One entry of rune_inventory_contents. // type is GOLD or BOOTYTABLE. // GOLD carries "gold": three floats (meaning not yet confirmed). // BOOTYTABLE carries "table": two numbers (meaning not yet confirmed). public static class InventoryEntry { public String type; public float[] values; } }