AugmentationData.java 29 KB

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