123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- /*
- * 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 net.sf.l2j.gameserver.datatables;
- import java.io.File;
- import java.util.List;
- import java.util.StringTokenizer;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import javax.xml.parsers.DocumentBuilderFactory;
- import javolution.util.FastList;
- import net.sf.l2j.Config;
- import net.sf.l2j.gameserver.model.L2Augmentation;
- import net.sf.l2j.gameserver.model.L2ItemInstance;
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.skills.Stats;
- import net.sf.l2j.util.Rnd;
- import org.w3c.dom.Document;
- import org.w3c.dom.NamedNodeMap;
- import org.w3c.dom.Node;
- /**
- * This class manages the augmentation data and can also create new augmentations.
- *
- * @author durgus
- */
- public class AugmentationData
- {
- private static final Logger _log = Logger.getLogger(AugmentationData.class.getName());
-
- // =========================================================
- private static AugmentationData _instance;
-
- public static final AugmentationData getInstance()
- {
- if (_instance == null)
- {
- _instance = new AugmentationData();
- }
- return _instance;
- }
-
- // =========================================================
- // Data Field
-
- // chances
- //private static final int CHANCE_STAT = 88;
- private static final int CHANCE_SKILL = 11;
- private static final int CHANCE_BASESTAT = 1;
-
- // stats
- private static final int STAT_START = 1;
- private static final int STAT_END = 14560;
- private static final int STAT_BLOCKSIZE = 3640;
- //private static final int STAT_NUMBEROF_BLOCKS = 4;
- private static final int STAT_SUBBLOCKSIZE = 91;
- //private static final int STAT_NUMBEROF_SUBBLOCKS = 40;
-
- // basestats
- private static final int BASESTAT_STR = 16341;
- private static final int BASESTAT_CON = 16342;
- private static final int BASESTAT_INT = 16343;
- private static final int BASESTAT_MEN = 16344;
-
- private FastList<?> _augmentationStats[];
- private FastList<augmentationSkill> _activeSkills;
- private FastList<augmentationSkill> _passiveSkills;
- private FastList<augmentationSkill> _chanceSkills;
-
- // =========================================================
- // Constructor
- public AugmentationData()
- {
- _log.info("Initializing AugmentationData.");
-
- _augmentationStats = new FastList[4];
- _augmentationStats[0] = new FastList<augmentationStat>();
- _augmentationStats[1] = new FastList<augmentationStat>();
- _augmentationStats[2] = new FastList<augmentationStat>();
- _augmentationStats[3] = new FastList<augmentationStat>();
-
- _activeSkills = new FastList<augmentationSkill>();
- _passiveSkills = new FastList<augmentationSkill>();
- _chanceSkills = new FastList<augmentationSkill>();
-
- load();
-
- // Use size*4: since theres 4 blocks of stat-data with equivalent size
- _log.info("AugmentationData: Loaded: " + (_augmentationStats[0].size() * 4) + " augmentation stats.");
- _log.info("AugmentationData: Loaded: " + _activeSkills.size() + " active, " + _passiveSkills.size() + " passive and " + _chanceSkills.size() + " chance skills");
- }
-
- // =========================================================
- // Nested Class
-
- public class augmentationSkill
- {
- private int _skillId;
- private int _maxSkillLevel;
- private int _augmentationSkillId;
-
- public augmentationSkill(int skillId, int maxSkillLevel, int augmentationSkillId)
- {
- _skillId = skillId;
- _maxSkillLevel = maxSkillLevel;
- _augmentationSkillId = augmentationSkillId;
- }
-
- public L2Skill getSkill(int level)
- {
- if (level > _maxSkillLevel)
- return SkillTable.getInstance().getInfo(_skillId, _maxSkillLevel);
- return SkillTable.getInstance().getInfo(_skillId, level);
- }
-
- public int getAugmentationSkillId()
- {
- return _augmentationSkillId;
- }
- }
-
- public class augmentationStat
- {
- private Stats _stat;
- private int _singleSize;
- private int _combinedSize;
- private float _singleValues[];
- private float _combinedValues[];
-
- public augmentationStat(Stats stat, float sValues[], float cValues[])
- {
- _stat = stat;
- _singleSize = sValues.length;
- _singleValues = sValues;
- _combinedSize = cValues.length;
- _combinedValues = cValues;
- }
-
- public int getSingleStatSize()
- {
- return _singleSize;
- }
-
- public int getCombinedStatSize()
- {
- return _combinedSize;
- }
-
- public float getSingleStatValue(int i)
- {
- if (i >= _singleSize || i < 0)
- return _singleValues[_singleSize - 1];
- return _singleValues[i];
- }
-
- public float getCombinedStatValue(int i)
- {
- if (i >= _combinedSize || i < 0)
- return _combinedValues[_combinedSize - 1];
- return _combinedValues[i];
- }
-
- public Stats getStat()
- {
- return _stat;
- }
- }
-
- // =========================================================
- // Method - Private
-
- @SuppressWarnings("unchecked")
- private final void load()
- {
- // Load the skillmap
- // Note: the skillmap data is only used when generating new augmentations
- // the client expects a different id in order to display the skill in the
- // items description...
- try
- {
- SkillTable st = SkillTable.getInstance();
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setValidating(false);
- factory.setIgnoringComments(true);
-
- File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_skillmap.xml");
- if (!file.exists())
- {
- if (Config.DEBUG)
- _log.info("The augmentation skillmap file is missing.");
- return;
- }
-
- Document doc = factory.newDocumentBuilder().parse(file);
-
- for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
- {
- if ("list".equalsIgnoreCase(n.getNodeName()))
- {
- for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
- {
- if ("augmentation".equalsIgnoreCase(d.getNodeName()))
- {
- NamedNodeMap attrs = d.getAttributes();
- int skillId = 0, augmentationId = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
- String type = "passive";
-
- for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
- {
- if ("skillId".equalsIgnoreCase(cd.getNodeName()))
- {
- attrs = cd.getAttributes();
- skillId = Integer.parseInt(attrs.getNamedItem("val").getNodeValue());
- }
- else if ("type".equalsIgnoreCase(cd.getNodeName()))
- {
- attrs = cd.getAttributes();
- type = attrs.getNamedItem("val").getNodeValue();
- }
- }
-
- if (type.equalsIgnoreCase("active"))
- _activeSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
- else if (type.equalsIgnoreCase("passive"))
- _passiveSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
- else
- _chanceSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
- }
- }
- }
- }
- }
- catch (Exception e)
- {
- _log.log(Level.SEVERE, "Error parsing augmentation_skillmap.xml.", e);
- return;
- }
-
- // Load the stats from xml
- for (int i = 1; i < 5; i++)
- {
- try
- {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setValidating(false);
- factory.setIgnoringComments(true);
-
- File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_stats" + i + ".xml");
- if (!file.exists())
- {
- if (Config.DEBUG)
- _log.info("The augmentation stat data file " + i + " is missing.");
- return;
- }
-
- Document doc = factory.newDocumentBuilder().parse(file);
-
- for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
- {
- if ("list".equalsIgnoreCase(n.getNodeName()))
- {
- for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
- {
- if ("stat".equalsIgnoreCase(d.getNodeName()))
- {
- NamedNodeMap attrs = d.getAttributes();
- String statName = attrs.getNamedItem("name").getNodeValue();
- float soloValues[] = null, combinedValues[] = null;
-
- for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
- {
- if ("table".equalsIgnoreCase(cd.getNodeName()))
- {
- attrs = cd.getAttributes();
- String tableName = attrs.getNamedItem("name").getNodeValue();
-
- StringTokenizer data = new StringTokenizer(cd.getFirstChild().getNodeValue());
- List<Float> array = new FastList<Float>();
- while (data.hasMoreTokens())
- array.add(Float.parseFloat(data.nextToken()));
-
- if (tableName.equalsIgnoreCase("#soloValues"))
- {
- soloValues = new float[array.size()];
- int x = 0;
- for (float value : array)
- soloValues[x++] = value;
- }
- else
- {
- combinedValues = new float[array.size()];
- int x = 0;
- for (float value : array)
- combinedValues[x++] = value;
- }
- }
- }
- // store this stat
- ((FastList<augmentationStat>) _augmentationStats[(i - 1)]).add(new augmentationStat(Stats.valueOfXml(statName), soloValues, combinedValues));
- }
- }
- }
- }
- }
- catch (Exception e)
- {
- _log.log(Level.SEVERE, "Error parsing augmentation_stats" + i + ".xml.", e);
- return;
- }
- }
- }
-
- // =========================================================
- // Properties - Public
-
- /**
- * Generate a new random augmentation
- * @param item
- * @param lifeStoneLevel
- * @param lifeSoneGrade
- * @return L2Augmentation
- */
- public L2Augmentation generateRandomAugmentation(L2ItemInstance item, int lifeStoneLevel, int lifeStoneGrade)
- {
- // Note that stat12 stands for stat 1 AND 2 (same for stat34 ;p )
- // this is because a value can contain up to 2 stat modifications
- // (there are two short values packed in one integer value, meaning 4 stat modifications at max)
- // for more info take a look at getAugStatsById(...)
-
- // Note: lifeStoneGrade: (0 means low grade, 3 top grade)
- // First: decide which grade the augmentation result is going to have:
- // 0:yellow, 1:blue, 2:purple, 3:red
- int resultColor = 0;
- // The chances used here are most likely custom,
- // whats known is: u can also get a red result from a normal grade lifeStone
- // however I will make it so that a higher grade lifeStone will more likely result in a
- // higher grade augmentation... and the augmentation result will at least have the grade
- // of the life stone
- resultColor = Rnd.get(0, 100);
- if (lifeStoneGrade == 3 || resultColor <= (15 * lifeStoneGrade) + 10)
- resultColor = 3;
- else if (lifeStoneGrade == 2 || resultColor <= (15 * lifeStoneGrade) + 20)
- resultColor = 2;
- else if (lifeStoneGrade == 1 || resultColor <= (15 * lifeStoneGrade) + 30)
- resultColor = 1;
- else
- resultColor = 0;
-
- // Second: Calculate the subblock offset for the choosen color,
- // and the level of the lifeStone
- int colorOffset = (resultColor * (STAT_SUBBLOCKSIZE * 10)) + ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE);
-
- int offset = ((3 - lifeStoneGrade) * STAT_BLOCKSIZE) + colorOffset;
-
- int stat12 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE);
- int stat34 = 0;
- boolean generateSkill = false;
-
- // use a chance to determine whether we will add a skill or not
- if (Rnd.get(1, 100) <= CHANCE_SKILL)
- generateSkill = true;
- // only if no skill is going to be applyed
- else if (Rnd.get(1, 100) <= CHANCE_BASESTAT)
- stat34 = Rnd.get(BASESTAT_STR, BASESTAT_MEN);
-
- // is neither a skill nor basestat used for stat34? then generate a normal stat
- if (stat34 == 0 && !generateSkill)
- {
- offset = (lifeStoneGrade * STAT_BLOCKSIZE) + colorOffset;
-
- stat34 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE);
- }
-
- // generate a skill if neccessary
- L2Skill skill = null;
- if (generateSkill)
- {
- augmentationSkill temp = null;
- switch (Rnd.get(1, 3))
- {
- case 1: // chance skill
- temp = _chanceSkills.get(Rnd.get(0, _chanceSkills.size() - 1));
- skill = temp.getSkill(lifeStoneLevel);
- stat34 = temp.getAugmentationSkillId();
- break;
- case 2: // active skill
- temp = _activeSkills.get(Rnd.get(0, _activeSkills.size() - 1));
- skill = temp.getSkill(lifeStoneLevel);
- stat34 = temp.getAugmentationSkillId();
- break;
- case 3: // passive skill
- temp = _passiveSkills.get(Rnd.get(0, _passiveSkills.size() - 1));
- skill = temp.getSkill(lifeStoneLevel);
- stat34 = temp.getAugmentationSkillId();
- break;
- }
- }
-
- return new L2Augmentation(item, ((stat34 << 16) + stat12), skill, true);
- }
-
- public class AugStat
- {
- private Stats _stat;
- private float _value;
-
- public AugStat(Stats stat, float value)
- {
- _stat = stat;
- _value = value;
- }
-
- public Stats getStat()
- {
- return _stat;
- }
-
- public float getValue()
- {
- return _value;
- }
- }
-
- /**
- * Returns the stat and basestat boni for a given augmentation id
- * @param augmentationId
- * @return
- */
- public FastList<AugStat> getAugStatsById(int augmentationId)
- {
- FastList<AugStat> temp = new FastList<AugStat>();
- // An augmentation id contains 2 short vaues so we gotta seperate them here
- // both values contain a number from 1-16380, the first 14560 values are stats
- // the 14560 stats are devided into 4 blocks each holding 3640 values
- // each block contains 40 subblocks holding 91 stat values
- // the first 13 values are so called Solo-stats and they have the highest stat increase possible
- // after the 13 Solo-stats come 78 combined stats (thats every possible combination of the 13 solo stats)
- // the first 12 combined stats (14-26) is the stat 1 combined with stat 2-13
- // the next 11 combined stats then are stat 2 combined with stat 3-13 and so on...
- // to get the idea have a look @ optiondata_client-e.dat - thats where the data came from :)
- int stats[] = new int[2];
- stats[0] = 0x0000FFFF & augmentationId;
- stats[1] = (augmentationId >> 16);
-
- for (int i = 0; i < 2; i++)
- {
- // its a stat
- if (stats[i] >= STAT_START && stats[i] <= STAT_END)
- {
- int block = 0;
- while (stats[i] > STAT_BLOCKSIZE)
- {
- stats[i] -= STAT_BLOCKSIZE;
- block++;
- }
-
- int subblock = 0;
- while (stats[i] > STAT_SUBBLOCKSIZE)
- {
- stats[i] -= STAT_SUBBLOCKSIZE;
- subblock++;
- }
-
- if (stats[i] < 14) // solo stat
- {
- augmentationStat as = ((augmentationStat) _augmentationStats[block].get((stats[i] - 1)));
- temp.add(new AugStat(as.getStat(), as.getSingleStatValue(subblock)));
- }
- else
- // twin stat
- {
- stats[i] -= 13; // rescale to 0 (if first of first combined block)
- int x = 12; // next combi block has 12 stats
- int rescales = 0; // number of rescales done
-
- while (stats[i] > x)
- {
- stats[i] -= x;
- x--;
- rescales++;
- }
- // get first stat
- augmentationStat as = ((augmentationStat) _augmentationStats[block].get(rescales));
- if (rescales == 0)
- temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock)));
- else
- temp.add(new AugStat(as.getStat(), as.getCombinedStatValue((subblock * 2) + 1)));
-
- // get 2nd stat
- as = ((augmentationStat) _augmentationStats[block].get(rescales + stats[i]));
- if (as.getStat() == Stats.CRITICAL_DAMAGE)
- temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock)));
- else
- temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock * 2)));
- }
- }
- // its a base stat
- else if (stats[i] >= BASESTAT_STR && stats[i] <= BASESTAT_MEN)
- {
- switch (stats[i])
- {
- case BASESTAT_STR:
- temp.add(new AugStat(Stats.STAT_STR, 1.0f));
- break;
- case BASESTAT_CON:
- temp.add(new AugStat(Stats.STAT_CON, 1.0f));
- break;
- case BASESTAT_INT:
- temp.add(new AugStat(Stats.STAT_INT, 1.0f));
- break;
- case BASESTAT_MEN:
- temp.add(new AugStat(Stats.STAT_MEN, 1.0f));
- break;
- }
- }
- }
-
- return temp;
- }
- }
|