AugmentationData.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package net.sf.l2j.gameserver.datatables;
  16. import java.io.File;
  17. import java.util.List;
  18. import java.util.StringTokenizer;
  19. import java.util.logging.Level;
  20. import java.util.logging.Logger;
  21. import javax.xml.parsers.DocumentBuilderFactory;
  22. import javolution.util.FastList;
  23. import net.sf.l2j.Config;
  24. import net.sf.l2j.gameserver.model.L2Augmentation;
  25. import net.sf.l2j.gameserver.model.L2ItemInstance;
  26. import net.sf.l2j.gameserver.model.L2Skill;
  27. import net.sf.l2j.gameserver.skills.Stats;
  28. import net.sf.l2j.util.Rnd;
  29. import org.w3c.dom.Document;
  30. import org.w3c.dom.NamedNodeMap;
  31. import org.w3c.dom.Node;
  32. /**
  33. * This class manages the augmentation data and can also create new augmentations.
  34. *
  35. * @author durgus
  36. */
  37. public class AugmentationData
  38. {
  39. private static final Logger _log = Logger.getLogger(AugmentationData.class.getName());
  40. // =========================================================
  41. private static AugmentationData _instance;
  42. public static final AugmentationData getInstance()
  43. {
  44. if (_instance == null)
  45. {
  46. _instance = new AugmentationData();
  47. }
  48. return _instance;
  49. }
  50. // =========================================================
  51. // Data Field
  52. // chances
  53. //private static final int CHANCE_STAT = 88;
  54. private static final int CHANCE_SKILL = 11;
  55. private static final int CHANCE_BASESTAT = 1;
  56. // stats
  57. private static final int STAT_START = 1;
  58. private static final int STAT_END = 14560;
  59. private static final int STAT_BLOCKSIZE = 3640;
  60. //private static final int STAT_NUMBEROF_BLOCKS = 4;
  61. private static final int STAT_SUBBLOCKSIZE = 91;
  62. //private static final int STAT_NUMBEROF_SUBBLOCKS = 40;
  63. // basestats
  64. private static final int BASESTAT_STR = 16341;
  65. private static final int BASESTAT_CON = 16342;
  66. private static final int BASESTAT_INT = 16343;
  67. private static final int BASESTAT_MEN = 16344;
  68. private FastList<?> _augmentationStats[];
  69. private FastList<augmentationSkill> _activeSkills;
  70. private FastList<augmentationSkill> _passiveSkills;
  71. private FastList<augmentationSkill> _chanceSkills;
  72. // =========================================================
  73. // Constructor
  74. public AugmentationData()
  75. {
  76. _log.info("Initializing AugmentationData.");
  77. _augmentationStats = new FastList[4];
  78. _augmentationStats[0] = new FastList<augmentationStat>();
  79. _augmentationStats[1] = new FastList<augmentationStat>();
  80. _augmentationStats[2] = new FastList<augmentationStat>();
  81. _augmentationStats[3] = new FastList<augmentationStat>();
  82. _activeSkills = new FastList<augmentationSkill>();
  83. _passiveSkills = new FastList<augmentationSkill>();
  84. _chanceSkills = new FastList<augmentationSkill>();
  85. load();
  86. // Use size*4: since theres 4 blocks of stat-data with equivalent size
  87. _log.info("AugmentationData: Loaded: " + (_augmentationStats[0].size() * 4) + " augmentation stats.");
  88. _log.info("AugmentationData: Loaded: " + _activeSkills.size() + " active, " + _passiveSkills.size() + " passive and " + _chanceSkills.size() + " chance skills");
  89. }
  90. // =========================================================
  91. // Nested Class
  92. public class augmentationSkill
  93. {
  94. private int _skillId;
  95. private int _maxSkillLevel;
  96. private int _augmentationSkillId;
  97. public augmentationSkill(int skillId, int maxSkillLevel, int augmentationSkillId)
  98. {
  99. _skillId = skillId;
  100. _maxSkillLevel = maxSkillLevel;
  101. _augmentationSkillId = augmentationSkillId;
  102. }
  103. public L2Skill getSkill(int level)
  104. {
  105. if (level > _maxSkillLevel)
  106. return SkillTable.getInstance().getInfo(_skillId, _maxSkillLevel);
  107. return SkillTable.getInstance().getInfo(_skillId, level);
  108. }
  109. public int getAugmentationSkillId()
  110. {
  111. return _augmentationSkillId;
  112. }
  113. }
  114. public class augmentationStat
  115. {
  116. private Stats _stat;
  117. private int _singleSize;
  118. private int _combinedSize;
  119. private float _singleValues[];
  120. private float _combinedValues[];
  121. public augmentationStat(Stats stat, float sValues[], float cValues[])
  122. {
  123. _stat = stat;
  124. _singleSize = sValues.length;
  125. _singleValues = sValues;
  126. _combinedSize = cValues.length;
  127. _combinedValues = cValues;
  128. }
  129. public int getSingleStatSize()
  130. {
  131. return _singleSize;
  132. }
  133. public int getCombinedStatSize()
  134. {
  135. return _combinedSize;
  136. }
  137. public float getSingleStatValue(int i)
  138. {
  139. if (i >= _singleSize || i < 0)
  140. return _singleValues[_singleSize - 1];
  141. return _singleValues[i];
  142. }
  143. public float getCombinedStatValue(int i)
  144. {
  145. if (i >= _combinedSize || i < 0)
  146. return _combinedValues[_combinedSize - 1];
  147. return _combinedValues[i];
  148. }
  149. public Stats getStat()
  150. {
  151. return _stat;
  152. }
  153. }
  154. // =========================================================
  155. // Method - Private
  156. @SuppressWarnings("unchecked")
  157. private final void load()
  158. {
  159. // Load the skillmap
  160. // Note: the skillmap data is only used when generating new augmentations
  161. // the client expects a different id in order to display the skill in the
  162. // items description...
  163. try
  164. {
  165. SkillTable st = SkillTable.getInstance();
  166. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  167. factory.setValidating(false);
  168. factory.setIgnoringComments(true);
  169. File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_skillmap.xml");
  170. if (!file.exists())
  171. {
  172. if (Config.DEBUG)
  173. _log.info("The augmentation skillmap file is missing.");
  174. return;
  175. }
  176. Document doc = factory.newDocumentBuilder().parse(file);
  177. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  178. {
  179. if ("list".equalsIgnoreCase(n.getNodeName()))
  180. {
  181. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  182. {
  183. if ("augmentation".equalsIgnoreCase(d.getNodeName()))
  184. {
  185. NamedNodeMap attrs = d.getAttributes();
  186. int skillId = 0, augmentationId = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
  187. String type = "passive";
  188. for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
  189. {
  190. if ("skillId".equalsIgnoreCase(cd.getNodeName()))
  191. {
  192. attrs = cd.getAttributes();
  193. skillId = Integer.parseInt(attrs.getNamedItem("val").getNodeValue());
  194. }
  195. else if ("type".equalsIgnoreCase(cd.getNodeName()))
  196. {
  197. attrs = cd.getAttributes();
  198. type = attrs.getNamedItem("val").getNodeValue();
  199. }
  200. }
  201. if (type.equalsIgnoreCase("active"))
  202. _activeSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
  203. else if (type.equalsIgnoreCase("passive"))
  204. _passiveSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
  205. else
  206. _chanceSkills.add(new augmentationSkill(skillId, st.getMaxLevel(skillId, 1), augmentationId));
  207. }
  208. }
  209. }
  210. }
  211. }
  212. catch (Exception e)
  213. {
  214. _log.log(Level.SEVERE, "Error parsing augmentation_skillmap.xml.", e);
  215. return;
  216. }
  217. // Load the stats from xml
  218. for (int i = 1; i < 5; i++)
  219. {
  220. try
  221. {
  222. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  223. factory.setValidating(false);
  224. factory.setIgnoringComments(true);
  225. File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_stats" + i + ".xml");
  226. if (!file.exists())
  227. {
  228. if (Config.DEBUG)
  229. _log.info("The augmentation stat data file " + i + " is missing.");
  230. return;
  231. }
  232. Document doc = factory.newDocumentBuilder().parse(file);
  233. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  234. {
  235. if ("list".equalsIgnoreCase(n.getNodeName()))
  236. {
  237. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  238. {
  239. if ("stat".equalsIgnoreCase(d.getNodeName()))
  240. {
  241. NamedNodeMap attrs = d.getAttributes();
  242. String statName = attrs.getNamedItem("name").getNodeValue();
  243. float soloValues[] = null, combinedValues[] = null;
  244. for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
  245. {
  246. if ("table".equalsIgnoreCase(cd.getNodeName()))
  247. {
  248. attrs = cd.getAttributes();
  249. String tableName = attrs.getNamedItem("name").getNodeValue();
  250. StringTokenizer data = new StringTokenizer(cd.getFirstChild().getNodeValue());
  251. List<Float> array = new FastList<Float>();
  252. while (data.hasMoreTokens())
  253. array.add(Float.parseFloat(data.nextToken()));
  254. if (tableName.equalsIgnoreCase("#soloValues"))
  255. {
  256. soloValues = new float[array.size()];
  257. int x = 0;
  258. for (float value : array)
  259. soloValues[x++] = value;
  260. }
  261. else
  262. {
  263. combinedValues = new float[array.size()];
  264. int x = 0;
  265. for (float value : array)
  266. combinedValues[x++] = value;
  267. }
  268. }
  269. }
  270. // store this stat
  271. ((FastList<augmentationStat>) _augmentationStats[(i - 1)]).add(new augmentationStat(Stats.valueOfXml(statName), soloValues, combinedValues));
  272. }
  273. }
  274. }
  275. }
  276. }
  277. catch (Exception e)
  278. {
  279. _log.log(Level.SEVERE, "Error parsing augmentation_stats" + i + ".xml.", e);
  280. return;
  281. }
  282. }
  283. }
  284. // =========================================================
  285. // Properties - Public
  286. /**
  287. * Generate a new random augmentation
  288. * @param item
  289. * @param lifeStoneLevel
  290. * @param lifeSoneGrade
  291. * @return L2Augmentation
  292. */
  293. public L2Augmentation generateRandomAugmentation(L2ItemInstance item, int lifeStoneLevel, int lifeStoneGrade)
  294. {
  295. // Note that stat12 stands for stat 1 AND 2 (same for stat34 ;p )
  296. // this is because a value can contain up to 2 stat modifications
  297. // (there are two short values packed in one integer value, meaning 4 stat modifications at max)
  298. // for more info take a look at getAugStatsById(...)
  299. // Note: lifeStoneGrade: (0 means low grade, 3 top grade)
  300. // First: decide which grade the augmentation result is going to have:
  301. // 0:yellow, 1:blue, 2:purple, 3:red
  302. int resultColor = 0;
  303. // The chances used here are most likely custom,
  304. // whats known is: u can also get a red result from a normal grade lifeStone
  305. // however I will make it so that a higher grade lifeStone will more likely result in a
  306. // higher grade augmentation... and the augmentation result will at least have the grade
  307. // of the life stone
  308. resultColor = Rnd.get(0, 100);
  309. if (lifeStoneGrade == 3 || resultColor <= (15 * lifeStoneGrade) + 10)
  310. resultColor = 3;
  311. else if (lifeStoneGrade == 2 || resultColor <= (15 * lifeStoneGrade) + 20)
  312. resultColor = 2;
  313. else if (lifeStoneGrade == 1 || resultColor <= (15 * lifeStoneGrade) + 30)
  314. resultColor = 1;
  315. else
  316. resultColor = 0;
  317. // Second: Calculate the subblock offset for the choosen color,
  318. // and the level of the lifeStone
  319. int colorOffset = (resultColor * (STAT_SUBBLOCKSIZE * 10)) + ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE);
  320. int offset = ((3 - lifeStoneGrade) * STAT_BLOCKSIZE) + colorOffset;
  321. int stat12 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE);
  322. int stat34 = 0;
  323. boolean generateSkill = false;
  324. // use a chance to determine whether we will add a skill or not
  325. if (Rnd.get(1, 100) <= CHANCE_SKILL)
  326. generateSkill = true;
  327. // only if no skill is going to be applyed
  328. else if (Rnd.get(1, 100) <= CHANCE_BASESTAT)
  329. stat34 = Rnd.get(BASESTAT_STR, BASESTAT_MEN);
  330. // is neither a skill nor basestat used for stat34? then generate a normal stat
  331. if (stat34 == 0 && !generateSkill)
  332. {
  333. offset = (lifeStoneGrade * STAT_BLOCKSIZE) + colorOffset;
  334. stat34 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE);
  335. }
  336. // generate a skill if neccessary
  337. L2Skill skill = null;
  338. if (generateSkill)
  339. {
  340. augmentationSkill temp = null;
  341. switch (Rnd.get(1, 3))
  342. {
  343. case 1: // chance skill
  344. temp = _chanceSkills.get(Rnd.get(0, _chanceSkills.size() - 1));
  345. skill = temp.getSkill(lifeStoneLevel);
  346. stat34 = temp.getAugmentationSkillId();
  347. break;
  348. case 2: // active skill
  349. temp = _activeSkills.get(Rnd.get(0, _activeSkills.size() - 1));
  350. skill = temp.getSkill(lifeStoneLevel);
  351. stat34 = temp.getAugmentationSkillId();
  352. break;
  353. case 3: // passive skill
  354. temp = _passiveSkills.get(Rnd.get(0, _passiveSkills.size() - 1));
  355. skill = temp.getSkill(lifeStoneLevel);
  356. stat34 = temp.getAugmentationSkillId();
  357. break;
  358. }
  359. }
  360. return new L2Augmentation(item, ((stat34 << 16) + stat12), skill, true);
  361. }
  362. public class AugStat
  363. {
  364. private Stats _stat;
  365. private float _value;
  366. public AugStat(Stats stat, float value)
  367. {
  368. _stat = stat;
  369. _value = value;
  370. }
  371. public Stats getStat()
  372. {
  373. return _stat;
  374. }
  375. public float getValue()
  376. {
  377. return _value;
  378. }
  379. }
  380. /**
  381. * Returns the stat and basestat boni for a given augmentation id
  382. * @param augmentationId
  383. * @return
  384. */
  385. public FastList<AugStat> getAugStatsById(int augmentationId)
  386. {
  387. FastList<AugStat> temp = new FastList<AugStat>();
  388. // An augmentation id contains 2 short vaues so we gotta seperate them here
  389. // both values contain a number from 1-16380, the first 14560 values are stats
  390. // the 14560 stats are devided into 4 blocks each holding 3640 values
  391. // each block contains 40 subblocks holding 91 stat values
  392. // the first 13 values are so called Solo-stats and they have the highest stat increase possible
  393. // after the 13 Solo-stats come 78 combined stats (thats every possible combination of the 13 solo stats)
  394. // the first 12 combined stats (14-26) is the stat 1 combined with stat 2-13
  395. // the next 11 combined stats then are stat 2 combined with stat 3-13 and so on...
  396. // to get the idea have a look @ optiondata_client-e.dat - thats where the data came from :)
  397. int stats[] = new int[2];
  398. stats[0] = 0x0000FFFF & augmentationId;
  399. stats[1] = (augmentationId >> 16);
  400. for (int i = 0; i < 2; i++)
  401. {
  402. // its a stat
  403. if (stats[i] >= STAT_START && stats[i] <= STAT_END)
  404. {
  405. int block = 0;
  406. while (stats[i] > STAT_BLOCKSIZE)
  407. {
  408. stats[i] -= STAT_BLOCKSIZE;
  409. block++;
  410. }
  411. int subblock = 0;
  412. while (stats[i] > STAT_SUBBLOCKSIZE)
  413. {
  414. stats[i] -= STAT_SUBBLOCKSIZE;
  415. subblock++;
  416. }
  417. if (stats[i] < 14) // solo stat
  418. {
  419. augmentationStat as = ((augmentationStat) _augmentationStats[block].get((stats[i] - 1)));
  420. temp.add(new AugStat(as.getStat(), as.getSingleStatValue(subblock)));
  421. }
  422. else
  423. // twin stat
  424. {
  425. stats[i] -= 13; // rescale to 0 (if first of first combined block)
  426. int x = 12; // next combi block has 12 stats
  427. int rescales = 0; // number of rescales done
  428. while (stats[i] > x)
  429. {
  430. stats[i] -= x;
  431. x--;
  432. rescales++;
  433. }
  434. // get first stat
  435. augmentationStat as = ((augmentationStat) _augmentationStats[block].get(rescales));
  436. if (rescales == 0)
  437. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock)));
  438. else
  439. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue((subblock * 2) + 1)));
  440. // get 2nd stat
  441. as = ((augmentationStat) _augmentationStats[block].get(rescales + stats[i]));
  442. if (as.getStat() == Stats.CRITICAL_DAMAGE)
  443. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock)));
  444. else
  445. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(subblock * 2)));
  446. }
  447. }
  448. // its a base stat
  449. else if (stats[i] >= BASESTAT_STR && stats[i] <= BASESTAT_MEN)
  450. {
  451. switch (stats[i])
  452. {
  453. case BASESTAT_STR:
  454. temp.add(new AugStat(Stats.STAT_STR, 1.0f));
  455. break;
  456. case BASESTAT_CON:
  457. temp.add(new AugStat(Stats.STAT_CON, 1.0f));
  458. break;
  459. case BASESTAT_INT:
  460. temp.add(new AugStat(Stats.STAT_INT, 1.0f));
  461. break;
  462. case BASESTAT_MEN:
  463. temp.add(new AugStat(Stats.STAT_MEN, 1.0f));
  464. break;
  465. }
  466. }
  467. }
  468. return temp;
  469. }
  470. }