/*
* 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();
}
}