/* * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If * not, see <http://www.gnu.org/licenses/>. */ package silentium.gameserver.data.xml; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import silentium.gameserver.configs.MainConfig; import silentium.gameserver.data.xml.parsers.XMLDocumentFactory; import silentium.gameserver.model.L2EnchantSkillData; import silentium.gameserver.model.L2EnchantSkillLearn; import silentium.gameserver.model.L2PledgeSkillLearn; import silentium.gameserver.model.L2Skill; import silentium.gameserver.model.L2SkillLearn; import silentium.gameserver.model.actor.instance.L2PcInstance; import silentium.gameserver.model.base.ClassId; import silentium.gameserver.tables.SkillTable; public class SkillTreeData { private static Logger _log = LoggerFactory.getLogger(SkillTreeData.class.getName()); private Map<ClassId, Map<Integer, L2SkillLearn>> _skillTrees; private List<L2SkillLearn> _fishingSkillTrees; private List<L2SkillLearn> _expandDwarvenCraftSkillTrees; private List<L2PledgeSkillLearn> _pledgeSkillTrees; private Map<Integer, L2EnchantSkillData> _enchantSkillData; private List<L2EnchantSkillLearn> _enchantSkillTrees; public static SkillTreeData getInstance() { return SingletonHolder._instance; } protected SkillTreeData() { load(); } private void load() { // / General skills tree _skillTrees = new HashMap<>(); // Fishing skills tree && Expand dwarven craft skills tree try { _fishingSkillTrees = new ArrayList<>(); _expandDwarvenCraftSkillTrees = new ArrayList<>(); File f = new File(MainConfig.DATAPACK_ROOT + "/data/xml/skillstrees/fishing_skills_tree.xml"); Document doc = XMLDocumentFactory.getInstance().loadDocument(f); for (Node list = doc.getFirstChild().getFirstChild(); list != null; list = list.getNextSibling()) { if ("skill".equalsIgnoreCase(list.getNodeName())) { NamedNodeMap attrs = list.getAttributes(); int id = Integer.valueOf(attrs.getNamedItem("id").getNodeValue()); int lvl = Integer.valueOf(attrs.getNamedItem("lvl").getNodeValue()); int minLvl = Integer.parseInt(attrs.getNamedItem("minLvl").getNodeValue()); int itemId = Integer.parseInt(attrs.getNamedItem("itemId").getNodeValue()); int itemCount = Integer.parseInt(attrs.getNamedItem("count").getNodeValue()); if (Boolean.parseBoolean(attrs.getNamedItem("isDwarf").getNodeValue())) _expandDwarvenCraftSkillTrees.add(new L2SkillLearn(id, lvl, minLvl, 0, itemId, itemCount)); else _fishingSkillTrees.add(new L2SkillLearn(id, lvl, minLvl, 0, itemId, itemCount)); } } } catch (Exception e) { _log.warn("FishingTable: Error while loading fishing skills: " + e); } // Enchant skills tree && Enchant data try { _enchantSkillData = new HashMap<>(); _enchantSkillTrees = new ArrayList<>(); File f = new File(MainConfig.DATAPACK_ROOT + "/data/xml/skillstrees/enchant_skills_tree.xml"); Document doc = XMLDocumentFactory.getInstance().loadDocument(f); for (Node list = doc.getFirstChild().getFirstChild(); list != null; list = list.getNextSibling()) { if ("enchanttype".equalsIgnoreCase(list.getNodeName())) { for (Node type = list.getFirstChild(); type != null; type = type.getNextSibling()) { if ("data".equalsIgnoreCase(type.getNodeName())) { NamedNodeMap dataAttrs = type.getAttributes(); int enchant = Integer.valueOf(dataAttrs.getNamedItem("enchant").getNodeValue()); int exp = Integer.valueOf(dataAttrs.getNamedItem("exp").getNodeValue()); int sp = Integer.valueOf(dataAttrs.getNamedItem("sp").getNodeValue()); int itemId = 0; int itemCount = 0; Node att = dataAttrs.getNamedItem("itemId"); if (att != null) { itemId = Integer.valueOf(att.getNodeValue()); att = dataAttrs.getNamedItem("itemCount"); itemCount = (att == null) ? 1 : Integer.valueOf(att.getNodeValue()); } int rate76 = Integer.valueOf(dataAttrs.getNamedItem("rate_76").getNodeValue()); int rate77 = Integer.valueOf(dataAttrs.getNamedItem("rate_77").getNodeValue()); int rate78 = Integer.valueOf(dataAttrs.getNamedItem("rate_78").getNodeValue()); _enchantSkillData.put(enchant, new L2EnchantSkillData(exp, sp, itemId, itemCount, rate76, rate77, rate78)); } } } else if ("enchant".equalsIgnoreCase(list.getNodeName())) { NamedNodeMap enchantAttrs = list.getAttributes(); int id = Integer.valueOf(enchantAttrs.getNamedItem("id").getNodeValue()); int baseLvl = Integer.valueOf(enchantAttrs.getNamedItem("baseLvl").getNodeValue()); for (Node skill = list.getFirstChild(); skill != null; skill = skill.getNextSibling()) { if ("data".equalsIgnoreCase(skill.getNodeName())) { NamedNodeMap dataAttrs = skill.getAttributes(); int lvl = Integer.valueOf(dataAttrs.getNamedItem("level").getNodeValue()); int enchant = Integer.valueOf(dataAttrs.getNamedItem("enchant").getNodeValue()); int prevLvl = lvl - 1; if (lvl == 101 || lvl == 141) prevLvl = baseLvl; _enchantSkillTrees.add(new L2EnchantSkillLearn(id, lvl, baseLvl, prevLvl, enchant)); } } } } } catch (Exception e) { _log.error("EnchantSkillTable: Error while loading enchant skills tree: " + e); } // Pledge skills tree try { _pledgeSkillTrees = new ArrayList<>(); File f = new File(MainConfig.DATAPACK_ROOT + "/data/xml/skillstrees/pledge_skills_tree.xml"); Document doc = XMLDocumentFactory.getInstance().loadDocument(f); for (Node list = doc.getFirstChild().getFirstChild(); list != null; list = list.getNextSibling()) { if ("clan".equals(list.getNodeName())) { int clanLvl = Integer.parseInt(list.getAttributes().getNamedItem("lvl").getNodeValue()); for (Node clan = list.getFirstChild(); clan != null; clan = clan.getNextSibling()) { if ("skill".equals(clan.getNodeName())) { NamedNodeMap skillAttr = clan.getAttributes(); int skillId = Integer.parseInt(skillAttr.getNamedItem("id").getNodeValue()); int skillLvl = Integer.parseInt(skillAttr.getNamedItem("lvl").getNodeValue()); int repCost = Integer.parseInt(skillAttr.getNamedItem("repCost").getNodeValue()); int itemId = 0; Node att = skillAttr.getNamedItem("itemId"); if (att != null) itemId = Integer.valueOf(att.getNodeValue()); _pledgeSkillTrees.add(new L2PledgeSkillLearn(skillId, skillLvl, clanLvl, repCost, itemId)); } } } } } catch (Exception e) { _log.error("PledgeTable: Error while loading pledge skills: " + e); } _log.info("FishingSkillTreeData: Loaded " + _fishingSkillTrees.size() + " general skills."); _log.info("DwarvenCraftSkillTreeData: Loaded " + _expandDwarvenCraftSkillTrees.size() + " dwarven skills."); _log.info("EnchantSkillTreeData: Loaded " + _enchantSkillData.size() + " enchant types and " + _enchantSkillTrees.size() + " enchant skills."); _log.info("PledgeSkillTreeData: Loaded " + _pledgeSkillTrees.size() + " pledge skills."); } /** * Adds list of skills to general skill tree according to classId. * * @param skills * List of general skills to be added. * @param classId * ClassId of skill owner. * @param parentId * Parent id of skill owner. */ public void addSkillsToSkillTrees(final List<L2SkillLearn> skills, final int classId, final int parentId) { if (skills == null || skills.isEmpty()) return; Map<Integer, L2SkillLearn> tmp = new HashMap<>(); if (parentId > -1) { Map<Integer, L2SkillLearn> parent = _skillTrees.get(ClassId.values()[parentId]); if (parent != null) for (L2SkillLearn skillLearn : parent.values()) if (skillLearn != null) tmp.put(SkillTable.getSkillHashCode(skillLearn.getId(), skillLearn.getLevel()), skillLearn); } for (L2SkillLearn skillLearn : skills) if (skillLearn != null) tmp.put(SkillTable.getSkillHashCode(skillLearn.getId(), skillLearn.getLevel()), skillLearn); if (!tmp.isEmpty()) _skillTrees.put(ClassId.values()[classId], tmp); } /** * Returns size of general skills tree (amount of classes with general skills). * * @return int : Size of general skill tree. */ public int getSkillTreesSize() { return _skillTrees.size(); } /** * @param cha * L2PcInstance player whom skills are compared. * @param classId * ClassId as a source for skill tree. * @return list of available general skills for L2PcInstance. */ public List<L2SkillLearn> getAvailableSkills(L2PcInstance cha, ClassId classId) { List<L2SkillLearn> result = new ArrayList<>(); L2Skill[] chaSkills = cha.getAllSkills(); int level = cha.getLevel(); for (L2SkillLearn sl : _skillTrees.get(classId).values()) { // Exception for Lucky skill, it can't be learned back once lost. if (sl.getId() == L2Skill.SKILL_LUCKY) continue; if (sl.getMinLevel() <= level) { boolean found = false; for (L2Skill s : chaSkills) { if (s.getId() == sl.getId()) { // this is the next level of a skill that we know if (s.getLevel() == sl.getLevel() - 1) result.add(sl); found = true; break; } } if (!found && sl.getLevel() == 1) result.add(sl); } } return result; } /** * @param cha * L2PcInstance, player whom skills are compared. * @param classId * ClassId, as a source for skill tree. * @return list of all available general skills <b>of maximal level</b> for L2PcInstance. */ public Collection<L2SkillLearn> getAllAvailableSkills(L2PcInstance cha, ClassId classId) { Map<Integer, L2SkillLearn> result = new HashMap<>(); int skillId, level = cha.getLevel(); L2SkillLearn skill; for (L2SkillLearn sl : _skillTrees.get(classId).values()) { skillId = sl.getId(); // Exception for Lucky skill, it can't be learned back once lost. if (skillId == L2Skill.SKILL_LUCKY) continue; if (sl.getMinLevel() <= level) { skill = result.get(skillId); if (skill == null) result.put(skillId, sl); else if (sl.getLevel() > skill.getLevel()) result.put(skillId, sl); } } for (L2Skill s : cha.getAllSkills()) { skillId = s.getId(); skill = result.get(skillId); if (skill != null) if (s.getLevel() >= skill.getLevel()) result.remove(skillId); } return result.values(); } /** * @param cha * L2PcInstance, player whom level is checked. * @param classId * ClassId, as a source for skill tree. * @return the minimum level for next general skill for L2PcInstance. */ public int getMinLevelForNewSkill(L2PcInstance cha, ClassId classId) { int level = cha.getLevel(); int result = Integer.MAX_VALUE; for (L2SkillLearn sl : _skillTrees.get(classId).values()) { int minLevel = sl.getMinLevel(); if (minLevel > level && sl.getSpCost() != 0) if (minLevel < result) result = minLevel; } if (result == Integer.MAX_VALUE) return 0; return result; } /** * @param classId * ClassId, as a source for skill tree. * @return the collection of general skill for given classId. */ public Collection<L2SkillLearn> getAllowedSkills(ClassId classId) { return _skillTrees.get(classId).values(); } /** * @param cha * L2PcInstance, player whom skills are compared. * @return list of available fishing and expand dwarven craft skills for L2PcInstance. */ public List<L2SkillLearn> getAvailableFishingDwarvenCraftSkills(L2PcInstance cha) { List<L2SkillLearn> result = new ArrayList<>(); List<L2SkillLearn> skills = new ArrayList<>(); skills.addAll(_fishingSkillTrees); if (cha.hasDwarvenCraft()) skills.addAll(_expandDwarvenCraftSkillTrees); L2Skill[] chaSkills = cha.getAllSkills(); int level = cha.getLevel(); for (L2SkillLearn sl : skills) { if (sl.getMinLevel() <= level) { boolean found = false; for (L2Skill s : chaSkills) { if (s.getId() == sl.getId()) { if (s.getLevel() == sl.getLevel() - 1) result.add(sl); found = true; break; } } if (!found && sl.getLevel() == 1) result.add(sl); } } return result; } /** * @param cha * L2PcInstance, player whom level is checked. * @return the minimum level for next fishing and expand dwarven craft skill for L2PcInstance. */ public int getMinLevelForNewFishingDwarvenCraftSkill(L2PcInstance cha) { List<L2SkillLearn> skills = new ArrayList<>(); skills.addAll(_fishingSkillTrees); if (cha.hasDwarvenCraft()) skills.addAll(_expandDwarvenCraftSkillTrees); int level = cha.getLevel(); int result = Integer.MAX_VALUE; for (L2SkillLearn sl : skills) { int minLvl = sl.getMinLevel(); if (minLvl > level) if (minLvl < result) result = minLvl; } if (result == Integer.MAX_VALUE) return 0; return result; } /** * @param cha * L2PcInstance, player whom skills are compared. * @return list of available enchant skills for L2PcInstance. */ public List<L2EnchantSkillLearn> getAvailableEnchantSkills(L2PcInstance cha) { List<L2EnchantSkillLearn> result = new ArrayList<>(); L2Skill[] chaSkills = cha.getAllSkills(); for (L2EnchantSkillLearn esl : _enchantSkillTrees) { for (L2Skill skill : chaSkills) { if (skill.getId() == esl.getId()) if (skill.getLevel() == esl.getPrevLevel()) { result.add(esl); break; } } } return result; } /** * Get skill enchant data under L2EnchantSkillData model, or null if none data found. * * @param enchant * the Enchant ID. * @return L2EnchantSkillData corresponding to the given id or null. */ public L2EnchantSkillData getEnchantSkillData(int enchant) { return _enchantSkillData.get(enchant); } /** * @param cha * L2PcInstance, player whom skills are compared. * @return list of available pledge skills for L2PcInstance. */ public List<L2PledgeSkillLearn> getAvailablePledgeSkills(L2PcInstance cha) { List<L2PledgeSkillLearn> result = new ArrayList<>(); L2Skill[] clanSkills = cha.getClan().getAllSkills(); int clanLvl = cha.getClan().getLevel(); for (L2PledgeSkillLearn psl : _pledgeSkillTrees) { if (psl.getBaseLevel() <= clanLvl) { boolean found = false; for (L2Skill s : clanSkills) { if (s.getId() == psl.getId()) { if (s.getLevel() == psl.getLevel() - 1) result.add(psl); found = true; break; } } if (!found && psl.getLevel() == 1) result.add(psl); } } return result; } public int getMinSkillLevel(int skillId, int skillLvl) { int skillHashCode = SkillTable.getSkillHashCode(skillId, skillLvl); for (Map<Integer, L2SkillLearn> map : _skillTrees.values()) { if (map.containsKey(skillHashCode)) return map.get(skillHashCode).getMinLevel(); } return 0; } public int getExpertiseLevel(int grade) { if (grade <= 0) return 0; Map<Integer, L2SkillLearn> learnMap = _skillTrees.get(ClassId.paladin); int skillHashCode = SkillTable.getSkillHashCode(239, grade); if (learnMap.containsKey(skillHashCode)) return learnMap.get(skillHashCode).getMinLevel(); _log.error("Expertise not found for grade " + grade); return 0; } private static class SingletonHolder { protected static final SkillTreeData _instance = new SkillTreeData(); } }