AugmentationData.java 30 KB

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