AugmentationData.java 30 KB

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