// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.objects; import engine.Enum; import engine.math.Vector3fImmutable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.pmw.tinylog.Logger; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; public class ItemTemplate { // Global template lookup public static HashMap 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 float combat_health_current; public float combat_health_full; public HashMap combat_attack_resist = new HashMap<>(); public Enum.ItemType item_type; public int item_eq_slots_value; public boolean item_eq_slots_type; public EnumSet item_eq_slots_or = EnumSet.noneOf(Enum.EquipSlotType.class); public EnumSet item_eq_slots_and = EnumSet.noneOf(Enum.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 HashMap 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 HashMap item_weapon_damage = new HashMap<>(); public ArrayList weapon_attack_anim_right = new ArrayList(); public ArrayList weapon_attack_anim_left = new ArrayList(); public Enum.AttributeType item_primary_attr = Enum.AttributeType.None; public Enum.AttributeType item_secondary_attr = Enum.AttributeType.None; public EnumSet item_flags = EnumSet.noneOf(Enum.ItemFlags.class); public EnumSet item_use_flags = EnumSet.noneOf(Enum.ItemUseFlags.class); public int item_initial_charges; public HashMap item_skill_required = new HashMap<>(); public EnumSet item_race_req = EnumSet.noneOf(Enum.MonsterType.class); public EnumSet item_race_res = EnumSet.noneOf(Enum.MonsterType.class); public EnumSet item_class_req = EnumSet.noneOf(Enum.ClassType.class); public EnumSet item_class_res = EnumSet.noneOf(Enum.ClassType.class); public EnumSet item_disc_req = EnumSet.noneOf(Enum.DisciplineType.class); public EnumSet item_disc_res = EnumSet.noneOf(Enum.DisciplineType.class); public int item_level_req; public Enum.SexType item_sex_req; public HashMap item_user_power_action = new HashMap<>(); public static HashMap item_power_grant = new HashMap<>(); public HashMap item_power_action = new HashMap<>(); public HashMap item_resource_cost = new HashMap<>(); public int modTable; // Deed related fields 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 ItemTemplate(JSONObject jsonObject) { try { // Reading a String obj_name = (String) jsonObject.get("obj_name"); // Reading a boolean obj_pickable = (boolean) jsonObject.get("obj_pickable"); // Reading floats from an array (note always check for empty arrays) JSONArray scaleData = (JSONArray) jsonObject.get("obj_scale"); if (scaleData.isEmpty() == false) obj_scale = new Vector3fImmutable(((Double) scaleData.get(0)).floatValue(), ((Double) scaleData.get(1)).floatValue(), ((Double) scaleData.get(2)).floatValue()); JSONArray forwardVector = (JSONArray) jsonObject.get("obj_forward_vector"); if (forwardVector.isEmpty() == false) obj_forward_vector = new Vector3fImmutable(((Double) forwardVector.get(0)).floatValue(), ((Double) forwardVector.get(1)).floatValue(), ((Double) forwardVector.get(2)).floatValue()); JSONArray defaultAlighment = (JSONArray) jsonObject.get("obj_default_alignment"); if (defaultAlighment.isEmpty() == false) obj_default_alignment = new Vector3fImmutable(((Double) defaultAlighment.get(0)).floatValue(), ((Double) defaultAlighment.get(1)).floatValue(), ((Double) defaultAlighment.get(2)).floatValue()); // Reading an integer value obj_render_object = ((Long) jsonObject.get("obj_render_object")).intValue(); obj_icon = ((Long) jsonObject.get("obj_icon")).intValue(); // Reading float values combat_health_current = ((Double) jsonObject.get("combat_health_current")).floatValue(); combat_health_full = ((Double) jsonObject.get("combat_health_full")).floatValue(); // Reading a hashmap of floats JSONObject resist_json = (JSONObject) jsonObject.get("combat_attack_resist"); for (Object key : resist_json.keySet()) { float resist = ((Double) resist_json.get(key)).floatValue(); combat_attack_resist.put((String) key, resist); } // Parsing an enum item_type = Enum.ItemType.valueOf((String) jsonObject.get("item_type")); item_eq_slots_value = ((Long) jsonObject.get("item_eq_slots_value")).intValue(); item_eq_slots_type = (boolean) jsonObject.get("item_eq_slots_type"); // Parsing an enumset JSONArray eq_slots_or = (JSONArray) jsonObject.get("item_eq_slots_or"); if (eq_slots_or.isEmpty() == false) for (Object o : eq_slots_or) item_eq_slots_or.add(Enum.EquipSlotType.valueOf((String) o)); JSONArray eq_slots_and = (JSONArray) jsonObject.get("item_eq_slots_and"); if (eq_slots_and.isEmpty() == false) for (Object o : eq_slots_and) item_eq_slots_and.add(Enum.EquipSlotType.valueOf((String) o)); item_takeable = (boolean) jsonObject.get("item_takeable"); item_value = ((Long) jsonObject.get("item_value")).intValue(); item_wt = ((Long) jsonObject.get("item_wt")).intValue(); item_passive_defense_mod = ((Double) jsonObject.get("item_passive_defense_mod")).floatValue(); item_base_name = (String) jsonObject.get("item_base_name"); item_dsc = (String) jsonObject.get("item_dsc"); item_render_object_female = ((Long) jsonObject.get("item_render_object_female")).intValue(); item_health_full = ((Double) jsonObject.get("item_health_full")).floatValue(); 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 = ((Long) jsonObject.get("item_parry_anim_id")).intValue(); // Reading offering data JSONArray offering_data = (JSONArray) jsonObject.get("item_offering_info"); for (Object entry : offering_data) { JSONObject offering_entry = (JSONObject) entry; String offering_type = ((String) offering_entry.get("offering_type")).replaceAll("-", ""); int offering_value = ((Long) offering_entry.get("offering_value")).intValue(); item_offering_info.put(Enum.ShrineType.valueOf(offering_type), offering_value); } // Fields only present for ARMOR if (item_type.equals(Enum.ItemType.ARMOR)) { item_bulk_factor = ((Double) jsonObject.get("item_bulk_factor")).floatValue(); item_defense_rating = ((Long) jsonObject.get("item_defense_rating")).intValue(); } // Fields only present for WEAPON if (item_type.equals(Enum.ItemType.WEAPON)) { JSONObject item_weapon = (JSONObject) jsonObject.get("item_weapon"); item_weapon_wepspeed = ((Double) item_weapon.get("weapon_wepspeed")).floatValue(); item_weapon_max_range = ((Double) item_weapon.get("weapon_max_range")).floatValue(); item_weapon_projectile_id = ((Long) item_weapon.get("weapon_projectile_id")).intValue(); item_weapon_projectile_speed = ((Double) item_weapon.get("weapon_projectile_speed")).floatValue(); item_weapon_combat_idle_anim = ((Long) item_weapon.get("weapon_combat_idle_anim")).intValue(); JSONArray weapon_damage = (JSONArray) item_weapon.get("weapon_damage"); if (weapon_damage.isEmpty() == false) for (Object o : weapon_damage) { JSONObject damage_entry = (JSONObject) o; Enum.DamageType damageType = Enum.DamageType.getDamageType(((String) damage_entry.get("damage_type"))); int min = ((Long) damage_entry.get("damage_min")).intValue(); int max = ((Long) damage_entry.get("damage_max")).intValue(); int[] minMax = {min, max}; item_weapon_damage.put(damageType, minMax); } JSONArray attack_anim_right = (JSONArray) item_weapon.get("weapon_attack_anim_right"); if (attack_anim_right.isEmpty() == false) for (Object o : attack_anim_right) { JSONArray animationEntry = (JSONArray) o; int animation = ((Long) animationEntry.get(0)).intValue(); int duration = ((Long) animationEntry.get(1)).intValue(); weapon_attack_anim_right.add(new int[]{animation, duration}); } JSONArray attack_anim_left = (JSONArray) item_weapon.get("weapon_attack_anim_left"); if (attack_anim_left.isEmpty() == false) for (Object o : attack_anim_left) { JSONArray animationEntry = (JSONArray) o; int animation = ((Long) animationEntry.get(0)).intValue(); int duration = ((Long) animationEntry.get(1)).intValue(); weapon_attack_anim_left.add(new int[]{animation, duration}); } item_primary_attr = Enum.AttributeType.valueOf((String) jsonObject.get("item_primary_attr")); item_secondary_attr = Enum.AttributeType.valueOf((String) jsonObject.get("item_secondary_attr")); } JSONArray itemflags = (JSONArray) jsonObject.get("item_flags"); if (itemflags.isEmpty() == false) for (Object o : itemflags) { String flag = (String) o; if (flag.equals("None") == false) item_flags.add(Enum.ItemFlags.valueOf((String) o)); } JSONArray itemUseflags = (JSONArray) jsonObject.get("item_use_flags"); if (itemUseflags.isEmpty() == false) for (Object o : itemUseflags) item_use_flags.add(Enum.ItemUseFlags.valueOf((String) o)); item_initial_charges = ((Long) jsonObject.get("item_initial_charges")).intValue(); JSONArray skill_required = (JSONArray) jsonObject.get("item_skill_req"); if (skill_required.isEmpty() == false) for (Object o : skill_required) { JSONObject skill_req = (JSONObject) o; String skill_type = (String) skill_req.get("skill_type"); int skill_level = ((Long) skill_req.get("skill_level")).intValue(); if (skill_type.equals("Cloth") == false) item_skill_required.put(skill_type, skill_level); } JSONObject race_required = (JSONObject) jsonObject.get("item_race_req"); boolean restrict = ((Boolean) race_required.get("restrict")); JSONArray races = (JSONArray) race_required.get("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(Enum.MonsterType.valueOf(race)); else item_race_req.add(Enum.MonsterType.valueOf(race)); } JSONObject class_required = (JSONObject) jsonObject.get("item_class_req"); restrict = ((Boolean) class_required.get("restrict")); JSONArray classes = (JSONArray) class_required.get("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(Enum.ClassType.valueOf(classEntry)); else item_class_req.add(Enum.ClassType.valueOf(classEntry)); } JSONObject disc_required = (JSONObject) jsonObject.get("item_disc_req"); restrict = ((Boolean) disc_required.get("restrict")); JSONArray discs = (JSONArray) disc_required.get("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(Enum.DisciplineType.valueOf(disc)); else item_disc_req.add(Enum.DisciplineType.valueOf(disc)); } item_level_req = ((Long) jsonObject.get("item_level_req")).intValue(); item_sex_req = Enum.SexType.valueOf((String) jsonObject.get("item_sex_req")); JSONArray userPowerActions = (JSONArray) jsonObject.get("item_user_power_action"); if (userPowerActions.isEmpty() == false) for (Object o : userPowerActions) { JSONObject powerActionEntry = (JSONObject) o; String power = (String) powerActionEntry.get("power"); JSONArray args = (JSONArray) powerActionEntry.get("arguments"); int[] arguments = {((Long) args.get(0)).intValue(), ((Long) args.get(1)).intValue()}; item_user_power_action.put(power, arguments); } JSONArray powerGrantsArray = (JSONArray) jsonObject.get("item_power_grant"); if (powerGrantsArray.isEmpty() == false) { for (Object grantArrayEntry : powerGrantsArray) { JSONObject powerGrantEntry = (JSONObject) grantArrayEntry; JSONObject powerGrant = (JSONObject) powerGrantEntry.get("power_granted_powers"); for (Object key : powerGrant.keySet()) { String powerString = (String) key; int powerLevel = ((Long) powerGrant.get(key)).intValue(); item_power_grant.put(powerString, powerLevel); } } } JSONArray item_power_actions = (JSONArray) jsonObject.get("item_power_action"); if (item_power_actions.isEmpty() == false) for (Object o : item_power_actions) { JSONObject powerActionEntry = (JSONObject) o; String power = (String) powerActionEntry.get("power_type"); JSONArray power_actions = (JSONArray) powerActionEntry.get("power_actions"); JSONObject argument_entry = (JSONObject) power_actions.get(0); JSONArray power_args = (JSONArray) argument_entry.get("power_arguments"); int[] power_arguments = {((Long) power_args.get(0)).intValue(), ((Long) power_args.get(1)).intValue()}; item_power_action.put(power, power_arguments); } JSONArray resource_costs = (JSONArray) jsonObject.get("item_resource_costs"); if (resource_costs.isEmpty() == false) for (Object o : resource_costs) { JSONObject resource_entry = (JSONObject) o; Enum.ResourceType resource_type = Enum.ResourceType.valueOf(((String) resource_entry.get("resource_type")).toUpperCase()); int resource_value = ((Long) resource_entry.get("resource_value")).intValue(); item_resource_cost.put(resource_type, resource_value); } // Deed related fields if (item_type.equals(Enum.ItemType.DEED)) { deed_type = ((Long) jsonObject.get("deed_type")).intValue(); deed_furniture_id = ((Long) jsonObject.get("deed_furniture_id")).intValue(); deed_target_id = ((Long) jsonObject.get("deed_target_id")).intValue(); deed_employment = ((Long) jsonObject.get("deed_employment")).intValue(); deed_start_rank = ((Long) jsonObject.get("deed_start_rank")).intValue(); deed_name_lookup = ((Long) jsonObject.get("deed_name_lookup")).intValue(); deed_indoors = ((Boolean) jsonObject.get("deed_indoors")); deed_is_fortress = ((Boolean) jsonObject.get("deed_is_fortress")); deed_namelookup_val = ((Double) jsonObject.get("deed_namelookup_val")).floatValue(); deed_custom_city = ((Boolean) jsonObject.get("deed_custom_city")); deed_structure_id = ((Long) jsonObject.get("deed_structure_id")).intValue(); } } catch (Exception e) { Logger.error(e); } } public static Boolean ValidRace(Item item, Enum.MonsterType race) { if (item.template.item_race_req.isEmpty() && item.template.item_race_res.isEmpty()) return true; if (item.template.item_race_req.isEmpty() == false) if (item.template.item_race_req.contains(race)) return true; if (item.template.item_race_res.isEmpty() == false) if (item.template.item_class_res.contains(race) == false) return true; return false; } public static Boolean ValidClass(Item item, Enum.ClassType base, Enum.ClassType profession) { // Early exit if no entry if (item.template.item_class_req.isEmpty() && item.template.item_class_res.isEmpty()) return true; if (item.template.item_class_req.isEmpty() == false) if (item.template.item_class_req.contains(base) || item.template.item_class_req.contains(profession)) return true; if (item.template.item_class_res.isEmpty() == false) if (item.template.item_class_res.contains(base) == false && item.template.item_class_res.contains(profession) == false) return true; return false; } public static Boolean ValidDiscipline(Item item, EnumSet discs) { // Early exit if no entry if (item.template.item_disc_req.isEmpty() && item.template.item_disc_res.isEmpty()) return true; EnumSet workSet = EnumSet.copyOf(discs); if (item.template.item_disc_req.isEmpty() == false) { workSet.retainAll(item.template.item_disc_req); if (workSet.isEmpty() == false) return true; } if (item.template.item_disc_res.isEmpty() == false) { workSet.retainAll(item.template.item_disc_res); if (workSet.isEmpty() == false) return true; } return false; } public static Boolean canCharacterEquip(Item item, AbstractCharacter character) { return ValidRace(item, character.absRace) && ValidClass(item, character.absBaseClass, character.absPromotionClass) && ValidDiscipline(item, character.absDisciplines); } public static boolean validForSkills(Item item, ConcurrentHashMap skills) { CharacterSkill characterSkill; if (item.template.item_skill_required.isEmpty()) return true; for (String skillRequired : item.template.item_skill_required.keySet()) { int required_value = item.template.item_skill_required.get(skillRequired); characterSkill = skills.get(skillRequired); if (characterSkill == null) return false; if (characterSkill.getModifiedAmountBeforeMods() > required_value) return true; } return false; } public static boolean isTwoHanded(Item item) { if (!item.template.item_type.equals(Enum.ItemType.WEAPON)) return false; return item.template.item_eq_slots_and.contains(EnumSet.of(Enum.EquipSlotType.LHELD, Enum.EquipSlotType.RHELD)); } public static boolean isTwoHanded(ItemTemplate template) { if (!template.item_type.equals(Enum.ItemType.WEAPON)) return false; return template.item_eq_slots_and.contains(EnumSet.of(Enum.EquipSlotType.LHELD, Enum.EquipSlotType.RHELD)); } public static boolean isShield(Item item) { if (item.template.item_skill_required.containsKey("Block")) return true; return false; } public static boolean isShield(ItemTemplate template) { if (template.item_skill_required.containsKey("Block")) return true; return false; } public static boolean validForSlot(Enum.EquipSlotType slot, ConcurrentHashMap equipped, Item item) { boolean validSlot = false; if (equipped == null) return false; //Item not valid for slot if (item.template.item_eq_slots_or.contains(slot) == false) return false; // Slot is taken if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false) return false; // Two handed weapons take up two slots if ((isTwoHanded(item)) && ((slot == Enum.EquipSlotType.LHELD && equipped.get(Enum.EquipSlotType.RHELD) != null) || (slot == Enum.EquipSlotType.RHELD && equipped.get(Enum.EquipSlotType.LHELD) != null))) return false; if (item.template.item_type.equals(Enum.ItemType.WEAPON)) if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false) return false; return true; } public static boolean canEquip(Enum.EquipSlotType slot, CharacterItemManager itemManager, AbstractCharacter abstractCharacter, Item item) { if (itemManager == null || abstractCharacter == null) return false; // Early exit for mobiles and NPCS. // Perhaps not needed now that mobs have skills. if (EnumSet.of(Enum.GameObjectType.NPC, Enum.GameObjectType.Mob).contains(abstractCharacter.getObjectType())) return false; if (!validForSlot(slot, itemManager.getEquipped(), item)) return false; if (!validForSkills(item, abstractCharacter.getSkills())) return false; if (canCharacterEquip(item, abstractCharacter) == false) return false; //players can't wear 0 value items. return item.template.item_value != 0 || Kit.IsNoobGear(item.templateID); } }