AugmentationData.java 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  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.Map;
  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 javolution.util.FastMap;
  24. import net.sf.l2j.Config;
  25. import net.sf.l2j.gameserver.model.L2Augmentation;
  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. * edited by Gigiikun
  37. */
  38. public class AugmentationData
  39. {
  40. private static final Logger _log = Logger.getLogger(AugmentationData.class.getName());
  41. public static final AugmentationData getInstance()
  42. {
  43. return SingletonHolder._instance;
  44. }
  45. // =========================================================
  46. // Data Field
  47. // stats
  48. private static final int STAT_START = 1;
  49. private static final int STAT_END = 14560;
  50. private static final int STAT_BLOCKSIZE = 3640;
  51. //private static final int STAT_NUMBEROF_BLOCKS = 4;
  52. private static final int STAT_SUBBLOCKSIZE = 91;
  53. //private static final int STAT_NUMBEROF_SUBBLOCKS = 40;
  54. private static final int STAT_NUM = 13;
  55. private static final byte[] STATS1_MAP = new byte[STAT_SUBBLOCKSIZE];
  56. private static final byte[] STATS2_MAP = new byte[STAT_SUBBLOCKSIZE];
  57. // skills
  58. private static final int BLUE_START = 14561;
  59. // private static final int PURPLE_START = 14578;
  60. // private static final int RED_START = 14685;
  61. private static final int SKILLS_BLOCKSIZE = 178;
  62. // basestats
  63. private static final int BASESTAT_STR = 16341;
  64. private static final int BASESTAT_CON = 16342;
  65. private static final int BASESTAT_INT = 16343;
  66. private static final int BASESTAT_MEN = 16344;
  67. // accessory
  68. private static final int ACC_START = 16669;
  69. private static final int ACC_BLOCKS_NUM = 10;
  70. private static final int ACC_STAT_SUBBLOCKSIZE = 21;
  71. private static final int ACC_STAT_NUM = 6;
  72. private static final int ACC_RING_START = ACC_START;
  73. private static final int ACC_RING_SKILLS = 18;
  74. private static final int ACC_RING_BLOCKSIZE = ACC_RING_SKILLS + 4 * ACC_STAT_SUBBLOCKSIZE;
  75. private static final int ACC_RING_END = ACC_RING_START + ACC_BLOCKS_NUM * ACC_RING_BLOCKSIZE - 1;
  76. private static final int ACC_EAR_START = ACC_RING_END + 1;
  77. private static final int ACC_EAR_SKILLS = 18;
  78. private static final int ACC_EAR_BLOCKSIZE = ACC_EAR_SKILLS + 4 * ACC_STAT_SUBBLOCKSIZE;
  79. private static final int ACC_EAR_END = ACC_EAR_START + ACC_BLOCKS_NUM * ACC_EAR_BLOCKSIZE - 1;
  80. private static final int ACC_NECK_START = ACC_EAR_END + 1;
  81. private static final int ACC_NECK_SKILLS = 24;
  82. private static final int ACC_NECK_BLOCKSIZE = ACC_NECK_SKILLS + 4 * ACC_STAT_SUBBLOCKSIZE;
  83. private static final int ACC_END = ACC_NECK_START + ACC_BLOCKS_NUM * ACC_NECK_BLOCKSIZE;
  84. private static final byte[] ACC_STATS1_MAP = new byte[ACC_STAT_SUBBLOCKSIZE];
  85. private static final byte[] ACC_STATS2_MAP = new byte[ACC_STAT_SUBBLOCKSIZE];
  86. private FastList<?> _augmentationStats[];
  87. private Map<Integer, FastList<augmentationSkill>> _blueSkills;
  88. private Map<Integer, FastList<augmentationSkill>> _purpleSkills;
  89. private Map<Integer, FastList<augmentationSkill>> _redSkills;
  90. private FastList<?> _augmentationAccStats[];
  91. // =========================================================
  92. // Constructor
  93. private AugmentationData()
  94. {
  95. _log.info("Initializing AugmentationData.");
  96. _augmentationStats = new FastList[4];
  97. _augmentationStats[0] = new FastList<augmentationStat>();
  98. _augmentationStats[1] = new FastList<augmentationStat>();
  99. _augmentationStats[2] = new FastList<augmentationStat>();
  100. _augmentationStats[3] = new FastList<augmentationStat>();
  101. _augmentationAccStats = new FastList[4];
  102. _augmentationAccStats[0] = new FastList<augmentationStat>();
  103. _augmentationAccStats[1] = new FastList<augmentationStat>();
  104. _augmentationAccStats[2] = new FastList<augmentationStat>();
  105. _augmentationAccStats[3] = new FastList<augmentationStat>();
  106. // Lookup tables structure: STAT1 represent first stat, STAT2 - second.
  107. // If both values are the same - use solo stat, if different - combined.
  108. byte idx;
  109. // weapon augmentation block: solo values first
  110. // 00-00, 01-01 ... 11-11,12-12
  111. for (idx = 0; idx < STAT_NUM; idx++)
  112. {
  113. // solo stats
  114. STATS1_MAP[idx] = idx;
  115. STATS2_MAP[idx] = idx;
  116. }
  117. // combined values next.
  118. // 00-01,00-02,00-03 ... 00-11,00-12;
  119. // 01-02,01-03 ... 01-11,01-12;
  120. // ...
  121. // 09-10,09-11,09-12;
  122. // 10-11,10-12;
  123. // 11-12
  124. for (int i = 0; i < STAT_NUM; i++)
  125. {
  126. for (int j = i + 1; j < STAT_NUM; idx++, j++)
  127. {
  128. // combined stats
  129. STATS1_MAP[idx] = (byte)i;
  130. STATS2_MAP[idx] = (byte)j;
  131. }
  132. }
  133. idx = 0;
  134. // accessory augmentation block, structure is different:
  135. // 00-00,00-01,00-02,00-03,00-04,00-05
  136. // 01-01,01-02,01-03,01-04,01-05
  137. // 02-02,02-03,02-04,02-05
  138. // 03-03,03-04,03-05
  139. // 04-04,04-05
  140. // 05-05
  141. // First values always solo, next are combined
  142. for (int i = 0; i < ACC_STAT_NUM; i++)
  143. {
  144. for (int j = i; j < ACC_STAT_NUM; idx++, j++)
  145. {
  146. ACC_STATS1_MAP[idx] = (byte)i;
  147. ACC_STATS2_MAP[idx] = (byte)j;
  148. }
  149. }
  150. _blueSkills = new FastMap<Integer, FastList<augmentationSkill>>();
  151. _purpleSkills = new FastMap<Integer, FastList<augmentationSkill>>();
  152. _redSkills = new FastMap<Integer, FastList<augmentationSkill>>();
  153. for (int i = 1; i <= 10; i++)
  154. {
  155. _blueSkills.put(i, new FastList<augmentationSkill>());
  156. _purpleSkills.put(i, new FastList<augmentationSkill>());
  157. _redSkills.put(i, new FastList<augmentationSkill>());
  158. }
  159. load();
  160. // Use size*4: since theres 4 blocks of stat-data with equivalent size
  161. _log.info("AugmentationData: Loaded: " + (_augmentationStats[0].size() * 4) + " augmentation stats.");
  162. _log.info("AugmentationData: Loaded: " + (_augmentationAccStats[0].size() * 4) + " accessory augmentation stats.");
  163. for (int i = 1; i <= 10; i++)
  164. {
  165. _log.info("AugmentationData: Loaded: " + _blueSkills.get(i).size() + " blue, " + _purpleSkills.get(i).size() + " purple and "
  166. + _redSkills.get(i).size() + " red skills for lifeStoneLevel " + i);
  167. }
  168. }
  169. // =========================================================
  170. // Nested Class
  171. public class augmentationSkill
  172. {
  173. private int _skillId;
  174. private int _skillLevel;
  175. private int _augmentationSkillId;
  176. public augmentationSkill(int skillId, int skillLevel, int augmentationSkillId)
  177. {
  178. _skillId = skillId;
  179. _skillLevel = skillLevel;
  180. _augmentationSkillId = augmentationSkillId;
  181. }
  182. public L2Skill getSkill()
  183. {
  184. return SkillTable.getInstance().getInfo(_skillId, _skillLevel);
  185. }
  186. public int getAugmentationSkillId()
  187. {
  188. return _augmentationSkillId;
  189. }
  190. }
  191. public class augmentationStat
  192. {
  193. private final Stats _stat;
  194. private final int _singleSize;
  195. private final int _combinedSize;
  196. private final float _singleValues[];
  197. private final float _combinedValues[];
  198. public augmentationStat(Stats stat, float sValues[], float cValues[])
  199. {
  200. _stat = stat;
  201. _singleSize = sValues.length;
  202. _singleValues = sValues;
  203. _combinedSize = cValues.length;
  204. _combinedValues = cValues;
  205. }
  206. public int getSingleStatSize()
  207. {
  208. return _singleSize;
  209. }
  210. public int getCombinedStatSize()
  211. {
  212. return _combinedSize;
  213. }
  214. public float getSingleStatValue(int i)
  215. {
  216. if (i >= _singleSize || i < 0)
  217. return _singleValues[_singleSize - 1];
  218. return _singleValues[i];
  219. }
  220. public float getCombinedStatValue(int i)
  221. {
  222. if (i >= _combinedSize || i < 0)
  223. return _combinedValues[_combinedSize - 1];
  224. return _combinedValues[i];
  225. }
  226. public Stats getStat()
  227. {
  228. return _stat;
  229. }
  230. }
  231. // =========================================================
  232. // Method - Private
  233. @SuppressWarnings("unchecked")
  234. private final void load()
  235. {
  236. // Load the skillmap
  237. // Note: the skillmap data is only used when generating new augmentations
  238. // the client expects a different id in order to display the skill in the
  239. // items description...
  240. try
  241. {
  242. int badAugmantData = 0;
  243. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  244. factory.setValidating(false);
  245. factory.setIgnoringComments(true);
  246. File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_skillmap.xml");
  247. if (!file.exists())
  248. {
  249. if (Config.DEBUG)
  250. _log.info("The augmentation skillmap file is missing.");
  251. return;
  252. }
  253. Document doc = factory.newDocumentBuilder().parse(file);
  254. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  255. {
  256. if ("list".equalsIgnoreCase(n.getNodeName()))
  257. {
  258. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  259. {
  260. if ("augmentation".equalsIgnoreCase(d.getNodeName()))
  261. {
  262. NamedNodeMap attrs = d.getAttributes();
  263. int skillId = 0, augmentationId = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
  264. int skillLvL = 0;
  265. String type = "blue";
  266. for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
  267. {
  268. if ("skillId".equalsIgnoreCase(cd.getNodeName()))
  269. {
  270. attrs = cd.getAttributes();
  271. skillId = Integer.parseInt(attrs.getNamedItem("val").getNodeValue());
  272. }
  273. else if ("skillLevel".equalsIgnoreCase(cd.getNodeName()))
  274. {
  275. attrs = cd.getAttributes();
  276. skillLvL = Integer.parseInt(attrs.getNamedItem("val").getNodeValue());
  277. }
  278. else if ("type".equalsIgnoreCase(cd.getNodeName()))
  279. {
  280. attrs = cd.getAttributes();
  281. type = attrs.getNamedItem("val").getNodeValue();
  282. }
  283. }
  284. if (skillId == 0)
  285. {
  286. if (Config.DEBUG)
  287. _log.log(Level.SEVERE, "Bad skillId in augmentation_skillmap.xml in the augmentationId:"
  288. + augmentationId);
  289. badAugmantData++;
  290. continue;
  291. }
  292. else if (skillLvL == 0)
  293. {
  294. if (Config.DEBUG)
  295. _log.log(Level.SEVERE, "Bad skillLevel in augmentation_skillmap.xml in the augmentationId:"
  296. + augmentationId);
  297. badAugmantData++;
  298. continue;
  299. }
  300. int k = 1;
  301. while ((augmentationId - k * SKILLS_BLOCKSIZE) >= BLUE_START)
  302. k++;
  303. if (type.equalsIgnoreCase("blue"))
  304. _blueSkills.get(k).add(new augmentationSkill(skillId, skillLvL, augmentationId));
  305. else if (type.equalsIgnoreCase("purple"))
  306. _purpleSkills.get(k).add(new augmentationSkill(skillId, skillLvL, augmentationId));
  307. else
  308. _redSkills.get(k).add(new augmentationSkill(skillId, skillLvL, augmentationId));
  309. }
  310. }
  311. }
  312. }
  313. if (badAugmantData != 0)
  314. _log.info("AugmentationData: " + badAugmantData + " bad skill(s) were skipped.");
  315. }
  316. catch (Exception e)
  317. {
  318. _log.log(Level.SEVERE, "Error parsing augmentation_skillmap.xml.", e);
  319. return;
  320. }
  321. // Load the stats from xml
  322. for (int i = 1; i < 5; i++)
  323. {
  324. try
  325. {
  326. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  327. factory.setValidating(false);
  328. factory.setIgnoringComments(true);
  329. File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_stats" + i + ".xml");
  330. if (!file.exists())
  331. {
  332. if (Config.DEBUG)
  333. _log.info("The augmentation stat data file " + i + " is missing.");
  334. return;
  335. }
  336. Document doc = factory.newDocumentBuilder().parse(file);
  337. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  338. {
  339. if ("list".equalsIgnoreCase(n.getNodeName()))
  340. {
  341. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  342. {
  343. if ("stat".equalsIgnoreCase(d.getNodeName()))
  344. {
  345. NamedNodeMap attrs = d.getAttributes();
  346. String statName = attrs.getNamedItem("name").getNodeValue();
  347. float soloValues[] = null, combinedValues[] = null;
  348. for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
  349. {
  350. if ("table".equalsIgnoreCase(cd.getNodeName()))
  351. {
  352. attrs = cd.getAttributes();
  353. String tableName = attrs.getNamedItem("name").getNodeValue();
  354. StringTokenizer data = new StringTokenizer(cd.getFirstChild().getNodeValue());
  355. FastList<Float> array = new FastList<Float>();
  356. while (data.hasMoreTokens())
  357. array.add(Float.parseFloat(data.nextToken()));
  358. if (tableName.equalsIgnoreCase("#soloValues"))
  359. {
  360. soloValues = new float[array.size()];
  361. int x = 0;
  362. for (float value : array)
  363. soloValues[x++] = value;
  364. }
  365. else
  366. {
  367. combinedValues = new float[array.size()];
  368. int x = 0;
  369. for (float value : array)
  370. combinedValues[x++] = value;
  371. }
  372. }
  373. }
  374. // store this stat
  375. ((FastList<augmentationStat>) _augmentationStats[(i - 1)]).add(new augmentationStat(Stats.valueOfXml(statName), soloValues, combinedValues));
  376. }
  377. }
  378. }
  379. }
  380. }
  381. catch (Exception e)
  382. {
  383. _log.log(Level.SEVERE, "Error parsing augmentation_stats" + i + ".xml.", e);
  384. return;
  385. }
  386. try
  387. {
  388. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  389. factory.setValidating(false);
  390. factory.setIgnoringComments(true);
  391. File file = new File(Config.DATAPACK_ROOT + "/data/stats/augmentation/augmentation_jewel_stats" + i + ".xml");
  392. if (!file.exists())
  393. {
  394. if (Config.DEBUG)
  395. _log.info("The jewel augmentation stat data file " + i + " is missing.");
  396. return;
  397. }
  398. Document doc = factory.newDocumentBuilder().parse(file);
  399. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  400. {
  401. if ("list".equalsIgnoreCase(n.getNodeName()))
  402. {
  403. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  404. {
  405. if ("stat".equalsIgnoreCase(d.getNodeName()))
  406. {
  407. NamedNodeMap attrs = d.getAttributes();
  408. String statName = attrs.getNamedItem("name").getNodeValue();
  409. float soloValues[] = null, combinedValues[] = null;
  410. for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
  411. {
  412. if ("table".equalsIgnoreCase(cd.getNodeName()))
  413. {
  414. attrs = cd.getAttributes();
  415. String tableName = attrs.getNamedItem("name").getNodeValue();
  416. StringTokenizer data = new StringTokenizer(cd.getFirstChild().getNodeValue());
  417. FastList<Float> array = new FastList<Float>();
  418. while (data.hasMoreTokens())
  419. array.add(Float.parseFloat(data.nextToken()));
  420. if (tableName.equalsIgnoreCase("#soloValues"))
  421. {
  422. soloValues = new float[array.size()];
  423. int x = 0;
  424. for (float value : array)
  425. soloValues[x++] = value;
  426. }
  427. else
  428. {
  429. combinedValues = new float[array.size()];
  430. int x = 0;
  431. for (float value : array)
  432. combinedValues[x++] = value;
  433. }
  434. }
  435. }
  436. // store this stat
  437. ((FastList<augmentationStat>) _augmentationAccStats[(i - 1)]).add(new augmentationStat(Stats.valueOfXml(statName), soloValues, combinedValues));
  438. }
  439. }
  440. }
  441. }
  442. }
  443. catch (Exception e)
  444. {
  445. _log.log(Level.SEVERE, "Error parsing jewel augmentation_stats" + i + ".xml.", e);
  446. return;
  447. }
  448. }
  449. }
  450. // =========================================================
  451. // Properties - Public
  452. /**
  453. * Generate a new random augmentation
  454. * @param item
  455. * @param lifeStoneLevel
  456. * @param lifeSoneGrade
  457. * @return L2Augmentation
  458. */
  459. public L2Augmentation generateRandomAugmentation(int lifeStoneLevel, int lifeStoneGrade)
  460. {
  461. // Note that stat12 stands for stat 1 AND 2 (same for stat34 ;p )
  462. // this is because a value can contain up to 2 stat modifications
  463. // (there are two short values packed in one integer value, meaning 4 stat modifications at max)
  464. // for more info take a look at getAugStatsById(...)
  465. // Note: lifeStoneGrade: (0 means low grade, 3 top grade)
  466. // First: determine whether we will add a skill/baseStatModifier or not
  467. // because this determine which color could be the result
  468. int skill_Chance = 0;
  469. int stat34 = 0;
  470. boolean generateSkill = false;
  471. boolean generateGlow = false;
  472. int resultColor = 0;
  473. //lifestonelevel is used for stat Id and skill level, but here the max level is 10
  474. if (lifeStoneLevel > 10)
  475. lifeStoneLevel = 10;
  476. switch (lifeStoneGrade)
  477. {
  478. case 0:
  479. skill_Chance = Config.AUGMENTATION_NG_SKILL_CHANCE;
  480. if (Rnd.get(1, 100) <= Config.AUGMENTATION_NG_GLOW_CHANCE)
  481. generateGlow = true;
  482. break;
  483. case 1:
  484. skill_Chance = Config.AUGMENTATION_MID_SKILL_CHANCE;
  485. if (Rnd.get(1, 100) <= Config.AUGMENTATION_MID_GLOW_CHANCE)
  486. generateGlow = true;
  487. break;
  488. case 2:
  489. skill_Chance = Config.AUGMENTATION_HIGH_SKILL_CHANCE;
  490. if (Rnd.get(1, 100) <= Config.AUGMENTATION_HIGH_GLOW_CHANCE)
  491. generateGlow = true;
  492. break;
  493. case 3:
  494. skill_Chance = Config.AUGMENTATION_TOP_SKILL_CHANCE;
  495. if (Rnd.get(1, 100) <= Config.AUGMENTATION_TOP_GLOW_CHANCE)
  496. generateGlow = true;
  497. }
  498. if (Rnd.get(1, 100) <= skill_Chance)
  499. generateSkill = true;
  500. else if (Rnd.get(1, 100) <= Config.AUGMENTATION_BASESTAT_CHANCE)
  501. stat34 = Rnd.get(BASESTAT_STR, BASESTAT_MEN);
  502. // Second: decide which grade the augmentation result is going to have:
  503. // 0:yellow, 1:blue, 2:purple, 3:red
  504. // The chances used here are most likely custom,
  505. // whats known is: you cant have yellow with skill(or baseStatModifier)
  506. // noGrade stone can not have glow, mid only with skill, high has a chance(custom), top allways glow
  507. if (stat34 == 0 && !generateSkill)
  508. {
  509. resultColor = Rnd.get(0, 100);
  510. if (resultColor <= (15 * lifeStoneGrade) + 40)
  511. resultColor = 1;
  512. else
  513. resultColor = 0;
  514. }
  515. else
  516. {
  517. resultColor = Rnd.get(0, 100);
  518. if (resultColor <= (10 * lifeStoneGrade) + 5 || stat34 != 0)
  519. resultColor = 3;
  520. else if (resultColor <= (10 * lifeStoneGrade) + 10)
  521. resultColor = 1;
  522. else
  523. resultColor = 2;
  524. }
  525. // Third: Calculate the subblock offset for the choosen color,
  526. // and the level of the lifeStone
  527. // from large number of retail augmentations:
  528. // no skill part
  529. // Id for stat12:
  530. // A:1-910 B:911-1820 C:1821-2730 D:2731-3640 E:3641-4550 F:4551-5460 G:5461-6370 H:6371-7280
  531. // Id for stat34(this defines the color):
  532. // I:7281-8190(yellow) K:8191-9100(blue) L:10921-11830(yellow) M:11831-12740(blue)
  533. // you can combine I-K with A-D and L-M with E-H
  534. // using C-D or G-H Id you will get a glow effect
  535. // there seems no correlation in which grade use which Id except for the glowing restriction
  536. // skill part
  537. // Id for stat12:
  538. // same for no skill part
  539. // A same as E, B same as F, C same as G, D same as H
  540. // A - no glow, no grade LS
  541. // B - weak glow, mid grade LS?
  542. // C - glow, high grade LS?
  543. // D - strong glow, top grade LS?
  544. // is neither a skill nor basestat used for stat34? then generate a normal stat
  545. int stat12 = 0;
  546. if (stat34 == 0 && !generateSkill)
  547. {
  548. int temp = Rnd.get(2, 3);
  549. int colorOffset = resultColor * (10 * STAT_SUBBLOCKSIZE) + temp * STAT_BLOCKSIZE + 1;
  550. int offset = ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE) + colorOffset;
  551. stat34 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE - 1);
  552. if (generateGlow && lifeStoneGrade >= 2)
  553. offset = ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE) + (temp - 2) * STAT_BLOCKSIZE + lifeStoneGrade
  554. * (10 * STAT_SUBBLOCKSIZE) + 1;
  555. else
  556. offset = ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE) + (temp - 2) * STAT_BLOCKSIZE + Rnd.get(0, 1)
  557. * (10 * STAT_SUBBLOCKSIZE) + 1;
  558. stat12 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE - 1);
  559. }
  560. else
  561. {
  562. int offset;
  563. if (!generateGlow)
  564. offset = ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE) + Rnd.get(0, 1) * STAT_BLOCKSIZE + 1;
  565. else
  566. offset = ((lifeStoneLevel - 1) * STAT_SUBBLOCKSIZE) + Rnd.get(0, 1) * STAT_BLOCKSIZE + (lifeStoneGrade + resultColor) / 2
  567. * (10 * STAT_SUBBLOCKSIZE) + 1;
  568. stat12 = Rnd.get(offset, offset + STAT_SUBBLOCKSIZE - 1);
  569. }
  570. // generate a skill if neccessary
  571. L2Skill skill = null;
  572. if (generateSkill)
  573. {
  574. augmentationSkill temp = null;
  575. switch (resultColor)
  576. {
  577. case 1: // blue skill
  578. temp = _blueSkills.get(lifeStoneLevel).get(Rnd.get(0, _blueSkills.get(lifeStoneLevel).size() - 1));
  579. skill = temp.getSkill();
  580. stat34 = temp.getAugmentationSkillId();
  581. break;
  582. case 2: // purple skill
  583. temp = _purpleSkills.get(lifeStoneLevel).get(Rnd.get(0, _purpleSkills.get(lifeStoneLevel).size() - 1));
  584. skill = temp.getSkill();
  585. stat34 = temp.getAugmentationSkillId();
  586. break;
  587. case 3: // red skill
  588. temp = _redSkills.get(lifeStoneLevel).get(Rnd.get(0, _redSkills.get(lifeStoneLevel).size() - 1));
  589. skill = temp.getSkill();
  590. stat34 = temp.getAugmentationSkillId();
  591. break;
  592. }
  593. }
  594. if (Config.DEBUG)
  595. _log.info("Augmentation success: stat12=" + stat12 + "; stat34=" + stat34 + "; resultColor=" + resultColor + "; level="
  596. + lifeStoneLevel + "; grade=" + lifeStoneGrade);
  597. return new L2Augmentation(((stat34 << 16) + stat12), skill);
  598. }
  599. public class AugStat
  600. {
  601. private final Stats _stat;
  602. private final float _value;
  603. public AugStat(Stats stat, float value)
  604. {
  605. _stat = stat;
  606. _value = value;
  607. }
  608. public Stats getStat()
  609. {
  610. return _stat;
  611. }
  612. public float getValue()
  613. {
  614. return _value;
  615. }
  616. }
  617. /**
  618. * Returns the stat and basestat boni for a given augmentation id
  619. * @param augmentationId
  620. * @return
  621. */
  622. public FastList<AugStat> getAugStatsById(int augmentationId)
  623. {
  624. FastList<AugStat> temp = new FastList<AugStat>();
  625. // An augmentation id contains 2 short vaues so we gotta seperate them here
  626. // both values contain a number from 1-16380, the first 14560 values are stats
  627. // the 14560 stats are divided into 4 blocks each holding 3640 values
  628. // each block contains 40 subblocks holding 91 stat values
  629. // the first 13 values are so called Solo-stats and they have the highest stat increase possible
  630. // after the 13 Solo-stats come 78 combined stats (thats every possible combination of the 13 solo stats)
  631. // the first 12 combined stats (14-26) is the stat 1 combined with stat 2-13
  632. // the next 11 combined stats then are stat 2 combined with stat 3-13 and so on...
  633. // to get the idea have a look @ optiondata_client-e.dat - thats where the data came from :)
  634. int stats[] = new int[2];
  635. stats[0] = 0x0000FFFF & augmentationId;
  636. stats[1] = (augmentationId >> 16);
  637. for (int i = 0; i < 2; i++)
  638. {
  639. // weapon augmentation - stats
  640. if (stats[i] >= STAT_START && stats[i] <= STAT_END)
  641. {
  642. int base = stats[i] - STAT_START;
  643. int color = base / STAT_BLOCKSIZE; // 4 color blocks
  644. int subblock = base % STAT_BLOCKSIZE; // offset in color block
  645. int level = subblock / STAT_SUBBLOCKSIZE; // stat level (sub-block number)
  646. int stat = subblock % STAT_SUBBLOCKSIZE; // offset in sub-block - stat
  647. byte stat1 = STATS1_MAP[stat];
  648. byte stat2 = STATS2_MAP[stat];
  649. if (stat1 == stat2) // solo stat
  650. {
  651. augmentationStat as = ((augmentationStat) _augmentationStats[color].get(stat1));
  652. temp.add(new AugStat(as.getStat(), as.getSingleStatValue(level)));
  653. }
  654. else // combined stat
  655. {
  656. augmentationStat as = ((augmentationStat) _augmentationStats[color].get(stat1));
  657. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(level)));
  658. as = ((augmentationStat) _augmentationStats[color].get(stat2));
  659. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(level)));
  660. }
  661. }
  662. // its a base stat
  663. else if (stats[i] >= BASESTAT_STR && stats[i] <= BASESTAT_MEN)
  664. {
  665. switch (stats[i])
  666. {
  667. case BASESTAT_STR:
  668. temp.add(new AugStat(Stats.STAT_STR, 1.0f));
  669. break;
  670. case BASESTAT_CON:
  671. temp.add(new AugStat(Stats.STAT_CON, 1.0f));
  672. break;
  673. case BASESTAT_INT:
  674. temp.add(new AugStat(Stats.STAT_INT, 1.0f));
  675. break;
  676. case BASESTAT_MEN:
  677. temp.add(new AugStat(Stats.STAT_MEN, 1.0f));
  678. break;
  679. }
  680. }
  681. // accessory augmentation
  682. // 3 areas for rings, earrings and necklaces
  683. // each area consist of 10 blocks (level)
  684. // each block has skills first (18 or 24 for necklaces)
  685. // and sub-block for stats next
  686. else if (stats[i] >= ACC_START && stats[i] <= ACC_END)
  687. {
  688. int base, level, subblock;
  689. if (stats[i] <= ACC_RING_END) // rings area
  690. {
  691. base = stats[i] - ACC_RING_START; // calculate base offset
  692. level = base / ACC_RING_BLOCKSIZE; // stat level (block number)
  693. subblock = (base % ACC_RING_BLOCKSIZE) - ACC_RING_SKILLS; // skills first
  694. }
  695. else if (stats[i] <= ACC_EAR_END) //earrings area
  696. {
  697. base = stats[i] - ACC_EAR_START;
  698. level = base / ACC_EAR_BLOCKSIZE;
  699. subblock = (base % ACC_EAR_BLOCKSIZE) - ACC_EAR_SKILLS;
  700. }
  701. else // necklaces
  702. {
  703. base = stats[i] - ACC_NECK_START;
  704. level = base / ACC_NECK_BLOCKSIZE;
  705. subblock = (base % ACC_NECK_BLOCKSIZE) - ACC_NECK_SKILLS;
  706. }
  707. if (subblock >= 0) // stat, not skill
  708. {
  709. int color = subblock / ACC_STAT_SUBBLOCKSIZE;
  710. int stat = subblock % ACC_STAT_SUBBLOCKSIZE;
  711. byte stat1 = ACC_STATS1_MAP[stat];
  712. byte stat2 = ACC_STATS2_MAP[stat];
  713. if (stat1 == stat2) // solo
  714. {
  715. augmentationStat as = ((augmentationStat) _augmentationAccStats[color].get(stat1));
  716. temp.add(new AugStat(as.getStat(), as.getSingleStatValue(level)));
  717. }
  718. else // combined
  719. {
  720. augmentationStat as = ((augmentationStat) _augmentationAccStats[color].get(stat1));
  721. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(level)));
  722. as = ((augmentationStat) _augmentationAccStats[color].get(stat2));
  723. temp.add(new AugStat(as.getStat(), as.getCombinedStatValue(level)));
  724. }
  725. }
  726. }
  727. }
  728. return temp;
  729. }
  730. @SuppressWarnings("synthetic-access")
  731. private static class SingletonHolder
  732. {
  733. protected static final AugmentationData _instance = new AugmentationData();
  734. }
  735. }