AugmentationData.java 30 KB

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